;********************************************************************** ; Filename: lmod.asm * ; Date: 2004-02-01 * ; File Version: 1.0 * ; * ; Author: Per Zander * ;********************************************************************** ; Files required: p16c505.inc * ;********************************************************************** ; Description: ; ; General: ; ; This is a program for model railroad lamp control via a 9600 baud ; serial link. ; ; Serial protocol: ; ; 9600 baud, 8 bits, no parity, no handshaking. Data is inverted, so that ; an rs232 signal can be used directly (after appropriate clamping). ; ; Lamp control packet: ; ; A lamp control packet consists of two bytes. ; First byte: ; ; 7 6 5 4 3 2 1 0 ; _______________________________ ; | 0 | Device address | ; |___|___________________________| ; ; Second byte: ; ; 7 6 5 4 3 2 1 0 ; _______________________________ ; | 1 | 0 | c | 0 | 0 |Port number| ; |___|___|___|___|___|___________| ; ; A correct packet addressed to the device, and with the c bit set, activates ; the selected port. If the c bit is cleared, the selected port is ; deactivated. ; ; Intensity configuration packet: ; ; An intensity configuration packet consists of three bytes. ; First byte: ; ; 7 6 5 4 3 2 1 0 ; _______________________________ ; | 0 | Device address | ; |___|___________________________| ; ; Second byte: ; ; 7 6 5 4 3 2 1 0 ; _______________________________ ; | 1 | 1 | X | 0 | 0 |Port number| ; |___|___|___|___|___|___________| ; ; Third byte: ; ; 7 6 5 4 3 2 1 0 ; _______________________________ ; | 1 | Intensity | ; |___|___________________________| ; ; The intensity can be set in steps of 1/64 of max intensity. A value of 64 ; or higher will give full intensity, which is the default setting. ; ;********************************************************************** ;*---------- Device address definition. ----------* my_addr EQU 0x00 ; Change for each unit. MSB should be 0. hi_addr EQU 0x80 ; Should be 0x80. ;*---------- Device select. ----------* list p=16c505 ; List directive to define processor #include ; Processor specific variable definitions ;*---------- Configuration specification. ----------* __CONFIG _CP_OFF & _WDT_OFF & _MCLRE_OFF & _XT_OSC ;*---------- Port definitions. ----------* din EQU 0x05 ; Data in on RC5 sw0l EQU 0x00 ; Contact X0 left on RB0 sw0r EQU 0x01 ; Contact X0 right on RB1 sw1l EQU 0x02 ; Contact X1 left on RB2 sw1r EQU 0x00 ; Contact X1 right on RC0 sw2l EQU 0x01 ; Contact X2 left on RC1 sw2r EQU 0x02 ; Contact X2 right on RC2 sw3l EQU 0x04 ; Contact X3 left on RC4 sw3r EQU 0x03 ; Contact X3 right on RC3 ;*---------- Variable definitions. ----------* ; For receiver: ser_st EQU 0x08 ; Serial state pointer. rec_d EQU 0x09 ; Receive data shift register. new_d EQU 0x0A ; Received_data. ; For command handler: cmd_st EQU 0x0B ; For lamp output: sig_msk EQU 0x0C ; Mask for outputs. wrap_c EQU 0x0D ; Counter 1...64->1 sw0l_t EQU 0x10 ; Timing values sw0r_t EQU 0x11 sw1l_t EQU 0x12 sw1r_t EQU 0x13 sw2l_t EQU 0x14 sw2r_t EQU 0x15 sw3l_t EQU 0x16 sw3r_t EQU 0x17 sw0l_c EQU 0x18 ; Counter values. sw0r_c EQU 0x19 sw1l_c EQU 0x1A sw1r_c EQU 0x1B sw2l_c EQU 0x1C sw2r_c EQU 0x1D sw3l_c EQU 0x1E sw3r_c EQU 0x1F ; General: cmd_fl EQU 0x0F ;*---------- Program flag definitions. ----------* dav EQU 0x00 ; Data available flag from receiver. dend EQU 0x01 ; Serial receiver end of byte flag. ;********************************************************************** ;* The program starts here after reset. ;********************************************************************** ORG 0x3FF ; Processor reset vector. movlw 0x38 ; Osc. cal. value. ORG 0x000 ; Coding begins here. startofprog movwf OSCCAL ; Update register with factory cal value. clrf FSR ; Ensure FSR register points to Bank0. goto init_code ; Jump over a few words to enable ; reprogramming. ORG 0x006 ;***************************************************** ;*---------- Serial receive state machine. ----------* ;***************************************************** ;*---------- Jump to current state. ----------* serial_proc movf ser_st, W ; t2 movwf PCL ; t3, t4 ;*---------- Idle state. Must see stop bit to leave it. ----------* ser_idle btfss PORTC, din ; t5 movlw wait_start movwf ser_st nop retlw 0 ;t9, t10 ;*---------- Wait for start bit. ----------* wait_start btfsc PORTC, din movlw sample_data movwf ser_st bcf cmd_fl, dend ; Clear data end flag. retlw 0 ;*---------- Sample data in. ----------* sample_data btfsc PORTC, din bcf rec_d, 7 ; Clear data bit if input is logic 0. movlw get_end_bit movwf ser_st retlw 0 ;*---------- Wait for next bit. ----------* get_end_bit btfss rec_d, 0 bsf cmd_fl, dend movlw shift_data movwf ser_st retlw 0 ;*---------- Shift data in. ----------* shift_data rrf rec_d, F ; Shift received data. bsf rec_d, 7 ; Preset current bit to 7. movlw test_bitnr movwf ser_st retlw 0 ;*---------- Check if it is the last bit. ----------* test_bitnr movlw sample_data btfsc cmd_fl, dend movlw sample_last ; Last bit if start bit is in rec_bit. movwf ser_st retlw 0 ;*---------- Sample last bit. ----------* sample_last btfsc PORTC, din bcf rec_d, 7 ; Clear data bit if input is 0. movlw wait_end ser_return movwf ser_st retlw 0 ;*---------- Wait before sampling stop bit. ----------* wait_end movlw set_new_data goto ser_return ;*---------- Move new data. ----------* set_new_data movf rec_d, W movwf new_d movlw preset_rec movwf ser_st retlw 0 ;*---------- Set receive data to all 1's. ----------* preset_rec movlw 0xFF movwf rec_d movlw sample_stop movwf ser_st retlw 0 ;*---------- Sample stop bit. ----------* sample_stop btfss PORTC, din bsf cmd_fl, dav ; Set dav if correct stop bit. movlw ser_idle movwf ser_st retlw 0 ;****************************************************** ;*---------- Command handler state machine. ----------* ;****************************************************** ;*---------- Idle state, wait for dav. ----------* idle_w_dav movlw my_addr ; t16 xorwf new_d, W ; Check for my address. movlw wait_dav ; Default next state btfsc cmd_fl, dav btfss STATUS, Z movlw idle_w_dav ; Stay if no dav or address mismatch. movwf cmd_st bcf cmd_fl, dav goto main_loop ; t24, t25 ;*---------- Address match found, wait for new dav. ----------* wait_dav movlw my_addr xorwf new_d, W movlw check_hi_addr btfsc cmd_fl, dav btfsc STATUS, Z movlw wait_dav ; Stay here if no dav or address match. movwf cmd_st bcf cmd_fl, dav goto main_loop ;*---------- Check high address bits. ----------* check_hi_addr movlw hi_addr xorwf new_d, W andlw 0x98 ; Mask out relevant bits, and set Z flag. movlw decode_cmd btfss STATUS, Z movlw idle_w_dav ; Go to idle if there is hi addr mismatch. movwf cmd_st nop goto main_loop ;*---------- Decode command. ----------* decode_cmd movlw perform_cmd btfsc new_d, 6 movlw get_param movwf cmd_st movf new_d, W andlw 7 iorlw 0x10 movwf FSR ; Set FSR to the selected counter. goto main_loop ;*---------- Get a parameter value. ----------* get_param btfsc cmd_fl, dav movlw idle_w_dav movwf cmd_st ; Go to idle when dav occurs. movf new_d, W btfsc cmd_fl, dav btfss new_d, 7 movf INDF, W ; Keep old value if new addr. or no dav. movwf INDF ; Update parameter. goto main_loop ;*---------- Perform the command. ----------* perform_cmd movlw idle_w_dav movwf cmd_st movf INDF, W andlw 0x7f ; Mask off highest bit bsf FSR, 3 ; Redirect FSR to counters. movwf INDF btfss new_d, 5 clrf INDF goto main_loop ORG 0x100 init_code movlw 0xc7 ; Internal timer clock, max prescaler. option clrf PORTB ; Clear output ports. clrf PORTC movlw 0x38 ; Set output ports to out. tris PORTB movlw 0x20 tris PORTC movlw ser_idle movwf ser_st ; Initial state of serial receiver. movlw idle_w_dav movwf cmd_st ; Initial state of command handler. movlw 0x1 movwf wrap_c ; Initialize intensity counter to 1. movlw 0x40 ; Default full strength on all outputs. movwf sw0l_t movwf sw0r_t movwf sw1l_t movwf sw1r_t movwf sw2l_t movwf sw2r_t movwf sw3l_t movwf sw3r_t clrf sw0l_c ; Clear all pulse counters. clrf sw0r_c clrf sw1l_c clrf sw1r_c clrf sw2l_c clrf sw2r_c clrf sw3l_c clrf sw3r_c clrf cmd_fl ; Clear dav and dend flags clrf sig_msk ; Clear the mask for output values. movlw 0xFF movwf rec_d ; Set receiver shift register to all 1's. ;****************************************** ;*---------- Main program loop. ----------* ;****************************************** main_loop call serial_proc decf wrap_c, F ; t11 btfsc STATUS, Z bsf wrap_c, 6 ; If (wrap_c == 0) wrap_c = 64; movf wrap_c, W subwf sw3l_c, W ; t15 rlf sig_msk, F ; Set bit in sig_msk if port count >= wrap_c movf wrap_c, W subwf sw3r_c, W rlf sig_msk, F movf wrap_c, W subwf sw2r_c, W rlf sig_msk, F ; t22 movf wrap_c, W subwf sw2l_c, W rlf sig_msk, F ; t25 call serial_proc movf wrap_c, W ; t11 subwf sw1r_c, W rlf sig_msk, W movwf PORTC movf wrap_c, W subwf sw1l_c, W rlf sig_msk, F movf wrap_c, W subwf sw0r_c, W rlf sig_msk, F movf wrap_c, W subwf sw0l_c, W rlf sig_msk, W ; t23 andlw 0x07 movwf PORTB ;t25 call serial_proc nop ; t11 nop ;*---------- Jump to current command handler state. ----------* command_proc movf cmd_st, W ; t13 movwf PCL ; t14, t15 ;*----- The command procedure ends with a jump to main_loop. -----* endofprog END ; directive 'end of program'