2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
23 #if defined(USE_RX_PPM)
25 #include "build/build_config.h"
26 #include "build/debug.h"
28 #include "common/utils.h"
30 #include "drivers/time.h"
32 #include "drivers/nvic.h"
33 #include "drivers/io.h"
36 #include "pwm_output.h"
37 #include "pwm_mapping.h"
41 #define PPM_CAPTURE_COUNT 16
42 #define INPUT_FILTER_TICKS 10
43 #define PPM_TIMER_PERIOD 0x10000
45 void pwmICConfig(TIM_TypeDef
*tim
, uint8_t channel
, uint16_t polarity
);
47 static uint16_t captures
[PPM_CAPTURE_COUNT
];
49 static uint8_t ppmFrameCount
= 0;
50 static uint8_t lastPPMFrameCount
= 0;
51 static uint8_t ppmCountDivisor
= 1;
53 typedef struct ppmDevice_s
{
55 uint32_t currentCapture
;
58 uint32_t captures
[PPM_CAPTURE_COUNT
];
59 uint32_t largeCounter
;
61 int8_t numChannelsPrevFrame
;
62 uint8_t stableFramesSeenCount
;
70 #define PPM_IN_MIN_SYNC_PULSE_US 2700 // microseconds
71 #define PPM_IN_MIN_CHANNEL_PULSE_US 750 // microseconds
72 #define PPM_IN_MAX_CHANNEL_PULSE_US 2250 // microseconds
73 #define PPM_STABLE_FRAMES_REQUIRED_COUNT 25
74 #define PPM_IN_MIN_NUM_CHANNELS 4
75 #define PPM_IN_MAX_NUM_CHANNELS PPM_CAPTURE_COUNT
77 bool isPPMDataBeingReceived(void)
79 return (ppmFrameCount
!= lastPPMFrameCount
);
82 void resetPPMDataReceivedState(void)
84 lastPPMFrameCount
= ppmFrameCount
;
87 #define MIN_CHANNELS_BEFORE_PPM_FRAME_CONSIDERED_VALID 4
89 static void ppmInit(void)
91 ppmDev
.pulseIndex
= 0;
92 ppmDev
.currentCapture
= 0;
93 ppmDev
.currentTime
= 0;
95 ppmDev
.largeCounter
= 0;
96 ppmDev
.numChannels
= -1;
97 ppmDev
.numChannelsPrevFrame
= -1;
98 ppmDev
.stableFramesSeenCount
= 0;
99 ppmDev
.tracking
= false;
100 ppmDev
.overflowed
= false;
103 static void ppmOverflowCallback(struct TCH_s
* tch
, uint32_t capture
)
107 ppmDev
.largeCounter
+= capture
+ 1;
108 if (capture
== PPM_TIMER_PERIOD
- 1) {
109 ppmDev
.overflowed
= true;
113 static void ppmEdgeCallback(struct TCH_s
* tch
, uint32_t capture
)
119 uint32_t previousTime
= ppmDev
.currentTime
;
120 uint32_t previousCapture
= ppmDev
.currentCapture
;
122 /* Grab the new count */
123 uint32_t currentTime
= capture
;
125 /* Convert to 32-bit timer result */
126 currentTime
+= ppmDev
.largeCounter
;
128 if (capture
< previousCapture
) {
129 if (ppmDev
.overflowed
) {
130 currentTime
+= PPM_TIMER_PERIOD
;
134 // Divide to match output protocol
135 currentTime
= currentTime
/ ppmCountDivisor
;
137 /* Capture computation */
138 if (currentTime
> previousTime
) {
139 ppmDev
.deltaTime
= currentTime
- (previousTime
+ (ppmDev
.overflowed
? (PPM_TIMER_PERIOD
/ ppmCountDivisor
) : 0));
141 ppmDev
.deltaTime
= (PPM_TIMER_PERIOD
/ ppmCountDivisor
) + currentTime
- previousTime
;
144 ppmDev
.overflowed
= false;
147 /* Store the current measurement */
148 ppmDev
.currentTime
= currentTime
;
149 ppmDev
.currentCapture
= capture
;
152 static uint32_t deltaTimes
[20];
153 static uint8_t deltaIndex
= 0;
155 deltaIndex
= (deltaIndex
+ 1) % 20;
156 deltaTimes
[deltaIndex
] = ppmDev
.deltaTime
;
162 static uint32_t captureTimes
[20];
163 static uint8_t captureIndex
= 0;
165 captureIndex
= (captureIndex
+ 1) % 20;
166 captureTimes
[captureIndex
] = capture
;
167 UNUSED(captureTimes
);
170 /* Sync pulse detection */
171 if (ppmDev
.deltaTime
> PPM_IN_MIN_SYNC_PULSE_US
) {
172 if (ppmDev
.pulseIndex
== ppmDev
.numChannelsPrevFrame
173 && ppmDev
.pulseIndex
>= PPM_IN_MIN_NUM_CHANNELS
174 && ppmDev
.pulseIndex
<= PPM_IN_MAX_NUM_CHANNELS
) {
175 /* If we see n simultaneous frames of the same
176 number of channels we save it as our frame size */
177 if (ppmDev
.stableFramesSeenCount
< PPM_STABLE_FRAMES_REQUIRED_COUNT
) {
178 ppmDev
.stableFramesSeenCount
++;
180 ppmDev
.numChannels
= ppmDev
.pulseIndex
;
183 ppmDev
.stableFramesSeenCount
= 0;
186 /* Check if the last frame was well formed */
187 if (ppmDev
.pulseIndex
== ppmDev
.numChannels
&& ppmDev
.tracking
) {
188 /* The last frame was well formed */
189 for (i
= 0; i
< ppmDev
.numChannels
; i
++) {
190 captures
[i
] = ppmDev
.captures
[i
];
192 for (i
= ppmDev
.numChannels
; i
< PPM_IN_MAX_NUM_CHANNELS
; i
++) {
193 captures
[i
] = PPM_RCVR_TIMEOUT
;
198 ppmDev
.tracking
= true;
199 ppmDev
.numChannelsPrevFrame
= ppmDev
.pulseIndex
;
200 ppmDev
.pulseIndex
= 0;
202 /* We rely on the supervisor to set captureValue to invalid
203 if no valid frame is found otherwise we ride over it */
204 } else if (ppmDev
.tracking
) {
205 /* Valid pulse duration 0.75 to 2.5 ms*/
206 if (ppmDev
.deltaTime
> PPM_IN_MIN_CHANNEL_PULSE_US
207 && ppmDev
.deltaTime
< PPM_IN_MAX_CHANNEL_PULSE_US
208 && ppmDev
.pulseIndex
< PPM_IN_MAX_NUM_CHANNELS
) {
209 ppmDev
.captures
[ppmDev
.pulseIndex
] = ppmDev
.deltaTime
;
212 /* Not a valid pulse duration */
213 ppmDev
.tracking
= false;
214 for (i
= 0; i
< PPM_CAPTURE_COUNT
; i
++) {
215 ppmDev
.captures
[i
] = PPM_RCVR_TIMEOUT
;
221 bool ppmInConfig(const timerHardware_t
*timerHardwarePtr
)
223 static timerCallbacks_t callbacks
;
224 TCH_t
* tch
= timerGetTCH(timerHardwarePtr
);
231 IO_t io
= IOGetByTag(timerHardwarePtr
->tag
);
232 IOInit(io
, OWNER_PPMINPUT
, RESOURCE_INPUT
, 0);
233 IOConfigGPIOAF(io
, timerHardwarePtr
->ioMode
, timerHardwarePtr
->alternateFunction
);
235 timerConfigure(tch
, (uint16_t)PPM_TIMER_PERIOD
, PWM_TIMER_HZ
);
236 timerChInitCallbacks(&callbacks
, (void*)&ppmDev
, &ppmEdgeCallback
, &ppmOverflowCallback
);
237 timerChConfigCallbacks(tch
, &callbacks
);
238 timerChConfigIC(tch
, true, INPUT_FILTER_TICKS
);
239 timerChCaptureEnable(tch
);
244 uint16_t ppmRead(uint8_t channel
)
246 return captures
[channel
];