program dsPIC33EP_NMRAnetCommandStation; // ****************************************************************************** // // * Copyright: // (c) Mustangpeak Software 2012. // // The contents of this file are subject to the GNU GPL v3 licence/ you maynot use // this file except in compliance with the License. You may obtain a copy of the // License at http://www.gnu.org/licenses/gpl.html // // * Revision History: // 2012-02-01: Created // 2012-11-13: Converted to the dsPIC33EP // 2012-11-13: Updated to v2 of the NMRAbus Library // // * Description: // Implements a NMRABus based Command Station // // // // ****************************************************************************** uses NMRAnetStateMachine, NMRAnetDefines, NMRAnetAppCallbacks, NMRAnetDCC, MCU_Setup_dsPIC33EP64GP502, _25AAxxxx, NMRAnetBufferPools, NMRAnetNode, HelperFunctions, NodeIDs, NMRAnetServiceMode, ServiceModeDefines; {$I Options.inc} var // EEPROM SPI PINS CS_Bank_0 : sbit at LATB6_bit; CS_Bank_0_Direction : sbit at TRISB6_bit; EE_PROM_Hold : sbit at LATB5_bit; EEPROM_Hold_Direction : sbit at TRISB5_bit; // DCC SIGNAL PINS H_Bridge_A_Lo : sbit at LATA1_bit; H_Bridge_A_Hi : sbit at LATB0_bit; H_Bridge_B_Lo : sbit at LATB1_bit; H_Bridge_B_Hi : sbit at LATB4_bit; H_Bridge_A_Lo_Direction : sbit at TRISA1_bit; H_Bridge_A_Hi_Direction : sbit at TRISB0_bit; H_Bridge_B_Lo_Direction : sbit at TRISB1_bit; H_Bridge_B_Hi_Direction : sbit at TRISB4_bit; DCC_Programming_ACK_Pin : sbit at RB2_bit; // Input so use the port and not the latch DCC_Programming_ACK_Direction : sbit at TRISB2_bit; Ack_TimerEnabled_Bit : sbit at TON_T5CON_bit; // ***************************************************************************** // INTERRUPTS // ***************************************************************************** var DCCTime: Word; ServiceMode: Boolean; Timer: DWord; procedure INTERRUPT_DCC_Timer(); iv IVT_ADDR_T1INTERRUPT; begin T1IF_bit := 0; // Clear the Flag // Toggle the Bridge H_Bridge_A_Lo := 0; // Bridge Off H_Bridge_A_Hi := 0; // Bridge Off H_Bridge_B_Lo := 0; // Bridge Off H_Bridge_B_Hi := 0; // Bridge Off LATB15_bit := 0; // DCC Bus Transmitter LATB14_bit := 0; // DCC Bus Transmitter if ServiceMode then begin if ServiceModeInfo.iStateMachine < STATE_SERVICEMODE_DONE then begin if Programming.TX_Flags.TRANSMITTING_FLAG_DCC_PIN_BIT = 1 then begin LATB15_bit := 1; // DCC Bus Transmitter H_Bridge_A_Lo := 1; H_Bridge_B_Hi := 1; end else begin LATB14_bit := 1; // DCC Bus Transmitter H_Bridge_A_Hi := 1; H_Bridge_B_Lo := 1; end; // Now we can update the xxxx_DCC_PIN_BIT flags for the next 56us time slot ServiceMode_56us_TimeTick; NMRA_DCC_TransmitterStateMachine(@Programming); ServiceMode_StateMachine(@Programming); end end else begin if Track.TX_Flags.TRANSMITTING_FLAG_RAIL_COM_CUTOUT_BIT = 1 then begin if Track.TX_Flags.TRANSMITTING_FLAG_DCC_PIN_BIT = 1 then begin LATB15_bit := 1; // DCC Bus Transmitter H_Bridge_A_Lo := 1; H_Bridge_B_Hi := 1; PR1 := DCC_TIMER_29US; end else begin if PR1 = DCC_TIMER_29US then PR1 := DCC_TIMER_56US; H_Bridge_A_Lo := 1; H_Bridge_B_Lo := 1; end; end else begin if Track.TX_Flags.TRANSMITTING_FLAG_DCC_PIN_BIT = 1 then begin LATB15_bit := 1; // DCC Bus Transmitter H_Bridge_A_Lo := 1; H_Bridge_B_Hi := 1; end else begin LATB14_bit := 1; // DCC Bus Transmitter H_Bridge_A_Hi := 1; H_Bridge_B_Lo := 1; end; end; // Now we can update the xxxx_DCC_PIN_BIT flags for the next 56us time slot NMRA_DCC_56us_TimeTick(@Track); // < 1us NMRA_DCC_TransmitterStateMachine(@Track); // < 5us NMRA_DCC_LoadPacketIntoTransmitterStateMachine(@Track, PREAMBLE_BIT_COUNT_NORMAL); // < 11us Max end; Inc(Timer); end; procedure INTERRUPT_100ms_Timer(); iv IVT_ADDR_T2INTERRUPT; // Called once every 100m var i: Integer; begin T2IF_bit := 0; // Clear the Flag for i := 0 to Nodes.AllocatedCount - 1 do NMRAnetStateMachine_100ms_Timer(Nodes.AllocatedList[i]); NMRAnetBufferPools_100ms_TimeTick; ServiceMode_100ms_TimeTick end; procedure INTERRUPT_5ms_Timer(); iv IVT_ADDR_T5INTERRUPT; // Called once every 5m during Service MOde begin T5IF_bit := 0; // Clear the Flag ServiceMode_5ms_TimeTick end; function IsPrintableChar(C: Char): Boolean; begin Result := ((Ord( C) >= 32) and (Ord( C) <= 126)) or ((Ord( C) >= 128) and (Ord( C) <= 255)) end; procedure DumpEEProm(ASCII: Boolean); var Buffer: array[0..15] of byte; i, j, Offset: Integer; s: string[64]; Output: string[256]; begin i := 0; Offset := 0; while i < 1024 do // Max is 8192 but that is too much begin _25AAxxxx_Read(EEPROM_BANK_0, Offset, 16, @Buffer[0]); WordToHex(Offset, s); Output := s + ' | '; for j := 0 to 15 do begin s := '' ; if ASCII then s := s + Char(Buffer[j]) else ByteToHex(Buffer[j], s); Output := Output + s + ' '; end; UART1_Write_Text(Output + LF); Offset := Offset + 16; Inc(i); end; end; // ******************* // MAIN LOOP // ******************* var ActiveNode, TempNode: PNMRAnetNode; i, j: Integer; x, y: Word; TickTime: real; LastRxTime, LastStateMachineTime, LastDCCTime: Word; ADC_Ticker: DWord; PrevTrackCurrent, TrackCurrent: Word; ProxyData: PDccProxyData; CV_Value, WriteVal: Byte; RunningAllCVs: Boolean; begin ANSELA := 0; ANSELB := 0; ServiceMode := False; TRISA4_bit := 0; LATA4_bit := 1; // Output _25AAxxxx_Initialize; NMRAnetStateMachine_Initialize(MUSTANGPEAK_ID_0_HI, {MUSTANGPEAK_SERVICETRACK_NODE_ID_0_LO} MUSTANGPEAK_COMMANDSTATION_ID_0_LO); MCU_Setup_Initialize; // Start the timers and perpherials last NMRA_DCC_Initialize; ServiceMode_Initialize; H_Bridge_A_Lo := 0; // Bridge Off H_Bridge_A_Hi := 0; // Bridge Off H_Bridge_B_Lo := 0; // Bridge Off H_Bridge_B_Hi := 0; // Bridge Off H_Bridge_A_Lo_Direction := 0; // Output H_Bridge_A_Hi_Direction := 0; // Output H_Bridge_B_Lo_Direction := 0; // Output H_Bridge_B_Hi_Direction := 0; // Output LATB15_bit := 1; LATB14_bit := 0; TRISB15_bit := 0; // Output TRISB14_bit := 0; // Output UART1_Write_Text('Starting 1'+LF); {$IFNDEF DCCTIMER_DISABLE} TON_T1CON_bit := 1; // Start the DCC Timer Delay_ms(10); NMRA_DCC_Packet_Init; // Send our 20 Idle Packets per the spec, note we are not on the OLCB bus yet so this will block until done. {$ENDIF} UART1_Write_Text('Starting 2'+LF); x := 0; y := 0; j := 0; i := 0; MaxTime_RX := 0; LastRxTime := 0; MaxTime_StateMachine := 0; LastStateMachineTime := 0; DCCTime := 0; LastDCCTime := 0; CV_Value := 0; RunningAllCVs := False; Timer := 0; WriteVal := 0; j := 0; ADC_Ticker := 0; PrevTrackCurrent := 0; // Timer 1 = DCC; Timer 2 = GP Timer; Timer 5 = Service Mode Timer TON_T3CON_bit := 1; // Turn on Timer 3 to time loops TON_T4CON_bit := 1; // Turn on Timer 3 to time loops ADC1_Read(0); // cheating, intializes and set to Analog input // NMRAnetNode_Allocate; // One Proxy ready to go while (TRUE) do begin ActiveNode := NMRAnetNode_NextNode; if ActiveNode <> PNMRAnetNode( nil) then begin { Inc(ADC_Ticker); if ADC_Ticker > 200000 then begin TrackCurrent := ADC1_Get_Sample(0) - 3; TrackCurrent := (TrackCurrent + PrevTrackCurrent) div 2; FloatToStr( (Double( TrackCurrent) * 0.520 * 0.0032227), s1); // .00322266 V/Bit * 0.520A/V * Bits = PrevTrackCurrent := TrackCurrent; TruncFloat(@s1, 3, 6); UART1_Write_Text('Track Current: '+ s1 + ' Amps' + LF); ADC_Ticker := 0; end; } TMR3 := 0; NMRAnetStateMachine_Process(ActiveNode); if TMR3 > MaxTime_StateMachine then MaxTime_StateMachine := TMR3; { if MaxTime_StateMachine > LastStateMachineTime then begin LastStateMachineTime := MaxTime_StateMachine; TickTime := real( LastStateMachineTime) * 16.6666666e-9; // nano seconds TickTime := TickTime*1e6; // micro seconds (us) FloatToStr(TickTime, s1) ; UART1_Write_Text('StateMachineTime = ' + s1 + 'us'+LF); end; if MaxTime_Rx > LastRxTime then begin LastRxTime := MaxTime_Rx; TickTime := real( LastRxTime) * 16.6666666e-9; // nano seconds TickTime := TickTime*1e6; // micro seconds (us) FloatToStr(TickTime, s1) ; UART1_Write_Text('Rx Interrupt = ' + s1 + 'us'+LF); end; if DCCTime > LastDCCTime then begin LastDCCTime:= DCCTime; TickTime := real( LastDCCTime) * 16.6666666e-9; // nano seconds TickTime := TickTime*1e6; // micro seconds (us) FloatToStr(TickTime, s1) ; UART1_Write_Text('DCC Interrupt = ' + s1 + 'us'+LF); end; } if UART1_Data_Ready then begin case UART1_Read of 'P' : begin //PrintDMABuffers end; 'X' : begin NMRAnetStateMachine_TrySendVerifyNodeID(ActiveNode, 0); end; 'A', 'a' : begin NMRAnetNode_Allocate; end; 'D', 'd' : begin TempNode := NMRAnetNode_FindFirstVirtualNode; if TempNode <> nil then begin ProxyData := GetProxyData( TempNode); ProxyData^.State := ProxyData^.State and not PS_DCC_ADDRESS_ALLOCATED; end; end; 'F', 'f' : begin TempNode := NMRAnetNode_FindFirstVirtualNode; if TempNode <> nil then begin // BETTER TO SET A STATE OF THE NODE TO SPIN UNTIL IT CAN SEND THE MESSAGE while not NMRAnetStateMachine_TrySendAliasMapReset(TempNode) do; NMRAnetNode_Release(TempNode); end; end; 'Z', 'z' : begin _25AAxxxx_Erase(EEPROM_BANK_0); end; 'V' : begin DumpEEProm(True); end; 'v' : begin DumpEEProm(False); end; 'M', 'm' : begin for i := 0 to MAX_NODE_COUNT - 1 do begin LongWordToStr(Nodes.RawList[i].ConfigurationAddress, s1); UART1_Write_Text('Offset: ' + s1 +LF); end; for i := 0 to MAX_NODE_COUNT - 1 do begin LongWordToStr(Nodes.RawList[i].RAMAddress, s1); UART1_Write_Text('RAM Offset: ' + s1 +LF); end; end; 'S' : begin Timer := 0; ServiceMode_EngageServiceMode(@Programming, True, SERVICEMODE_READ, SERVICE_MODE_TYPE_DIRECT_BYTE, CV_Value, 0); RunningAllCVs := True; end; 's' : begin Timer := 0; ServiceMode_EngageServiceMode(@Programming, True, SERVICEMODE_READ, SERVICE_MODE_TYPE_DIRECT_BIT, CV_Value, 0); RunningAllCVs := True; end; 'T' : begin ServiceMode_EngageServiceMode(@Programming, True, SERVICEMODE_WRITE, SERVICE_MODE_TYPE_DIRECT_BYTE, CV_Value, WriteVal); Inc(WriteVal); end; 't' : begin ServiceMode_EngageServiceMode(@Programming, True, SERVICEMODE_WRITE, SERVICE_MODE_TYPE_DIRECT_BIT, CV_Value, 0); end; '0' : CV_Value := 0; 'Q', 'q' : begin RunningAllCVs := False; end; 'r' : begin Dec(RailComCount) // Decrease the number of 56us windows to black for a Cutout end; 'R' : begin Inc(RailComCount) // Increase the number of 56us windows to black for a Cutout end; 'E' : begin Track.TX_Flags.TRANSMITTING_FLAG_RAIL_COM_ENABLED := 1; // Enabled RailCom end; 'e' : begin Track.TX_Flags.TRANSMITTING_FLAG_RAIL_COM_ENABLED := 0; // Disable RailCom end; end; end; end; if ServiceMode_ReadResults then begin ByteToSTr(CV_Value + 1, s1); UART1_Write_Text('ServiceMode: CV = ' + s1); ByteToSTr(ServiceModeInfo.Value, s1); UART1_Write_Text(' Value = ' + s1); case ServiceModeInfo.ServiceModeResult of SERVICE_MODE_RESULT_DATA_NOT_FOUND : begin UART1_Write_Text(' Data Not Received'); if ServiceModeInfo.Flags.PROGRAMMING_ACK_TIMER_DETECTED_LEADINGEDGE_BIT = 1 then UART1_Write_Text(': Ack Hi Found' + LF); if ServiceModeInfo.Flags.PROGRAMMING_ACK_FAILED_TO_DETECT_TRAILINGEDGE_BIT = 1 then UART1_Write_Text(': Ack Low at 5ms'); if ServiceModeInfo.Flags.PROGRAMMING_ACK_DETECTED_BIT = 1 then UART1_Write_Text(': Ack High at 5ms'); UART1_Write_Text(LF); end; SERVICE_MODE_RESULT_RESPONSE_DIRECT_MODE_READ : begin if ServiceModeInfo.ServiceModeType = SERVICE_MODE_TYPE_DIRECT_BYTE then UART1_Write_Text(' Direct Mode - Bit' + LF) else UART1_Write_Text(' Direct Mode - Byte' + LF) end; // SERVICE_MODE_RESULT_RESPONSE_PAGE_MODE : UART1_Write_Text(' Page Mode' + LF); SERVICE_MODE_RESULT_SHORTCIRCUIT : UART1_Write_Text(' Short Detected' + LF); end; Inc(CV_Value); if (CV_Value = 0) or not RunningAllCVs then begin ServiceMode_Quit; ByteToStr(CV_Value, s1); UART1_Write_Text('Time to read ' + s1); FloatToStr(Timer * 56e-6, s1); TruncFloat(@s1, 3, 6); UART1_Write_Text(' CVs: ' + s1 + ' Seconds' + LF); end else begin case ServiceModeInfo.ServiceModeType of SERVICE_MODE_TYPE_DIRECT_BYTE : ServiceMode_EngageServiceMode(@Programming, False, SERVICEMODE_READ, SERVICE_MODE_TYPE_DIRECT_BYTE, CV_Value, 0); SERVICE_MODE_TYPE_DIRECT_BIT : ServiceMode_EngageServiceMode(@Programming, False, SERVICEMODE_READ, SERVICE_MODE_TYPE_DIRECT_BIT, CV_Value, 0); end end end; if C1IE_bit = 0 then UART1_Write_Text('CAN Disabled!'); end; end.