Таймер в sx127x

В процессе разработки поддержки FSK модуляции для sx127x, я столкнулся с интересной задачей. В чипах sx127x есть уникальный режим, который позволяет периодически отправлять сообщения. Для этого достаточно задать байты для передачи, интервал между сообщениями и запустить стейт-машину, которая будет отправлять одинаковое сообщение с указанным интервалом.

Особенность задачи заключается в том, что интервал задаётся двумя таймерами. И для каждого таймера нужно задать два параметра: разрешение таймера и количество тиков. Разрешение таймера может быть трёх типов: 64 мкс, 4.1 мс и 262 мс. Количество тиков может быть от 0 до 255. Таким образом формула для вычисления интервала будет следующая:

$$ интервал = X_1\times Y_1 + X_2\times Y_2 $$

Это уравнение с четырмя неизвестными. Я не каждый день решаю уравнения с четырмя неизвестными, а если быть точнее, то мне никогда не приходилось этого делать за всю мою программисткую карьеру. Поэтому поначалу я немного задумался.

Если чуть-чуть приглядется к условию, то можно попробовать решить это уравнение методом перебора. Ведь разрешение таймера может быть всего трёх типов. А значит нужно перебрать всего 6 различных условий.

При этом в любом решении будет ошибка дискретизации, которая схематично должна выглядеть как-то так:

Интервал
Интервал
Ошибка
дискретизации
Ошибка дискретизации
Text is not SVG - cannot display

Собственно поиск решения, должен свестись к тому, чтобы уменьшить эту ошибку на всём поддерживаемом интервале. А это от 0 мкс до \( 255\times 262\times 2 \) мс.

Я разбил весь интервал на несколько, каждый из которых имеет своё разрешение. В итоге получилась следующая программа:

float p1 = 0.064f;
float p2 = 4.1f;
float p3 = 262;

float max = p3 * 255 * 2;
float totalError = 0.0f;
for (float i = p1; i <= max; i += p1) {

	float t1Resolution = 0.0f;
	float t2Resolution = 0.0f;
	int t1Ticks = 0;
	int t2Ticks = 0;

	if (i / p1 < 1) {
		continue;
	}
	if (i <= 255 * p1) {
		t1Resolution = p1;
		t1Ticks = (int) (i / p1);
	} else if (i <= 255 * p1 * 2) {
		t1Resolution = p1;
		t2Resolution = p1;
		t1Ticks = (int) (i / p1 / 2);
	} else if (i <= (255 * p2 + 255 * p1)) {
		t1Resolution = p2;
		t2Resolution = p1;
		t1Ticks = (int) (i / p2);
	} else if (i <= 255 * p2 * 2) {
		t1Resolution = p2;
		t2Resolution = p2;
		t1Ticks = (int) (i / p2 / 2);
	} else if (i <= (255 * p3 + 255 * p1)) {
		t1Resolution = p3;
		t2Resolution = p1;
		t1Ticks = (int) (i / p3);
	} else if (i <= (255 * p3 + 255 * p2)) {
		t1Resolution = p3;
		t2Resolution = p2;
		t1Ticks = (int) (i / p3);
	} else {
		t1Resolution = p3;
		t2Resolution = p3;
		t1Ticks = (int) (i / p3 / 2);
	}

	if (t2Resolution != 0) {
		t2Ticks = (int) ((i - t1Resolution * t1Ticks) / t2Resolution);
	}

	float actual = t1Resolution * t1Ticks + t2Resolution * t2Ticks;

	totalError += Math.abs(i - actual);
}

Правда, в результате ошибка распределена не так, как я ожидал:

Видно, что начиная с момента \(262\times 255 + 4.1\times 255 \) мс, интервал нельзя достаточно точно представить.

Работа по добавлению поддержки FSK далека от завершения. Необходимо разобраться с совсем уж мистическими ошибками и протестировать всевозможные варианты работы. На каждом этапе появляются вот такие вот небольшие задачки, которые заставляют подумать над проблемами под совершенно разными углами.