;------------------------------------------------------------------------------- ; SPROG ; ; DCC Serial port PROGrammer/tester (C) Copyright Andrew Crosland 2003-2005 ; web: http://www.sheerstock.fsnet.co.uk/dcc ; e-mail: dcc@sheerstock.fsnet.co.uk ; ; This code is for a serial port/USB DCC decoder programmer intended for use ; with DecoderPro. It also has the ability to function as a mini command station, ; booster and DCC tester. It uses a PIC16F870 running at 16MHz. If the Microchip ; bootloader is used then it supports firmware upgrade via the serial port or ; USB. ; ; Revision History ; ; 14/05/06 3.9 Always call recovery_time() during direct mode ; 11/06/05 3.8 Send reset packets after ack during direct verify ; required by Digitrax decoders ; 04/06/05 3.7 Fixed forced hex parsing in O command ; Boot command now requires exactly "b 1 1 1" to prevent ; unintentional entry to bootloader ; 07/11/04 3.6 Fixed parsing of long address in A command ; Fixed bug in uart_bcd_word during printing of long address ; 12/09/04 3.5 Added Q command to support host based command station ; Error on command input now correctly calls cmd_error instead of jumping ; there and executing an unwanted return ; Changed R> prompt to P> to help DecoderPro command monitor ; O command always treats arguments as hex to make DecoderPro throttles ; functions work ; 20/03/04 3.4 Added support for extended addressing ; 28/02/04 3.3 Fixed problem with error handling of illegal digits in input ; 28/01/04 3.2 Z command for ZTC compatibility mode added. Decreases DCC bit timing to ; work with some older ZTC decoders. ; T comand added to set/examine TMR0 reload value ; Overload detection now samples twice with a 116ms delay to prevent false ; detection on current spikes ; Fixed 128 speed step packet generation in pkt_128 ; Fixed direction bit in 14/28 and 128 speed step packets ; 05/12/03 3.1 Modified bootloader code: ; Use CTS to prevent receiver overflow when transmitting at ; end of line. ; Only invalidates existing code after a valid line of the hex ; file is downloaded ; Drains checksum byte from USART when end of file received ; 11/10/03 3.0a Integrated bootloader into main code. ; P command clears RR_MODE ; Added USB or RS232 RTS check depending on USB_N input ; Made RTS/CTS naming sensible and consistent with schematics ; Removed stand-alone booster and mode and supporting code to make ; way for speedo functions. Added heartbeat timer. ; 11/10/03 1.2d Check for ACK before checking for overload so that violent ACK ; pulse is not treated as overload. ; Overload check uses IMAX instead of I_LIMIT (which is a literal ; but was being used as a memory address) ; Clear DCC_CHK_OVLD when overload is detected to prevent multiple ; overload messages. ; Removed RC0 ACK monitor ; 21/06/03 1.2c Removed check for ACK during recovery_time to fix programing of ; Digitrax decoders ; 09/04/03 1.2b Added ACK monitor on RC0 ; 02/04/03 1.2a Delay between reading bits in direct mode to reduce effect of ack ; pulses driving loco ; 15/03/03 1.2 Ported to F870 for new hardware ; 14/11/02 1.1 Removed some features, simplified UI to be JMRI friendly ; 03/10/01 1A Created from MERG progg.asm but many parts re-written. ; Routines not re-written: ; Much of CV read and write ; Power on/off ; Packet_RW_Paged ; Calc_Page_Reg ; Packet_RW_Direct ; ; Non-MERG originated portions of this code are Copyright (C) 2003 Andrew ; Crosland but may be used on a not for profit basis only on condition that this ; copyright message is retained. ;------------------------------------------------------------------------------- list p=16f870, st=OFF, x=OFF, n=0 ; errorlevel -302 #include __CONFIG _BODEN_OFF & _CP_OFF & _PWRTE_ON & _WDT_OFF & _WRT_ENABLE_ON & _HS_OSC & _DEBUG_OFF & _CPD_OFF & _LVP_OFF ;#define DEBUG ;#define DEBUGAD ;------------------------------------------------------------------------------- ; Constants ; FREQ equ .16 ; MHz BAUD_RATE equ .9600 ; .19200 BRG_VAL equ FREQ * .1000000 / (.64 * BAUD_RATE) - 1 ; 57600 = 3, 19200 = 12, 9600 = 25 ;BRG_VAL equ .25 ;------------------------------------------------------------------------------- ; Common RAM variables @ 0x70 - 0x7F ;------------------------------------------------------------------------------- W_ISR equ 0x70 ; save W during ISR S_ISR equ 0x71 ; save STATUS PCLATH_ISR equ 0x72 ; save PCLATH FSR_ISR equ 0x73 ; save FSR COMM_FLAGS equ 0x74 ; flags for serial I/O routines EE_DATA equ 0x75 ; Temp store for data to be written to EEPROM ;------------------------------------------------------------------------------- ; RAM Bank 0 ;------------------------------------------------------------------------------- CBLOCK 0x20 MEM_START ; Start of memory STRING_PTR ; Used when outputting text string to USART UART_TEMP ; Temporary stores for USART byte conversions UART_TEMPH UART_TEMPL UART_SUBH UART_SUBL UART_COUNT UART_DIGIT MODE_WORDH ; Operating mode MODE_WORDL RX_PTR ; Receive buffer pointer CMD ARGC ; Number of arguments on command line ARGVH ARGVL DIGITS ; max number of digits to parse GH_COUNT ; Temp counter for get_hex routine MATHH ; Temp math buffer MATHL FSR_TEMP ; 0x30 ECHO_BUFFER RR_ADDRH ; Rolling road address high byte RR_ADDRL ; Rolling road address low byte RR_SPEED ; Rolling road speed RR_SP_INC ; Rolling road speed increment BIT_FLAG ; DCC Out "0" or "1" jump table offset FSR_TEMP2 DELAY ; System Delay X_DELAY ; System Delay CV_VALUE ; CV Packet formation CV_COUNTER ; CV Packet formation CV_READ ; CV Packet formation CV_BIT_COUNTER ; CV Packet formation CV_REGISTER ; CV Packet formation RT_COUNTER AN0H ; A/D results channel 0 - Vinsense AN0L ; 0x40 AN1H ; channel 1 - Isense AN1L AN2H AN2L VREFH VREFL AN4H AN4L AN1PREV ; prevous sample AN1AVE ; average of two samples OVLD_DELAY ; delay to check for overload AD_STATE ; A/D state machine DCC_PRE ; Number of DCC preamble bits to send DCC_BYTES ; Number of bytes (inc error) to send DCC_PTR ; DCC buffer pointer DCC_BITS ; bit count DCC_FLAGS ; 0x50 DCC_BUFF1 ; DCC packet transmit buffer DCC_BUFF2 DCC_BUFF3 DCC_BUFF4 DCC_BUFF5 DCC_BUFF6 DCC_TEMP ; used during 'O' command ICCQ ; Quiescent decoder current IMAX ; Booster mode current limit OP_FLAGS TIME_US TIME_14MS TIME_3S TMR0_RELOAD ENDC MEM_STOP equ 0x6f ; End of banked memory ;------------------------------------------------------------------------------- ; RAM Bank 1 ;------------------------------------------------------------------------------- MEM_START1 equ 0xa0 ; Start of memory ARGV1H equ 0xa0 ARGV1L equ 0xa1 ARGV2H equ 0xa2 ARGV2L equ 0xa3 ARGV3H equ 0xa4 ARGV3L equ 0xa5 ARGV4H equ 0xa6 ARGV4L equ 0xa7 ARGV5H equ 0xa8 ARGV5L equ 0xa9 ARGV6H equ 0xaa ARGV6L equ 0xab ARGS_MAX equ 6 RX_BUFFS equ 0xac ; Start of serial receive buffer RX_BUFFE equ 0xbf ; End of serial receive buffer 64 bytes MEM_STOP1 equ 0xbf ; End of banked memory ;------------------------------------------------------------------------------- ; Declare constants ;------------------------------------------------------------------------------- ; Port A analogue inputs ;v_sense equ 0 ; Analogue channel 0 - Input voltage ;i_sense equ 1 ; Analogue channel 1 - o/p Current sense ;spare equ 2 ; Analogue channel 2 ;spare equ 3 ; Analogue channel 3 ;spare equ 4 ; ;spare equ 5 ; Analogue channel 4 PORTA_DDR equ 0xff PORTA_INIT equ 0x0 ; Port B BOOT equ 0 ; Reserved for bootloader CTS_OUT_N equ 1 ; serial port flow control o/p RTS_IN_N equ 2 ; serial port flow control i/p POWER equ 3 ; power ontrol o/p to booster DCC_POS equ 4 ; one side of booster H-bridge o/p DCC_NEG equ 5 ; other side o/p ;spare equ 6 ; ;spare equ 7 ; PORTB_DDR equ 0xc5 PORTB_INIT equ 0x00 ; power off, CTS on ; Port C USB_N equ 0 ; Input tied Low for USB ;spare equ 1 ;spare equ 2 ;spare equ 3 U_CTS_OUT_N equ 4 ; USB flow control o/p to FT232 CTS# i/p U_RTS_IN_N equ 5 ; USB flow control i/p from FT232 RTS# o/p ;usart_tx equ 6 ; Reserved used by USART ;usart_rx equ 7 ; Reserved used by USART PORTC_DDR equ 0xaf PORTC_INIT equ 0xef ; DCC_FLAGS register used for DCC packet transmission DCC_RDY equ 0 ; set if Tx ready for a new packet DCC_LONG_PRE equ 1 ; set forces long preamble ;spare equ 2 DCC_ACK equ 3 DCC_OVERLOAD equ 4 ; set if overload detected DCC_CHECK_ACK equ 5 DCC_CHECK_OVLD equ 6 ; COMM_FLAGS register for serial I/O CMD_RDY equ 0 ; complete command line received OVERRUN equ 1 ; Overrun error EOB equ 2 ; End of Buffer ECHO equ 3 ; echo character DLE_RX equ 4 ; DLE was received ; MODE_WORDL flags ; equ 0 ECHO_ON equ 1 ; equ 2 CALC_ERROR equ 3 RR_MODE equ 4 ; Rolling road mode - calls packetgen ZTC_MODE equ 5 ; ZTC compatibility mode ; MODE_WORDH flags DIR equ 0 ; rolling road direction SP14 equ 1 ; speed direction mode for rolling road SP28 equ 2 SP128 equ 3 LONG equ 4 ; long addressing LOG equ 5 ; speedo logging on O_CMD equ 6 ; OP_FLAGS for DCC output OP_PWR equ 0 OP_BIT equ 1 ; Current sensing ; 5V reference => 5/1024 = 4.88mV resolution ; Sense resistor is 0R47 ; so 60mA is 28.2mV Vsense => 5 steps ; 250mA overload is 117.5mV Vsense => 24 steps I_ACK_DIFF equ 5 ; No. steps for additional 60ma ACK pulse I_OVERLOAD equ .24 I_LIMIT equ .96 ; 2.5V reference => 2.5/1024 = 2.44mV resolution ; Sense resistor is 0R47 ; so 60mA is 28.2mV Vsense => 11 steps ; 250mA overload is 117.5mV Vsense => 48 steps ;I_ACK_DIFF equ 11 ; No. steps for additional 60ma ACK pulse ;I_OVERLOAD equ .48 ;I_LIMIT equ .193 ASCII_BEL equ 0x07 ASCII_BS equ 0x08 ASCII_TAB equ 0x09 ASCII_LF equ 0x0a ASCII_CR equ 0x0d ASCII_ESC equ 0x1b ASCII_DLE equ 0x10 MAGIC equ .93 ; bootloader TEST_INPUT EQU 0 ;Port B Pin 0 input indicates download ;------------------------------------------------------------------------------- ; Start of code ;------------------------------------------------------------------------------- org 0 ; Reset vector ; movlw high BootMain ; movwf PCLATH ; set page bits for page3 ; goto BootMain ; go to boot loader clrf PCLATH goto pre_boot org 4 ; Interrupt Vector goto isr ; 1 + 1 for pipeline reload org 8 ; Known location for bootloader to jump to main_jump goto main ;------------------------------------------------------------------------------- ; Text Strings ;------------------------------------------------------------------------------- string_whoami dt "SPROG Ver 3.9" dt ASCII_CR, ASCII_LF, 0 string_prompt_p dt "P> ", 0 ; programmer prompt string_prompt_r dt "P> ", 0 ; rolling road prompt string_cv dt "CV ", 0 string_equals dt "= ", 0 string_value dt "Value ", 0 string_overload dt "!O", 0 string_error dt "!E", ASCII_BEL, 0 string_mode dt "Mode", 0 string_ok dt "OK", 0 string_no_ack dt "No Ack",0 string_overrun dt "Overrun", ASCII_BEL, 0 ;------------------------------------------------------------------------------- ; Send string pointed to by W to USART ; ; Stack: calls 1-deep ; ; !!! Must be in first 256 bytes of code space ;------------------------------------------------------------------------------- uart_string clrf PCLATH movwf STRING_PTR ; Save pointer call string_next ; Get next character xorlw 0 ; end of string? btfsc STATUS, Z return call put_char incf STRING_PTR, W ; Point to next character goto uart_string string_next movwf PCL ;------------------------------------------------------------------------------- ; Jump tables ;------------------------------------------------------------------------------- ; commands: ;A [
] [] display[set] address used for rolling road ;B Start Boot Loader ;C [] display [program] CV using direct mode ;F toggle function ;M [] display[set] mode word ;O [] [] [] DCC packet output. Each byte is hex ; or decimal format separated by spaces ;P Set programmer mode ;Q... DCC binary packet output. Each byte is 0-255 with ; no spaces between bytes. Any byte equal to ASCII_CR or ASCII_DLE ; must be preceded by ASCII_DLE except for final ASCII_CR to ; terminate the line. Error byte must not be supplied ;R Read mode from EEPROM ;S report status ;V [] display [program] CV using paged mode ;W Write mode to EEPROM ;? help ;+ power on ;- power off ;< [speed] display [set] reverse speed step ;> [speed] display [set] forward speed step arg_jump movlw '+' subwf CMD, W bz cmd_pon addlw -2 bz cmd_poff movlw 'Z'+1 subwf CMD, W bc cmd_error ; C means no borrow so CMD > 'Z' movlw '<' subwf CMD, W bnc cmd_error ; no C means borrow so CMD < '<' movlw high $ movwf PCLATH ; set PCLATH ready for computed goto movlw '<' subwf CMD, W ; re-base command for jump table addwf PCL, F ; add to program counter goto cmd_speed ; < goto cmd_error ; = goto cmd_speed ; > goto cmd_help ; ? goto cmd_error ; @ goto cmd_address ; A goto cmd_boot ; B goto cmd_cv_direct ; C goto cmd_error ; D goto cmd_error ; E goto cmd_function ; F goto cmd_error ; G goto cmd_error ; H goto cmd_error ; I goto cmd_error ; J goto cmd_error ; K goto cmd_log_on ; L goto cmd_mode ; M goto cmd_log_off ; N goto cmd_dcc ; O goto cmd_prog ; P goto cmd_error ; Q goto cmd_rmode ; R goto cmd_status ; S goto cmd_tmr0 ; T goto cmd_error ; U goto cmd_cv_paged ; V goto cmd_wmode ; W goto cmd_error ; X goto cmd_error ; Y goto cmd_ztc ; Z dcc_jump movlw high dcc_jump ; set PCLATH ready for computed goto movwf PCLATH movf BIT_FLAG, W ; addwf PCL, F ; add offset to program counter goto first_edge ; 0 goto second_edge_bit1 ; 1 goto second_midpoint_bit0 ; 2 goto second_edge_bit0 ; 3 goto first_midpoint_bit0 ; 4 goto second_idle ; 5 goto first_idle ; 6 goto second_pre ; 7 goto first_pre ; 8 goto second_mid_start ; 9 goto second_start ; .10 goto first_mid_start ; .11 goto first_start ; .12 ad_jump movlw high ad_jump ; set PCLATH ready for computed goto movwf PCLATH movf AD_STATE, W addwf PCL, F ; add offset to program counter goto ad_reset ; 0 goto acq_an0 ; 1 goto conv_an0 ; 2 goto acq_an1 ; 3 goto conv_an1 ; 4 goto acq_an2 ; 5 goto conv_an2 ; 6 goto acq_vref ; 7 goto conv_vref ; 8 goto acq_an4 ; 9 goto conv_an4 ; 10 goto dummy1 ; 11 goto dummy2 ; 12 goto dummy3 ; 13 goto dummy4 ; 14 goto dummy5 ; 15 goto dummy6 ; 16 goto dummy7 ; 17 goto dummy8 ; 18 goto dummy9 ; 19 goto dummy10 ; 20 ;------------------------------------------------------------------------------- ; 125uS delay with 16Mhz clock ; ; Not accurate when heartbeat is running but not used for anything critical ;------------------------------------------------------------------------------- delay125 movlw 0a4 movwf DELAY delay500_loop decfsz DELAY, F goto delay500_loop return ;------------------------------------------------------------------------------- ; Delay W * 500uS ; ; Not accurate when heartbeat is running but not used for anything critical ;------------------------------------------------------------------------------- x_delay500 movwf X_DELAY rlf X_DELAY, F rlf X_DELAY, F x_delay500_loop call delay125 decfsz X_DELAY, F goto x_delay500_loop return ;------------------------------------------------------------------------------- ; Place a reset packet in the DCC transmit buffer ;------------------------------------------------------------------------------- packet_reset btfss DCC_FLAGS, DCC_RDY goto packet_reset movlw 0 goto packet_ir ;------------------------------------------------------------------------------- ; Place an idle packet in the DCC transmit buffer ;------------------------------------------------------------------------------- packet_idle btfss DCC_FLAGS, DCC_RDY goto packet_idle movlw 0xff packet_ir movwf DCC_BUFF1 ; First byte clrf DCC_BUFF2 ; second byte movwf DCC_BUFF3 ; error byte movlw 3 ; 3 byte packet movwf DCC_BYTES movlw .20 ; long preamble movwf DCC_PRE bcf DCC_FLAGS, DCC_RDY return ;------------------------------------------------------------------------------- ; ee_read - read from data EEPROM ; ; Address to be read in W on entry, returns data in W ;------------------------------------------------------------------------------- ee_read bsf STATUS, RP1 bcf STATUS, RP0 ; Bank 2 movwf EEADR ; Data Memory Address to read bsf STATUS, RP0 ; Bank 3 bcf EECON1, EEPGD ; Point to DATA memory bsf EECON1, RD ; EEPROM Read bcf STATUS, RP0 ; Bank 2 movf EEDATA, W ; W = EEDATA bcf STATUS, RP1 ; Bank 0 return ;------------------------------------------------------------------------------- ; ee_write - write to data EEPROM ; ; Address to be written in W on entry, data in EE_DATA ; ; Waits for write to complete before returning in bank 0 ;------------------------------------------------------------------------------- ee_write bsf STATUS, RP1 bcf STATUS, RP0 ; Bank 2 movwf EEADR ; Data Memory Address to write movf EE_DATA, W movwf EEDATA ; Data Memory Value to write bsf STATUS, RP0 ; Bank 3 bcf EECON1, EEPGD ; Point to DATA memory bsf EECON1, WREN ; Enable writes bcf INTCON, GIE ; Disable Interrupts movlw 0x55 movwf EECON2 ; Write 55h movlw 0xAA ; movwf EECON2 ; Write AAh bsf EECON1, WR ; Set WR bit to begin write nop nop bsf INTCON, GIE ; Enable Interrupts bcf STATUS, RP1 bcf STATUS, RP0 ; Bank 0 ee_wr_wait btfss PIR2, EEIF goto ee_wr_wait bcf PIR2, EEIF bsf STATUS, RP0 bsf STATUS, RP1 ; bank 3 bcf EECON1, WREN ; Disable writes bcf STATUS, RP1 bcf STATUS, RP0 ; bank 0 return ;------------------------------------------------------------------------------- ; USART routines ; ; !!!Don't call these from ISR, stack may overflow!!! ;------------------------------------------------------------------------------- ;------------------------------------------------------------------------------- ; Send W as ASCII, waiting, if necessary, until transmit buffer is empty and CTS ; status is OK ; ; Stack: none ;------------------------------------------------------------------------------- put_char btfss PIR1, TXIF goto $-1 btfsc PORTC, USB_N ; decide which RTS to check goto pc232 btfsc PORTC, U_RTS_IN_N ;check RTS to see if data can be sent goto $-1 goto pcok pc232 btfsc PORTB, RTS_IN_N ;check RTS to see if data can be sent goto $-1 pcok movwf TXREG return ;------------------------------------------------------------------------------- ; Wait to receive a character and return it in W ; ; Stack: none ;------------------------------------------------------------------------------- get_char bcf PORTC, U_CTS_OUT_N ; USB - Reset CTS on for data to be received bcf PORTB, CTS_OUT_N ; RS232 btfss PIR1, RCIF ; wait for receive flag goto get_char btfss RCSTA, OERR ; check for overrun goto gc_exit bcf RCSTA, CREN ; clear the overrun bsf RCSTA, CREN movf RCREG, W ; throw away one char gc_exit movf RCREG, W return ;------------------------------------------------------------------------------- ; Send ASCII ; ; Stack: calls 1-deep ;------------------------------------------------------------------------------- uart_crlf movlw ASCII_CR call put_char movlw ASCII_LF call put_char return ;------------------------------------------------------------------------------- ; Send low nibble (hex or bcd) of W as ASCII character ; ; Add 6 to W which will cause the digit carry flag to be set if W was > 9. ; Add 0x31 or 0x2a to give ASCII alpha or numeric character. ; ; Stack: calls 1-deep ;------------------------------------------------------------------------------- uart_hex_nibble addlw 6 btfss STATUS, DC goto uart_hex_num addlw 0x31 call put_char return uart_hex_num addlw 0x2a call put_char return ;------------------------------------------------------------------------------- ; Send byte in W as two ASCII hex characters with 'h' prefix ; ; uart_hex_byte1 doesn't print the prefix ; ; Stack: calls 2-deep ;------------------------------------------------------------------------------- uart_hex_byte movwf UART_TEMP movlw 'h' call put_char uhb swapf UART_TEMP, W ; MSB first andlw 0xF call uart_hex_nibble movf UART_TEMP, W ; now do LSB andlw 0xF call uart_hex_nibble return uart_hex_byte1 movwf UART_TEMP goto uhb ;------------------------------------------------------------------------------- ; Send byte in W as 3 ASCII BCD characters ; ; Stack: calls 3-deep ;------------------------------------------------------------------------------- uart_bcd_byte movwf UART_TEMPL ; save W clrf UART_TEMPH goto uart_bcd_word ;------------------------------------------------------------------------------- ; Send word in {UART_TEMPL, UART_TEMPH} as 5 ASCII BCD characters ; ; Stack: calls 3-deep ;------------------------------------------------------------------------------- uart_bcd_word movlw high .10000 movwf UART_SUBH movlw low .10000 movwf UART_SUBL call bcd_sub call uart_hex_nibble movlw high .1000 movwf UART_SUBH movlw low .1000 movwf UART_SUBL call bcd_sub call uart_hex_nibble clrf UART_SUBH movlw .100 movwf UART_SUBL call bcd_sub call uart_hex_nibble movlw .10 movwf UART_SUBL call bcd_sub call uart_hex_nibble movf UART_TEMPL, W call uart_hex_nibble return bcd_sub clrf UART_DIGIT ; clear digit bcd_sub_loop movf UART_SUBL, W ; try subtracting subwf UART_TEMPL, F btfsc STATUS, C ; no C means borrow goto bcd_sub_loop_h movlw 1 subwf UART_TEMPH, F btfss STATUS, C ; C means no borrow goto bcd_restore_l bcd_sub_loop_h movf UART_SUBH, W subwf UART_TEMPH, F btfss STATUS, C ; C means no borrow goto bcd_restore incf UART_DIGIT goto bcd_sub_loop bcd_restore_l movf UART_SUBL, W ; restore low byte and carry addwf UART_TEMPL, F btfsc STATUS, C incf UART_TEMPH, F movf UART_DIGIT, W ; return digit return bcd_restore movf UART_SUBL, W ; restore both bytes addwf UART_TEMPL, F btfsc STATUS, C incf UART_TEMPH, F movf UART_SUBH, W addwf UART_TEMPH, F movf UART_DIGIT, W ; return digit return ;------------------------------------------------------------------------------- ; Send byte in W as 8 binary characters with 'b' prefix ; ; uart_binary1 doesn't print prefix ; ; Stack: calls 2-deep ;------------------------------------------------------------------------------- ;uart_binary ; movwf UART_TEMP ; save byte ; movlw 'b' ; call put_char ;ub ; movlw 8 ; movwf UART_COUNT ; counter ; clrf UART_TEMPL ;uart_bin_1 ; rlf UART_TEMP, F ; get next MSB in C ; rlf UART_TEMPL, W ; now in W ; call uart_hex_nibble ; decfsz UART_COUNT, F ; goto uart_bin_1 ; return ; ;uart_binary1 ; movwf UART_TEMP ; save byte ; goto ub ;------------------------------------------------------------------------------- ; *** Send error code in W as ASCII prefixed by 'E' ; ; Stack: calls ?-deep ;------------------------------------------------------------------------------- uart_error return ;------------------------------------------------------------------------------- ; Convert W to upper case ;------------------------------------------------------------------------------- to_upper movwf UART_TEMP movlw 'a' ; Convert to upper case subwf UART_TEMP, W btfss STATUS, C goto is_upper ; no C means borrow so INDF < 'a' movlw 'z'+1 subwf UART_TEMP, W btfsc STATUS, C goto is_upper ; C means no borrow so INDF > 'z' bcf UART_TEMP, 5 ; 'a' - 'z' -> 'A' - 'Z' is_upper movf UART_TEMP, W return ;------------------------------------------------------------------------------- ; Start boot loader ; ; Arrives here for B command. To prevent inadvertent entry to bootloader, ; exactly 3 arguments must be supplied. ;------------------------------------------------------------------------------- cmd_boot movlw 3 ; ARGC=3 ? xorwf ARGC, W btfss STATUS, Z goto boot_error bsf STATUS, RP0 movf ARGV1H, W ; check values iorwf ARGV2H, W iorwf ARGV3H, W bcf STATUS, RP0 btfss STATUS, Z goto boot_error bsf STATUS, RP0 xorwf ARGV1L, W xorwf ARGV2L, W xorwf ARGV3L, W xorlw 1 bcf STATUS, RP0 btfss STATUS, Z goto boot_error clrf INTCON movlw high Loader movwf PCLATH goto Loader boot_error movlw string_error call uart_string return ;------------------------------------------------------------------------------- ; Speedo logging on ; ; Arrives here for L command. ;------------------------------------------------------------------------------- cmd_log_on bsf MODE_WORDH, LOG return ;------------------------------------------------------------------------------- ; Speedo logging off ; ; Arrives here for N command. ;------------------------------------------------------------------------------- cmd_log_off bcf MODE_WORDH, LOG return ;------------------------------------------------------------------------------- ; Read/Program CV direct mode ; ; Arrives here for C command. Decides whether to read or program based on ; number of arguments on command line. ; C can have one 16 bit CV followed by none (read) or one (program) 8 bit argument. ;------------------------------------------------------------------------------- cmd_cv_direct call power_off_cycle call check_cv_address ; check CV address btfsc STATUS, C goto cmd_error movlw 1 ; ARGC=1 ? xorwf ARGC, W btfsc STATUS, Z goto cv_read_direct ; yes - read goto cv_write_direct ; no - write ;------------------------------------------------------------------------------- ; Read/Program CV paged mode ; ; Arrives here for C command. Decides whether to read or program based on ; number of arguments on command line. ; C can have one 16 bit CV followed by none (read) or one (program) 8 bit argument. ;------------------------------------------------------------------------------- cmd_cv_paged call power_off_cycle call check_cv_address ; check CV address btfsc STATUS, C goto cmd_error movlw 1 ; ARGC=1 ? xorwf ARGC, W btfsc STATUS, Z goto cv_read_paged ; yes - read goto cv_write_paged ; no - write ;------------------------------------------------------------------------------- ; Check CV address ; ; CV is in ARG1 just need to check 1 <= CV <= 1024 (0x400) ;------------------------------------------------------------------------------- check_cv_address bsf STATUS, RP0 ; bank 1 for ARGs movf ARGV1H, W ; check for 0 iorwf ARGV1L, W btfss STATUS, Z goto cv_nz cv_err bcf STATUS, RP0 movlw string_cv ; "CV" call uart_string bsf STATUS, C ; "Error" wil be printed by cmd_error return cv_nz movlw 4 subwf ARGV1H, W ; high byte must be < 4 btfss STATUS, C ; C means no borrow goto cv_ok iorlw 0 ; else high byte must have been 4... btfss STATUS, Z goto cv_err movf ARGV1L, W ; ...and low byte must be 0 btfss STATUS, Z goto cv_err cv_ok bcf STATUS, RP0 bcf STATUS, C return ;------------------------------------------------------------------------------- ; Calculate the Page and Register ofsets for paged mode ; ; Using RP-923 algorithm ; Subtract 1 from CV address ; Divide result by 4 ; Remainder is the Register address 0 - 3 ; Add 1 to the quotient to give the Page number ; e.g. CV #65 - 1 = 64 ; 64 divided by 4 = 16, 0 remainder. ; Register 0, Page 17 ;------------------------------------------------------------------------------- get_page_reg clrf CV_REGISTER movlw 1 ; subtract 1 from CV bsf STATUS, RP0 subwf ARGV1L, F btfss STATUS, C ; C set means no borrow subwf ARGV1H, F clrc ; divide by 4 saving remainder rrf ARGV1H, F rrf ARGV1L, F bcf STATUS, RP0 rrf CV_REGISTER, F clrc bsf STATUS, RP0 rrf ARGV1H, F rrf ARGV1L, F bcf STATUS, RP0 rrf CV_REGISTER, F swapf CV_REGISTER, F ; get remainder in correct bits rrf CV_REGISTER, F rrf CV_REGISTER, F ; CV_REGISTER holds register number bsf STATUS, RP0 incf ARGV1L, F ; add one for page number in ARGV1L bcf STATUS, RP0 return ;------------------------------------------------------------------------------- ; Write CV Paged Mode ;------------------------------------------------------------------------------- cv_write_paged bsf STATUS, RP0 movf ARGV2H, W ; value must be <= 255 bcf STATUS, RP0 btfsc STATUS, Z goto cvwp_skip1 cv_wrval_err movlw string_value call uart_string movlw string_error call uart_string return cvwp_skip1 ; ********************* ; Write CV Paged Mode ; ********************* call get_page_reg ; Calculate the page and register movlw 7D ; write to page reg bsf STATUS, RP0 movwf ARGV1H movf ARGV1L, W ; page to be addressed bcf STATUS, RP0 movwf CV_VALUE call power_on_cycle btfsc DCC_FLAGS, DCC_OVERLOAD return ; Return if overload detected call packet_rw_paged ; 8 Program Packets call recovery_time bcf DCC_FLAGS, DCC_ACK ; Clear the Ack Flag movlw 0x78 ; 1st byte 0111CRRR - write iorwf CV_REGISTER, W ; add register address bsf STATUS, RP0 movwf ARGV1H bcf STATUS, RP0 bsf STATUS, RP0 movf ARGV2L, W bcf STATUS, RP0 movwf CV_VALUE cvwp_skip1a call packet_rw_paged ; 8 Program Packets cvwp_skip2 call power_off_cycle btfsc DCC_FLAGS, DCC_ACK goto cvwp_skip3 movlw string_no_ack call uart_string call uart_crlf return cvwp_skip3 movlw string_ok call uart_string call uart_crlf return ;------------------------------------------------------------------------------- ; Write CV Direct Mode ;------------------------------------------------------------------------------- cv_write_direct bsf STATUS, RP0 movf ARGV2H, W ; value must be <= 255 bcf STATUS, RP0 btfsc STATUS, Z goto cvd_skip0 goto cv_wrval_err cvd_skip0 ; ********************** ; Write CV Direct Mode ; ********************** bsf STATUS, RP0 movlw 1 ; Subtract 1 from the address subwf ARGV1L, F ; ie CV1 = 00 skpc subwf ARGV1H, F bcf STATUS, RP0 cvd_skip call power_on_cycle btfsc DCC_FLAGS, DCC_OVERLOAD return ; goto loop ; Return if overload detected movlw 0x7C ; 1st byte 0111CCAA - write bsf STATUS, RP0 iorwf ARGV1H, F ; add address msbs ; 2nd byte is address in ARGV1L bcf STATUS, RP0 bsf STATUS, RP0 movf ARGV2L, W bcf STATUS, RP0 movwf CV_VALUE ; 3rd byte cvd_skip2 call packet_rw_direct ; 8 Program Packets call power_off_cycle btfsc DCC_FLAGS, DCC_ACK goto cvd_skip3 movlw string_no_ack call uart_string call uart_crlf return cvd_skip3 movlw string_ok call uart_string call uart_crlf return ;------------------------------------------------------------------------------- ; Read CV Paged Mode ;------------------------------------------------------------------------------- cv_read_paged movlw string_equals call uart_string ; ********************* ; Read CV Paged Mode ; ********************* call get_page_reg ; Calculate the page and register movlw 0x7D ; write to page reg bsf STATUS, RP0 movwf ARGV1H movf ARGV1L, W ; page to be addressed bcf STATUS, RP0 movwf CV_VALUE call power_on_cycle btfsc DCC_FLAGS, DCC_OVERLOAD return ; Return if overload detected call packet_rw_paged ; 8 Program Packets for page address call recovery_time bcf DCC_FLAGS, DCC_ACK ; Clear the Ack Flag movlw 0 movwf CV_BIT_COUNTER movlw 0x70 ; 1st byte 0111CRRR - verify iorwf CV_REGISTER, W ; add register address bsf STATUS, RP0 movwf ARGV1H bcf STATUS, RP0 cvrp_255_loop movf CV_BIT_COUNTER, W movwf CV_VALUE call packet_rw_paged ; 8 Program Packets btfsc DCC_FLAGS, DCC_ACK goto cvrp_skip2 incf CV_BIT_COUNTER, F bnz cvrp_255_loop goto cvvrp_exit cvrp_skip2 ; ACK detected movf CV_BIT_COUNTER, W movwf CV_VALUE cvrp_skip3 movf CV_VALUE, W call uart_hex_byte call power_off_cycle call uart_crlf return cvvrp_exit call power_off_cycle movlw string_no_ack call uart_string call uart_crlf return ;------------------------------------------------------------------------------- ; Read CV Direct Mode ;------------------------------------------------------------------------------- cv_read_direct movlw string_equals call uart_string ; **************** ; Read CV Direct ; **************** bsf STATUS, RP0 movlw 1 ; Subtract 1 from the address subwf ARGV1L, F ; ie CV1 = 00 skpc subwf ARGV1H, F bcf STATUS, RP0 call power_on_cycle btfsc DCC_FLAGS, DCC_OVERLOAD return ; Return if overload detected movlw 0x78 ; 1st byte 0111CCAA - bit manipulation bsf STATUS, RP0 iorwf ARGV1H, F ; add address msbs ; 2nd byte is address in ARGV1L bcf STATUS, RP0 movlw 0xe7 ; 3rd byte 111KDBBB - verify bit 7 == 0 movwf CV_VALUE movlw 8 movwf CV_BIT_COUNTER clrf CV_READ cvvd_loop2 call packet_rw_direct ; 8 Program Packets btfsc DCC_FLAGS, DCC_ACK goto cvvd_skip1 setc ; no ACK so bit == 1 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 call recovery_time ; ACK so send some reset packets ; movlw 0xA ; Delay 5ms wait for decoder to remove acknowledge ; movlw 0xff ; Delay 128ms 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 ; Now verify the byte just read in case there was never any ACK movf CV_READ, W ; Save the CV value movwf CV_VALUE bsf STATUS, RP0 bcf ARGV1H, 3 ; Change ARGV1H to verify byte bsf ARGV1H, 2 bcf STATUS, RP0 call packet_rw_direct ; 8 Program Packets btfsc DCC_FLAGS, DCC_ACK goto cvrp_skip3 goto cvvrp_exit ;------------------------------------------------------------------------------- ; packet_rw_direct ; ; Generate packet for Direct Mode Program/Read/Verify of the form ; long-preamble 0 0111CCAA 0 AAAAAAAA 0 DDDDDDDD 0 EEEEEEEE 1 ; ; On entry ARGV1H holds first packet byte (0111CCAA) ; ARGV1L holds Address byte (AAAAAAAA) ; CV_VALUE hold data byte (DDDDDDDD or 111KDBBB for bit manipulation) ; Packet sent up to 8 times ;------------------------------------------------------------------------------- packet_rw_direct bcf DCC_FLAGS, DCC_ACK ; Clear the Ack Flag movlw 8 movwf CV_COUNTER prwd_wait btfss DCC_FLAGS, DCC_RDY ; wait for DCC buffer goto prwd_wait prwd_loop1 movlw .20 ; long preamble movwf DCC_PRE bsf STATUS, RP0 movf ARGV1H, W ; First byte bcf STATUS, RP0 movwf DCC_BUFF1 movwf DCC_BUFF4 ; error byte bsf STATUS, RP0 movf ARGV1L, W ; second byte bcf STATUS, RP0 movwf DCC_BUFF2 xorwf DCC_BUFF4, F movf CV_VALUE, W ; third byte movwf DCC_BUFF3 xorwf DCC_BUFF4, F movlw 4 movwf DCC_BYTES bcf DCC_FLAGS, DCC_RDY prwd_loop2 btfss DCC_FLAGS, DCC_RDY ; wait for DCC Tx complete goto prwd_loop2 btfsc DCC_FLAGS, DCC_ACK ; check for ACK goto prwd_skip1a decfsz CV_COUNTER, F goto prwd_loop1 prwd_skip1 return prwd_skip1a return ;------------------------------------------------------------------------------- ; packet_rw_paged ; ; Generate a packet for Page Mode Program/Read of the form ; long-preamble 0 0111CRRR 0 DDDDDDDD 0 EEEEEEEE 1 ; ; On entry ARGV1H holds First Byte ; CV_VALUE holds data byte ; Packet sent up to 8 times ;------------------------------------------------------------------------------- packet_rw_paged bcf DCC_FLAGS, DCC_ACK ; Clear the Ack Flag movlw 8 movwf CV_COUNTER prwp_wait btfss DCC_FLAGS, DCC_RDY ; wait for DCC buffer goto prwp_wait prwp_loop1 movlw .20 ; long preamble movwf DCC_PRE bsf STATUS, RP0 movf ARGV1H, W ; First byte bcf STATUS, RP0 movwf DCC_BUFF1 movwf DCC_BUFF3 ; error byte movf CV_VALUE, W ; second byte movwf DCC_BUFF2 xorwf DCC_BUFF3, F movlw 3 movwf DCC_BYTES bcf DCC_FLAGS, DCC_RDY prwp_loop2 btfss DCC_FLAGS, DCC_RDY ; wait for DCC Tx complete goto prwp_loop2 btfsc DCC_FLAGS, DCC_ACK goto prwp_skip1a decfsz CV_COUNTER, F goto prwp_loop1 prwp_skip1 return prwp_skip1a return ;------------------------------------------------------------------------------- ; Set Mode word ;------------------------------------------------------------------------------- cmd_mode movf ARGC, W btfsc STATUS, Z goto show_mode bsf STATUS, RP0 ; bank 1 movf ARGV1L, W bcf STATUS, RP0 movwf MODE_WORDL bsf STATUS, RP0 ; bank 1 movf ARGV1H, W bcf STATUS, RP0 movwf MODE_WORDH return show_mode movlw 'M' call put_char movlw '=' call put_char movf MODE_WORDH, W call uart_hex_byte movf MODE_WORDL, W call uart_hex_byte1 call uart_crlf return ;------------------------------------------------------------------------------- ; Power on command ; ; Turns on Rolling road mode and sends some reset packets ;------------------------------------------------------------------------------- cmd_pon bsf MODE_WORDL, RR_MODE call power_on_cycle bcf DCC_FLAGS, DCC_CHECK_ACK return ;------------------------------------------------------------------------------- ; Power off command ; ; Turns off Rolling road mode and resets speed to zero ;------------------------------------------------------------------------------- cmd_poff bcf PORTB, POWER ; Power off bcf OP_FLAGS, OP_PWR bcf MODE_WORDL, RR_MODE ; clear rolling road mode clrf RR_SPEED ; and speed return ;------------------------------------------------------------------------------- ; Power On Cycle ; ; RP923 calls for at least 20 valid packets with a check for overload of 250mA ; sustained after 100ms. 14 reset packets gives us approx 105ms, after which the ; decoder quiescent current is logged and checked for overload. We then enable ; gross overload checking in the A/D routines and a further 6 reset packets ; complete the power on cycle. ;------------------------------------------------------------------------------- power_on_cycle movlw .14 ; movlw .30 movwf CV_COUNTER clrf ICCQ bsf PORTB, POWER ; Power on bsf OP_FLAGS, OP_PWR bcf DCC_FLAGS, DCC_OVERLOAD bcf DCC_FLAGS, DCC_CHECK_OVLD bcf DCC_FLAGS, DCC_CHECK_ACK bcf DCC_FLAGS, DCC_ACK pon_loop1 call packet_reset ; 14 Reset Packets ifdef DEBUGAD ; monitor current sense movf AN1AVE, W call uart_hex_byte1 movlw ' ' call put_char endif decfsz CV_COUNTER, F goto pon_loop1 ;*** Overload check should be in ISR & look for 2 (or more) successive samples ; movf AN1L, W ; Read decoder current ifdef DEBUGAD ; monitor current sense movf AN1AVE, W call uart_hex_byte call uart_crlf endif movf AN1AVE, W ; Read decoder current movwf ICCQ movlw I_OVERLOAD subwf ICCQ, W ; W = ICCQ - I_OVERLOAD btfsc STATUS, C ; goto overload_detected ; C set means no borrow bsf DCC_FLAGS, DCC_CHECK_OVLD bsf DCC_FLAGS, DCC_CHECK_ACK movlw .6 movwf CV_COUNTER pon_loop2 call packet_reset ; 6 Reset Packets decfsz CV_COUNTER, F goto pon_loop2 return power_off_cycle call recovery_time bcf PORTB, POWER ; Turn off power to service track bcf OP_FLAGS, OP_PWR return ;------------------------------------------------------------------------------- ; Decoder Recovery Time ; ; RP923 calls for the same service mode write packet or reset packet to be sent ; until the specified packet time or acknowledge. Here we use reset packets. The ; worst case requirement is 10 packets. ;------------------------------------------------------------------------------- recovery_time movlw .10 movwf RT_COUNTER rec_loop call packet_reset ; 10 Reset Packets decfsz RT_COUNTER, F goto rec_loop return overload_detected bcf PORTB, POWER ; Power off ifdef DEBUGAD movlw 'o' call put_char movlw ' ' call put_char endif bcf OP_FLAGS, OP_PWR bsf DCC_FLAGS, DCC_OVERLOAD return ;------------------------------------------------------------------------------- ; cmd_dcc 'O' command ; ; Place a packet in the DCC transmit buffer ; ; send bytes in ARGVs as DCC packet, adding error byte if neccessary ;------------------------------------------------------------------------------- cmd_dcc btfss DCC_FLAGS, DCC_RDY goto cmd_dcc movlw .14 ; normal preamble movwf DCC_PRE movf ARGC, W ; number of bytes movwf DCC_BYTES movlw DCC_BUFF1 ; save pointer to DCC buffer movwf FSR_TEMP2 movlw ARGV1H ; point to ARGs movwf FSR cmd_dcc_loop ; copy ARGS to DCC_BUFF - ARGs are in bank 1 movf INDF, W btfss STATUS, Z ; high byte must be zero goto cmd_error incf FSR, F movf INDF, W ; get ARG low byte movwf DCC_TEMP ; save it incf FSR, F movf FSR, W ; save ARG pointer movwf FSR_TEMP movf FSR_TEMP2, W ; get buffer pointer movwf FSR movf DCC_TEMP, W ; get ARG movwf INDF ; into buffer incf FSR, F ; inc pointer movf FSR, W ; save buffer pointer movwf FSR_TEMP2 movf FSR_TEMP, W ; restore ARG pointer movwf FSR decfsz ARGC, F ; count bytes goto cmd_dcc_loop btfsc MODE_WORDL, CALC_ERROR ; calculate error byte? call calc_error_byte bcf DCC_FLAGS, DCC_RDY ;*** wait for transmit to complete? return ; calculate the DCC error byte calc_error_byte movf DCC_BYTES, W ; number of bytes movwf CV_COUNTER incf DCC_BYTES, F ; extra byte movlw DCC_BUFF1 ; setup pointer movwf FSR clrf DCC_TEMP ; result cmd_dcc_xor movf INDF, W ; next byte xorwf DCC_TEMP, F ; partial result incf FSR, F decfsz CV_COUNTER, F goto cmd_dcc_xor movf DCC_TEMP, W ; result movwf INDF ; to end of buffer return ;------------------------------------------------------------------------------- ; Status ; ; Shows Vin and Vsense A/D readings and Port B bits 7 & 6 ; Also displays timebase ;------------------------------------------------------------------------------- cmd_status movlw 'S' call put_char movlw '=' call put_char movf AN0H, W call uart_hex_byte movf AN0L, W call uart_hex_byte1 movlw ' ' call put_char movf AN1H, W call uart_hex_byte movf AN1L, W call uart_hex_byte1 movlw ' ' call put_char movf PORTB, W andlw 0xc0 call uart_hex_byte movlw ' ' call put_char movf TIME_3S, W call uart_hex_byte movf TIME_14MS, W call uart_hex_byte1 movf TIME_US, W call uart_hex_byte1 call uart_crlf return ;------------------------------------------------------------------------------- ; cmd_address ; ; Set or display address to be used for rolling road commands ; ; By default, addresses greater than 127 are long addresses. Appending another ; argument to the command will force the address to be a long address, ; e.g. A 1 1 ; ; At the moment, long addresses are forced into range with no bounds checking. ;------------------------------------------------------------------------------- cmd_address movf ARGC, W ; any args? btfss STATUS, Z goto set_address movlw string_equals ; no so display address call uart_string movf RR_ADDRL, W movwf UART_TEMPL movf RR_ADDRH, W movwf UART_TEMPH call uart_bcd_word call uart_crlf return set_address bsf STATUS, RP0 movf ARGV1L, W bcf STATUS, RP0 movwf RR_ADDRL bsf STATUS, RP0 movf ARGV1H, W bcf STATUS, RP0 movwf RR_ADDRH xorlw 0 ; test high byte for 0 btfss STATUS, Z goto set_addr_long ; no must be long btfsc RR_ADDRL, 7 ; test low byte > 127 goto set_addr_long ; yes must be long bcf MODE_WORDH, LONG ; default to short movlw 2 ; check for 2nd arg xorwf ARGC btfss STATUS, Z return set_addr_long movlw 0x28 ; is high byte <= 0x27 subwf RR_ADDRH, W btfsc STATUS, C ; no carry means borrow goto cmd_error set_addr_long2 bsf MODE_WORDH, LONG return ;------------------------------------------------------------------------------- ; cmd_speed ; ; Rolling road '<' or '>' set or display speed direct ;------------------------------------------------------------------------------- cmd_speed bcf MODE_WORDH, DIR ; forward movlw '<' ; reverse? xorwf CMD, W btfsc STATUS, Z bsf MODE_WORDH, DIR ; reverse movf ARGC, W ; any args? btfss STATUS, Z goto set_speed show_speed movlw string_equals ; display current step call uart_string movf RR_SPEED, W call uart_bcd_byte movlw '/' ; show max speed step call put_char movlw .126 ; assume 126 btfsc MODE_WORDH, SP28 movlw .28 btfsc MODE_WORDH, SP14 movlw .14 call uart_bcd_byte call uart_crlf return set_speed bsf STATUS, RP0 movf ARGV1H,W btfss STATUS, Z goto speed_err movf ARGV1L, W bcf STATUS, RP0 movwf RR_SPEED movlw 0xf0 ; form mask for invalid speed bits btfsc MODE_WORDH, SP128 movlw 0x80 btfsc MODE_WORDH, SP28 movlw 0xe0 andwf RR_SPEED, W btfsc STATUS, Z return speed_err clrf RR_SPEED bcf STATUS, RP0 movlw string_error call uart_string return ;------------------------------------------------------------------------------- ; cmd_speedu/d ; ; Rolling road '<' (speedd) or '>' (speedu) set and display speed relative ; count_ssteps counts '<' or '>' characters on the command line and sets the ; speed increment/decrement ; ; Speed is incremented towards limit or decremented towards 0 depending on ; command and direction: ; command direction speed ; ------- --------- ----- ; > forward increment towards limit ; > reverse decrement towards zero ; < forward decrement towards zero ; < reverse increment towards limit ; ; Speed sticks at zero or limit. Limit value depends on 14/28/128 mode ;------------------------------------------------------------------------------- cmd_speedu movf RR_SPEED, W ; check for zero btfsc STATUS, Z bcf MODE_WORDH, DIR ; default forwards call steps_up btfsc MODE_WORDH, DIR goto speed_dec speed_inc movlw 0xf ; form mask for valid speed bits btfsc MODE_WORDH, SP128 movlw 0x7f btfsc MODE_WORDH, SP28 movlw 0x1f xorwf RR_SPEED, W btfsc STATUS, Z goto show_speed ; reached limit incf RR_SPEED, F decfsz RR_SP_INC, F goto speed_inc goto show_speed cmd_speedd movf RR_SPEED, W ; check for zero btfsc STATUS, Z bsf MODE_WORDH, DIR ; default reverse call steps_down btfsc MODE_WORDH, DIR goto speed_inc speed_dec btfss MODE_WORDH, SP128 ; 128 step? goto sp_dec14_28 movlw 2 ; check for lowest speed xorwf RR_SPEED, W btfsc STATUS, Z decf RR_SPEED, F ; skip emergency stop sp_not_stop decfsz RR_SPEED, F ; skip if now zero goto sp_ndec ; else next count goto show_speed sp_ndec decfsz RR_SP_INC, F goto speed_dec goto show_speed sp_dec14_28 movlw 2 btfsc MODE_WORDH, SP28 movlw 4 xorwf RR_SPEED, W ; check for lowest speed btfss STATUS, Z goto sp_not_stop clrf RR_SPEED ; skip emergency stop goto show_speed ;------------------------------------------------------------------------------- ; count speed steps ; ; We know there are at least two '<' or '>' and INDF points at the second. We ; ignore the first and start counting at the second. ;------------------------------------------------------------------------------- steps_up clrf RR_SP_INC next_up incf RR_SP_INC, F ; count this one incf FSR, F ; point to the next movlw '>' xorwf INDF, W bz next_up return steps_down clrf RR_SP_INC next_down incf RR_SP_INC, F ; count this one incf FSR, F ; point to the next movlw '<' xorwf INDF, W bz next_down return ;------------------------------------------------------------------------------- ; cmd_function ; ; *** Rolling road send command to toggle function output ;------------------------------------------------------------------------------- cmd_function return ;------------------------------------------------------------------------------- ; cmd_rmode ; ; Read mode word and current limit from EEPROM ;------------------------------------------------------------------------------- cmd_rmode movlw EE_MWH call ee_read movwf MODE_WORDH movlw EE_MWL call ee_read movwf MODE_WORDL movlw EE_IMAX call ee_read movwf IMAX return ;------------------------------------------------------------------------------- ; cmd_wmode ; ; Write mode word and current limit to EEPROM ;------------------------------------------------------------------------------- cmd_wmode movf IMAX, W movwf EE_DATA movlw EE_IMAX call ee_write movf MODE_WORDH, W movwf EE_DATA movlw EE_MWH call ee_write movf MODE_WORDL, W movwf EE_DATA movlw EE_MWL call ee_write movlw MAGIC movwf EE_DATA movlw EE_MAGIC call ee_write return ;------------------------------------------------------------------------------- ; cmd_prog ; ; Set Programmer mode ;------------------------------------------------------------------------------- cmd_prog bcf PORTB, POWER ; Power off bcf OP_FLAGS, OP_PWR bcf MODE_WORDL, RR_MODE bcf ADCON0, 0 ; A/D off bsf STATUS, RP0 movlw 0x82 ; result right justified, 5 analogue inputs movwf ADCON1 ; Configure A/D inputs bcf STATUS, RP0 movlw b'10000001' ; Tosc/32, Channel 0, On movwf ADCON0 clrf AD_STATE clrf ICCQ return ;------------------------------------------------------------------------------- ; ZTC Mode ; ; Arrives here for Z command. If MODE_WORDL, ZTC_MODE is set, TMR0 period is ; changed to generate interrupts every 52us. ;------------------------------------------------------------------------------- cmd_ztc movlw 1 ; ARGC=1 ? xorwf ARGC, W btfss STATUS, Z return bcf MODE_WORDL, ZTC_MODE movlw 0x97 movwf TMR0_RELOAD bsf STATUS, RP0 movlw 1 ; set ZTC mode? xorwf ARGV1L, W btfsc STATUS, Z goto set_ztc bcf STATUS, RP0 return set_ztc bcf STATUS, RP0 bsf MODE_WORDL, ZTC_MODE movlw 0xa3 movwf TMR0_RELOAD return ;------------------------------------------------------------------------------- ; TMR0 Adjust ; ; Arrives here for T command to set new value for TMR0_RELOAD and adjust DCC bit ; timing. ;------------------------------------------------------------------------------- cmd_tmr0 movlw 1 ; ARGC=1 ? xorwf ARGC, W btfss STATUS, Z goto show_tmr0 bsf STATUS, RP0 movf ARGV1L, W bcf STATUS, RP0 movwf TMR0_RELOAD return show_tmr0 movf TMR0_RELOAD, W call uart_hex_byte call uart_crlf return ;------------------------------------------------------------------------------- ; next_digit ; ; increments pointer. Returns with Carry set if end of buffer or CR found ;------------------------------------------------------------------------------- next_digit incf FSR, F this_digit movlw RX_BUFFE+1 xorwf FSR, W bsf STATUS, C btfsc STATUS, Z return movf INDF, W call to_upper movwf INDF bsf STATUS, C xorlw ASCII_CR btfsc STATUS, Z return bcf STATUS, C return ;------------------------------------------------------------------------------- ; Skip over spaces in input buffer ; ; Returns with Carry set if end of buffer or CR found ;------------------------------------------------------------------------------- skip_space call this_digit btfsc STATUS, C return movlw ' ' xorwf INDF, W btfsc STATUS, Z goto skip_next movlw ASCII_TAB xorwf INDF, W btfss STATUS, Z return skip_next incf FSR, F goto skip_space ;------------------------------------------------------------------------------- ; Convert an argument in the serial input buffer ; ; On entry FSR must point to non-space character in receive buffer ; DIGITS must be set to max number of digits to get (0 means 256) ; *** DIGITS only implemented for hex input ; ; Results are placed in ARGVxH & ARGVxL (bank 1). Current value of ARGC is used ; as pointer, then increments ARGC. Number of digits is not checked. ; ; Returns with Carry set if error during conversion ;------------------------------------------------------------------------------- get_arg clrf ARGVL ; clear arg buffer in bank 0 clrf ARGVH bcf STATUS, C ; if forced for 'O' command then get hex btfsc MODE_WORDH, O_CMD goto forced_hex ; if 'b' then get binary movf INDF, W xorlw 'B' btfsc STATUS, Z goto gb_next ; else if 'h' then get hex movf INDF, W xorlw 'H' btfsc STATUS, Z goto gh_next ; else if 'd' then get bcd movf INDF, W xorlw 'D' btfsc STATUS, Z goto gbcd_next ; fall through to decimal ;------------------------------------------------------------------------------- ; get_bcd ;------------------------------------------------------------------------------- get_bcd btfsc STATUS, C ; check C in case we looped here goto save_arg movlw '0' ; must be >= '0' subwf INDF, F btfss STATUS, C ; C means no borrow goto not_digit ; may be space or CR which is OK movlw 0xa ; numeric must be < 10 subwf INDF, W btfsc STATUS, C ; no C means borrow goto arg_err rlf ARGVL, F ; buffer = arg * 2 rlf ARGVH, F movf ARGVL, W ; save buffer movwf MATHL movf ARGVH, W movwf MATHH bcf STATUS, C rlf ARGVL, F ; buffer = arg * 4 rlf ARGVH, F bcf STATUS, C rlf ARGVL, F ; buffer = arg * 8 rlf ARGVH, F bcf STATUS, C movf INDF, W ; digit addwf MATHL, W ; + arg * 2 (low) btfsc STATUS, C incf ARGVH, F ; carry addwf ARGVL, F ; + arg * 8 (low) btfsc STATUS, C incf ARGVH, F ; carry movf MATHH, W ; arg * 2 (high) addwf ARGVH, F ; + arg * 8 (high) gbcd_next call next_digit goto get_bcd ;------------------------------------------------------------------------------- ; get_hex ;------------------------------------------------------------------------------- get_hex btfsc STATUS, C ; check C in case we looped here goto save_arg movlw '0' ; must be >= '0' subwf INDF, F btfss STATUS, C ; C means no borrow goto not_digit ; may be space or CR which is OK movlw 0xa ; numeric must be < 10 subwf INDF, W btfss STATUS, C ; C means no borrow goto shift_hex movlw 0x11 ; alpha must be >= 17 subwf INDF, F btfss STATUS, C ; C means no borrow goto arg_err movlw 0x6 ; alpha must now be < 6 subwf INDF, W btfsc STATUS, C ; no C means borrow goto arg_err movlw 0xa ; correction for alpha addwf INDF, F shift_hex swapf INDF, F ; rotate W lsb nibble into buffer movlw 4 movwf GH_COUNT bcf STATUS, C ; just in case sh_loop rlf INDF, F rlf ARGVL, F rlf ARGVH, F decfsz GH_COUNT, F goto sh_loop decfsz DIGITS, F goto gh_next goto save_arg gh_next call next_digit goto get_hex forced_hex ; O command forced to hexadecimal goto get_hex ;------------------------------------------------------------------------------- ; get_binary ;------------------------------------------------------------------------------- get_binary btfsc STATUS, C ; check C in case we looped here goto save_arg movlw '0' ; must be >= '0' subwf INDF, F btfss STATUS, C ; C means no borrow goto not_digit ; may be space or CR which is OK movlw 0x2 ; numeric must be < 2 subwf INDF, W btfsc STATUS, C ; no C means borrow goto arg_err btfsc INDF, 0 ; is it '0' or '1' bsf STATUS, C rlf ARGVL, F ; rotate into buffer rlf ARGVH, F gb_next call next_digit goto get_binary not_digit movlw '0' ; Correct the digit addwf INDF, F movlw ' ' ; space is OK xorwf INDF, W btfsc STATUS, Z goto save_arg movlw ASCII_TAB xorwf INDF, W btfss STATUS, Z goto arg_err save_arg movf FSR, W ; save receive buffer pointer movwf FSR_TEMP movlw ARGV1H ; form pointer = ARGV1H + 2 * ARGC addwf ARGC, W addwf ARGC, W movwf FSR ; points to ARGs in bank 1 movf ARGVH, W ; copy result to ARGV[ARGC] movwf INDF incf FSR, F movf ARGVL, W movwf INDF incf ARGC, F ; count arg movf FSR_TEMP, W ; restore pointer movwf FSR bcf STATUS, C return arg_err bsf STATUS, C return ;------------------------------------------------------------------------------- ; Pre boot code ; ; Was part of main but now separate to allow USART to be set up cleanly whether ; or not bootloader is required. ;------------------------------------------------------------------------------- pre_boot ; setup initial values before enabling ports ; Port A are analogue bcf OP_FLAGS, OP_PWR movlw PORTB_INIT movwf PORTB movlw PORTC_INIT movwf PORTC bsf STATUS, RP0 movlw 0xff movwf TRISA movlw PORTB_DDR movwf TRISB movlw PORTC_DDR movwf TRISC bcf STATUS, RP0 clrf INTCON ; Setup USART ; ; Interrupts are not used but the flag bits will be polled during the ; TMR0 ISR to handle Rx data. Tx is handled by polling in put_char clrf RCSTA bsf STATUS, RP0 clrf TXSTA movlw BRG_VAL ; set baud rate movwf SPBRG movlw 0x20 ; 8-bit tx, tx enabled, async, BRG low movwf TXSTA bcf STATUS, RP0 movlw 0x90 ; 8-bit rx, rx enabled, serial port enabled movwf RCSTA movlw high BootMain movwf PCLATH goto BootMain ; go to boot loader ;------------------------------------------------------------------------------- ; Start of main code ; ; Setup ADCON ; Enable interrupts ; Loop for input ;------------------------------------------------------------------------------- main ; Assign prescaler to TMR0 2:1 bsf STATUS, RP0 ifdef DEBUG movlw 0x87 ; 256:1 prescaler else movlw 0x80 endif movwf OPTION_REG bcf STATUS, RP0 clrf TMR0 clrf OVLD_DELAY movlw 6 ; idle state movwf BIT_FLAG clrf DCC_FLAGS bsf DCC_FLAGS, DCC_RDY call cmd_rmode ; read mode & current limit movlw EE_MAGIC ; check for mode word magic value call ee_read xorlw .93 btfsc STATUS, Z goto got_mode default_mode ; not found so set defaults movlw 0xc movwf MODE_WORDL ; CALC_ERROR, VERBOSE, ECHO_OFF clrf MODE_WORDH ; forward bsf MODE_WORDH, SP28 ; 28 step movlw I_LIMIT movwf IMAX got_mode ; Setup A/D - 5 i/ps with internal reference bsf STATUS, RP0 movlw 0x82 ; result right justified movwf ADCON1 ; Configure A/D inputs bcf STATUS, RP0 ; Select Bank0 movlw b'10000001' ; Tosc/32, Channel 0, On movwf ADCON0 clrf AD_STATE clrf ICCQ ; Enable TMR0 interrupts clrf TIME_US clrf TIME_14MS clrf TIME_3S movlw 0x97 ; 58us period movwf TMR0_RELOAD movlw 0xa3 ; 52us period for ZTC mode btfsc MODE_WORDL, ZTC_MODE movwf TMR0_RELOAD bsf INTCON, T0IE bsf INTCON, GIE ; Rolling road defaults to address 3 stopped clrf RR_ADDRH movlw 3 movwf RR_ADDRL clrf RR_SPEED ; zero speed cmd_help call uart_crlf movlw string_whoami call uart_string loop ; Main Program Loop movlw RX_BUFFS movwf RX_PTR bcf MODE_WORDH, O_CMD prompt movlw string_prompt_p btfsc MODE_WORDL, RR_MODE movlw string_prompt_r show_prompt call uart_string bcf PORTC, U_CTS_OUT_N ; enable USB input again bcf PORTB, CTS_OUT_N ; enable RS232 input again clrf COMM_FLAGS wait_cmd btfss DCC_FLAGS, DCC_OVERLOAD goto wait_cmd1a movlw string_overload call uart_string bcf DCC_FLAGS, DCC_OVERLOAD goto loop wait_cmd1a btfss COMM_FLAGS, ECHO ; char to echo? goto wait_cmd2 bcf COMM_FLAGS, ECHO movlw ASCII_ESC ; shutdown? xorwf ECHO_BUFFER, W btfss STATUS, Z goto wait_cmd1 bcf PORTB, POWER bcf OP_FLAGS, OP_PWR bcf MODE_WORDL, RR_MODE goto loop wait_cmd1 btfss MODE_WORDL, ECHO_ON ; want to echo? goto wait_cmd2 ; *** Use EOB flag to send bell instead movf ECHO_BUFFER, W call put_char xorlw ASCII_CR ; carriage return? btfss STATUS, Z goto wait_cmd2 movlw ASCII_LF ; append line feed call put_char wait_cmd2 btfsc COMM_FLAGS, CMD_RDY ; anything to do? goto action btfsc COMM_FLAGS, OVERRUN ; USART overrun? goto orun_err btfss DCC_FLAGS, DCC_RDY ; and DCC tx buffer rdy? goto wait_cmd btfss MODE_WORDL, RR_MODE ; Rolling road mode? goto wait_cmd send_packet call packet_gen ; send a packet goto wait_cmd orun_err movlw string_overrun ; ISR has already cleaned up the buffer call uart_string ; so just send error message call uart_crlf goto loop action ; *** should wait for current DCC activity to finish ; the individual commands may do this ; e.g. read won't return until the read is complete ; cmd buffer is ready - first get command movlw RX_BUFFS ; set up buffer pointer movwf FSR call skip_space btfsc STATUS, C ; check for end of buffer goto loop ; line was empty so no error report movf INDF, W ; get command call to_upper ; to upper case movwf INDF movwf CMD incf FSR, F ; bump pointer clrf ARGC ; now get arguments clrf ARGVL clrf ARGVH movlw '<' ; special case for '<' and '>' xorwf CMD, W bz try_speed movlw '>' xorwf CMD, W bz try_speed goto parse_args try_speed movf INDF, W ; is next char also '<' or '>' xorlw '<' bnz try_speedu call cmd_speedd goto loop try_speedu movf INDF, W xorlw '>' bnz parse_args ; if not it's handled below call cmd_speedu goto loop parse_args movlw 'Q' xorwf CMD, W ; special case for Q command bz binary_args movlw 'O' xorwf CMD, W ; special case for O command btfsc STATUS, Z bsf MODE_WORDH, O_CMD call skip_space ; skip over spaces btfsc STATUS, C ; check for end of line goto got_args clrf DIGITS ; default to large number call get_arg ; convert argument btfsc STATUS, C goto cmd_error1 movlw ARGS_MAX ; finish if we've got enough subwf ARGC, W btfss STATUS, C ; C set means no borrow so ARGC >= ARGS_MAX goto parse_args got_args call arg_jump goto loop cmd_error1 call cmd_error goto loop cmd_error movlw string_error ; display error message call uart_string call uart_crlf return ; This may not be 100% optimal as Rx buffer is always reset when we loop ; it's not treated as a circular buffer so extra DCC preamble may be inserted ; *** use of CTS??? binary_args bcf MODE_WORDL, RR_MODE ; clear RR mode btfsc OP_FLAGS, OP_PWR ; Is power on goto ba1 call power_on_cycle bcf DCC_FLAGS, DCC_CHECK_ACK ba1 btfss DCC_FLAGS, DCC_RDY ; wait for DCC buffer free goto binary_args movlw DCC_BUFF1 ; set up DCC buffer pointer movwf FSR_TEMP2 clrf DCC_BYTES ; clear byte count bin_loop movlw ASCII_CR ; end of line? xorwf INDF, W btfsc STATUS, Z goto got_bin_args movlw ASCII_DLE ; is next byte DLE? xorwf INDF, W btfsc STATUS, Z incf FSR, F ; yes, so skip it movf INDF, W ; get byte movwf DCC_TEMP ; save it incf DCC_BYTES, F ; count byte incf FSR, W ; update Rx buffer pointer movwf FSR_TEMP ; and save it movf FSR_TEMP2, W ; get DCC buffer pointer movwf FSR movf DCC_TEMP, W ; get byte movwf INDF ; into buffer incf FSR, W ; update DCC buffer pointer movwf FSR_TEMP2 ; and save it movf FSR_TEMP, W ; restore Rx buffer pointer movwf FSR goto bin_loop got_bin_args call calc_error_byte bcf DCC_FLAGS, DCC_RDY ; mark DCC buffer as valid goto loop ;------------------------------------------------------------------------------- ; Interrupt Service Routine ; ; TMR0 generates a heartbeat every 58uS to generate timing for the DCC bit ; stream. If no DCC packet to be transmitted then preamble is generated. ; ; USART receive buffer is polled on every tick. ; ; A/D readings are stored in RAM. ;------------------------------------------------------------------------------- isr movwf W_ISR ; 3 Copy W to shared RAM swapf STATUS, W ; 4 Swap status to be saved into W movwf S_ISR ; 5 Save status movf PCLATH, w ; 6 Save program page movwf PCLATH_ISR ; 7 Save PCLATH nop ; 8 nop ; 9 nop ; 10 movf FSR, w ; 11 Save FSR movwf FSR_ISR ; 12 clrf STATUS ; 13 Clear IRP,RP1,RP0 (bank 0) bcf INTCON, T0IF ; 14 Clear Timer interrupt flag ; Reload TMR0 for interrupt every 58us ; Tcyc = 250ns @ 16MHz ; Interrupt every 58/(.25) = 232 Tcyc ; Value loaded into TMR0 needs to be adjusted for: ; - TMR0 interrupt latency (3 Tcyc) ; - Number of instructions from interrupt to reload ; - TMR0 inhibit after reload (2 Tcyc with 2:1 prescaler) ; Prescaler = 2:1 ; So value is 0 - (232 - 3 - 17 - 2)/2 = -105 = 0x97 nop ; 15 1 Tcyc pad movf TMR0_RELOAD, W ; 16 movwf TMR0 ; 17 ; Original MERG design only enables TMR0 when there's something to do ; Now it's enabled all the time and we send a continuous preamble ; as required for booster operation. The power may, however, be off ; during this time goto dcc_jump first_idle ; state 6 call toggle_dcc decf BIT_FLAG, F ; goto second_idle goto service_usart second_idle ; state 5 call toggle_dcc movlw 6 ; first idle btfss DCC_FLAGS, DCC_RDY ; packet to transmit? movlw 8 ; yes goto first pre movwf BIT_FLAG btfsc DCC_FLAGS, DCC_RDY ; packet to transmit? goto service_usart movlw DCC_BUFF1 ; yes set up pointer movwf DCC_PTR movlw 8 ; number of bits movwf DCC_BITS goto service_usart ; *** could keep track of how many preamble bits have been sent with power on ; but for now we assume we are starting from scratch first_pre ; state 8 call toggle_dcc decf BIT_FLAG, F ; goto second_pre goto service_usart second_pre ; state 7 call toggle_dcc movlw .12 ; ready for start bit decfsz DCC_PRE, F ; but maybe more preamble? movlw 8 ; so back to first pre movwf BIT_FLAG goto service_usart first_start ; state .12 call toggle_dcc decf BIT_FLAG, F ; goto first_mid_start goto service_usart first_mid_start ; state .11 decf BIT_FLAG, F ; goto second_start goto service_usart second_start ; state .10 call toggle_dcc decf BIT_FLAG, F ; goto second_mid_start goto service_usart second_mid_start ; state 9 clrf BIT_FLAG ; first edge goto service_usart first_edge ; state 0 call toggle_dcc movf DCC_PTR, W ; point to buffer movwf FSR movlw 1 ; second edge bit1 btfss INDF, 7 ; Check next data bit movlw 4 ; first_midpoint_bit0 movwf BIT_FLAG goto service_usart second_edge_bit1 ; state 1 call toggle_dcc goto next_bit second_midpoint_bit0 ; state 2 goto next_bit second_edge_bit0 ; state 3 call toggle_dcc decf BIT_FLAG, F ; goto second_midpoint_bit0 goto service_usart first_midpoint_bit0 ; state 4 decf BIT_FLAG, F ; goto second_edge_bit0 goto service_usart next_bit clrf BIT_FLAG ; likely to be first edge next movf DCC_PTR, W ; point to buffer movwf FSR rlf INDF, F ; Rotate DCC data byte for next bit decfsz DCC_BITS, F ; count bits goto service_usart incf DCC_PTR, F ; all bits done - next byte movlw 8 ; Reload bit counter movwf DCC_BITS decfsz DCC_BYTES, F ; count bytes goto more_bytes movlw 6 ; end movwf BIT_FLAG bsf DCC_FLAGS, DCC_RDY goto service_usart more_bytes movlw .12 ; more bytes so another start bit movwf BIT_FLAG goto service_usart service_usart btfss PIR1, RCIF ; Poll for Received character goto service_ad ;! btfss RCSTA, OERR ; check for overrun goto char_in ; Overrun means something must be very wrong. Clean it up and throw away ; receive buffer. Main loop will print an error message bcf RCSTA, CREN ; disable receiver bcf RCSTA, CREN ; clear error flag movf RCREG, W ; throw away received data movf RCREG, W movlw RX_BUFFS ; Reset receive buffer pointer movwf RX_PTR bsf COMM_FLAGS, OVERRUN bsf RCSTA, CREN ; enable receiver goto service_ad char_in ; *** should really check for framing error movf RX_PTR, W ; set up pointer movwf FSR ; FSR now points to buffer in bank 1 movf RCREG, W ; get the character movwf INDF ; into the buffer movwf ECHO_BUFFER ; to be echoed bsf COMM_FLAGS, ECHO btfss COMM_FLAGS, DLE_RX ; was previous character a DLE? goto check_dle bcf COMM_FLAGS, DLE_RX ; clear flag goto exit_usart ; and take current character as is check_dle xorlw ASCII_DLE ; is current character a DLE? btfsc STATUS, Z bsf COMM_FLAGS, DLE_RX movlw ASCII_BS ; back space? xorwf INDF, W btfss STATUS, Z goto test_cr movlw RX_BUFFS ; check if already at beginning of buffer xorwf FSR, W btfss STATUS, Z decf RX_PTR, F goto service_ad test_cr movlw ASCII_CR ; end of line? xorwf INDF, W btfss STATUS, Z goto exit_usart bsf PORTC, U_CTS_OUT_N ; set USB CTS off bsf PORTB, CTS_OUT_N ; set RS232 CTS off bsf COMM_FLAGS, CMD_RDY ; command is ready exit_usart incf RX_PTR, F ; update pointer movlw RX_BUFFE ; check for end of buffer (leaves room for CR) xorwf FSR, W btfsc STATUS, Z bsf COMM_FLAGS, EOB service_ad ; ; one state covers aquisition time, a second state the conversion time and there are ; 5 channels. A further 10 dummy states mean that each channel converted every ; 20*TMR0_RELOAD or 1160us assuming nominal DCC bit timing ; ; Current sense samples on channel 1 are averaged over two conversion periods. ; ; If a potential overload condition is sensed, a timer is started to check again after ; 116ms. If the overload is still present then DCC_FLAGS, DCC_OVERLOAD is set goto ad_jump ad_reset incf AD_STATE, F ; -> acq_an0 goto exit_ad acq_an0 incf AD_STATE, F ; -> conv_an0 bsf ADCON0, GO goto exit_ad conv_an0 btfsc ADCON0, GO goto exit_ad movf ADRESH, W ; get result movwf AN0H bsf STATUS, RP0 movf ADRESL, W bcf STATUS, RP0 movwf AN0L movlw b'10001001' ; select channel 1 movwf ADCON0 incf AD_STATE, F ; -> acq_an1 goto exit_ad acq_an1 incf AD_STATE, F ; -> conv_an1 bsf ADCON0, GO goto exit_ad conv_an1 btfsc ADCON0, GO goto exit_ad movf AN1L, W ; Save previous sample movwf AN1PREV movf ADRESH, W ; get result movwf AN1H bsf STATUS, RP0 movf ADRESL, W bcf STATUS, RP0 movwf AN1L movlw b'10010001' ; select channel 2 movwf ADCON0 incf AD_STATE, F ; -> acq_an2 movf AN1PREV, W ; Average over 2 samples addwf AN1L, W movwf AN1AVE rrf AN1AVE, F movf OVLD_DELAY, W ; have we had a potential overload? btfss STATUS, Z goto wait_ovld ; yes - wait before checking again btfss DCC_FLAGS, DCC_CHECK_ACK goto check_overload ; has current increased, denoting ACK? movf ICCQ, W addlw I_ACK_DIFF ; W = ICCQ + I_ACK_DIFF ; *** should really do over 16 bits of AN1 subwf AN1AVE, W ; W = AN1AVE - (ICCQ + I_ACK_DIFF) btfsc STATUS, C bsf DCC_FLAGS, DCC_ACK ; C means no borrow goto exit_ad check_overload btfss DCC_FLAGS, DCC_CHECK_OVLD goto exit_ad movf IMAX, W ; check for gross overload ; *** should really do over 16 bits of AN1 subwf AN1AVE, W ; W = AN1AVE - IMAX btfss STATUS, C goto exit_ad movlw .100 ; 10*1.16ms = 116ms delay movwf OVLD_DELAY goto exit_ad wait_ovld decfsz OVLD_DELAY goto exit_ad movf IMAX, W ; check again for gross overload ; *** should really do over 16 bits of AN1 subwf AN1AVE, W ; W = AN1AVE - IMAX btfss STATUS, C goto exit_ad bsf DCC_FLAGS, DCC_OVERLOAD ; C means no borrow so overload bcf DCC_FLAGS, DCC_CHECK_OVLD bcf PORTB, POWER bcf OP_FLAGS, OP_PWR goto exit_ad acq_an2 incf AD_STATE, F ; -> conv_an2 bsf ADCON0, GO goto exit_ad conv_an2 btfsc ADCON0, GO goto exit_ad movf ADRESH, W ; get result movwf AN2H bsf STATUS, RP0 movf ADRESL, W bcf STATUS, RP0 movwf AN2L bcf STATUS, RP0 movlw b'10011001' ; select channel 3 movwf ADCON0 incf AD_STATE, F ; -> acq_vref goto exit_ad acq_vref incf AD_STATE, F ; -> conv_vref bsf ADCON0, GO goto exit_ad conv_vref btfsc ADCON0, GO goto exit_ad movf ADRESH, W ; get result movwf VREFH bsf STATUS, RP0 movf ADRESL, W bcf STATUS, RP0 movwf VREFL bcf STATUS, RP0 movlw b'10100001' ; select channel 4 movwf ADCON0 incf AD_STATE, F ; -> acq_an4 goto exit_ad acq_an4 incf AD_STATE, F ; -> conv_an4 bsf ADCON0, GO goto exit_ad conv_an4 btfsc ADCON0, GO goto exit_ad movf ADRESH, W ; get result movwf AN4H bsf STATUS, RP0 movf ADRESL, W bcf STATUS, RP0 movwf AN4L bcf STATUS, RP0 movlw b'10000001' ; select channel 0 movwf ADCON0 ; movlw 1 ; movwf AD_STATE ; -> acq_an0 incf AD_STATE, F ; -> dummy1 dummy1 incf AD_STATE, F ; -> dummy2 dummy2 incf AD_STATE, F ; -> dummy3 dummy3 incf AD_STATE, F ; -> dummy4 dummy4 incf AD_STATE, F ; -> dummy5 dummy5 incf AD_STATE, F ; -> dummy6 dummy6 incf AD_STATE, F ; -> dummy7 dummy7 incf AD_STATE, F ; -> dummy8 dummy8 incf AD_STATE, F ; -> dummy9 dummy9 incf AD_STATE, F ; -> dummy10 dummy10 movlw 1 movwf AD_STATE ; -> acq_an0 exit_ad ; Heartbeat incfsz TIME_US, F ; 58us goto exit_isr incfsz TIME_14MS, F ; .058 * 256 = 14.848ms goto exit_isr incf TIME_3S, F ; .058 * 256 * 256 = 3.801088s ;*** check and de-bounce PORTB 7 & 6 nop exit_isr movf FSR_ISR, W ; restore FSR movwf FSR movf PCLATH_ISR, W movwf PCLATH ; Restore program page swapf S_ISR, W ; Swap S_ISR register into W ; (sets bank to original state) movwf STATUS ; Move W into STATUS register swapf W_ISR, F ; Swap W_ISR IN shared RAM swapf W_ISR, W ; Swap W_ISR into W retfie ; Return & enable interrupts ;------------------------------------------------------------------------------- ; toggle_dcc ; ; Called from ISR to toggle the two DCC outputs. Turns both off and waits 2us to ; allow MOSFETS to turn off. Returns if power is supposed to be off else turns ; one output on. ;------------------------------------------------------------------------------- toggle_dcc movf PORTB, W ; turn both outputs off andlw 0xcf movwf PORTB nop ; wait 2us nop nop nop nop nop nop nop btfss OP_FLAGS, OP_PWR return btfsc OP_FLAGS, OP_BIT goto set_pos bsf OP_FLAGS, OP_BIT bsf PORTB, DCC_NEG return set_pos bcf OP_FLAGS, OP_BIT bsf PORTB, DCC_POS return ;------------------------------------------------------------------------------- ; packet_gen ; ; Called from the input wait loop to handle rolling road packet generation. ; ; In rolling road mode, packet is built based on address and speed parameters ; as set by A, < and > commands. ; ; 14/28 speed step is a 3 or 4 byte packet
<01DUSSSS> ; ought to be able to set U to FL control ; 128 speed step is a 4 or 5 byte packet
<00111111> ; ;
can be 7 bit in a single byte <0aaaaaaa> or 14 bit in two bytes ; <11aaaaaa> . The first byte of a 14 bit address has a valid range of ; 0xc0 to 0xe7. ;------------------------------------------------------------------------------- packet_gen movlw .14 ; normal preamble movwf DCC_PRE movlw 2 ; number of bytes - 1 movwf DCC_BYTES movf RR_ADDRL, W ; address movwf DCC_BUFF1 btfsc MODE_WORDH, SP128 goto pkt_128 movf RR_SPEED, W ; speed andlw 0x1f ; mask 28 step speed bits btfss MODE_WORDH, SP28 andlw 0xf ; mask 14 step speed bits movwf DCC_BUFF2 btfss MODE_WORDH, SP28 goto pkt_14_28 bcf STATUS, C rrf DCC_BUFF2, F ; get lsb into C btfsc STATUS, C ; then put it in bit 4 bsf DCC_BUFF2, 4 pkt_14_28 bsf DCC_BUFF2, 6 ; command <01000000> reverse btfss MODE_WORDH, DIR bsf DCC_BUFF2, 5 ; forward btfsc MODE_WORDH, LONG ; long address? goto pkt_long pkt_calc_err ;*** combine this with 'O' command movf DCC_BYTES, W ; number of bytes - 1 movwf CV_COUNTER incf DCC_BYTES, F ; count error byte movlw DCC_BUFF1 ; setup pointer movwf FSR clrf DCC_BUFF5 ; result pkt_dcc_xor movf INDF, W ; next byte xorwf DCC_BUFF5, F ; partial result incf FSR, F decfsz CV_COUNTER, F goto pkt_dcc_xor movf DCC_BUFF5, W ; result movwf INDF ; to end of buffer bcf DCC_FLAGS, DCC_RDY return pkt_128 incf DCC_BYTES, F ; extra byte for 128 step movlw 0x3f ; command <00111111> movwf DCC_BUFF2 movf RR_SPEED, W movwf DCC_BUFF3 bsf DCC_BUFF3, 7 ; forward btfsc MODE_WORDH, DIR bcf DCC_BUFF3, 7 ; reverse movf DCC_BUFF3, W btfsc MODE_WORDH, LONG ; long address? goto pkt_long goto pkt_calc_err pkt_long incf DCC_BYTES, F ; extra byte for long address movf DCC_BUFF3, W movwf DCC_BUFF4 movf DCC_BUFF2, W movwf DCC_BUFF3 movf RR_ADDRL, W ; get long address movwf DCC_BUFF2 movf RR_ADDRH, W iorlw 0xc0 movwf DCC_BUFF1 goto pkt_calc_err ;------------------------------------------------------------------------------- ; Bootloader code based on Microchip AN732 and boot877.asm. Once load is ; complete, SPROG will restart with new code. ; ; Any new code downloaded must preserve the code at the reset vector and at ; address 8. The bootloader jumps to address 8 when complete as a way to reach ; the main routine which may move around with each code version. ; ; Bootloader can is entered at reset if the boot switch is closed or if there ; is no valid code present. Bootloader can also be called from the command ; line. ; ;----------------------------------------------------------------------------- ;----------------------------------------------------------------------------- ;Variables in bank0 CBLOCK 0x20 AddressH: 1 ;flash program memory address high byte AddressL: 1 ;flash program memory address low byte NumWords: 1 ;number of words in line of hex file Checksum: 1 ;byte to hold checksum of incoming data Counter: 1 ;to count words being saved or programmed HexByte: 1 ;byte from 2 incoming ascii characters DataPointer: 1 ;pointer to data in buffer DataArray: 0x40 ;buffer for storing incoming data ENDC ;----------------------------------------------------------------------------- ;Macros to select the register bank ;Many bank changes can be optimised when only one STATUS bit changes Bank0 MACRO ;macro to select data RAM bank 0 bcf STATUS,RP0 bcf STATUS,RP1 ENDM Bank1 MACRO ;macro to select data RAM bank 1 bsf STATUS,RP0 bcf STATUS,RP1 ENDM Bank2 MACRO ;macro to select data RAM bank 2 bcf STATUS,RP0 bsf STATUS,RP1 ENDM Bank3 MACRO ;macro to select data RAM bank 3 bsf STATUS,RP0 bsf STATUS,RP1 ENDM ;============================================================================= ;Start of boot code in upper memory traps accidental entry into boot code area ORG 0x0700 ;Use last part of page0 for PIC16F870/1 StartOfBoot: movlw high TrapError ;trap if execution runs into boot code movwf PCLATH ;set correct page TrapError: goto TrapError ;trap error and wait for reset ;----------------------------------------------------------------------------- ;Program memory location to show whether valid code has been programmed CodeStatus: DA 0x0 ;0 for valid code, 0x3fff for no code ;----------------------------------------------------------------------------- ; Main boot code routine ; Tests to see if a load should occur (boot switch closed) and if valid user ; code exists BootMain: Bank0 ;change to bank0 in case of soft reset btfss PORTB,TEST_INPUT ;check pin for boot load goto Loader ;if low then do bootload call LoadStatusAddr ;load address of CodeStatus word call FlashRead ;read data at CodeStatus location Bank2 movf EEDATA,F ;set Z flag if data is zero Bank0 btfss STATUS,Z goto Loader ; no valid code so start bootloader clrf PCLATH goto main_jump ; jump to main routine ;----------------------------------------------------------------------------- ;Start of routine to load and program new code. B command comes here. Loader: movlw 'L' call SerialTransmit movlw '>' call SerialTransmit movlw 0x0d call SerialTransmit movlw 0x0a call SerialTransmit ;----------------------------------------------------------------------------- ;Get new line of hex file starting with ':' ;Get first 8 bytes after ':' and extract address and number of bytes GetNewLine: call SerialReceive ;get new byte from serial port xorlw ':' ;check if ':' received btfss STATUS,Z goto GetNewLine ;if not then wait for next byte clrf Checksum ;start with checksum zero call GetHexByte ;get number of program data bytes in line andlw 0x1F ;limit number in case of error in file movwf NumWords bcf STATUS,C rrf NumWords,F ;divide by 2 to get number of words call GetHexByte ;get upper half of program start address movwf AddressH call GetHexByte ;get lower half of program start address movwf AddressL bcf STATUS,C rrf AddressH,F ;divide address by 2 to get word address rrf AddressL,F call GetHexByte ;get record type xorlw 0x01 btfsc STATUS,Z ;check if end of file record (0x01) goto FileDone ;if end of file then all done movf HexByte,W xorlw 0x00 btfss STATUS,Z ;check if regular line record (0x00) goto LineDone ;if not then ignore line and send '.' movlw 0xe0 addwf AddressH,W ;check if address < 0x2000 btfsc STATUS,C ;which is ID locations and config bits goto LineDone ;if so then ignore line and send '.' ;----------------------------------------------------------------------------- ;Get data bytes and checksum from line of hex file movlw DataArray movwf FSR ;set pointer to start of array movf NumWords,W movwf Counter ;set counter to number of words GetData: call GetHexByte ;get low data byte movwf INDF ;save in array incf FSR,F ;point to high byte call GetHexByte ;get high data byte movwf INDF ;save in array incf FSR,F ;point to next low byte decfsz Counter,F goto GetData call GetHexByte ;get checksum movf Checksum,W ;check if checksum correct btfss STATUS,Z goto ErrorMessage bsf PORTC,U_CTS_OUT_N ;set CTS off to stop data being received bsf PORTB,CTS_OUT_N ;set CTS off to stop data being received call LoadStatusAddr ;load address of CodeStatus word call FlashRead ;read data at CodeStatus location Bank2 movf EEDATA,F ;set Z flag if data is zero Bank0 btfss STATUS,Z goto code_invalid call LoadStatusAddr ;load address of CodeStatus word movlw 0x3f ;load data to indicate no program movwf EEDATH movlw 0xff ;load data to indicate no program movwf EEDATA call FlashWrite ;write new CodeStatus word code_invalid: ;----------------------------------------------------------------------------- ;Get saved data one word at a time to program into flash movlw DataArray movwf FSR ;point to start of array movf NumWords,W movwf Counter ;set counter to half number of bytes ;----------------------------------------------------------------------------- ;Check if address is too high and conflicts with boot loader CheckAddress: movlw high StartOfBoot ;get high byte of address subwf AddressH,W btfss STATUS,C ;test if less than boot code address goto LoadAddress ;yes so continue with write btfss STATUS,Z ;test if equal to boot code address goto LineDone ;no so ignore the whole line movlw low StartOfBoot ;get low byte of address subwf AddressL,W btfsc STATUS,C ;test if less than boot code address goto LineDone ;no so ignore the rest of the line ;----------------------------------------------------------------------------- ;Load address and data and write data into flash LoadAddress: movf AddressH,W ;get high address Bank2 ;change from bank0 to bank2 movwf EEADRH ;load high address Bank0 ;change from bank2 to bank0 movf AddressL,W ;get low address Bank2 ;change from bank0 to bank2 movwf EEADR ;load low address LoadData: movf INDF,W ;get low byte from array movwf EEDATA ;load low byte incf FSR,F ;point to high data byte movf INDF,W ;get high byte from array movwf EEDATH ;load high byte incf FSR,F ;point to next low data byte call FlashWrite ;write data to program memory Bank0 ;change from bank3 to bank0 incfsz AddressL,F ;increment low address byte goto CheckLineDone ;check for rollover incf AddressH,F ;if so then increment high address byte CheckLineDone: decfsz Counter,F ;check if all words have been programmed goto CheckAddress ;if not then go program next word ;----------------------------------------------------------------------------- ;Done programming line of file LineDone: bsf PORTC,U_CTS_OUT_N ;set CTS off to stop data being received bsf PORTB,CTS_OUT_N ;set CTS off to stop data being received movlw '.' ;line has been programmed so call SerialTransmit ;transmit progress indicator back movlw 0x0d call SerialTransmit movlw 0x0a call SerialTransmit goto GetNewLine ;go get next line hex file ;----------------------------------------------------------------------------- ;Done programming file so send success indicator and trap execution until reset FileDone: call SerialReceive ; drain end of file record checksum call SerialReceive ; drain end of file record checksum movlw 'S' ;programming complete so call SerialTransmit ;transmit success indicator back movlw 0x0d call SerialTransmit movlw 0x0a call SerialTransmit call LoadStatusAddr ;load address of CodeStatus word clrf EEDATH ;load data to indicate program exists clrf EEDATA ;load data to indicate program exists call FlashWrite clrf PCLATH goto main_jump ; Restart ;----------------------------------------------------------------------------- ;Error in hex file so send failure indicator and trap error ErrorMessage: movlw 'F' ;error occurred so call SerialTransmit ;transmit failure indicator back TrapError3: goto TrapError3 ;trap error and wait for reset ;----------------------------------------------------------------------------- ;Load address of CodeStatus word into flash memory address registers ;This routine returns in bank2 LoadStatusAddr: Bank2 ;change from bank0 to bank2 movlw high CodeStatus ;load high addr of CodeStatus location movwf EEADRH movlw low CodeStatus ;load low addr of CodeStatus location movwf EEADR return ;----------------------------------------------------------------------------- ;Receive two ascii digits and convert into one hex byte ;This routine returns in bank0 GetHexByte: call SerialReceive ;get new byte from serial port addlw 0xbf ;add -'A' to Ascii high byte btfss STATUS,C ;check if positive addlw 0x07 ;if not, add 17 ('0' to '9') addlw 0x0a ;else add 10 ('A' to 'F') movwf HexByte ;save nibble swapf HexByte,F ;move nibble to high position call SerialReceive ;get new byte from serial port addlw 0xbf ;add -'A' to Ascii low byte btfss STATUS,C ;check if positive addlw 0x07 ;if not, add 17 ('0' to '9') addlw 0x0a ;else add 10 ('A' to 'F') iorwf HexByte,F ;add low nibble to high nibble movf HexByte,W ;put result in W reg addwf Checksum,F ;add to cumulative checksum return ;----------------------------------------------------------------------------- ;Wait for byte to be received in USART and return with byte in W ;This routine returns in bank0 SerialReceive: Bank0 ;change from unknown bank to bank0 bcf PORTC,U_CTS_OUT_N ;set CTS on for data to be received bcf PORTB,CTS_OUT_N ;set CTS on for data to be received btfss PIR1,RCIF ;check if data received goto $-1 ;wait until new data movf RCREG,W ;get received data into W bsf PORTC,U_CTS_OUT_N ;set CTS off to stop data being received bsf PORTB,CTS_OUT_N ;set CTS off to stop data being received CALL SerialTransmit return ;----------------------------------------------------------------------------- ;Transmit byte in W register from USART ;This routine returns in bank0 SerialTransmit: Bank0 ;change from unknown bank to bank0 btfsc PORTC, USB_N ; decide which RTS to check goto ST232 btfsc PORTC, U_RTS_IN_N ;check RTS to see if data can be sent goto $-1 goto STok ST232 btfsc PORTB, RTS_IN_N ;check RTS to see if data can be sent goto $-1 STok btfss PIR1,TXIF ;check that buffer is empty goto $-1 movwf TXREG ;transmit byte return ;----------------------------------------------------------------------------- ;Write to a location in the flash program memory ;Address in EEADRH and EEADR, data in EEDATH and EEDATA ;This routine returns in bank3 FlashWrite: Bank3 ;change from bank2 to bank3 movlw 0x84 ;enable writes to program flash movwf EECON1 movlw 0x55 ;do timed access writes movwf EECON2 movlw 0xaa movwf EECON2 bsf EECON1,WR ;begin writing to flash nop ;processor halts here while writing nop return ;----------------------------------------------------------------------------- ;Read from a location in the flash program memory ;Address is in EEADRH and EEADR, data returned in EEDATH and EEDATA ;Routine is only called once and can be placed in-line saving a call and return ;This routine returns in bank3 and is called when in bank2 FlashRead: movlw 0x1f ;keep address within range andwf EEADRH,F Bank3 ;change from bank2 to bank3 movlw 0x80 ;enable reads from program flash movwf EECON1 bsf EECON1,RD ;read from flash nop ;processor waits while reading nop return ;------------------------------------------------------------------------------- ; default EEPROM values ;------------------------------------------------------------------------------- org 0x2100 data 0 ; not MAGIC data 0 ; MODE_WORDL data 0 ; MODE_WORDH data I_LIMIT ; IMAX ; EEPROM Addresses EE_MAGIC equ 0 EE_MWL equ 1 EE_MWH equ 2 EE_IMAX equ 3 ;------------------------------------------------------------------------------- ;------------------------------------------------------------------------------- end