Arduino Playground is read-only starting December 31st, 2018. For more info please look at this Forum Post

Read Receiver

This code uses pin change interrupts and timer 1 to mesure the time between the rise and fall of 3 channels of PPM (Though often called PWM, see this Arduino Forum thread) on a typical RC car receiver. It could be extended to as many channels as you like. It uses the PinChangeInt library to notice when the signal pin goes high and low, and the Timer1 library to record the time between.

  1. /*
  2. Copyright 2011 Lex Talionis (Lex.V.Talionis at gmail)
  3. This program is free software: you can redistribute it
  4. and/or modify it under the terms of the GNU General Public
  5. License as published by the Free Software Foundation,
  6. either version 3 of the License, or (at your option) any
  7. later version.
  8.  
  9. This code uses pin change interrupts and timer 1 to measure the
  10. time between the rise and fall of 3 channels of PPM
  11. (Though often called PWM, see https://forum.arduino.cc/index.php/topic,14146.html)
  12. on a typical RC car receiver.  It could be extended to as
  13. many channels as you like.  It uses the PinChangeInt library
  14. to notice when the signal pin goes high and low, and the
  15. Timer1 library to record the time between.
  16.  
  17.  
  18. */
  19. #include <PinChangeInt.h>    // https://playground.arduino.cc/Main/PinChangeInt
  20. #include <PinChangeIntConfig.h>
  21. #include <TimerOne.h>        // https://playground.arduino.cc/Code/Timer1
  22.  
  23. #define NO_PORTB_PINCHANGES //PinChangeInt setup
  24. #define NO_PORTC_PINCHANGES    //only port D pinchanges (see: https://playground.arduino.cc/Learning/Pins)
  25. #define PIN_COUNT 3    //number of channels attached to the receiver
  26. #define MAX_PIN_CHANGE_PINS PIN_COUNT
  27.  
  28. #define RC_TURN 3    //Arduino pins attached to the receiver
  29. #define RC_FWD 2
  30. #define RC_FIRE 4
  31. byte pin[] = {RC_FWD, RC_TURN, RC_FIRE};    //for maximum efficiency these pins should be attached
  32. unsigned int time[] = {0,0,0};                // to the receiver's channels in the order listed here
  33.  
  34. byte state=0;
  35. byte burp=0;    // a counter to see how many times the int has executed
  36. byte cmd=0;     // a place to put our serial data
  37. byte i=0;       // global counter for tracking what pin we are on
  38.  
  39. void setup() {
  40.     Serial.begin(115200);
  41.     Serial.print("PinChangeInt ReciverReading test");
  42.     Serial.println();            //warm up the serial port
  43.  
  44.     Timer1.initialize(2200);    //longest pulse in PPM is usually 2.1 milliseconds,
  45.                                 //pick a period that gives you a little headroom.
  46.     Timer1.stop();                //stop the counter
  47.     Timer1.restart();            //set the clock to zero
  48.  
  49.     for (byte i=0; i<3; i++)
  50.     {
  51.         pinMode(pin[i], INPUT);     //set the pin to input
  52.         digitalWrite(pin[i], HIGH); //use the internal pullup resistor
  53.     }
  54.     PCintPort::attachInterrupt(pin[i], rise,RISING); // attach a PinChange Interrupt to our first pin
  55. }
  56.  
  57. void loop() {
  58.     cmd=Serial.read();        //while you got some time gimme a systems report
  59.     if (cmd=='p')
  60.     {
  61.         Serial.print("time:\t");
  62.         for (byte i=0; i<PIN_COUNT;i++)
  63.         {
  64.             Serial.print(i,DEC);
  65.             Serial.print(":");
  66.             Serial.print(time[i],DEC);
  67.             Serial.print("\t");
  68.         }
  69.         Serial.print(burp, DEC);
  70.         Serial.println();
  71. /*      Serial.print("\t");
  72.         Serial.print(clockCyclesToMicroseconds(Timer1.pwmPeriod), DEC);
  73.         Serial.print("\t");
  74.         Serial.print(Timer1.clockSelectBits, BIN);
  75.         Serial.print("\t");
  76.         Serial.println(ICR1, DEC);*/
  77.     }
  78.     cmd=0;
  79.     switch (state)
  80.     {
  81.         case RISING: //we have just seen a rising edge
  82.             PCintPort::detachInterrupt(pin[i]);
  83.             PCintPort::attachInterrupt(pin[i], fall, FALLING); //attach the falling end
  84.             state=255;
  85.             break;
  86.         case FALLING: //we just saw a falling edge
  87.             PCintPort::detachInterrupt(pin[i]);
  88.             i++;                //move to the next pin
  89.             i = i % PIN_COUNT;  //i ranges from 0 to PIN_COUNT
  90.             PCintPort::attachInterrupt(pin[i], rise,RISING);
  91.             state=255;
  92.             break;
  93.         /*default:
  94.             //do nothing
  95.             break;*/
  96.     }
  97. }
  98.  
  99. void rise()        //on the rising edge of the currently interesting pin
  100. {
  101.     Timer1.restart();        //set our stopwatch to 0
  102.     Timer1.start();            //and start it up
  103.     state=RISING;
  104. //  Serial.print('r');
  105.     burp++;
  106. }
  107.  
  108. void fall()        //on the falling edge of the signal
  109. {
  110.     state=FALLING;
  111.     time[i]=readTimer1();    // read the time since timer1 was restarted
  112. //  time[i]=Timer1.read();    // The function below has been ported into the
  113.                             // the latest TimerOne class, if you have the
  114.                             // new Timer1 lib you can use this line instead
  115.     Timer1.stop();
  116. //  Serial.print('f');
  117. }
  118.  
  119. unsigned long readTimer1()        //returns the value of the timer in microseconds
  120. {                                    //remember! phase and freq correct mode counts
  121.                                     //up to ICR1 then down again
  122.     unsigned int tmp=TCNT1;
  123.     char scale=0;
  124.     switch (Timer1.clockSelectBits)
  125.     {
  126.     case 1:// no prescale
  127.         scale=0;
  128.         break;
  129.     case 2:// x8 prescale
  130.         scale=3;
  131.         break;
  132.     case 3:// x64
  133.         scale=6;
  134.         break;
  135.     case 4:// x256
  136.         scale=8;
  137.         break;
  138.     case 5:// x1024
  139.         scale=10;
  140.         break;
  141.     }
  142.     while (TCNT1==tmp) //if the timer has not ticked yet
  143.     {
  144.         //do nothing -- max delay here is ~1023 cycles
  145.     }
  146.     tmp = (  (TCNT1>tmp) ? (tmp) : (ICR1-TCNT1)+ICR1  );//if we are counting down add the top value
  147.                                                         //to how far we have counted down
  148.     return ((tmp*1000L)/(F_CPU /1000L))<<scale;
  149. }