; ; tempboard.asm ; RS422 bus temperature sensor program for AT90S2313 ; .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 irnextbit =r4 ; this holds the "next" bit to be transmitted .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 txcount =r9 ; TX-left count .def pstate =r10 ; program state reg .def templow =r12 ; temp low counter .def temphigh =r13 ; temp high counter .def auxcnt =r14 ; auxillary counter ; pevent bits: ; 0 = packet received OK ; 1 = packet receive error ; 2 = packet receive timeout ; 3 = packet transmitted ; 4 = 1s timeout ; pstate bits: ; ... .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 .equ STOREDVAL =0xa8 ; where per-second info is stored .org 0 rjmp reset rjmp int0vec ; int 0 rjmp int1vec ; 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,0x01 ; PB0 output (calibration) out DDRB,r16 clr r16 out PORTB,r16 ; low (+ tri-state inputs) ldi r16,0x70 ; PD6,PD5 (422 LED,TX),PD4 (calibration) out DDRD,r16 sbi PORTD,6 ; LED off cbi PORTD,5 ; enable RS422 receive cbi PORTD,4 ; calibrator off cbi PORTB,0 ; calibrator 0v select ; 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 pstate clr templow clr temphigh clr auxcnt ser r16 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 ; PD2, PD3 are interrupt lines, set input with pullup cbi DDRD,2 sbi PORTD,2 cbi DDRD,3 sbi PORTD,3 ; set GIMSK/MCUCR up for INT1,INT0 falling edge, sleep-enable ldi r16,0x2a out MCUCR,r16 ldi r16,0xc0 ; INT1,INT0 enable out GIMSK,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 rcall uart_start_rx ; ensure receiver active sei ; enable interrupts rjmp main ; go! ;}}} ;------------------------------------------------------------------------------ ; INT0 vector -- triggered by V/F temperature circuit ;------------------------------------------------------------------------------ int0vec: ;{{{ int0vec (interrupt): in tmpreg,SREG inc templow brne i0v_l0 inc temphigh i0v_l0: out SREG,tmpreg reti ;}}} ;------------------------------------------------------------------------------ ; INT1 vector -- triggered by auxillary circuit ;------------------------------------------------------------------------------ int1vec: ;{{{ int1vec (interrupt): in tmpreg,SREG inc auxcnt out SREG,tmpreg reti ;}}} ;------------------------------------------------------------------------------ ; timer/counter 1 overflow interrupt -- 1 second counter ;------------------------------------------------------------------------------ tim1_ovr: ;{{{ tim1_ovr (interrupt): in tmpreg,SREG push r16 ; set bit 4 in pevent reg mov r16,pevent ori r16,0x10 mov pevent,r16 ; reset 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, clj/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 ; skip if not 1s timeout 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 ; 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 ;}}} second_to: ;{{{ second_to (jump): cli mov r16,pevent andi r16,0xef ; mask off bit 4 mov pevent,r16 sei ; copy and reset counters ldi Ylow,STOREDVAL mov r16,templow clr templow mov r17,temphigh clr temphigh st Y+,r17 st Y+,r16 mov r16,auxcnt clr auxcnt st Y,r16 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 tst r16 brmi l_dd_l9 ; branch if minus (not nearly enough data) breq l_dd_l9 ; not enough data! ld r16,Y+ cpi r16,0x00 ; set normal operation brne l_dd_l1 rjmp l_dsd_setnorm l_dd_l1: cpi r16,0x01 ; set high calibration value brne l_dd_l2 rjmp l_dsd_setchigh l_dd_l2: cpi r16,0x02 ; set low calibration value brne l_dd_l3 rjmp l_dsd_setclow l_dd_l3: cpi r16,0x03 ; read values brne l_dd_l4 rjmp l_dsd_readvals l_dd_l4: cpi r16,0x04 ; set normal and acknowledge brne l_dd_l5 rjmp l_dsd_setacknorm l_dd_l5: cpi r16,0x05 ; set high calibration value and acknowledge brne l_dd_l6 rjmp l_dsd_setackchigh l_dd_l6: cpi r16,0x06 ; set low calibration value and acknowledge brne l_dd_l7 rjmp l_dsd_setackclow l_dd_l7: l_dd_l9: ; unknown command, ignore rjmp l_dsd9 ;{{{ l_dsd_setnorm (jump): l_dsd_setnorm: cbi PORTD,4 ; calibrator off cbi PORTB,0 ; calibrator 0v select rjmp l_dsd9 ;}}} ;{{{ l_dsd_setchigh (jump): l_dsd_setchigh: sbi PORTD,4 ; calibrator on sbi PORTB,0 ; calibrator 0.5v select rjmp l_dsd9 ;}}} ;{{{ l_dsd_setclow (jump): l_dsd_setclow: sbi PORTD,4 ; calibrator on cbi PORTB,0 ; calibrator 0v select rjmp l_dsd9 ;}}} ;{{{ l_dsd_readvals (jump): l_dsd_readvals: rcall do_pause ldi Ylow,STOREDVAL ld r16,Y+ ; temp high val ld r17,Y+ ; temp low val ld r18,Y ; aux counter ldi Ylow,RAMSTART ldi r19,8 ; eventual packet size st Y+,r19 clr r19 ; target LUN st Y+,r19 mov r19,lunreg ; our LUN st Y+,r19 ldi r19,0x03 ; command byte st Y+,r19 st Y+,r16 ; temp high st Y+,r17 ; temp low st Y+,r18 ; aux ldi r16,8 mov txcount,r16 rcall uart_start_tx rjmp main ;}}} ;{{{ l_dsd_setacknorm (jump): l_dsd_setacknorm: cbi PORTD,4 ; calibrator off cbi PORTB,0 ; calibrator 0v select rcall do_pause ldi Ylow,RAMSTART ldi r16,5 ; eventual packet size st Y+,r16 clr r16 ; target LUN st Y+,r16 mov r16,lunreg ; our LUN st Y+,r16 ldi r16,0x04 ; command byte st Y+,r16 ldi r16,5 mov txcount,r16 rcall uart_start_tx rjmp main ;}}} ;{{{ l_dsd_setackchigh (jump): l_dsd_setackchigh: sbi PORTD,4 ; calibrator on sbi PORTB,0 ; calibrator 0.5v select rcall do_pause ldi Ylow,RAMSTART ldi r16,5 ; eventual packet size st Y+,r16 clr r16 ; target LUN st Y+,r16 mov r16,lunreg ; our LUN st Y+,r16 ldi r16,0x05 ; command byte st Y+,r16 ldi r16,5 mov txcount,r16 rcall uart_start_tx rjmp main ;}}} ;{{{ l_dsd_setackclow (jump): l_dsd_setackclow: sbi PORTD,4 ; calibrator on cbi PORTB,0 ; calibrator 0v select rcall do_pause ldi Ylow,RAMSTART ldi r16,5 ; eventual packet size st Y+,r16 clr r16 ; target LUN st Y+,r16 mov r16,lunreg ; our LUN st Y+,r16 ldi r16,0x06 ; command byte st Y+,r16 ldi r16,5 mov txcount,r16 rcall uart_start_tx rjmp main ;}}} 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 ;}}}