Использование 4 16-битных таймеров для 400 Гц ШИМ

Я имею дело с мега квадрокоптером Arduino и пытаюсь сделать частоту ШИМ для 4 двигателей — 400 Гц каждый.
Я нашел интересное решение, в котором 4 16-битных таймера ATmega2560 используются для управления 4 ESC с помощью ШИМ, чтобы он мог достигать частоты 400 Гц. От 700 до 2000 мкс — нормальная ширина импульса, с которой имеет дело ESC.

1 с / REFRESH_INTERVAL = 1 / 0,0025 = 400 Гц.

this is servo.h lib:
#define MIN_PULSE_WIDTH       700     // the shortest pulse sent to a servo
#define MAX_PULSE_WIDTH      2000     // the longest pulse sent to a servo
#define DEFAULT_PULSE_WIDTH  1000     // default pulse width when servo is attached
#define REFRESH_INTERVAL     2500     // minimum time to refresh servos in microseconds

#define SERVOS_PER_TIMER       1     // the maximum number of servos controlled by one timer
#define MAX_SERVOS   (_Nbr_16timers  * SERVOS_PER_TIMER)

Проблема состоит в том, чтобы заставить его работать, каждый ШИМ должен управляться с 1 16-битным таймером. В противном случае, скажем, 2 escs на 1 таймер даст 200 Гц. Таким образом, все 16-битные таймеры заняты управлением 4 ESC, но мне все еще нужно прочитать входную PPM от приемника. Для этого мне нужен как минимум еще один 16-битный таймер, которого у меня больше нет.
Это все еще один свободный 8-битный таймер, он может читать только цифры 0..255, в то время как обычные числа escs работают с 1000..2000 и так далее.

Так что произойдет, если я буду использовать один и тот же 16-битный таймер для чтения как pwm, так и ppm? Будет ли это работать? Будет ли это резко снизить скорость?
У меня есть arduino, работающий в паре с Raspberry Pi, который управляет фильтрацией данных, отладкой и прочим, лучше ли перенести чтение ppm в Raspberry?

0

Решение

Чтобы ответить на один из ваших вопросов:

Так что же произойдет, если я буду использовать один и тот же 16-битный таймер для PWM и PPM
чтение? Будет ли это работать?

Да. Когда срабатывает прерывание смены булавки, вы можете просто читать текущее значение TCNT, чтобы узнать, сколько времени прошло с момента последнего. Это никоим образом не повлияет на работу аппаратного ШИМ таймера.

Будет ли это резко снизить скорость?

Нет. ШИМ выполняется специальным оборудованием, программные операции, работающие в то же время, не влияют на его скорость, равно как и любые ISR, которые вы могли активировать для соответствующего таймера. Следовательно, вы можете позволить таймеру сгенерировать ШИМ по своему усмотрению и при этом использовать его для а) считывания с него текущего значения счетчика и б) привязки к нему выходного сравнения и / или переполнения ISR для создания расширенного программным таймером.

Отредактируйте в ответ на ваш комментарий:

Обратите внимание, что фактическим значением в регистре TCNT является текущий счетчик таймера (тика) в любой момент, независимо от того, активен ли ШИМ или нет. Кроме того, прерывание Timer OVerflow (TOV) можно использовать в любом режиме. Эти два свойства позволяют создать программно-расширенный таймер для произвольных других задач измерения времени с помощью следующих шагов:

  1. Установите и активируйте прерывание по переполнению таймера для таймера / счетчика, который вы хотите использовать. В ISR вы просто увеличиваете глобальную переменную (volatile!) (timer1OvfCount например), который эффективно подсчитывает переполнения таймера и таким образом расширяет фактический диапазон таймера. Текущий абсолютный счетчик тиков может быть рассчитан как timer1OvfCount * topTimerValue + TCNTx,
  2. Когда происходит событие, например, передний фронт на одном штыре, в процедуре обработки (например, ISR смены штыря) вы считываете текущее значение таймера / коутера (TCNT) а также timer1OvfCount и сохранить эти значения в другой глобальной переменной (например, startTimestamp), эффективно начиная ваше измерение времени.
  3. Когда происходит второе событие, например спадающий фронт на одном штырьке, в процедуре обработки (например, ISR смены штыря) вы читаете текущее значение таймера / коутера (TCNT) а также timer1OvfCount, Теперь у вас есть отметка времени начала сигнала в startTimestamp и отметка времени окончания сигнала в другой переменной. Разница между этими двумя временными метками заключается именно в продолжительности импульса, который вы ищете.

Однако следует учитывать два момента:

  1. При использовании фазово-корректных режимов ШИМ таймер будет последовательно переключаться между счетом вверх и вниз. Это делает поиск фактического числа пропущенных тактов с момента последнего прерывания TOV немного более сложным.
  2. Может быть условие гонки между одним фрагментом кода, сначала читающим TCNT, а затем читающим timer1OvfCountи ТОВ ИЗР. Этому можно противостоять, отключая прерывания, затем читая TCNT, затем читая timer1OvfCountи затем проверка флага прерывания TOV; если флаг установлен, есть ожидающие, необработанные прерывания переполнения -> включить прерывания и повторить.

Тем не менее, я уверен, что есть несколько библиотечных функций для поддержки программно-расширенных таймеров / счетчиков, которые выполняют всю обработку таймера за вас.

2

Другие решения

что такое единица измерения 700 и 2000? Я полагаю, что вы используете. Вы не очень-то объяснили в своем вопросе, но я определил, что вам нужны импульсы длительностью 25 мсек, при которых 700 мксек на время могут быть 0 градусов, а 2000 — на 180 градусов, теперь импульсный вход каждого сервопривода можно подключить к любому GPIO AVR. И эти GPIO предоставляют сигнал ШИМ серво. Так что, я думаю, вы даже можете управлять всеми этими двигателями только за один раз. С этим кодом:

Предположим, у вас есть таймер, который генерирует Inturrupt на каждые 50 месяцев.
Теперь, если вы хотите 700 мксек для мотора 1800 мцек для мотора 2 900 мцек для мотора 3 & 1000 мксек для двигателя 4, тогда просто сделайте это:

#define CYCLE_PERIOD 500 // for 25 msec = 50 usec * 500

unsigned short motor1=14;  // 700usec = 50x14
unsigned short motor2=16;  // 800usec
unsigned short motor3=18;  // 900usec
unsigned short motor4=20;  // 1000usec

unsigned char motor1_high_flag=1;
unsigned char motor2_high_flag=1;
unsigned char motor3_high_flag=1;
unsigned char motor4_high_flag=1;

PA.0 = 1; // IO for motor1
PA.1 = 1; // IO for motor2
PA.2 = 1; // IO for motor3
PA.3 = 1; // IO for motor4

void timer_inturrupt_at_50usec()
{
motor1--;motor2--;motor3--;motor4--;
if(!motor1)
{
if(motor1_high_flag)
{
motor1_high_flag = 0;
PA.0 = 0;
motor1 = CYCLE_PERIOD - motor1;
}
if(!motor1_high_flag)
{
motor1_high_flag = 1;
PA.0 = 1;
motor1 = 14;    // this one is dummy;if you want to change duty time update this in main
}
}

if(!motor2)
{
if(motor2_high_flag)
{
motor2_high_flag = 0;
PA.1 = 0;
motor2 = CYCLE_PERIOD - motor2;
}
if(!motor2_high_flag)
{
motor2_high_flag = 1;
PA.1 = 1;
motor2 = 16;
}
}if(!motor3)
{
if(motor3_high_flag)
{
motor3_high_flag = 0;
PA.2 = 0;
motor3 = CYCLE_PERIOD - motor3;
}
if(!motor3_high_flag)
{
motor3_high_flag = 1;
PA.2 = 1;
motor3 = 18;
}
}

if(!motor4)
{
if(motor4_high_flag)
{
motor4_high_flag = 0;
PA.3 = 0;
motor4 = CYCLE_PERIOD - motor4;
}
if(!motor4_high_flag)
{
motor4_high_flag = 1;
PA.3 = 1;
motor4 = 19;
}
}
}

& скажи мне, что такое ESC?

0

По вопросам рекламы [email protected]