Advertisement
osnwt

AVR PPM decoder

Jun 30th, 2011
875
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 9.44 KB | None | 0 0
  1. //=============================================================================
  2. // Interrupt-driven PPM decoder (advanced).
  3. // It reads up to MAX_CHANNELS pulses from PPM stream and stores their lengths
  4. // into data buffer available for output interface.
  5. // Additional options allow verification of input packets to reduce jitter.
  6. //
  7. // $Id: in_ppm_adv.c,v 1.3 2008/11/26 15:01:35 SOY Exp $
  8. //=============================================================================
  9.  
  10. #include "common.h"
  11.  
  12. #if IN_PPM_ADV
  13.  
  14. //=============================================================================
  15. // Servo tester option.
  16. // Define this for channel number used for servo tester.
  17. // No servo tester option supported if this is defined to 0.
  18. #ifndef IN_PPM_ADV_SERVO_TEST_CHANNEL
  19. #define IN_PPM_ADV_SERVO_TEST_CHANNEL   0   // range is 0 or 1..MAX_CHANNELS
  20. #endif
  21.  
  22. // PPM polarity option.
  23. // It should not be changed. But for some rare cases it may be useful
  24. // if there is no PPM detected.
  25. #ifndef IN_PPM_ADV_POLARITY
  26. #define IN_PPM_ADV_POLARITY             0   // 0 for direct, 1 for inverted
  27. #endif
  28.  
  29. // Advanced data verification options.
  30. // You may set one of the following options to choose PPM data verification mode.
  31. // By default all options are set to 0 that means no additional data verification.
  32. // Up to MAX_CHANNELS will be read in that case.
  33.  
  34. // If this option is set to non-zero then ONLY first N_CHANNELS_ONLY channels
  35. // will be read and made available for output. This option may be useful for
  36. // Walkera transmitters which have some PCM data after standard PPM stream.
  37. #ifndef IN_PPM_ADV_N_CHANNELS_ONLY
  38. #define IN_PPM_ADV_N_CHANNELS_ONLY      0   // range is 0 or [1..MAX_CHANNELS]
  39. #endif
  40.  
  41. // If this option is set to non-zero then input PPM stream will be checked to
  42. // see if it consists of exactly N_CHANNELS_EXACT channels. Any deviation will
  43. // result in refusal of whole PPM data packet.
  44. #ifndef IN_PPM_ADV_N_CHANNELS_EXACT
  45. #define IN_PPM_ADV_N_CHANNELS_EXACT     0   // range is 0 or [1..MAX_CHANNELS]
  46. #endif
  47.  
  48. // If this option is set to non-zero then input PPM stream vill be evaluated
  49. // to find the number of PPM channels. If last N_CHANNELS_AUTO PPM packets
  50. // consist of equal number of pulses, then PPM data packet will be treated
  51. // as valid. Otherwise it will be rejected. This is the recomended option
  52. // with number of packets to check 2-10.
  53. #ifndef IN_PPM_ADV_N_CHANNELS_AUTO
  54. #define IN_PPM_ADV_N_CHANNELS_AUTO      0   // range is 0 or 2-10 (recommended option)
  55. #endif
  56.  
  57. //=============================================================================
  58. // Global variables
  59.  
  60. // Channel data variables
  61. int  channelData[MAX_CHANNELS];         // 16 bit per channel value in uS (732-2268)
  62. volatile char newDataFound;             // new data packet found flag
  63.  
  64. //-----------------------------------------------------------------------------
  65. // Decoder timing
  66. //
  67. // XTAL = 12,000,000 Hz (timer input frequency), prescaler = 8
  68. // ppmLength (uS) = Timer / TCNT_CYCLES, where TCNT_CYCLES=1.5 (12/8)
  69. //
  70. // Timer = 30000 -> 20000 uS (PPM packet period)
  71. //          4500 ->  3000 uS (syncro pause)
  72. //          1050 ->   700 uS (minimum pulse)
  73. //          2250 ->  1500 uS (middle pulse)
  74. //          3300 ->  2200 uS (maximum value)
  75.  
  76. // Hardware configuration
  77. #define TIMER           TCNT1       // 16-bit timer register
  78. #define ICR             ICR1        // input capture register
  79. #define TCCRA           TCCR1A
  80. #define TCCRB           TCCR1B
  81. #define OCRA            OCR1A
  82. #define ICF             ICF1
  83. #define OCF             OCF1A
  84. #define TICIE           TICIE1
  85. #define OCIE            OCIE1A
  86. #define ICES            ICES1
  87. #define TIMER_CAPT_vect TIMER1_CAPT_vect
  88.  
  89. // Capture port settings (required for turning on the internal pull-up resistor)
  90. #define ICP_PORT_IN     IO_INPORT(ICP_PORT_NAME)
  91. #define ICP_PORT_OUT    IO_OUTPORT(ICP_PORT_NAME)
  92. #define ICP_PORT_DD     IO_DDRPORT(ICP_PORT_NAME)
  93. #define ICP_BIT         ICP_PORT_BIT
  94.  
  95. // Servo tester port settings (if enabled then servo test output goes here)
  96. #define ST_PORT_OUT     IO_OUTPORT(ST_PORT_NAME)
  97. #define ST_PORT_DD      IO_DDRPORT(ST_PORT_NAME)
  98. #define ST_BIT          ST_PORT_BIT
  99.  
  100. // Software configuration
  101. #define PRESCALER_BITS  ((0<<CS12)|(1<<CS11)|(0<<CS10))                     // prescaler = 8
  102. #define TCCRA_INIT      ((0<<WGM11)|(0<<WGM10))                             // normal mode
  103. #define TCCRB_INIT      ((0<<WGM13)|(0<<WGM12))|(IN_PPM_ADV_POLARITY<<ICES) // input capture edge select
  104. #define TIFR_INIT       (1<<ICF)                                            // reset ICF flag
  105. #define TIMSK_INIT      (1<<TICIE)                                          // enable ICF interrupt
  106.  
  107. //-----------------------------------------------------------------------------
  108. //
  109. // Initialize PPM decoder
  110. //
  111. void inDecoderInit(void)
  112. {
  113.     ICP_PORT_DD &= ~(1<<ICP_BIT);               // input
  114.     ICP_PORT_OUT |= (1<<ICP_BIT);               // enable pullup
  115.  
  116.     TCCRA  = TCCRA_INIT;                        // initialize timer
  117.     TCCRB  = TCCRB_INIT | PRESCALER_BITS;
  118.     TIFR  |= TIFR_INIT;                         // reset pending interrupt flags if any
  119.     TIMSK |= TIMSK_INIT;                        // enable timer interrupts
  120.  
  121. #if IN_PPM_ADV_SERVO_TEST_CHANNEL
  122.     ST_PORT_DD |= (1<<ST_BIT);                  // prepare output pin for servo tester
  123.     ST_PORT_OUT &= ~(1<<ST_BIT);
  124. #endif
  125. }
  126.  
  127. //
  128. // Stop PPM decoder interrupts
  129. //
  130. void inDecoderStop(void)
  131. {
  132.     TIMSK &= ~(TIMSK_INIT);                     // disable timer interrupts
  133.     TIFR  |= TIFR_INIT;                         // reset pending interrupt flags if any
  134. }
  135.  
  136. //
  137. // Timer capture ISR.
  138. //
  139. #pragma vector=TIMER_CAPT_vect
  140. static __nested __interrupt void ISR_Timer_Capture(void)
  141. {
  142.     static unsigned ppmData[MAX_CHANNELS];  // temporary data buffer
  143.     static unsigned char ppmChannel;        // PPM channel to receive
  144.     static unsigned timerLast;              // last timer captured value
  145.     unsigned timer;                         // captured value
  146.     unsigned timerDiff;                     // timer difference
  147.     unsigned ppmLength;                     // PPM pulse length in uS
  148.  
  149.     // calculate the length of pulse in uS (dt / 1.5)
  150.     timer     = ICR;
  151.     timerDiff = timer - timerLast;
  152.     timerLast = timer;
  153.     ppmLength = (timerDiff << 1) / 3;
  154.  
  155.     // if sycnro pause is found then process whole packet,
  156.     // else simply store the pulse length into temp buffer
  157.     if (ppmLength < 3000)
  158.     {
  159.         // next PPM pulse is found
  160.         if (ppmChannel < MAX_CHANNELS) ppmData[ppmChannel] = ppmLength;
  161.         ppmChannel++;
  162.  
  163.         // servo tester support
  164. #if IN_PPM_ADV_SERVO_TEST_CHANNEL
  165.         if (ppmChannel == (IN_PPM_ADV_SERVO_TEST_CHANNEL-1))
  166.         {
  167.             // start of required channel pulse found
  168.             ST_PORT_OUT |= (1<<ST_BIT);
  169.         }
  170.         else if (ppmChannel == IN_PPM_ADV_SERVO_TEST_CHANNEL)
  171.         {
  172.             // end of required channel pulse found
  173.             ST_PORT_OUT &= ~(1<<ST_BIT);
  174.         }
  175. #endif
  176.     }
  177.     else
  178.     {
  179.         // end of packet is found
  180.  
  181. #if IN_PPM_ADV_N_CHANNELS_ONLY
  182.         // use only first N_CHANNELS_ONLY channels, the rest is ignored
  183.         if (ppmChannel > IN_PPM_ADV_N_CHANNELS_ONLY) ppmChannel = IN_PPM_ADV_N_CHANNELS_ONLY;
  184.  
  185. #elif IN_PPM_ADV_N_CHANNELS_EXACT
  186.         // use packet only if exact N_CHANNELS_EXACT channels are there
  187.         if (ppmChannel != IN_PPM_ADV_N_CHANNELS_EXACT) ppmChannel = 0;
  188.  
  189. #elif IN_PPM_ADV_N_CHANNELS_AUTO
  190.         // check for N_CHANNELS_AUTO valid packets
  191.         static char ppmChannelLast;                         // last number of channels
  192.         static char ppmChannelCount;                        // good packet counter
  193.  
  194.         if (ppmChannel != ppmChannelLast)
  195.         {
  196.             // number of channels has changed
  197.             ppmChannelLast = ppmChannel;                    // new number of channels
  198.             ppmChannelCount = IN_PPM_ADV_N_CHANNELS_AUTO;   // preset counter
  199.             ppmChannel = 0;                                 // do not use this packet
  200.         }
  201.         else
  202.         {
  203.             // number of channels is the same
  204.             if (ppmChannelCount)
  205.             {
  206.                 // but not N_CHANNELS_AUTO times yet, still counting
  207.                 ppmChannelCount--;                          // count as good
  208.                 ppmChannel = 0;                             // but do not use yet
  209.             }
  210.         }
  211.  
  212. #else
  213.         // do nothing, no special processing
  214. #endif
  215.  
  216.         // copy data into output buffer if there are new data
  217.         if (ppmChannel)
  218.         {
  219.             // use up to MAX_CHANNELS
  220.             if (ppmChannel > MAX_CHANNELS) ppmChannel = MAX_CHANNELS;
  221.  
  222.             // copy data up to ppmChannel channels
  223.             for (char i = 0; i < ppmChannel; i++)
  224.             {
  225.                 // ensure the atomic operation
  226.                 cli();
  227.                 channelData[i] = ppmData[i];
  228.                 sei();
  229.             }
  230.  
  231.             // set new data flag to non-zero (actually the number of channels)
  232.             newDataFound = ppmChannel;
  233.  
  234.             // prepare for next PPM data packet
  235.             ppmChannel = 0;
  236.         }
  237.  
  238.         // servo tester support
  239. #if IN_PPM_ADV_SERVO_TEST_CHANNEL
  240.         if (IN_PPM_ADV_SERVO_TEST_CHANNEL == 1)
  241.         {
  242.             // start of the first channel pulse found
  243.             ST_PORT_OUT |= (1<<ST_BIT);
  244.         }
  245. #endif
  246.     }
  247. }
  248.  
  249. #endif  // IN_PPM_ADV
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement