// ****************************************************************************** // // Copyright: // (c) Mustangpeak, 2013. // // 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: // 2013-03-29: Created // // * Description: // Common Service Mode functions // // ****************************************************************************** unit CommonServiceMode; {$I Options.inc} uses NMRAnetDCC, ServiceModeDefines; type TServiceModePtr = procedure(BufferPtr: PDCCBufferInfo); PServiceModePtr = ^TServiceModePtr; procedure CommonServiceMode_DirectLoadTransmitter(BufferPtr: PDCCBufferInfo; RegisterOffset, ReadWrite, DataByte: Byte); // Valid for Paged, Register, Address Modes Only procedure CommonServiceMode_DirectLoadTransmitterDirectMode(BufferPtr: PDCCBufferInfo; CV_Address: Word; InstructionCode, DataByte: Byte); procedure CommonServiceMode_DirectLoadTransmitterPagePreset(BufferPtr: PDCCBufferInfo); procedure CommonServiceMode_PowerOnCycle(BufferPtr: PDCCBufferInfo; InstructionCount, NextState: Word); procedure CommonServiceMode_ResetCycle(BufferPtr: PDCCBufferInfo; InstructionPtr: PServiceModePtr; PacketCount: Word; NextState: Word; PrepareForAck: Boolean); procedure CommonServiceMode_SendInstructions(BufferPtr: PDCCBufferInfo; SendInstruction, AckHandler, NoAckHandler: PServiceModePtr; InstructionCount: Word); procedure CommonServiceMode_NoAckHandler(BufferPtr: PDCCBufferInfo); procedure CommonServiceMode_AckHandlerByte(BufferPtr: PDCCBufferInfo); procedure CommonServiceMode_ResultReady(BufferPtr: PDCCBufferInfo); implementation procedure ResetACK; begin ServiceModeInfo.State.PROGRAMMING_ACK_SCANNING_FOR_ACK_BIT := 1; // Start Looking ServiceModeInfo.State.PROGRAMMING_ACK_DETECTED_BIT := 0; ServiceModeInfo.State.PROGRAMMING_ACK_TIMER_DETECTED_LEADINGEDGE_BIT := 0; ServiceModeInfo.State.PROGRAMMING_ACK_FAILED_TO_DETECT_TRAILINGEDGE_BIT := 0; end; // *************************************************************************** // procedure CommonServiceMode_AckHandlerByte // // Parameters: None // // Result: None // // Description: // *************************************************************************** procedure CommonServiceMode_AckHandlerByte(BufferPtr: PDCCBufferInfo); begin case ServiceModeInfo.ServiceModeType of SERVICE_MODE_TYPE_DIRECT_BYTE : ServiceModeInfo.ServiceModeResult := SERVICE_MODE_RESULT_RESPONSE_DIRECT_BYTE_MODE; SERVICE_MODE_TYPE_DIRECT_BIT : ServiceModeInfo.ServiceModeResult := SERVICE_MODE_RESULT_RESPONSE_DIRECT_BIT_MODE; SERVICE_MODE_TYPE_REGISTER : ServiceModeInfo.ServiceModeResult := SERVICE_MODE_RESULT_RESPONSE_REGISTER_MODE; SERVICE_MODE_TYPE_PAGED : ServiceModeInfo.ServiceModeResult := SERVICE_MODE_RESULT_RESPONSE_PAGED_MODE; end; if ServiceModeInfo.ReadWrite = SERVICEMODE_WRITE then begin // Write ServiceModeInfo.iInstructionCountSent := 1; ServiceModeInfo.iStateMachine := STATE_SERVICEMODE_WRITE_RECOVERY; end else begin // Read ServiceModeInfo.Value := ServiceModeInfo.ReadIndex; ServiceModeInfo.iStateMachine := STATE_SERVICEMODE_RESULTS_READY; end; NMRA_DCC_LoadResetPacketIntoTransmitter(BufferPtr, PREAMBLE_BIT_COUNT_SERVICEMODE); end; // *************************************************************************** // procedure CommonServiceMode_NoAckHandler // // Parameters: None // // Result: None // // Description: // *************************************************************************** procedure CommonServiceMode_NoAckHandler(BufferPtr: PDCCBufferInfo); begin if ServiceModeInfo.ReadWrite = SERVICEMODE_READ then begin Inc(ServiceModeInfo.ReadIndex); // Try the next Value if ServiceModeInfo.Value = ServiceModeInfo.ReadIndex then // Have we run all the way around the horn once begin ServiceModeInfo.ServiceModeResult := SERVICE_MODE_RESULT_NO_ACK; // Run all possible values and an ACK was not found ServiceModeInfo.iStateMachine := STATE_SERVICEMODE_RESULTS_READY end else begin ServiceModeInfo.iInstructionCountSent := 1; // Run the next possible Value cycle ServiceModeInfo.iStateMachine := STATE_SERVICEMODE_RESET_CYCLE; // Rurun it end; end else begin // No Ack, just quit ServiceModeInfo.iInstructionCountSent := 1; ServiceModeInfo.ServiceModeResult := SERVICE_MODE_RESULT_NO_ACK; ServiceModeInfo.iStateMachine := STATE_SERVICEMODE_RESULTS_READY; end; NMRA_DCC_LoadResetPacketIntoTransmitter(BufferPtr, PREAMBLE_BIT_COUNT_SERVICEMODE); end; // *************************************************************************** // procedure CommonServiceMode_DirectLoadTransmitterDirectMode // // Parameters: None // // Result: None // // Description: // *************************************************************************** procedure CommonServiceMode_DirectLoadTransmitterDirectMode(BufferPtr: PDCCBufferInfo; CV_Address: Word; InstructionCode, DataByte: Byte); var i: Integer; begin BufferPtr^.TX_TransmittingPacket.PacketBytes[0] := %01110000 or InstructionCode or Hi(CV_Address); BufferPtr^.TX_TransmittingPacket.PacketBytes[1] := Lo(CV_Address); BufferPtr^.TX_TransmittingPacket.PacketBytes[2] := DataByte; BufferPtr^.TX_TransmittingPacket.Flags := 3; BufferPtr^.TX_XOR_Byte := 0; for i := 0 to 2 do BufferPtr^.TX_XOR_Byte := BufferPtr^.TX_XOR_Byte xor BufferPtr^.TX_TransmittingPacket.PacketBytes[i]; BufferPtr^.TX_PreambleBitCount := PREAMBLE_BIT_COUNT_SERVICEMODE; end; // *************************************************************************** // procedure CommonServiceMode_DirectLoadTransmitterPagePreset // // Parameters: None // // Result: None // // Description: // *************************************************************************** procedure CommonServiceMode_DirectLoadTransmitterPagePreset(BufferPtr: PDCCBufferInfo); begin BufferPtr^.TX_TransmittingPacket.PacketBytes[0] := %01111101; BufferPtr^.TX_TransmittingPacket.PacketBytes[1] := %00000001; BufferPtr^.TX_TransmittingPacket.Flags := 2; BufferPtr^.TX_XOR_Byte := %011111100; BufferPtr^.TX_PreambleBitCount := PREAMBLE_BIT_COUNT_SERVICEMODE; end; // *************************************************************************** // procedure CommonServiceMode_DirectLoadTransmitter // // Parameters: None // // Result: None // // Description: // *************************************************************************** procedure CommonServiceMode_DirectLoadTransmitter(BufferPtr: PDCCBufferInfo; RegisterOffset, ReadWrite, DataByte: Byte); var i: Integer; begin BufferPtr^.TX_TransmittingPacket.PacketBytes[0] := %01110000 or ReadWrite or RegisterOffset; BufferPtr^.TX_TransmittingPacket.PacketBytes[1] := DataByte; BufferPtr^.TX_TransmittingPacket.Flags := 2; BufferPtr^.TX_XOR_Byte := 0; for i := 0 to 1 do BufferPtr^.TX_XOR_Byte := BufferPtr^.TX_XOR_Byte xor BufferPtr^.TX_TransmittingPacket.PacketBytes[i]; BufferPtr^.TX_PreambleBitCount := PREAMBLE_BIT_COUNT_SERVICEMODE; end; // *************************************************************************** // procedure CommonServiceMode_PowerOnCycle // // Parameters: None // // Result: None // // Description: // *************************************************************************** procedure CommonServiceMode_PowerOnCycle(BufferPtr: PDCCBufferInfo; InstructionCount, NextState: Word); begin if ServiceModeInfo.iInstructionCountSent < InstructionCount then begin Inc(ServiceModeInfo.iInstructionCountSent); NMRA_DCC_LoadIdlePacketIntoTransmitter(BufferPtr, PREAMBLE_BIT_COUNT_SERVICEMODE) end else begin ServiceModeInfo.iInstructionCountSent := 1; NMRA_DCC_LoadResetPacketIntoTransmitter(BufferPtr, PREAMBLE_BIT_COUNT_SERVICEMODE); // Next State is always a Reset Packet for all Modes ServiceModeInfo.iStateMachine := NextState end end; // *************************************************************************** // procedure CommonServiceMode_ResetCycle // // Parameters: None // // Result: None // // Description: // *************************************************************************** procedure CommonServiceMode_ResetCycle(BufferPtr: PDCCBufferInfo; InstructionPtr: PServiceModePtr; PacketCount: Word; NextState: Word; PrepareForAck: Boolean); begin if ServiceModeInfo.iInstructionCountSent < PacketCount then begin Inc(ServiceModeInfo.iInstructionCountSent); NMRA_DCC_LoadResetPacketIntoTransmitter(BufferPtr, PREAMBLE_BIT_COUNT_SERVICEMODE) end else begin ServiceModeInfo.iInstructionCountSent := 1; if InstructionPtr <> nil then InstructionPtr(BufferPtr) else NMRA_DCC_LoadResetPacketIntoTransmitter(BufferPtr, PREAMBLE_BIT_COUNT_SERVICEMODE); // If the sender did not pass an Instruction pointer load the transmitter then fill in with an Idle Packet ServiceModeInfo.iStateMachine := NextState; if PrepareForAck then ResetACK; end; end; // *************************************************************************** // procedure CommonServiceMode_SendInstructions // // Parameters: None // // Result: None // // Description: // *************************************************************************** procedure CommonServiceMode_SendInstructions(BufferPtr: PDCCBufferInfo; SendInstruction, AckHandler, NoAckHandler: PServiceModePtr; InstructionCount: Word); begin if ServiceModeInfo.State.PROGRAMMING_ACK_DETECTED_BIT = 1 then begin // ACK detected and we are done AckHandler(BufferPtr); end else if ServiceModeInfo.State.PROGRAMMING_ACK_FAILED_TO_DETECT_TRAILINGEDGE_BIT = 1 then begin ServiceModeInfo.Value := 0; ServiceModeInfo.ServiceModeResult := SERVICE_MODE_RESULT_SHORT_ACK; NMRA_DCC_LoadResetPacketIntoTransmitter(BufferPtr, PREAMBLE_BIT_COUNT_SERVICEMODE); // Per 9.2.3 ServiceModeInfo.iStateMachine := STATE_SERVICEMODE_RESULTS_READY; end else if ServiceModeInfo.State.PROGRAMMING_ACK_TIMER_DETECTED_LEADINGEDGE_BIT = 1 then NMRA_DCC_LoadResetPacketIntoTransmitter(BufferPtr, PREAMBLE_BIT_COUNT_SERVICEMODE) // Ack start detected so drop into Reset Packets are either success or failure handled above after 5ms else begin if ServiceModeInfo.iInstructionCountSent < InstructionCount then begin Inc(ServiceModeInfo.iInstructionCountSent); SendInstruction(BufferPtr); end else begin NoAckHandler(BufferPtr); ServiceModeInfo.iInstructionCountSent := 1; end end end; // *************************************************************************** // procedure CommonServiceMode_ResultReady // // Parameters: None // // Result: None // // Description: // *************************************************************************** procedure CommonServiceMode_ResultReady(BufferPtr: PDCCBufferInfo); begin // Spin here until the application calls ServiceMode_ReadResults to grab the Result and move to Done NMRA_DCC_LoadResetPacketIntoTransmitter(BufferPtr, PREAMBLE_BIT_COUNT_SERVICEMODE); // Per 9.2.3 // Don't come out of Service Mode end; end.