[FLYWOOF411] add board documentation
[inav/snaewe.git] / src / main / drivers / rx_pwm.c
blobc915e58d61a884741a6a37b1a4f7fc844f6918ee
1 /*
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/>.
18 #include <stdbool.h>
19 #include <stdint.h>
21 #include <platform.h>
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"
34 #include "timer.h"
36 #include "pwm_output.h"
37 #include "pwm_mapping.h"
39 #include "rx_pwm.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 {
54 uint8_t pulseIndex;
55 uint32_t currentCapture;
56 uint32_t currentTime;
57 uint32_t deltaTime;
58 uint32_t captures[PPM_CAPTURE_COUNT];
59 uint32_t largeCounter;
60 int8_t numChannels;
61 int8_t numChannelsPrevFrame;
62 uint8_t stableFramesSeenCount;
64 bool tracking;
65 bool overflowed;
66 } ppmDevice_t;
68 ppmDevice_t ppmDev;
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;
94 ppmDev.deltaTime = 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)
105 UNUSED(tch);
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)
115 UNUSED(tch);
117 int32_t i;
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));
140 } else {
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;
151 #if 0
152 static uint32_t deltaTimes[20];
153 static uint8_t deltaIndex = 0;
155 deltaIndex = (deltaIndex + 1) % 20;
156 deltaTimes[deltaIndex] = ppmDev.deltaTime;
157 UNUSED(deltaTimes);
158 #endif
161 #if 0
162 static uint32_t captureTimes[20];
163 static uint8_t captureIndex = 0;
165 captureIndex = (captureIndex + 1) % 20;
166 captureTimes[captureIndex] = capture;
167 UNUSED(captureTimes);
168 #endif
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++;
179 } else {
180 ppmDev.numChannels = ppmDev.pulseIndex;
182 } else {
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;
195 ppmFrameCount++;
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;
210 ppmDev.pulseIndex++;
211 } else {
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);
225 if (tch == NULL) {
226 return false;
229 ppmInit();
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);
241 return true;
244 uint16_t ppmRead(uint8_t channel)
246 return captures[channel];
248 #endif