Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
TivaDCC.hxx
Go to the documentation of this file.
1
34#ifndef _FREERTOS_DRIVERS_TI_TIVADCC_HXX_
35#define _FREERTOS_DRIVERS_TI_TIVADCC_HXX_
36
37#ifndef gcc
38#define gcc
39#endif
40
41#if (!defined(TIVADCC_TIVA)) && (!defined(TIVADCC_CC3200))
42#error must define either TIVADCC_TIVA or TIVADCC_CC3200
43#endif
44
45#include <algorithm>
46#include <cstdint>
47
48#include "driverlib/interrupt.h"
49#include "driverlib/rom.h"
50#include "driverlib/rom_map.h"
51#include "driverlib/timer.h"
52#include "driverlib/uart.h"
53#include "freertos/can_ioctl.h"
54#include "inc/hw_memmap.h"
55#include "inc/hw_timer.h"
56#include "inc/hw_types.h"
57#include "inc/hw_uart.h"
58
59#ifdef TIVADCC_TIVA
60#include "driverlib/sysctl.h"
61#include "TivaGPIO.hxx"
62#else
63#include "driverlib/prcm.h"
64#include "driverlib/utils.h"
65#endif
66
68#include "Devtab.hxx"
69#include "RailcomDriver.hxx"
70#include "dcc/DccOutput.hxx"
71#include "dcc/Packet.hxx"
72#include "dcc/RailCom.hxx"
74
77extern "C" uint8_t spreadSpectrum;
78
142template<class HW>
143class TivaDCC : public Node
144{
145public:
151 TivaDCC(const char *name, RailcomDriver *railcom);
152
156 {
157 }
158
161 inline void interrupt_handler() __attribute__((always_inline));
162
166 inline void os_interrupt_handler() __attribute__((always_inline));
167
169 struct Timing {
173 uint32_t period;
175 uint32_t transition_a;
177 uint32_t transition_b;
180 uint16_t spread_min = 0;
183 uint16_t spread_max = 0;
184 };
185
186 /* WARNING: these functions (hw_init, enable_output, disable_output) MUST
187 * be static, because they will be called from hw_preinit, which happens
188 * before the C++ constructors have run. This means that at the time of
189 * calling these functions the state of the object would be undefined /
190 * uninintialized. The only safe solution is to make them static. */
192 static void hw_preinit()
193 {
194#ifdef TIVADCC_TIVA
195 MAP_SysCtlPeripheralEnable(HW::CCP_PERIPH);
196 MAP_SysCtlPeripheralEnable(HW::INTERVAL_PERIPH);
197 MAP_SysCtlPeripheralEnable(HW::RAILCOM_UART_PERIPH);
198 HW::PIN_H::hw_init();
199 HW::PIN_L::hw_init();
200 HW::RAILCOM_TRIGGER_Pin::hw_init();
201 HW::RAILCOM_UARTPIN::hw_init();
202#else
203 MAP_PRCMPeripheralClkEnable(HW::CCP_PERIPH, PRCM_RUN_MODE_CLK);
204 MAP_PRCMPeripheralClkEnable(HW::INTERVAL_PERIPH, PRCM_RUN_MODE_CLK);
205 MAP_PRCMPeripheralClkEnable(HW::RAILCOM_UART_PERIPH, PRCM_RUN_MODE_CLK);
206#endif
207 HW::Output1::hw_preinit();
208 HW::Output2::hw_preinit();
209 HW::Output3::hw_preinit();
210 MAP_UARTConfigSetExpClk(
211 HW::RAILCOM_UART_BASE, configCPU_CLOCK_HZ, 250000,
212 UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE);
213 MAP_UARTFIFOEnable(HW::RAILCOM_UART_BASE);
214 // Disables the uart receive until the railcom cutout is here.
215 HWREG(HW::RAILCOM_UART_BASE + UART_O_CTL) &= ~UART_CTL_RXE;
216 }
217
218private:
225 ssize_t read(File *file, void *buf, size_t count) OVERRIDE;
226
233 ssize_t write(File *file, const void *buf, size_t count) OVERRIDE;
234
240 int ioctl(File *file, unsigned long int key, unsigned long data) OVERRIDE;
241
242 void enable() OVERRIDE {}
243 void disable() OVERRIDE {}
247 void flush_buffers() override {};
248
250 static const size_t MAX_PKT_SIZE = 6;
251
252
255
301
307 Timing timings[NUM_TIMINGS];
308
310 enum State
311 {
312 // DCC preamble bits
313 PREAMBLE,
314 // Packet start bit
315 START,
316 // Data bits for DCC
317 DATA_0,
318 DATA_1,
319 DATA_2,
320 DATA_3,
321 DATA_4,
322 DATA_5,
323 DATA_6,
324 DATA_7,
325 // end-of-byte bit for DCC (either middle-of-packet or end-of-packet)
326 FRAME,
327 // Marklin preamble "bit" (constant negative voltage)
328 ST_MM_PREAMBLE,
329 // MM data bits. The bit number here is an offset in the current input
330 // byte, because MM packets contain 18 consecutive bits.
331 MM_DATA_0,
332 MM_DATA_1,
333 MM_DATA_2,
334 MM_DATA_3,
335 MM_DATA_4,
336 MM_DATA_5,
337 MM_DATA_6,
338 MM_DATA_7,
339 // Internal state used for end-of-packet resynchronization of timers.
340 RESYNC,
341 // State at the end of packet where we make a decision whether to go
342 // into a railcom cutout or not.
343 DCC_MAYBE_RAILCOM,
344 // If we did not generate a cutout, we generate 5 empty one bits here
345 // in case a booster wants to insert a cutout.
346 DCC_NO_CUTOUT,
347 // Counts 26 usec into the appropriate preamble bit after which to turn
348 // off output power.
349 DCC_CUTOUT_PRE,
350 // Start of railcom. Turns off output power and enables UART.
351 DCC_START_RAILCOM_RECEIVE,
352 // Point between railcom window 1 and railcom window 2 where we have to
353 // read whatever arrived for channel1.
354 DCC_MIDDLE_RAILCOM_CUTOUT,
355 // Railcom end-of-channel2 window. Reads out the UART values, and
356 // enables output power.
357 DCC_STOP_RAILCOM_RECEIVE,
358 // Same for marklin. A bit of negative voltage after the packet is over
359 // but before loading the next packet. This ensures that old marklin
360 // decoders confirm receiving the packet correctly before we go into a
361 // potential protocol switch from the next packet loaded.
362 MM_LEADOUT,
363
364 // Turn on without going through the slow start sequence.
365 POWER_IMM_TURNON,
366 };
369
384 void fill_timing(BitEnum ofs, uint32_t period_usec,
385 uint32_t transition_usec, uint32_t interval_period_usec,
386 uint32_t timing_spread_usec = 0);
387
390 {
391 if (HW::Output1::should_be_enabled())
392 {
393 HW::Output1::enable_output();
394 }
395 if (HW::Output2::should_be_enabled())
396 {
397 HW::Output2::enable_output();
398 }
399 if (HW::Output3::should_be_enabled())
400 {
401 HW::Output3::enable_output();
402 }
403 }
404
405#ifdef TIVADCC_CC3200
406 // This function is called differently in tivaware than CC3200.
407 void MAP_SysCtlDelay(unsigned ticks3) {
408 ROM_UtilsDelay(ticks3);
409 }
410#endif
411
415 static constexpr unsigned RAILCOM_CUTOUT_START_USEC = 26;
419 static constexpr unsigned RAILCOM_CUTOUT_MID_USEC = 185;
423 static constexpr unsigned RAILCOM_CUTOUT_END_USEC = 486;
424
430 unsigned seed_ = 0xb7a11bae;
431
433 static constexpr unsigned PMOD = 65213;
435 static constexpr unsigned PMUL = 52253;
437 static constexpr unsigned PADD = 42767;
441
443};
444
445
448template <class HW>
449__attribute__((optimize("-O3")))
450inline void TivaDCC<HW>::interrupt_handler()
451{
452 static int preamble_count = 0;
453 static BitEnum last_bit = DCC_ONE;
454 static int count = 0;
455 static int packet_repeat_count = 0;
456 static int bit_repeat_count = 0;
457 static const dcc::Packet *packet = &IDLE_PKT;
458 static bool resync = true;
459 BitEnum current_bit;
460 bool get_next_packet = false;
461
462 MAP_TimerIntClear(HW::INTERVAL_BASE, TIMER_TIMA_TIMEOUT);
463
464 switch (state_)
465 {
466 default:
467 case RESYNC:
468 if (packet->packet_header.is_marklin)
469 {
470 current_bit = MM_PREAMBLE;
471 state_ = ST_MM_PREAMBLE;
472 break;
473 }
474 else
475 {
476 state_ = PREAMBLE;
477 }
478 // fall through
479 case PREAMBLE:
480 {
481 current_bit = DCC_ONE;
482 // preamble zero is output twice due to the resync, so we deduct
483 // one from the count.
484 int preamble_needed = HW::dcc_preamble_count() - 1;
485 if (HW::generate_railcom_halfzero() &&
486 !packet->packet_header.send_long_preamble)
487 {
488 if (preamble_count == 0)
489 {
490 current_bit = DCC_RC_HALF_ZERO;
491 }
492 preamble_needed++;
493 }
494 if (packet->packet_header.send_long_preamble)
495 {
496 preamble_needed = 21;
497 }
498 if (++preamble_count >= preamble_needed)
499 {
500 state_ = START;
501 preamble_count = 0;
502 }
503 break;
504 }
505 case START:
506 current_bit = DCC_ZERO;
507 count = 0;
508 state_ = DATA_0;
509 break;
510 case DATA_0:
511 case DATA_1:
512 case DATA_2:
513 case DATA_3:
514 case DATA_4:
515 case DATA_5:
516 case DATA_6:
517 case DATA_7:
518 {
519 uint8_t bit = (packet->payload[count] >> (DATA_7 - state_)) & 0x01;
520 current_bit = static_cast<BitEnum>(DCC_ZERO + bit);
521 state_ = static_cast<State>(static_cast<int>(state_) + 1);
522 break;
523 }
524 case FRAME:
525 if (++count >= packet->dlc)
526 {
527 current_bit = DCC_RC_ONE; // end-of-packet bit
528 state_ = DCC_MAYBE_RAILCOM;
529 preamble_count = 0;
530 }
531 else
532 {
533 current_bit = DCC_ZERO; // end-of-byte bit
534 state_ = DATA_0;
535 }
536 break;
537 case DCC_MAYBE_RAILCOM:
538 if ((packet->packet_header.send_long_preamble == 0) &&
539 (HW::Output1::need_railcom_cutout() ||
540 HW::Output2::need_railcom_cutout() ||
541 HW::Output3::need_railcom_cutout()))
542 {
543 current_bit = DCC_RC_ONE;
544 // It takes about 5 usec to get here from the previous
545 // transition of the output.
546 // We change the time of the next IRQ.
547 MAP_TimerLoadSet(HW::INTERVAL_BASE, TIMER_A,
548 timings[RAILCOM_CUTOUT_PRE].interval_period);
549 state_ = DCC_CUTOUT_PRE;
550 }
551 else
552 {
553 railcomDriver_->no_cutout();
554 current_bit = DCC_ONE;
555 state_ = DCC_NO_CUTOUT;
556 }
557 break;
558 case DCC_NO_CUTOUT:
559 current_bit = DCC_ONE;
560 ++preamble_count;
561 // maybe railcom already sent one extra ONE bit after the
562 // end-of-packet one bit. We need four more.
563 if (preamble_count >= 4)
564 {
565 current_bit = DCC_EOP_ONE;
566 }
567 if (preamble_count >= 5)
568 {
569 // The last bit will be removed by the next packet's beginning
570 // sync.
571 get_next_packet = true;
572 }
573 break;
574 case DCC_CUTOUT_PRE:
575 current_bit = DCC_RC_ONE;
576 // It takes about 3.6 usec to get here from the transition seen on
577 // the output.
578 // We change the time of the next IRQ.
579 MAP_TimerLoadSet(HW::INTERVAL_BASE, TIMER_A,
580 timings[RAILCOM_CUTOUT_FIRST].interval_period);
581 state_ = DCC_START_RAILCOM_RECEIVE;
582 break;
583 case DCC_START_RAILCOM_RECEIVE:
584 {
585 bool rc1 = HW::Output1::need_railcom_cutout();
586 bool rc2 = HW::Output2::need_railcom_cutout();
587 bool rc3 = HW::Output3::need_railcom_cutout();
588 unsigned delay = 0;
589 // Phase 1
590 if (rc1)
591 {
592 delay =
593 std::max(delay, HW::Output1::start_railcom_cutout_phase1());
594 HW::Output1::isRailcomCutoutActive_ = 1;
595 }
596 if (rc2)
597 {
598 delay =
599 std::max(delay, HW::Output2::start_railcom_cutout_phase1());
600 HW::Output2::isRailcomCutoutActive_ = 1;
601 }
602 if (rc3)
603 {
604 delay =
605 std::max(delay, HW::Output3::start_railcom_cutout_phase1());
606 HW::Output3::isRailcomCutoutActive_ = 1;
607 }
608 // Delay
609 if (delay)
610 {
611 MAP_SysCtlDelay(usecDelay_ * delay);
612 }
613 delay = 0;
614 // Phase 2
615 if (rc1)
616 {
617 delay =
618 std::max(delay, HW::Output1::start_railcom_cutout_phase2());
619 }
620 if (rc2)
621 {
622 delay =
623 std::max(delay, HW::Output2::start_railcom_cutout_phase2());
624 }
625 if (rc3)
626 {
627 delay =
628 std::max(delay, HW::Output3::start_railcom_cutout_phase2());
629 }
630 // Delay
631 if (delay)
632 {
633 MAP_SysCtlDelay(usecDelay_ * delay);
634 }
635 // Enables UART RX.
636 railcomDriver_->start_cutout();
637 // Set up for next wakeup.
638 current_bit = DCC_RC_ONE;
639 MAP_TimerLoadSet(HW::INTERVAL_BASE, TIMER_A,
640 timings[RAILCOM_CUTOUT_SECOND].interval_period);
641 state_ = DCC_MIDDLE_RAILCOM_CUTOUT;
642 break;
643 }
644 case DCC_MIDDLE_RAILCOM_CUTOUT:
645 railcomDriver_->middle_cutout();
646 current_bit = DCC_RC_ONE;
647 MAP_TimerLoadSet(HW::INTERVAL_BASE, TIMER_A,
648 timings[RAILCOM_CUTOUT_POST].interval_period);
649 state_ = DCC_STOP_RAILCOM_RECEIVE;
650 break;
651 case DCC_STOP_RAILCOM_RECEIVE:
652 {
653 current_bit = RAILCOM_CUTOUT_POST;
654 // This causes the timers to be reinitialized so no fractional bits
655 // are left in their counters.
656 resync = true;
657 get_next_packet = true;
658 railcomDriver_->end_cutout();
659 unsigned delay = 0;
660 if (HW::Output1::isRailcomCutoutActive_)
661 {
662 delay =
663 std::max(delay, HW::Output1::stop_railcom_cutout_phase1());
664 }
665 if (HW::Output2::isRailcomCutoutActive_)
666 {
667 delay =
668 std::max(delay, HW::Output2::stop_railcom_cutout_phase1());
669 }
670 if (HW::Output3::isRailcomCutoutActive_)
671 {
672 delay =
673 std::max(delay, HW::Output3::stop_railcom_cutout_phase1());
674 }
675 // Delay
676 if (delay)
677 {
678 MAP_SysCtlDelay(usecDelay_ * delay);
679 }
680 if (HW::Output1::isRailcomCutoutActive_)
681 {
682 HW::Output1::stop_railcom_cutout_phase2();
683 }
684 HW::Output1::isRailcomCutoutActive_ = 0;
685 if (HW::Output2::isRailcomCutoutActive_)
686 {
687 HW::Output2::stop_railcom_cutout_phase2();
688 }
689 HW::Output2::isRailcomCutoutActive_ = 0;
690 if (HW::Output3::isRailcomCutoutActive_)
691 {
692 HW::Output3::stop_railcom_cutout_phase2();
693 }
694 HW::Output3::isRailcomCutoutActive_ = 0;
695 check_and_enable_outputs();
696 break;
697 }
698 case ST_MM_PREAMBLE:
699 current_bit = MM_PREAMBLE;
700 ++preamble_count;
701 if (preamble_count == 7 ||
702 preamble_count == 7 + 6 ||
703 preamble_count == 7 + 6 + 10 ||
704 preamble_count == 7 + 6 + 10 + 6)
705 {
706 // first byte contains two bits.
707 state_ = MM_DATA_6;
708 }
709 break;
710 case MM_LEADOUT:
711 // MM packets never have a cutout.
712 railcomDriver_->no_cutout();
713 current_bit = MM_PREAMBLE;
714 if (++preamble_count >= 2) {
715 get_next_packet = true;
716 }
717 break;
718 case MM_DATA_0:
719 case MM_DATA_1:
720 case MM_DATA_2:
721 case MM_DATA_3:
722 case MM_DATA_4:
723 case MM_DATA_5:
724 case MM_DATA_6:
725 {
726 uint8_t bit =
727 (packet->payload[count] >> (MM_DATA_7 - state_)) & 0x01;
728 current_bit = static_cast<BitEnum>(MM_ZERO + bit);
729 state_ = static_cast<State>(static_cast<int>(state_) + 1);
730 break;
731 }
732 case MM_DATA_7:
733 {
734 uint8_t bit =
735 (packet->payload[count] >> (MM_DATA_7 - state_)) & 0x01;
736 current_bit = static_cast<BitEnum>(MM_ZERO + bit);
737 ++count;
738 if (count == 3) {
739 state_ = ST_MM_PREAMBLE;
740 } else if (count >= packet->dlc) {
741 preamble_count = 0;
742 state_ = MM_LEADOUT;
743 } else {
744 state_ = MM_DATA_0;
745 }
746 break;
747 }
748 case POWER_IMM_TURNON:
749 current_bit = DCC_ONE;
750 packet_repeat_count = 0;
751 get_next_packet = true;
752 resync = true;
753 break;
754 }
755
756 if (resync) {
757 resync = false;
758 TDebug::Resync::toggle();
759 auto* timing = &timings[current_bit];
760 // We are syncing -- cause timers to restart counting when these
761 // execute.
762 HWREG(HW::CCP_BASE + TIMER_O_TAMR) &=
763 ~(TIMER_TAMR_TAMRSU | TIMER_TAMR_TAILD);
764 HWREG(HW::CCP_BASE + TIMER_O_TBMR) &=
765 ~(TIMER_TBMR_TBMRSU | TIMER_TBMR_TBILD);
766 HWREG(HW::INTERVAL_BASE + TIMER_O_TAMR) &=
767 ~(TIMER_TAMR_TAMRSU | TIMER_TAMR_TAILD);
768
769 // These have to happen very fast because syncing depends on it. We do
770 // direct register writes here instead of using the plib calls.
771 HWREG(HW::INTERVAL_BASE + TIMER_O_TAILR) =
772 timing->interval_period + hDeadbandDelay_ * 2;
773
774 if (!HW::H_DEADBAND_DELAY_NSEC)
775 {
776 TDebug::Resync::toggle();
777 MAP_TimerDisable(HW::CCP_BASE, TIMER_A|TIMER_B);
778 // Sets final values for the cycle.
779 MAP_TimerLoadSet(HW::CCP_BASE, TIMER_A|TIMER_B, timing->period);
780 MAP_TimerMatchSet(HW::CCP_BASE, TIMER_A, timing->transition_a);
781 MAP_TimerMatchSet(HW::CCP_BASE, TIMER_B, timing->transition_b);
782 MAP_TimerEnable(HW::CCP_BASE, TIMER_A | TIMER_B);
783
784 MAP_TimerDisable(HW::INTERVAL_BASE, TIMER_A);
785 MAP_TimerLoadSet(
786 HW::INTERVAL_BASE, TIMER_A, timing->interval_period);
787 MAP_TimerEnable(HW::INTERVAL_BASE, TIMER_A);
788 TDebug::Resync::toggle();
789
790 // Switches back to asynch timer update.
791 HWREG(HW::CCP_BASE + TIMER_O_TAMR) |=
792 (TIMER_TAMR_TAMRSU | TIMER_TAMR_TAILD);
793 HWREG(HW::CCP_BASE + TIMER_O_TBMR) |=
794 (TIMER_TBMR_TBMRSU | TIMER_TBMR_TBILD);
795 HWREG(HW::INTERVAL_BASE + TIMER_O_TAMR) |=
796 (TIMER_TAMR_TAMRSU | TIMER_TAMR_TAILD);
797 }
798 else
799 {
800
801 HWREG(HW::CCP_BASE + TIMER_O_TBMATCHR) =
802 timing->transition_b; // final
803 // since timer A starts later, the deadband delay cycle we set to be
804 // constant off (match == period)
805 HWREG(HW::CCP_BASE + TIMER_O_TAMATCHR) = hDeadbandDelay_; // tmp
806 HWREG(HW::CCP_BASE + TIMER_O_TBILR) = timing->period; // final
807 HWREG(HW::CCP_BASE + TIMER_O_TAILR) = hDeadbandDelay_; // tmp
808
809// timer synchronize (if it works...)
810#ifdef TIVADCC_TIVA
811 HWREG(TIMER0_BASE + TIMER_O_SYNC) = HW::TIMER_SYNC;
812#endif
813
814 // Switches back to asynch timer update.
815 HWREG(HW::CCP_BASE + TIMER_O_TAMR) |=
816 (TIMER_TAMR_TAMRSU | TIMER_TAMR_TAILD);
817 HWREG(HW::CCP_BASE + TIMER_O_TBMR) |=
818 (TIMER_TBMR_TBMRSU | TIMER_TBMR_TBILD);
819 HWREG(HW::INTERVAL_BASE + TIMER_O_TAMR) |=
820 (TIMER_TAMR_TAMRSU | TIMER_TAMR_TAILD);
821
822 // Sets final values for the cycle.
823 MAP_TimerLoadSet(HW::CCP_BASE, TIMER_A, timing->period);
824 MAP_TimerMatchSet(HW::CCP_BASE, TIMER_A, timing->transition_a);
825 MAP_TimerLoadSet(
826 HW::INTERVAL_BASE, TIMER_A, timing->interval_period);
827 }
828
829 last_bit = current_bit;
830 bit_repeat_count = 0;
831 if (current_bit == RAILCOM_CUTOUT_POST)
832 {
833 // RAILCOM_CUTOUT_POST purposefully misaligns the two timers. We
834 // need to resync when the next interval timer ticks to get them
835 // back.
836 resync = true;
837 }
838 else if (current_bit == DCC_RC_HALF_ZERO)
839 {
840 // After resync the same bit is output twice. We don't want that
841 // with the half-zero, so we preload the DCC preamble bit.
842 current_bit = DCC_ONE;
843 }
844 }
845 if (bit_repeat_count >= 4)
846 {
847 // Forces reinstalling the timing.
848 last_bit = NUM_TIMINGS;
849 }
850 if (last_bit != current_bit)
851 {
852 auto* timing = &timings[current_bit];
853 // The delta in ticks we add to each side of the signal.
854 uint32_t spread = 0;
855 if (spreadSpectrum)
856 {
857 spread = timing->spread_max - timing->spread_min;
858 seed_ *= PMUL;
859 seed_ += PADD;
860 seed_ %= PMOD;
861 spread = (seed_ % spread) + timing->spread_min;
862 }
863 MAP_TimerLoadSet(HW::INTERVAL_BASE, TIMER_A,
864 timing->interval_period + (spread << 1));
865 MAP_TimerLoadSet(HW::CCP_BASE, TIMER_A, timing->period + (spread << 1));
866 MAP_TimerLoadSet(HW::CCP_BASE, TIMER_B, timing->period + (spread << 1));
867 MAP_TimerMatchSet(HW::CCP_BASE, TIMER_A, timing->transition_a + spread);
868 MAP_TimerMatchSet(HW::CCP_BASE, TIMER_B, timing->transition_b + spread);
869 last_bit = current_bit;
870 bit_repeat_count = 0;
871 }
872 else
873 {
874 bit_repeat_count++;
875 }
876
877 if (get_next_packet)
878 {
879 check_and_enable_outputs();
880 TDebug::NextPacket::toggle();
881 if (packet_repeat_count) {
882 --packet_repeat_count;
883 }
884 if (packet != &IDLE_PKT && packet_repeat_count == 0)
885 {
886 packetQueue_.increment_front();
887 // Notifies the OS that we can write to the buffer.
888 MAP_IntPendSet(HW::OS_INTERRUPT);
889 resync = true;
890 } else {
891 }
892 if (!packetQueue_.empty())
893 {
894 packet = &packetQueue_.front();
895 railcomDriver_->set_feedback_key(packet->feedback_key);
896 }
897 else
898 {
899 packet = &IDLE_PKT;
900 resync = true;
901 }
902 preamble_count = 0;
903 count = 0;
904 if (!packet_repeat_count) {
905 packet_repeat_count = packet->packet_header.rept_count + 1;
906 }
907 // If we are in the repeat loop for a marklin packet, we do not do a
908 // resync to not disturb the marklin preamble (which is DC_negative).
909 if (resync || !packet->packet_header.is_marklin) {
910 state_ = RESYNC;
911 } else {
912 state_ = ST_MM_PREAMBLE;
913 }
914 }
915}
916
920static const uint32_t usec_to_clocks(uint32_t usec) {
921 return (configCPU_CLOCK_HZ / 1000000) * usec;
922}
923
927static uint32_t nsec_to_clocks(uint32_t nsec) {
928 // We have to be careful here not to underflow or overflow.
929 return ((configCPU_CLOCK_HZ / 1000000) * nsec) / 1000;
930}
931
932template <class HW>
933void TivaDCC<HW>::fill_timing(BitEnum ofs, uint32_t period_usec,
934 uint32_t transition_usec, uint32_t interval_usec, uint32_t spread_max)
935{
936 auto* timing = &timings[ofs];
937 timing->period = usec_to_clocks(period_usec);
938 timing->interval_period = usec_to_clocks(interval_usec);
939 if (transition_usec == 0) {
940 // DC voltage negative.
941 timing->transition_a = timing->transition_b = timing->period;
942 } else if (transition_usec >= period_usec) {
943 // DC voltage positive.
944 // We use the PLO feature of the timer.
945 timing->transition_a = timing->transition_b = timing->period + 1;
946 } else {
947 int32_t nominal_transition =
948 timing->period - usec_to_clocks(transition_usec);
949 timing->transition_a =
950 nominal_transition + (hDeadbandDelay_ + lDeadbandDelay_) / 2;
951 timing->transition_b =
952 nominal_transition - (hDeadbandDelay_ + lDeadbandDelay_) / 2;
953 }
954 if (spread_max > 0)
955 {
956 timing->spread_min = usec_to_clocks(1) / 2;
957 timing->spread_max = usec_to_clocks(spread_max);
958 }
959}
960
961template<class HW>
963
964template <class HW>
965TivaDCC<HW>::TivaDCC(const char *name, RailcomDriver *railcom_driver)
966 : Node(name)
967 , hDeadbandDelay_(nsec_to_clocks(HW::H_DEADBAND_DELAY_NSEC))
968 , lDeadbandDelay_(nsec_to_clocks(HW::L_DEADBAND_DELAY_NSEC))
969 , usecDelay_(nsec_to_clocks(1000) / 3)
970 , writableNotifiable_(nullptr)
971 , railcomDriver_(railcom_driver)
972{
973 state_ = PREAMBLE;
974
975 fill_timing(DCC_ZERO, 100 << 1, 100, 100 << 1, 5);
976 fill_timing(DCC_ONE, 56 << 1, 56, 56 << 1, 4);
979 fill_timing(DCC_RC_ONE, 57 << 1, 57, 57 << 1);
980
981 // The following #if switch controls whether or not the
982 // "generate_railcom_halfzero()" will actually generate a half zero bit
983 // or if it will in actuality generate a full zero bit. It was determined
984 // that the half zero workaround does not work with some older decoders,
985 // but the full zero workaround does. It also works with older decoders
986 // that needed the half zero, so it seems to be a true super-set workaround.
987 //
988 // There is an issue filed to reevaluate this after more field data is
989 // collected. The idea was to make the most minimal change necessary
990 // until more data can be collected.
991 // https://github.com/bakerstu/openmrn/issues/652
992#if 0
993 // A small pulse in one direction then a half zero bit in the other
994 // direction.
995 fill_timing(DCC_RC_HALF_ZERO, 100 + 56, 56, 100 + 56, 5);
996#else
997 // A full zero bit inserted following the RailCom cutout.
998 fill_timing(DCC_RC_HALF_ZERO, 100 << 1, 100, 100 << 1, 5);
999#endif
1000
1001 // At the end of the packet the resync process will happen, which means that
1002 // we modify the timer registers in synchronous mode instead of double
1003 // buffering to remove any drift that may have happened during the packet.
1004 // This means that we need to kick off the interval timer a bit earlier than
1005 // nominal to compensate for the CPU execution time. At the same time we
1006 // stretch the negative side of the output waveform, because the next packet
1007 // might be marklin. Stretching avoids outputting a short positive glitch
1008 // between the negative half of the last dcc bit and the fully negative
1009 // marklin preamble.
1011 DCC_EOP_ONE, (56 << 1) + 20, 56, (56 << 1) - HW::RESYNC_DELAY_USEC);
1012
1013 fill_timing(MM_ZERO, 208, 26, 208, 2);
1014 fill_timing(MM_ONE, 208, 182, 208, 2);
1015 // Motorola preamble is negative DC signal.
1016 fill_timing(MM_PREAMBLE, 208, 0, 208);
1017
1018 unsigned h_deadband = 2 * (HW::H_DEADBAND_DELAY_NSEC / 1000);
1019 unsigned railcom_part = 0;
1020 unsigned target =
1021 RAILCOM_CUTOUT_START_USEC + HW::RAILCOM_CUTOUT_START_DELTA_USEC;
1022 fill_timing(RAILCOM_CUTOUT_PRE, 56 << 1, 56, target - railcom_part);
1023 railcom_part = target;
1024
1025 target = RAILCOM_CUTOUT_MID_USEC + HW::RAILCOM_CUTOUT_MID_DELTA_USEC;
1026 fill_timing(RAILCOM_CUTOUT_FIRST, 56 << 1, 56, target - railcom_part);
1027 railcom_part = target;
1028
1029 target = RAILCOM_CUTOUT_END_USEC + HW::RAILCOM_CUTOUT_END_DELTA_USEC;
1030 fill_timing(RAILCOM_CUTOUT_SECOND, 56 << 1, 56, target - railcom_part);
1031 railcom_part = target;
1032
1033 static_assert((5 * 56 * 2 - 56 + HW::RAILCOM_CUTOUT_POST_DELTA_USEC) >
1034 RAILCOM_CUTOUT_END_USEC + HW::RAILCOM_CUTOUT_END_DELTA_USEC,
1035 "railcom cutout too long");
1036 target = 5 * 56 * 2 - 56 + HW::RAILCOM_CUTOUT_POST_DELTA_USEC;
1037 unsigned remaining_high = target - railcom_part;
1038 // remaining time until 5 one bits are complete. For the PWM timer we have
1039 // some fraction of the high part, then a full low side, then we stretch
1040 // the low side to avoid the packet transition glitch.
1041 fill_timing(RAILCOM_CUTOUT_POST, remaining_high + 56 + 20, remaining_high,
1042 remaining_high + 56 + h_deadband +
1043 HW::RAILCOM_CUTOUT_POST_NEGATIVE_DELTA_USEC);
1044
1045 // We need to disable the timers before making changes to the config.
1046 MAP_TimerDisable(HW::CCP_BASE, TIMER_A);
1047 MAP_TimerDisable(HW::CCP_BASE, TIMER_B);
1048
1049#ifdef TIVADCC_TIVA
1050 MAP_TimerClockSourceSet(HW::CCP_BASE, TIMER_CLOCK_SYSTEM);
1051 MAP_TimerClockSourceSet(HW::INTERVAL_BASE, TIMER_CLOCK_SYSTEM);
1052#endif
1053 MAP_TimerConfigure(HW::CCP_BASE, TIMER_CFG_SPLIT_PAIR |
1054 TIMER_CFG_A_PWM |
1055 TIMER_CFG_B_PWM);
1056 MAP_TimerControlStall(HW::CCP_BASE, TIMER_BOTH, true);
1057
1058
1059 // This will cause reloading the timer values only at the next period
1060 // instead of immediately. The PLO bit needs to be set to allow for DC
1061 // voltage output.
1062 HWREG(HW::CCP_BASE + TIMER_O_TAMR) |=
1063 TIMER_TAMR_TAPLO | TIMER_TAMR_TAMRSU | TIMER_TAMR_TAILD;
1064 HWREG(HW::CCP_BASE + TIMER_O_TBMR) |=
1065 TIMER_TBMR_TBPLO | TIMER_TBMR_TBMRSU | TIMER_TBMR_TBILD;
1066
1067 HWREG(HW::INTERVAL_BASE + TIMER_O_TAMR) |=
1068 TIMER_TAMR_TAMRSU | TIMER_TAMR_TAILD;
1069
1070 MAP_TimerConfigure(HW::INTERVAL_BASE, TIMER_CFG_SPLIT_PAIR |
1071 TIMER_CFG_A_PERIODIC);
1072 MAP_TimerControlStall(HW::INTERVAL_BASE, TIMER_A, true);
1073
1074 MAP_TimerControlLevel(HW::CCP_BASE, TIMER_A, HW::PIN_H_INVERT);
1075 MAP_TimerControlLevel(HW::CCP_BASE, TIMER_B, !HW::PIN_L_INVERT);
1076
1077 MAP_TimerLoadSet(HW::CCP_BASE, TIMER_A, timings[DCC_ONE].period);
1078 MAP_TimerLoadSet(HW::CCP_BASE, TIMER_B, hDeadbandDelay_);
1079 MAP_TimerLoadSet(HW::INTERVAL_BASE, TIMER_A, timings[DCC_ONE].period + hDeadbandDelay_ * 2);
1080 MAP_TimerMatchSet(HW::CCP_BASE, TIMER_A, timings[DCC_ONE].transition_a);
1081 MAP_TimerMatchSet(HW::CCP_BASE, TIMER_B, timings[DCC_ONE].transition_b);
1082
1083 MAP_IntDisable(HW::INTERVAL_INTERRUPT);
1084 MAP_IntPrioritySet(HW::INTERVAL_INTERRUPT, 0x20);
1085 MAP_TimerIntEnable(HW::INTERVAL_BASE, TIMER_TIMA_TIMEOUT);
1086
1087 MAP_TimerEnable(HW::CCP_BASE, TIMER_A);
1088 MAP_TimerEnable(HW::CCP_BASE, TIMER_B);
1089 MAP_TimerEnable(HW::INTERVAL_BASE, TIMER_A);
1090
1091#ifdef TIVADCC_TIVA
1092 MAP_TimerSynchronize(TIMER0_BASE, HW::TIMER_SYNC);
1093#endif
1094
1095 MAP_TimerLoadSet(HW::CCP_BASE, TIMER_B, timings[DCC_ONE].period);
1096 MAP_TimerLoadSet(
1097 HW::INTERVAL_BASE, TIMER_A, timings[DCC_ONE].interval_period);
1098 MAP_IntEnable(HW::INTERVAL_INTERRUPT);
1099
1100 // The OS interrupt does not come from the hardware timer.
1101 MAP_TimerIntDisable(HW::CCP_BASE, 0xFFFFFFFF);
1102 // The OS interrupt comes under the freertos kernel.
1103 MAP_IntPrioritySet(HW::OS_INTERRUPT, configKERNEL_INTERRUPT_PRIORITY);
1104 MAP_IntEnable(HW::OS_INTERRUPT);
1105}
1106
1113template<class HW>
1114ssize_t TivaDCC<HW>::read(File *file, void *buf, size_t count)
1115{
1116 return -EINVAL;
1117}
1118
1125template<class HW>
1126__attribute__((optimize("-O3")))
1127ssize_t TivaDCC<HW>::write(File *file, const void *buf, size_t count)
1128{
1129 if (count != sizeof(dcc::Packet))
1130 {
1131 return -EINVAL;
1132 }
1133
1134 OSMutexLock l(&lock_);
1135
1136 if (packetQueue_.full())
1137 {
1138 return -ENOSPC;
1139 }
1140
1141 dcc::Packet* packet = &packetQueue_.back();
1142 memcpy(packet, buf, count);
1143
1144 // Duplicates the marklin packet if it came single.
1145 if (packet->packet_header.is_marklin) {
1146 if (packet->dlc == 3) {
1147 packet->dlc = 6;
1148 packet->payload[3] = packet->payload[0];
1149 packet->payload[4] = packet->payload[1];
1150 packet->payload[5] = packet->payload[2];
1151 } else {
1152 HASSERT(packet->dlc == 6);
1153 }
1154 }
1155
1156 packetQueue_.increment_back();
1157 static uint8_t flip = 0;
1158 if (++flip >= 4)
1159 {
1160 flip = 0;
1161 HW::flip_led();
1162 }
1163
1164 return count;
1165}
1166
1173template<class HW>
1174int TivaDCC<HW>::ioctl(File *file, unsigned long int key, unsigned long data)
1175{
1176 if (IOC_TYPE(key) == CAN_IOC_MAGIC &&
1177 IOC_SIZE(key) == NOTIFIABLE_TYPE &&
1178 key == CAN_IOC_WRITE_ACTIVE) {
1179 Notifiable* n = reinterpret_cast<Notifiable*>(data);
1180 HASSERT(n);
1181 // If there is no space for writing, we put the incomng notification
1182 // into the holder. Otherwise we notify it immediately.
1183 if (packetQueue_.full())
1184 {
1185 portENTER_CRITICAL();
1186 if (packetQueue_.full())
1187 {
1188 // We are in a critical section now. If we got into this
1189 // branch, then the buffer was full at the beginning of the
1190 // critical section. If the hardware interrupt kicks in now,
1191 // and sets the os_interrupt to pending, the os interrupt will
1192 // not happen until we leave the critical section, and thus the
1193 // swap will be in effect by then.
1194 std::swap(n, writableNotifiable_);
1195 }
1196 portEXIT_CRITICAL();
1197 }
1198 if (n) {
1199 n->notify();
1200 }
1201 return 0;
1202 }
1203 errno = EINVAL;
1204 return -1;
1205}
1206
1207template <class HW>
1208__attribute__((optimize("-O3")))
1209inline void TivaDCC<HW>::os_interrupt_handler()
1210{
1211 if (!packetQueue_.full()) {
1212 Notifiable* n = writableNotifiable_;
1213 writableNotifiable_ = nullptr;
1214 if (n) n->notify_from_isr();
1215 }
1216}
1217
1218
1219#endif // _FREERTOS_DRIVERS_TI_TIVADCC_HXX_
uint8_t spreadSpectrum
If non-zero, enables the jitter feature to spread the EMC spectrum of DCC signal.
Definition TivaDCC.hxx:77
static const uint32_t usec_to_clocks(uint32_t usec)
Converts a time length given in microseconds to the number of clock cycles.
Definition TivaDCC.hxx:920
static uint32_t nsec_to_clocks(uint32_t nsec)
Converts a time length given in nanoseconds to the number of clock cycles.
Definition TivaDCC.hxx:927
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,...
Node information.
Definition Devtab.hxx:549
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
Abstract base class for railcom drivers.
A device driver for sending DCC packets.
Definition TivaDCC.hxx:144
static const size_t MAX_PKT_SIZE
maximum packet size we can support
Definition TivaDCC.hxx:250
Timing timings[NUM_TIMINGS]
Precalculated bit timings (translated to clock cycles).
Definition TivaDCC.hxx:307
static constexpr unsigned RAILCOM_CUTOUT_END_USEC
Standard timing value of the railcom cutout end, measured from the transition of the end-of-packet-on...
Definition TivaDCC.hxx:423
static constexpr unsigned PADD
Parameters for a linear RNG: additive.
Definition TivaDCC.hxx:437
RailcomDriver * railcomDriver_
Will be notified for railcom cutout events.
Definition TivaDCC.hxx:428
int ioctl(File *file, unsigned long int key, unsigned long data) OVERRIDE
Request an ioctl transaction.
Definition TivaDCC.hxx:1174
FixedQueue< dcc::Packet, HW::Q_SIZE > packetQueue_
Packets still waiting to be sent.
Definition TivaDCC.hxx:426
void enable() OVERRIDE
function to enable device
Definition TivaDCC.hxx:242
void disable() OVERRIDE
function to disable device
Definition TivaDCC.hxx:243
~TivaDCC()
Destructor.
Definition TivaDCC.hxx:155
static constexpr unsigned PMOD
Parameters for a linear RNG: modulus.
Definition TivaDCC.hxx:433
ssize_t write(File *file, const void *buf, size_t count) OVERRIDE
Write to a file or device.
Definition TivaDCC.hxx:1127
void interrupt_handler()
Handle an interrupt.
Definition TivaDCC.hxx:450
BitEnum
Bit timings that we store and precalculate.
Definition TivaDCC.hxx:258
@ DCC_ZERO
Zero bit for DCC (this is the longer)
Definition TivaDCC.hxx:260
@ MM_ZERO
Zero bit for MM packet, which is a short pulse in one direction, then a long pulse in the other.
Definition TivaDCC.hxx:294
@ DCC_EOP_ONE
One bit for DCC that we generate at the end of packet transition.
Definition TivaDCC.hxx:266
@ RAILCOM_CUTOUT_POST
This is not a bit, but specifies when to wake up during the railcom cutout.
Definition TivaDCC.hxx:289
@ MM_PREAMBLE
Long negative DC pulse to act as a preamble for a Marklin packet.
Definition TivaDCC.hxx:291
@ DCC_RC_ONE
One bit for DCC that we generate during the railcom cutout.
Definition TivaDCC.hxx:270
@ DCC_ONE
One bit for DCC (this is the shorter)
Definition TivaDCC.hxx:262
@ RAILCOM_CUTOUT_FIRST
This is not a bit, but specifies when to wake up during the railcom cutout.
Definition TivaDCC.hxx:282
@ DCC_RC_HALF_ZERO
Half zero bit which is sent directly after the railcom cutout is over.
Definition TivaDCC.hxx:275
@ RAILCOM_CUTOUT_SECOND
This is not a bit, but specifies when to wake up during the railcom cutout.
Definition TivaDCC.hxx:286
@ RAILCOM_CUTOUT_PRE
This is not a bit, but specifies when to wake up during the railcom cutout.
Definition TivaDCC.hxx:278
@ MM_ONE
One bit for MM packet, which is a long pulse in one direction, then a short pulse in the other.
Definition TivaDCC.hxx:297
void os_interrupt_handler()
Handles a software interrupt to FreeRTOS.
Definition TivaDCC.hxx:1209
int usecDelay_
1 usec of delay in clock count
Definition TivaDCC.hxx:304
State
Internal state machine states.
Definition TivaDCC.hxx:311
State state_
Current state of internal state machine.
Definition TivaDCC.hxx:368
static dcc::Packet IDLE_PKT
idle packet
Definition TivaDCC.hxx:254
Notifiable * writableNotifiable_
Notify this when we have free buffers.
Definition TivaDCC.hxx:427
int lDeadbandDelay_
high->low deadband delay in clock count
Definition TivaDCC.hxx:303
void fill_timing(BitEnum ofs, uint32_t period_usec, uint32_t transition_usec, uint32_t interval_period_usec, uint32_t timing_spread_usec=0)
Prepares a timing entry.
Definition TivaDCC.hxx:933
static constexpr unsigned PMUL
Parameters for a linear RNG: multiplier.
Definition TivaDCC.hxx:435
static constexpr unsigned RAILCOM_CUTOUT_START_USEC
Standard timing value of when the railcom cutout should start, measured from the transition of the en...
Definition TivaDCC.hxx:415
void flush_buffers() override
Discards all pending buffers.
Definition TivaDCC.hxx:247
ssize_t read(File *file, void *buf, size_t count) OVERRIDE
Read from a file or device.
Definition TivaDCC.hxx:1114
static void hw_preinit()
Initializes the DCC output hardware.
Definition TivaDCC.hxx:192
void check_and_enable_outputs()
Checks each output and enables those that need to be on.
Definition TivaDCC.hxx:389
TivaDCC()
Default constructor.
static constexpr unsigned RAILCOM_CUTOUT_MID_USEC
Standard timing value of the railcom cutout middle, measured from the transition of the end-of-packet...
Definition TivaDCC.hxx:419
unsigned seed_
Seed for a pseudorandom sequence.
Definition TivaDCC.hxx:430
int hDeadbandDelay_
low->high deadband delay in clock count
Definition TivaDCC.hxx:302
#define NOTIFIABLE_TYPE
ioctl minor type used for the read/write active notifiable integration.
#define CAN_IOC_WRITE_ACTIVE
write active ioctl.
#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 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
uint8_t send_long_preamble
1: send long preamble instead of packet.
Definition packet.h:69
uint8_t is_marklin
0: DCC packet, 1: motorola packet.
Definition packet.h:62
uint8_t rept_count
The packet will be sent 1 + rept_count times to the wire.
Definition packet.h:74
uintptr_t feedback_key
An opaque key used by the hardware driver to attribute feedback information to the source of the pack...
Definition packet.h:104
uint8_t payload[DCC_PACKET_MAX_PAYLOAD]
Packet payload bytes.
Definition packet.h:97
uint8_t dlc
Specifies the number of used payload bytes.
Definition packet.h:95
File information.
Definition Devtab.hxx:52
Structure for supporting bit timing.
Definition TivaDCC.hxx:169
uint32_t transition_a
When to transition output A; must be within the period.
Definition TivaDCC.hxx:175
uint32_t interval_period
In clock cycles: period of the interval timer.
Definition TivaDCC.hxx:171
uint32_t transition_b
When to transition output B; must be within the period.
Definition TivaDCC.hxx:177
uint32_t period
In clock cycles: period of the PWM timer.
Definition TivaDCC.hxx:173
Represents a command to be sent to the track driver.
Definition Packet.hxx:52