Таймер в sx127x
В процессе разработки поддержки FSK модуляции для sx127x, я столкнулся с интересной задачей. В чипах sx127x есть уникальный режим, который позволяет периодически отправлять сообщения. Для этого достаточно задать байты для передачи, интервал между сообщениями и запустить стейт-машину, которая будет отправлять одинаковое сообщение с указанным интервалом.
Особенность задачи заключается в том, что интервал задаётся двумя таймерами. И для каждого таймера нужно задать два параметра: разрешение таймера и количество тиков. Разрешение таймера может быть трёх типов: 64 мкс, 4.1 мс и 262 мс. Количество тиков может быть от 0 до 255. Таким образом формула для вычисления интервала будет следующая:
$$ интервал = X_1\times Y_1 + X_2\times Y_2 $$
Это уравнение с четырмя неизвестными. Я не каждый день решаю уравнения с четырмя неизвестными, а если быть точнее, то мне никогда не приходилось этого делать за всю мою программисткую карьеру. Поэтому поначалу я немного задумался.
Если чуть-чуть приглядется к условию, то можно попробовать решить это уравнение методом перебора. Ведь разрешение таймера может быть всего трёх типов. А значит нужно перебрать всего 6 различных условий.
При этом в любом решении будет ошибка дискретизации, которая схематично должна выглядеть как-то так:
Собственно поиск решения, должен свестись к тому, чтобы уменьшить эту ошибку на всём поддерживаемом интервале. А это от 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 далека от завершения. Необходимо разобраться с совсем уж мистическими ошибками и протестировать всевозможные варианты работы. На каждом этапе появляются вот такие вот небольшие задачки, которые заставляют подумать над проблемами под совершенно разными углами.