This program reads a PPM signal. It is based on RC_Simulator by Jafar Qutteineh. Additions include a number of settings to customize the routine to individual needs, as well as a calibration routine for joystick/slider controls.

Dependencies:   mbed

Files at this revision

API Documentation at this revision

Comitter:
jwolter
Date:
Wed Apr 11 01:56:50 2012 +0000
Commit message:
Initial release

Changed in this revision

main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Wed Apr 11 01:56:50 2012 +0000
@@ -0,0 +1,264 @@
+#include "mbed.h"
+#include "usbhid.h"
+
+
+/* PPM reader by John Wolter from RC_Simulator by Jafar Qutteineh*/
+/* This program takes the PPM Signal (Pulse Position Modulation) from your RC transmitter.  Includes a calibration routine
+   for 
+*/
+   
+/*Setup: Just connect the GND and PPM signal of your transmitter to the GND and P11 respectively.
+         Put a switch between P5 and Vout for calibration mode.*/
+
+/*How it works: 
+    -A PPM signal is a stream of low (0V) and high (5V) inputs which indicate the value of multiple channels.  
+    -The PPM signal consists of a bunch of variable length low pulses separated by fixed length high pulses (or vice versa).
+    -A set of pulses which represent one set of channel values is called a frame.
+    -The overall length of the frame is fixed (20 ms for example).
+    -The value of each channel is determined by the length of its pulse. (0.5 to 1.5 ms for example)
+    -The end of the frame is filled with a variable length pulse to fill the frame.  This pulse is much longer than the 
+     channel pulses so that it acts at the marker for the start of a new frame
+    -A single pin is configured as InturreptIn pin. whenever the PPM signal goes down Inturrept SignalRise() or SignalFall() is fired.
+    -SignalRise or SignalFall keep track of timing:
+        a) If Pulse width is <300 uS it's most probably a glitch.
+        b) If pulse width is >300 uS but less than < 2000 uS this is a correct channel. Read it and wait for next channel
+        c) If Pulse width is >MinSync uS this is a new frame, start again.
+        d) Timing will be different from transmitter to another, you may want to play a little bit here
+*/   
+   
+
+DigitalOut led1(LED1);
+DigitalOut led2(LED2);
+DigitalOut led3(LED3);
+DigitalOut led4(LED4);
+InterruptIn PPMsignal(p11); // connect PPM signal to this, you can change to anyother pin
+InterruptIn CalSwitch(p5); // Switch to initiate calibration mode
+LocalFileSystem local("local");  // Create the local filesystem under the name "local"
+Serial PC(USBTX,USBRX);   // I used this for debuging
+Timer timer;        
+Timer timer2;        
+
+const int NChannels=6; // This is the number of channels in your PPM signal
+unsigned char CurChannel=0; //This will point to the current channel in PPM frame. 
+int Channels[NChannels]={0,0,0,0,0,0}; //This where the channels value is stored until frame is complete.
+unsigned int Times[NChannels]={0,0,0,0,0,0}; //This where the channel pulse time value is stored until frame is complete.
+float C0s[NChannels]; // Zeroth order coefficient for converting times to channels
+float C1s[NChannels]; // First order coefficient for converting times to channels
+int MsgCount[6]={0,0,0,0,0,0}; // Counter for analyzing data flow, i.e. number of errors, etc.
+
+bool CanUpdate=false;       // Once PPM frame is complete this will be true
+bool CalMode=false;       // Once calibraton switch is pressed this will be true
+int FrameCount=0;
+int i;
+int TimeElapsed =0; //Keeps track of time between PPM interrupts
+int TimeElapsed2 =0; //Keeps track of time between button interrupts
+int TLow;
+int THigh;
+char dum1,dum2;
+
+/* Here are the parameters that might need to be adjusted depending on your setup */
+const bool debug=true;
+const bool VerboseDebug=true;
+int MinSync = 6000; // Minimum time of the sync pulse (us)
+int ShortTime = 800; // If the pulse time for a channel is this short, something is wrong (us)
+int TimeMin = 1000; // The minimum pulse time for a channel should be this long (us)
+int TimeMax = 2000; // The maximum pulse time for a channel should be this long (us)
+int ChannelMin = -127; // Desired minimum value for outputs
+int ChannelMax = 127; // Desired maximum value for outputs
+const int JCCount=4; //Number of joystick channels
+unsigned char JoystickChannels[JCCount]={0,1,2,3}; // List of joystick channels
+
+//Raise_Message is just a function that helped me in development process. You can ignore it all togather.
+void Raise_Message (unsigned char Err_Code, int info) {
+    switch (Err_Code) {
+        case 0:
+            MsgCount[0]++;
+//            led1 = 1;
+//            PC.printf ("%i\n",info);
+            break;
+        case 1:
+            MsgCount[1]++;
+//            led2 = 1;
+//            PC.printf ("Broke@ %i\n",info);
+            break;
+        case 2:
+            MsgCount[2]++;
+//            led3 = 1;
+//            PC.printf ("Set ok\n");
+            break;
+        case 3:
+            MsgCount[3]++;
+//            led4 = 1;
+//            PC.printf ("%i\n",info);
+            break;
+        case 255:
+            MsgCount[4]++;
+//            PC.printf("Initalized sucessfully \n");
+            break;
+        default:
+            MsgCount[5]++;
+//            PC.printf("I shouldn't be here \n");
+    }
+
+}
+
+//Here were all the work decoding the PPM signal takes place
+void SignalRise() {
+    led4=1-led4;
+    TimeElapsed = timer.read_us(); // Since we are measuring from signal rise to signal rise, note that TimeElapsed includes the fixed separator time as well
+    if (TimeElapsed < ShortTime) { 
+        Raise_Message(0,TimeElapsed);
+        return; //Channel timing too short; ignore, it's a glitch. Don't move to the next channel
+    }
+    __disable_irq();
+    timer.reset();  
+    if ((TimeElapsed > MinSync ) && (CurChannel != 0)) {   //somehow before reaching the end of PPM frame you read   "New" frame signal???
+                                                         //Ok, it happens. Just ignore this frame and start a new one        
+        Raise_Message (1,CurChannel);          //incomplete channels set
+        CurChannel=0;              
+    }
+    if ((TimeElapsed > MinSync ) && (CurChannel == 0)) {
+        Raise_Message (2,CurChannel);          //New frame started
+      __enable_irq();       // This is good. You've received "New" frame signal as expected
+        return;
+    }
+
+    // Process current channel. This is a correct channel in a correct frame so far
+    //Channels[CurChannel]= (TimeElapsed-1000)*255/1000; // Normalize reading (Min: 900us Max: 1900 us). This is my readings, yours can be different
+    Channels[CurChannel]= C0s[CurChannel] + C1s[CurChannel]*TimeElapsed; // Normalize reading (Min: 900us Max: 1900 us). This is my readings, yours can be different
+    Times[CurChannel] = TimeElapsed;
+    CurChannel++;
+    
+    if (CurChannel==NChannels ) {  // great!, you've a complete correct frame. Set CanUpdate and start a new frame
+        FrameCount++;
+        CurChannel=0;
+        Raise_Message(3,0); // Successful frame!
+        CanUpdate= true;
+        led1=1-led1; // blink the LED
+    }
+    __enable_irq();
+}
+
+// Interrupt for calibration button press
+void CalSwitchRise() {
+    TimeElapsed2 = timer2.read(); // Since we are measuring from signal rise to signal rise, note that TimeElapsed includes the fixed separator time as well
+    if (TimeElapsed2 > 0.3) {
+        __disable_irq();
+        CalMode=true;
+        timer2.reset();  
+        __enable_irq();
+    }
+}
+
+void Initalize () {
+    __disable_irq();
+    PC.baud(9600); // set baud rate
+    if (debug) {PC.printf("\n\nInitializing...\n");}
+    PPMsignal.mode (PullUp);
+    PPMsignal.rise(&SignalRise); //Attach SignalRise routine to handle PPMsignal rise
+    CalSwitch.rise(&CalSwitchRise); //Attach CalSwitchRise routine to handle CalSwitch rise
+    timer.start();
+    timer2.start();
+    Raise_Message(255,0); // return successful
+    led1 = 0;
+    led2 = 0;
+    led3 = 0;
+    led4 = 0;
+    //Initialize all channels to produce "sane" outputs (depending on your definition of sanity ;-).
+    C1s[0] = 1.0*(ChannelMax-ChannelMin)/(TimeMax-TimeMin);
+    C0s[0] = 1.0*ChannelMin - TimeMin*C1s[0];
+    for (i=1; i<NChannels; i++)
+    {
+        C1s[i]=C1s[0];
+        C0s[i]=C0s[0];
+    }    
+    if (debug) {
+        PC.printf("Coefficients read from file:\n");
+        for(i=0; i<NChannels; i++)
+        {
+            PC.printf("Channel %d - C0=%6f, C1=%6f\n",i,C0s[i],C1s[i]);
+        }
+    }
+    // Initialize joystick channels using saved calibration information
+    FILE *fpi = fopen("/local/coefs.txt", "r");  // Open coefficient file on the local file system for reading
+    for(i=0; i<JCCount; i++)
+    {
+        fscanf(fpi, "%f%c%f%c",&C0s[JoystickChannels[i]],&dum1,&C1s[JoystickChannels[i]],&dum2);
+    }
+    fclose(fpi);
+    if (debug) {
+        PC.printf("Coefficients read from file:\n");
+        for(i=0; i<JCCount; i++)
+        {
+            PC.printf("Channel %d - C0=%6f, C1=%6f\n",JoystickChannels[i],C0s[JoystickChannels[i]],C1s[JoystickChannels[i]]);
+        }    
+        PC.printf("...Initialization complete.\n");
+    }
+    timer.reset();
+    timer2.reset();
+    __enable_irq();
+}
+
+int main() {
+    //unsigned int *pOut = Times;
+    int *pOut = Channels;
+    
+    Initalize();
+    //wait(5);
+    
+
+    while (1) {
+        if (CanUpdate) {        // We have a new frame to read
+            __disable_irq();
+            CanUpdate=false;
+            
+            //Here is where the response to the PPM inputs should go.
+            
+            if (VerboseDebug) {PC.printf("%6d,  %6d,  %6d,  %6d,  %6d,  %6d\n",pOut[0],pOut[1],pOut[2],pOut[3],pOut[4],pOut[5]); }
+            //if (VerboseDebug) {PC.printf("%6f,  %6f,  %6f,  %6f,  %6f,  %6f\n",pOut[0],pOut[1],pOut[2],pOut[3],pOut[4],pOut[5]); }
+            __enable_irq();
+
+        }
+        if (CalMode) { // Calibration mode triggered
+            __disable_irq();
+            wait(1.); // Debounce
+            led2 = 1;
+            for(i=0; i<JCCount; i++)
+            {
+                PC.printf("Push joystick channel %2d to the LOW position and press the calibrate button\n",JoystickChannels[i]+1);
+                __enable_irq();
+                CalMode=false;
+                while (!CalMode) {wait(0.1);} // Wait until the calibrate button is pressed.
+                CanUpdate=false;
+                while (!CanUpdate) {wait(0.02);} // Wait until a new PPM frame is acquired.
+                __disable_irq();
+                TLow = Times[JoystickChannels[i]]; // Save the time value in the low position.
+                wait(1.); // Debounce
+                PC.printf("Push joystick channel %2d to the HIGH position and press the calibrate button\n",JoystickChannels[i]+1);
+                __enable_irq();
+                CalMode=false;
+                while (!CalMode) {wait(0.1);} // Wait until the calibrate button is pressed.
+                CanUpdate=false;
+                while (!CanUpdate) {wait(0.02);} // Wait until a new PPM frame is acquired.
+                __disable_irq();
+                THigh = Times[JoystickChannels[i]]; // Save the time value in the low position.
+                wait(1.); // Debounce
+                C1s[i] = 1.0*(ChannelMax-ChannelMin)/(THigh-TLow);
+                C0s[i] = 1.0*ChannelMin - TLow*C1s[i];
+                PC.printf("Coefficients: C0=%6f, C1=%6f\n",C0s[i],C1s[i]);
+            }
+            FILE *fpo = fopen("/local/coefs.txt", "w");  // Open coefficient file on the local file system for writing
+            for(i=0; i<JCCount; i++)
+            {
+                fprintf(fpo, "%6f,%6f\n",C0s[JoystickChannels[i]],C1s[JoystickChannels[i]]);
+            }
+            fclose(fpo);
+            PC.printf("Calibration completed.\n");
+            led2=0;
+            CalMode=false;
+            CanUpdate=false;
+            __enable_irq();
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Wed Apr 11 01:56:50 2012 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/63bcd7ba4912