Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
Railcom.hxx
1
57#ifndef _FREERTOS_DRIVERS_TI_TIVARAILCOM_HXX_
58#define _FREERTOS_DRIVERS_TI_TIVARAILCOM_HXX_
59
60#if (!defined(TIVADCC_TIVA)) && (!defined(TIVADCC_CC3200))
61#error must define either TIVADCC_TIVA or TIVADCC_CC3200
62#endif
63
64#include "TivaDCC.hxx" // for FixedQueue
65
66#include "RailcomDriver.hxx"
67#include "dcc/RailCom.hxx"
68
69/*
70struct RailcomHw
71{
72 static const uint32_t CHANNEL_COUNT = 4;
73 static const uint32_t UART_PERIPH[CHANNEL_COUNT];
74 static const uint32_t UART_BASE[CHANNEL_COUNT];
75 // Make sure there are enough entries here for all the channels times a few
76 // DCC packets.
77 static const uint32_t Q_SIZE = 16;
78
79 static const auto OS_INTERRUPT = INT_UART2;
80
81 GPIO_HWPIN(CH1, GpioHwPin, C, 4, U4RX);
82 GPIO_HWPIN(CH2, GpioHwPin, C, 6, U3RX);
83 GPIO_HWPIN(CH3, GpioHwPin, G, 4, U2RX);
84 GPIO_HWPIN(CH4, GpioHwPin, E, 0, U7RX);
85
86 static void hw_init() {
87 CH1_Pin::hw_init();
88 CH2_Pin::hw_init();
89 CH3_Pin::hw_init();
90 CH4_Pin::hw_init();
91 }
92
93 static void set_input() {
94 CH1_Pin::set_input();
95 CH2_Pin::set_input();
96 CH3_Pin::set_input();
97 CH4_Pin::set_input();
98 }
99
100 static void set_hw() {
101 CH1_Pin::set_hw();
102 CH2_Pin::set_hw();
103 CH3_Pin::set_hw();
104 CH4_Pin::set_hw();
105 }
106
109 static uint8_t sample() {
110 uint8_t ret = 0;
111 if (!CH1_Pin::get()) ret |= 1;
112 if (!CH2_Pin::get()) ret |= 2;
113 if (!CH3_Pin::get()) ret |= 4;
114 if (!CH4_Pin::get()) ret |= 8;
115 return ret;
116 }
117};
118
119// The weak attribute is needed if the definition is put into a header file.
120const uint32_t RailcomHw::UART_BASE[] __attribute__((weak)) = {UART4_BASE, UART3_BASE, UART2_BASE, UART7_BASE};
121const uint32_t RailcomHw::UART_PERIPH[]
122__attribute__((weak)) = {SYSCTL_PERIPH_UART4, SYSCTL_PERIPH_UART3, SYSCTL_PERIPH_UART2, SYSCTL_PERIPH_UART7};
123
124*/
125
131template <class HW>
132class RailcomDriverBase : public RailcomDriver, private Node
133{
134public:
137 : Node(name)
138 , readableNotifiable_(nullptr)
139 {
140 HW::hw_init();
141 MAP_IntPrioritySet(HW::OS_INTERRUPT, configKERNEL_INTERRUPT_PRIORITY);
142 MAP_IntEnable(HW::OS_INTERRUPT);
143 }
144
146 {
147 MAP_IntDisable(HW::OS_INTERRUPT);
148 }
149
150private:
151 ssize_t write(File *, const void *, size_t) OVERRIDE
152 {
153 // This device is read-only.
154 return -ENOSYS;
155 }
156
157 ssize_t read(File *file, void *buf, size_t count) OVERRIDE
158 {
159 if (count != sizeof(dcc::Feedback))
160 {
161 return -EINVAL;
162 }
163 OSMutexLock l(&lock_);
164 if (feedbackQueue_.empty())
165 {
166 return -EAGAIN;
167 }
168 memcpy(buf, &feedbackQueue_.front(), count);
170 return count;
171 }
172
174 {
175 while (!feedbackQueue_.empty())
176 {
178 }
179 }
180
181 // Asynchronous interface
182 int ioctl(File *file, unsigned long int key, unsigned long data) override
183 {
184 if (IOC_TYPE(key) == CAN_IOC_MAGIC &&
186 {
187 Notifiable *n = reinterpret_cast<Notifiable *>(data);
188 HASSERT(n);
189 // If there is no data for reading, we put the incoming notification
190 // into the holder. Otherwise we notify it immediately.
191 if (feedbackQueue_.empty())
192 {
193 portENTER_CRITICAL();
194 if (feedbackQueue_.empty())
195 {
196 // We are in a critical section now. If we got into this
197 // branch, then the buffer was full at the beginning of the
198 // critical section. If the hardware interrupt kicks in
199 // now, and sets the os_interrupt to pending, the os
200 // interrupt will not happen until we leave the critical
201 // section, and thus the swap will be in effect by then.
202 std::swap(n, readableNotifiable_);
203 }
204 portEXIT_CRITICAL();
205 }
206 if (n)
207 {
208 n->notify();
209 }
210 return 0;
211 }
212 errno = EINVAL;
213 return -1;
214 }
215
216public:
219 void os_interrupt_handler() __attribute__((always_inline))
220 {
221 if (!feedbackQueue_.empty())
222 {
224 readableNotifiable_ = nullptr;
225 if (n)
226 {
227 n->notify_from_isr();
228 os_isr_exit_yield_test(true);
229 }
230 }
231 }
232
233private:
234 void set_feedback_key(uint32_t key) OVERRIDE
235 {
236 feedbackKey_ = key;
237 }
238
239protected:
246 {
248 {
249 return nullptr;
250 }
253 entry->reset(feedbackKey_);
254 entry->channel = channel;
255 return entry;
256 }
257
261 void add_sample(int sample) {
262 if (sample < 0) return; // skipped sampling
263 if (feedbackQueue_.full()) {
264 extern uint32_t feedback_sample_overflow_count;
265 ++feedback_sample_overflow_count;
266 return;
267 }
269 feedbackQueue_.back().channel = HW::get_feedback_channel();
271 uint32_t tick_timer = HW::get_timer_tick();
272 memcpy(feedbackQueue_.back().ch2Data, &tick_timer, 4);
274 MAP_IntPendSet(HW::OS_INTERRUPT);
275 }
276
282 uint32_t feedbackKey_;
285 dcc::Feedback *returnedPackets_[HW::CHANNEL_COUNT];
286};
287
293template <class HW> class TivaRailcomDriver : public RailcomDriverBase<HW>
294{
295public:
297 TivaRailcomDriver(const char *path) : RailcomDriverBase<HW>(path)
298 {
299 }
300
301private:
303 bool inCutout_ = false;
304
306 // File node interface
308 {
309 for (unsigned i = 0; i < ARRAYSIZE(HW::UART_BASE); ++i)
310 {
311#ifdef TIVADCC_TIVA
312 MAP_SysCtlPeripheralEnable(HW::UART_PERIPH[i]);
313#elif defined(TIVADCC_CC3200)
314 MAP_PRCMPeripheralClkEnable(HW::UART_PERIPH[i], PRCM_RUN_MODE_CLK);
315#endif
316 MAP_UARTConfigSetExpClk(HW::UART_BASE[i], cm3_cpu_clock_hz, 250000,
317 UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
318 UART_CONFIG_PAR_NONE);
319 MAP_UARTFIFOEnable(HW::UART_BASE[i]);
320 // Disables the receiver.
321 HWREGBITW(HW::UART_BASE[i] + UART_O_CTL, UART_CTL_RXE) = 0;
322 MAP_UARTEnable(HW::UART_BASE[i]);
323 }
324 }
326 {
327 for (unsigned i = 0; i < ARRAYSIZE(HW::UART_BASE); ++i)
328 {
329 MAP_UARTDisable(HW::UART_BASE[i]);
330 }
331 }
332
333 // RailcomDriver interface
335 HW::enable_measurement(true);
336 this->add_sample(HW::sample());
337 HW::disable_measurement();
338 }
339
341 {
342 HW::enable_measurement(false);
343 const bool need_ch1_cutout = HW::need_ch1_cutout() || (this->feedbackKey_ < 11000);
344 Debug::RailcomRxActivate::set(true);
345 for (unsigned i = 0; i < ARRAYSIZE(HW::UART_BASE); ++i)
346 {
347 if (need_ch1_cutout)
348 {
349 HWREG(HW::UART_BASE[i] + UART_O_CTL) |= UART_CTL_RXE;
350 }
351 // HWREGBITW(HW::UART_BASE[i] + UART_O_CTL, UART_CTL_RXE) =
352 // UART_CTL_RXE;
353 // flush fifo
354 while (MAP_UARTCharGetNonBlocking(HW::UART_BASE[i]) >= 0)
355 ;
356 returnedPackets_[i] = 0;
357 }
358 Debug::RailcomDriverCutout::set(true);
359 }
360
362 {
363 Debug::RailcomDriverCutout::set(false);
364 for (unsigned i = 0; i < ARRAYSIZE(HW::UART_BASE); ++i)
365 {
366 while (MAP_UARTCharsAvail(HW::UART_BASE[i]))
367 {
368 Debug::RailcomDataReceived::toggle();
369 Debug::RailcomAnyData::set(true);
370 if (!returnedPackets_[i])
371 {
373 }
374 if (!returnedPackets_[i])
375 {
376 break;
377 }
378 long data = MAP_UARTCharGetNonBlocking(HW::UART_BASE[i]);
379 if (data < 0 || data > 0xff) {
380 // We reset the receiver circuitry because we got an error
381 // in channel 1. Typical cause of this error is if there
382 // are multiple locomotives on the block (e.g. if this is
383 // the global detector) and they talk over each other
384 // during ch1 broadcast. There is a good likelihood that
385 // the asynchronous receiver is out of sync with the
386 // transmitter, but right now we should be in the
387 // between-byte space.
388 HWREG(HW::UART_BASE[i] + UART_O_CTL) &= ~UART_CTL_RXE;
389 Debug::RailcomError::toggle();
390 returnedPackets_[i]->add_ch1_data(0xF8 | ((data >> 8) & 0x7));
391 continue;
392 }
394 }
395 HWREG(HW::UART_BASE[i] + UART_O_CTL) |= UART_CTL_RXE;
396 }
397 HW::middle_cutout_hook();
398 Debug::RailcomDriverCutout::set(true);
399 }
400
402 {
403 HW::disable_measurement();
404 bool have_packets = false;
405 for (unsigned i = 0; i < ARRAYSIZE(HW::UART_BASE); ++i)
406 {
407 while (MAP_UARTCharsAvail(HW::UART_BASE[i]))
408 {
409 Debug::RailcomDataReceived::toggle();
410 Debug::RailcomAnyData::set(true);
411 Debug::RailcomCh2Data::set(true);
412 if (!returnedPackets_[i])
413 {
415 }
416 if (!returnedPackets_[i])
417 {
418 break;
419 }
420 long data = MAP_UARTCharGetNonBlocking(HW::UART_BASE[i]);
421 if (data < 0 || data > 0xff) {
422 Debug::RailcomError::toggle();
423 returnedPackets_[i]->add_ch2_data(0xF8 | ((data >> 8) & 0x7));
424 continue;
425 }
426 if (data == 0xE0) {
427 Debug::RailcomE0::toggle();
428 }
430 }
431 HWREG(HW::UART_BASE[i] + UART_O_CTL) &= ~UART_CTL_RXE;
432 Debug::RailcomRxActivate::set(false);
433 //HWREGBITW(HW::UART_BASE[i] + UART_O_CTL, UART_CTL_RXE) = 0;
434 if (returnedPackets_[i]) {
435 have_packets = true;
437 Debug::RailcomPackets::toggle();
438 returnedPackets_[i] = nullptr;
439 MAP_IntPendSet(HW::OS_INTERRUPT);
440 }
441 }
442 if (!have_packets)
443 {
444 // Ensures that at least one feedback packet is sent back even when
445 // it is with no railcom payload.
446 auto *p = this->alloc_new_packet(0);
447 if (p)
448 {
450 Debug::RailcomPackets::toggle();
451 MAP_IntPendSet(HW::OS_INTERRUPT);
452 }
453 }
454 Debug::RailcomCh2Data::set(false);
455 Debug::RailcomDriverCutout::set(false);
456 }
457
459 {
460 for (unsigned i = 0; i < ARRAYSIZE(HW::UART_BASE); ++i)
461 {
462 if (!returnedPackets_[i])
463 {
465 }
466 if (returnedPackets_[i])
467 {
469 Debug::RailcomPackets::toggle();
470 returnedPackets_[i] = nullptr;
471 MAP_IntPendSet(HW::OS_INTERRUPT);
472 }
473 }
474 }
475};
476
477#endif // _FREERTOS_DRIVERS_TI_TIVARAILCOM_HXX_
const char * name
device name
Definition Devtab.hxx:266
This structure is safe to use from an interrupt context and a regular context at the same time,...
bool empty()
void increment_front()
Removes the head of the FIFO from the queue.
bool has_noncommit_space()
void increment_back()
Commits the element at back() into the queue.
void commit_back()
Commits the oldest entry reserved by noncommit_back.
T & front()
Returns the head of the FIFO (next element to read).
T & back()
Returns the space to write the next element to.
void noncommit_back()
Increments the back pointer without committing the entry into the queue.
bool full()
Node information.
Definition Devtab.hxx:549
OSMutex lock_
protects internal structures.
Definition Devtab.hxx:588
An object that can schedule itself on an executor to run.
virtual void notify()=0
Generic callback.
Class to allow convenient locking and unlocking of mutexes in a C context.
Definition OS.hxx:494
Base class for railcom drivers.
Definition Railcom.hxx:133
Notifiable * readableNotifiable_
Notify this when we have data in our buffers.
Definition Railcom.hxx:278
void os_interrupt_handler()
Implementation of the interrupt handler running at the kernel interrupt priority.
Definition Railcom.hxx:219
RailcomDriverBase(const char *name)
Constructor.
Definition Railcom.hxx:136
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
ssize_t read(File *file, void *buf, size_t count) OVERRIDE
Read from a file or device.
Definition Railcom.hxx:157
void flush_buffers() OVERRIDE
Instructs the device driver to drop all TX and RX queues.
Definition Railcom.hxx:173
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 set_feedback_key(uint32_t key) OVERRIDE
Specifies the feedback key to write into the received railcom data packets.
Definition Railcom.hxx:234
int ioctl(File *file, unsigned long int key, unsigned long data) override
Request an ioctl transaction.
Definition Railcom.hxx:182
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
ssize_t write(File *, const void *, size_t) OVERRIDE
Write to a file or device.
Definition Railcom.hxx:151
Abstract base class for railcom drivers.
Railcom driver for TI Tiva-class microcontrollers using the TivaWare peripheral library.
Definition Railcom.hxx:294
bool inCutout_
True when we are currently within a cutout.
Definition Railcom.hxx:303
TivaRailcomDriver(const char *path)
Constructor.
Definition Railcom.hxx:297
void start_cutout() OVERRIDE
Instructs the driver that the railcom cutout is starting now.
Definition Railcom.hxx:340
void disable() OVERRIDE
This will be called when reference count goes from non-zero to 0.
Definition Railcom.hxx:325
void end_cutout() OVERRIDE
Instructs the driver that the railcom cutout is over now.
Definition Railcom.hxx:401
void no_cutout() OVERRIDE
Called instead of start/mid/end-cutout at the end of the current packet if there was no cutout reques...
Definition Railcom.hxx:458
void enable() OVERRIDE
This will be called once when reference-count goes from 0 to positive.
Definition Railcom.hxx:307
void middle_cutout() OVERRIDE
Notifies the driver that the railcom cutout has reached the middle point, i.e., the first window is p...
Definition Railcom.hxx:361
void feedback_sample() OVERRIDE
Call to the driver for sampling the current sensors.
Definition Railcom.hxx:334
#define CAN_IOC_READ_ACTIVE
read active ioctl.
#define NOTIFIABLE_TYPE
ioctl minor type used for the read/write active notifiable integration.
#define CAN_IOC_MAGIC
Magic number for this driver's ioctl calls.
#define IOC_SIZE(_num)
Decode ioctl size.
#define IOC_TYPE(_num)
Decode ioctl type.
#define OVERRIDE
Function attribute for virtual functions declaring that this funciton is overriding a funciton that s...
Definition macros.h:180
#define ARRAYSIZE(a)
Returns the number of elements in a statically defined array (of static size)
Definition macros.h:185
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
uint8_t channel
Used by multi-channel railcom receiver drivers.
Definition railcom.h:54
uint8_t ch2Data[6]
Payload of channel 2.
Definition railcom.h:51
File information.
Definition Devtab.hxx:52
Structure used for reading (railcom) feedback data from DCC / Railcom device drivers.
Definition RailCom.hxx:50
void reset(uint32_t feedback_key)
Clears the structure and sets the feedback key to a specific value.
Definition RailCom.hxx:52
void add_ch1_data(uint8_t data)
Appends a byte to the channel 1 payload.
Definition RailCom.hxx:61
void add_ch2_data(uint8_t data)
Appends a byte to the channel 2 payload.
Definition RailCom.hxx:70