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/>.
24 #include "build_config.h"
27 #include "common/utils.h"
35 #include "pwm_mapping.h"
41 #define PPM_CAPTURE_COUNT 12
42 #define PWM_INPUT_PORT_COUNT 8
44 #if PPM_CAPTURE_COUNT > PWM_INPUT_PORT_COUNT
45 #define PWM_PORTS_OR_PPM_CAPTURE_COUNT PPM_CAPTURE_COUNT
47 #define PWM_PORTS_OR_PPM_CAPTURE_COUNT PWM_INPUT_PORT_COUNT
50 // TODO - change to timer clocks ticks
51 #define INPUT_FILTER_TO_HELP_WITH_NOISE_FROM_OPENLRS_TELEMETRY_RX 0x03
53 pwmRxConfig_t pwmRxConfig
;
55 void pwmICConfig(TIM_TypeDef
*tim
, uint8_t channel
, uint16_t polarity
);
64 uint8_t channel
; // only used for pwm, ignored by ppm
67 captureCompare_t rise
;
68 captureCompare_t fall
;
69 captureCompare_t capture
;
73 const timerHardware_t
*timerHardware
;
74 timerCCHandlerRec_t edgeCb
;
75 timerOvrHandlerRec_t overflowCb
;
78 static pwmInputPort_t pwmInputPorts
[PWM_INPUT_PORT_COUNT
];
80 static uint16_t captures
[PWM_PORTS_OR_PPM_CAPTURE_COUNT
];
82 #define PPM_TIMER_PERIOD 0x10000
83 #define PWM_TIMER_PERIOD 0x10000
85 static uint8_t ppmFrameCount
= 0;
86 static uint8_t lastPPMFrameCount
= 0;
87 static uint8_t ppmCountShift
= 0;
89 typedef struct ppmDevice_s
{
91 //uint32_t previousTime;
92 uint32_t currentCapture
;
95 uint32_t captures
[PWM_PORTS_OR_PPM_CAPTURE_COUNT
];
96 uint32_t largeCounter
;
98 int8_t numChannelsPrevFrame
;
99 uint8_t stableFramesSeenCount
;
108 #define PPM_IN_MIN_SYNC_PULSE_US 2700 // microseconds
109 #define PPM_IN_MIN_CHANNEL_PULSE_US 750 // microseconds
110 #define PPM_IN_MAX_CHANNEL_PULSE_US 2250 // microseconds
111 #define PPM_STABLE_FRAMES_REQUIRED_COUNT 25
112 #define PPM_IN_MIN_NUM_CHANNELS 4
113 #define PPM_IN_MAX_NUM_CHANNELS PWM_PORTS_OR_PPM_CAPTURE_COUNT
116 bool isPPMDataBeingReceived(void)
118 return (ppmFrameCount
!= lastPPMFrameCount
);
121 void resetPPMDataReceivedState(void)
123 lastPPMFrameCount
= ppmFrameCount
;
126 #define MIN_CHANNELS_BEFORE_PPM_FRAME_CONSIDERED_VALID 4
138 typedef struct ppmISREvent_s
{
139 eventSource_e source
;
143 static ppmISREvent_t ppmEvents
[20];
144 static uint8_t ppmEventIndex
= 0;
146 void ppmISREvent(eventSource_e source
, uint32_t capture
)
148 ppmEventIndex
= (ppmEventIndex
+ 1) % (sizeof(ppmEvents
) / sizeof(ppmEvents
[0]));
150 ppmEvents
[ppmEventIndex
].source
= source
;
151 ppmEvents
[ppmEventIndex
].capture
= capture
;
154 void ppmISREvent(eventSource_e source
, uint32_t capture
) {}
157 static void ppmInit(void)
159 ppmDev
.pulseIndex
= 0;
160 ppmDev
.currentCapture
= 0;
161 ppmDev
.currentTime
= 0;
162 ppmDev
.deltaTime
= 0;
163 ppmDev
.largeCounter
= 0;
164 ppmDev
.numChannels
= -1;
165 ppmDev
.numChannelsPrevFrame
= -1;
166 ppmDev
.stableFramesSeenCount
= 0;
167 ppmDev
.tracking
= false;
168 ppmDev
.overflowed
= false;
171 static void ppmOverflowCallback(timerOvrHandlerRec_t
* cbRec
, captureCompare_t capture
)
174 ppmISREvent(SOURCE_OVERFLOW
, capture
);
176 ppmDev
.largeCounter
+= capture
+ 1;
177 if (capture
== PPM_TIMER_PERIOD
- 1) {
178 ppmDev
.overflowed
= true;
183 static void ppmEdgeCallback(timerCCHandlerRec_t
* cbRec
, captureCompare_t capture
)
186 ppmISREvent(SOURCE_EDGE
, capture
);
190 uint32_t previousTime
= ppmDev
.currentTime
;
191 uint32_t previousCapture
= ppmDev
.currentCapture
;
193 /* Grab the new count */
194 uint32_t currentTime
= capture
;
196 /* Convert to 32-bit timer result */
197 currentTime
+= ppmDev
.largeCounter
;
199 if (capture
< previousCapture
) {
200 if (ppmDev
.overflowed
) {
201 currentTime
+= PPM_TIMER_PERIOD
;
205 // Divide by 8 if Oneshot125 is active and this is a CC3D board
206 currentTime
= currentTime
>> ppmCountShift
;
208 /* Capture computation */
209 if (currentTime
> previousTime
) {
210 ppmDev
.deltaTime
= currentTime
- (previousTime
+ (ppmDev
.overflowed
? (PPM_TIMER_PERIOD
>> ppmCountShift
) : 0));
212 ppmDev
.deltaTime
= (PPM_TIMER_PERIOD
>> ppmCountShift
) + currentTime
- previousTime
;
215 ppmDev
.overflowed
= false;
218 /* Store the current measurement */
219 ppmDev
.currentTime
= currentTime
;
220 ppmDev
.currentCapture
= capture
;
223 static uint32_t deltaTimes
[20];
224 static uint8_t deltaIndex
= 0;
226 deltaIndex
= (deltaIndex
+ 1) % 20;
227 deltaTimes
[deltaIndex
] = ppmDev
.deltaTime
;
233 static uint32_t captureTimes
[20];
234 static uint8_t captureIndex
= 0;
236 captureIndex
= (captureIndex
+ 1) % 20;
237 captureTimes
[captureIndex
] = capture
;
238 UNUSED(captureTimes
);
241 /* Sync pulse detection */
242 if (ppmDev
.deltaTime
> PPM_IN_MIN_SYNC_PULSE_US
) {
243 if (ppmDev
.pulseIndex
== ppmDev
.numChannelsPrevFrame
244 && ppmDev
.pulseIndex
>= PPM_IN_MIN_NUM_CHANNELS
245 && ppmDev
.pulseIndex
<= PPM_IN_MAX_NUM_CHANNELS
) {
246 /* If we see n simultaneous frames of the same
247 number of channels we save it as our frame size */
248 if (ppmDev
.stableFramesSeenCount
< PPM_STABLE_FRAMES_REQUIRED_COUNT
) {
249 ppmDev
.stableFramesSeenCount
++;
251 ppmDev
.numChannels
= ppmDev
.pulseIndex
;
255 ppmDev
.stableFramesSeenCount
= 0;
258 /* Check if the last frame was well formed */
259 if (ppmDev
.pulseIndex
== ppmDev
.numChannels
&& ppmDev
.tracking
) {
260 /* The last frame was well formed */
261 for (i
= 0; i
< ppmDev
.numChannels
; i
++) {
262 captures
[i
] = ppmDev
.captures
[i
];
264 for (i
= ppmDev
.numChannels
; i
< PPM_IN_MAX_NUM_CHANNELS
; i
++) {
265 captures
[i
] = PPM_RCVR_TIMEOUT
;
270 ppmDev
.tracking
= true;
271 ppmDev
.numChannelsPrevFrame
= ppmDev
.pulseIndex
;
272 ppmDev
.pulseIndex
= 0;
274 /* We rely on the supervisor to set captureValue to invalid
275 if no valid frame is found otherwise we ride over it */
276 } else if (ppmDev
.tracking
) {
277 /* Valid pulse duration 0.75 to 2.5 ms*/
278 if (ppmDev
.deltaTime
> PPM_IN_MIN_CHANNEL_PULSE_US
279 && ppmDev
.deltaTime
< PPM_IN_MAX_CHANNEL_PULSE_US
280 && ppmDev
.pulseIndex
< PPM_IN_MAX_NUM_CHANNELS
) {
281 ppmDev
.captures
[ppmDev
.pulseIndex
] = ppmDev
.deltaTime
;
284 /* Not a valid pulse duration */
285 ppmDev
.tracking
= false;
286 for (i
= 0; i
< PWM_PORTS_OR_PPM_CAPTURE_COUNT
; i
++) {
287 ppmDev
.captures
[i
] = PPM_RCVR_TIMEOUT
;
293 #define MAX_MISSED_PWM_EVENTS 10
295 bool isPWMDataBeingReceived(void)
298 for (channel
= 0; channel
< PWM_PORTS_OR_PPM_CAPTURE_COUNT
; channel
++) {
299 if (captures
[channel
] != PPM_RCVR_TIMEOUT
) {
306 static void pwmOverflowCallback(timerOvrHandlerRec_t
* cbRec
, captureCompare_t capture
)
309 pwmInputPort_t
*pwmInputPort
= container_of(cbRec
, pwmInputPort_t
, overflowCb
);
311 if (++pwmInputPort
->missedEvents
> MAX_MISSED_PWM_EVENTS
) {
312 captures
[pwmInputPort
->channel
] = PPM_RCVR_TIMEOUT
;
313 pwmInputPort
->missedEvents
= 0;
317 static void pwmEdgeCallback(timerCCHandlerRec_t
*cbRec
, captureCompare_t capture
)
319 pwmInputPort_t
*pwmInputPort
= container_of(cbRec
, pwmInputPort_t
, edgeCb
);
320 const timerHardware_t
*timerHardwarePtr
= pwmInputPort
->timerHardware
;
322 if (pwmInputPort
->state
== 0) {
323 pwmInputPort
->rise
= capture
;
324 pwmInputPort
->state
= 1;
325 pwmICConfig(timerHardwarePtr
->tim
, timerHardwarePtr
->channel
, TIM_ICPolarity_Falling
);
327 pwmInputPort
->fall
= capture
;
329 // compute and store capture
330 pwmInputPort
->capture
= pwmInputPort
->fall
- pwmInputPort
->rise
;
331 captures
[pwmInputPort
->channel
] = pwmInputPort
->capture
;
334 pwmInputPort
->state
= 0;
335 pwmICConfig(timerHardwarePtr
->tim
, timerHardwarePtr
->channel
, TIM_ICPolarity_Rising
);
336 pwmInputPort
->missedEvents
= 0;
340 static void pwmGPIOConfig(GPIO_TypeDef
*gpio
, uint32_t pin
, GPIO_Mode mode
)
346 cfg
.speed
= Speed_2MHz
;
347 gpioInit(gpio
, &cfg
);
350 void pwmICConfig(TIM_TypeDef
*tim
, uint8_t channel
, uint16_t polarity
)
352 TIM_ICInitTypeDef TIM_ICInitStructure
;
354 TIM_ICStructInit(&TIM_ICInitStructure
);
355 TIM_ICInitStructure
.TIM_Channel
= channel
;
356 TIM_ICInitStructure
.TIM_ICPolarity
= polarity
;
357 TIM_ICInitStructure
.TIM_ICSelection
= TIM_ICSelection_DirectTI
;
358 TIM_ICInitStructure
.TIM_ICPrescaler
= TIM_ICPSC_DIV1
;
360 if (pwmRxConfig
.inputFilteringMode
== INPUT_FILTERING_ENABLED
) {
361 TIM_ICInitStructure
.TIM_ICFilter
= INPUT_FILTER_TO_HELP_WITH_NOISE_FROM_OPENLRS_TELEMETRY_RX
;
363 TIM_ICInitStructure
.TIM_ICFilter
= 0x00;
366 TIM_ICInit(tim
, &TIM_ICInitStructure
);
369 void pwmInConfig(const timerHardware_t
*timerHardwarePtr
, uint8_t channel
)
371 pwmInputPort_t
*self
= &pwmInputPorts
[channel
];
374 self
->missedEvents
= 0;
375 self
->channel
= channel
;
376 self
->mode
= INPUT_MODE_PWM
;
377 self
->timerHardware
= timerHardwarePtr
;
379 pwmGPIOConfig(timerHardwarePtr
->gpio
, timerHardwarePtr
->pin
, timerHardwarePtr
->gpioInputMode
);
380 pwmICConfig(timerHardwarePtr
->tim
, timerHardwarePtr
->channel
, TIM_ICPolarity_Rising
);
382 timerConfigure(timerHardwarePtr
, (uint16_t)PWM_TIMER_PERIOD
, PWM_TIMER_MHZ
);
384 timerChCCHandlerInit(&self
->edgeCb
, pwmEdgeCallback
);
385 timerChOvrHandlerInit(&self
->overflowCb
, pwmOverflowCallback
);
386 timerChConfigCallbacks(timerHardwarePtr
, &self
->edgeCb
, &self
->overflowCb
);
389 #define UNUSED_PPM_TIMER_REFERENCE 0
390 #define FIRST_PWM_PORT 0
392 void ppmAvoidPWMTimerClash(const timerHardware_t
*timerHardwarePtr
, TIM_TypeDef
*sharedPwmTimer
)
394 if (timerHardwarePtr
->tim
== sharedPwmTimer
) {
395 ppmCountShift
= 3; // Divide by 8 if the timer is running at 8 MHz
399 void ppmInConfig(const timerHardware_t
*timerHardwarePtr
)
403 pwmInputPort_t
*self
= &pwmInputPorts
[FIRST_PWM_PORT
];
405 self
->mode
= INPUT_MODE_PPM
;
406 self
->timerHardware
= timerHardwarePtr
;
408 pwmGPIOConfig(timerHardwarePtr
->gpio
, timerHardwarePtr
->pin
, timerHardwarePtr
->gpioInputMode
);
409 pwmICConfig(timerHardwarePtr
->tim
, timerHardwarePtr
->channel
, TIM_ICPolarity_Rising
);
411 timerConfigure(timerHardwarePtr
, (uint16_t)PPM_TIMER_PERIOD
, PWM_TIMER_MHZ
);
413 timerChCCHandlerInit(&self
->edgeCb
, ppmEdgeCallback
);
414 timerChOvrHandlerInit(&self
->overflowCb
, ppmOverflowCallback
);
415 timerChConfigCallbacks(timerHardwarePtr
, &self
->edgeCb
, &self
->overflowCb
);
418 uint16_t ppmRead(uint8_t channel
)
420 return captures
[channel
];
423 uint16_t pwmRead(uint8_t channel
)
425 return captures
[channel
];