Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
Stm32DCCDecoder.hxx
Go to the documentation of this file.
1
35#include "FreeRTOSConfig.h"
36#include "Stm32Gpio.hxx" // for pin definitions
38
39#include "stm32f_hal_conf.hxx"
40
42
55
57
62
65
69
72
74
78
80
83
85
88
91
96
99
101
106
110
115template <class HW> class Stm32DccTimerModule
116{
117public:
119 using NRZ_Pin = typename HW::NRZ_Pin;
120
121 // These constants are exported from HW to the driver.
122
125 static constexpr uint32_t TIMER_MAX_VALUE = 0xffff;
128 static constexpr uint32_t SAMPLE_PERIOD_CLOCKS = HW::SAMPLE_PERIOD_TICKS;
130 static constexpr unsigned Q_SIZE = HW::Q_SIZE;
131
133 static uint32_t get_ticks_per_usec()
134 {
135 // We set the timer prescaler to go at one tick per usec.
136 return 1;
137 }
138
140 static void module_init();
141
143 static void module_enable();
144
146 static void module_disable();
147
150 {
151 NVIC_SetPendingIRQ(HW::OS_IRQn);
152 }
153
155 static void before_cutout_hook()
156 {
157 HW::before_cutout_hook();
158 }
159
162 {
163 HW::dcc_before_cutout_hook();
164 }
165
168 {
169 HW::dcc_packet_finished_hook();
170 }
171
174 {
175 HW::after_feedback_hook();
176 }
177
180 {
181 return HW::time_delta_railcom_pre_usec();
182 }
183
186 {
187 return HW::time_delta_railcom_mid_usec();
188 }
189
192 {
193 return HW::time_delta_railcom_end_usec();
194 }
195
199 static inline bool int_get_and_clear_capture_event();
200
205 static inline uint32_t get_capture_counter();
206
211 static inline bool int_get_and_clear_delay_event();
212
219 static void set_cap_timer_delay_usec(int usec)
220 {
221 Debug::DccPacketDelay::toggle();
222 // This code handles underflow of the timer correctly. We cannot wait
223 // longer than one full cycle though (65 msec -- typical RailCom waits
224 // are 20-500 usec).
225 uint32_t new_match_v = usecTimerStart_ + usec;
226 new_match_v &= 0xffff;
227 __HAL_TIM_SET_COMPARE(
228 usec_timer_handle(), HW::USEC_CHANNEL, new_match_v);
229 __HAL_TIM_CLEAR_IT(usec_timer_handle(), HW::USEC_IF);
230 __HAL_TIM_ENABLE_IT(usec_timer_handle(), HW::USEC_IF);
231 }
232
236 {
237 __HAL_TIM_CLEAR_IT(capture_timer_handle(), HW::CAPTURE_IF);
239 __HAL_TIM_ENABLE_IT(capture_timer_handle(), HW::CAPTURE_IF);
240 }
241
245 static void set_cap_timer_time()
246 {
247 if (shared_timers()) {
249 } else {
250 usecTimerStart_ = __HAL_TIM_GET_COUNTER(usec_timer_handle());
251 }
253 // channel setup already happened in module_enable.
254 }
255
259 {
260 __HAL_TIM_DISABLE_IT(usec_timer_handle(), HW::USEC_IF);
261 //TIM_CCxChannelCmd(usec_timer(), HW::USEC_CHANNEL, TIM_CCx_DISABLE);
262 }
263
264private:
265 static TIM_HandleTypeDef captureTimerHandle_;
266 static TIM_HandleTypeDef usecTimerHandle_;
270 static uint32_t usecTimerStart_;
271
275 static void init_timer(TIM_HandleTypeDef *handle, TIM_TypeDef *instance)
276 {
277 memset(handle, 0, sizeof(*handle));
278 handle->Instance = instance;
279 handle->Init.Period = TIMER_MAX_VALUE;
280 // 1 usec per tick
281 handle->Init.Prescaler = configCPU_CLOCK_HZ / 1000000;
282 handle->Init.ClockDivision = 0;
283 handle->Init.CounterMode = TIM_COUNTERMODE_UP;
284 handle->Init.RepetitionCounter = 0;
285 handle->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
286
287 HASSERT(HAL_TIM_IC_DeInit(handle) == HAL_OK);
288 HASSERT(HAL_TIM_IC_Init(handle) == HAL_OK);
289 }
290
293 static inline uint32_t get_raw_capture_counter();
294
296 static TIM_TypeDef *capture_timer()
297 {
298 return reinterpret_cast<TIM_TypeDef *>(HW::CAPTURE_TIMER);
299 }
301 static TIM_HandleTypeDef *capture_timer_handle()
302 {
303 return &captureTimerHandle_;
304 }
305
307 static TIM_TypeDef *usec_timer()
308 {
309 return reinterpret_cast<TIM_TypeDef *>(HW::USEC_TIMER);
310 }
313 static TIM_HandleTypeDef *usec_timer_handle()
314 {
315 if (shared_timers())
316 {
317 return capture_timer_handle();
318 }
319 return &usecTimerHandle_;
320 }
321
324 static bool shared_timers()
325 {
326 return HW::CAPTURE_TIMER == HW::USEC_TIMER;
327 }
328
331
333};
334
335template <class HW>
337template <class HW> TIM_HandleTypeDef Stm32DccTimerModule<HW>::usecTimerHandle_;
338template <class HW> uint32_t Stm32DccTimerModule<HW>::usecTimerStart_;
339
340template <class HW> void Stm32DccTimerModule<HW>::module_init()
341{
342 memset(&captureTimerHandle_, 0, sizeof(captureTimerHandle_));
343 memset(&usecTimerHandle_, 0, sizeof(usecTimerHandle_));
344 // GPIO input.
345 NRZ_Pin::hw_init();
346}
347
349{
350 init_timer(capture_timer_handle(), capture_timer());
351 // Switches pin to GPIO input.
352 NRZ_Pin::hw_init();
353 // This must precede switching the pin to alternate mode to avoid the MCU
354 // driving the pin as an output in case the timer channel registers are
355 // uninitialized or in PWM mode.
356 set_cap_timer_capture();
357
358 GPIO_InitTypeDef gpio_init;
359 memset(&gpio_init, 0, sizeof(gpio_init));
360 gpio_init.Mode = GPIO_MODE_AF_PP;
361 gpio_init.Pull = GPIO_PULLUP;
362 gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
363 gpio_init.Alternate = HW::CAPTURE_AF_MODE;
364 gpio_init.Pin = HW::NRZ_Pin::pin();
365 HAL_GPIO_Init(HW::NRZ_Pin::port(), &gpio_init);
366
367 if (!shared_timers())
368 {
369 init_timer(usec_timer_handle(), usec_timer());
370 }
371
372 // Set up capture channel.
373 {
374 TIM_IC_InitTypeDef channel_init;
375 memset(&channel_init, 0, sizeof(channel_init));
376 channel_init.ICPolarity = TIM_ICPOLARITY_BOTHEDGE;
377 channel_init.ICSelection = TIM_ICSELECTION_DIRECTTI;
378 channel_init.ICPrescaler = TIM_ICPSC_DIV1;
379 channel_init.ICFilter = HW::CAPTURE_FILTER;
380 HASSERT(HAL_TIM_IC_ConfigChannel(capture_timer_handle(), &channel_init,
381 HW::CAPTURE_CHANNEL) == HAL_OK);
382 }
383
384 HASSERT(HAL_TIM_IC_Start_IT(capture_timer_handle(), HW::CAPTURE_CHANNEL) ==
385 HAL_OK);
386 // Disable interrupt until the set_cap_timer_capture() is called.
387 __HAL_TIM_DISABLE_IT(capture_timer_handle(), HW::CAPTURE_IF);
388
389 // Set up timing channel
390 {
391 TIM_OC_InitTypeDef channel_init;
392 memset(&channel_init, 0, sizeof(channel_init));
393 channel_init.OCMode = TIM_OCMODE_TIMING; // frozen -- no output
394 channel_init.Pulse = (__HAL_TIM_GET_COUNTER(usec_timer_handle()) + 1) &
395 0xffff; // will be reloaded in the delay_usec function.
396 // the rest are irrelevant.
397 channel_init.OCPolarity = TIM_OCPOLARITY_HIGH;
398 channel_init.OCNPolarity = TIM_OCNPOLARITY_HIGH;
399 channel_init.OCFastMode = TIM_OCFAST_DISABLE;
400 channel_init.OCIdleState = TIM_OCIDLESTATE_RESET;
401 channel_init.OCNIdleState = TIM_OCNIDLESTATE_RESET;
402 HASSERT(HAL_TIM_OC_ConfigChannel(usec_timer_handle(), &channel_init,
403 HW::USEC_CHANNEL) == HAL_OK);
404 }
405
406 HASSERT(
407 HAL_TIM_OC_Start_IT(usec_timer_handle(), HW::USEC_CHANNEL) == HAL_OK);
408 // Disable interrupt until the delay_usec() is called.
409 __HAL_TIM_DISABLE_IT(usec_timer_handle(), HW::USEC_IF);
410
411#if defined(GCC_ARMCM0)
412 HAL_NVIC_SetPriority(HW::CAPTURE_IRQn, 0, 0);
413 HAL_NVIC_SetPriority(HW::TIMER_IRQn, 0, 0);
414 HAL_NVIC_SetPriority(HW::OS_IRQn, 3, 0);
415#elif defined(GCC_ARMCM3)
416 SetInterruptPriority(HW::CAPTURE_IRQn, 0x20);
417 SetInterruptPriority(HW::TIMER_IRQn, 0x20);
418 SetInterruptPriority(HW::OS_IRQn, configKERNEL_INTERRUPT_PRIORITY);
419#else
420#error not defined how to set interrupt priority
421#endif
422
423 NVIC_EnableIRQ(HW::CAPTURE_IRQn);
424 NVIC_EnableIRQ(HW::TIMER_IRQn);
425 NVIC_EnableIRQ(HW::OS_IRQn);
426}
427
429{
430 // Switches pin to GPIO input.
431 NRZ_Pin::hw_init();
432
433 capture_timer_handle()->Instance = capture_timer();
434 usec_timer_handle()->Instance = usec_timer();
435 NVIC_DisableIRQ(HW::CAPTURE_IRQn);
436 NVIC_DisableIRQ(HW::TIMER_IRQn);
437 NVIC_DisableIRQ(HW::OS_IRQn);
438 HASSERT(HAL_TIM_IC_Stop_IT(capture_timer_handle(), HW::CAPTURE_CHANNEL) ==
439 HAL_OK);
440
441 HASSERT(
442 HAL_TIM_OC_Stop_IT(usec_timer_handle(), HW::USEC_CHANNEL) == HAL_OK);
443 if (!shared_timers())
444 {
445 HASSERT(HAL_TIM_IC_DeInit(usec_timer_handle()) == HAL_OK);
446 }
447 HASSERT(HAL_TIM_IC_DeInit(capture_timer_handle()) == HAL_OK);
448}
449
450template <class HW>
452{
453 if (__HAL_TIM_GET_FLAG(capture_timer_handle(), HW::CAPTURE_IF))
454 {
455 //Debug::DccDecodeInterrupts::toggle();
456 HW::cap_event_hook();
457 __HAL_TIM_CLEAR_FLAG(capture_timer_handle(), HW::CAPTURE_IF);
458 return true;
459 }
460 return false;
461}
462
464{
465 // Simulates down-counting.
466 return TIMER_MAX_VALUE - get_raw_capture_counter();
467}
468
470{
471 // This if structure will be optimized away.
472 if (HW::CAPTURE_CHANNEL == TIM_CHANNEL_1)
473 {
474 return capture_timer()->CCR1;
475 }
476 else if (HW::CAPTURE_CHANNEL == TIM_CHANNEL_2)
477 {
478 return capture_timer()->CCR2;
479 }
480 else if (HW::CAPTURE_CHANNEL == TIM_CHANNEL_3)
481 {
482 return capture_timer()->CCR3;
483 }
484 else if (HW::CAPTURE_CHANNEL == TIM_CHANNEL_4)
485 {
486 return capture_timer()->CCR4;
487 }
488 else
489 {
490 DIE("Unknown capture channel");
491 return 0;
492 }
493}
494
495template <class HW>
497{
498 if (__HAL_TIM_GET_FLAG(usec_timer_handle(), HW::USEC_IF))
499 {
500 Debug::DccDecodeInterrupts::set(true);
501 __HAL_TIM_CLEAR_IT(usec_timer_handle(), HW::USEC_IF);
502 // we also disable the interrupt until it is reenabled by loading a new
503 // usec delay target.
504 __HAL_TIM_DISABLE_IT(usec_timer_handle(), HW::USEC_IF);
505 return true;
506 }
507 return false;
508}
509
510template <class HW> using Stm32DccDecoder = DccDecoder<Stm32DccTimerModule<HW>>;
Device driver for decoding a DCC signal using a Timer resource.
Helper module for decoding a DCC signal on an STM32 class microcontroller.
static TIM_TypeDef * usec_timer()
static void trigger_os_interrupt()
Calls a software interrupt.
static void set_cap_timer_capture()
Sets the timer to capture mode.
static bool int_get_and_clear_capture_event()
Called from the capture interrupt handler.
static void module_init()
Called once during construction time.
static constexpr uint32_t SAMPLE_PERIOD_CLOCKS
After how many timer counts we should take one sample for occupancy feedback.
static bool int_get_and_clear_delay_event()
Called from the timeout interrupt handler.
static TIM_TypeDef * capture_timer()
static void set_cap_timer_time()
Sets the timer to oneshot (timer) mode.
static int time_delta_railcom_mid_usec()
How many usec later should the railcom cutout middle happen.
static uint32_t usecTimerStart_
Holds the base value from where the usec timer should be counting down when the delay_usec call is in...
static void before_cutout_hook()
hook
static uint32_t get_raw_capture_counter()
Helper function to read the raw capture register value.
typename HW::NRZ_Pin NRZ_Pin
Exports the input pin to the driver on the module interface.
static int time_delta_railcom_pre_usec()
How many usec later should the railcom cutout start happen.
Stm32DccTimerModule()
Private constructor. This class cannot be instantiated.
static constexpr unsigned Q_SIZE
Length of the device queue.
static void module_enable()
Called inline with Device::enable().
static void stop_cap_timer_time()
Called once inline in an interrupt.
static uint32_t get_ticks_per_usec()
static void dcc_packet_finished_hook()
hook
static void module_disable()
Called inline with Device::disable().
static uint32_t get_capture_counter()
Called from the interrupt handler if int_get_and_clear_capture_event said yes.
static TIM_HandleTypeDef * capture_timer_handle()
static void dcc_before_cutout_hook()
hook
static TIM_HandleTypeDef * usec_timer_handle()
static void after_feedback_hook()
hook
static int time_delta_railcom_end_usec()
How many usec later should the railcom cutout middle happen.
static void set_cap_timer_delay_usec(int usec)
Delays a given number of usec using the usec timer feature.
static constexpr uint32_t TIMER_MAX_VALUE
This is the counter from which the timer starts counting down.
static bool shared_timers()
static void init_timer(TIM_HandleTypeDef *handle, TIM_TypeDef *instance)
Initializes a timer resource (shared for all channels).
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
#define DIE(MSG)
Unconditionally terminates the current process with a message.
Definition macros.h:143
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
Removes default copy-constructor and assignment added by C++.
Definition macros.h:171
GPIO Pin definition structure with no actual pin behind it.
Definition DummyGPIO.hxx:43