; ; parprog.asm -- experimental parallel programmer for AT90S{1200,2313}'s ; Copyright (C) 2002-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 "at90s8515.inc" ; this is here because SRO is used in an interrupt handler .equ DDR_NRE =DDRD .equ PORT_NRE =PORTD .equ BIT_NRE =6 .equ DDR_DRO =DDRD .equ PORT_DRO =PORTD .equ PIN_DRO =PIND .equ BIT_DRO =4 .equ DDR_DDI =DDRD .equ PORT_DDI =PORTD .equ PIN_DDI =PIND .equ BIT_DDI =5 .equ DDR_SRO =DDRD .equ PORT_SRO =PORTD .equ PIN_SRO =PIND .equ BIT_SRO =3 .equ DDR_SDI =DDRD .equ PORT_SDI =PORTD .equ PIN_SDI =PIND .equ BIT_SDI =7 .equ CHIP_TMHZ =40 ; 4 MHz chip on this one .org 0 rjmp reset ; RESET handler nop ; INT0 handler rjmp anavec ; INT1 handler nop ; TIMER/COUNTER 1 capture nop ; TIMER/COUNTER 1 compare match A nop ; TIMER/COUNTER 1 compare match B rjmp tim1_ovh ; TIMER1 overflow nop ; TIMER0 overflow nop ; SPI complete rjmp uart_rxh ; UART Rx complete nop ; UART data register empty rjmp uart_txh ; UART Tx complete nop ; Analog comparator interrupt ; these are used for the INT1 handler -- RS422 receive .def rs422reg =r14 .def rs422data =r13 .def rs422id =r12 .def rs422cmd =r11 .def bufsize =r10 ; decoded buffer size (when appropiate) .def pbufptr =r9 ; valid bytes at PBUFHEAD .def bufptr =r8 ; valid bytes at TBUFHEAD .def pevent =r3 ; program event reg: ; bit 7 = waiting for host init ; bit 6 = busy using serial port ; bit 5 = waiting for data from RS422 (timer active) ; bit 4 = busy using RS422 port (PORTA) ; bit 3 = buffer ready for processing ; ; when waiting for host init: ; bit 0 = got first X ; bit 1 = got second X ; bit 2 = got third X ; ; when running: ; bit 2 = stuff from RS422 ready .def tmpreg =r2 ; where SREG gets saved during interrupts .equ TBUFHEADH =0x00 .equ TBUFHEADL =0x60 ; first word in internal RAM .equ PBUFHEADH =0x01 .equ PBUFHEADL =0x00 ; 0xa0'th word in RAM .equ PR_XTAL1 =6 ; PORTC .equ PR_RDY =7 ; PINC .equ PR_NOE =0 ; PORTB .equ PR_NWR =1 ; PORTB .equ PR_BS =2 ; PORTB .equ PR_XA0 =3 ; PORTB .equ PR_XA1 =4 ; PORTB ; data lines sit on PORTA ; ; handler for serial port RX data ; uart_rxh: in tmpreg,SREG push r16 push r17 cbi PORTC,4 ; turn on green LED in r16,UDR ; get byte out of serial port ; switch on program state sbrc pevent,7 ; skip jump if not init rjmp uart_rxh_l1 ; in init sequence somewhere ; running regular, processing command ? sbrc pevent,3 ; skip jump if not processing rjmp uart_rxh_l0 ; jump to echo character ; place in input buffer push XREGH push XREGL ldi XREGH,TBUFHEADH ldi XREGL,TBUFHEADL add XREGL,bufptr brcc uart_rxh_l0a inc XREGH ; flopped into the next word uart_rxh_l0a: st X,r16 ; store in memory inc bufptr ; increment pointer pop XREGL pop XREGH ldi r17,2 cp bufptr,r17 ; got first 2 bytes ? brne uart_rxh_10c ; skip decode if not 2 ; decode size field push XREGH push XREGL push r18 ldi XREGH,TBUFHEADH ldi XREGL,TBUFHEADL ld r17,X+ subi r17,'0' mov r18,r17 lsl r17 ; r17 = H * 2 lsl r17 ; r17 = H * 4 lsl r17 ; r17 = H * 8 add r17,r18 ; r17 = H * 9 add r17,r18 ; r17 = H * 10 ld r18,X subi r18,'0' add r17,r18 ; r17 = (H * 10) + L mov bufsize,r17 pop r18 pop XREGL pop XREGH rjmp uart_rxh_l0b ; jump out uart_rxh_10c: ldi r17,1 cp bufptr,r17 breq uart_rxh_l0b ; jump out if only 1 byte ; when we got the whole buffer, r16=0x0d and bufptr = bufsize + 1 inc bufsize cp bufptr,bufsize breq uart_rxh_10d dec bufsize rjmp uart_rxh_l0b ; not got it all, continue ; got the right amount of data, check char was 0x0d uart_rxh_10d: dec bufsize ; fix decoded size cpi r16,0x0d ; end of input ? brne uart_rxh_l0b ; jump if not ; set ready bit in program event reg mov r17,pevent sbr r17,0x08 ; set bit 3 (input ready) mov pevent,r17 uart_rxh_l0b: rjmp uart_rxh_out ; leave -- main code will produce output if needed ; um, just echo it back i guess uart_rxh_l0: sbrs pevent,6 ; skip call if busy (don't echo) rcall txchar rjmp uart_rxh_out ; test if X uart_rxh_l1: cpi r16,0x58 ; compare with 'X' breq uart_rxh_l4 ; jump if yes ; clear any previous set X bits mov r17,pevent andi r17,0xf8 ; mask off low 3 bits mov pevent,r17 rjmp uart_rxh_l0 ; echo back character and continue uart_rxh_l4: sbrs pevent,0 ; skip jump if seen first 'X' rjmp uart_rxh_l2 sbrs pevent,1 ; skip jump if seen second 'X' rjmp uart_rxh_l3 ; third (final) X mov r17,pevent ori r17,0x44 ; set busy/init3 andi r17,0x7f ; not initialising mov pevent,r17 ; second X uart_rxh_l3: mov r17,pevent ori r17,0x02 mov pevent,r17 ; first X uart_rxh_l2: mov r17,pevent ori r17,0x01 mov pevent,r17 uart_rxh_out: sbi PORTC,4 ; turn off green LED pop r17 pop r16 out SREG,tmpreg reti ; ; uart_txh. invoked when transmitted byte has left ; uart_txh: in tmpreg,SREG sbis USR,5 ; skip if UDR empty (bit set) rjmp uart_txh_out ; nothing to do sbi PORTC,5 ; turn off green LED cbi UCR,6 ; turn off this interrupt uart_txh_out: out SREG,tmpreg reti ; ; tim1_ovh. timer 1 overflow handler. This indicates that we timed ; out while waiting for RS422 data, so put a suitable error condition ; in it and set bit2 (RS422 ready) in pevent ; tim1_ovh: in tmpreg,SREG push r16 ; first turn off INT1 to stop any bits leaking through in r16,GIMSK andi r16,0x7f ; mask off bit 7 (INT1) out GIMSK,r16 in r16,GIFR ori r16,0x80 ; bit 7 (clear pending interrupt) out GIFR,r16 ; then turn off _this_ timer in r16,TIMSK andi r16,0x7f ; disable timer1 overflow out TIMSK,r16 mov r16,pevent ori r16,0x04 ; data ready (bit 2) andi r16,0xdf ; clear bit 5 (waiting for data) mov pevent,r16 ldi r16,0x00 mov rs422id,r16 ; it would be impossible to get a reply of our own ID ldi r16,0xff mov rs422cmd,r16 mov rs422data,r16 clr rs422reg ; reset RS422 receive state ; clear up and get out pop r16 out SREG,tmpreg reti ; ; anavec. code from little atmel -- here interrupts on INT1 rising edge ; anavec: in tmpreg,SREG push r24 ; save this cbi PORTC,1 ; DEBUG ; decide what bit we're expecting first mov r24,rs422reg andi r24,0xf0 ; save high nibble ori r24,0x04 cp rs422reg,r24 ; less than 4 ? brlt anavec_l0 ; yes, still collecting start bits andi r24,0xf0 ; save high nibble ori r24,0x0c cp rs422reg,r24 ; less than 12 ? brlt anavec_l1 ; yes, collecting data cp rs422reg,r24 ; equal to 12 ? breq anavec_l2 ; bit 12 ? expect 1 rjmp anavec_l3 ; bit 13, expect 0 ; collect data-bit anavec_l1: ; which register depends on high nibble of rs422reg (id,cmd,data) ldi r24,0x10 cp rs422reg,r24 brlt anavec_l1i ; reading ID ldi r24,0x20 cp rs422reg,r24 brlt anavec_l1c ; reading command ; must be reading data otherwise clc sbic PIN_DRO,BIT_DRO sec rol rs422data ; rotate bit into register sbi PORTC,1 ; DEBUG rjmp anavec_l1z ; reading ID anavec_l1i: clc sbic PIN_DRO,BIT_DRO sec rol rs422id ; rotate bit into register cbi PORTC,2 ; DEBUG rjmp anavec_l1z ; reading command anavec_l1c: clc sbic PIN_DRO,BIT_DRO sec rol rs422cmd ; rotate bit into register cbi PORTC,3 ; DEBUG anavec_l1z: inc rs422reg ; increment state for next time (safe) rjmp anavec_l99 ; collect start-bits anavec_l0: mov r24,rs422reg andi r24,0xf0 ; save high nibble ori r24,0x01 cp rs422reg,r24 breq anavec_l2 ; expect 1 inc r24 ; set low nibble to 0x02 cp rs422reg,r24 breq anavec_l2 ; expect 1 ; expect 0, fall through anavec_l3: clc sbic PIN_DRO,BIT_DRO sec brcc anavec_l4 ; looks good ; failed. rjmp anavec_l98 ; clears rs422reg ; expect 1 anavec_l2: clc sbic PIN_DRO,BIT_DRO sec brcs anavec_l4 ; looks good ; failed. rjmp anavec_l98 ; clears rs422reg ; good good anavec_l4: inc rs422reg ; got it all ? mov r24,rs422reg andi r24,0xf0 ; save high nibble ori r24,0x0e cp rs422reg,r24 ; end of byte ? brne anavec_l99 ; nope, then do next bit ; zero low nibble mov r24,rs422reg andi r24,0xf0 ; clear low nibble to zero mov rs422reg,r24 ; increment high nibble swap rs422reg inc rs422reg mov r24,rs422reg swap rs422reg ; put right again andi r24,0x0f ; mask out "high" nibble from rs422reg (not necessary actually) cpi r24,0x03 brne anavec_l99 ; loop for next byte sbi PORTC,0 ; DEBUG mov r24,pevent ori r24,0x04 ; set bit 2 (rs422 data ready) mov pevent,r24 anavec_l98: clr rs422reg ; ready for next command anavec_l99: pop r24 ; restore r24 out SREG,tmpreg reti ; ; program entry-point (reset vector) ; reset: cli ldi r16,HRAMEND out SPH,r16 ldi r16,LRAMEND out SPL,r16 clr r16 mov bufptr,r16 ; set buffer ptr to 0 ; initialise program event register (pevent) clr pevent ldi r16,0x80 ; waiting for event mov pevent,r16 ; initialise port A (data bus for programmer) clr r16 ; all input initially out DDRA,r16 out PORTA,r16 ; high impedance states ; initialise port C (lights + 2 programmer control lines (PB6 output)) ldi r16,0x7f out DDRC,r16 clr r16 out PORTC,r16 ; all lights on, *XTAL1 low, *RDY input high-Z ; initialise PD2 (interrupt source) with pullup ldi r16,0x04 out PORTD,r16 ; initialise bits of PORTB associated with programmer (all output) ldi r16,0x1f out DDRB,r16 clr r16 out PORTB,r16 ; noddy display initialisation thing ldi r18,0x30 l1: ldi r19,0x00 add r18,r19 breq l2 ldi r16,0x00 ; all LEDs on out PORTC,r16 rcall delayloop ldi r16,0x3f ; all LEDs off out PORTC,r16 rcall delayloop dec r18 cpi r18,0x20 breq l2 rjmp l1 l2: ; initialise the UART stuff ldi r16,12 ; 19200 on a 4MHz crystal out UBRR,r16 ; set baud rate register ldi r16,0x98 ; RX interrupt, no TX interrupts, 8-bit chars, enable RX/TX out UCR,r16 ; set second red light to show we're waiting for init (visual debug :)) cbi PORTC,1 ; enable RS422 bus (master), put in idle state rcall rs422_init rcall rs422_transmit ; enable interrupts and go sei ; wait for the first 3 init bits to get processed loop: sbrs pevent,0 rjmp loop sbrs pevent,1 rjmp loop sbrs pevent,2 rjmp loop ; got sent the 3 X's :) sbi PORTC,1 ; loop sending the first string of EEPROM data clr r18 clr r17 rcall generic_tx_eeprom mov r16,pevent cbr r16,0x40 ; clear busy bit mov pevent,r16 ; RS422 SRO line is linked into PB3 (PORT_SRO,BIT_SRO), ; setup interrupt stuff but don't activate (done only when receiving) cli mov r16,pevent andi r16,0xf8 ; clear 3 init bits (and RS422 ready bit = 2) mov pevent,r16 sei clr rs422reg ; clear RS422 receive state in r16,MCUCR ori r16,0x0c ; set bits 2 and 3 in MCUCR (INT1 on rising edge) out MCUCR,r16 in r16,GIFR ori r16,0x80 ; clear any pending interrupt for it (still disabled) out GIFR,r16 ; okay, initialised. clr r16 mov bufptr,r16 ; clear serial buffer out l5: sbrc pevent,3 ; skip jump if bit set rjmp user_data_in ; something arrived from the serial port sbrc pevent,2 ; skip jump if bit set rjmp user_rs422_in ; something arrived from the RS422 port rjmp l5 ; loop ; ; jumped-to from the main loop (returns to l5) -- processes line of user stuff ; user_data_in: mov r17,pevent ori r17,0x40 ; set busy mov pevent,r17 ; check for nn:c: ldi r16,0x05 cp bufptr,r16 ; check at least 5 chars brge udi_l5 rjmp udi_lecho ; oops, echo + jump out udi_l5: ldi XREGH,TBUFHEADH ldi XREGL,TBUFHEADL adiw XREGL,2 ld r17,X+ cpi r17,':' breq udi_l2 rjmp udi_lecho ; dunno, echo + out udi_l2: ld r16,X+ ; save command in r16 ld r17,X+ cpi r17,':' breq udi_l3 rjmp udi_lecho ; dunno, echo + out udi_l3: ; oki, got colons, subtract 5 and check command byte mov r17,bufsize subi r17,0x05 mov bufsize,r17 cpi r16,'P' ; programmer brne udi_l7 ; skip if not rcall user_prog_cmd ; call command processor rjmp udi_l1 ; done udi_l7: cpi r16,'V' ; version brne udi_l8 ; skip if not rcall user_version ; call version rjmp udi_l1 ; done udi_l8: cpi r16,'F' ; something for the RS422 bus brne udi_l9 rcall user_rs422 rjmp udi_l1 udi_l9: ; check more.. ; echo the whole lot back (default if unknown..) udi_lecho: ldi XREGH,TBUFHEADH ldi XREGL,TBUFHEADL udi_l0: ld r16,X+ rcall txchar dec bufptr breq udi_l1 rjmp udi_l0 udi_l1: clr bufptr clr bufsize mov r17,pevent andi r17,0xb7 ; mask off bits 3 + 6 (process data + busy) mov pevent,r17 rjmp l5 ; ; user_rs422. called to handle RS422 line command (P). X points at the ; string sent, register "bufsize" hold the size of the rest of it ; user_rs422: ; set busy (not really used) mov r17,pevent ori r17,0x40 ; mask in bit 6 (busy) mov pevent,r17 ld r16,X+ ; load sub-command dec bufsize ; slice one off cpi r16,'T' ; transmit sub-command brne urs_l0 rjmp user_rs422_transmit ; transmit, returns to urs_l99 urs_l0: cpi r16,'I' ; idle sub-command brne urs_l1 ;rcall rs422_idle ; DISABLED rjmp urs_l99 urs_l1: cpi r16,'Z' ; zero sub-command brne urs_l2 ;rcall rs422_zero ; DISABLED rjmp urs_l99 urs_l2: cpi r16,'Y' ; send start/stop bit group (6 bits) brne urs_l3 rjmp user_rs422_startstop ; returns to urs_l99 urs_l3: cpi r16,'0' ; send a zero bit brne urs_l4 clc rcall rs422_setbit rjmp urs_l99 urs_l4: cpi r16,'1' ; send a one bit brne urs_l5 sec rcall rs422_setbit rjmp urs_l99 urs_l5: cpi r16,'R' ; transmit and expect a reply sub-command brne urs_l6 rjmp user_rs422_txrx ; transmit, receive, returns to urs_l99 urs_l6: cpi r16,'S' ; synchronise the RS422 bus (sends a load of 1's) brne urs_l7 rjmp user_rs422_sync urs_l7: urs_l99: mov r17,pevent andi r17,0xbf ; mask off bit 6 (busy) mov pevent,r17 ret ; user_rs422_transmit -- transmits "bufsize" bytes at X user_rs422_transmit: rcall rs422_transmit ; make sure forward idle mode urst_l0: ld r16,X+ ; load bit of data rcall rs422_tx ; transmit byte dec bufsize breq urst_l1 rjmp urst_l0 ; loop for more urst_l1: ; all done transmitting, leave rjmp urs_l99 ; jump back ; user_rs422_txrx -- transmits "bufsize" bytes at X then reads 3 bytes back ; into buffer and echos the HEX codes back up the serial port, or error if ; it all times out somewhere. This is done by interrupt handling now. ; we just send the data then ready receive and timeout. bit5 indicates "waiting" user_rs422_txrx: rcall rs422_transmit ; forward idle push XREGH push XREGL urstr_l0: ld r16,X+ ; load bit of data rcall rs422_tx ; transmit byte dec bufsize breq urstr_l1 ; done rjmp urstr_l0 ; loop for more urstr_l1: pop XREGL pop XREGH ; try and receive 3 bytes...! rcall rs422_receive ; reverse idle ; set waiting bit cli mov r16,pevent ori r16,0x20 ; set bit 5 (expecting RS422 data) mov pevent,r16 rcall user_rs422_receive_init ; does all the interrupt/timer setup sei rjmp urstr_l99 nop nop urstr_l99: ; go back rjmp urs_l99 ; user_rs422_sync -- synchronises the rs422 bus user_rs422_sync: ldi r17,0x30 ; 48 bits ought to do the trick urss_l0: sec rcall rs422_setbit dec r17 brne urss_l0 ; loop some ; go back rjmp urs_l99 ; user_rs422_startstop -- sends start and stop bits user_rs422_startstop: ; send start bit 0110 clc rcall rs422_setbit sec rcall rs422_setbit sec rcall rs422_setbit clc rcall rs422_setbit ; send stop bit 10 sec rcall rs422_setbit clc rcall rs422_setbit ; go back rjmp urs_l99 ; ; user_rs422_in. called when we receive something from the RS422 bus :-))) ; assert: we were expecting it. ; user_rs422_in: ; disable any pending timer and the interrupt cli in r16,GIMSK andi r16,0x7f ; turn off INT1 out GIMSK,r16 in r16,GIFR ori r16,0x80 ; clear any pending interrupt out GIFR,r16 in r16,TIMSK andi r16,0x6a ; disable timer1 overflow (and mask off non-writable bits) out TIMSK,r16 in r16,TIFR ori r16,0x80 out TIFR,r16 ; better STOP the timer/counter :) clr r16 out TCCR1B,r16 ; disable sei sbi PORTC,0 ; DEBUG (not waiting anymore) sbi PORTC,1 ; DEBUG sbi PORTC,2 ; DEBUG sbi PORTC,3 ; DEBUG ; if bit 5 in pevent is still set, we didn't time out. mov r16,pevent andi r16,0x20 ; mask to keep waiting bit tst r16 breq ursi_l0 ; timed out ; send success message :) clr r17 ldi r18,0xf0 ; address of (partial) success message in EEPROM rcall generic_tx_eeprom mov r16,rs422id rcall txhex mov r16,rs422cmd rcall txhex mov r16,rs422data rcall txhex ldi r16,0x0d rcall txchar rjmp ursi_l99 ; failed :( send rubbished data anyway -- make sure it works (!) ursi_l0: clr r17 ldi r18,0xe0 ; address of failure message in EEPROM rcall generic_tx_eeprom ; fall through ursi_l99: ; put rs422 bus right-way-round rcall rs422_transmit cli mov r16,pevent andi r16,0xdb ; mask out bits 5 (waiting) and 2 (ready) mov pevent,r16 sei rjmp l5 ; bit jump back to main loop (!) ; ; user_rs422_receive_init. initialises rs422 for receive. sets up interrupt ; pin and timeout ; user_rs422_receive_init: cbi PORTC,0 ; DEBUG (waiting) clr rs422reg ; initalise rs422 receive state in r16,GIMSK ori r16,0x80 ; enable interrupt 1 (bits in MCUCR already set) out GIMSK,r16 ldi r16,0x00 out TCCR1A,r16 ldi r16,0x05 ; CK/1024 -- ~3906.25 ticks per second at 4 MHz out TCCR1B,r16 ; inlined old loadrtimer basically here in r16,TIMSK andi r16,0x6a ; mask off non-writable and TOIE1 bits out TIMSK,r16 ; write out ldi r17,0xf0 out TCNT1H,r17 ldi r17,0xbc out TCNT1L,r17 ; it will tick 3908 times, or there abouts ori r16,0x80 ; enable overflow out TIMSK,r16 ret ; ; user_prog_cmd. called to handle programmer command (P). X points at the ; string sent, register "bufsize" holds size of the rest of it ; user_prog_cmd: ; set busy (not really used, but heyho) mov r17,pevent ori r17,0x40 ; mask in bit 6 (busy) mov pevent,r17 ld r16,X+ ; load sub-command cpi r16,'I' ; initialise brne upc_l0 ; programmer is all over the place in this version. cbi PORTB,PR_BS ; BS low sbi PORTB,PR_NOE ; NOE high sbi PORTB,PR_NWR ; NWR high sbi PORTB,PR_XA0 ; XA0, XA1 = idle sbi PORTB,PR_XA1 cbi PORTC,PR_XTAL1 ; XTAL1 low ; human should do the reset thing here, send a little message clr r17 ldi r18,0x50 rcall generic_tx_eeprom rjmp upc_l99 ; check next upc_l0: cpi r16,'S' ; read signature bytes brne upc_l2 rjmp upc_devsig ; do sig (returns to upc_l99) upc_l2: cpi r16,'X' ; chip erase brne upc_l3 rjmp upc_chiperase ; do chip erase (returns to upc_l99) upc_l3: cpi r16,'T' ; chip test (eeprom) brne upc_l4 ;rjmp upc_chiptest ; do chip test (returns to upc_l99) nop upc_l4: cpi r16,'F' ; flash program brne upc_l5 rjmp upc_flashprog upc_l5: cpi r16,'Z' ; deinitialise programming interface brne upc_l6 ;sbi PORTB,3 ; RESET high nop ; nothing to do (?) rjmp upc_l99 ; done upc_l6: cpi r16,'E' ; eeprom program brne upc_l7 rjmp upc_eepromprog upc_l7: upc_l99: mov r17,pevent andi r17,0xbf ; mask off bit 6 (busy) mov pevent,r17 ret ; gets device signature upc_devsig: clr pbufptr ldi YREGH,PBUFHEADH ldi YREGL,PBUFHEADL clr r18 upc_ds_l1: ldi r16,0x08 ; read signature row command rcall upi_loadcommand mov r16,r18 rcall upi_loadlowaddress push r18 ldi r18,0x0a rcall sdelayloop pop r18 clr r16 out DDRA,r16 ; PORTA all input out PORTA,r16 ; all high-Z state cbi PORTB,PR_NOE ; set NOE = 0 cbi PORTB,PR_BS ; set BS = 0 push r18 ldi r18,0x0a rcall sdelayloop pop r18 in r16,PINA ; read stuff from PINA nop nop sbi PORTB,PR_NOE ; set NOE = 1 ; store in PBUF st Y+,r16 inc r18 cpi r18,0x03 ; all done breq upc_ds_l2 rjmp upc_ds_l1 upc_ds_l2: clr r17 ldi r18,0x70 ; SIG string (early finish) in eeprom rcall generic_tx_eeprom ldi YREGH,PBUFHEADH ldi YREGL,PBUFHEADL ldi r18,0x03 upd_ds_l3: ld r16,Y+ rcall txhex dec r18 brne upd_ds_l3 ldi r16,0x0d rcall txchar rjmp upc_l99 ; go back ; sends chip erase command upc_chiperase: ldi r16,0x80 ; chip-erase command rcall upi_loadcommand rcall upi_pulsenwr ; pulse NWR to enable lock-bit erase ldi r18,0x0c rcall delayloop ; wait >10ms rjmp upc_l99 ; go back ; _could_ use loops here, but it's an 8k device :) ; programs the EEPROM (data bytes at X) upc_eepromprog: ld r17,X+ ; grad quad-byte offset (0-15 -> 0-63) lsl r17 lsl r17 ldi r16,0x11 ; flash program command rcall upi_loadcommand mov r16,r17 ; low address rcall upi_loadlowaddress ld r16,X+ ; grad word rcall upi_loaddatabyte rcall upi_writelowdata inc r17 ; next byte ldi r16,0x11 ; flash program command rcall upi_loadcommand mov r16,r17 ; low address rcall upi_loadlowaddress ld r16,X+ ; grad word rcall upi_loaddatabyte rcall upi_writelowdata inc r17 ; next byte ldi r16,0x11 ; flash program command rcall upi_loadcommand mov r16,r17 ; low address rcall upi_loadlowaddress ld r16,X+ ; grad word rcall upi_loaddatabyte rcall upi_writelowdata inc r17 ; next byte ldi r16,0x11 ; flash program command rcall upi_loadcommand mov r16,r17 ; low address rcall upi_loadlowaddress ld r16,X+ ; grab word rcall upi_loaddatabyte rcall upi_writelowdata ; set Y=X, rewind X, read EEPROM into Y mov YREGH,XREGH mov YREGL,XREGL ld r16,-X ld r16,-X ld r16,-X ld r16,-X subi r17,3 ; put back ; read eeprom ldi r16,0x03 ; read EEPROM command rcall upi_loadcommand mov r16,r17 ; low address rcall upi_loadlowaddress clr r16 out DDRA,r16 ; PORTA all inut out PORTA,r16 ; all high-Z state nop nop cbi PORTB,PR_NOE ; set NOE low cbi PORTB,PR_BS ; set BS to 0 nop nop in r16,PINA ; read byte into r16 st Y+,r16 ; and store in Y nop nop sbi PORTB,PR_NOE ; set NOE high nop nop ser r16 out DDRA,r16 ; PORTA back to output nop nop inc r17 ldi r16,0x03 ; read EEPROM command rcall upi_loadcommand mov r16,r17 ; low address rcall upi_loadlowaddress clr r16 out DDRA,r16 ; PORTA all inut out PORTA,r16 ; all high-Z state nop nop cbi PORTB,PR_NOE ; set NOE low cbi PORTB,PR_BS ; set BS to 0 nop nop in r16,PINA ; read byte into r16 st Y+,r16 ; and store in Y nop nop sbi PORTB,PR_NOE ; set NOE high nop nop ser r16 out DDRA,r16 ; PORTA back to output nop nop inc r17 ldi r16,0x03 ; read EEPROM command rcall upi_loadcommand mov r16,r17 ; low address rcall upi_loadlowaddress clr r16 out DDRA,r16 ; PORTA all inut out PORTA,r16 ; all high-Z state nop nop cbi PORTB,PR_NOE ; set NOE low cbi PORTB,PR_BS ; set BS to 0 nop nop in r16,PINA ; read byte into r16 st Y+,r16 ; and store in Y nop nop sbi PORTB,PR_NOE ; set NOE high nop nop ser r16 out DDRA,r16 ; PORTA back to output nop nop inc r17 ldi r16,0x03 ; read EEPROM command rcall upi_loadcommand mov r16,r17 ; low address rcall upi_loadlowaddress clr r16 out DDRA,r16 ; PORTA all inut out PORTA,r16 ; all high-Z state nop nop cbi PORTB,PR_NOE ; set NOE low cbi PORTB,PR_BS ; set BS to 0 nop nop in r16,PINA ; read byte into r16 st Y+,r16 ; and store in Y nop nop sbi PORTB,PR_NOE ; set NOE high nop nop ser r16 out DDRA,r16 ; PORTA back to output nop nop ; rewind Y then compare ld r16,-Y ld r16,-Y ld r16,-Y ld r16,-Y push YREGH push YREGL ldi r17,4 upc_ep_l1: ld r16,X+ ld r18,Y+ cp r16,r18 brne upc_ep_l3 ; wasn't the same dec r17 breq upc_ep_l2 ; yay, same stuff rjmp upc_ep_l1 ; loop for next upc_ep_l3: ; failed to compare OK pop YREGL pop YREGH clr r17 ldi r18,0xd0 ; address of (partial) EP:FAIL message in EEPROM rcall generic_tx_eeprom ld r16,Y+ rcall txhex ld r16,Y+ rcall txhex ld r16,Y+ rcall txhex ld r16,Y+ rcall txhex ldi r16,0x0d rcall txchar rjmp upc_ep_l4 upc_ep_l2: ; compare GOOD pop YREGL pop YREGH clr r17 ldi r18,0xc0 ; address of (partial) EP:OK message in EEPROM rcall generic_tx_eeprom ld r16,Y+ rcall txhex ld r16,Y+ rcall txhex ld r16,Y+ rcall txhex ld r16,Y+ rcall txhex ldi r16,0x0d rcall txchar rjmp upc_ep_l4 upc_ep_l4: rjmp upc_l99 ; go back ; programs the flash (data bytes at X) upc_flashprog: ld r18,X+ ; grab high address byte ld r17,X+ ; grab low address byte lsl r18 ; *2 lsl r17 ; shift high bit into carry (becomes address high byte) brcc upc_fp_l0 ori r18,0x01 ; then place in r18 if set upc_fp_l0: ; load program flash command, address start is at r18:r17 ldi r16,0x10 ; program-flash command rcall upi_loadcommand mov r16,r17 ; low address rcall upi_loadlowaddress mov r16,r18 ; high address rcall upi_loadhighaddress ; program in high and low bytes of data ld r19,X+ ; high byte ld r16,X+ ; low byte (loads first) rcall upi_loaddatabyte rcall upi_writelowdata ; writes data low byte ldi r16,0x10 ; program-flash command rcall upi_loadcommand mov r16,r17 ; low address rcall upi_loadlowaddress mov r16,r18 ; high address rcall upi_loadhighaddress mov r16,r19 ; high byte (loads next) rcall upi_loaddatabyte rcall upi_writehighdata ; writes data high byte nop nop mov r16,r17 andi r16,0x01 cpi r16,0x01 ; was this the odd one ? breq upc_fp_l1 ori r17,0x01 ; odd flash address rjmp upc_fp_l0 ; program next byte of flash upc_fp_l1: ; set Y = X then rewind X, read written stuff into Y mov YREGH,XREGH mov YREGL,XREGL ld r16,-X ld r16,-X ld r16,-X ld r16,-X andi r17,0xfe ; even flash address upc_fp_l2: ; read flash ldi r16,0x02 ; read flash command rcall upi_loadcommand mov r16,r17 ; low address rcall upi_loadlowaddress mov r16,r18 ; high address rcall upi_loadhighaddress clr r16 out DDRA,r16 ; PORTA all input out PORTA,r16 ; all high-Z state nop nop cbi PORTB,PR_NOE ; set NOE low cbi PORTB,PR_BS ; set BS to 0 nop nop in r19,PINA ; read low byte into r19 nop nop sbi PORTB,PR_NOE ; set NOE high nop nop ser r16 out DDRA,r16 ; PORTA back to output nop nop ldi r16,0x02 ; read flash command rcall upi_loadcommand mov r16,r17 ; low address rcall upi_loadlowaddress mov r16,r18 ; high address rcall upi_loadhighaddress ; read stuff nop nop clr r16 out DDRA,r16 ; PORTA all input out PORTA,r16 ; all high-Z state nop nop cbi PORTB,PR_NOE ; set NOE low cbi PORTB,PR_BS ; set BS to 0 cbi PORTB,PR_NOE ; set NOE low sbi PORTB,PR_BS ; set BS to 1 nop nop in r16,PINA ; read high byte into r16 st Y+,r16 st Y+,r19 ; store low byte read earlier nop nop sbi PORTB,PR_NOE ; set NOE high mov r16,r17 andi r16,0x01 cpi r16,0x01 ; was this the odd one ? breq upc_fp_l3 ori r17,0x01 ; odd flash address rjmp upc_fp_l2 ; read next byte of flash upc_fp_l3: ; rewind Y, then compare ld r16,-Y ld r16,-Y ld r16,-Y ld r16,-Y push YREGH push YREGL ldi r17,4 upc_fp_l4: ld r16,X+ ld r18,Y+ cp r16,r18 brne upc_fp_l5 ; wasn't the same dec r17 breq upc_fp_l6 ; yay, same stuff rjmp upc_fp_l4 ; loop upc_fp_l5: pop YREGL pop YREGH ; restore Y (data read) clr r17 ldi r18,0xb0 ; address of (partial) FP:FAIL message in EEPROM rcall generic_tx_eeprom ld r16,Y+ rcall txhex ld r16,Y+ rcall txhex ld r16,Y+ rcall txhex ld r16,Y+ rcall txhex ldi r16,0x0d rcall txchar rjmp upc_fp_l7 upc_fp_l6: pop YREGL pop YREGH ; retore Y (data read) clr r17 ldi r18,0xa0 ; address of (partial) FP:OK message in EEPROM rcall generic_tx_eeprom ld r16,Y+ rcall txhex ld r16,Y+ rcall txhex ld r16,Y+ rcall txhex ld r16,Y+ rcall txhex ldi r16,0x0d rcall txchar rjmp upc_fp_l7 upc_fp_l7: rjmp upc_l99 ; ; upi_pulsextal. gives xtal1 to the little atmel a pulse ; upi_pulsextal: sbi PORTC,PR_XTAL1 nop nop nop nop cbi PORTC,PR_XTAL1 ret ; ; upi_pulsenwr. gives nwr a pulse (active low) ; upi_pulsenwr: cbi PORTB,PR_NWR nop nop nop nop sbi PORTB,PR_NWR ret ; ; upi_loadcommand. loads command onto the wires, ; pulses xtal. r16=command byte ; upi_loadcommand: sbi PORTB,PR_XA1 cbi PORTB,PR_XA0 ; set XA1,XA0 to 10 (load command) cbi PORTB,PR_BS ; set BS to 0 push r16 ser r16 out DDRA,r16 ; PORTA all output pop r16 out PORTA,r16 ; put command on wires rcall upi_pulsextal ; pulse XTAL ret ; ; upi_loadlowaddress. loads low address byte onto the ; wires, pulses xtal. r16=low addr ; upi_loadlowaddress: cbi PORTB,PR_XA1 cbi PORTB,PR_XA0 ; set XA1,XA0 to 00 (load address) cbi PORTB,PR_BS ; set BS to 0 (low address) push r16 ser r16 out DDRA,r16 ; PORTA all output pop r16 out PORTA,r16 ; put low address on wires rcall upi_pulsextal ; pulse XTAL ret ; ; upi_Loadhighaddress. loads high address byte (or bit for 1k devices) ; onto the wires, pulses xtal. r16=high addr ; upi_loadhighaddress: cbi PORTB,PR_XA1 cbi PORTB,PR_XA0 ; set XA1,XA0 to 00 (load address) sbi PORTB,PR_BS ; set BS to 1 (high address) push r16 ser r16 out DDRA,r16 ; PORTA all output pop r16 out PORTA,r16 ; put high address on wires rcall upi_pulsextal ; pulse XTAL ret ; ; upi_loaddatabyte. loads data byte, pulses xtal. r16=data ; upi_loaddatabyte: cbi PORTB,PR_XA1 sbi PORTB,PR_XA0 ; set XA1,XA0 to 01 (load data) push r16 ser r16 out DDRA,r16 ; PORTA all output pop r16 out PORTA,r16 ; put data byte on wires nop nop rcall upi_pulsextal ; pulse XTAL nop nop cbi PORTB,PR_XA0 ; docs indicate this in the waveform.. ret ; ; upi_writelowdata. writes low data byte to device and waits for RDY ; upi_writelowdata: cbi PORTB,PR_BS ; set BS to 0 (low data) nop nop rcall upi_pulsenwr ; pulse nwr, ~20ns for RDY to go low nop nop upi_wld_l0: sbis PINC,PR_RDY ; skip if set rjmp upi_wld_l0 ; loop while busy ret ; ; upi_writehighdata. writes high data byte to device and waits for RDY ; upi_writehighdata: sbi PORTB,PR_BS ; set BS to 1 (high data) nop nop rcall upi_pulsenwr ; pulse nwr, ~20ns for RDY to go low nop nop upi_whd_l0: sbis PINC,PR_RDY ; skip if set rjmp upi_whd_l0 ; loop while busy ret ; ; generic_tx_eeprom. transmits message in EEPROM at r17:r18 [H:L] ; generic_tx_eeprom: push r16 push r18 out EEARH,r17 gen_txe_l1: out EEARL,r18 sbi EECR,0 ; read EEPROM in r16,EEDR ; read data tst r16 breq gen_txe_l2 ; jump if read 0 rcall txchar ; transmit char inc r18 rjmp gen_txe_l1 ; loop for next char gen_txe_l2: pop r18 pop r16 ret ; ; user_version. called to send the version (located at EEPROM address 0x40) ; user_version: ldi r18,0x40 ; eeprom low byte for version clr r17 rjmp generic_tx_eeprom ; send message ; ; txhex. transmits hex characters for the value in r16 ; txhex: push r18 push r17 push r16 mov r17,r16 lsr r16 lsr r16 lsr r16 lsr r16 cpi r16,0x0a brsh txhex_hi10 ; high nibble is less than 10 ldi r18,0x30 add r16,r18 rcall txchar rjmp txhex_lor16 ; high nibble is >= 10 txhex_hi10: ldi r18,0x37 add r16,r18 rcall txchar txhex_lor16: mov r16,r17 andi r16,0x0f cpi r16,0x0a brsh txhex_lo10 ; low nibble is less than 10 ldi r18,0x30 add r16,r18 rcall txchar rjmp txhex_done ; low niggle is >= 10 txhex_lo10: ldi r18,0x37 add r16,r18 rcall txchar txhex_done: pop r16 pop r17 pop r18 ret ; ; txchar. transmits character in r16. waits for the transmite reg to empty ; txchar: cbi PORTC,5 ; turn on green LED txchar_l: sbis USR,5 rjmp txchar_l out UDR,r16 txchar_w: sbis USR,5 rjmp txchar_w sbi UCR,6 ; enable interrupt when bits get really transmitted ret ; ; delayloop. called with some value in r18 which controls delay (0xff == ~0.5s at 1 MHz) ; delayloop: push r18 push r17 dlloop: ldi r17,0xff dloop: nop nop nop nop dec r17 breq deloop rjmp dloop deloop: dec r18 breq de2loop rjmp dlloop de2loop: pop r17 pop r18 ret ; ; sdelayloop. small delay loop (value in r18) ; sdelayloop: push r18 sdloop_1: nop nop nop nop dec r18 brne sdloop_1 pop r18 ret ; ; included RS422 stuff ; .include "rs422code.asm"