Помогите разобраться с arduino Nano и PPM

Vorona
Offline
Зарегистрирован: 06.07.2015

Доброго времени суток, господа.

Сказу оговорюсь, с ардуино совсем "на вы". Есть задача:

На входе PPM сигнал(https://en.wikipedia.org/wiki/Pulse-position_modulation) 8 каналов.

Необходимо получить на выходе PPM сигнал, но поменять местами 2 канала.

Есть код, который, впринципе, с этой задачей справился, но есть одно но. Значения каналов на выходе колеблются в пределах 0,1 мсек, при неизменном входном сигнале. Во время отладки

платформа Arduino nano 328p

Прошу помощи в этом вопросе. Заранее благодарен.

код:

 





//this programm will put out a PPM signal

//////////////////////CONFIGURATION///////////////////////////////
#define chanel_number 8  //set the number of chanels
#define default_servo_value 0  //set the default servo value
#define PPM_FrLen 22500  //set the PPM frame length in microseconds (1ms = 1000µs)
#define PPM_PulseLen 300  //set the pulse length
#define onState 0  //set polarity of the pulses: 1 is positive, 0 is negative
#define sigPin 10  //set PPM signal output pin on the arduino
//////////////////////////////////////////////////////////////////

/*this array holds the servo values for the ppm signal
 change theese values in your code (usually servo values move between 1000 and 2000)*/
int *ppm[chanel_number];
int ppm_in[chanel_number];

void setup(){  
  //initiallize default ppm values
  for(int i=0; i<chanel_number; i++){
    ppm[i] = &ppm_in[i];
    *ppm[i]= default_servo_value;
  }
  ppm[0] = &ppm_in[1];
  ppm[1] = &ppm_in[0];

  pinMode(sigPin, OUTPUT);
  digitalWrite(sigPin, !onState);  //set the PPM signal pin to the default state (off)
  pinMode(2, INPUT); //Pin 3 as input
  
  cli();
  TCCR1A = 0; // set entire TCCR1 register to 0
  TCCR1B = 0;
  
  OCR1A = 100;  // compare match register, change this
  TCCR1B |= (1 << WGM12);  // turn on CTC mode
  TCCR1B |= (1 << CS11);  // 8 prescaler: 0,5 microseconds at 16mhz
  TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
  sei();
}

void loop(){
  while(pulseIn(2, LOW) < 5000){
  } //Wait for the beginning of the frame
  for(int x=0; x<=chanel_number-1; x++)//Loop to store all the channel position
  {
    ppm_in[x]=pulseIn(2, LOW)+400;
  }
 }

ISR(TIMER1_COMPA_vect){  //leave this alone
  static boolean state = true;
  
  TCNT1 = 0;
  
  if(state) {  //start pulse
    digitalWrite(sigPin, onState);
    OCR1A = PPM_PulseLen * 2;
    state = false;
  }
  else{  //end pulse and calculate when to start the next pulse
    static byte cur_chan_numb;
    static unsigned int calc_rest;
  
    digitalWrite(sigPin, !onState);
    state = true;

    if(cur_chan_numb >= chanel_number){
      cur_chan_numb = 0;
      calc_rest = calc_rest + PPM_PulseLen;// 
      OCR1A = (PPM_FrLen - calc_rest) * 2;
      calc_rest = 0;
    }
    else{
      OCR1A = (*ppm[cur_chan_numb] - PPM_PulseLen) * 2;
      calc_rest = calc_rest + *ppm[cur_chan_numb];
      cur_chan_numb++;
    }     
  }
}

 

Vorona
Offline
Зарегистрирован: 06.07.2015

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

Puhlyaviy
Puhlyaviy аватар
Offline
Зарегистрирован: 22.05.2013

Поменять местами 2 проводка?
Писец... скоро будут спрашивать как в розетку вилку втыкать?

Vorona
Offline
Зарегистрирован: 06.07.2015

полётный контроллер соединяется с приёмником 3-мя проводками 5v, gnd и ppm. Там нет этих 2-х проводков.

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

На аппаратуре ( передатчике) разве нельзя в микшерах поменять каналы?
Или это что-то жёстко забито и не программируется?
Если оба сервы- переключить....
Если джойстики - перекинуть их каналы

Sloper
Sloper аватар
Offline
Зарегистрирован: 30.03.2015

trembo пишет:

На аппаратуре ( передатчике) разве нельзя в микшерах поменять каналы?
Или это что-то жёстко забито и не программируется?
Если оба сервы- переключить....
Если джойстики - перекинуть их каналы

Вот вы пля все умные!!! 

Человек не зря спрашивает. Нихуа там не поменяешь. Не во всех аппах каналы можно переназначить.

Скорее всег овпрост по совместимости Futaba/JR или что то типа. Порядок каналов другой. Газ с высотой перепутаны.  

Перекинуть провода джойстиков - не вариант. Пробовал. На газ нет экспоненты например, и еще есть програмные привязки, триммеры опять же. Там много интересного. Проще в дуне перекинуть порядок каналов. 

Или купить Турнигу 9XR за 50 доларов, она и в JR и Futabе выдает PPM и каналы любые на любые настраиваются.

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

er9x, и это правильно!

Vorona
Offline
Зарегистрирован: 06.07.2015

Sloper пишет:

trembo пишет:

На аппаратуре ( передатчике) разве нельзя в микшерах поменять каналы?
Или это что-то жёстко забито и не программируется?
Если оба сервы- переключить....
Если джойстики - перекинуть их каналы

Вот вы пля все умные!!! 

Человек не зря спрашивает. Нихуа там не поменяешь. Не во всех аппах каналы можно переназначить.

Скорее всег овпрост по совместимости Futaba/JR или что то типа. Порядок каналов другой. Газ с высотой перепутаны.  

Перекинуть провода джойстиков - не вариант. Пробовал. На газ нет экспоненты например, и еще есть програмные привязки, триммеры опять же. Там много интересного. Проще в дуне перекинуть порядок каналов. 

Или купить Турнигу 9XR за 50 доларов, она и в JR и Futabе выдает PPM и каналы любые на любые настраиваются.

Ни прибавить, ни отнять. элеватор с элеронами. RDS8000 мать её. И менять не хочу - эту уже затюнил, но вот чехарда с каналами всё испортила.

Vorona
Offline
Зарегистрирован: 06.07.2015

Немного разобрался. Но проблема осталась. При использовании pulseIn появляется большая погрешность. Духи в онлайне говорят, что тут для точности лучше использовать таймер. Действительно, нашел рабочий пример, который выдаёт погрешность в 1 наносекунду, что приемлимо. Но вот незадача. на ардуино нано, который я использую, 3 таймера 0й и 2й 8-битный и 1 16 битный. Для моей задачи требуется 2 таймера 16 бит. Как можно выти из данной ситуации? Или посоветуйте платформу с 2 16-битными таймерами, которая по размеру не больше нано.

Vorona
Offline
Зарегистрирован: 06.07.2015

Пока что приходит на ум только запользовать 2 нано - один читает и передаёт по TTL на другой, а второй генерит сигнал. Но на мой взгляд - это очень большой костыль. Я точно знаю, есть решенее элегантнее.

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

Я бы сделал так, попробую на словах:

У нас приходит 8 импульсов. Период их известен и пауза между пачками тоже.
16-ти битовым таймером достаточно точно измеряем длительности каждого импульса  и складываем в массив.
В массиве меняем что  нам нужно местами и во время когда должна прийти вторая пачка воспроизводим нашу.
Минус всего этого- мы пропускаем одну пачку.
Хотя не думаю что это так смертельно, ведь пальчики управляющие джойстиками работают намного медленнее

Дэфалтные настройки ППМ от Турниги с прошивкой er9x, скриншот программы eepe:

Вот подробнее про эти настройки:

Ещё:  http://www.fpv-hero.de/Images/PPM1.jpg

Vorona
Offline
Зарегистрирован: 06.07.2015

trembo пишет:

Я бы сделал так, попробую на словах:

У нас приходит 8 импульсов. Период их известен и пауза между пачками тоже.
16-ти битовым таймером достаточно точно измеряем длительности каждого импульса  и складываем в массив.
В массиве меняем что  нам нужно местами и во время когда должна прийти вторая пачка воспроизводим нашу.
Минус всего этого- мы пропускаем одну пачку.
Хотя не думаю что это так смертельно, ведь пальчики управляющие джойстиками работают намного медленнее

 

Благодарю за ответ. Сегодня попробую сделать смешнее. Я попробую использовать все три таймера. При этом для генерации 16 битный, а для считывания 2 8 битных, при этом сделать на одном делитель 1024, а на другом 8. И использовать их как старший и младший байт. То есть попытаться сделать из 2х 8 битных таймеров 1 16 битный.

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

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

Будет задержка на один пакет. То есть около 40 миллисекунд.
Один таймер16  пишет в массив, другой  8 читает и воспроизводит.

Vorona
Offline
Зарегистрирован: 06.07.2015

Так проблема-то в том, что 8 бит не хватает, чтоб адекватно хранить задержку с высокой точностью. То есть 8 бит оперирует числами 0-256, чего не хватает для хранения 900-2500. Поэтому и хочу пользовать 2 8 битных. А чтение/запись вообще хочу делать ассинхронно, то есть есть массив. По прерыванию int0(изменения ноги 2) туда впихиваются значения таймера, при этом по прерыванию второго таймера они пишутся в выход. То есть я loop() задействую только для отладки. В этом случае задержка может варьироваться от 0 до 22 мс.

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

Сосчитать таймером до 256 4 раза будет 1024 ( примерно) 10 раз -   2560
Фактически нужно просто подсчитывать моменты его переполнения.
А по кругу он зациклится сам.
Ну и потом добавить немного сколько надо , хотя, конечно, на перезапись таймера возможно  время неучтённое потратится.

Недавно вроде подобное было на форуме в виде функции.

Vorona
Offline
Зарегистрирован: 06.07.2015

Хм... тоже интересно, попробую сегодня, спасибо!

Vorona
Offline
Зарегистрирован: 06.07.2015

Всем спасибо за комментарии и советы! Долго и упоротоупорно пытался завести на ардуино нано. Ничего из выше предложенного не сработало :) Поступил проще - заказал ардуино про мини на атмеге 32u4, где есть 2 таймера по 16 бит. И вгрузил в него вот что:





/*This program puts the servo values into an array,
 reagrdless of channel number, polarity, ppm frame length, etc...
 You can even change these while scanning!*/

#define PPM_Pin 3  //this must be 2 or 3

#define chanel_number 8  //set the number of chanels
#define default_servo_value 0  //set the default servo value
#define PPM_FrLen 22500  //set the PPM frame length in microseconds (1ms = 1000µs)
#define PPM_PulseLen 300  //set the pulse length
#define onState 1  //set polarity of the pulses: 1 is positive, 0 is negative
#define sigPin 10  //set PPM signal output pin on the arduino

int ppm[16];  //array for storing up to 16 servo signals

int *ppm_out[chanel_number];

void setup()
{
  
  //initiallize default ppm values
  for(int i=0; i<chanel_number; i++){
    ppm[i]= default_servo_value;
    ppm_out[i] = &ppm[i];
  }
  ppm_out[0] = &ppm[1];
  ppm_out[1] = &ppm[0];
  
//  Serial.begin(115200);
//  Serial.println("ready");

  pinMode(sigPin, OUTPUT);
  digitalWrite(sigPin, !onState);  //set the PPM signal pin to the default state (off)

  pinMode(PPM_Pin, INPUT);

  cli();
  
  attachInterrupt(PPM_Pin - 2, read_ppm, CHANGE);  
  
  TCCR1A = 0; // set entire TCCR1 register to 0
  TCCR1B = 0;
  
  OCR1A = 100;  // compare match register, change this
  TCCR1B |= (1 << WGM12);  // turn on CTC mode
  TCCR1B |= (1 << CS11);  // 8 prescaler: 0,5 microseconds at 16mhz
  TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
  
  TCCR3A = 0;  //reset timer1
  TCCR3B = 0;
  TCCR3B |= (1 << CS11);  //set timer1 to increment every 0,5 us
  sei();
}

void loop()
{
//  //You can delete everithing inside loop() and put your own code here
//  int count;
//
//  while(ppm[count] != 0){  //print out the servo values
//    Serial.print(ppm[count]);
//    Serial.print("  ");
//    count++;
//  }
//  Serial.println("");
//  delay(100);  //you can even use delays!!!
}



void read_ppm(){  //leave this alone
  static unsigned int pulse;
  static unsigned long counter;
  static byte channel;

  counter = TCNT3;
  TCNT3 = 0;

  if(counter < 1020){  //must be a pulse if less than 510us
    pulse = counter;
  }
  else if(counter > 3820){  //sync pulses over 1910us
    channel = 0;
  }
  else{  //servo values between 510us and 2420us will end up here
    ppm[channel] = (counter + pulse)/2;
    channel++;
  }
}

ISR(TIMER1_COMPA_vect){  //leave this alone
  static boolean state = true;
  
  TCNT1 = 0;
  
  if(state) {  //start pulse
    digitalWrite(sigPin, onState);
    OCR1A = PPM_PulseLen * 2;
    state = false;
  }
  else{  //end pulse and calculate when to start the next pulse
    static byte cur_chan_numb;
    static unsigned int calc_rest;
  
    digitalWrite(sigPin, !onState);
    state = true;

    if(cur_chan_numb >= chanel_number){
      cur_chan_numb = 0;
      calc_rest = calc_rest + PPM_PulseLen;// 
      OCR1A = (PPM_FrLen - calc_rest) * 2;
      calc_rest = 0;
    }
    else{
      OCR1A = (*ppm_out[cur_chan_numb] - PPM_PulseLen) * 2;
      calc_rest = calc_rest + *ppm_out[cur_chan_numb];
      cur_chan_numb++;
    }     
  }
}

Всё взлетело и работает - каналы подменяются!