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

Tester500
eliash:

А можно больше информации? Дело в том, что с приемника PWM не идет… идет PPM и в начале темы есть мой код считывания.

Ну у кого что приемником называется. Если это приемник на выходе которого готовые каналы (модуль приемника с N-каналами), то в каждом канале PWM.
Если это кусок электронной схемы на входе которой антенна, а на выходе не демультиплексированный на каналы низкочастотный сигнал, то это PPM (или PCM)

Bare

Вот кусок кода AeroQuad (Receive), читают и ppm и pwm. А вот как от туда выдернуть только нужное, я не знаю.

Receiver.zip

Bare
Tester500:

Ну вроде примеров для оцифровки PPM хватает. Есть проекты PPM-> USB (для симуляторов), вот простой:
github.com/thomaspfeifer/PPM2USB/archives/master

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

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. В процедуре прерывания анализируется уровень на портах, и вычисляется длительность именно того сервоимпульса, завершение которого вызвало прерывание.