Опять же, в сети полно информации о том где скачать готовую прошивку, как ее загрузить в контроллер и настроить. Достаточно обратиться к гуглу.
Я хочу поговорить не об этом. Этот материал для тех, кому интересны внутренности, принципы и алгоритмы, заложенные в прошивку.
Если Уважаемое Сообщество сочтет, что этой теме тут не место - пишите в комментариях - статью уберу и новых писать не буду.
Вообще, материала много и я планирую сделать несколько статей на эту тему. К сожалению, дело не быстрое...
Начать предлагаю с самого простого: програмный фильтр низких частот (low pass filter).
Велосипед тут изобрести сложно. В интернете есть масса информации на эту тему.
Итак, имеем некоторый сигнал канала Ch, полученный с приемника. В состоянии покоя, этот сигнал имеет "дрожание" +/-2 микросекунды. Чтобы сгладить эти мелкие колебания воспользуемся самым простым ФНЧ на основе интегратора. Для этого после каждого нового вычисления значения канала Ch применим к нему формулу Ch_f = (1-w) * Ch_f + w * Ch. Здесь Ch_f - сигнал на выходе фильтра, полученный при предыдущем вычислении, Ch - сигнал с приемника, w - некоторый весовой коэффициент.
Значение w выбирается в диапазоне от 0 до 1 и определяет, какие колебания требуют подавления. Как следует из формулы, w указывает приоритет входного сигнала относительно ранее полученного. Чем оно ближе к 1, тем больше шумов будет пропускать фильтр. А чем ближе к нулю, тем больше будет "запаздывание" конечного сигнала и, как результат, более вялая реакция на изменение состояния органов управления аппаратуры.
Весовой коэффициент можно подобрать эмпирическим путем или вычислить. В последнем случае воспользуемся следующей формулой:
t = (1 - w) * Tp / w
Здесь Tp - период вычислений или длинна пакета PPM-сигнала, t - период времени, который отделяет слишком быстрые изменения от требуемых.
Отсюда w = Tp / (Tp + t).
К примеру, пусть Tp = 20ms, а t = 50ms. Тогда весовой коэффициент будет равен примерно 0.286.
На практике я использовал коэффициент 0.14.
Я доработал скетч из выше указанного примера:
#include <PinChangeInt.h>
#define PPM_PIN 5
#define MAX_PPM_CHANNELS 25
#define Kw 0.14
volatile uint16_t temp_time;
volatile uint16_t up_time;
volatile uint16_t d_time;
volatile uint16_t ChannelsCount;
volatile float Channel[MAX_PPM_CHANNELS];
volatile uint8_t Curr_Channel;
void setup()
{
digitalWrite(19, HIGH); // включить резистор на выводе аналогового входа 0
pinMode(13, OUTPUT);
ChannelsCount==0xff;
Curr_Channel=0;
Serial.begin(9600);
Serial.println("Start");
TCCR1B = 0; //stop timer
TCCR1A = 0;
TCNT1 = 0; //setup
TCCR1A = 0;
up_time = 0;
TCCR1B = 0<<CS12 | 1<<CS11 | 0<<CS10;//0x1A; //start timer with 1/8
PCintPort::attachInterrupt(PPM_PIN, CalcPPM, CHANGE);
}
float Rssi;
void loop()
{
Rssi = (1-0.05)*Rssi + 0.05*analogRead(5);
delay(50);
Serial.print("RSSI="); Serial.print(round(Rssi));
Serial.print("Channels="+String(ChannelsCount));
for(uint8_t i=0;i<ChannelsCount;i++){
Serial.print(" Ch[="+String(i)+"]="); Serial.print(round(Channel[i]));
}
Serial.println(" ");
digitalWrite(13, !digitalRead(13));
}
void CalcPPM()
{
temp_time = TCNT1;
if (!PCintPort::pinState)
{
if (up_time>temp_time) d_time=(0xffff-up_time+temp_time)>>1;
d_time = (temp_time-up_time)>>1;
up_time = temp_time;
if (d_time>3500)
{
//Sync
ChannelsCount=Curr_Channel;
Curr_Channel = 0;
}
else
{
//Channel
Channel[Curr_Channel]=(1-Kw)*Channel[Curr_Channel] + Kw * d_time;
Curr_Channel++;
}
}
}
В следующей статье я предполагаю описать некоторые теоретические основы для получения информации о положении аппарата в пространстве.
Я собрался сделать декодер ППМ на Ардуино и сразу нашёл Вашу и заодно вот эту: http://rc-master.ucoz.ru/publ/19-1-0-28 статью. Но задача немножко отличается. Мне надо не просто распечатать через порт длительность импульсов, а управлять реальными сервами, имея возможность вручную микшировать каналы. Действовал следующим образом:
1. Запустил Ардуину с Вашим скетчем, подал на 5-й пин ППМ с разъёма Турниги, убедился что всё работает.
2. Удалил всё что связано с РССИ (мне не надо). Перекинул вход ППМ на 0-й пин. Опять проверил - всё ок.
3. Добавил библиотеку Servo, объявил названия серв. Опять всё проверил - работает.
4. Добавил в "void setup" привязки серв к пинам Ардуино в виде "servoname.attach(pin);" И тут всё встало....
Программа перестаёт распознавать количество каналов и печатает полную пургу. Бился долго и безрезультатно.
В чём засада? Как воспользоваться результатом не для печати, а для реального управления сервами?
Так что опубликую по мере готовности...
Единственно хотелось бы на будущее (для не очень понятливых) побольше комментариев в тексте программы. Хотя бы основные блоки, процедуры и т.п. (что делают и для чего нужны) Так-то все понятно, но все же.
Channel[Curr_Channel]=(1-Kw)*Channel[Curr_Channel] + Kw * d_time;
значение каждого канала рассчитывается как сумма (1 - w) * на пред. состояние этого канала и w * на свежеполученные показания того же канала.
Я думаю, ту статью сюда переносить не стоит. Комментарии я там отключил - все равно кроме спама ни кто ничего не пишет. Но там есть форма обратной связи. Пишите свои вопросы и я дополню материал.
Алгоритмов много... Общая тенденция в том, что чем длиннее ряд, тем меньше погрешность, тем качественнее фильтр... тем больше затраты памяти МК. А ресурсы последнего достаточно скудные... Во всем надо искать золотую середину.
А эта задача сама по себе смысла не имеет. Это всего лишь маленький кирпичик.
Но я говорил в первую очередь не про код. Этот алгоритм хранит всего одно последнее значение. Алгоритмы на 3-4-5 последних значений затребуют в 3-4-5 раз больше оперативки. Да и все зависит от конкретных условий...
Если есть возможность и необходимость можно и их использовать. Кто бы спорил!
Не находите, что это существенная разница?
Несколько абсурдная задача, но все же...
3 оси гироскопа + 3 оси акселя + 3 оси компаса + барометр и т.д....
на каждое 4 значения вместо одного...
Но еще раз скажу: 1.это вспомогательная задача; 2. не стоит разбрасываться ресурсами если в этом нет необходимости.
Я не претендую на роль эксперта в этом деле - так, немного "плюшками балуюсь"...