dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pios_ppm.c
Go to the documentation of this file.
1 
16 /*
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.
21  *
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
25  * for more details.
26  *
27  * You should have received a copy of the GNU General Public License along
28  * with this program; if not, see <http://www.gnu.org/licenses/>
29  */
30 
31 /* Project Includes */
32 #include "pios.h"
33 #include "pios_ppm_priv.h"
34 
35 #if defined(PIOS_INCLUDE_PPM)
36 
37 /* Provide a RCVR driver */
38 static int32_t PIOS_PPM_Get(uintptr_t rcvr_id, uint8_t channel);
39 
41  .read = PIOS_PPM_Get,
42 };
43 
44 #define PIOS_PPM_IN_MIN_NUM_CHANNELS 4
45 #define PIOS_PPM_IN_MAX_NUM_CHANNELS PIOS_PPM_NUM_INPUTS
46 #define PIOS_PPM_STABLE_CHANNEL_COUNT 10 // frames
47 #define PIOS_PPM_IN_MIN_SYNC_PULSE_US 3000 // microseconds
48 #define PIOS_PPM_IN_MIN_CHANNEL_PULSE_US 750 // microseconds
49 #define PIOS_PPM_IN_MAX_CHANNEL_PULSE_US 2250 // microseconds
50 
51 /* Local Variables */
52 static TIM_ICInitTypeDef TIM_ICInitStructure;
53 
54 static void PIOS_PPM_Supervisor(uintptr_t ppm_id);
55 
56 enum pios_ppm_dev_magic {
57  PIOS_PPM_DEV_MAGIC = 0xee014d8b,
58 };
59 
60 struct pios_ppm_dev {
61  enum pios_ppm_dev_magic magic;
62  const struct pios_ppm_cfg * cfg;
63 
64  uint8_t PulseIndex;
65  uint32_t PreviousTime;
66  uint32_t CurrentTime;
67  uint32_t DeltaTime;
68  uint32_t CaptureValue[PIOS_PPM_IN_MAX_NUM_CHANNELS];
69  uint32_t CaptureValueNewFrame[PIOS_PPM_IN_MAX_NUM_CHANNELS];
70  uint32_t LargeCounter;
71  int8_t NumChannels;
72  int8_t NumChannelsPrevFrame;
73  uint8_t NumChannelCounter;
74 
75  uint8_t supv_timer;
76  volatile bool Tracking;
77  volatile bool Fresh;
78 };
79 
80 static bool PIOS_PPM_validate(struct pios_ppm_dev * ppm_dev)
81 {
82  return (ppm_dev->magic == PIOS_PPM_DEV_MAGIC);
83 }
84 
85 static struct pios_ppm_dev * PIOS_PPM_alloc(void)
86 {
87  struct pios_ppm_dev * ppm_dev;
88 
89  ppm_dev = (struct pios_ppm_dev *)PIOS_malloc(sizeof(*ppm_dev));
90  if (!ppm_dev) return(NULL);
91 
92  ppm_dev->magic = PIOS_PPM_DEV_MAGIC;
93  return(ppm_dev);
94 }
95 
96 static void PIOS_PPM_tim_overflow_cb (uintptr_t id, uintptr_t context, uint8_t channel, uint16_t count);
97 static void PIOS_PPM_tim_edge_cb (uintptr_t id, uintptr_t context, uint8_t channel, uint16_t count);
98 const static struct pios_tim_callbacks tim_callbacks = {
99  .overflow = PIOS_PPM_tim_overflow_cb,
100  .edge = PIOS_PPM_tim_edge_cb,
101 };
102 
103 extern int32_t PIOS_PPM_Init(uintptr_t * ppm_id, const struct pios_ppm_cfg * cfg)
104 {
105  PIOS_DEBUG_Assert(ppm_id);
106  PIOS_DEBUG_Assert(cfg);
107 
108  struct pios_ppm_dev * ppm_dev;
109 
110  ppm_dev = (struct pios_ppm_dev *) PIOS_PPM_alloc();
111  if (!ppm_dev) goto out_fail;
112 
113  /* Bind the configuration to the device instance */
114  ppm_dev->cfg = cfg;
115 
116  /* Set up the state variables */
117  ppm_dev->PulseIndex = 0;
118  ppm_dev->PreviousTime = 0;
119  ppm_dev->CurrentTime = 0;
120  ppm_dev->DeltaTime = 0;
121  ppm_dev->LargeCounter = 0;
122  ppm_dev->NumChannels = -1;
123  ppm_dev->NumChannelsPrevFrame = -1;
124  ppm_dev->NumChannelCounter = 0;
125  ppm_dev->Tracking = false;
126  ppm_dev->Fresh = false;
127 
128  for (uint8_t i = 0; i < PIOS_PPM_IN_MAX_NUM_CHANNELS; i++) {
129  /* Flush counter variables */
130  ppm_dev->CaptureValue[i] = PIOS_RCVR_TIMEOUT;
131  ppm_dev->CaptureValueNewFrame[i] = PIOS_RCVR_TIMEOUT;
132 
133  }
134 
135  uintptr_t tim_id;
136  if (PIOS_TIM_InitChannels(&tim_id, cfg->channels, cfg->num_channels, &tim_callbacks, (uintptr_t)ppm_dev)) {
137  return -1;
138  }
139 
140  /* Configure the channels to be in capture/compare mode */
141  for (uint8_t i = 0; i < cfg->num_channels; i++) {
142  const struct pios_tim_channel * chan = &cfg->channels[i];
143 
144  /* Configure timer for input capture */
145  TIM_ICInitTypeDef TIM_ICInitStructure = cfg->tim_ic_init;
146  TIM_ICInitStructure.TIM_Channel = chan->timer_chan;
147  TIM_ICInit(chan->timer, &TIM_ICInitStructure);
148 
149  /* Enable the Capture Compare Interrupt Request */
150  switch (chan->timer_chan) {
151  case TIM_Channel_1:
152  TIM_ITConfig(chan->timer, TIM_IT_CC1 | TIM_IT_Update, ENABLE);
153  break;
154  case TIM_Channel_2:
155  TIM_ITConfig(chan->timer, TIM_IT_CC2 | TIM_IT_Update, ENABLE);
156  break;
157  case TIM_Channel_3:
158  TIM_ITConfig(chan->timer, TIM_IT_CC3 | TIM_IT_Update, ENABLE);
159  break;
160  case TIM_Channel_4:
161  TIM_ITConfig(chan->timer, TIM_IT_CC4 | TIM_IT_Update, ENABLE);
162  break;
163  }
164  }
165 
166  /* Setup local variable which stays in this scope */
167  /* Doing this here and using a local variable saves doing it in the ISR */
168  TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
169  TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
170  TIM_ICInitStructure.TIM_ICFilter = 0x0;
171 
172  if (!PIOS_RTC_RegisterTickCallback(PIOS_PPM_Supervisor, (uintptr_t)ppm_dev)) {
174  }
175 
176  *ppm_id = (uintptr_t)ppm_dev;
177 
178  return(0);
179 
180 out_fail:
181  return(-1);
182 }
183 
191 static int32_t PIOS_PPM_Get(uintptr_t rcvr_id, uint8_t channel)
192 {
193  struct pios_ppm_dev * ppm_dev = (struct pios_ppm_dev *)rcvr_id;
194 
195  if (!PIOS_PPM_validate(ppm_dev)) {
196  /* Invalid device specified */
197  return PIOS_RCVR_INVALID;
198  }
199 
200  if (channel >= PIOS_PPM_IN_MAX_NUM_CHANNELS) {
201  /* Channel out of range */
202  return PIOS_RCVR_INVALID;
203  }
204  return ppm_dev->CaptureValue[channel];
205 }
206 
207 static void PIOS_PPM_tim_overflow_cb (uintptr_t tim_id, uintptr_t context, uint8_t channel, uint16_t count)
208 {
209  struct pios_ppm_dev * ppm_dev = (struct pios_ppm_dev *)context;
210 
211  if (!PIOS_PPM_validate(ppm_dev)) {
212  /* Invalid device specified */
213  return;
214  }
215 
216  ppm_dev->LargeCounter += count;
217 
218  return;
219 }
220 
221 
222 static void PIOS_PPM_tim_edge_cb (uintptr_t tim_id, uintptr_t context, uint8_t chan_idx, uint16_t count)
223 {
224  /* Recover our device context */
225  struct pios_ppm_dev * ppm_dev = (struct pios_ppm_dev *)context;
226 
227  if (!PIOS_PPM_validate(ppm_dev)) {
228  /* Invalid device specified */
229  return;
230  }
231 
232  if (chan_idx >= ppm_dev->cfg->num_channels) {
233  /* Channel out of range */
234  return;
235  }
236 
237  /* Shift the last measurement out */
238  ppm_dev->PreviousTime = ppm_dev->CurrentTime;
239 
240  /* Grab the new count */
241  ppm_dev->CurrentTime = count;
242 
243  /* Convert to 32-bit timer result */
244  ppm_dev->CurrentTime += ppm_dev->LargeCounter;
245 
246  /* Capture computation */
247  ppm_dev->DeltaTime = ppm_dev->CurrentTime - ppm_dev->PreviousTime;
248 
249  ppm_dev->PreviousTime = ppm_dev->CurrentTime;
250 
251  /* Sync pulse detection */
252  if (ppm_dev->DeltaTime > PIOS_PPM_IN_MIN_SYNC_PULSE_US) {
253  if (ppm_dev->PulseIndex == ppm_dev->NumChannelsPrevFrame
254  && ppm_dev->PulseIndex >= PIOS_PPM_IN_MIN_NUM_CHANNELS
255  && ppm_dev->PulseIndex <= PIOS_PPM_IN_MAX_NUM_CHANNELS)
256  {
257  /* If we see n simultaneous frames of the same
258  number of channels we save it as our frame size */
259  if (ppm_dev->NumChannelCounter < PIOS_PPM_STABLE_CHANNEL_COUNT)
260  ppm_dev->NumChannelCounter++;
261  else
262  ppm_dev->NumChannels = ppm_dev->PulseIndex;
263  } else {
264  ppm_dev->NumChannelCounter = 0;
265  }
266 
267  /* Check if the last frame was well formed */
268  if (ppm_dev->PulseIndex == ppm_dev->NumChannels && ppm_dev->Tracking) {
269  /* The last frame was well formed */
270  ppm_dev->Fresh = true;
271 
272  for (uint32_t i = 0; i < ppm_dev->NumChannels; i++) {
273  ppm_dev->CaptureValue[i] = ppm_dev->CaptureValueNewFrame[i];
274  }
275  for (uint32_t i = ppm_dev->NumChannels;
276  i < PIOS_PPM_IN_MAX_NUM_CHANNELS; i++) {
277  ppm_dev->CaptureValue[i] = PIOS_RCVR_TIMEOUT;
278  }
279  }
280 
281  ppm_dev->Tracking = true;
282  ppm_dev->NumChannelsPrevFrame = ppm_dev->PulseIndex;
283  ppm_dev->PulseIndex = 0;
284 
285  /* We rely on the supervisor to set CaptureValue to invalid
286  if no valid frame is found otherwise we ride over it */
287 
288  } else if (ppm_dev->Tracking) {
289  /* Valid pulse duration 0.75 to 2.5 ms*/
290  if (ppm_dev->DeltaTime > PIOS_PPM_IN_MIN_CHANNEL_PULSE_US
291  && ppm_dev->DeltaTime < PIOS_PPM_IN_MAX_CHANNEL_PULSE_US
292  && ppm_dev->PulseIndex < PIOS_PPM_IN_MAX_NUM_CHANNELS) {
293 
294  ppm_dev->CaptureValueNewFrame[ppm_dev->PulseIndex] = ppm_dev->DeltaTime;
295  ppm_dev->PulseIndex++;
296  } else {
297  /* Not a valid pulse duration */
298  ppm_dev->Tracking = false;
299  for (uint32_t i = 0; i < PIOS_PPM_IN_MAX_NUM_CHANNELS ; i++) {
300  ppm_dev->CaptureValueNewFrame[i] = PIOS_RCVR_TIMEOUT;
301  }
302  }
303  }
304 }
305 
306 static void PIOS_PPM_Supervisor(uintptr_t ppm_id) {
307  /* Recover our device context */
308  struct pios_ppm_dev * ppm_dev = (struct pios_ppm_dev *)ppm_id;
309 
310  if (!PIOS_PPM_validate(ppm_dev)) {
311  /* Invalid device specified */
312  return;
313  }
314 
315  /*
316  * RTC runs at 625Hz so divide down the base rate so
317  * that this loop runs at twice the period required
318  * for the frame locking algorithm to declare lock.
319  */
320 #define SUPERVISOR_TICK_PERIOD_US 1600
321 #define PPM_FRAME_PERIOD_US 20000
322 #define SUPERVISOR_TICK_DIVIDER (2 * PPM_FRAME_PERIOD_US * PIOS_PPM_STABLE_CHANNEL_COUNT / SUPERVISOR_TICK_PERIOD_US)
323 #if SUPERVISOR_TICK_DIVIDER * SUPERVISOR_TICK_PERIOD_US > 500000
324 #error Unsafe supervisor timeout
325 #endif
326  if(++(ppm_dev->supv_timer) < SUPERVISOR_TICK_DIVIDER) {
327  return;
328  }
329 
330  ppm_dev->supv_timer = 0;
331 
332  if (!ppm_dev->Fresh) {
333  ppm_dev->Tracking = false;
334 
335  for (int32_t i = 0; i < PIOS_PPM_IN_MAX_NUM_CHANNELS ; i++) {
336  ppm_dev->CaptureValue[i] = PIOS_RCVR_TIMEOUT;
337  ppm_dev->CaptureValueNewFrame[i] = PIOS_RCVR_TIMEOUT;
338  }
339  }
340 
341  ppm_dev->Fresh = false;
342 }
343 
344 #endif
345 
int32_t(* read)(uintptr_t id, uint8_t channel)
Definition: pios_rcvr.h:35
Main PiOS header to include all the compiled in PiOS options.
const uint8_t count
Definition: panel.c:515
#define PIOS_DEBUG_Assert(test)
Definition: pios_debug.h:51
void * PIOS_malloc(size_t size)
Definition: pios_heap.c:125
void(* overflow)(uintptr_t tim_id, uintptr_t context, uint8_t chan_idx, uint16_t count)
Definition: pios_tim_priv.h:22
bool PIOS_RTC_RegisterTickCallback(void(*fn)(uintptr_t id), uintptr_t data)
Definition: pios_delay.c:185
uint8_t num_channels
Definition: pios_ppm_priv.h:39
static struct flyingpicmd_cfg_fa cfg
Definition: main.c:49
const struct pios_tim_channel * channels
Definition: pios_ppm_priv.h:38
uint8_t i
Definition: msp_messages.h:97
ppm private structures.
uint32_t magic
TIM_ICInitTypeDef tim_ic_init
Definition: pios_ppm_priv.h:37
int32_t PIOS_TIM_InitChannels(uintptr_t *tim_id, const struct pios_tim_channel *channels, uint8_t num_channels, const struct pios_tim_callbacks *callbacks, uintptr_t context)
Definition: pios_tim.c:172
int32_t PIOS_PPM_Init(uintptr_t *ppm_id, const struct pios_ppm_cfg *cfg)
TIM_TypeDef * timer
Definition: pios_tim_priv.h:14
const struct pios_rcvr_driver pios_ppm_rcvr_driver