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) 2010.
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_stm32.h>
38 #include "pios_ppm_priv.h"
40 /* Provide a RCVR driver */
41 static int32_t PIOS_PPM_Get(uint32_t rcvr_id
, uint8_t channel
);
42 static xSemaphoreHandle
PIOS_PPM_Get_Semaphore(uint32_t rcvr_id
, uint8_t channel
);
44 const struct pios_rcvr_driver pios_ppm_rcvr_driver
= {
46 #if defined(PIOS_INCLUDE_FREERTOS)
47 .get_semaphore
= PIOS_PPM_Get_Semaphore
51 #define PIOS_PPM_IN_MIN_NUM_CHANNELS 4
52 #define PIOS_PPM_IN_MAX_NUM_CHANNELS PIOS_PPM_NUM_INPUTS
53 #define PIOS_PPM_STABLE_CHANNEL_COUNT 25 // frames
54 #define PIOS_PPM_IN_MIN_SYNC_PULSE_US 2700 // microseconds
55 #define PIOS_PPM_IN_MIN_CHANNEL_PULSE_US 750 // microseconds
56 #define PIOS_PPM_IN_MAX_CHANNEL_PULSE_US 2250 // microseconds
59 // static TIM_ICInitTypeDef TIM_ICInitStructure;
61 static void PIOS_PPM_Supervisor(uint32_t ppm_id
);
63 enum pios_ppm_dev_magic
{
64 PIOS_PPM_DEV_MAGIC
= 0xee014d8b,
68 enum pios_ppm_dev_magic magic
;
69 const struct pios_ppm_cfg
*cfg
;
72 uint32_t PreviousTime
;
75 uint32_t CaptureValue
[PIOS_PPM_IN_MAX_NUM_CHANNELS
];
76 uint32_t CaptureValueNewFrame
[PIOS_PPM_IN_MAX_NUM_CHANNELS
];
77 uint32_t LargeCounter
;
79 int8_t NumChannelsPrevFrame
;
80 uint8_t NumChannelCounter
;
86 #ifdef PIOS_INCLUDE_FREERTOS
87 xSemaphoreHandle new_sample_semaphores
[PIOS_PPM_IN_MIN_NUM_CHANNELS
];
88 #endif /* PIOS_INCLUDE_FREERTOS */
91 static bool PIOS_PPM_validate(struct pios_ppm_dev
*ppm_dev
)
93 return ppm_dev
->magic
== PIOS_PPM_DEV_MAGIC
;
96 #if defined(PIOS_INCLUDE_FREERTOS)
97 static struct pios_ppm_dev
*PIOS_PPM_alloc(void)
99 struct pios_ppm_dev
*ppm_dev
;
101 ppm_dev
= (struct pios_ppm_dev
*)pios_malloc(sizeof(*ppm_dev
));
106 // Initialize the semaphores to 0.
107 for (uint8_t i
= 0; i
< PIOS_PPM_IN_MIN_NUM_CHANNELS
; ++i
) {
108 ppm_dev
->new_sample_semaphores
[i
] = 0;
111 ppm_dev
->magic
= PIOS_PPM_DEV_MAGIC
;
115 static struct pios_ppm_dev pios_ppm_devs
[PIOS_PPM_MAX_DEVS
];
116 static uint8_t pios_ppm_num_devs
;
117 static struct pios_ppm_dev
*PIOS_PPM_alloc(void)
119 struct pios_ppm_dev
*ppm_dev
;
121 if (pios_ppm_num_devs
>= PIOS_PPM_MAX_DEVS
) {
125 ppm_dev
= &pios_ppm_devs
[pios_ppm_num_devs
++];
126 ppm_dev
->magic
= PIOS_PPM_DEV_MAGIC
;
130 #endif /* if defined(PIOS_INCLUDE_FREERTOS) */
132 static void PIOS_PPM_tim_overflow_cb(uint32_t id
, uint32_t context
, uint8_t channel
, uint16_t count
);
133 static void PIOS_PPM_tim_edge_cb(uint32_t id
, uint32_t context
, uint8_t channel
, uint16_t count
);
134 static const struct pios_tim_callbacks tim_callbacks
= {
135 .overflow
= PIOS_PPM_tim_overflow_cb
,
136 .edge
= PIOS_PPM_tim_edge_cb
,
139 extern int32_t PIOS_PPM_Init(uint32_t *ppm_id
, const struct pios_ppm_cfg
*cfg
)
141 PIOS_DEBUG_Assert(ppm_id
);
142 PIOS_DEBUG_Assert(cfg
);
144 struct pios_ppm_dev
*ppm_dev
;
146 ppm_dev
= (struct pios_ppm_dev
*)PIOS_PPM_alloc();
151 /* Bind the configuration to the device instance */
154 /* Set up the state variables */
155 ppm_dev
->PulseIndex
= 0;
156 ppm_dev
->PreviousTime
= 0;
157 ppm_dev
->CurrentTime
= 0;
158 ppm_dev
->DeltaTime
= 0;
159 ppm_dev
->LargeCounter
= 0;
160 ppm_dev
->NumChannels
= -1;
161 ppm_dev
->NumChannelsPrevFrame
= -1;
162 ppm_dev
->NumChannelCounter
= 0;
163 ppm_dev
->Tracking
= FALSE
;
164 ppm_dev
->Fresh
= FALSE
;
166 for (uint8_t i
= 0; i
< PIOS_PPM_IN_MAX_NUM_CHANNELS
; i
++) {
167 /* Flush counter variables */
168 ppm_dev
->CaptureValue
[i
] = PIOS_RCVR_TIMEOUT
;
169 ppm_dev
->CaptureValueNewFrame
[i
] = PIOS_RCVR_TIMEOUT
;
173 if (PIOS_TIM_InitChannels(&tim_id
, cfg
->channels
, cfg
->num_channels
, &tim_callbacks
, (uint32_t)ppm_dev
)) {
177 TIM_ICInitTypeDef TIM_ICInitStructure
= cfg
->tim_ic_init
;
179 /* Configure the channels to be in capture/compare mode */
180 for (uint8_t i
= 0; i
< cfg
->num_channels
; i
++) {
181 const struct pios_tim_channel
*chan
= &cfg
->channels
[i
];
183 /* Configure timer for input capture */
184 TIM_ICInitStructure
.TIM_Channel
= chan
->timer_chan
;
185 TIM_ICInit(chan
->timer
, &TIM_ICInitStructure
);
187 /* Enable the Capture Compare Interrupt Request */
188 switch (chan
->timer_chan
) {
190 TIM_ITConfig(chan
->timer
, TIM_IT_CC1
| TIM_IT_Update
, ENABLE
);
193 TIM_ITConfig(chan
->timer
, TIM_IT_CC2
| TIM_IT_Update
, ENABLE
);
196 TIM_ITConfig(chan
->timer
, TIM_IT_CC3
| TIM_IT_Update
, ENABLE
);
199 TIM_ITConfig(chan
->timer
, TIM_IT_CC4
| TIM_IT_Update
, ENABLE
);
204 ppm_dev
->supv_timer
= 0;
205 if (!PIOS_RTC_RegisterTickCallback(PIOS_PPM_Supervisor
, (uint32_t)ppm_dev
)) {
206 PIOS_DEBUG_Assert(0);
209 *ppm_id
= (uint32_t)ppm_dev
;
217 #if defined(PIOS_INCLUDE_FREERTOS)
218 static xSemaphoreHandle
PIOS_PPM_Get_Semaphore(uint32_t rcvr_id
, uint8_t channel
)
220 struct pios_ppm_dev
*ppm_dev
= (struct pios_ppm_dev
*)rcvr_id
;
222 if (!PIOS_PPM_validate(ppm_dev
)) {
223 /* Invalid device specified */
227 if (channel
>= PIOS_PPM_IN_MAX_NUM_CHANNELS
) {
228 /* Channel out of range */
232 if (ppm_dev
->new_sample_semaphores
[channel
] == 0) {
233 vSemaphoreCreateBinary(ppm_dev
->new_sample_semaphores
[channel
]);
235 return ppm_dev
->new_sample_semaphores
[channel
];
237 #endif /* if defined(PIOS_INCLUDE_FREERTOS) */
240 * Get the value of an input channel
241 * \param[in] channel Number of the channel desired (zero based)
242 * \output PIOS_RCVR_INVALID channel not available
243 * \output PIOS_RCVR_TIMEOUT failsafe condition or missing receiver
244 * \output >=0 channel value
246 static int32_t PIOS_PPM_Get(uint32_t rcvr_id
, uint8_t channel
)
248 struct pios_ppm_dev
*ppm_dev
= (struct pios_ppm_dev
*)rcvr_id
;
250 if (!PIOS_PPM_validate(ppm_dev
)) {
251 /* Invalid device specified */
252 return PIOS_RCVR_INVALID
;
255 if (channel
>= PIOS_PPM_IN_MAX_NUM_CHANNELS
) {
256 /* Channel out of range */
257 return PIOS_RCVR_INVALID
;
259 return ppm_dev
->CaptureValue
[channel
];
262 static void PIOS_PPM_tim_overflow_cb(__attribute__((unused
)) uint32_t tim_id
,
264 __attribute__((unused
)) uint8_t channel
,
267 struct pios_ppm_dev
*ppm_dev
= (struct pios_ppm_dev
*)context
;
269 if (!PIOS_PPM_validate(ppm_dev
)) {
270 /* Invalid device specified */
274 ppm_dev
->LargeCounter
+= count
;
278 static void PIOS_PPM_tim_edge_cb(__attribute__((unused
)) uint32_t tim_id
,
283 /* Recover our device context */
284 struct pios_ppm_dev
*ppm_dev
= (struct pios_ppm_dev
*)context
;
286 if (!PIOS_PPM_validate(ppm_dev
)) {
287 /* Invalid device specified */
291 if (chan_idx
>= ppm_dev
->cfg
->num_channels
) {
292 /* Channel out of range */
296 /* Shift the last measurement out */
297 ppm_dev
->PreviousTime
= ppm_dev
->CurrentTime
;
299 /* Grab the new count */
300 ppm_dev
->CurrentTime
= count
;
302 /* Convert to 32-bit timer result */
303 ppm_dev
->CurrentTime
+= ppm_dev
->LargeCounter
;
305 /* Capture computation */
306 ppm_dev
->DeltaTime
= ppm_dev
->CurrentTime
- ppm_dev
->PreviousTime
;
308 ppm_dev
->PreviousTime
= ppm_dev
->CurrentTime
;
310 /* Sync pulse detection */
311 if (ppm_dev
->DeltaTime
> PIOS_PPM_IN_MIN_SYNC_PULSE_US
) {
312 if (ppm_dev
->PulseIndex
== ppm_dev
->NumChannelsPrevFrame
313 && ppm_dev
->PulseIndex
>= PIOS_PPM_IN_MIN_NUM_CHANNELS
314 && ppm_dev
->PulseIndex
<= PIOS_PPM_IN_MAX_NUM_CHANNELS
) {
315 /* If we see n simultaneous frames of the same
316 number of channels we save it as our frame size */
317 if (ppm_dev
->NumChannelCounter
< PIOS_PPM_STABLE_CHANNEL_COUNT
) {
318 ppm_dev
->NumChannelCounter
++;
320 ppm_dev
->NumChannels
= ppm_dev
->PulseIndex
;
323 ppm_dev
->NumChannelCounter
= 0;
326 /* Check if the last frame was well formed */
327 if (ppm_dev
->PulseIndex
== ppm_dev
->NumChannels
&& ppm_dev
->Tracking
) {
328 /* The last frame was well formed */
329 for (int32_t i
= 0; i
< ppm_dev
->NumChannels
; i
++) {
330 ppm_dev
->CaptureValue
[i
] = ppm_dev
->CaptureValueNewFrame
[i
];
332 for (uint32_t i
= ppm_dev
->NumChannels
;
333 i
< PIOS_PPM_IN_MAX_NUM_CHANNELS
; i
++) {
334 ppm_dev
->CaptureValue
[i
] = PIOS_RCVR_TIMEOUT
;
336 #if defined(PIOS_INCLUDE_FREERTOS)
337 /* Signal that a new sample is ready on this channel. */
338 if (ppm_dev
->new_sample_semaphores
[chan_idx
] != 0) {
339 signed portBASE_TYPE pxHigherPriorityTaskWoken
= pdFALSE
;
340 if (xSemaphoreGiveFromISR(ppm_dev
->new_sample_semaphores
[chan_idx
], &pxHigherPriorityTaskWoken
) == pdTRUE
) {
341 portEND_SWITCHING_ISR(pxHigherPriorityTaskWoken
); /* FIXME: is this the right place for this? */
344 #endif /* USE_FREERTOS */
347 i
< PIOS_PPM_IN_MAX_NUM_CHANNELS
; i
++) {
348 ppm_dev
->CaptureValue
[i
] = PIOS_RCVR_TIMEOUT
;
352 ppm_dev
->Fresh
= TRUE
;
353 ppm_dev
->Tracking
= TRUE
;
354 ppm_dev
->NumChannelsPrevFrame
= ppm_dev
->PulseIndex
;
355 ppm_dev
->PulseIndex
= 0;
357 /* We rely on the supervisor to set CaptureValue to invalid
358 if no valid frame is found otherwise we ride over it */
359 } else if (ppm_dev
->Tracking
) {
360 /* Valid pulse duration 0.75 to 2.5 ms*/
361 if (ppm_dev
->DeltaTime
> PIOS_PPM_IN_MIN_CHANNEL_PULSE_US
362 && ppm_dev
->DeltaTime
< PIOS_PPM_IN_MAX_CHANNEL_PULSE_US
363 && ppm_dev
->PulseIndex
< PIOS_PPM_IN_MAX_NUM_CHANNELS
) {
364 ppm_dev
->CaptureValueNewFrame
[ppm_dev
->PulseIndex
] = ppm_dev
->DeltaTime
;
365 ppm_dev
->PulseIndex
++;
367 /* Not a valid pulse duration */
368 ppm_dev
->Tracking
= FALSE
;
369 for (uint32_t i
= 0; i
< PIOS_PPM_IN_MAX_NUM_CHANNELS
; i
++) {
370 ppm_dev
->CaptureValueNewFrame
[i
] = PIOS_RCVR_TIMEOUT
;
376 static void PIOS_PPM_Supervisor(uint32_t ppm_id
)
378 /* Recover our device context */
379 struct pios_ppm_dev
*ppm_dev
= (struct pios_ppm_dev
*)ppm_id
;
381 if (!PIOS_PPM_validate(ppm_dev
)) {
382 /* Invalid device specified */
387 * RTC runs at 625Hz so divide down the base rate so
388 * that this loop runs at 25Hz.
390 if (++(ppm_dev
->supv_timer
) < 25) {
393 ppm_dev
->supv_timer
= 0;
395 if (!ppm_dev
->Fresh
) {
396 ppm_dev
->Tracking
= FALSE
;
398 for (int32_t i
= 0; i
< PIOS_PPM_IN_MAX_NUM_CHANNELS
; i
++) {
399 ppm_dev
->CaptureValue
[i
] = PIOS_RCVR_TIMEOUT
;
400 ppm_dev
->CaptureValueNewFrame
[i
] = PIOS_RCVR_TIMEOUT
;
404 ppm_dev
->Fresh
= FALSE
;
407 #endif /* PIOS_INCLUDE_PPM */