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

Committer:
jwolter
Date:
Wed Apr 11 01:56:50 2012 +0000
Revision:
0:32b684f1caad
Initial release

Who changed what in which revision?

UserRevisionLine numberNew contents of line
jwolter 0:32b684f1caad 1 #include "mbed.h"
jwolter 0:32b684f1caad 2 #include "usbhid.h"
jwolter 0:32b684f1caad 3
jwolter 0:32b684f1caad 4
jwolter 0:32b684f1caad 5 /* PPM reader by John Wolter from RC_Simulator by Jafar Qutteineh*/
jwolter 0:32b684f1caad 6 /* This program takes the PPM Signal (Pulse Position Modulation) from your RC transmitter. Includes a calibration routine
jwolter 0:32b684f1caad 7 for
jwolter 0:32b684f1caad 8 */
jwolter 0:32b684f1caad 9
jwolter 0:32b684f1caad 10 /*Setup: Just connect the GND and PPM signal of your transmitter to the GND and P11 respectively.
jwolter 0:32b684f1caad 11 Put a switch between P5 and Vout for calibration mode.*/
jwolter 0:32b684f1caad 12
jwolter 0:32b684f1caad 13 /*How it works:
jwolter 0:32b684f1caad 14 -A PPM signal is a stream of low (0V) and high (5V) inputs which indicate the value of multiple channels.
jwolter 0:32b684f1caad 15 -The PPM signal consists of a bunch of variable length low pulses separated by fixed length high pulses (or vice versa).
jwolter 0:32b684f1caad 16 -A set of pulses which represent one set of channel values is called a frame.
jwolter 0:32b684f1caad 17 -The overall length of the frame is fixed (20 ms for example).
jwolter 0:32b684f1caad 18 -The value of each channel is determined by the length of its pulse. (0.5 to 1.5 ms for example)
jwolter 0:32b684f1caad 19 -The end of the frame is filled with a variable length pulse to fill the frame. This pulse is much longer than the
jwolter 0:32b684f1caad 20 channel pulses so that it acts at the marker for the start of a new frame
jwolter 0:32b684f1caad 21 -A single pin is configured as InturreptIn pin. whenever the PPM signal goes down Inturrept SignalRise() or SignalFall() is fired.
jwolter 0:32b684f1caad 22 -SignalRise or SignalFall keep track of timing:
jwolter 0:32b684f1caad 23 a) If Pulse width is <300 uS it's most probably a glitch.
jwolter 0:32b684f1caad 24 b) If pulse width is >300 uS but less than < 2000 uS this is a correct channel. Read it and wait for next channel
jwolter 0:32b684f1caad 25 c) If Pulse width is >MinSync uS this is a new frame, start again.
jwolter 0:32b684f1caad 26 d) Timing will be different from transmitter to another, you may want to play a little bit here
jwolter 0:32b684f1caad 27 */
jwolter 0:32b684f1caad 28
jwolter 0:32b684f1caad 29
jwolter 0:32b684f1caad 30 DigitalOut led1(LED1);
jwolter 0:32b684f1caad 31 DigitalOut led2(LED2);
jwolter 0:32b684f1caad 32 DigitalOut led3(LED3);
jwolter 0:32b684f1caad 33 DigitalOut led4(LED4);
jwolter 0:32b684f1caad 34 InterruptIn PPMsignal(p11); // connect PPM signal to this, you can change to anyother pin
jwolter 0:32b684f1caad 35 InterruptIn CalSwitch(p5); // Switch to initiate calibration mode
jwolter 0:32b684f1caad 36 LocalFileSystem local("local"); // Create the local filesystem under the name "local"
jwolter 0:32b684f1caad 37 Serial PC(USBTX,USBRX); // I used this for debuging
jwolter 0:32b684f1caad 38 Timer timer;
jwolter 0:32b684f1caad 39 Timer timer2;
jwolter 0:32b684f1caad 40
jwolter 0:32b684f1caad 41 const int NChannels=6; // This is the number of channels in your PPM signal
jwolter 0:32b684f1caad 42 unsigned char CurChannel=0; //This will point to the current channel in PPM frame.
jwolter 0:32b684f1caad 43 int Channels[NChannels]={0,0,0,0,0,0}; //This where the channels value is stored until frame is complete.
jwolter 0:32b684f1caad 44 unsigned int Times[NChannels]={0,0,0,0,0,0}; //This where the channel pulse time value is stored until frame is complete.
jwolter 0:32b684f1caad 45 float C0s[NChannels]; // Zeroth order coefficient for converting times to channels
jwolter 0:32b684f1caad 46 float C1s[NChannels]; // First order coefficient for converting times to channels
jwolter 0:32b684f1caad 47 int MsgCount[6]={0,0,0,0,0,0}; // Counter for analyzing data flow, i.e. number of errors, etc.
jwolter 0:32b684f1caad 48
jwolter 0:32b684f1caad 49 bool CanUpdate=false; // Once PPM frame is complete this will be true
jwolter 0:32b684f1caad 50 bool CalMode=false; // Once calibraton switch is pressed this will be true
jwolter 0:32b684f1caad 51 int FrameCount=0;
jwolter 0:32b684f1caad 52 int i;
jwolter 0:32b684f1caad 53 int TimeElapsed =0; //Keeps track of time between PPM interrupts
jwolter 0:32b684f1caad 54 int TimeElapsed2 =0; //Keeps track of time between button interrupts
jwolter 0:32b684f1caad 55 int TLow;
jwolter 0:32b684f1caad 56 int THigh;
jwolter 0:32b684f1caad 57 char dum1,dum2;
jwolter 0:32b684f1caad 58
jwolter 0:32b684f1caad 59 /* Here are the parameters that might need to be adjusted depending on your setup */
jwolter 0:32b684f1caad 60 const bool debug=true;
jwolter 0:32b684f1caad 61 const bool VerboseDebug=true;
jwolter 0:32b684f1caad 62 int MinSync = 6000; // Minimum time of the sync pulse (us)
jwolter 0:32b684f1caad 63 int ShortTime = 800; // If the pulse time for a channel is this short, something is wrong (us)
jwolter 0:32b684f1caad 64 int TimeMin = 1000; // The minimum pulse time for a channel should be this long (us)
jwolter 0:32b684f1caad 65 int TimeMax = 2000; // The maximum pulse time for a channel should be this long (us)
jwolter 0:32b684f1caad 66 int ChannelMin = -127; // Desired minimum value for outputs
jwolter 0:32b684f1caad 67 int ChannelMax = 127; // Desired maximum value for outputs
jwolter 0:32b684f1caad 68 const int JCCount=4; //Number of joystick channels
jwolter 0:32b684f1caad 69 unsigned char JoystickChannels[JCCount]={0,1,2,3}; // List of joystick channels
jwolter 0:32b684f1caad 70
jwolter 0:32b684f1caad 71 //Raise_Message is just a function that helped me in development process. You can ignore it all togather.
jwolter 0:32b684f1caad 72 void Raise_Message (unsigned char Err_Code, int info) {
jwolter 0:32b684f1caad 73 switch (Err_Code) {
jwolter 0:32b684f1caad 74 case 0:
jwolter 0:32b684f1caad 75 MsgCount[0]++;
jwolter 0:32b684f1caad 76 // led1 = 1;
jwolter 0:32b684f1caad 77 // PC.printf ("%i\n",info);
jwolter 0:32b684f1caad 78 break;
jwolter 0:32b684f1caad 79 case 1:
jwolter 0:32b684f1caad 80 MsgCount[1]++;
jwolter 0:32b684f1caad 81 // led2 = 1;
jwolter 0:32b684f1caad 82 // PC.printf ("Broke@ %i\n",info);
jwolter 0:32b684f1caad 83 break;
jwolter 0:32b684f1caad 84 case 2:
jwolter 0:32b684f1caad 85 MsgCount[2]++;
jwolter 0:32b684f1caad 86 // led3 = 1;
jwolter 0:32b684f1caad 87 // PC.printf ("Set ok\n");
jwolter 0:32b684f1caad 88 break;
jwolter 0:32b684f1caad 89 case 3:
jwolter 0:32b684f1caad 90 MsgCount[3]++;
jwolter 0:32b684f1caad 91 // led4 = 1;
jwolter 0:32b684f1caad 92 // PC.printf ("%i\n",info);
jwolter 0:32b684f1caad 93 break;
jwolter 0:32b684f1caad 94 case 255:
jwolter 0:32b684f1caad 95 MsgCount[4]++;
jwolter 0:32b684f1caad 96 // PC.printf("Initalized sucessfully \n");
jwolter 0:32b684f1caad 97 break;
jwolter 0:32b684f1caad 98 default:
jwolter 0:32b684f1caad 99 MsgCount[5]++;
jwolter 0:32b684f1caad 100 // PC.printf("I shouldn't be here \n");
jwolter 0:32b684f1caad 101 }
jwolter 0:32b684f1caad 102
jwolter 0:32b684f1caad 103 }
jwolter 0:32b684f1caad 104
jwolter 0:32b684f1caad 105 //Here were all the work decoding the PPM signal takes place
jwolter 0:32b684f1caad 106 void SignalRise() {
jwolter 0:32b684f1caad 107 led4=1-led4;
jwolter 0:32b684f1caad 108 TimeElapsed = timer.read_us(); // Since we are measuring from signal rise to signal rise, note that TimeElapsed includes the fixed separator time as well
jwolter 0:32b684f1caad 109 if (TimeElapsed < ShortTime) {
jwolter 0:32b684f1caad 110 Raise_Message(0,TimeElapsed);
jwolter 0:32b684f1caad 111 return; //Channel timing too short; ignore, it's a glitch. Don't move to the next channel
jwolter 0:32b684f1caad 112 }
jwolter 0:32b684f1caad 113 __disable_irq();
jwolter 0:32b684f1caad 114 timer.reset();
jwolter 0:32b684f1caad 115 if ((TimeElapsed > MinSync ) && (CurChannel != 0)) { //somehow before reaching the end of PPM frame you read "New" frame signal???
jwolter 0:32b684f1caad 116 //Ok, it happens. Just ignore this frame and start a new one
jwolter 0:32b684f1caad 117 Raise_Message (1,CurChannel); //incomplete channels set
jwolter 0:32b684f1caad 118 CurChannel=0;
jwolter 0:32b684f1caad 119 }
jwolter 0:32b684f1caad 120 if ((TimeElapsed > MinSync ) && (CurChannel == 0)) {
jwolter 0:32b684f1caad 121 Raise_Message (2,CurChannel); //New frame started
jwolter 0:32b684f1caad 122 __enable_irq(); // This is good. You've received "New" frame signal as expected
jwolter 0:32b684f1caad 123 return;
jwolter 0:32b684f1caad 124 }
jwolter 0:32b684f1caad 125
jwolter 0:32b684f1caad 126 // Process current channel. This is a correct channel in a correct frame so far
jwolter 0:32b684f1caad 127 //Channels[CurChannel]= (TimeElapsed-1000)*255/1000; // Normalize reading (Min: 900us Max: 1900 us). This is my readings, yours can be different
jwolter 0:32b684f1caad 128 Channels[CurChannel]= C0s[CurChannel] + C1s[CurChannel]*TimeElapsed; // Normalize reading (Min: 900us Max: 1900 us). This is my readings, yours can be different
jwolter 0:32b684f1caad 129 Times[CurChannel] = TimeElapsed;
jwolter 0:32b684f1caad 130 CurChannel++;
jwolter 0:32b684f1caad 131
jwolter 0:32b684f1caad 132 if (CurChannel==NChannels ) { // great!, you've a complete correct frame. Set CanUpdate and start a new frame
jwolter 0:32b684f1caad 133 FrameCount++;
jwolter 0:32b684f1caad 134 CurChannel=0;
jwolter 0:32b684f1caad 135 Raise_Message(3,0); // Successful frame!
jwolter 0:32b684f1caad 136 CanUpdate= true;
jwolter 0:32b684f1caad 137 led1=1-led1; // blink the LED
jwolter 0:32b684f1caad 138 }
jwolter 0:32b684f1caad 139 __enable_irq();
jwolter 0:32b684f1caad 140 }
jwolter 0:32b684f1caad 141
jwolter 0:32b684f1caad 142 // Interrupt for calibration button press
jwolter 0:32b684f1caad 143 void CalSwitchRise() {
jwolter 0:32b684f1caad 144 TimeElapsed2 = timer2.read(); // Since we are measuring from signal rise to signal rise, note that TimeElapsed includes the fixed separator time as well
jwolter 0:32b684f1caad 145 if (TimeElapsed2 > 0.3) {
jwolter 0:32b684f1caad 146 __disable_irq();
jwolter 0:32b684f1caad 147 CalMode=true;
jwolter 0:32b684f1caad 148 timer2.reset();
jwolter 0:32b684f1caad 149 __enable_irq();
jwolter 0:32b684f1caad 150 }
jwolter 0:32b684f1caad 151 }
jwolter 0:32b684f1caad 152
jwolter 0:32b684f1caad 153 void Initalize () {
jwolter 0:32b684f1caad 154 __disable_irq();
jwolter 0:32b684f1caad 155 PC.baud(9600); // set baud rate
jwolter 0:32b684f1caad 156 if (debug) {PC.printf("\n\nInitializing...\n");}
jwolter 0:32b684f1caad 157 PPMsignal.mode (PullUp);
jwolter 0:32b684f1caad 158 PPMsignal.rise(&SignalRise); //Attach SignalRise routine to handle PPMsignal rise
jwolter 0:32b684f1caad 159 CalSwitch.rise(&CalSwitchRise); //Attach CalSwitchRise routine to handle CalSwitch rise
jwolter 0:32b684f1caad 160 timer.start();
jwolter 0:32b684f1caad 161 timer2.start();
jwolter 0:32b684f1caad 162 Raise_Message(255,0); // return successful
jwolter 0:32b684f1caad 163 led1 = 0;
jwolter 0:32b684f1caad 164 led2 = 0;
jwolter 0:32b684f1caad 165 led3 = 0;
jwolter 0:32b684f1caad 166 led4 = 0;
jwolter 0:32b684f1caad 167 //Initialize all channels to produce "sane" outputs (depending on your definition of sanity ;-).
jwolter 0:32b684f1caad 168 C1s[0] = 1.0*(ChannelMax-ChannelMin)/(TimeMax-TimeMin);
jwolter 0:32b684f1caad 169 C0s[0] = 1.0*ChannelMin - TimeMin*C1s[0];
jwolter 0:32b684f1caad 170 for (i=1; i<NChannels; i++)
jwolter 0:32b684f1caad 171 {
jwolter 0:32b684f1caad 172 C1s[i]=C1s[0];
jwolter 0:32b684f1caad 173 C0s[i]=C0s[0];
jwolter 0:32b684f1caad 174 }
jwolter 0:32b684f1caad 175 if (debug) {
jwolter 0:32b684f1caad 176 PC.printf("Coefficients read from file:\n");
jwolter 0:32b684f1caad 177 for(i=0; i<NChannels; i++)
jwolter 0:32b684f1caad 178 {
jwolter 0:32b684f1caad 179 PC.printf("Channel %d - C0=%6f, C1=%6f\n",i,C0s[i],C1s[i]);
jwolter 0:32b684f1caad 180 }
jwolter 0:32b684f1caad 181 }
jwolter 0:32b684f1caad 182 // Initialize joystick channels using saved calibration information
jwolter 0:32b684f1caad 183 FILE *fpi = fopen("/local/coefs.txt", "r"); // Open coefficient file on the local file system for reading
jwolter 0:32b684f1caad 184 for(i=0; i<JCCount; i++)
jwolter 0:32b684f1caad 185 {
jwolter 0:32b684f1caad 186 fscanf(fpi, "%f%c%f%c",&C0s[JoystickChannels[i]],&dum1,&C1s[JoystickChannels[i]],&dum2);
jwolter 0:32b684f1caad 187 }
jwolter 0:32b684f1caad 188 fclose(fpi);
jwolter 0:32b684f1caad 189 if (debug) {
jwolter 0:32b684f1caad 190 PC.printf("Coefficients read from file:\n");
jwolter 0:32b684f1caad 191 for(i=0; i<JCCount; i++)
jwolter 0:32b684f1caad 192 {
jwolter 0:32b684f1caad 193 PC.printf("Channel %d - C0=%6f, C1=%6f\n",JoystickChannels[i],C0s[JoystickChannels[i]],C1s[JoystickChannels[i]]);
jwolter 0:32b684f1caad 194 }
jwolter 0:32b684f1caad 195 PC.printf("...Initialization complete.\n");
jwolter 0:32b684f1caad 196 }
jwolter 0:32b684f1caad 197 timer.reset();
jwolter 0:32b684f1caad 198 timer2.reset();
jwolter 0:32b684f1caad 199 __enable_irq();
jwolter 0:32b684f1caad 200 }
jwolter 0:32b684f1caad 201
jwolter 0:32b684f1caad 202 int main() {
jwolter 0:32b684f1caad 203 //unsigned int *pOut = Times;
jwolter 0:32b684f1caad 204 int *pOut = Channels;
jwolter 0:32b684f1caad 205
jwolter 0:32b684f1caad 206 Initalize();
jwolter 0:32b684f1caad 207 //wait(5);
jwolter 0:32b684f1caad 208
jwolter 0:32b684f1caad 209
jwolter 0:32b684f1caad 210 while (1) {
jwolter 0:32b684f1caad 211 if (CanUpdate) { // We have a new frame to read
jwolter 0:32b684f1caad 212 __disable_irq();
jwolter 0:32b684f1caad 213 CanUpdate=false;
jwolter 0:32b684f1caad 214
jwolter 0:32b684f1caad 215 //Here is where the response to the PPM inputs should go.
jwolter 0:32b684f1caad 216
jwolter 0:32b684f1caad 217 if (VerboseDebug) {PC.printf("%6d, %6d, %6d, %6d, %6d, %6d\n",pOut[0],pOut[1],pOut[2],pOut[3],pOut[4],pOut[5]); }
jwolter 0:32b684f1caad 218 //if (VerboseDebug) {PC.printf("%6f, %6f, %6f, %6f, %6f, %6f\n",pOut[0],pOut[1],pOut[2],pOut[3],pOut[4],pOut[5]); }
jwolter 0:32b684f1caad 219 __enable_irq();
jwolter 0:32b684f1caad 220
jwolter 0:32b684f1caad 221 }
jwolter 0:32b684f1caad 222 if (CalMode) { // Calibration mode triggered
jwolter 0:32b684f1caad 223 __disable_irq();
jwolter 0:32b684f1caad 224 wait(1.); // Debounce
jwolter 0:32b684f1caad 225 led2 = 1;
jwolter 0:32b684f1caad 226 for(i=0; i<JCCount; i++)
jwolter 0:32b684f1caad 227 {
jwolter 0:32b684f1caad 228 PC.printf("Push joystick channel %2d to the LOW position and press the calibrate button\n",JoystickChannels[i]+1);
jwolter 0:32b684f1caad 229 __enable_irq();
jwolter 0:32b684f1caad 230 CalMode=false;
jwolter 0:32b684f1caad 231 while (!CalMode) {wait(0.1);} // Wait until the calibrate button is pressed.
jwolter 0:32b684f1caad 232 CanUpdate=false;
jwolter 0:32b684f1caad 233 while (!CanUpdate) {wait(0.02);} // Wait until a new PPM frame is acquired.
jwolter 0:32b684f1caad 234 __disable_irq();
jwolter 0:32b684f1caad 235 TLow = Times[JoystickChannels[i]]; // Save the time value in the low position.
jwolter 0:32b684f1caad 236 wait(1.); // Debounce
jwolter 0:32b684f1caad 237 PC.printf("Push joystick channel %2d to the HIGH position and press the calibrate button\n",JoystickChannels[i]+1);
jwolter 0:32b684f1caad 238 __enable_irq();
jwolter 0:32b684f1caad 239 CalMode=false;
jwolter 0:32b684f1caad 240 while (!CalMode) {wait(0.1);} // Wait until the calibrate button is pressed.
jwolter 0:32b684f1caad 241 CanUpdate=false;
jwolter 0:32b684f1caad 242 while (!CanUpdate) {wait(0.02);} // Wait until a new PPM frame is acquired.
jwolter 0:32b684f1caad 243 __disable_irq();
jwolter 0:32b684f1caad 244 THigh = Times[JoystickChannels[i]]; // Save the time value in the low position.
jwolter 0:32b684f1caad 245 wait(1.); // Debounce
jwolter 0:32b684f1caad 246 C1s[i] = 1.0*(ChannelMax-ChannelMin)/(THigh-TLow);
jwolter 0:32b684f1caad 247 C0s[i] = 1.0*ChannelMin - TLow*C1s[i];
jwolter 0:32b684f1caad 248 PC.printf("Coefficients: C0=%6f, C1=%6f\n",C0s[i],C1s[i]);
jwolter 0:32b684f1caad 249 }
jwolter 0:32b684f1caad 250 FILE *fpo = fopen("/local/coefs.txt", "w"); // Open coefficient file on the local file system for writing
jwolter 0:32b684f1caad 251 for(i=0; i<JCCount; i++)
jwolter 0:32b684f1caad 252 {
jwolter 0:32b684f1caad 253 fprintf(fpo, "%6f,%6f\n",C0s[JoystickChannels[i]],C1s[JoystickChannels[i]]);
jwolter 0:32b684f1caad 254 }
jwolter 0:32b684f1caad 255 fclose(fpo);
jwolter 0:32b684f1caad 256 PC.printf("Calibration completed.\n");
jwolter 0:32b684f1caad 257 led2=0;
jwolter 0:32b684f1caad 258 CalMode=false;
jwolter 0:32b684f1caad 259 CanUpdate=false;
jwolter 0:32b684f1caad 260 __enable_irq();
jwolter 0:32b684f1caad 261 }
jwolter 0:32b684f1caad 262 }
jwolter 0:32b684f1caad 263 }
jwolter 0:32b684f1caad 264