Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
DccDecoder.hxx
Go to the documentation of this file.
1
36#include "RailcomDriver.hxx" // for debug pins
37#include "dcc/Receiver.hxx"
39#include "dcc/packet.h"
40
84template <class Module> class DccDecoder : public Node
85{
86public:
92 DccDecoder(const char *name, RailcomDriver *railcom_driver);
93
95 {
96 inputData_->destroy();
97 }
98
106
108 inline void interrupt_handler() __attribute__((always_inline));
109
111 inline void rcom_interrupt_handler() __attribute__((always_inline));
112
114 inline void os_interrupt_handler() __attribute__((always_inline));
115
116private:
124 ssize_t read(File *file, void *buf, size_t count) OVERRIDE {
125 HASSERT((count % inputData_->member_size()) == 0);
126 unsigned max = count / inputData_->member_size();
127 while (true)
128 {
129 portENTER_CRITICAL();
130 unsigned copied = inputData_->get((input_data_type *)buf, max);
131 if (!decoder_.pkt() && inputData_->space())
132 {
134 inputData_->data_write_pointer(&next);
136 }
137 portEXIT_CRITICAL();
138 if (copied > 0)
139 {
140 return copied * inputData_->member_size();
141 }
142 if (file->flags & O_NONBLOCK)
143 {
144 return -EAGAIN;
145 }
146 inputData_->block_until_condition(file, true);
147 }
148 }
149
157 ssize_t write(File *file, const void *buf, size_t count) OVERRIDE
158 {
159 return -EINVAL;
160 }
161
168 bool select(File *file, int mode) OVERRIDE
169 {
170 bool retval = false;
171 portENTER_CRITICAL();
172 switch (mode)
173 {
174 case FREAD:
175 if (inputData_->pending() > 0)
176 {
177 retval = true;
178 }
179 else
180 {
181 inputData_->select_insert();
182 }
183 break;
184 default:
185 break;
186 }
187 portEXIT_CRITICAL();
188 return retval;
189 }
190
191 void enable() override;
192 void disable() override;
196 void flush_buffers() override
197 {
198 inputData_->flush();
199 };
200
201#ifdef DCC_DECODER_DEBUG
202 LogRing<uint16_t, 256> debugLog_;
203#endif
204
205 typedef DCCPacket input_data_type;
206 DeviceBuffer<DCCPacket> *inputData_ {
207 DeviceBuffer<DCCPacket>::create(Module::Q_SIZE)};
208 bool nextPacketFilled_{false};
214 uint32_t nextSample_;
216 bool sampleActive_ = false;
218 unsigned lastLevel_;
222 bool inCutout_ = false;
225 bool prepCutout_ = false;
227 uint32_t cutoutState_;
229 uint32_t packetId_ = 0;
231 //uint32_t overflowCount_ {0};
232
235
238
240 dcc::DccDecoder decoder_ {Module::get_ticks_per_usec()};
241
244 static const auto RAILCOM_CUTOUT_PRE = 26;
247 static const auto RAILCOM_CUTOUT_MID = 185;
250 static const auto RAILCOM_CUTOUT_END = 471;
251
253};
254
255template <class Module>
256DccDecoder<Module>::DccDecoder(const char *name, RailcomDriver *railcom_driver)
257 : Node(name)
258 , railcomDriver_(railcom_driver)
259{
260 Module::NRZ_Pin::hw_init();
261 Module::module_init();
262 disable();
263}
264
265template <class Module> void DccDecoder<Module>::enable()
266{
267 disable();
268
269 Module::module_enable();
270 Module::set_cap_timer_capture();
271
272 lastTimerValue_ = Module::TIMER_MAX_VALUE;
273 nextSample_ = lastTimerValue_ - Module::SAMPLE_PERIOD_CLOCKS;
274
275 if (!decoder_.pkt() && inputData_->space())
276 {
277 DCCPacket *next;
278 inputData_->data_write_pointer(&next);
279 decoder_.set_packet(next);
280 }
281}
282
283template <class Module> void DccDecoder<Module>::disable()
284{
285 Module::module_disable();
286}
287
288template <class Module>
289__attribute__((optimize("-O3"))) void DccDecoder<Module>::interrupt_handler()
290{
291 Debug::DccDecodeInterrupts::set(true);
292 if (Module::int_get_and_clear_capture_event())
293 {
294 // We have a capture event at hand.
295 // Debug::DccDecodeInterrupts::toggle();
296 uint32_t raw_new_value = Module::get_capture_counter();
297 uint32_t old_value = lastTimerValue_;
298#ifdef DCC_DECODER_DEBUG
299 debugLog_.add(0);
300 debugLog_.add(old_value);
301 debugLog_.add(raw_new_value);
302#endif
303 if (raw_new_value > old_value) {
304 // Timer has overflowed.
305 if (nextSample_ < old_value) {
306 nextSample_ += Module::TIMER_MAX_VALUE;
307 }
308 old_value += Module::TIMER_MAX_VALUE;
309 waitSampleForOverflow_ = false;
310 Debug::CapTimerOverflow::set(false);
311 }
312 if (raw_new_value < nextSample_ && !waitSampleForOverflow_) {
313 sampleActive_ = true;
314 if (nextSample_ <= Module::SAMPLE_PERIOD_CLOCKS)
315 {
316 nextSample_ += Module::TIMER_MAX_VALUE;
317 waitSampleForOverflow_ = true;
318 Debug::CapTimerOverflow::set(true);
319 }
320 nextSample_ -= Module::SAMPLE_PERIOD_CLOCKS;
321 }
322 uint32_t new_value = old_value - raw_new_value;
323#ifdef DCC_DECODER_DEBUG
324 debugLog_.add(new_value);
325#endif
326 bool cutout_just_finished = false;
327 decoder_.process_data(new_value);
328 if (decoder_.before_dcc_cutout())
329 {
330 prepCutout_ = true;
331 auto* p = decoder_.pkt();
332 if (p)
333 {
334 p->feedback_key = ++packetId_;
335 }
336 railcomDriver_->set_feedback_key(packetId_);
337 Module::dcc_before_cutout_hook();
338 }
339 // If we are at the second half of the last 1 bit and the
340 // value of the input pin is 1, then we cannot recognize when
341 // the first half of the cutout bit disappears thus we'll
342 // never get the DCC cutout signal. We will therefore start
343 // the cutout by hand with a bit of delay.
344 else if (decoder_.state() == dcc::DccDecoder::DCC_MAYBE_CUTOUT &&
345 true) // Module::NRZ_Pin::get())
346 {
347 //Debug::RailcomDriverCutout::set(true);
348 Module::set_cap_timer_time();
349 Module::set_cap_timer_delay_usec(
350 RAILCOM_CUTOUT_PRE + Module::time_delta_railcom_pre_usec());
351 inCutout_ = true;
352 cutoutState_ = 0;
353 if (decoder_.pkt())
354 {
355 nextPacketFilled_ = true;
356 }
357 Module::trigger_os_interrupt();
358 }
359 else if (decoder_.state() == dcc::DccDecoder::DCC_CUTOUT)
360 {
361 //railcomDriver_->start_cutout();
362 //inCutout_ = true;
363 }
364 else if (decoder_.state() == dcc::DccDecoder::DCC_PACKET_FINISHED)
365 {
366 Debug::DccPacketFinishedHook::set(true);
367 if (inCutout_) {
368 //railcomDriver_->end_cutout();
369 inCutout_ = false;
370 }
371 Module::dcc_packet_finished_hook();
372 prepCutout_ = false;
373 cutout_just_finished = true;
374 Debug::DccPacketFinishedHook::set(false);
375 }
376 lastTimerValue_ = raw_new_value;
377 if (sampleActive_ && Module::NRZ_Pin::get() && !prepCutout_ &&
378 !cutout_just_finished)
379 {
380 sampleActive_ = false;
381 // The first positive edge after the sample timer expired (but
382 // outside of the cutout).
383 railcomDriver_->feedback_sample();
384 Module::after_feedback_hook();
385 }
386 }
387}
388
389template <class Module>
390__attribute__((optimize("-O3"))) void
391DccDecoder<Module>::rcom_interrupt_handler()
392{
393 Debug::DccDecodeInterrupts::set(true);
394 if (Module::int_get_and_clear_delay_event())
395 {
396 // Debug::RailcomDriverCutout::set(false);
397 switch (cutoutState_)
398 {
399 case 0:
400 {
401 Module::set_cap_timer_delay_usec(
402 RAILCOM_CUTOUT_MID + Module::time_delta_railcom_mid_usec());
403 railcomDriver_->start_cutout();
404 cutoutState_ = 1;
405 break;
406 }
407 case 1:
408 {
409 Module::set_cap_timer_delay_usec(
410 RAILCOM_CUTOUT_END + Module::time_delta_railcom_end_usec());
411 railcomDriver_->middle_cutout();
412 cutoutState_ = 2;
413 break;
414 }
415 default:
416 {
417 Module::stop_cap_timer_time();
418 Module::set_cap_timer_capture();
419 railcomDriver_->end_cutout();
420 inCutout_ = false;
421 break;
422 }
423 }
424 }
425 Debug::DccDecodeInterrupts::set(false);
426}
427
428template <class Module>
429__attribute__((optimize("-O3"))) void DccDecoder<Module>::os_interrupt_handler()
430{
431 unsigned woken = 0;
432 if (nextPacketFilled_)
433 {
434 if (packetProcessor_)
435 {
436 packetProcessor_->packet_arrived(decoder_.pkt(), railcomDriver_);
437 }
438 inputData_->advance(1);
439 nextPacketFilled_ = false;
440 inputData_->signal_condition_from_isr();
441 woken = 1;
442 decoder_.set_packet(nullptr);
443 }
444 if (!decoder_.pkt() && inputData_->space())
445 {
446 DCCPacket *next;
447 inputData_->data_write_pointer(&next);
448 decoder_.set_packet(next);
449 }
450 portYIELD_FROM_ISR(woken);
451}
Device driver for decoding a DCC signal using a Timer resource.
bool prepCutout_
True for the last bit time before the cutout, to prevent sampling fro colliding with cutout.
RailcomDriver * railcomDriver_
How many times did we lose a DCC packet due to no buffer available.
DccDecoder(const char *name, RailcomDriver *railcom_driver)
Constructor.
static const auto RAILCOM_CUTOUT_END
How many usec the railcom has to the end of the window (measured from the packet end 1 bit complete)
bool select(File *file, int mode) OVERRIDE
Device select method.
bool waitSampleForOverflow_
if true then sampling will be suspended until the timer overflows.
dcc::PacketProcessor * packetProcessor_
notified for every arrived DCC / MM packet within the interrupt.
void os_interrupt_handler()
Handles a software interrupt to FreeRTOS.
ssize_t read(File *file, void *buf, size_t count) OVERRIDE
Read from a file or device.
uint32_t cutoutState_
Which window of the cutout we are in.
unsigned lastLevel_
Seems unused?
void set_packet_processor(dcc::PacketProcessor *p)
Installs a hook that will be called in the interrupt context for each incoming packet.
static const auto RAILCOM_CUTOUT_MID
How many usec the railcom has to the middle of window (measured from the packet end 1 bit complete)
uint32_t nextSample_
Holds the timer value when we should be taking an occupancy sample the next time.
static const auto RAILCOM_CUTOUT_PRE
How many usec the railcom has before the cutout (measured from the packet end 1 bit complete)
ssize_t write(File *file, const void *buf, size_t count) OVERRIDE
Write to a file or device.
void interrupt_handler()
Handles a raw interrupt.
void disable() override
function to disable device
bool sampleActive_
true if the next edge we shall sample.
bool inCutout_
True if the current internal state is the cutout state.
void enable() override
function to enable device
void flush_buffers() override
Discards all pending buffers.
uint32_t packetId_
Counts unique identifiers for DCC packets to be returned.
void rcom_interrupt_handler()
Handles interrupt from the second timer used for railcom timing.
uint32_t lastTimerValue_
Holds the value of the free running timer at the time we captured the previous edge.
dcc::DccDecoder decoder_
DCC packet decoder state machine and internal state.
void flush()
flush all the data out of the buffer and reset the buffer.
size_t space()
Return the number of items for which space is available.
size_t pending()
Return the number of items in the queue.
void select_insert()
Add client to list of clients needing woken.
Implements a smart buffer specifically designed for character device drivers.
size_t get(T *buf, size_t items)
remove a number of items from the buffer.
static constexpr unsigned member_size()
size_t data_write_pointer(T **buf)
Get a reference to the current location in the buffer for write.
void destroy()
Destroy an existing DeviceBuffer instance.
static DeviceBuffer * create(size_t size, size_t level=0)
Create a DeviceBuffer instance.
const char * name
device name
Definition Devtab.hxx:266
Alternative for hundreds of entries.
Definition SimpleLog.hxx:69
Node information.
Definition Devtab.hxx:549
Abstract base class for railcom drivers.
State machine for decoding a DCC packet flow.
Definition Receiver.hxx:61
void set_packet(DCCPacket *pkt)
Sets where to write the decoded DCC data to.
Definition Receiver.hxx:102
DCCPacket * pkt()
Retrieves the last applied DCC packet pointer.
Definition Receiver.hxx:114
Abstract class that is used as a plugin in the DCC decoder.
#define FREAD
Workaround for missing header defines on some newlib versions.
Definition fcntl.h:53
#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
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
Removes default copy-constructor and assignment added by C++.
Definition macros.h:171
Stores a DCC packet in memory.
Definition packet.h:55
Device * next
next device in linked list
Definition Devtab.hxx:538
File information.
Definition Devtab.hxx:52