Как принять сигнал с приемника в AVR (PWM|Digital)

Probelzaelo
Bare:

мне надо считать 3 канала с конечных выходов приемника(куда подключаются сервы). И желательно используя только одно прерывание.

не вопрос, заводите их, каждую на свою ногу и одновременно через диод типа кд521/522 на одно, общее прерывание… (получится этакая схема 3ИЛИ), при вызове прерывания читайте состояние всех трех ног, по какой из них произошел вызов легко сами определитесь 😉
Но есть подозрение что не очень красиво это будет работать… ибо там, на каналах, PWM. а значит практически одновременно может быть сигнал. Надеюсь что каналы ШИМят все же по очереди, и в том же порядке как это было принято из входящего PPM…

msv

Не надейтесь… 😃 Последовательно шимы появляются пожалуй только в простейших декодерах на сдвиговом регистре…

Probelzaelo

Я не надеюсь на столько уж тупо, но что то мне подсказывает что периодичность поступления PWM должна быть синхронна с прилетевшим PPM для серв и спидконтроллеров это вполне приемлемо, какой смысл молотить туда импульсы чаще?.
Сознайтесь плиз кто нибудь из тех, кто туда наступал осциллером … а то будет как в прошлый раз …

Bare
Probelzaelo:

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

А если используется например FASST или DSM ? Там могут прилетать данные по каналам в одном пакете, соответственно и выставляться на приемнике могут почти одновременно.

Probelzaelo
Bare:

А если используется например FASST или DSM ? Там могут прилетать данные по каналам в одном пакете, соответственно и выставляться на приемнике могут почти одновременно.

Могут, и наверняка будут… в принципе для программера было бы даже проще загрузить нужное число (период повторения) в единственный таймер, одновременно запустить сразу все импульсы и потом по очереди ставить константы = длительности импульса и снимать их ровняясь на цифровой компаратор, по этому таймеру … а потом точно так же повторять внутри основного цикла после каждого обнуления таймера…
отдельно следить за входящим PPM и по мере получения “новостей” корректировать константы по каналам …

AndyBig

Я так думаю, что в контроллере приемника узел декодирования PPM и узел формирования ШИМ - разные, и синхронизация между ними совершенно необязательна и даже почти невозможна. Поэтому и вполне могут быть расхождения.
ШИМ может быть с разной частотой - для аналоговых, для цифровых серв… Не подстраиваться же под это передатчику 😃 Ну и опять же - частоту ШИМ формирует приемник, а частоту PPM - передатчик, так что синхронизации здесь и не может быть.

Probelzaelo

Это зачем же им разные узлы делать в одном приемнике? если и одно и второе достаточно не сложные задачи для практически любого микроконтроллера, просто два программных блока очевидно работающих совершенно независимо друг от друга… и главное что у контроллера даже самого слабого и дешевенького при этом будет еще полным-полно времени на выполнение каких либо других, не менее полезных задач… а синхронизация действительно не очень то и нужна, но по логике вещей было бы очень даже полезно чтобы два устройства, приемник и передатчик были по максимуму согласованы и работали что называется в унисон, тем более что одна из задач по любому получается синхронизированной… а вторую, без разницы, я практически уверен что у разных производителей решения разные, у кого то синхронизированно у кого то нет, а у кого то МК ведет себя точно так же как

msv:

в простейших декодерах на сдвиговом регистре

просто МК делает сразу все что нужно и плюс еще немного но в одном корпусе, в отличии от просто сдв.регистра. а один корпус это снижение и массы и цены и сложности и увеличение надежности всего изделия.

Tester500

Формализуем ТЗ, пока до Канта не дошло 😃

  1. нужно оцифровать сигналы PWM, которые поступают с канальных выводов приемника (PPM)
  2. Оцифрованный канал должен иметь …-бит (указать битность)
  3. На выходе имеем переменные, содержащие пропорционально длительности сигнала число.
  4. Использовать не более одного таймера и одного прерывания (видимо проще от этого таймера)

Такое ТЗ пойдет?

msv
  1. Точность определения длины импульса не хуже … мкс.
    (Разрядность можно сделать хоть long, но это не обещает некоторую гарантированную точность…)
Tester500

Длина импульса 2.1 мс, минимум 0.8 мс
Кто что еще подскажет?

Bare

Нашел один пример, написан на Си, но не под ардуино, поэтому я до конца его понять не могу. Хочется сделать библиотеку под ардуино.
приведу куски кода ( ниже приложу весь архив)
#define RX_ROLL REGISTER_BIT(PIND,1)
#define RX_PITCH REGISTER_BIT(PIND,2) // INT0
#define RX_COLL REGISTER_BIT(PIND,3) // INT1
#define RX_YAW REGISTER_BIT(PINB,7)
#define RX_ROLL_DIR REGISTER_BIT(DDRD,1)
#define RX_PITCH_DIR REGISTER_BIT(DDRD,2)
#define RX_COLL_DIR REGISTER_BIT(DDRD,3)
#define RX_YAW_DIR REGISTER_BIT(DDRB,7)

// RX_ROLL
ISR(PCINT2_vect)
{
if ( RX_ROLL ) // rising
{
RxChannel1Start = TCNT1;

} else { // falling
RxChannelsUpdatingFlag = 1;
RxChannel1 = TCNT1 - RxChannel1Start;
RxChannelsUpdatingFlag = 0;
}
}

// RX_PITCH
ISR(INT0_vect)
{
if (RX_PITCH)
{
RxChannel2Start = TCNT1;

} else { // falling
RxChannelsUpdatingFlag = 1;
RxChannel2 = TCNT1 - RxChannel2Start;
RxChannelsUpdatingFlag = 0;
}
}

// RX_COLL
ISR(INT1_vect)
{
if (RX_COLL)
{
RxChannel3Start = TCNT1;

} else { // falling
RxChannelsUpdatingFlag = 1;
RxChannel3 = TCNT1 - RxChannel3Start;
RxChannelsUpdatingFlag = 0;
}
}

// RX_YAW
ISR(PCINT0_vect)
{
if ( RX_YAW ) // rising
{
RxChannel4Start = TCNT1;

} else { // falling
RxChannelsUpdatingFlag = 1;
RxChannel4 = TCNT1 - RxChannel4Start;
RxChannelsUpdatingFlag = 0;
}
}

----------------------------

// pin change interrupt enables
PCICR |= (1 << PCIE0); // PCINT0…7
PCICR |= (1 << PCIE2); // PCINT16…23

// pin change masks
PCMSK0 |= (1 << PCINT7); // PB7
PCMSK2 |= (1 << PCINT17); // PD1
// external interrupts
EICRA = (1 << ISC00) | (1 << ISC10); // Any change INT0, INT1
EIMSK = (1 << INT0) | (1 << INT1); // External Interrupt Mask Register
EIFR |= (1 << INTF0) | (1 << INTF1);

// timer0 (8bit) - run @ 8MHz
// used to control ESC/servo pulse length
TCCR0A = 0; // normal operation
TCCR0B = (1 << CS00); // clk/0
TIMSK0 = 0; // no interrupts

// timer1 (16bit) - run @ 1Mhz
// used to measure Rx Signals & control ESC/servo output rate
TCCR1A = 0;
TCCR1B = (1 << CS11);

// timer2 8bit - run @ 8MHz / 1024 = 7812.5KHz
// and Stick-Arming
TCCR2A = 0;
TCCR2B = (1 << CS22) | (1 << CS21) | (1 << CS20); // /1024
TIMSK2 = 0;
TIFR2 = 0;
TCNT2 = 0; // reset counter

В другом проекте, автор читает так:
Вешает функцию на прерывание attachInterrupt(0, interruptHandler, RISING);
А это основная часть из interruptHandler
#define TIMEOUT 2500
#define XMITFACTOR_ADR 12
#define THROTTLEPIN 4
#define ROLLPIN 2
#define PITCHPIN 3
#define YAWPIN 6
#define GEARPIN 7
#define AUXPIN 5

roll = pulseIn(ROLLPIN, HIGH, TIMEOUT);
aux = pulseIn(AUXPIN, HIGH, TIMEOUT);
gear = pulseIn(GEARPIN, HIGH, TIMEOUT);
pitch = pulseIn(PITCHPIN, HIGH, TIMEOUT);
throttle = pulseIn(THROTTLEPIN, HIGH, TIMEOUT);
yaw = pulseIn(YAWPIN, HIGH, TIMEOUT);

Мне этот код не нравится по нескольким причинам:

  1. Слишком много времени тратится на таймауты в pulsein, если нет сигнала на большинстве каналов.
  2. Если нет сигнала на первом канале, то и остальные читаться не будут.

XXcontroller_KR_v1_5.zip

Tester500

Моя мыслЯ такая:
Допустим нужно оцифровать сигнал с 8-битной точностью. Настраиваем таймер так, чтобы он выдавал прерывания каждую (2мс-1мс)/256. 2мс общая длина импульса, 1мс длительность сигнала до начала отсчета. Далее по прерыванию опрашиваем нужные порты-каналы. По изменению уровней (асинхронно для каждого канала) производим математические вычисления длины. Код изображу позже, счас уже думать нечем.
PS: оверхед будет не большим, думаю займется не более 4% общего процессорного времени.

AndyBig
Probelzaelo:

Это зачем же им разные узлы делать в одном приемнике? если и одно и второе достаточно не сложные задачи для практически любого микроконтроллера, просто два программных блока очевидно работающих совершенно независимо друг от друга…

Я об этом и говорил. Узлы - я имел в виду функциональные узлы (программные блоки, подпрограммы - называйте как больше нравится) в одном контроллере 😃

Probelzaelo:

было бы очень даже полезно чтобы два устройства, приемник и передатчик были по максимуму согласованы

Для чего? Какой смысл в этом? Передатчик должен гнать пакеты с достаточной для плавного управления частотой, а приемник должен выдавать ШИМ с частотой, требуемой сервами. Нафига синхронизация между ними? 😃

Bare
Tester500:

Моя мыслЯ такая: Допустим нужно оцифровать сигнал с 8-битной точностью. Настраиваем таймер так, чтобы он выдавал прерывания каждую (2мс-1мс)/256. 2мс общая длина импульса, 1мс длительность сигнала до начала отсчета. Далее по прерыванию опрашиваем нужные порты-каналы. По изменению уровней (асинхронно для каждого канала) производим математические вычисления длины. Код изображу позже, счас уже думать нечем.

Если еще одновременно использовать библиотеку servo, то timer1 и timer3 заняты. Остается только timer2.

Bare

В прошлом сообщение написал ерунду.
Сейчас нашел в интернете
Документация по таймеру на ардуино
sites.google.com/…/tajmery-sceetciki-arduino
Документация по прерываниям на ардуино
…google.com/…/ispolzovanie-preryvanij-arduino

Пытаюсь понять исходники библиотеки servo, немного не хватает знаний. Не пойму какие таймеры использует эта библиотека?

V_Alex
Tester500:

Моя мыслЯ такая:
Допустим нужно оцифровать сигнал с 8-битной точностью…

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

Boroda

Давайте рассуждать логически, с колокольни разработчика приёмника. Кристалл с 8-12-ю аппаратными ШИМ генераторами на борту будет слишком дорогой и громоздкий для данного применения. Для четырёхканального приёмника городить раздельный шим тоже не имеет смысла, мало кто будет втыкать в него дорогущие цифровые сервы. С точки зрения программера наиболее логичный вариант - крутить однобитовый шаблон в выходном порту по прерыванию таймера. По сути - тот же сдвиговый регистр, только программный. Во всех встречавшихся мне цифровых FM примниках так и было, так я делал сам, и так работают DSM2 оранжики. Могу проверить ещё турнигу ради интереса.

Можно считать, что большинство примников не выдают одновременно сервосигналы на нескольких ногах, и лишь некоторые дорогие многоканальные модели способны на это. С принятием такого ограничения на полную совместимость задачка многоканального захвата значительно упрощается. Выходы примника подаются на порты ввода меги и на базы транзисторов. Их коллекторы совершенно в штатном режиме соединяются с ногой ICP1 по схеме “монтажного ИЛИ-НЕ”, и включается аппаратный захват TCNT1 по ICR. В процедуре прерывания анализируется уровень на портах, и вычисляется длительность именно того сервоимпульса, завершение которого вызвало прерывание.

V_Alex

В варианте “программного сдвигового регистра” возникает ограничение на количество каналов приемника. В старых РРМ приемниках наличие синхропаузы ограничивало канальность 7-8 каналами для такта до 25 мс (40 Гц). Если сэкономленную синхропаузу задействовать под дополнительные каналы, можно добавить еще пару-тройку каналов. В такт 20 мс (50 Гц) можно воткнуть по-хорошему 10 “сдвиговых” каналов по 2 мс. В то-же время генерация пачкой, несколькими пачками или с наложением канальных импульсов такое ограничение снимает. Для сервы главное, чтобы генерируемый импульс был “съедобным”.

msv
Boroda:

Давайте рассуждать логически

Если бы не было других реал-тайм задач (как минимум прием и анализ PPM посылки), c логикой и выводами может и можно согласиться… А в реале в этом случае слишком велика вероятность оказаться в одном прерывании при срочной необходимости обрабатывать другое. Я бы, со своей колокольни, никогда бы так не делал (и не делаю…).
Да и вообще о чем речь? Если делать для себя любимого, можно из приемника вытащить исходный PPM (если он там есть…). Если в глобальном масштабе- по любому надо быть готовым, что декодер приемника не обязан выдавать канальные PWM с определенным, нам удобным, фазовым сдвигом…

V_Alex:

В такт 20 мс (50 Гц) можно воткнуть по-хорошему 10 “сдвиговых” каналов по 2 мс.

А как тогда делать синхронизацию?..

V_Alex
msv:

Если в глобальном масштабе- по любому надо быть готовым, что декодер приемника не обязан выдавать канальные PWM с определенным, нам удобным, фазовым сдвигом…

Именно так. Единого стандарта нет и, скорее всего, уже не будет никогда.
Да и РРМа в приемнике скорее всего не обнаружится. Входит цифра, проверяется и сразу перегоняется в цифру.

Tester500
V_Alex:

Для работы с сервой 8 бит мало, тем более, что оцифровываться будет цифровой сигнал, но как аналоговый, джиттер гарантирован.

8бит для примера, оно хоть 16 можно. Вот далее каша, что значит “оцифровываться будет цифровой сигнал”? Когда PPM успел стать цифровым? Каналы приемника демультиплексированный PPM, оно так задумывалось задолго до первого микроконтроллера, и работало в полном аналоге. Джиттера не будет, откуда он там.
И зачем знать алгоритм прохождение и прочее, если достаточно в ТЗ предусмотреть самый сложный асинхронный режим. Решение задачи от этого не пострадает.

Bare:

Сейчас нашел в интернете
Документация по таймеру на ардуино

Блин, для winavr у меня все есть, пришлось качать для ардуины, счас сижу и разбираюсь в ихних стандартах. Оно там сишное, только свои обозначения переменных (ноги,таймеры, и т.д.).
PS: вообще подобные вещи лучше затачивать сразу под некое подобие RTOS, легче будет добавлять функционал, при ограниченных аппаратных ресурсах.

Bare:

Пытаюсь понять исходники библиотеки servo, немного не хватает знаний.

Помогу, в этой библиотеке:
Servo - Class for manipulating servo motors connected to Arduino pins.
Это для управления сервоприводами, которые висят на ногах контроллера.