Прерывания на Arduino Due (переход с Mega 2560)

Eismeer
Eismeer аватар
Offline
Зарегистрирован: 21.08.2015

Здраствуйте, делаю проект пульта ДУ для FPV самолета из компьютерного джойстика (defender cobra m5) - считывание 5 аналоговых каналов, масштабирование, тримирование, инверсия, экспонента и прочее..., а далее формирование РРМ сигнала прерыванием.

Столкнулся с нехваткой производительности mega2560, если только считывать аналоговые выходы, масштабировать и формировать РРМ сигнал то все отлично, если выполнять остальные функции то цикл loop, насколько я понимаю, не успевает выполниться за цикл РРМ (22,5мс) - получаю один правильный пакет РРМ и 5 пустых.. 

Решил сильно не упрощать код (наверное зря) и перейть на Due, вот только не знаю как в ней сформировать РРМ сигнал прерываниями. Внизу выкладываю рабочий скетч формирования РРМ для mega2560:

 
unsigned char outPinPPM = 12;    // PPM output pin (do not change - pin 10 tied to ISR)
int pulseMid = 1200;
int PPM_array[9];
int PPMFreq_uS = 22500;          // PPM frame length total in uS
int Fixed_uS = 300;              // PPM frame padding LOW phase in uS
const int InvertPPM = 0; 
 
void setup() {
 
  // Setup Digital I/O
  pinMode(outPinPPM, OUTPUT);   // sets as output
  
  // Initialize PPM channel array
  char i=0;
  for(i=0; i<8; i++) { PPM_array[i] = pulseMid; }
  PPM_array[i] = -1;   // Mark end
 
  // Initialise ISR Timer 1 - PPM generation
  TCCR1A = B00110001;  // Compare register B used in mode 3 -Установка выходной линии OC1B (на линии высокий уровень) и Работа таймера/счетчика1 в 8-разрядном ШИМ режиме
  TCCR1B = B00010010;  // WGM13 & CS11 set to 1 -? и деление частоты на 8
  TCCR1C = B00000000;  // All set to 0 -принудительно устанавливают значение на выводах OC1A и OC1B =0
  TIMSK1 = B00000010;  // Interrupt on compare B -OCIE1A разрешают прерывания при совпадении с А и B
  TIFR1  = B00000010;  // Interrupt on compare B -OCF1A устанавливаются в 1 при совпадение с A, B
  OCR1A = PPMFreq_uS;  // PPM frequency -регистр сравнения A (22500)
  OCR1B = Fixed_uS;    // PPM off time (lo padding) -регистр сравнения В (300)
  if (InvertPPM == 1) TCCR1A = B00100001;
 }
void loop() { // Main loop
int PPM1=1200;
int PPM2=1200;
int PPM3=1200;
int PPM4=1200;
int PPM5=1200;
int PPM6=1200;
int PPM7=1200;
int PPM8=1200;
 
 
   PPM_array[0] = PPM1 + Fixed_uS;
   PPM_array[1] = PPM2 + Fixed_uS;
   PPM_array[2] = PPM3 + Fixed_uS;
   PPM_array[3] = PPM4 + Fixed_uS;
   PPM_array[4] = PPM5 + Fixed_uS;
   PPM_array[5] = PPM6 + Fixed_uS;
   PPM_array[6] = PPM7 + Fixed_uS;
   PPM_array[7] = PPM8 + Fixed_uS;
}
 
 
 
 
int ACC_PPM_length = 0;          // Pulse length current total, used to calculate sync pulse
int *PPM_pointer = PPM_array;
int PPM_len;
 
// *********************** TIMER 1 **************************
ISR(TIMER1_COMPA_vect) { //-Совпадение A таймера-счётчика 1 и  Регистр TCNT1 принял значение, равное регистру OCR1A
  PPM_len = *(PPM_pointer++);
  if(PPM_len > -1) {
    OCR1A = PPM_len;                        // Set pulse length
    ACC_PPM_length += PPM_len;              // Add pulse length to accumulator
  } else {
    PPM_pointer = PPM_array;                // Reset table position pointer
    OCR1A = PPMFreq_uS - ACC_PPM_length;    // Calculate final sync pulse length
    ACC_PPM_length = 0;                     // Reset accumulator
  }
}
Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

Eismeer пишет:

Решил сильно не упрощать код (наверное зря) и перейть на Due, вот только не знаю как в ней сформировать РРМ сигнал прерываниями. Внизу выкладываю рабочий скетч формирования РРМ для mega2560:

Для начала надо взять даташит на микроконтроллер и читать про аппаратные таймеры, прерывания, их регистры.

Eismeer
Eismeer аватар
Offline
Зарегистрирован: 21.08.2015

Не имею профильного образования в этой области. Поэтому когда делал РРМ на mega2560 взял несколько проектов и построчно их расписал, изучил кучу литературы (как по мне на 2560 ее больше, чем на ARM) и только после этого смог выправить эти проекты под себя. Некоторые моменты по ARM я почитал, боюсь не осилю в ближайшей перспективе. Поэтому и задал вопрос, возможно кто-то уже с этим сталкивался.

MagicianT
Offline
Зарегистрирован: 03.10.2015

И теперь вы предлагаете всем делать то же самое с вашим кодом? Построчно расписывать? Я там них. не могу понять, какие 9 каналов? Почему таймер перезаписыватся, есть же библиотеки для серво моторов, они как раз и формируют PPM. Тот который 50 Гц и 900-2200 микросекунд пульсы?

Eismeer
Eismeer аватар
Offline
Зарегистрирован: 21.08.2015

1. int PPM_array[9] да там должна быть цифра 8

2. есть библиотеки чтобы считать PPM сигнал и сформировать PWM для серв

3. в loop в полной версии кода у меня производится считывание 8 аналоговых каналов масштабирование считаных значений в диапазон 700-1700 (длина импульса РРМ) потом я прибавляю к этому импульсу межканальный интервал 300 мс (в реальном коде я масштабирую в диапазон 1000-2000 чтобы не делать лишних вычислений)

4.Генератор РРМ первые 300мс импульса РРМ находится в "1", потом 700-1700мс в "0" - после значение счетчика перезаписывается и формируется следующие 7 импульсов

5.OCR1A = PPMFreq_uS - ACC_PPM_length - тут вычисляется значение защитного интревала (22500 - PPM_array[i]*8) - чтобы длина пакета РРМ всегда была одинаковой

Eismeer
Eismeer аватар
Offline
Зарегистрирован: 21.08.2015

 6.  if (InvertPPM == 1) TCCR1A = B00100001 - инвертирует РРМ сигнал (межимпульсный интервал становится "1", испульс РРМ "0")

MagicianT
Offline
Зарегистрирован: 03.10.2015
Тут в постах 10 и 18 говорят что код проверено работает.
Если хотите написать сами, советую скачать  библиотеку DueTimer-master и распотрошить/изучить внутренности.
 
Eismeer
Eismeer аватар
Offline
Зарегистрирован: 21.08.2015

спасибо большое

Eismeer
Eismeer аватар
Offline
Зарегистрирован: 21.08.2015

Подправил код генератора PPM для Due, может кому пригодится

uint32_t periods[]={42000,42000,42000,42000,42000,42000,42000,42000,42000}; // timer is clocked at 42MHz, so 42 ticks per us
uint32_t num_periods=8+1;      // number of channels +1

int ppm_channels[8];
long Frame;
long Sum;
const int InvertPPM = 0; 

void TC0_Handler()
{
   long dummy=REG_TC0_SR0;    // vital - reading this clears some flag
                              // otherwise you get infinite interrupts
   static int i=0;
   REG_TC0_RC0=periods[i++];
   if (i>=num_periods)i=0;
}

void setup(){
 pinMode(2,OUTPUT);           // port B pin 25  
 analogWrite(2,255);          // sets up some other registers I haven't worked out yet
 REG_PIOB_PDR  = 1<<25;       // disable PIO, enable peripheral
 REG_PIOB_ABSR = 1<<25;       // select peripheral B
 REG_TC0_WPMR  = 0x54494D00;  // enable write to registers
 REG_TC0_CMR0  = 0b00000000000010011100010000000000; // set channel mode register (see datasheet)
 if (InvertPPM == 1) REG_TC0_CMR0  = 0b00000000000001101100010000000000; // alternative CMR for inverted output
 REG_TC0_RC0   = 100000000;   // counter period
 REG_TC0_CCR0  = 0b101;       // start counter
 REG_TC0_IER0  = 0b00010000;  // enable interrupt on counter = rc
 REG_TC0_IDR0  = 0b11101111;  // disable other interrupts
 REG_TC0_RA0   = 12600; // Pulse lenght    0.3*1000*42=0.3ms
 Frame         = 945000; // ppm frame lenght 22.5*1000*42=22.5ms  
 NVIC_EnableIRQ(TC0_IRQn);    // enable TC0 interrupts
}

void loop(){
// 1. PPM channels
 ppm_channels[0]= 511;          // channel 1 from 0 to 1023
 ppm_channels[1]= 511;          // channel 2 from 0 to 1023
 ppm_channels[2]= 511;          // channel 3 from 0 to 1023
 ppm_channels[3]= 511;          // channel 4 from 0 to 1023
 ppm_channels[4]= 511;          // channel 5 from 0 to 1023
 ppm_channels[5]= 511;          // channel 6 from 0 to 1023
 ppm_channels[6]= 511;          // channel 7 from 0 to 1023
 ppm_channels[7]= 511;          // channel 8 from 0 to 1023
 
 // 2. Calculate the 8 channels
 Sum = 0;
 for (int i = 0; i < 8; i++)  {
   periods[i] = map(ppm_channels[i], 0, 1023, 42000, 84000);
   Sum = Sum + periods[i]; 
 }
 // 3. Calculate the sync frame
 periods[8] = Frame - Sum;
}