Как принять сигнал с приемника в AVR (PWM|Digital)
Библиотечки/примеры и тп есть, я уже копал, просто мне интересно было чем то из этих пользовался или нет, потому и спрашивал.
Ну я тоже гуглил, и много чего успел пересмотреть и прочитать, там главное концепцию понять - а дальше чужой код не всегда хорошо 😃
Кстати я тоже заметил, что буржуи намного больше кода публикуют на форумах, чем у нас… У нас если прогер написал 10 строк - то уже горд собой и ни с кем делиться не хочет, за исключением небольшого количества нормальных людей.
Да у них есть где подсмотреть…
я больше с серво-прокси смотрел, там хороший вариант захвата ппм с определением длины сигнала, но там тоже нет фильтрации помех, а я заметил что помех принимает и отдает приемник не мало, так что надо фильтровать все что меньше 0,9мс…
Я хочу собрать на arduino небольшой девайс для контроля бензинового двигателя. Мне надо считывать с приемника один канал(PWM) постоянно, и параллельно с пары датчиков. Не знаю как лучше читать с приемника, может покажите свой кусок кода по чтению сигнала с приемника?
А можно больше информации? Дело в том, что с приемника PWM не идет… идет PPM и в начале темы есть мой код считывания.
Если вам надо получить данные от двух или более датчиков - то надо их опрашивать по очереди. если они не умеют сами постить данные по какому-нибудь протоколу (I2C,1wire,2wire)
Ясен, перец, что в чужих кодах только редкие мысли…
А можно больше информации? Дело в том, что с приемника PWM не идет… идет PPM и в начале темы есть мой код считывания. Если вам надо получить данные от двух или более датчиков - то надо их опрашивать по очереди. если они не умеют сами постить данные по какому-нибудь протоколу (I2C,1wire,2wire)
Я наверно не правильно выразился. Я считал что когда идет сразу несколько каналов с одной шины ( например тренерский разъем на передатчике) это PPM, а если снимается конкретный канал с разъема приемника (куда подключается серва), то там PWM. К ардуине я буду подключать 2 сервы и два канала с приемника, полюс датчик холла. С датчика холла я хочу считать импульсы через счетчик повешенный на прерывание. Еще одно прерывание буду использовать для таймера. Вот и кончились внешние прерывания. В связи с этим, негде взять еще 2 прерывания, для двух каналов, для чтения сигнала с приемника. Смотрел исходники AeroQuad, там вроде они читают сразу с четырех или более каналов , параллельно меняя вектора прерывания. Ном там все так сильно завязано на другой код и куча классов, что я не смог разобраться.
А можно больше информации? Дело в том, что с приемника PWM не идет… идет PPM и в начале темы есть мой код считывания.
Ну у кого что приемником называется. Если это приемник на выходе которого готовые каналы (модуль приемника с N-каналами), то в каждом канале PWM.
Если это кусок электронной схемы на входе которой антенна, а на выходе не демультиплексированный на каналы низкочастотный сигнал, то это PPM (или PCM)
Вот кусок кода AeroQuad (Receive), читают и ppm и pwm. А вот как от туда выдернуть только нужное, я не знаю.
Ну вроде примеров для оцифровки PPM хватает. Есть проекты PPM-> USB (для симуляторов), вот простой:
github.com/thomaspfeifer/PPM2USB/archives/master
Ну вроде примеров для оцифровки PPM хватает. Есть проекты PPM-> USB (для симуляторов), вот простой:
github.com/thomaspfeifer/PPM2USB/archives/master
Мне как раз PPM и не надо, мне надо считать 3 канала с конечных выходов приемника(куда подключаются сервы). И желательно используя только одно прерывание.
мне надо считать 3 канала с конечных выходов приемника(куда подключаются сервы). И желательно используя только одно прерывание.
не вопрос, заводите их, каждую на свою ногу и одновременно через диод типа кд521/522 на одно, общее прерывание… (получится этакая схема 3ИЛИ), при вызове прерывания читайте состояние всех трех ног, по какой из них произошел вызов легко сами определитесь 😉
Но есть подозрение что не очень красиво это будет работать… ибо там, на каналах, PWM. а значит практически одновременно может быть сигнал. Надеюсь что каналы ШИМят все же по очереди, и в том же порядке как это было принято из входящего PPM…
Не надейтесь… 😃 Последовательно шимы появляются пожалуй только в простейших декодерах на сдвиговом регистре…
Я не надеюсь на столько уж тупо, но что то мне подсказывает что периодичность поступления PWM должна быть синхронна с прилетевшим PPM для серв и спидконтроллеров это вполне приемлемо, какой смысл молотить туда импульсы чаще?.
Сознайтесь плиз кто нибудь из тех, кто туда наступал осциллером … а то будет как в прошлый раз …
Я не надеюсь на столько уж тупо, но что то мне подсказывает что периодичность поступления PWM должна быть синхронна с прилетевшим PPM для серв и спидконтроллеров это вполне приемлемо, какой смысл молотить туда импульсы чаще?.
А если используется например FASST или DSM ? Там могут прилетать данные по каналам в одном пакете, соответственно и выставляться на приемнике могут почти одновременно.
А если используется например FASST или DSM ? Там могут прилетать данные по каналам в одном пакете, соответственно и выставляться на приемнике могут почти одновременно.
Могут, и наверняка будут… в принципе для программера было бы даже проще загрузить нужное число (период повторения) в единственный таймер, одновременно запустить сразу все импульсы и потом по очереди ставить константы = длительности импульса и снимать их ровняясь на цифровой компаратор, по этому таймеру … а потом точно так же повторять внутри основного цикла после каждого обнуления таймера…
отдельно следить за входящим PPM и по мере получения “новостей” корректировать константы по каналам …
Я так думаю, что в контроллере приемника узел декодирования PPM и узел формирования ШИМ - разные, и синхронизация между ними совершенно необязательна и даже почти невозможна. Поэтому и вполне могут быть расхождения.
ШИМ может быть с разной частотой - для аналоговых, для цифровых серв… Не подстраиваться же под это передатчику 😃 Ну и опять же - частоту ШИМ формирует приемник, а частоту PPM - передатчик, так что синхронизации здесь и не может быть.
Это зачем же им разные узлы делать в одном приемнике? если и одно и второе достаточно не сложные задачи для практически любого микроконтроллера, просто два программных блока очевидно работающих совершенно независимо друг от друга… и главное что у контроллера даже самого слабого и дешевенького при этом будет еще полным-полно времени на выполнение каких либо других, не менее полезных задач… а синхронизация действительно не очень то и нужна, но по логике вещей было бы очень даже полезно чтобы два устройства, приемник и передатчик были по максимуму согласованы и работали что называется в унисон, тем более что одна из задач по любому получается синхронизированной… а вторую, без разницы, я практически уверен что у разных производителей решения разные, у кого то синхронизированно у кого то нет, а у кого то МК ведет себя точно так же как
в простейших декодерах на сдвиговом регистре
просто МК делает сразу все что нужно и плюс еще немного но в одном корпусе, в отличии от просто сдв.регистра. а один корпус это снижение и массы и цены и сложности и увеличение надежности всего изделия.
Формализуем ТЗ, пока до Канта не дошло 😃
- нужно оцифровать сигналы PWM, которые поступают с канальных выводов приемника (PPM)
- Оцифрованный канал должен иметь …-бит (указать битность)
- На выходе имеем переменные, содержащие пропорционально длительности сигнала число.
- Использовать не более одного таймера и одного прерывания (видимо проще от этого таймера)
Такое ТЗ пойдет?
- Точность определения длины импульса не хуже … мкс.
(Разрядность можно сделать хоть long, но это не обещает некоторую гарантированную точность…)
Длина импульса 2.1 мс, минимум 0.8 мс
Кто что еще подскажет?
Нашел один пример, написан на Си, но не под ардуино, поэтому я до конца его понять не могу. Хочется сделать библиотеку под ардуино.
приведу куски кода ( ниже приложу весь архив)
#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);
Мне этот код не нравится по нескольким причинам:
- Слишком много времени тратится на таймауты в pulsein, если нет сигнала на большинстве каналов.
- Если нет сигнала на первом канале, то и остальные читаться не будут.
Моя мыслЯ такая:
Допустим нужно оцифровать сигнал с 8-битной точностью. Настраиваем таймер так, чтобы он выдавал прерывания каждую (2мс-1мс)/256. 2мс общая длина импульса, 1мс длительность сигнала до начала отсчета. Далее по прерыванию опрашиваем нужные порты-каналы. По изменению уровней (асинхронно для каждого канала) производим математические вычисления длины. Код изображу позже, счас уже думать нечем.
PS: оверхед будет не большим, думаю займется не более 4% общего процессорного времени.
Это зачем же им разные узлы делать в одном приемнике? если и одно и второе достаточно не сложные задачи для практически любого микроконтроллера, просто два программных блока очевидно работающих совершенно независимо друг от друга…
Я об этом и говорил. Узлы - я имел в виду функциональные узлы (программные блоки, подпрограммы - называйте как больше нравится) в одном контроллере 😃
было бы очень даже полезно чтобы два устройства, приемник и передатчик были по максимуму согласованы
Для чего? Какой смысл в этом? Передатчик должен гнать пакеты с достаточной для плавного управления частотой, а приемник должен выдавать ШИМ с частотой, требуемой сервами. Нафига синхронизация между ними? 😃
Моя мыслЯ такая: Допустим нужно оцифровать сигнал с 8-битной точностью. Настраиваем таймер так, чтобы он выдавал прерывания каждую (2мс-1мс)/256. 2мс общая длина импульса, 1мс длительность сигнала до начала отсчета. Далее по прерыванию опрашиваем нужные порты-каналы. По изменению уровней (асинхронно для каждого канала) производим математические вычисления длины. Код изображу позже, счас уже думать нечем.
Если еще одновременно использовать библиотеку servo, то timer1 и timer3 заняты. Остается только timer2.