Входной Режим захвата PPM сигнала
Приветствую всех участников Форума. Я хотел бы выразить свою глубочайшую благодарность в связи с названием этой должности. На прошлой неделе я пытаюсь захватить и декодировать сигнал PPM от RC-передатчика. Хотя я видел много различных подходов и кодов в Интернете, используя такие методы, как режим захвата ввода и изменение контакта, я хотел бы обратиться за помощью в моем подходе. Большинство кодов, которые я нашел в Интернете, представляют собой сбои и нестабильности при попытке прочитать время между восходящими импульсами. Я попытался прочитать и понять, как настроить входные регистры захвата на чипе Atmega 32u4, он же Arduino Pro Micro / Lepnardo, но я не могу найти правильный способ определения регистров, а также функции ISR (_vec). В следующих нескольких строках я попытаюсь объяснить код, который я написал до сих пор, но мне понадобится некоторая помощь в отношении инициализации регистров. Пожалуйста, оголитесь со мной, так как любая помощь будет очень ценна. Без лишних слов позвольте представить вам следующие строки.
#define STICK_CENTER 1500
#define STICK_HALFWAY 500
#define THRESHOLD 15
#define MIN_PULSE_WIDTH (STICK_CENTER - STICK_HALFWAY - 15)
#define MAX_PULSE_WIDTH (STICK_CENTER + STICK_HALFWAY + 15)
#define RC_CHANNELS_COUNT 8
#define PPM_CAPTURE_PIN 4
#define NEWFRAME_PULSE_WIDTH 2000
#define TIMER_COUNT_DIVIDER 2
void rc_setup_ppm() {
setupPins();
initTimer();
}
void setupPins(void) {
// Set up the Input Capture pin
pinMode(PPM_CAPTURE_PIN, INPUT);
digitalWrite(PPM_CAPTURE_PIN, 1); // enable the pullup
}
void initTimer(void) {
cli();
TIFR = (1 << ICF1); //Clear any Interrupt flags
PRR |= (0 << PRTIM1); //Enable Timer1
TCCR1A = 0; //Clear TCCR1A
TCCR1B = 0; //Clear TCCR1B
TCCR1C = 0; //Clear TCCR1C
TCCR1B = (1 << ICNC1) | (1 << ICES1) | (1 << WGM13) | (1 << WGM12) | (0 << CS12) | (1 << CS11) | (0 << CS10);
TIMSK1 |= (1 << ICIE1); //Enable input capture
TIFR1 = (1 << ICF1); //Clear interrupt flag
sei();
}
uint16_t adjust(uint16_t diff, uint8_t chan) {
return diff / TIMER_COUNT_DIVIDER;
}
ISR(TIMER1_CAPT_vect) {
union twoBytes {
uint16_t word;
uint8_t byte[2];
} timeValue;
uint16_t now, diff;
static uint16_t last = 0;
static uint8_t chan = 0;
timeValue.byte[0] = ICR1L; // grab captured timer value (low byte)
timeValue.byte[1] = ICR1H; // grab captured timer value (high byte)
now = timeValue.word;
diff = now - last;
last = now;
//all numbers are microseconds multiplied by TIMER_COUNT_DIVIDER (as prescaler is set to 1/8 of 16 MHz)
if (diff > (NEWFRAME_PULSE_WIDTH * TIMER_COUNT_DIVIDER)) {
chan = 0; // new data frame detected, start again
}
else {
if (diff > (MIN_PULSE_WIDTH * TIMER_COUNT_DIVIDER - THRESHOLD)
&& diff < (MAX_PULSE_WIDTH * TIMER_COUNT_DIVIDER + THRESHOLD)
&& chan < RC_CHANNELS_COUNT)
{
rc_values[chan] = adjust(diff, chan); //store detected value
}
chan++; //no value detected within expected range, move to next channel
}
}
Учитывая вышеизложенное, я хотел бы помочь установить процедуру инициализации регистров. Немного информации о моем сигнале:
PPM включает в себя 8 различных каналов в качестве суммированного сигнала. Всего 9 импульсов, при этом каждый из них, имеет временную длительность от 998 до 2012 микросек.
Временные рамки каждого импульса легко изменить с помощью настроек передатчика. Также может быть изменен общий период кадра. Под первым я подразумеваю МАКСИМАЛЬНОЕ время каждого импульса, а под вторым-общий период кадра после сканирования 8 каналов. Учитывая это, временные периоды составляют 26,5 милл и 350 микросекунд.
Хватит с них. Возвращаясь к фактическому коду, вы увидите функции cli(); и sei ();. Действительно ли я должен устанавливать их во время этой части кода? Кроме того, нужно ли мне сбрасывать или удалять какие-либо активные флаги? Должен ли я включать какие-либо флаги (например, флаг переполнения после того, как счетчик достиг своего пикового значения)?
В этот момент я также хотел бы отметить, что я использую прескалер 1/8, что приводит к тиковому периоду примерно 0,5 микросекунд. Кроме того, я улавливаю только восходящую часть импульсов. И я использую входной канализатор, но, учитывая спецификации данных, я видел, что кадр отмены имеет 4 периода. Есть ли какой-либо способ использовать схему фильтрации с низкой задержкой?
Пожалуйста, потратьте некоторое время, чтобы прочитать этот пост, так как я уверен, что любые ответы помогут другим тоже прояснить эти темы. Я знаю, что я включил много разных аспектов, и мне жаль это.
Большое спасибо, Кутсук Феодосий
@k. theodosis, 👍1
Обсуждение1 ответ
Действительно ли я должен устанавливать их во время этой части кода?
- Нет, не знаешь. Однако я бы изменил порядок последних инициализаций:
Обновите
TIFR1
прямо передTIMSK1
, чтобы избежать немедленного срабатывания прерывания.Установите
TCR1B
последним, чтобы таймер запускался только тогда, когда он был полностью настроен.
Кроме того, нужно ли мне сбрасывать или удалять какие-либо активные флаги?
AFAIK ядро Arduino не использует прерывания таймера 1. Вы можете взять
под контроль все биты в регистрах (например, TIMSK1 = (1 << ICIE1);
)
вместо только некоторых выбранных битов. Хотя это не должно иметь значения
.
Должен ли я включать какие-либо флаги (например, флаг переполнения после того, как счетчик достиг своего пикового значения)?
Не беспокойтесь о переливах. Вычисление diff = now - last
выполняется по модулю 216 и, в силу правил модульной
арифметики, дает правильный результат даже при переполнении.
И я использую подавитель входного сигнала, но, учитывая технические характеристики таблицы данных, я видел, что кадр отмены имеет длину 4 периода.
Это создает задержку в “четыре системных такта”, или 0,25 мкс. Неужели вас действительно волнует такая маленькая задержка? Обратите внимание, что, поскольку все ребра страдают одинаковой задержкой, это не влияет на расчетные различия.
А теперь, если позволите, пара замечаний по поводу вашего кода:
pinMode(PPM_CAPTURE_PIN, INPUT);
digitalWrite(PPM_CAPTURE_PIN, 1); // включить подтягивание
Это можно упростить и сделать более явным:
pinMode(PPM_CAPTURE_PIN, INPUT_PULLUP);
Затем,
union twoBytes {
uint16_t word;
uint8_t byte[2];
} timeValue;
timeValue.byte[0] = ICR1L; // grab captured timer value (low byte)
timeValue.byte[1] = ICR1H; // grab captured timer value (high byte)
now = timeValue.word;
Когда-то давно это был способ чтения 16-битных регистров. Так как уже много лет компилятор C знает, как это сделать, вам просто нужно выдать:
now = ICR1;
Здесь содержится большая часть того, что я писал, так что я просто собираюсь проголосовать за это., @timemage
PRR |= (0 << PRTIM1);
->PRR0 &= ~(1 << PRTIM1);
, @timemage
TCR1C = 0;
не делает ничего полезного., @timemage
Дорогой Эдгар, маг времени, большое спасибо за ваши ответы. Особенно тому, от Точильщика, который включал в себя все, что я просил. Просто поражен простотой и тем, как вы передали всю информацию. Ваше здоровье!, @k. theodosis
- Использование INT2/3 на Arduino Micro (Atmega32u4)
- Прерывание переполнения Timer0 не работает
- Использование millis() и micros() внутри процедуры прерывания
- Arduino непрерывно считывает значение АЦП с помощью прерывания
- Использование TIMER0_COMPB_vect
- Программирование Arduino Micro через RX/TX
- 4-битный счетчик вверх и вниз
- TCCR1A и TCCR2A на Леонардо
У меня такое чувство, что ты собираешься вытащить кролика из шляпы или вызвать нас на дуэль или что-то в этом роде. Если говорить серьезно, то вам, вероятно, повезет больше, если вы это сократите., @timemage
Извините за содержание моего поста. Я хотел бы обратиться за советами только к инициализации режима захвата ввода. Только регистры и как ими пользоваться., @k. theodosis
Вероятно, я мог бы написать что-нибудь о том, как настроить захват входных данных. Но в своем вопросе вы упоминаете, что пытаетесь сделать это с помощью **8** разные сигналы. Только у 32u4 есть **2** входные контакты захвата. Если я правильно понимаю, для вас это будет проблемой., @timemage
Дорогой маг времени, спасибо за ответ. Это было что-то, что я написал неправильно, так что, пожалуйста, простите меня. Я имел в виду, что сигнал ppm содержит 8 различных сигналов (всего 9 импульсов)., @k. theodosis
Если "после того, как 8 *каналов* были отсканированы" означает, что после "после 8 *импульсов*..." вы можете [отредактировать свой вопрос](https://arduino.stackexchange.com/posts/81610/edit) сказать это. Разъяснения к вопросам, задаваемым здесь, обычно предполагаются отредактированными в самом вопросе., @timemage
Еще раз большое спасибо, я изменил его и включил в него то, что происходит на самом деле., @k. theodosis