Generate PPM



  • Hi all, I would like to send a PPM to this transmitter to control gimbal axes (PAN,TILT,ROL) with M5Stack gyro remotely.
    the only sketch I found to generate PPM is for Arduino and use timer interrupt

    //https://forum.arduino.cc/index.php/topic,163199.msg1220724.html#msg1220724
    
    // Started 18 Apr 2013 - the loco chassis is more or less ready for testing.
    //
    // This is a test program to drive a loco on R/C channel 1 based on the position of a potentiometer.
    //
    // The centre connection of the pot is connected to pin A2
    // The control signal from the Arduino to the transmitter is on Pin D7.
    //
    // I intend that parts of this program will be used in the computer controlled version.
    //
    // The R/C receiver is programmed to treat the centre value (128?) as off with 0 and 255 representing
    //    full forward and full reverse. Some experimentation will probably be necessary to determine the
    //    correct 0 point.
    // The transmitter can receive a sequence of pulses for 2 to 7 channels. For this program pulses will be
    //    prepared for 7 channels with channels 2 to 7 being values that correspond to centre off.
    //    The receiver would not work with only 3 channels - it needs at least 4..
    //
    // The Transmitter expects a sequence of pulses each of whose width (duration) conveys the
    //   speed setting. The pulse lengths must vary between 1.0 and 2.0 millisecs with 1.5ms representing
    //   centre off. Each pulse must be separated by a short pulse of 0.3ms and there must be a long pulse
    //   to separate the groups of pulses. Ideally the group of pulses should repeat every 22ms
    //   So, 2.0 + 0.3ms for 7 pulses takes 16.1ms thus requiring a separating pulse of 5.9ms.
    //
    // The pulses are generated by timer1 (16 bit) interrupts and to simpify matters when the computer is
    //    controlling things the numbers needed to produce the sequence of pulse lengths will be stored
    //    in an array. At each interrupt the next value in the array is used to set the duration for the
    //    following interrupt. For this program the values in the array position corresponding to channel 3
    //    will be derived from the potentiometer position. (The Rx seems to be programmed to drive the motor
    //      on ch3).
    //
    // Timer1 is used because the longer periods afforded by the 16bits are needed. The prescaler is set
    //    to divide the CPU clock by 64 giving a timer clock of 250khz or 4 microsecs (4us).
    //    On that basis 0.3ms requires a delay of 75 clock steps
    //                  1.0                      250
    //                  1.5                      375
    //                  2.0                      500
    //                  5.9                     1475
    //                 15.1                     3775
    //
    // This program only communicates with the PC for program testing.
    
    // Data variables
    unsigned int ppm[16]; // the array of values for the timer1 interrupt timings
    unsigned int potVal; // value read from potemtiometer
    int curPpm = 0; // the position in the array of timings
    byte curPpmMax; // the actual number of timer steps
    
    char pulsePin = 7; // the digital pin for sending pulses to the transmitter
    char debugPin = 13; // something to watch to check things are working
    char potPin = 2; // the analog pin for the potentiometer
    
    byte loopCount = 0; // a crude substitute for delay()
    int analogIn; // for reading potentiometer position
    
    
    boolean testing = true;
    
    void setup() {
    
      if (testing) {
        Serial.begin(9600);
        Serial.println("Starting TrainRadioPot");
      }
    
      // set the pin directions
      pinMode(pulsePin, OUTPUT);
      //  pinMode(debugPin, OUTPUT);
    
      // set the timing values
      ppm[0] = 1475; //3775; // long pulse - see notes above
      ppm[1] = 75; // short dividing pulse
      ppm[2] = 305; // loco1 ch1
      ppm[3] = 75; // short
      ppm[4] = 305; // loco2 ch2
      ppm[5] = 75; // short
      ppm[6] = 305; // loco3 ch3
      ppm[7] = 75; // short
      ppm[8] = 305; // loco4 ch4
      ppm[9] = 75; // short
      ppm[10] = 305; // loco5 ch5
      ppm[11] = 75; // short
      ppm[12] = 305; // loco6 ch6
      ppm[13] = 75; // short
      ppm[14] = 305; // loco7 ch7
      ppm[15] = 75; // short
    
      curPpmMax = 16; // the number of timer values
      curPpm = 0; // the starting position in the array of timings
    
      // setup and start timer interrupts
      // ppm is achieved by sending different delays to the timer interrupt
      noInterrupts();
      TCCR1A = 0;     // set entire TCCR1A register to 0
      TCCR1B = 0;     // same for TCCR1B
      // set compare match register to desired timer count:
      OCR1A = 1000; // It works by causing an interrupt when TCNT2 counts up to this number
      // This number will be set from the ppm array when the program is running
      digitalWrite(pulsePin, 0); // start with pin low
      // turn on CTC mode: Clear Timer on Compare
      bitSet(TCCR1B, WGM12);
      // Set CS10 and CS11 bits for 64 prescaler: 10, 12 = 1024
      bitSet(TCCR1B, CS10);
      bitSet(TCCR1B, CS11);
      bitClear(TCCR1B, CS12);
      // enable timer compare A interrupt:
      bitSet(TIMSK1, OCIE1A);
      interrupts();
    
    }
    
    void loop() {
    
      if (loopCount == 1) {
        analogIn = analogRead(potPin);
        // the reading must be converted from the range 0 - 1023 to the range 250 - 500
        //   which means dividing by 4 and adding 180 to give 305 at the centre
        potVal = (analogIn >> 2) + 180;
        //		if (potVal > 302 && potVal < 308) {
        if (potVal > 280 && potVal < 320) {
          potVal = 305; // to create an off space without jitter
        }
        //	ppm[2] = potVal;
        //  ppm[8] = potVal;// } CH4 ROL STABE
        // ppm[4] = potVal; //   CH2 PAN STABE YAW
        ppm[6] = potVal; //    CH3 TILT STABE PICH
    
      }
    
    
      //	loopCount = 0; // for testing - so analog read is ignored
      loopCount = loopCount + 1; // it will roll over to zero automatically
      // the purpose of loopCount is so that the pot isn't read every loop
    }
    
    
    // interrupt routine that is triggered when the timer counts up to the preset value
    ISR(TIMER1_COMPA_vect) {
      noInterrupts();
      digitalWrite(pulsePin, !digitalRead(pulsePin)); // change the state of the pin
      OCR1A = ppm[curPpm]; // set the value for the next time interval
      curPpm = ((curPpm + 1) % curPpmMax); // move the index on
      // the modulus operator makes the index roll over to the start
      interrupts();
    }
    

    I also found this function (not yet tested with the transmitter)

    //this programm will put out a PPM signal
    
    //////////////////////CONFIGURATION///////////////////////////////
    #define chanel_number 8  //set the number of chanels
    #define default_servo_value 1500  //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: 1 is positive, 0 is negative
    #define sigPin 10  //set PPM signal pin on the arduino
    //////////////////////////////////////////////////////////////////
    
    
    /*this array holds the servo values for the ppm signal
     change theese values in your code (usually servo values are between 1000 and 2000)*/
    int ppm[chanel_number];
    
    void setup(){  
    
      //initiallize default ppm values
      for(int i=0; i<chanel_number; i++){
        ppm[i]= default_servo_value;
      }
    
      pinMode(sigPin, OUTPUT);
      digitalWrite(sigPin, !onState);  //set the PPM signal pin to the default state (off)
    }
    
    void loop(){
      //put main code here
      ppmWrite();
    }
    
    void ppmWrite(){  //generate PPM signal
      static unsigned long lastFrLen;
      static unsigned long lastServo;
      static unsigned long lastPulse;
      static boolean PPM_run;
      static boolean pulse;
      static boolean pulseStart = true;
      static byte counter;
      static byte part = true;
    
      if(micros() - lastFrLen >= PPM_FrLen){  //start PPM signal after PPM_FrLen has passed
        lastFrLen = micros();
        PPM_run = true;
      }
    
      if(counter >= chanel_number){
        PPM_run = false;
        counter = 0;
        pulse = true;  //put out the last pulse
      }
    
      if(PPM_run){
        if (part){  //put out the pulse
          pulse = true;
          part = false;
          lastServo = micros();
        }
        else{  //wait till servo signal time (values from the ppm array) has passed
          if(micros() - lastServo >= ppm[counter]){
            counter++;  //do the next channel
            part = true;
          }
        }
      }
    
      if(pulse){
        if(pulseStart == true){  //start the pulse
          digitalWrite(sigPin, onState);
          pulseStart = false;
          lastPulse = micros();
        }
        else{  //will wait till PPM_PulseLen has passed and finish the pulse
          if(micros() - lastPulse >= PPM_PulseLen){
            digitalWrite(sigPin, !onState);
            pulse = false;
            pulseStart = true;
          }
        }
      }
    }
    

    I really don't know how to port all of this to M5Stack.
    I'm trying to merge the first sketch with a timer interrupt sketch for esp32

    volatile int interruptCounter;
    int totalInterruptCounter;
    
    hw_timer_t * timer = NULL;
    portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
    
    void IRAM_ATTR onTimer() {
      portENTER_CRITICAL_ISR(&timerMux);
      interruptCounter++;
      portEXIT_CRITICAL_ISR(&timerMux);
    
    }
    
    void setup() {
    
      Serial.begin(115200);
    
      timer = timerBegin(0, 80, true);
      timerAttachInterrupt(timer, &onTimer, true);
      //  timerAlarmWrite(timer, 1000000, true);
      timerAlarmWrite(timer,   22000, true); PER IL PPM ?? ??
    
      timerAlarmEnable(timer);
    
    }
    
    void loop() {
    
      if (interruptCounter > 0) {
    
        portENTER_CRITICAL(&timerMux);
        interruptCounter--;
        portEXIT_CRITICAL(&timerMux);
    
        totalInterruptCounter++;
    
        Serial.print("An interrupt as occurred. Total number: ");
        Serial.println(totalInterruptCounter);
    
      }
    }
    

    I'm very confused....



  • Hi
    do you find a solution ? also interest by this.
    Thx