2 ******************************************************************************
3 * @addtogroup PIOS PIOS Core hardware abstraction layer
5 * @addtogroup PIOS_PPM PPM Input Functions
6 * @brief Code to measure PPM input and seperate into channels
10 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2016.
11 * The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
12 * @brief PPM Input functions (STM32 dependent)
13 * @see The GNU Public License (GPL) Version 3
15 *****************************************************************************/
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 3 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
27 * You should have received a copy of the GNU General Public License along
28 * with this program; if not, write to the Free Software Foundation, Inc.,
29 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34 #ifdef PIOS_INCLUDE_PPM
36 #include "pios_ppm_priv.h"
38 /* Provide a RCVR driver */
39 static int32_t PIOS_PPM_Get(uint32_t rcvr_id
, uint8_t channel
);
40 static xSemaphoreHandle
PIOS_PPM_Get_Semaphore(uint32_t rcvr_id
, uint8_t channel
);
42 const struct pios_rcvr_driver pios_ppm_rcvr_driver
= {
44 #if defined(PIOS_INCLUDE_FREERTOS)
45 .get_semaphore
= PIOS_PPM_Get_Semaphore
49 #define PIOS_PPM_IN_MIN_NUM_CHANNELS 4
50 #define PIOS_PPM_IN_MAX_NUM_CHANNELS PIOS_PPM_NUM_INPUTS
51 #define PIOS_PPM_STABLE_CHANNEL_COUNT 25 // frames
52 #define PIOS_PPM_IN_MIN_SYNC_PULSE_US 2700 // microseconds
53 #define PIOS_PPM_IN_MIN_CHANNEL_PULSE_US 750 // microseconds
54 #define PIOS_PPM_IN_MAX_CHANNEL_PULSE_US 2250 // microseconds
56 static void PIOS_PPM_Supervisor(uint32_t ppm_id
);
58 enum pios_ppm_dev_magic
{
59 PIOS_PPM_DEV_MAGIC
= 0xee014d8b,
63 enum pios_ppm_dev_magic magic
;
64 const struct pios_ppm_cfg
*cfg
;
67 uint32_t PreviousTime
;
70 uint32_t CaptureValue
[PIOS_PPM_IN_MAX_NUM_CHANNELS
];
71 uint32_t CaptureValueNewFrame
[PIOS_PPM_IN_MAX_NUM_CHANNELS
];
72 uint32_t LargeCounter
;
74 int8_t NumChannelsPrevFrame
;
75 uint8_t NumChannelCounter
;
81 #ifdef PIOS_INCLUDE_FREERTOS
82 xSemaphoreHandle new_sample_semaphores
[PIOS_PPM_IN_MIN_NUM_CHANNELS
];
83 #endif /* PIOS_INCLUDE_FREERTOS */
86 static bool PIOS_PPM_validate(struct pios_ppm_dev
*ppm_dev
)
88 return ppm_dev
->magic
== PIOS_PPM_DEV_MAGIC
;
91 #if defined(PIOS_INCLUDE_FREERTOS)
92 static struct pios_ppm_dev
*PIOS_PPM_alloc(void)
94 struct pios_ppm_dev
*ppm_dev
;
96 ppm_dev
= (struct pios_ppm_dev
*)pios_malloc(sizeof(*ppm_dev
));
101 // Initialize the semaphores to 0.
102 for (uint8_t i
= 0; i
< PIOS_PPM_IN_MIN_NUM_CHANNELS
; ++i
) {
103 ppm_dev
->new_sample_semaphores
[i
] = 0;
106 ppm_dev
->magic
= PIOS_PPM_DEV_MAGIC
;
110 static struct pios_ppm_dev pios_ppm_devs
[PIOS_PPM_MAX_DEVS
];
111 static uint8_t pios_ppm_num_devs
;
112 static struct pios_ppm_dev
*PIOS_PPM_alloc(void)
114 struct pios_ppm_dev
*ppm_dev
;
116 if (pios_ppm_num_devs
>= PIOS_PPM_MAX_DEVS
) {
120 ppm_dev
= &pios_ppm_devs
[pios_ppm_num_devs
++];
121 ppm_dev
->magic
= PIOS_PPM_DEV_MAGIC
;
125 #endif /* if defined(PIOS_INCLUDE_FREERTOS) */
127 static void PIOS_PPM_tim_overflow_cb(uint32_t id
, uint32_t context
, uint8_t channel
, uint16_t count
);
128 static void PIOS_PPM_tim_edge_cb(uint32_t id
, uint32_t context
, uint8_t channel
, uint16_t count
);
129 static const struct pios_tim_callbacks tim_callbacks
= {
130 .overflow
= PIOS_PPM_tim_overflow_cb
,
131 .edge
= PIOS_PPM_tim_edge_cb
,
134 extern int32_t PIOS_PPM_Init(uint32_t *ppm_id
, const struct pios_ppm_cfg
*cfg
)
136 PIOS_DEBUG_Assert(ppm_id
);
137 PIOS_DEBUG_Assert(cfg
);
139 struct pios_ppm_dev
*ppm_dev
;
141 ppm_dev
= (struct pios_ppm_dev
*)PIOS_PPM_alloc();
146 /* Bind the configuration to the device instance */
149 /* Set up the state variables */
150 ppm_dev
->PulseIndex
= 0;
151 ppm_dev
->PreviousTime
= 0;
152 ppm_dev
->CurrentTime
= 0;
153 ppm_dev
->DeltaTime
= 0;
154 ppm_dev
->LargeCounter
= 0;
155 ppm_dev
->NumChannels
= -1;
156 ppm_dev
->NumChannelsPrevFrame
= -1;
157 ppm_dev
->NumChannelCounter
= 0;
158 ppm_dev
->Tracking
= false;
159 ppm_dev
->Fresh
= false;
161 for (uint8_t i
= 0; i
< PIOS_PPM_IN_MAX_NUM_CHANNELS
; i
++) {
162 /* Flush counter variables */
163 ppm_dev
->CaptureValue
[i
] = PIOS_RCVR_TIMEOUT
;
164 ppm_dev
->CaptureValueNewFrame
[i
] = PIOS_RCVR_TIMEOUT
;
168 if (PIOS_TIM_InitChannels(&tim_id
, cfg
->channels
, cfg
->num_channels
, &tim_callbacks
, (uint32_t)ppm_dev
)) {
172 TIM_ICInitTypeDef TIM_ICInitStructure
= cfg
->tim_ic_init
;
174 /* Configure the channels to be in capture/compare mode */
175 for (uint8_t i
= 0; i
< cfg
->num_channels
; i
++) {
176 const struct pios_tim_channel
*chan
= &cfg
->channels
[i
];
178 /* Configure timer for input capture */
179 TIM_ICInitStructure
.TIM_Channel
= chan
->timer_chan
;
180 TIM_ICInit(chan
->timer
, &TIM_ICInitStructure
);
182 /* Enable the Capture Compare Interrupt Request */
183 switch (chan
->timer_chan
) {
185 TIM_ITConfig(chan
->timer
, TIM_IT_CC1
| TIM_IT_Update
, ENABLE
);
188 TIM_ITConfig(chan
->timer
, TIM_IT_CC2
| TIM_IT_Update
, ENABLE
);
191 TIM_ITConfig(chan
->timer
, TIM_IT_CC3
| TIM_IT_Update
, ENABLE
);
194 TIM_ITConfig(chan
->timer
, TIM_IT_CC4
| TIM_IT_Update
, ENABLE
);
199 if (!PIOS_RTC_RegisterTickCallback(PIOS_PPM_Supervisor
, (uint32_t)ppm_dev
)) {
200 PIOS_DEBUG_Assert(0);
203 *ppm_id
= (uint32_t)ppm_dev
;
211 #if defined(PIOS_INCLUDE_FREERTOS)
212 static xSemaphoreHandle
PIOS_PPM_Get_Semaphore(uint32_t rcvr_id
, uint8_t channel
)
214 struct pios_ppm_dev
*ppm_dev
= (struct pios_ppm_dev
*)rcvr_id
;
216 if (!PIOS_PPM_validate(ppm_dev
)) {
217 /* Invalid device specified */
221 if (channel
>= PIOS_PPM_IN_MAX_NUM_CHANNELS
) {
222 /* Channel out of range */
226 if (ppm_dev
->new_sample_semaphores
[channel
] == 0) {
227 vSemaphoreCreateBinary(ppm_dev
->new_sample_semaphores
[channel
]);
229 return ppm_dev
->new_sample_semaphores
[channel
];
231 #endif /* if defined(PIOS_INCLUDE_FREERTOS) */
234 * Get the value of an input channel
235 * \param[in] channel Number of the channel desired (zero based)
236 * \output PIOS_RCVR_INVALID channel not available
237 * \output PIOS_RCVR_TIMEOUT failsafe condition or missing receiver
238 * \output >=0 channel value
240 static int32_t PIOS_PPM_Get(uint32_t rcvr_id
, uint8_t channel
)
242 struct pios_ppm_dev
*ppm_dev
= (struct pios_ppm_dev
*)rcvr_id
;
244 if (!PIOS_PPM_validate(ppm_dev
)) {
245 /* Invalid device specified */
246 return PIOS_RCVR_INVALID
;
249 if (channel
>= PIOS_PPM_IN_MAX_NUM_CHANNELS
) {
250 /* Channel out of range */
251 return PIOS_RCVR_INVALID
;
253 return ppm_dev
->CaptureValue
[channel
];
256 static void PIOS_PPM_tim_overflow_cb(__attribute__((unused
)) uint32_t tim_id
, uint32_t context
,
257 __attribute__((unused
)) uint8_t channel
, uint16_t count
)
259 struct pios_ppm_dev
*ppm_dev
= (struct pios_ppm_dev
*)context
;
261 if (!PIOS_PPM_validate(ppm_dev
)) {
262 /* Invalid device specified */
266 ppm_dev
->LargeCounter
+= count
;
270 static void PIOS_PPM_tim_edge_cb(__attribute__((unused
)) uint32_t tim_id
, uint32_t context
, uint8_t chan_idx
, uint16_t count
)
272 /* Recover our device context */
273 struct pios_ppm_dev
*ppm_dev
= (struct pios_ppm_dev
*)context
;
275 if (!PIOS_PPM_validate(ppm_dev
)) {
276 /* Invalid device specified */
280 if (chan_idx
>= ppm_dev
->cfg
->num_channels
) {
281 /* Channel out of range */
285 /* Shift the last measurement out */
286 ppm_dev
->PreviousTime
= ppm_dev
->CurrentTime
;
288 /* Grab the new count */
289 ppm_dev
->CurrentTime
= count
;
291 /* Convert to 32-bit timer result */
292 ppm_dev
->CurrentTime
+= ppm_dev
->LargeCounter
;
294 /* Capture computation */
295 ppm_dev
->DeltaTime
= ppm_dev
->CurrentTime
- ppm_dev
->PreviousTime
;
297 ppm_dev
->PreviousTime
= ppm_dev
->CurrentTime
;
299 /* Sync pulse detection */
300 if (ppm_dev
->DeltaTime
> PIOS_PPM_IN_MIN_SYNC_PULSE_US
) {
301 if (ppm_dev
->PulseIndex
== ppm_dev
->NumChannelsPrevFrame
302 && ppm_dev
->PulseIndex
>= PIOS_PPM_IN_MIN_NUM_CHANNELS
303 && ppm_dev
->PulseIndex
<= PIOS_PPM_IN_MAX_NUM_CHANNELS
) {
304 /* If we see n simultaneous frames of the same
305 number of channels we save it as our frame size */
306 if (ppm_dev
->NumChannelCounter
< PIOS_PPM_STABLE_CHANNEL_COUNT
) {
307 ppm_dev
->NumChannelCounter
++;
309 ppm_dev
->NumChannels
= ppm_dev
->PulseIndex
;
312 ppm_dev
->NumChannelCounter
= 0;
315 /* Check if the last frame was well formed */
316 if (ppm_dev
->PulseIndex
== ppm_dev
->NumChannels
&& ppm_dev
->Tracking
) {
317 /* The last frame was well formed */
318 for (int32_t i
= 0; i
< ppm_dev
->NumChannels
; i
++) {
319 ppm_dev
->CaptureValue
[i
] = ppm_dev
->CaptureValueNewFrame
[i
];
321 for (uint32_t i
= ppm_dev
->NumChannels
;
322 i
< PIOS_PPM_IN_MAX_NUM_CHANNELS
; i
++) {
323 ppm_dev
->CaptureValue
[i
] = PIOS_RCVR_TIMEOUT
;
325 #if defined(PIOS_INCLUDE_FREERTOS)
326 /* Signal that a new sample is ready on this channel. */
327 if (ppm_dev
->new_sample_semaphores
[chan_idx
] != 0) {
328 signed portBASE_TYPE pxHigherPriorityTaskWoken
= pdFALSE
;
329 if (xSemaphoreGiveFromISR(ppm_dev
->new_sample_semaphores
[chan_idx
], &pxHigherPriorityTaskWoken
) == pdTRUE
) {
330 portEND_SWITCHING_ISR(pxHigherPriorityTaskWoken
); /* FIXME: is this the right place for his? */
333 #endif /* USE_FREERTOS */
336 i
< PIOS_PPM_IN_MAX_NUM_CHANNELS
; i
++) {
337 ppm_dev
->CaptureValue
[i
] = PIOS_RCVR_TIMEOUT
;
341 ppm_dev
->Fresh
= true;
342 ppm_dev
->Tracking
= true;
343 ppm_dev
->NumChannelsPrevFrame
= ppm_dev
->PulseIndex
;
344 ppm_dev
->PulseIndex
= 0;
346 /* We rely on the supervisor to set CaptureValue to invalid
347 if no valid frame is found otherwise we ride over it */
348 } else if (ppm_dev
->Tracking
) {
349 /* Valid pulse duration 0.75 to 2.5 ms*/
350 if (ppm_dev
->DeltaTime
> PIOS_PPM_IN_MIN_CHANNEL_PULSE_US
351 && ppm_dev
->DeltaTime
< PIOS_PPM_IN_MAX_CHANNEL_PULSE_US
352 && ppm_dev
->PulseIndex
< PIOS_PPM_IN_MAX_NUM_CHANNELS
) {
353 ppm_dev
->CaptureValueNewFrame
[ppm_dev
->PulseIndex
] = ppm_dev
->DeltaTime
;
354 ppm_dev
->PulseIndex
++;
356 /* Not a valid pulse duration */
357 ppm_dev
->Tracking
= false;
358 for (uint32_t i
= 0; i
< PIOS_PPM_IN_MAX_NUM_CHANNELS
; i
++) {
359 ppm_dev
->CaptureValueNewFrame
[i
] = PIOS_RCVR_TIMEOUT
;
365 static void PIOS_PPM_Supervisor(uint32_t ppm_id
)
367 /* Recover our device context */
368 struct pios_ppm_dev
*ppm_dev
= (struct pios_ppm_dev
*)ppm_id
;
370 if (!PIOS_PPM_validate(ppm_dev
)) {
371 /* Invalid device specified */
376 * RTC runs at 625Hz so divide down the base rate so
377 * that this loop runs at 25Hz.
379 if (++(ppm_dev
->supv_timer
) < 25) {
382 ppm_dev
->supv_timer
= 0;
384 if (!ppm_dev
->Fresh
) {
385 ppm_dev
->Tracking
= false;
387 for (int32_t i
= 0; i
< PIOS_PPM_IN_MAX_NUM_CHANNELS
; i
++) {
388 ppm_dev
->CaptureValue
[i
] = PIOS_RCVR_TIMEOUT
;
389 ppm_dev
->CaptureValueNewFrame
[i
] = PIOS_RCVR_TIMEOUT
;
393 ppm_dev
->Fresh
= false;
396 #endif /* PIOS_INCLUDE_PPM */