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"
26 #include "common/utils.h"
34 #include "pwm_mapping.h"
38 #define PPM_CAPTURE_COUNT 12
39 #define PWM_INPUT_PORT_COUNT 8
41 #if PPM_CAPTURE_COUNT > MAX_PWM_INPUT_PORTS
42 #define PWM_PORTS_OR_PPM_CAPTURE_COUNT PPM_CAPTURE_COUNT
44 #define PWM_PORTS_OR_PPM_CAPTURE_COUNT PWM_INPUT_PORT_COUNT
47 // TODO - change to timer cloks ticks
48 #define INPUT_FILTER_TO_HELP_WITH_NOISE_FROM_OPENLRS_TELEMETRY_RX 0x03
50 static inputFilteringMode_e inputFilteringMode
;
52 void pwmICConfig(TIM_TypeDef
*tim
, uint8_t channel
, uint16_t polarity
);
61 uint8_t channel
; // only used for pwm, ignored by ppm
64 captureCompare_t rise
;
65 captureCompare_t fall
;
66 captureCompare_t capture
;
70 const timerHardware_t
*timerHardware
;
71 timerCCHandlerRec_t edgeCb
;
72 timerOvrHandlerRec_t overflowCb
;
75 static pwmInputPort_t pwmInputPorts
[PWM_INPUT_PORT_COUNT
];
77 static uint16_t captures
[PWM_PORTS_OR_PPM_CAPTURE_COUNT
];
79 #define PPM_TIMER_PERIOD 0x10000
80 #define PWM_TIMER_PERIOD 0x10000
82 static uint8_t ppmFrameCount
= 0;
83 static uint8_t lastPPMFrameCount
= 0;
84 static uint8_t ppmCountShift
= 0;
86 typedef struct ppmDevice
{
88 uint32_t previousTime
;
91 uint32_t captures
[PWM_PORTS_OR_PPM_CAPTURE_COUNT
];
92 uint32_t largeCounter
;
94 int8_t numChannelsPrevFrame
;
95 uint8_t stableFramesSeenCount
;
103 #define PPM_IN_MIN_SYNC_PULSE_US 2700 // microseconds
104 #define PPM_IN_MIN_CHANNEL_PULSE_US 750 // microseconds
105 #define PPM_IN_MAX_CHANNEL_PULSE_US 2250 // microseconds
106 #define PPM_STABLE_FRAMES_REQUIRED_COUNT 25
107 #define PPM_IN_MIN_NUM_CHANNELS 4
108 #define PPM_IN_MAX_NUM_CHANNELS PWM_PORTS_OR_PPM_CAPTURE_COUNT
111 bool isPPMDataBeingReceived(void)
113 return (ppmFrameCount
!= lastPPMFrameCount
);
116 void resetPPMDataReceivedState(void)
118 lastPPMFrameCount
= ppmFrameCount
;
121 #define MIN_CHANNELS_BEFORE_PPM_FRAME_CONSIDERED_VALID 4
123 void pwmRxInit(inputFilteringMode_e initialInputFilteringMode
)
125 inputFilteringMode
= initialInputFilteringMode
;
128 static void ppmInit(void)
130 ppmDev
.pulseIndex
= 0;
131 ppmDev
.previousTime
= 0;
132 ppmDev
.currentTime
= 0;
133 ppmDev
.deltaTime
= 0;
134 ppmDev
.largeCounter
= 0;
135 ppmDev
.numChannels
= -1;
136 ppmDev
.numChannelsPrevFrame
= -1;
137 ppmDev
.stableFramesSeenCount
= 0;
138 ppmDev
.tracking
= false;
141 static void ppmOverflowCallback(timerOvrHandlerRec_t
* cbRec
, captureCompare_t capture
)
144 ppmDev
.largeCounter
+= capture
+ 1;
147 static void ppmEdgeCallback(timerCCHandlerRec_t
* cbRec
, captureCompare_t capture
)
152 /* Shift the last measurement out */
153 ppmDev
.previousTime
= ppmDev
.currentTime
;
155 /* Grab the new count */
156 ppmDev
.currentTime
= capture
;
158 /* Convert to 32-bit timer result */
159 ppmDev
.currentTime
+= ppmDev
.largeCounter
;
161 // Divide by 8 if Oneshot125 is active and this is a CC3D board
162 ppmDev
.currentTime
= ppmDev
.currentTime
>> ppmCountShift
;
164 /* Capture computation */
165 ppmDev
.deltaTime
= ppmDev
.currentTime
- ppmDev
.previousTime
;
167 ppmDev
.previousTime
= ppmDev
.currentTime
;
170 static uint32_t deltaTimes
[20];
171 static uint8_t deltaIndex
= 0;
173 deltaIndex
= (deltaIndex
+ 1) % 20;
174 deltaTimes
[deltaIndex
] = ppmDev
.deltaTime
;
177 /* Sync pulse detection */
178 if (ppmDev
.deltaTime
> PPM_IN_MIN_SYNC_PULSE_US
) {
179 if (ppmDev
.pulseIndex
== ppmDev
.numChannelsPrevFrame
180 && ppmDev
.pulseIndex
>= PPM_IN_MIN_NUM_CHANNELS
181 && ppmDev
.pulseIndex
<= PPM_IN_MAX_NUM_CHANNELS
) {
182 /* If we see n simultaneous frames of the same
183 number of channels we save it as our frame size */
184 if (ppmDev
.stableFramesSeenCount
< PPM_STABLE_FRAMES_REQUIRED_COUNT
) {
185 ppmDev
.stableFramesSeenCount
++;
187 ppmDev
.numChannels
= ppmDev
.pulseIndex
;
190 ppmDev
.stableFramesSeenCount
= 0;
193 /* Check if the last frame was well formed */
194 if (ppmDev
.pulseIndex
== ppmDev
.numChannels
&& ppmDev
.tracking
) {
195 /* The last frame was well formed */
196 for (i
= 0; i
< ppmDev
.numChannels
; i
++) {
197 captures
[i
] = ppmDev
.captures
[i
];
199 for (i
= ppmDev
.numChannels
; i
< PPM_IN_MAX_NUM_CHANNELS
; i
++) {
200 captures
[i
] = PPM_RCVR_TIMEOUT
;
205 ppmDev
.tracking
= true;
206 ppmDev
.numChannelsPrevFrame
= ppmDev
.pulseIndex
;
207 ppmDev
.pulseIndex
= 0;
209 /* We rely on the supervisor to set captureValue to invalid
210 if no valid frame is found otherwise we ride over it */
211 } else if (ppmDev
.tracking
) {
212 /* Valid pulse duration 0.75 to 2.5 ms*/
213 if (ppmDev
.deltaTime
> PPM_IN_MIN_CHANNEL_PULSE_US
214 && ppmDev
.deltaTime
< PPM_IN_MAX_CHANNEL_PULSE_US
215 && ppmDev
.pulseIndex
< PPM_IN_MAX_NUM_CHANNELS
) {
216 ppmDev
.captures
[ppmDev
.pulseIndex
] = ppmDev
.deltaTime
;
219 /* Not a valid pulse duration */
220 ppmDev
.tracking
= false;
221 for (i
= 0; i
< PWM_PORTS_OR_PPM_CAPTURE_COUNT
; i
++) {
222 ppmDev
.captures
[i
] = PPM_RCVR_TIMEOUT
;
228 #define MAX_MISSED_PWM_EVENTS 10
230 bool isPWMDataBeingReceived(void)
233 for (channel
= 0; channel
< PWM_PORTS_OR_PPM_CAPTURE_COUNT
; channel
++) {
234 if (captures
[channel
] != PPM_RCVR_TIMEOUT
) {
241 static void pwmOverflowCallback(timerOvrHandlerRec_t
* cbRec
, captureCompare_t capture
)
244 pwmInputPort_t
*pwmInputPort
= container_of(cbRec
, pwmInputPort_t
, overflowCb
);
246 if (++pwmInputPort
->missedEvents
> MAX_MISSED_PWM_EVENTS
) {
247 captures
[pwmInputPort
->channel
] = PPM_RCVR_TIMEOUT
;
248 pwmInputPort
->missedEvents
= 0;
252 static void pwmEdgeCallback(timerCCHandlerRec_t
*cbRec
, captureCompare_t capture
)
254 pwmInputPort_t
*pwmInputPort
= container_of(cbRec
, pwmInputPort_t
, edgeCb
);
255 const timerHardware_t
*timerHardwarePtr
= pwmInputPort
->timerHardware
;
257 if (pwmInputPort
->state
== 0) {
258 pwmInputPort
->rise
= capture
;
259 pwmInputPort
->state
= 1;
260 pwmICConfig(timerHardwarePtr
->tim
, timerHardwarePtr
->channel
, TIM_ICPolarity_Falling
);
262 pwmInputPort
->fall
= capture
;
264 // compute and store capture
265 pwmInputPort
->capture
= pwmInputPort
->fall
- pwmInputPort
->rise
;
266 captures
[pwmInputPort
->channel
] = pwmInputPort
->capture
;
269 pwmInputPort
->state
= 0;
270 pwmICConfig(timerHardwarePtr
->tim
, timerHardwarePtr
->channel
, TIM_ICPolarity_Rising
);
271 pwmInputPort
->missedEvents
= 0;
275 static void pwmGPIOConfig(GPIO_TypeDef
*gpio
, uint32_t pin
, GPIO_Mode mode
)
281 cfg
.speed
= Speed_2MHz
;
282 gpioInit(gpio
, &cfg
);
285 void pwmICConfig(TIM_TypeDef
*tim
, uint8_t channel
, uint16_t polarity
)
287 TIM_ICInitTypeDef TIM_ICInitStructure
;
289 TIM_ICStructInit(&TIM_ICInitStructure
);
290 TIM_ICInitStructure
.TIM_Channel
= channel
;
291 TIM_ICInitStructure
.TIM_ICPolarity
= polarity
;
292 TIM_ICInitStructure
.TIM_ICSelection
= TIM_ICSelection_DirectTI
;
293 TIM_ICInitStructure
.TIM_ICPrescaler
= TIM_ICPSC_DIV1
;
295 if (inputFilteringMode
== INPUT_FILTERING_ENABLED
) {
296 TIM_ICInitStructure
.TIM_ICFilter
= INPUT_FILTER_TO_HELP_WITH_NOISE_FROM_OPENLRS_TELEMETRY_RX
;
298 TIM_ICInitStructure
.TIM_ICFilter
= 0x00;
301 TIM_ICInit(tim
, &TIM_ICInitStructure
);
304 void pwmInConfig(const timerHardware_t
*timerHardwarePtr
, uint8_t channel
)
306 pwmInputPort_t
*self
= &pwmInputPorts
[channel
];
309 self
->missedEvents
= 0;
310 self
->channel
= channel
;
311 self
->mode
= INPUT_MODE_PWM
;
312 self
->timerHardware
= timerHardwarePtr
;
314 pwmGPIOConfig(timerHardwarePtr
->gpio
, timerHardwarePtr
->pin
, timerHardwarePtr
->gpioInputMode
);
315 pwmICConfig(timerHardwarePtr
->tim
, timerHardwarePtr
->channel
, TIM_ICPolarity_Rising
);
317 timerConfigure(timerHardwarePtr
, (uint16_t)PWM_TIMER_PERIOD
, PWM_TIMER_MHZ
);
319 timerChCCHandlerInit(&self
->edgeCb
, pwmEdgeCallback
);
320 timerChOvrHandlerInit(&self
->overflowCb
, pwmOverflowCallback
);
321 timerChConfigCallbacks(timerHardwarePtr
, &self
->edgeCb
, &self
->overflowCb
);
324 #define UNUSED_PPM_TIMER_REFERENCE 0
325 #define FIRST_PWM_PORT 0
327 void ppmAvoidPWMTimerClash(const timerHardware_t
*timerHardwarePtr
, TIM_TypeDef
*sharedPwmTimer
)
329 if (timerHardwarePtr
->tim
== sharedPwmTimer
) {
330 ppmCountShift
= 3; // Divide by 8 if the timer is running at 8 MHz
334 void ppmInConfig(const timerHardware_t
*timerHardwarePtr
)
338 pwmInputPort_t
*self
= &pwmInputPorts
[FIRST_PWM_PORT
];
340 self
->mode
= INPUT_MODE_PPM
;
341 self
->timerHardware
= timerHardwarePtr
;
343 pwmGPIOConfig(timerHardwarePtr
->gpio
, timerHardwarePtr
->pin
, timerHardwarePtr
->gpioInputMode
);
344 pwmICConfig(timerHardwarePtr
->tim
, timerHardwarePtr
->channel
, TIM_ICPolarity_Rising
);
346 timerConfigure(timerHardwarePtr
, (uint16_t)PPM_TIMER_PERIOD
, PWM_TIMER_MHZ
);
348 timerChCCHandlerInit(&self
->edgeCb
, ppmEdgeCallback
);
349 timerChOvrHandlerInit(&self
->overflowCb
, ppmOverflowCallback
);
350 timerChConfigCallbacks(timerHardwarePtr
, &self
->edgeCb
, &self
->overflowCb
);
353 uint16_t ppmRead(uint8_t channel
)
355 return captures
[channel
];
358 uint16_t pwmRead(uint8_t channel
)
360 return captures
[channel
];