;******************************************************************* ; E-LAN MODULE ; DCC Programmer ; MERG ; prog.asm ; Issue 1 ; Date 6/11/98 ; Written by J.G.Eato ; ;**************************************************************************************** ; Amendment History ; ; Initial Version. ; Issue 1A ; ; 17/1/99 ; Issue 1B Bug: After Binary selected during programming, consequent reading of CVs only ; allowed CV address entry in binary digits. ; Fix:"Binary invalid flag" cleared before requesting Read CV address ; ; Bug: Entering binary CV value, when an address of more than 1 digit entered, ; input of CV value was off the LCD screen. ; Fix: Remove padding after CV and "=" ; ; 19/1/99 ; Issue 1C Add: Error trapping added to ensure 0 < CV Address <=1024 and CV value <=255 ; ; 25/1/99 ; Issue 1D Paged / Direct read and write routines rewritten to put repeated code ; into a subroutine, to recover program space. ; ; OBS: Direct bit reading could not differentiate between a CV read of 255 and ; no decoder ie neither return an ACK. ; Fix: Make a direct byte read after determining the CV value, a decoder should ; reply with an ACK ; ; Bug: Occasional switch bouce when reading keyboard ; Fix: Delay found to be 500uS because wrong subroutine called. Delay_500 was called ; instead of X_Delay_500. Debounce value also increased from 50mS to 100mS ; ; Half a second delay introduced between entering request and power being ; applied to the track. ; 13/2/99 ; Issue 1E Half second delay removed ; Power on sequence modified to give a string of 20 reset packets. ; Original gave simply DCC zero bits which did not put the MERG ; decoders into DCC mode so causing an 'Overload' state. ; Changed interrupt timer reload value from EC to EA so a 1 bit ; is 58 microsec. (NMRA standard) ; ; 1/5/99 ; Isue 1F Number of reset packets reduced to 14 so overload test occurs ; after 100 millisec as per NMRA recommendation. Timer ; put back to EC to maintain compatibility with ZTC (which does not ; program at 58 microsecs) ; ; 7/5/01 ; Issue 1G Mods by Mike Bolton ; Bit direct option removed. Direct Mode is now bit read by default. ; Removed separate ZTC direct mode. (use page mode) ; Added long address programming in one step. ; If CV17 selected, allows 4 digit number and programs CV17 and CV18 ; with long address. Individual numbers can be entered in CV17 and ; CV18 in binary mode. Tested 12/5/01 ; ; 19/12/02 ; progg2 As progg but modified for use with PIC 16F627 or 16F628 ; ; 16/10/05 ; Issue 1H Mods by Peter Ibbotson ; Added call to recovery_time when ACK sent in bit direct mode by ; Digitrax decoders. Adapted from the SPROG source code by Andrew ; Crosland. ;07/01/07 Code progg1H is progg2H but changed back for a 16F84 ; ;**************************************************************************************** ; LIST p=16F84 ; PIC16F84is the target processor __CONFIG h'3FF1' ; PUTimer on, WDT off, XT clock ; 4 Mhz Crystal include "p16f84.inc" ; *********************** ; Register Declarations ; *********************** CBLOCK 0x0C Mem_Start ; Start of memory Buffer ; Keyboard Input buffer Buffer_1 ; Keyboard Input buffer Buffer_2 ; Keyboard Input buffer Buffer_3 ; Keyboard Input buffer Buffer_4 ; Keyboard Input buffer Buffer_5 ; Keyboard Input buffer Buffer_6 ; Keyboard Input buffer Buffer_7 ; Keyboard Input buffer Counter ; Temporary counter. Only to be used within a subroutine. Temp_Store ; Temporary store. Only to be used within a subroutine. Number_Counter ; Keyboard input Routine Number_Max ; Keyboard input Routine Hex_Convert ; Hex to 2 ascii characters CV_Address ; CV Packet formation CV_Value ; CV Packet formation CV_Checksum ; CV Packet formation CV_Counter ; CV Packet formation CV_Byte ; CV Packet formation CV_Read ; CV Packet formation CV_Bit_Counter ; CV Packet formation CV_Register ; CV Packet formation Key ; Translated Value of Key pressed Text_Offset ; Used when outputting text string to LCD Text_Store ; Used when outputting text string to LCD Delay ; System Delay X_Delay ; System Delay DCC_Buffer ; DCC Output routine DCC_Register ; DCC Output routine DCC_Counter ; DCC Output routine Bit_Flag ; DCC Out indicates if "0" or "1" DEC_MSB ; Used in Hex to decimal Conversion DEC_LSB1 ; Used in Hex to decimal Conversion DEC_LSB ; Used in Hex to decimal Conversion Flag_Register ; General flag register Base_Flag ; Number base Decimal or Binary ;for interrupt stack W_TEMP ; W register interrupt "stack" S_TEMP ; Status register interrupt "stack" ENDC CBLOCK 0X50 Mem_Stop ; End of memory ENDC ;*********************** ; Literal Declarations ;*********************** ; Key transalation values Key_0 equ 0 Key_1 equ 1 Key_2 equ 2 Key_3 equ 3 Key_4 equ 4 Key_5 equ 5 Key_6 equ 6 Key_7 equ 7 Key_8 equ 8 Key_9 equ 9 Escape equ 0A Delete equ 0B Program equ 0C Read equ 0D Mode equ 0E Enter equ 0F ; PORTA LCD_RS equ 0 ; LCD Register Select LCD_E equ 1 ; LCD Display Enable Sounder equ 2 ; Error and acknowledgement bleep Power equ 3 ; Turn on power to Service track DCC_Out equ 4 ; DCC Ouput waveform ; PORTB Row_1 equ 0 ; Keyboard Overload equ 0 ; Overload Input Row_2 equ 1 ; Keyboard DCC_Ack equ 1 ; Acknowledge Row_3 equ 2 ; Keyboard Row_4 equ 3 ; Keyboard Column_1 equ 4 ; Keyboard Column_2 equ 5 ; Keyboard Column_3 equ 6 ; Keyboard Column_4 equ 7 ; Keyboard ; GENERAL ; Flag_Register Tx_Ready equ 0 ; Indicates status of transmit register (Ready to send) Start_Bit equ 1 ; Indicates if a start bit is required with this byte Preamble equ 2 ; Indicates this byte is preamble Ack equ 3 ; Set indicates ack received Mode_Set equ 4 ; Selects Paged or Direct mode Long equ 5 ; For long address mode Bit_Verify equ 6 ; Bit Verification flag Overload_Flag equ 7 ; Indicates overload ; Base_Flag Binary equ 0 ; Set if binary input selected Binary_Valid equ 1 ; Set if Binary input is valid ; ********* ; Vectors ; ********* BEGIN GOTO Initialise NOP NOP NOP GOTO SERVICE_INTERRUPT ; Interrupt Vector ; ****************** ; LCD Text Strings ; ****************** Text_DCC RETLW 44 ; D RETLW 43 ; C RETLW 43 ; C RETLW 0A0 ; + End of text Text_Program RETLW 50 ; P RETLW 52 ; R RETLW 4F ; O RETLW 47 ; G RETLW 52 ; R RETLW 41 ; A RETLW 0CD ; M + End of text Text_Programmer RETLW 4D ; M RETLW 45 ; E RETLW 0D2 ; R + End of text Text_Version RETLW 47 ; G RETLW 20 ; RETLW 56 ; V RETLW 45 ; E RETLW 52 ; R RETLW 2E ; . RETLW 31 ; 1 RETLW 0C8 ; H + End of text Text_Read RETLW 52 ; R RETLW 45 ; E RETLW 41 ; A RETLW 44 ; D RETLW 0A0 ; + End of text Text_CV RETLW 43 ; C RETLW 0D6 ; V+ End of text Text_Overload RETLW 4F ; O RETLW 56 ; V RETLW 45 ; E RETLW 52 ; R RETLW 4C ; L RETLW 4F ; O RETLW 41 ; A RETLW 0C4 ; D + End of text Text_Error RETLW 20 ; RETLW 45 ; E RETLW 52 ; R RETLW 52 ; R RETLW 4F ; O RETLW 0D2 ; R + End of text Text_Equals RETLW 0BD ; = + End of text Text_Mode RETLW 4D ; M RETLW 4F ; O RETLW 44 ; D RETLW 45 ; E RETLW 0A0 ; + End of text Text_OK RETLW 20 ; RETLW 4F ; O RETLW 4B ; K RETLW 20 ; RETLW 20 ; RETLW 0A0 ; + End of text Text_Page RETLW 50 ; P RETLW 41 ; A RETLW 47 ; G RETLW 45 ; E RETLW 0A0 ; + End of text Text_Direct RETLW 44 ; D RETLW 49 ; I RETLW 52 ; R RETLW 45 ; E RETLW 43 ; C RETLW 54 ; T RETLW 0A0 ; + End of text Text_No_Ack RETLW 20 ; RETLW 4E ; N RETLW 4F ; O RETLW 20 ; RETLW 41 ; A RETLW 43 ; C RETLW 0CB ; K+ End of text ; *********************************** ; Keyboard translation Lookup table ; *********************************** Translate_key MOVFW Temp_Store ADDWF PCL,F ; add offset to program counter RETLW Program ; Column 4 Row 1 Keyboard Layout. RETLW Read ; Column 4 Row 2 1 2 3 Prog RETLW Mode ; Column 4 Row 3 4 5 6 Read RETLW Enter ; Column 4 Row 4 7 8 9 Mode RETLW Key_3 ; Column 3 Row 1 Escape 0 Delete Enter RETLW Key_6 ; Column 3 Row 2 RETLW Key_9 ; Column 3 Row 3 RETLW Delete ; Column 3 Row 4 RETLW Key_2 ; Column 2 Row 1 RETLW Key_5 ; Column 2 Row 2 RETLW Key_8 ; Column 2 Row 3 RETLW Key_0 ; Column 2 Row 4 RETLW Key_1 ; Column 1 Row 1 RETLW Key_4 ; Column 1 Row 2 RETLW Key_7 ; Column 1 Row 3 RETLW Escape ; Column 1 Row 4 ; ********************************************* ; 4 bit Hex to Ascii translation Lookup table ; ********************************************* H_A_Convert ADDWF PCL,F ; add offset to program counter RETLW 30 ; 0 RETLW 31 ; 1 RETLW 32 ; 2 RETLW 33 ; 3 RETLW 34 ; 4 RETLW 35 ; 5 RETLW 36 ; 6 RETLW 37 ; 7 RETLW 38 ; 8 RETLW 39 ; 9 RETLW 41 ; A RETLW 42 ; B RETLW 43 ; C RETLW 44 ; D RETLW 45 ; E RETLW 46 ; F ; **************** ; Key Jump Table ; **************** Key_Jump ; Decode key ADDWF PCL,F ; add offset to program counter GOTO GN_Numeric ; 0 GOTO GN_Numeric ; 1 GOTO GN_Numeric ; 2 GOTO GN_Numeric ; 3 GOTO GN_Numeric ; 4 GOTO GN_Numeric ; 5 GOTO GN_Numeric ; 6 GOTO GN_Numeric ; 7 GOTO GN_Numeric ; 8 GOTO GN_Numeric ; 9 GOTO GN_Escape ; Escape GOTO GN_Delete ; Delete GOTO GN_Error ; Program GOTO GN_Error ; Read GOTO GN_Mode ; Mode GOTO GN_Enter ; Enter ; ************* ; Setup Ports ; ************* SETUP_PORTS ; 0 =Output 1 = Input ; Set Port B ; Bit 7 Output Keyboard Column 4 / LCD Display Bit 7 ; Bit 6 Output Keyboard Column 3 / LCD Display Bit 6 ; Bit 5 Output Keyboard Column 2 / LCD Display Bit 5 ; Bit 4 Output Keyboard Column 1 / LCD Display Bit 4 ; Bit 3 Input Keyboard Row 4 / Acknowledge ; Bit 2 Input Keyboard Row 3 / Overload ; Bit 1 Input Keyboard Row 2 ; Bit 0 Input Keyboard Row 1 ; Port B setup = 0F ; Set Port A bit ; Bit 4 Output DCC Output ; Bit 3 Output Power on ; Bit 2 Output Sounder ; Bit 1 Output LCD Enable ; Bit 0 Output LCD Register Select ; Port A setup = 00 ;movlw 7 ;turn off comparators ;movwf CMCON ;Not applicable to 16F84 BSF STATUS,5 ; Select Bank 1 MOVLW 0F ; Set Port B MOVWF TRISB ; MOVLW 00 ; Set Port A MOVWF TRISA ; BCF STATUS,5 ; Select Bank 0 RETURN ; ************* ; Setup Timer ; ************* ; Set timer to give a 58uS interrupt SETUP_TIMER BSF STATUS,RP0 ; Select Bank 1 MOVLW b'01010000' ; Bit 7 Port B Pull up enable ; Bit 5 Internal clock ; Bit 3 Prescaler assigned to Timer ; Bits 0-2 Prescaler ratio 2:1 MOVWF OPTION_REG BCF STATUS,RP0 ; Select Bank 0 ; ****************** ; Setup Interrupts ; ****************** SETUP_INTERRUPT BSF INTCON,T0IE ; Enable TIMER 1 interrupt BSF INTCON,GIE ; Globally enable interrupts MOVLW 1 MOVWF DCC_Counter ; Data Tx bit counter CLRF Bit_Flag RETURN ; ******************** ; Service Interrupts ; ******************** ; Interrupts serviced every 58uS SERVICE_INTERRUPT MOVWF W_TEMP ; Save W Register SWAPF STATUS,W MOVWF S_TEMP ; Save Status Register BCF STATUS,RP0 ; Select Bank 0 BCF INTCON,T0IF ; Clear Timer interrupt flag NOP ; 1uS pad MOVLW 0xEA ; Reset timer to 29uS MOVWF TMR0 ; Note Prescaler set to 2:1 ; giving interrupt every 58uS for NMRA ; Note. Use EC to give 52uS (for ZTC) ; compatibility ; *************** ; Timed Interupt ; *************** MOVFW Bit_Flag ADDWF PCL,F ; add offset to program counter GOTO First_Edge GOTO Second_Edge_Bit1 GOTO Second_midpoint_Bit0 GOTO Second_Edge_Bit0 GOTO First_midpoint_Bit0 First_Edge MOVLW 10 XORWF PORTA,F ; toggle data line MOVLW 01 BTFSS DCC_Register,7 ; Check next data bit MOVLW 04 MOVWF Bit_Flag ; Bit Flag = 1 if data bit 1 ; Bit Flag = 5 if data bit 0 MOVLW 8 XORWF DCC_Counter,W BNZ FE_Exit BTFSC Flag_Register,Start_Bit GOTO FE_Exit ; If Start bit flag is clear then send a start bit MOVLW 4 MOVWF Bit_Flag BSF Flag_Register,Start_Bit ; Set Start_Bit Flag GOTO Exit FE_Exit RLF DCC_Register,F ; Rotate DCC data byte for next bit GOTO Get_Data Second_Edge_Bit1 ; Output a "1" MOVLW 10 XORWF PORTA,F ; toggle data line DECF Bit_Flag,F GOTO Exit Second_midpoint_Bit0 ; Output a "0" CLRF Bit_Flag GOTO Exit Second_Edge_Bit0 MOVLW 10 XORWF PORTA,F ; toggle data line First_midpoint_Bit0 DECF Bit_Flag,F GOTO Exit Get_Data DECFSZ DCC_Counter,F GOTO Exit ; Reload Tx Register every 8 bits MOVFW DCC_Buffer ; Copy buffer into Tx Register MOVWF DCC_Register MOVLW 8 MOVWF DCC_Counter ; Reload loop counter BSF Flag_Register,Tx_Ready ; Set Tx Ready Flag Exit SWAPF S_TEMP,W MOVWF STATUS ; Reload Status register SWAPF W_TEMP, F SWAPF W_TEMP, W ; Reload W register BSF INTCON,GIE ; Globally enable interrupts RETFIE ; ************* ; 500uS Delay ; ************* Delay500 ; With 4Mhz clock. MOVLW 0A4 MOVWF Delay Delay500_Loop DECFSZ Delay,F GOTO Delay500_Loop RETURN ; ******* ; Delay ; ******* X_Delay500 ; Delay = 'W' * 500uS MOVWF X_Delay X_Delay500_Loop CALL Delay500 DECFSZ X_Delay,F GOTO X_Delay500_Loop RETURN ; ****************** ; DCC Send a Byte ; ****************** DCC_Send_Byte ; On Entry W holds the DCC byte BTFSS PORTB,DCC_Ack ; If acknowledge flagged by Loco set ack flag BSF Flag_Register,Ack BTFSS Flag_Register,Tx_Ready GOTO DCC_Send_Byte ; Loop until Tx ready flag set MOVWF DCC_Buffer ; Load DCC Buffer BCF Flag_Register,Tx_Ready ; Clear Tx ready Flag BTFSC Flag_Register,Preamble ; If clear this byte is preamble BCF Flag_Register,Start_Bit ; If clear a start bit will be sent with this byte RETURN ; ****************** ; LCD Write a Byte ; ****************** LCD_Write_Ascii ADDLW 30 LCD_Write ; on entry W holds the byte MOVWF Temp_Store ; save the byte MOVLW 0F ANDWF PORTB,F ; Clear the data lines MOVLW 0F0 ; Upper nibble ANDWF Temp_Store,W IORWF PORTB,F ; Put data on DB4,7 BSF PORTA,LCD_E ; Pulse LCD enable high. BCF PORTA,LCD_E MOVLW 0F ANDWF PORTB,F ; Clear the data lines SWAPF Temp_Store,F ; Lower nibble MOVLW 0F0 ANDWF Temp_Store,W IORWF PORTB,f ; Put data on DB4,7 BSF PORTA,LCD_E ; Pulse LCD enable high. BCF PORTA,LCD_E MOVLW 0D MOVWF Delay LCD_E_Loop DECFSZ Delay,F ; 45 uS delay GOTO LCD_E_Loop RETURN ; ************ ; LCD Delete ; ************ LCD_Delete BCF PORTA,LCD_RS ; Selects Instruction register MOVLW 10 ; Shift cursor left CALL LCD_Write BSF PORTA,LCD_RS ; Selects data register MOVLW 20 CALL LCD_Write ; Print BCF PORTA,LCD_RS ; Selects Instruction register MOVLW 10 ; Shift cursor left CALL LCD_Write BSF PORTA,LCD_RS ; Select Data register RETURN ; ******** ; LCD CR ; ******** LCD_CR BCF PORTA,LCD_RS ; Selects Instruction register MOVLW 0C0 ; CR /LF CALL LCD_Write BSF PORTA,LCD_RS ; Selects data register RETURN ; ********** ; LCD Home ; ********** LCD_HOME BCF PORTA,LCD_RS ; Selects Instruction register MOVLW 02 ; Home CALL LCD_Write BSF PORTA,LCD_RS ; Selects data register RETURN ; ************************ ; Initialise LCD Display ; ************************ INITIALISE_LCD BCF PORTA,LCD_RS BCF PORTA,LCD_E ; Selects Instruction register MOVLW 1E CALL Delay500 ; wait 15mS MOVLW 33 CALL LCD_Write MOVLW 32 CALL LCD_Write MOVLW 28 CALL LCD_Write MOVLW 06 CALL LCD_Write MOVLW 0C CALL LCD_Write MOVLW 01 CALL LCD_Write MOVLW 64 ; Wait 50mS CALL X_Delay500 BSF PORTA,LCD_RS ; Select Data register RETURN ; ****************** ; Print LCD String ; ****************** LCD_String ; On Entry W holds address of String MOVWF Text_Offset LS_Loop1 CALL LS_Sub1 MOVWF Text_Store ; Save character so it can be tested MOVLW 7F ANDWF Text_Store,W ; Clear top bit ; Send Character to LCD CALL LCD_Write INCF Text_Offset,F ; Move offset to next character MOVFW Text_Offset BTFSS Text_Store,7 ; If character bit 7=1 then end of text reached GOTO LS_Loop1 RETURN LS_Sub1 MOVWF PCL ; ********************************************* ; Convert an HEX byte to 2 ascii characters ; And write to LCD Display ; ********************************************* Hex_ascii_LCD ; On Entry "W" holds hex byte MOVWF Hex_Convert SWAPF Hex_Convert,F MOVLW 0F ; Get MSB nibbles ANDWF Hex_Convert,W CALL H_A_Convert ; Convert to ascii CALL LCD_Write ; Send To Display SWAPF Hex_Convert,F MOVLW 0F ; Get LSB nibbles ANDWF Hex_Convert,W CALL H_A_Convert ; Convert to ascii CALL LCD_Write ; Send To Display RETURN ; ********************************************* ; Convert an HEX byte to 3 BCD characters ; And write to LCD Display ; ********************************************* Hex_BCD ; On Entry "W" holds hex byte MOVWF Counter ; Save hex value INCF Counter,F CLRF DEC_MSB CLRF DEC_LSB1 CLRF DEC_LSB HBCD_Loop1 DECFSZ Counter,F GOTO HBCD_Skip1 MOVLW 30 ADDWF DEC_MSB,W CALL LCD_Write MOVLW 30 ADDWF DEC_LSB1,W CALL LCD_Write MOVLW 30 ADDWF DEC_LSB,W CALL LCD_Write RETURN HBCD_Skip1 INCF DEC_LSB,F MOVLW 0A ; if the byte is greater than 9 then add 6 SUBWF DEC_LSB,W SKPC GOTO HBCD_Loop1 INCF DEC_LSB1,F CLRF DEC_LSB MOVLW 0A ; if the byte is greater than 9 then add 6 SUBWF DEC_LSB1,W SKPC GOTO HBCD_Loop1 INCF DEC_MSB,F CLRF DEC_LSB1 GOTO HBCD_Loop1 ; **************************************** ; Convert 4 Digit Decimal to 2 byte Hex ; **************************************** ; On Entry Decimal number held in Buffer(MSB),Buffer1, ; Buffer2 and Buffer3 ; On Exit Hex number held in Buffer6(MSB), Buffer7(LSB) Convert_to_Hex CLRF Buffer_6 ; Clear temporary store CLRF Buffer_7 ; Clear temporary store MOVFW Buffer ; Get MSB MOVWF Counter MOVLW 03 ; 1000d = 03E8h MOVWF Buffer_5 ; Buffer_5 used as a temporary store MOVLW 0E8 ; Load Thousands counter CALL CtH_Sub1 CLRF Buffer_5 MOVFW Buffer_1 MOVWF Counter ; 100d = 64h MOVLW 64 ; Load Hundreds counter CALL CtH_Sub1 MOVFW Buffer_2 MOVWF Counter ; 10d = 0Ah MOVLW 0A ; Load Tens counter CALL CtH_Sub1 MOVFW Buffer_3 ; Add units ADDWF Buffer_7,F SKPNC INCF Buffer_6,F RETURN CtH_Sub1 MOVWF Temp_Store INCF Counter,F ; Adjust counter CtH_Loop1 DECFSZ Counter,F GOTO CtH_Skip1 RETURN CtH_Skip1 MOVFW Temp_Store ADDWF Buffer_7,F ; LSB SKPNC INCF Buffer_6,F MOVFW Buffer_5 ADDWF Buffer_6,F ; MSB GOTO CtH_Loop1 ; ********************************* ; Display CV value on LCD Display ; ********************************* Display_CV CALL Hex_BCD ; Display CV value in decimal CALL LCD_HOME MOVLW 0A ; Delay wait for LCD CALL X_Delay500 MOVLW Text_Read ; Display CV Read CALL LCD_String MOVLW 20 ; print CALL LCD_Write MOVLW 62 ; print "b" CALL LCD_Write ; Display CV in Binary value MOVLW 8 MOVWF CV_Counter DCV_Loop1 MOVLW 30 ; ascii "0" RLF CV_Value,F SKPNC MOVLW 31 ; ascii "1" CALL LCD_Write DECFSZ CV_Counter,F GOTO DCV_Loop1 RETURN ; *************** ; Scan Keyboard ; *************** ; D0 Row 1 Input ; D1 Row 2 Input ; D2 Row 3 Input ; D3 Row 4 Input ; D4 Column 1 Output ; D5 Column 2 Output ; D6 Column 3 Output ; D7 Column 4 Output Scan_Keyboard ; On exit: Key holds a translated value for the key pressed, or ; Bit 7 of Key is set if no key pressed. MOVLW 0F0 IORWF PORTB,F ; Deselect all columns BSF Key,7 ; Set key pressed bit BCF PORTB,Column_1 ; Select Column 1 CLRW ; Load W with column preset value (Bits 2 and 3) CALL Select_Column BSF PORTB,Column_1 ; Deselect Column 1 BCF PORTB,Column_2 ; Select Column 2 MOVLW 4 ; Load W with column preset value (Bits 2 and 3) CALL Select_Column BSF PORTB,Column_2 ; Deselect Column 2 BCF PORTB,Column_3 ; Select Column 3 MOVLW 8 ; Load W with column preset value (Bits 2 and 3) CALL Select_Column BSF PORTB,Column_3 ; Deselect Column 3 BCF PORTB,Column_4 ; Select Column 4 MOVLW 0C ; Load W with column preset value (Bits 2 and 3) CALL Select_Column RETURN Select_Column BTFSS Key,7 ; If key bit 7 is clear then a key is already detected RETURN MOVWF Key ; Store preset Key value CALL Scan_Row BTFSC Key,7 ; If key bit 7 is set no key pressed RETURN Got_a_Key MOVLW 64 ; Wait 50mS for switch bounce CALL X_Delay500 MOVFW Key MOVWF Temp_Store ; Temporarily store the key value GaK_Loop ; Now wait for the key to be released CLRF Key CALL Scan_Row BTFSS Key,7 Goto GaK_Loop MOVLW 64 ; Wait 50mS for switch bounce CALL X_Delay500 MOVFW Temp_Store CALL Translate_key ; Translate key MOVWF Key ; Store key value RETURN Scan_Row MOVLW 080 ; Set top bit (Default) BTFSS PORTB,Row_1 ; Value 0 MOVLW 0 BTFSS PORTB,Row_2 ; Value 1 MOVLW 1 BTFSS PORTB,Row_3 ; Value 2 MOVLW 2 BTFSS PORTB,Row_4 ; Value 3 MOVLW 3 ADDWF Key,F ; Add to preset key value(Bits 0 and 1) RETURN ; If Key bit 7 is set on exit then there is no key pressed ; ************ ; Get Number ; ************ Get_Number ; On entry W holds the maximum number of digits to enter MOVWF Number_Max ; Maximum number of digits MOVLW 8 MOVWF Number_Counter ; Tempory loop counter MOVLW Buffer ; Setup indirect memory pointer MOVWF FSR MOVLW 0 GN_Loop1 ; Clear the Buffer MOVWF INDF INCF FSR,F DECFSZ Number_Counter,F GOTO GN_Loop1 ; Number counter is now used to count the number of key entries. GN_Loop2 CALL Scan_Keyboard ; Loop until a key is pressed BTFSC Key,7 GOTO GN_Loop2 MOVFW Key GOTO Key_Jump ; Translate Key into an action GN_Error ; Key is rejected not a valid Key CALL Bleep ; Error so bleep GOTO GN_Loop2 GN_Numeric ; The key is numeric BTFSS Base_Flag,Binary_Valid GOTO GN_OK ; Skip if entering CV address only valid for CV value BTFSS Base_Flag,Binary GOTO GN_OK ; Skip if binary mode not set MOVLW 1 ; Binary Mode, Only 0 and 1 valid ANDWF Key,F GN_OK MOVFW Number_Max ; Ensure maximum character count is not reached XORWF Number_Counter,W SKPNZ GOTO GN_Loop2 ; Reject MOVLW Buffer ; Set buffer address to store digit ADDWF Number_Counter,W MOVWF FSR MOVFW Key MOVWF INDF ; Store the Digit CALL LCD_Write_Ascii ; Display the digit INCF Number_Counter,F GOTO GN_Loop2 GN_Delete MOVFW Number_Counter SKPNZ GOTO GN_Loop2 ; If count =0, there is nothing to delete DECF Number_Counter,F CALL LCD_Delete MOVLW Buffer ; Set buffer address to store digit ADDWF Number_Counter,W MOVWF FSR MOVLW 0 ; clear the last character from the buffer MOVWF INDF GOTO GN_Loop2 GN_Escape SETC RETURN GN_Mode BTFSS Base_Flag,Binary_Valid GOTO GN_Error ; Skip if entering CV address only valid for CV value MOVLW 0 XORWF Number_Counter,F BNZ GN_Error ; If character count is not 0 then mode is an illegal character BTFSC Base_Flag,Binary GOTO GNM_Decimal GNM_Binary BSF Base_Flag,Binary MOVLW 62 CALL LCD_Write BCF Flag_Register,Long ;no double write if in binary MOVLW 8 ; Maximum Character count =8 ie 0 to 11111111 GOTO Get_Number GNM_Decimal BCF Base_Flag,Binary CALL LCD_Delete MOVLW 4 ;4 chars if long address BTFSS Flag_Register,Long MOVLW 3 ; Maximum Character count =3 ie 0 to 255 GOTO Get_Number GN_Enter ; Enter key pressed CLRW XORWF Number_Counter,W BZ GN_Error ; Error if character count = 0 MOVLW 3 XORWF Number_Max,W SKPNZ INCF Number_Max,F ; Required input was 3 characters, this needs to be increased ; to 4 now as the Hex converter is set to convert 4 characters. MOVFW Number_Counter SUBWF Number_Max,F BZ GN_Exit GN_Loop3 ; This routines adds all the leading zeros ie adjust an address ; to 4 numbers, adjusts a value to 4 numbers and binary to 8 numbers MOVLW 7 MOVWF Counter MOVLW Buffer_6 MOVWF FSR GN_Loop4 MOVFW INDF INCF FSR,F MOVWF INDF DECF FSR,F DECF FSR,F DECFSZ Counter,F GOTO GN_Loop4 INCF FSR,F MOVLW 0 MOVWF INDF DECFSZ Number_Max,F GOTO GN_Loop3 BTFSS Base_Flag,Binary_Valid GOTO GN_Exit BTFSS Base_Flag,Binary GOTO GN_Exit ; If in binary mode rewrite value in 8 bits GN_Loop5 CALL LCD_Delete DECFSZ Number_Counter,F GOTO GN_Loop5 MOVLW 8 MOVWF Number_Counter MOVLW Buffer MOVWF FSR GN_Loop6 MOVFW INDF CALL LCD_Write_Ascii INCF FSR,F DECFSZ Number_Counter,F GOTO GN_Loop6 GN_Exit CLRC RETURN ; ************ ; Read Input ; ************ Read_Input CALL Scan_Keyboard BTFSC Key,7 RETURN ; Return, no key detected ; "Key" holds key pressed value CALL INITIALISE_LCD MOVLW Program XORWF Key,W BZ Input_Program MOVLW Read XORWF Key,W BZ Input_Read MOVLW Mode XORWF Key,W BZ Input_Mode MOVLW Escape XORWF Key,W BZ Input_Escape MOVLW Text_Error ; Display Error message CALL LCD_String CALL Bleep ; Error so bleep RETURN ; ******************* ; Get the CV ADDRESS ; ******************* Get_CV_Address CALL LCD_String CALL LCD_CR MOVLW Text_CV CALL LCD_String BCF Base_Flag,Binary_Valid ; Only Decimal input permitted MOVLW 4 ; Maximum Character count =4 ie 1 to 1024 CALL Get_Number ; Get CV Number BC Input_Escape ; Carry set if escape pressed CALL Convert_to_Hex MOVFW Buffer_7 ; Buffer_7 holds hex converted value MOVWF CV_Address ; Save CV address in Hex MOVFW Buffer_6 MOVWF CV_Byte ; Save 2 upper bits of address BNZ VA_Skip1 MOVFW Buffer_7 BNZ VA_Skip1 GOTO VA_Error ; Error if address is 0 VA_Skip1 MOVFW Buffer_6 SUBLW 3 BC VA_Skip2 MOVLW 0 XORWF Buffer_7,W BZ VA_Skip2 VA_Error MOVLW Text_Error ; Display Error message CALL LCD_String CALL Bleep ; Error so bleep SETC RETURN VA_Skip2 MOVLW 0 XORWF Buffer_6,W ;Hi bits must be clear if long address BNZ VA_Skip3 MOVLW .17 ;Is it a long address SUBWF Buffer_7,W BNZ VA_Skip3 BSF Flag_Register,Long VA_Skip3 CLRC RETURN ; ************ ; CV Program ; ************ Input_Program MOVLW Text_Page BTFSC Flag_Register,Mode_Set MOVLW Text_Direct CALL LCD_String BCF Flag_Register,Long MOVLW Text_Program ; Display CV Program initial screen CALL Get_CV_Address SKPNC ; Carry clear if CV address valid RETURN MOVLW Text_Equals CALL LCD_String BSF Base_Flag,Binary_Valid ; Decimal or Binary input permitted BTFSS Base_Flag,Binary GOTO IP_Decimal MOVLW 62 CALL LCD_Write ; "b" binary indicator MOVLW 8 ; Maximum Character count =8 ie 0 to 11111111 CALL Get_Number ; Get CV Number BC Input_Escape ; Carry set if escape pressed BTFSS Base_Flag,Binary GOTO IP_Decimal_skip IP_Binary_skip MOVLW 8 ; Convert binary to hex MOVWF Counter MOVLW Buffer_7 MOVWF FSR CLRF CV_Value ; CV_Value will hold hex value IPB_Loop1 CLRC BTFSC INDF,0 SETC RRF CV_Value,F DECF FSR,F DECFSZ Counter,f GOTO IPB_Loop1 MOVFW CV_Value MOVWF Buffer_7 ; Buffer 7 now holds the value in hex GOTO IP_Prog IP_Decimal MOVLW 4 ;If long address then 4 chars BTFSS Flag_Register,Long MOVLW 3 ; Maximum Character count =3 ie 0 to 255 CALL Get_Number ; Get CV Number BC Input_Escape ; Carry set if escape pressed BTFSC Base_Flag,Binary GOTO IP_Binary_skip IP_Decimal_skip CALL Convert_to_Hex ; On return Buffer_7 holds the CV value BTFSC Flag_Register,Long GOTO IP_Prog ;If long address, buffer 6 has hi byte MOVFW Buffer_6 BNZ VA_Error ; If buffer 6 not 0 then value is greater than 255 IP_Prog BTFSC Flag_Register,Mode_Set GOTO CV_Write_Direct ; ********************* ; Write CV Paged Mode ; ********************* CV_Write_Paged CALL Calc_Page_Reg ; Calculate the page and register MOVLW 7D MOVWF CV_Byte MOVFW CV_Address ; Load page value MOVWF CV_Value CALL Power_On_Cycle BTFSC Flag_Register,Overload_Flag RETURN ; Return if overload detected CALL Packet_RW_Paged ; 8 Program Packets CVWP_Skip1 CALL Recovery_Time BCF Flag_Register,Ack ; Clear the Ack Flag MOVLW 78 IORWF CV_Register,W MOVWF CV_Byte BTFSC Flag_Register,Long GOTO CVWP_Skip5 MOVFW Buffer_7 ; Buffer_7 holds hex converted value MOVWF CV_Value ; Save CV value in Hex CVWP_Skip1a CALL Packet_RW_Paged ; 8 Program Packets CVWP_Skip2 CALL ACK_Test BTFSC Flag_Register,Ack GOTO CVWP_Skip3 MOVLW Text_No_Ack CALL LCD_String RETURN CVWP_Skip3 BTFSC Flag_Register,Long GOTO CVWP_Skip4 MOVLW Text_OK CALL LCD_String CALL Bleep RETURN CVWP_Skip4 MOVLW .18 ;set next address MOVWF CV_Address BCF Flag_Register,Long GOTO CV_Write_Paged CVWP_Skip5 MOVFW Buffer_6 ;get hi byte of long address IORLW B'11000000' MOVWF CV_Value GOTO CVWP_Skip1a ACK_Test CALL Power_Off_Cycle CALL LCD_HOME MOVLW 0A ; Delay wait for LCD CALL X_Delay500 MOVLW Text_Program ; Display CV Program CALL LCD_String MOVLW 20 ; print CALL LCD_Write RETURN ; ********************** ; Write CV Direct Mode ; ********************** CV_Write_Direct MOVLW 1 ; Subtract 1 from the address SUBWF CV_Address,F ; ie CV1= 00 SKPC SUBWF CV_Byte,F ; CVD_Skip CALL SETUP_TIMER CALL Power_On_Cycle BTFSC Flag_Register,Overload_Flag RETURN ; Return if overload detected MOVLW 7C ; Setup first packet byte IORWF CV_Byte,F BTFSC Flag_Register,Long GOTO CVD_Skip5 MOVFW Buffer_7 MOVWF CV_Value CVD_Skip2 CALL Packet_RW_Direct ; 8 Program Packets CALL ACK_Test BTFSC Flag_Register,Ack GOTO CVD_Skip3 MOVLW Text_No_Ack CALL LCD_String RETURN CVD_Skip3 BTFSC Flag_Register,Long GOTO CVD_Skip4 MOVLW Text_OK CALL LCD_String CALL Bleep RETURN CVD_Skip4 MOVLW .18 MOVWF CV_Address BCF Flag_Register,Long GOTO CV_Write_Direct CVD_Skip5 MOVFW Buffer_6 IORLW B'11000000' MOVWF CV_Value GOTO CVD_Skip2 ; ********* ; CV Read ; ********* Input_Read MOVLW Text_Page BTFSC Flag_Register,Mode_Set MOVLW Text_Direct CALL LCD_String ; Print Direct or paged MOVLW Text_Read ; Display CV Read initial screen CALL Get_CV_Address SKPNC ; Carry clear if CV address valid RETURN MOVLW Text_Equals CALL LCD_String BTFSC Flag_Register,Mode_Set GOTO CV_Read_Direct ; ********************* ; Read CV Paged Mode ; ********************* CV_Read_Paged CALL Calc_Page_Reg ; Calculate the page and register MOVLW 7D MOVWF CV_Byte MOVFW CV_Address ; Load page value MOVWF CV_Value CALL Power_On_Cycle BTFSC Flag_Register,Overload_Flag RETURN ; Return if overload detected CALL Packet_RW_Paged ; 8 Program Packets CVRP_Skip1 CALL Recovery_Time BCF Flag_Register,Ack ; Clear the Ack Flag MOVLW 0 MOVWF CV_Bit_Counter MOVLW 70 IORWF CV_Register,W MOVWF CV_Byte CVRP_255_Loop MOVFW CV_Bit_Counter MOVWF CV_Value CALL Packet_RW_Paged ; 8 Program Packets BTFSC Flag_Register,Ack GOTO CVRP_Skip2 INCF CV_Bit_Counter,F BNZ CVRP_255_Loop GOTO CVVRP_Exit CVRP_Skip2 MOVFW CV_Bit_Counter MOVWF CV_Value CVRP_Skip3 MOVFW CV_Value ; CV Value put into W when entering this routine at CVRP_Skip3 CALL Display_CV CALL Power_Off_Cycle CALL Bleep RETURN CVVRP_Exit CALL Power_Off_Cycle MOVLW Text_No_Ack CALL LCD_String RETURN ; **************** ; Read CV Direct ; **************** CV_Read_Direct MOVLW 1 ; Subtract 1 from the address SUBWF CV_Address,F ; ie CV1= 00 SKPC SUBWF CV_Byte,F ; ********************************* ; Read CV Direct Bit Verification ; ********************************* CV_Read_DBit CALL Power_On_Cycle BTFSC Flag_Register,Overload_Flag RETURN ; Return if overload detected MOVLW 0E7 MOVWF CV_Value MOVLW 78 ; Setup first packet byte IORWF CV_Byte,F MOVLW 8 MOVWF CV_Bit_Counter CLRF CV_Read CVVD_Loop2 CALL Packet_RW_Direct ; 8 Program Packets BTFSC Flag_Register,Ack GOTO CVVD_Skip1 SETC GOTO CVVD_Skip2 CVVD_Skip1 CALL Recovery_Time ; ACK so send some reset packets CLRC CVVD_Skip2 RLF CV_Read,F ; Rotate carry into "read" CV value MOVLW 0A ; Delay wait for decoder to remove acknowledge CALL X_Delay500 DECF CV_Value,F ; Decrement CV value to read next bit DECFSZ CV_Bit_Counter,F GOTO CVVD_Loop2 CVVD_Exit MOVFW CV_Read ; Save the CV value MOVWF CV_Value BCF CV_Byte,3 BSF CV_Byte,2 ; Change CV byte to Read byte not Bit CALL Packet_RW_Direct ; 8 Program Packets BTFSC Flag_Register,Ack GOTO CVRP_Skip3 GOTO CVVRP_Exit ; ************ ; Input Mode ; ************ Input_Mode MOVLW Text_Mode CALL LCD_String BTFSS Flag_Register,Mode_Set GOTO Direct_Mode GOTO Paged_Mode Direct_Mode BSF Flag_Register,Mode_Set MOVLW Text_Direct CALL LCD_String RETURN Paged_Mode BCF Flag_Register,Bit_Verify BCF Flag_Register,Mode_Set MOVLW Text_Page CALL LCD_String RETURN Bleep BSF PORTA,Sounder ; Turn off Bleep MOVLW 0FF ; Delay CALL X_Delay500 BCF PORTA,Sounder ; Turn off Bleep RETURN Input_Escape CALL INITIALISE_LCD MOVLW Text_DCC ; Initialisation String CALL LCD_String MOVLW Text_Program CALL LCD_String MOVLW Text_Programmer CALL LCD_String SETC RETURN ; ************************************** ; Place a Reset Packet in the DCC Queue ; ************************************** Packet_Reset MOVLW 0FF ; Preamble CALL DCC_Send_Byte BCF Flag_Register,Preamble ; Flag clear this is preamble MOVLW 0FF ; Preamble CALL DCC_Send_Byte MOVLW 00 ; First byte CALL DCC_Send_Byte BSF Flag_Register,Preamble ; Flag set this is data MOVLW 00 ; Second byte CALL DCC_Send_Byte MOVLW 00 ; Third byte CALL DCC_Send_Byte RETURN ; ********************** ; Power On / Off Cycle ; ********************** Power_On_Cycle MOVLW .14 ;14 reset packets MOVWF CV_Counter POC_Loop MOVLW 0C8 ; Wait 100mS for switch bounce CALL X_Delay500 CALL SETUP_TIMER ; Start interrupts BSF PORTA,Power ; Turn on power to service track BCF Flag_Register,Overload_Flag POC_Loop2 CALL Packet_Reset ; 14 Reset Packets DECFSZ CV_Counter,f GOTO POC_Loop2 BTFSS PORTB,Overload ; After 100msec check the overload input GOTO Overload_Detected CALL Recovery_Time RETURN Power_Off_Cycle CALL Recovery_Time BCF PORTA,Power ; Turn off power to service track BCF INTCON,GIE ; Globally disable interrupts RETURN Recovery_Time MOVLW 0A ; MOVWF CV_Counter POC_Loop1 CALL Packet_Reset ; 10 Reset Packets DECFSZ CV_Counter,F GOTO POC_Loop1 RETURN Overload_Detected BCF PORTA,Power ; Turn off power to service track BCF INTCON,GIE ; Globally disable interrupts BSF Flag_Register,Overload_Flag CALL INITIALISE_LCD MOVLW Text_Overload ; Display Overload message CALL LCD_String CALL Bleep ; Bleep MOVLW 55 ; 100mS pause CALL X_Delay500 CALL Bleep ; Bleep RETURN ; ******************************************* ; Packet to Program/Read/Verify a CV Direct ; ******************************************* Packet_RW_Direct ; On entry CV_Byte holds first packet byte (0111CCAA) ; CV_Address holds Address byte ; CV_Value hold data byte ; Packet sent up to 8 times ; BCF Flag_Register,Ack ; Clear the Ack Flag MOVLW 8 MOVWF CV_Counter PRWD_Loop1 CALL P_RW_Direct ; 8 Program Packets BTFSC Flag_Register,Ack GOTO PRWD_Skip1 DECFSZ CV_Counter,F GOTO PRWD_Loop1 PRWD_Skip1 RETURN P_RW_Direct MOVLW 0FF ; Long Preamble CALL DCC_Send_Byte BCF Flag_Register,Preamble ; Flag clear this is preamble MOVLW 0FF ; Long Preamble CALL DCC_Send_Byte MOVLW 0FF ; Long Preamble CALL DCC_Send_Byte MOVFW CV_Byte ; First Byte MOVWF CV_Checksum CALL DCC_Send_Byte BSF Flag_Register,Preamble ; Flag set this is data MOVFW CV_Address ; Second Byte XORWF CV_Checksum,F CALL DCC_Send_Byte MOVFW CV_Value ; Third Byte XORWF CV_Checksum,F CALL DCC_Send_Byte MOVFW CV_Checksum ; Forth Byte CALL DCC_Send_Byte RETURN ; ********************************************** ; Calculate the Page and Register (Paged Mode) ; ********************************************** Calc_Page_Reg MOVLW 1 ; Calculate Register and Page SUBWF CV_Address,F SKPC SUBWF CV_Byte,F ; Decrement address by 1 MOVLW 3 ; Get register ANDWF CV_Address,W MOVWF CV_Register CLRC ; Divide by 4 RRF CV_Byte,F RRF CV_Address,F CLRC RRF CV_Byte,F RRF CV_Address,F INCF CV_Address,F ; CV_Address now holds the page number RETURN ; ******************************************* ; Packet to Program/Read a CV in Paged Mode ; ******************************************* Packet_RW_Paged ; On entry CV_Byte holds First Byte ; CV_Value holds data byte ; Packet sent up to 8 times ; BCF Flag_Register,Ack ; Clear the Ack Flag MOVLW 8 MOVWF CV_Counter PRWP_Loop1 CALL P_RW_Paged ; 8 Program Packets BTFSC Flag_Register,Ack GOTO PRWP_Skip1 DECFSZ CV_Counter,F GOTO PRWP_Loop1 PRWP_Skip1 RETURN P_RW_Paged MOVLW 0FF ; Long Preamble CALL DCC_Send_Byte BCF Flag_Register,Preamble ; Flag clear this is preamble MOVLW 0FF ; Long Preamble CALL DCC_Send_Byte MOVLW 0FF ; Long Preamble CALL DCC_Send_Byte MOVFW CV_Byte ; First Byte MOVWF CV_Checksum CALL DCC_Send_Byte BSF Flag_Register,Preamble ; Flag set this is data MOVFW CV_Value ; Second Byte XORWF CV_Checksum,F CALL DCC_Send_Byte MOVFW CV_Checksum ; Third Byte CALL DCC_Send_Byte RETURN ; ************************ ; Program Initialisation ; ************************ Initialise MOVLW Mem_Start MOVWF FSR Init_Loop ; Clear Workspace memory CLRW MOVWF INDF INCF FSR,F MOVLW Mem_Stop XORWF FSR,W BNZ Init_Loop CALL SETUP_PORTS BCF PORTA,Power ; Turn off power to service track BCF PORTA,Sounder ; Turn off Bleep CALL Input_Escape ; Prints top line of LCD Display CALL LCD_CR MOVLW Text_Programmer CALL LCD_String MOVLW Text_Version CALL LCD_String CALL Bleep LOOP ; Main Program Loop CALL Read_Input ; Check for input GOTO LOOP END