Пам'ятка по декодуванню pwm і ppm сигналу, записки програміста

Типова радіоапаратура працює якось так. Є передавач (власне, сама аппа) і приймач. Приймач з передавачем спілкуються по якомусь своєму протоколу, часто закритому. Приймач декодує цей протокол і передає положення ручок на апаратурі далі, наприклад, польотним контролера (ПК) квадрокоптера. ПК і приймач спілкуються за своїм протоколом, який повинні розуміти обидва. У цьому місці великою популярністю користуються PWM і PPM. Є й інші варіанти, зокрема, SBUS, DSM2 і DSX, але в рамках даної статті ми розглянемо тільки PWM і PPM.

PPM (Pulse Position Modulation) - це така спроба запхати багато PWM сигналів в один провід, ну або, принаймні, мені особисто подобається про нього так думати. PPM завжди передає короткі імпульси. Паузи між фронтами імпульсів відповідають тривалості одного PWM сигналу. Сигнали передаються послідовно, спочатку значення на першому каналі, потім на другому, і так далі. Відповідність між PPM і PWM сигналами добре відображає наступна осциллограмма, записана на Rigol DS1054Z:

Пам'ятка по декодуванню pwm і ppm сигналу, записки програміста

Осцилограма знята з приймача RadioLink R8EF, що має режим, при якому на два його Піна подаються сигнали PPM і SBUS, а на інші Піни - PWM сигнал для каналів з 3 по 8. Тому PWM сигнали для каналів 1 і 2 цієї статті не показані. Тут можна бачити кілька цікавих особливостей. По-перше, даний конкретний приймач використовує для логічної одиниці 3.3 В, хоча і живиться від 5 В. По-друге, в ньому PPM сигнал инвертирован в порівнянні з тим, як його зазвичай малюють на картинках (наприклад, в цьому тред). Як не дивно, все написане вище про фронти імпульсів при цьому залишається вірним. Зокрема, на зображенні курсором виділені фронти, відповідні PWM сигналу на 3 каналі. При цьому PPM сигнал виявляється зміщеним щодо PWM сигналів, але взагалі-то приймач і не зобов'язаний був їх якось синхронізувати.

PPM працює добре до тих пір, поки ви не намагаєтеся засунути в нього більше 8 каналів. Якщо каналів більше, то які сигнали починають оновлюватися із затримкою більше 20 мс (як у випадку з PWM), або доводиться «стискати» сигнали, втрачаючи їх точність, або використовувати додаткові дроти. Особисто я не бачив, щоб по PPM передавали більше 8 каналів.

Добре, тепер припустимо, що я хочу керувати своїм роботом за допомогою радіоапаратури. Ось відповідний код для PWM:

#include
#include

#define NCHANNELS 8
#define CH1_PIN 5 // CH2_PIN = CH1_PIN + 1, etc

volatile int pwm_value # 91; NCHANNELS # 93; = # 123; 0 # 125; ;
volatile int prev_time # 91; NCHANNELS # 93; = # 123; 0 # 125; ;

void rising # 40; # 41; ;

void falling # 40; # 41; # 123;
uint8_t pin = PCintPort. arduinoPin;
PCintPort. attachInterrupt # 40; pin, rising, RISING # 41; ;
pwm_value # 91; pin - CH1_PIN # 93; = micros # 40; # 41; - prev_time # 91; pin - CH1_PIN # 93; ;
# 125;

void rising # 40; # 41;
# 123;
uint8_t pin = PCintPort. arduinoPin;
PCintPort. attachInterrupt # 40; pin, falling, FALLING # 41; ;
prev_time # 91; pin - CH1_PIN # 93; = micros # 40; # 41; ;
# 125;

void setup # 40; # 41; # 123;
for # 40; uint8_t i = 0; i pinMode # 40; CH1_PIN + i, INPUT_PULLUP # 41; ;
PCintPort. attachInterrupt # 40; CH1_PIN + i, rising, RISING # 41; ;
# 125;

Serial. begin # 40; 9600 # 41; ;
# 125;

void loop # 40; # 41; # 123;
for # 40; uint8_t i = 0; i Serial. println # 40; "Ch" + String # 40; i + 1 # 41; + "=" + String # 40; pwm_value # 91; i # 93; # 41; # 41; ;
Serial. println # 40; "----------------" # 41; ;
delay # 40; 1000 # 41; ;
# 125;

А це - код для PPM:

#include
#include

#define PPM_PIN 6
#define MAX_CHANNELS 12

volatile int pwm_value # 91; MAX_CHANNELS # 93; = # 123; 0 # 125; ;
volatile int prev_time = 0;
volatile int curr_channel = 0;

volatile bool overflow = false;

void rising # 40; # 41;
# 123;
int tstamp = micros # 40; # 41; ;

/ * Overflow should never acutally happen, but who knows. * /
if # 40; curr_channel pwm_value # 91; curr_channel # 93; = Tstamp - prev_time;
if # 40; pwm_value # 91; curr_channel # 93;> 2100 # 41; # 123; / * It's actually a sync * /
pwm_value # 91; curr_channel # 93; = 0;
curr_channel = 0;
# 125; else
curr_channel ++;
# 125; else
overflow = true;

prev_time = tstamp;
# 125;

void setup # 40; # 41; # 123;
pinMode # 40; PPM_PIN, INPUT_PULLUP # 41; ;
PCintPort. attachInterrupt # 40; PPM_PIN, rising, RISING # 41; ;

Serial. begin # 40; 9600 # 41; ;
# 125;

void loop # 40; # 41; # 123;
for # 40; uint8_t i = 0; i Serial. println # 40; "Ch" + String # 40; i + 1 # 41; + "=" + String # 40; pwm_value # 91; i # 93; # 41; # 41; ;
if # 40; overflow # 41;
Serial. println # 40; "OVERFLOW!" # 41; ;
Serial. println # 40; "----------------" # 41; ;
delay # 40; 1000 # 41; ;
# 125;

Код був перевірений на радіоапаратурі RadioLink T8FB і приймачі до неї R8EF. На наступному фото приймач, перекладений в режим PPM, підключений до Arduino Nano з залитої в неї прошивкою для PPM:

Пам'ятка по декодуванню pwm і ppm сигналу, записки програміста

Приклад отладочного виведення по UART:

Призначення використаної вище бібліотеки PinChangeInt має бути зрозуміло за кодом. Вона дозволяє вішати переривання на передній і задній фронти (наростання і спад) сигналу на заданих пінах. Подробиці про цю бібліотеці можна знайти тут і тут. Повна версія коду прошивок для декодування PWM і PPM сигналів доступна на GitHub.

Збройні отриманими тут знаннями, ми можемо не тільки керувати Arduino за допомогою радіоапаратури, а й, наприклад, паяти перекодувальники PWM в / з PPM (якщо не хочеться платити за готові на AliExpress), або навіть виготовляти власні радіоапаратури на базі якого-небудь NRF24L01 або іншого радиомодуля. Це просто перше що мені особисто спало на думку.

А які шалені ідеї для творчості є у вас?

Сподобався пост? Поділися з іншими:

(Необхідно включити JS)

Схожі статті