; ; uarttest2.asm -- program for RS422 `test device' (AT90S2313) ; Copyright (C) 2004 Fred Barnes ; ; This program is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 2 of the License, or ; (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to the Free Software ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ; .include "at90s2313.inc" .equ BAUDRATE =25 ; 4 MHz: (2400 = 103, 4800 = 51, 9600 = 25, 19200 = 12) .def Xlow =r26 .def Xhigh =r27 .def Ylow =r28 .def Yhigh =r29 .def Zlow =r30 .def Zhigh =r31 .def rxbp =Xlow ; RX (next) address .def tmpreg =r2 ; where SREG gets saved during interrupts .def lunreg =r3 ; where the LUN (logical unit number) is stored .def txcsum =r5 ; TX outgoing checksum .def rxcsum =r6 ; RX incoming checksum .def pevent =r7 ; program event reg .def rxcount =r8 ; RX'd count .def pcount =r9 ; silly counter .def txcount =r10 ; TX-left count .def pstate =r11 ; program state reg ; pevent bits: ; 0 = packet received OK ; 1 = packet receive error ; 2 = packet receive timeout ; 3 = packet transmitted ; 4 = 1 second timeout ; pstate bits: ; 0 = messages enabled ; 1 = auto-discover enabled .equ STACKSTART =0xdf .equ RAMSTART =0x60 .equ RAMSTART_P1 =0x61 ; this is where the LUN typically is .equ DEVID =0xa0 ; packets will not over-run into this .org 0 rjmp reset nop ; int 0 nop ; int 1 nop ; timer/counter 1 capture nop ; timer/counter 1 compare rjmp tim1_ovr ; timer/counter 1 overflow rjmp tim0_ovr ; timer/counter 0 overflow rjmp uart_rxint ; UART RX interrupt rjmp uart_dreint ; UART data-register-empty interrupt rjmp uart_txint ; UART TX interrupt nop ; analogue comparator interrupt ;------------------------------------------------------------------------------ ; RESET ;------------------------------------------------------------------------------ reset: ;{{{ reset (entry-point): cli ; disable interrupts for setup ldi r16,0xff ; PB0 -> PB7 output LEDs out DDRB,r16 ldi r16,0x55 ; christmas tree out PORTB,r16 ;//ldi r16,0x7c ; PD6,PD5,PD4,PD3,PD2 output LED, rest input (atm..) ldi r16,0x60 ; PD6,PD5 output LED, rest input (atm..) out DDRD,r16 sbi PORTD,6 ; LED off ;//sbi PORTD,2 ; LED off ;//sbi PORTD,3 ; LED off ;//sbi PORTD,4 ; LED off cbi PORTD,5 ; enable RS422 receive ; initialise stack pointer.. :) ldi r16,STACKSTART out SPL,r16 ; local init clr rxcsum clr txcsum clr rxcount clr txcount clr Xhigh ldi r16,RAMSTART ; SRAM start (0x60 -> 0xDF) mov rxbp,r16 clr pevent clr pcount ldi r16,0x00 ; disable auto-discover mov pstate,r16 ldi r16,0xff mov lunreg,r16 ; set LUN register ; read "MAC" address out of EEPROM clr r16 clr Yhigh ldi Ylow,DEVID ldi r17,0x04 ; ID is 4 bytes rcall read_eeprom_buffer ; initialise timer/counter 1 (16-bit), 1 second counter clr r16 out TCCR1A,r16 ; disconnect from OC1, no PWM ldi r16,0x00 ; timer/counter 1 stopped out TCCR1B,r16 ; prescale, etc. ; timeout = 0xbdc at clk/64 and 4 MHz (counts up to 1s) ldi r16,0x0b out TCNT1H,r16 ldi r16,0xdc out TCNT1L,r16 ldi r16,0x03 ; timer/counter 1 prescale clk/64 out TCCR1B,r16 ; prescale, etc. ; initialise timer/counter 0 (8-bit), 50ms UART receive timeout clr r16 out TCCR0,r16 ; timer stopped ; enable timers ldi r16,0x82 ; TOIE1, TOIE0 (overflow int enable) out TIMSK,r16 ; set MCUCR up for INT0 rising edge, sleep-enable ldi r16,0x23 out MCUCR,r16 ; initialise the UART ldi r16,BAUDRATE out UBRR,r16 ldi r16,0xd0 ; 2/3 UART interrupts (not UDRE) and RX enable out UCR,r16 sei ; enable interrupts rjmp main ; go! ;}}} ;------------------------------------------------------------------------------ ; timer/counter 1 overflow interrupt ;------------------------------------------------------------------------------ tim1_ovr: ;{{{ tim1_ovr (interrupt): in tmpreg,SREG push r16 ; set bit 4 in event reg mov r16,pevent ori r16,0x10 mov pevent,r16 ; reset the counter clr r16 out TCCR1B,r16 ; disable timer ldi r16,0x0b out TCNT1H,r16 ldi r16,0xdc out TCNT1L,r16 ldi r16,0x03 ; enable timer, clk/64 out TCCR1B,r16 pop r16 out SREG,tmpreg reti ;}}} ;------------------------------------------------------------------------------ ; timer/counter 0 overflow interrupt ;------------------------------------------------------------------------------ tim0_ovr: ;{{{ tim0_ovr (interrupt); in tmpreg,SREG push r16 mov r16,pevent ori r16,0x04 mov pevent,r16 clr r16 out TCCR0,r16 ; disable timer/counter 0 pop r16 out SREG,tmpreg reti ;}}} ;------------------------------------------------------------------------------ ; UART character received ;------------------------------------------------------------------------------ uart_rxint: ;{{{ uart_rxint (interrupt): in tmpreg,SREG push r16 push Yhigh push Ylow cbi PORTD,6 ; set "busy" LED ; read byte from UDR and store in buffer at X in r16,UDR st X,r16 inc rxcount ; give up if framing error or overrun in r16,USR andi r16,0x18 brne l_urxi2 ; if either FE or OR is set, jump ; if rxcount == 1 (ie, this was the 1st, start 50ms timer) mov r16,rxcount cpi r16,0x01 brne l_urxi0 ; start timer ldi r16,0x3c ; ~50ms at clk/1024, 4 MHz out TCNT0,r16 ldi r16,0x05 out TCCR0,r16 ; enable (clk/1024) l_urxi0: ; if rxcount > 64, something's gone wrong..! ldi r16,0x40 cp r16,rxcount brlt l_urxi2 ; if 0x40 < rxcount, jump ; if rxcount == byte at RAMSTART (expected count), flag and stop timer clr Yhigh ldi Ylow,RAMSTART ld r16,Y cp r16,rxcount brne l_urxi1 ; the last byte read should be the checksum and match rxcsum ld r16,X cp rxcsum,r16 brne l_urxi2 ; bad checksum.. ; set flag mov r16,pevent sbr r16,0x01 mov pevent,r16 ; disable timer/counter 0 clr r16 out TCCR0,r16 ; right, disable UART receiver cbi UCR,4 ; RX disable rjmp l_urxi3 ; don't update checksum with checksum (!) l_urxi1: ld r16,X ; load byte just read add rxcsum,r16 ; update checksum l_urxi3: ; increment X inc rxbp ; restore and return pop Ylow pop Yhigh pop r16 out SREG,tmpreg reti l_urxi2: ; badness happened -- set another flag mov r16,pevent sbr r16,0x02 mov pevent,r16 ; disable UART receiver cbi UCR,4 ; RX disable ; restore and return pop Ylow pop Yhigh pop r16 out SREG,tmpreg reti ;}}} ;------------------------------------------------------------------------------ ; UART data-register (tx) empty ;------------------------------------------------------------------------------ uart_dreint: ;{{{ uart_dreint (interrupt): in tmpreg,SREG push r16 tst txcount breq l_udei2 ; nothing left to transmit dec txcount ; last byte is always a checksum tst txcount breq l_udei1 ; jump if only checksum left to transmit ld r16,X+ ; load byte from buffer add txcsum,r16 ; add byte to TX checksum out UDR,r16 ; write to UART data-register rjmp l_udei9 l_udei1: ; transmit the checksum out UDR,txcsum rjmp l_udei9 l_udei2: cbi UCR,5 ; disable this interrupt l_udei9: pop r16 out SREG,tmpreg reti ;}}} ;------------------------------------------------------------------------------ ; UART transmitted interrupt (after shift-out and empty UDR) ;------------------------------------------------------------------------------ uart_txint: ;{{{ uart_txint (interrupt): in tmpreg,SREG push r16 ; set bit in event register mov r16,pevent ori r16,0x08 ; bit 3 mov pevent,r16 ; disable UART transmitter cbi UCR,3 ; TX disable pop r16 out SREG,tmpreg reti ;}}} ;------------------------------------------------------------------------------ ; reads a byte out of eeprom memory. byte specified in r16, returned in r16 ;------------------------------------------------------------------------------ read_eeprom_byte: ;{{{ read_eeprom_byte (call): andi r16,0x7f ; AT90S2313 has 128 bytes of EEPROM out EEAR,r16 sbi EECR,0 ; read enable -- halts CPU for 2 cycles nop in r16,EEDR ret ;}}} ;------------------------------------------------------------------------------ ; reads r17 bytes of eeprom starting at r16 into the buffer pointed at by Y ;------------------------------------------------------------------------------ read_eeprom_buffer: ;{{{ read_eeprom_buffer (call) push r18 push Yhigh push Ylow mov r18,r16 l_reeb0: tst r17 breq l_reeb9 mov r16,r18 rcall read_eeprom_byte st Y+,r16 ; store in buffer inc r18 dec r17 rjmp l_reeb0 l_reeb9: pop Ylow pop Yhigh pop r18 ret ;}}} ;------------------------------------------------------------------------------ ; start data transmission ;------------------------------------------------------------------------------ uart_start_tx: ;{{{ uart_start_tx (call): push r16 ; first, reset X register clr Xhigh ldi r16,RAMSTART ; SRAM start (0x60 -> 0xDF) mov rxbp,r16 ; start transmitter sbi PORTD,5 ; enable RS422 transmit sbi UCR,3 ; TX enable ld r16,X+ ; load a byte mov txcsum,r16 ; start calculating checksum dec txcount out UDR,r16 ; start transmission sbi UCR,5 ; UDRIE enable cbi PORTD,6 ; set "busy" LED pop r16 ret ;}}} ;------------------------------------------------------------------------------ ; start data reception (disables transmitters, enables receiver) ;------------------------------------------------------------------------------ uart_start_rx: ;{{{ uart_start_rx (call): sbi PORTD,6 ; clear "busy" LED ; first, reset X register clr Xhigh ldi r16,RAMSTART ; SRAM start (0x60 -> 0xDF) mov rxbp,r16 clr rxcsum clr rxcount cbi UCR,3 ; TX disable sbi UCR,4 ; RX enable cbi PORTD,5 ; enable RS422 receive ret ;}}} ;------------------------------------------------------------------------------ ; stops data reception (disables receiver) ;------------------------------------------------------------------------------ uart_stop_rx: ;{{{ uart_stop_rx (call): cbi UCR,4 ; RX disable clr rxcount ret ;}}} ;------------------------------------------------------------------------------ ; pauses execution briefly (around 10ms) ;------------------------------------------------------------------------------ do_pause: ;{{{ do_pause (call): push r18 push r19 ; delay (roughly 10ms) ldi r18,0x30 l_dpse1: ldi r19,0xff l_dpse2: nop nop dec r19 brne l_dpse2 dec r18 brne l_dpse1 ; restore and return pop r19 pop r18 ret ;}}} ;------------------------------------------------------------------------------ ; pauses execution briefly (around 20ms), with moderate variation based ; on the device-ID (located in SRAM at DEVID for 4 bytes) ;------------------------------------------------------------------------------ do_pause_id: ;{{{ do_pause_id (call): push r18 push r19 push r20 push r21 push Ylow ldi Ylow,DEVID ld r18,Y+ ld r18,Y+ ; ignore first ID bit ld r19,Y+ ld r20,Y rol r20 rol r20 rol r20 rol r20 add r18,r20 eor r18,r19 ; r18 holds result ; begin delay loops mov r19,r18 l_dpseid1: ldi r20,200 l_dpseid2: ldi r21,170 l_dpseid3: nop nop dec r21 brne l_dpseid3 dec r20 brne l_dpseid2 dec r19 brne l_dpseid1 ; restore and return pop Ylow pop r21 pop r20 pop r19 pop r18 ret ;}}} ;------------------------------------------------------------------------------ ; main code starts here ;------------------------------------------------------------------------------ main: ;{{{ main (jump): tst pevent brne main_skip sleep rjmp main main_skip: ; something interesting happened ; pevent is in r17 ; RX events (x3) sbrc pevent,0 ; skip if not good data rjmp good_data sbrc pevent,1 ; skip if not error rjmp data_error sbrc pevent,2 ; skip if not timeout rjmp data_timeout ; TX event sbrc pevent,3 ; skip if not TX complete rjmp data_txd ; 1s timeout event sbrc pevent,4 rjmp second_to ; shouldn't really get here ... clr pevent rjmp main ; loop ;}}} good_data: ;{{{ good_data (jump): ; rxcount bytes ready in buffer, receiver is disabled ; clear local flag cli mov r16,pevent andi r16,0xfe ; mask off bit 0 mov pevent,r16 sei ; check LUN target first clr Yhigh ldi Ylow,RAMSTART_P1 ld r16,Y+ ; load packet LUN, Y++ cpi r16,0xff breq generic_data ; handle generic data cp r16,lunreg ; check against own LUN brne l_gddt1 rjmp device_data ; handle device-specific data l_gddt1: ; not-interesting, re-enable receiver and loop rcall uart_start_rx rjmp main ;}}} generic_data: ;{{{ generic_data (jump) ; got a broadcast packet, data start (cmd) is at Y ld r16,Y+ ; load command mov r17,r16 andi r17,0x80 ; mask in high-bit brne l_gdd2 ; jump to high-command tests cpi r16,0x00 ; DISCOVER (no data) breq generic_discover cpi r16,0x01 ; SOFT-RESET (no data) breq generic_soft_reset rjmp l_gdd9 l_gdd2: ; any high command has 4 device-ID bytes next. check these with stored ID ; as a pre-requisite, rxcount must be >= 7 ldi r17,6 cp rxcount,r17 brlt l_gdd9 ; not enough packet, get out.. ldi r17,4 ldi r18,DEVID l_gdd3: ld r19,Y+ ; load packet ID byte into r19 push Ylow mov Ylow,r18 ld r20,Y+ ; load stored packet ID byte into r20 mov r18,Ylow pop Ylow cp r19,r20 brne l_gdd9 ; wasn't for us dec r17 brne l_gdd3 ; loop for more ID bytes ; ID matches, Y points at the first byte after ; the ID, r16 contains command cpi r16,0x80 ; ASSIGN (data contains address and LUN) breq generic_assign cpi r16,0x81 ; DEVICE-PING (arbitrary data) breq generic_device_ping cpi r16,0x82 ; DEVICE-ID (no data) breq generic_device_id l_gdd9: ; hmm, well, unknown -- just ignore it ; re-enable receiver and loop rcall uart_start_rx rjmp main ;}}} generic_discover: ;{{{ generic_discover (jump): rcall do_pause_id ; packet length should be just 4 bytes ldi r16,4 cp rxcount,r16 brne l_gd9 ; bad length ; do we have a device ID yet ? ldi r17,0xff cp lunreg,r17 brne l_gd9 ; yup, we already do ; we're LUN-less, send a response ldi Ylow,RAMSTART ldi r16,24 ; eventual packet size st Y+,r16 clr r16 ; LUN st Y+,r16 st Y+,r16 ; command byte (DISCOVER) ; read device-ID code and device-ID string out of the ; EEPROM, 20 bytes worth (4 dev-ID, 14 str, 2 version) ldi r16,0 ; starting offset in EEPROM ldi r17,20 ; count rcall read_eeprom_buffer ldi r16,24 mov txcount,r16 ; start transmit rcall uart_start_tx rjmp main l_gd9: rcall uart_start_rx rjmp main ;}}} generic_soft_reset: ;{{{ generic_soft_reset (jump): ; disable UART transmitter and receiver cbi UCR,3 ; TX disable cbi UCR,4 ; RX disable cbi PORTD,6 ; turn on "busy" LED cli ; disable interrupts ; set watchdog to time-out after ~1s ldi r16,0x0e out WDTCR,r16 ; enable watchdog l_gsr1: sleep rjmp l_gsr1 ; loop until reset ;}}} generic_assign: ;{{{ generic_assign (jump): rcall do_pause ; ensure packet length is 9 bytes ldi r16,9 cp rxcount,r16 brne l_gass9 ; bad packet length ; byte at Y should be our new LUN ld r16,Y+ mov lunreg,r16 ; stop auto-discover if enabled mov r16,pstate andi r16,0xfd ; mask off bit 1 mov pstate,r16 ; echo the packet back, after changing target LUN ldi Ylow,RAMSTART_P1 clr r16 st Y,r16 mov txcount,rxcount rcall uart_start_tx rjmp main l_gass9: ; re-start receiver and go idle rcall uart_start_rx rjmp main ;}}} generic_device_ping: ;{{{ generic_device_ping (jump): rcall do_pause ; we pretty much echo back the packet as-is, but ; change the LUN value first ldi Ylow,RAMSTART_P1 clr r16 st Y,r16 ; store new LUN mov txcount,rxcount ; setup TX count rcall uart_start_tx ; start transmission rjmp main ;}}} generic_device_id: ;{{{ generic_device_id (jump) rcall do_pause ; modify packet LUN ldi Ylow,RAMSTART ldi r16,24 ; eventual packet size st Y+,r16 clr r16 ; LUN st Y+,r16 ldi r17,5 add Ylow,r17 ; advance Y past cmd and device ID ; read device ID string out of the EEPROM, 16 bytes worth (14 str, 2 version) ldi r16,4 ; starting offset in EEPROM ldi r17,16 ; count rcall read_eeprom_buffer ldi r16,24 mov txcount,r16 rcall uart_start_tx rjmp main ;}}} data_error: ;{{{ data_error (jump): ; receiver data error cli mov r16,pevent andi r16,0xfd ; mask off bit 1 mov pevent,r16 sei ; reenable receiver rcall uart_start_rx rjmp main ;}}} data_timeout: ;{{{ data_timeout (jump): ; receiver timerout cli mov r16,pevent andi r16,0xfb ; mask off bit 2 mov pevent,r16 sei ; reenable receiver rcall uart_start_rx rjmp main ;}}} device_data: ;{{{ device_data (jump): ; device-specific data (for this LUN), Y points at data start ldi r16,3 sub rxcount,r16 ; rxcount now number of specific data-bytes breq l_dsd9 ; not enough data! ld r16,Y+ cpi r16,0x00 ; reset 1s counter breq l_dsd_rset cpi r16,0x01 ; set 1s counter to xx breq l_dsd_set cpi r16,0x02 ; enable messages breq l_dsd_enbm cpi r16,0x03 ; disable messages breq l_dsd_dism ; unknown command, ignore rjmp l_dsd9 l_dsd_rset: ; reset counter clr pcount rjmp l_dsd9 l_dsd_set: ; set counter dec rxcount breq l_dsd9 ; skip if not enough data ld r16,Y+ mov pcount,r16 rjmp l_dsd9 l_dsd_enbm: ; enable messages mov r16,pstate ori r16,0x01 mov pstate,r16 rjmp l_dsd9 l_dsd_dism: ; disable messages mov r16,pstate andi r16,0xfe mov pstate,r16 l_dsd9: ; re-enable receiver and loop rcall uart_start_rx rjmp main ;}}} data_txd: ;{{{ data_txd (jump) ; data transmitted OK cli mov r16,pevent andi r16,0xf7 ; mask off bit 3 mov pevent,r16 sei ; re-enable receiver rcall uart_start_rx rjmp main ;}}} second_to: ;{{{ second_to (jump): ; one-second timeout cli mov r16,pevent andi r16,0xef ; mask off bit 4 mov pevent,r16 sei ; if rxcount = 0, and auto-discover on, transmit DISCOVER (response) packet tst rxcount brne l_st9 ; receiving already mov r16,pstate andi r16,0x02 breq l_st5 ; auto-discover off ; disable the receiver rcall uart_stop_rx ; fill up the buffer and transmit ldi Ylow,RAMSTART ldi r16,24 ; eventual packet size st Y+,r16 clr r16 ; LUN st Y+,r16 st Y+,r16 ; command byte (DISCOVER) ; read device-ID code and device-ID string out of the ; EEPROM, 20 bytes worth (4 dev-ID, 14 str, 2 version) ldi r16,0 ; starting offset in EEPROM ldi r17,20 ; count rcall read_eeprom_buffer ldi r16,24 mov txcount,r16 ; start transmit rcall uart_start_tx rjmp l_st9 l_st5: ; if rxcount = 0, and messages on, transmit debug message packet tst rxcount brne l_st9 mov r16,pstate andi r16,0x01 breq l_st9 ; messages off ; disable the receiver rcall uart_stop_rx ; fill up the buffer and transmit ldi Ylow,RAMSTART ldi r16,51 ; eventual packet size st Y+,r16 clr r16 ; LUN st Y+,r16 ldi r16,0xff ; command byte (ASYNC-MESSAGE) st Y+,r16 ; read message from EEPROM, 48 bytes worth ldi r16,0x20 ; starting offset in EEPROM ldi r17,0x30 ; 48 bytes of it rcall read_eeprom_buffer ldi r16,51 ; packet size mov txcount,r16 ; start transmit rcall uart_start_tx l_st9: inc pcount mov r16,pcount com r16 out PORTB,r16 rjmp main ;}}}