Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
Stm32Railcom.hxx
Go to the documentation of this file.
1
36#ifndef _FREERTOS_DRIVERS_ST_STM32RAILCOM_HXX_
37#define _FREERTOS_DRIVERS_ST_STM32RAILCOM_HXX_
38
39#include "stm32f_hal_conf.hxx"
40
41#include "freertos_drivers/common/RailcomImpl.hxx"
42
43#if defined(STM32F072xB) || defined(STM32F091xC)
44#include "stm32f0xx_ll_dma.h"
45#include "stm32f0xx_ll_usart.h"
46#elif defined(STM32F103xB)
47#include "stm32f1xx_ll_dma.h"
48#include "stm32f1xx_ll_usart.h"
49#elif defined(STM32F303xC) || defined(STM32F303xE)
50#include "stm32f3xx_ll_dma.h"
51#include "stm32f3xx_ll_usart.h"
52#elif defined(STM32L431xx) || defined(STM32L432xx)
53#include "stm32l4xx_ll_dma.h"
54#include "stm32l4xx_ll_usart.h"
55#elif defined(STM32F767xx)
56#include "stm32f7xx_ll_dma.h"
57#include "stm32f7xx_ll_usart.h"
58#else
59#error Dont know what STM32 chip you have.
60#endif
61
66{
67 union
68 {
71 uint32_t baseAddress;
73 USART_TypeDef *Instance;
74 };
75};
76
80{
81 union
82 {
85 uint32_t baseAddress;
87 DMA_Channel_TypeDef *Instance;
88 };
89};
90
91/*
92struct RailcomHw
93{
94 static const uint32_t CHANNEL_COUNT = 4;
95 static const RailcomUartHandle UART[CHANNEL_COUNT];
96
97 // If DMA channel routing is in use, that has to be set up externally
98 // (e.g. in hw_preinit).
99 static const RailcomDmaChannel DMA[CHANNEL_COUNT];
100
101 // Make sure there are enough entries here for all the channels times a few
102 // DCC packets.
103 static const uint32_t Q_SIZE = 32;
104
105 static const auto OS_INTERRUPT = USART2_IRQn;
106
107 GPIO_HWPIN(CH1, GpioHwPin, C, 4, U4RX);
108 GPIO_HWPIN(CH2, GpioHwPin, C, 6, U3RX);
109 GPIO_HWPIN(CH3, GpioHwPin, G, 4, U2RX);
110 GPIO_HWPIN(CH4, GpioHwPin, E, 0, U7RX);
111
112 static void hw_init() {
113 CH1_Pin::hw_init();
114 CH2_Pin::hw_init();
115 CH3_Pin::hw_init();
116 CH4_Pin::hw_init();
117 }
118
119 static void set_input() {
120 CH1_Pin::set_input();
121 CH2_Pin::set_input();
122 CH3_Pin::set_input();
123 CH4_Pin::set_input();
124 }
125
126 static void set_hw() {
127 CH1_Pin::set_hw();
128 CH2_Pin::set_hw();
129 CH3_Pin::set_hw();
130 CH4_Pin::set_hw();
131 }
132
135 static uint8_t sample() {
136 uint8_t ret = 0;
137 if (!CH1_Pin::get()) ret |= 1;
138 if (!CH2_Pin::get()) ret |= 2;
139 if (!CH3_Pin::get()) ret |= 4;
140 if (!CH4_Pin::get()) ret |= 8;
141 return ret;
142 }
143};
144
145// The weak attribute is needed if the definition is put into a header file.
146const uint32_t RailcomHw::UART[] __attribute__((weak)) = { USART4_BASE,
147USART1_BASE, USART3_BASE, USART2_BASE };
148
149const RailcomDmaChannel RailcomHw::DMA[] __attribute__((weak)) = {
150DMA1_Channel1_BASE, DMA1_Channel3_BASE, DMA1_Channel6_BASE, DMA1_Channel5_BASE
151};
152
153In hw_preinit(), we have this for DMA channel routing:
154 //DMA channel routing for railcom UARTs
155 MODIFY_REG(DMA1->CSELR, DMA_CSELR_C1S_Msk, DMA1_CSELR_CH1_USART4_RX);
156 MODIFY_REG(DMA1->CSELR, DMA_CSELR_C3S_Msk, DMA1_CSELR_CH3_USART1_RX);
157 MODIFY_REG(DMA1->CSELR, DMA_CSELR_C6S_Msk, DMA1_CSELR_CH3_USART3_RX);
158 MODIFY_REG(DMA1->CSELR, DMA_CSELR_C5S_Msk, DMA1_CSELR_CH3_USART2_RX);
159
160*/
166template <class HW> class Stm32RailcomDriver : public RailcomDriverBase<HW>
167{
168public:
170 Stm32RailcomDriver(const char *path)
171 : RailcomDriverBase<HW>(path)
172 {
173#ifdef GCC_CM3
174 SetInterruptPriority(HW::OS_INTERRUPT, configKERNEL_INTERRUPT_PRIORITY);
175#else
176 SetInterruptPriority(HW::OS_INTERRUPT, 0xff);
177#endif
178 HAL_NVIC_EnableIRQ(HW::OS_INTERRUPT);
179 }
180
182 {
183 HAL_NVIC_DisableIRQ(HW::OS_INTERRUPT);
184 }
185
186private:
188 bool inCutout_ = false;
189
191
194 static constexpr USART_TypeDef *uart(unsigned i)
195 {
196 return HW::UART[i].Instance;
197 }
198
201 static constexpr DMA_Channel_TypeDef *dma_ch(unsigned i)
202 {
203 return HW::DMA[i].Instance;
204 }
205
208 void int_set_pending(unsigned int_nr) override
209 {
210 HAL_NVIC_SetPendingIRQ((IRQn_Type)int_nr);
211 }
212
213 // File node interface
215 {
216 for (unsigned i = 0; i < HW::CHANNEL_COUNT; ++i)
217 {
218 UART_HandleTypeDef uart_handle;
219 memset(&uart_handle, 0, sizeof(uart_handle));
220 uart_handle.Init.BaudRate = 250000;
221 uart_handle.Init.WordLength = UART_WORDLENGTH_8B;
222 uart_handle.Init.StopBits = UART_STOPBITS_1;
223 uart_handle.Init.Parity = UART_PARITY_NONE;
224 uart_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
225 uart_handle.Init.Mode = UART_MODE_RX;
226 uart_handle.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
227 uart_handle.Instance = uart(i);
228 HAL_UART_DeInit(&uart_handle);
229 volatile auto ret = HAL_UART_Init(&uart_handle);
230 HASSERT(HAL_OK == ret);
231
232 // Disables the receiver.
233 LL_USART_SetTransferDirection(uart(i), LL_USART_DIRECTION_NONE);
234
235 // configure DMA
236
237 // peripheral address
238 dma_ch(i)->CPAR = (uint32_t)(&uart(i)->RDR);
239
240 // memory address and num transfers not set, these will come from
241 // each cutout.
242 // dma_ch->CMAR = (uint32_t)adcSamplesRaw_;
243 // dma_ch->CNDTR = NUM_SAMPLES * NUM_CHANNELS;
244
245 uint32_t config = LL_DMA_DIRECTION_PERIPH_TO_MEMORY |
246 LL_DMA_MODE_NORMAL | LL_DMA_PERIPH_NOINCREMENT |
247 LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_BYTE |
248 LL_DMA_MDATAALIGN_BYTE | LL_DMA_PRIORITY_HIGH;
249 MODIFY_REG(dma_ch(i)->CCR,
250 DMA_CCR_DIR | DMA_CCR_MEM2MEM | DMA_CCR_CIRC | DMA_CCR_PINC |
251 DMA_CCR_MINC | DMA_CCR_PSIZE | DMA_CCR_MSIZE | DMA_CCR_PL |
252 DMA_CCR_TCIE | DMA_CCR_HTIE | DMA_CCR_TEIE,
253 config);
254
255 LL_USART_Enable(uart(i));
256 }
257 }
258 void disable() override
259 {
260 for (unsigned i = 0; i < HW::CHANNEL_COUNT; ++i)
261 {
262 LL_USART_Disable(uart(i));
263 }
264 }
265
266 // RailcomDriver interface
267 void feedback_sample() override
268 {
269 HW::enable_measurement(true);
270 this->add_sample(HW::sample());
271 HW::disable_measurement();
272 }
273
274 void start_cutout() override
275 {
276 HW::enable_measurement(false);
277 const bool need_ch1_cutout =
278 HW::need_ch1_cutout() || (this->feedbackKey_ < 11000);
279 Debug::RailcomRxActivate::set(true);
280 for (unsigned i = 0; i < HW::CHANNEL_COUNT; ++i)
281 {
282 while (LL_USART_IsActiveFlag_RXNE(uart(i)))
283 {
284 uint8_t data = uart(i)->RDR;
285 (void)data;
286 }
288 if (need_ch1_cutout && returnedPackets_[i])
289 {
290 LL_USART_EnableDMAReq_RX(uart(i));
291 LL_USART_SetTransferDirection(uart(i), LL_USART_DIRECTION_RX);
292 LL_USART_ClearFlag_FE(uart(i));
293 LL_USART_Enable(uart(i));
294
295 dma_ch(i)->CNDTR = 2;
296 dma_ch(i)->CMAR = (uint32_t)returnedPackets_[i]->ch1Data;
297 dma_ch(i)->CCR |= DMA_CCR_EN; // enable DMA
298 }
299 }
300 Debug::RailcomDriverCutout::set(true);
301 }
302
303 void middle_cutout() override
304 {
305 Debug::RailcomDriverCutout::set(false);
306 for (unsigned i = 0; i < HW::CHANNEL_COUNT; ++i)
307 {
308 if (!returnedPackets_[i])
309 {
310 continue;
311 }
312 LL_USART_Disable(uart(i));
313 CLEAR_BIT(dma_ch(i)->CCR, DMA_CCR_EN);
314 // How many bytes did we transfer?
315 returnedPackets_[i]->ch1Size = 2 - dma_ch(i)->CNDTR;
316 if (returnedPackets_[i]->ch1Size == 1)
317 {
318 Debug::RailcomError::toggle();
319 }
320 if (returnedPackets_[i]->ch1Size)
321 {
322 Debug::RailcomDataReceived::toggle();
323 Debug::RailcomAnyData::set(true);
324 }
325
326 if (LL_USART_IsActiveFlag_FE(uart(i)))
327 {
328 // We reset the receiver circuitry because we got an error
329 // in channel 1. Typical cause of this error is if there
330 // are multiple locomotives on the block (e.g. if this is
331 // the global detector) and they talk over each other
332 // during ch1 broadcast. There is a good likelihood that
333 // the asynchronous receiver is out of sync with the
334 // transmitter, but right now we should be in the
335 // between-byte space.
336 LL_USART_SetTransferDirection(uart(i), LL_USART_DIRECTION_NONE);
337 LL_USART_ClearFlag_FE(uart(i));
338 Debug::RailcomError::toggle();
339 // Not a valid railcom byte.
340 returnedPackets_[i]->ch1Data[0] = 0xF8;
342 }
343
344 // Set up channel 2 reception with DMA.
345 dma_ch(i)->CNDTR = 6;
346 dma_ch(i)->CMAR = (uint32_t)returnedPackets_[i]->ch2Data;
347 dma_ch(i)->CCR |= DMA_CCR_EN; // enable DMA
348
349 LL_USART_SetTransferDirection(uart(i), LL_USART_DIRECTION_RX);
350 LL_USART_ClearFlag_FE(uart(i));
351 LL_USART_Enable(uart(i));
352 }
353 HW::middle_cutout_hook();
354 Debug::RailcomDriverCutout::set(true);
355 }
356
357 void end_cutout() override
358 {
359 HW::disable_measurement();
360 bool have_packets = false;
361 for (unsigned i = 0; i < HW::CHANNEL_COUNT; ++i)
362 {
363 LL_USART_Disable(uart(i));
364 if (!returnedPackets_[i])
365 {
366 continue;
367 }
368 CLEAR_BIT(dma_ch(i)->CCR, DMA_CCR_EN);
369 // How many bytes did we transfer?
370 returnedPackets_[i]->ch2Size = 6 - dma_ch(i)->CNDTR;
371 if (returnedPackets_[i]->ch2Size)
372 {
373 Debug::RailcomDataReceived::toggle();
374 Debug::RailcomAnyData::set(true);
375 Debug::RailcomCh2Data::set(true);
376 }
377 if (LL_USART_IsActiveFlag_FE(uart(i)))
378 {
379 Debug::RailcomError::toggle();
380 LL_USART_ClearFlag_FE(uart(i));
381 if (returnedPackets_[i]->ch2Size < 6)
382 {
384 }
385 }
386
387 LL_USART_SetTransferDirection(uart(i), LL_USART_DIRECTION_NONE);
388
389 Debug::RailcomPackets::toggle();
390 have_packets = true;
392 returnedPackets_[i] = nullptr;
393 HAL_NVIC_SetPendingIRQ(HW::OS_INTERRUPT);
394 }
395 Debug::RailcomRxActivate::set(false);
396 if (!have_packets)
397 {
398 // Ensures that at least one feedback packet is sent back even when
399 // it is with no railcom payload.
400 auto *p = this->alloc_new_packet(15);
401 if (p)
402 {
404 Debug::RailcomPackets::toggle();
405 HAL_NVIC_SetPendingIRQ(HW::OS_INTERRUPT);
406 }
407 }
408 Debug::RailcomCh2Data::set(false);
409 Debug::RailcomDriverCutout::set(false);
410 }
411
412 void no_cutout() override
413 {
414 // Ensures that at least one feedback packet is sent back even when
415 // it is with no railcom payload.
416 auto *p = this->alloc_new_packet(15);
417 if (p)
418 {
420 Debug::RailcomPackets::toggle();
421 HAL_NVIC_SetPendingIRQ(HW::OS_INTERRUPT);
422 }
423 }
424};
425
426#endif // _FREERTOS_DRIVERS_ST_STM32RAILCOM_HXX_
void commit_back()
Commits the oldest entry reserved by noncommit_back.
Base class for railcom drivers.
Definition Railcom.hxx:133
dcc::Feedback * returnedPackets_[HW::CHANNEL_COUNT]
Stores pointers to packets we are filling right now, one for each channel.
Definition Railcom.hxx:285
uint32_t feedbackKey_
Stores the key for the next packets to read.
Definition Railcom.hxx:282
dcc::Feedback * alloc_new_packet(uint8_t channel)
Takes a new empty packet at the front of the queue, fills in feedback key and channel information.
Definition Railcom.hxx:245
void add_sample(int sample)
Adds a sample for a preamble bit.
Definition Railcom.hxx:261
FixedQueue< dcc::Feedback, HW::Q_SIZE > feedbackQueue_
The packets we have read so far.
Definition Railcom.hxx:280
Railcom driver for STM32-class microcontrollers using the HAL middleware library.
bool inCutout_
True when we are currently within a cutout.
static constexpr DMA_Channel_TypeDef * dma_ch(unsigned i)
void disable() override
This will be called when reference count goes from non-zero to 0.
void int_set_pending(unsigned int_nr) override
Sets a given software interrupt pending.
void no_cutout() override
Called instead of start/mid/end-cutout at the end of the current packet if there was no cutout reques...
void feedback_sample() override
Call to the driver for sampling the current sensors.
static constexpr USART_TypeDef * uart(unsigned i)
void middle_cutout() override
Notifies the driver that the railcom cutout has reached the middle point, i.e., the first window is p...
void start_cutout() override
Instructs the driver that the railcom cutout is starting now.
void end_cutout() override
Instructs the driver that the railcom cutout is over now.
Stm32RailcomDriver(const char *path)
Constructor.
void enable() OVERRIDE
This will be called once when reference-count goes from 0 to positive.
#define OVERRIDE
Function attribute for virtual functions declaring that this funciton is overriding a funciton that s...
Definition macros.h:180
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
uint8_t ch2Size
Number of bytes in channel two.
Definition railcom.h:49
uint8_t ch1Size
Number of bytes in channel one.
Definition railcom.h:45
uint8_t ch1Data[2]
Payload of channel 1.
Definition railcom.h:47
This struct helps casting the DMA channel base addresses to the appropriate type.
DMA_Channel_TypeDef * Instance
Use this to access the registers.
uint32_t baseAddress
Initialized by the constants from the processor header like USART1_BASE.
This struct helps casting the UART base addresses to the appropriate type.
USART_TypeDef * Instance
Use this to access the registers.
uint32_t baseAddress
Initialized by the constants from the processor header like USART1_BASE.
void add_ch2_data(uint8_t data)
Appends a byte to the channel 2 payload.
Definition RailCom.hxx:70