;*************************************************************************** ;* ;* Title : AVR DCK ;* Version : 1.1 ;* Last updated : 2000.07.14 ;* Target : AT90S1200A (with RC oscillator enabled) ;* File : dckprog.asm ;* Author : Dave Keenan ;* ;* Originally : AVR ISP (Auto adr inc, 19200bps) ;* Version : 2.0 ;* Last updated : 1998.01.06 (mlund) ;* Target : AT90S1200 ;* File : ispprog.asm ;* Author : Atmel Norway ;* ;* I modified it to support my pseudo-parallel programmer which is ;* a mostly serial programmer that also has the ability to write and read ;* the fuse bits (e.g. RC enable) in parallel mode in a piggy-back socket. ;* ;* To fit the parallel programming code I have removed support ;* for the non-AVR AT89S8252. I have modified the UART routines to ;* allow the timer interrupt handler to also drive the voltage tripler that ;* generates the 12V supply for parallel programming, and made a number of ;* other minor code-saving changes. ;* ;* Because it's only running at 1.1MHz (RC osc) it was necessary to move all ;* commands that have arguments, to the beginning of the search. Otherwise ;* the arguments could arrive before the getc call that would receive them. ;* ;* ;* DESCRIPTION ;* The firmware on all programmers now support a unified protocol for ;* program and data memory programming. The host computer do not need ;* to know if the programmer operates in serial or parallel mode. ;* ;* The following commands are supported. All commands start with a ;* single letter. The programmer returns 13d (carriage return) or the ;* data read after the command is finished. ;* ;* +-------------+------------+------+ ;* Commands | Host writes | Host reads | | ;* -------- +-----+-------+------+-----+ | ;* | ID | data | data | | Note | ;* +-----------------------------------+-----+-------+------+-----+------+ ;* | Enter programming mode | 'P' | | | 13d | 1 | ;* | Set address | 'A' | ah al | | 13d | 2 | ;* | Write program memory, low byte | 'c' | dd | | 13d | 3 | ;* | Write program memory, high byte | 'C' | dd | | 13d | 3 | ;* | Issue Page Write | 'm' | | | 13d | | ;* | Read program memory | 'R' | |dd(dd)| | 4 | ;* | Write data memory | 'D' | dd | | 13d | | ;* | Read data memory | 'd' | | dd | | | ;* | Chip erase | 'e' | | | 13d | | ;* | Write lock bits | 'l' | dd | | 13d | | ;* | Write fuse bits | 'f' | dd | | 13d | 11 | ;* | Read fuse and lock bits | 'F' | | dd | | 11 | ;* | Leave programming mode | 'L' | | | 13d | 5 | ;* | Select device type | 'T' | dd | | 13d | 6 | ;* | Read signature bytes | 's' | | 3*dd | | | ;* | Return supported device codes | 't' | | n*dd | 00d | 7 | ;* | Return software identifier | 'S' | | s[7] | | 8 | ;* | Return sofware version | 'V' | |dd dd | | 9 | ;* | Return hardware version | 'v' | |dd dd | | 9 | ;* | Return programmer type | 'p' | | dd | | 10 | ;* | Set LED | 'x' | dd | | 13d | 12 | ;* | Clear LED | 'y' | dd | | 13d | 12 | ;* | Universial command | ':' | 3*dd | dd | 13d | | ;* +-----------------------------------+-----+-------+------+-----+------+ ;* ;* NOTE 1 ;* The Enter programming mode command MUST be sent one time prior to ;* the other commands, with the exception of the 't', 'S', 'V', 'v' ;* and 'T' commands. The 'T' command must be sent before this command ;* (see note 6). ;* ;* For programmers supporting both parallel and serial programming ;* mode this command enters parallel programming mode. For programmers ;* supporting only serial programming mode, this command enters serial ;* programming mode. ;* ;* NOTE 2 ;* The ah and al are the high and low order bytes of the address. For ;* parallel programmers this command issues the Load Address Low/High ;* Byte command. For serial programmers the address byte is stored for ;* use by the Read/Write commands. ;* ;* NOTE 3 ;* For parallel programmers this command issues the Program Flash ;* command. For serial programmers this command iussues the Write ;* Program Memory Command. For devices with byte-wide program memories ;* only the low byte command should be used. ;* ;* NOTE 4 ;* The contents of the program memory at the address given by the 'A' ;* command are written to the serial port in binary form. For byte ;* wide memories one byte is written. For 16 bit memories two bytes ;* are written,MSB first. ;* ;* NOTE 5 ;* This command must be executed after the programming is finished. ;* ;* NOTE 6 ;* The select device type command must be sent before the enter ;* programming command ;* ;* NOTE 7 ;* The supported device codes are returned in binary form terminated ;* by 0x00. ;* ;* NOTE 8 ;* This return a 7 character ASCII string identifying the programmer. ;* For the in-system programmer it is "AVR ISP". ;* ;* NOTE 9 ;* The software/hardware version are returned as two ASCII numbers. ;* ;* NOTE 10 ;* This command should be used to identify the programmer type. The ;* return value is 'S' for serial (or SPI) programmers or 'P' for ;* parallel programmers. ;* ;* NOTE 11 ;* The write fuse bits command are available only on parallel ;* programmers and only for AVR devices (device code < 0x80). The host ;* should use the return programmer type command to determine the ;* programmer type, do not use the "AVR PPR" idenifier because other ;* programmers may be available in the future. ;* ;* NOTE 12 ;* Currently only the AVR development board has LEDs. The other boards ;* must implement this commands as NOPs. ;* ;* NOTE 13 ;* Devices using Page Mode Programming write one page of flash memory ;* before issuing a Page Mode Write Pulse. ;*************************************************************************** ;**** includes **** .include "1200def.inc" ;*************************************************************************** ;* ;* CONSTANTS ;* device codes ;* ;* DESCRIPTION ;* The following device codes must be used by the host computer. Note ;* that the device codes are arbitrary selected, they do not have any ;* thing in common with the signature bytes stored in the device. ;* ;* The following devices are supported (make a new table for each ;* software release): ;* ;* SW_MAJOR=1, SW_MINOR=5 ;* AT90S1200 rev. C (abbreviated S1200C) ;* AT90S1200 rev. D (abbreviated S1200D) ;* AT90S8515 rev. A (abbreviated S8515A) ;* AT89S8252 (abbreviated S8252) ;* ;* SW_MAJOR=1, SW_MINOR=6 ;* AT90S1200 rev. C (abbreviated S1200C) ;* AT90S1200 rev. D (abbreviated S1200D) ;* AT90S8515 rev. A (abbreviated S8515A) ;* AT89S8252 (abbreviated S8252) ;* ATmega103 rev. A (abbreviated S01838A) ;* ;*************************************************************************** .equ S1200C = 0x12 .equ S1200D = 0x13 .equ S2313A = 0x20 .equ S4414A = 0x28 .equ S8515A = 0x38 .equ S2323A = 0x48 .equ S01838C = 0x40 .equ S01838D = 0x41 ;**** Revision Codes **** .equ SW_MAJOR = 1 ; Major SW revision number .equ SW_MINOR = 1 ; Minor SW revision number .equ HW_MAJOR = 1 ; Major HW revision number .equ HW_MINOR = 0 ; Minor HW revision number ;*************************************************************************** ;* ;* MACROS ;* Program Macros ;* ;*************************************************************************** .macro pulse_sck sbi portb,SCK ldi temp2,6 m0: dec temp2 brne m0 cbi portb,SCK ldi temp2,1 ; 3 m1: dec temp2 brne m1 .endm ;***************** ;* SPI Constants * ;***************** .equ MOSI = 5 ; Bit number on PORTB .equ MISO = 6 ; Bit number on PORTB .equ SCK = 7 ; Bit number on PORTB ;****************** ;* UART Constants * ;****************** ;**** Constant declarations Data Rate **** .equ PROC_CLOCK = 11300 ; * 100 Hz ; 1.13 MHz. Change to actual clock rate .equ DATA_RATE = 192 ; * 100 bit/s .equ TIMER_PRESCALE = 7 * (PROC_CLOCK/DATA_RATE > 184) + 1 ; Either 1 or 8 ; Intermediate result used in R and N calculations below .equ R = 1 + (TIMER_PRESCALE == 8) ; Either 1 or 2 .equ N = ((PROC_CLOCK + DATA_RATE/2)/DATA_RATE - 9 + TIMER_PRESCALE/2)/TIMER_PRESCALE + 8 ;.equ N = 95 ; 115200 BAUD when R=1 and XTAL=11.059MHz ;.equ N = 31 ; 57600 BAUD when R=2 and XTAL=11.059MHz ;.equ N = 43 ; 38400 BAUD when R=2 and XTAL=11.059MHz ;.equ N = 33 ; 19200 BAUD when R=2 and XTAL=4.00MHz ;.equ N = 102 ; 38400 BAUD when R=1 and XTAL=4.00MHz ;.equ N = 56 ; 19200 BAUD when R=1 and RCOSC~=1.10MHz (5V) ;**** UART transmit pin in PORTD **** .equ TXPIN = PD4 .equ RXPIN = PD5 ;**** Bit positions in UART Status Register **** .equ TXC = 0 ; Transmit complete .equ RXC = 1 ; Receive complete .equ VMUL= 2 ; Voltage multiplier on ;**** Port D bit definitions **** .equ NOT_RST_PDN =PD0 ;Output to pull-down slave /RESET via max 22k resistor. .equ V_MULTIPLIER =PD1 ;Output to drive voltage multiplier in par prog mode ; input with no pullup otherwise. .equ XTAL1 =PD2 ;Slave XTAL1 output in par prog mode ; input with no pullup otherwise. .equ NOT_OE =PD3 ;Slave /OE output in par prog mode ; low output otherwise. .equ NOT_WR_TX =PD4 ;Slave /WR output in par prog mode ; host TX output otherwise. .equ XA0_RX =PD5 ;Slave XA0 output in par prog mode ; host RX input with internal pullup otherwise. .equ XA1 =PD6 ;Slave XA1 output in par prog mode ; low output otherwise. ;In parallel programming mode, the XA1 and XA0 bits determine the action ;taken when the XTAL1 pin is given a positive pulse. ;XA1 XA0 Action ;---------------- ; 0 0 Load Flash or EEPROM Address (High or Low address byte for Flash determined by BS) ; 0 1 Load Data (High or Low data byte for Flash determined by BS) ; 1 0 Load Command (BS must be low) ; 1 1 No Action, Idle ;Parallel Programming Notes: ;1. BS is hardwired as (XA0_RX & XA1) so it is automatically low for ; loading address, command or data, so one cannot load a high address or high data byte ; but it can be set high or low for reading since the XA signals are not relevant. ; In particular both XA0_RX and XA1 can be set high to read fuse and lock bits. ;2. TX is hardwired as (NOT_OE | NOT_WR_TX), so these should never be both low ; in parallel programming mode or we will transmit junk to the host. ; Outside par prog mode NOT_OE must be a low output. ;3. When XA0_RX is used as the RX input (outside parallel programming mode) ; XA1 must be a low output to prevent excessive pullup and interference ; from BS which may become an output. ;4. XA0 is driven via a 180k resistor to prevent it interfering with RX ; and so about 3us should be allowed for it to rise or fall. ;5. RDY/BSY is not connected so we must rely on a sufficient delay after a write. ;Parallel programming commands .equ CHIP_ERASE_CMD =0b10000000 ;Command to erase (to 1s) flash, EEPROM and lock bits .equ WR_FUSE_BITS_CMD=0b01000000 ;Command to write the fuse bits .equ WR_LOCK_BITS_CMD=0b00100000 ;Command to write the lock bits .equ RD_FUSE_LOCK_CMD=0b00000100 ;Command to read the fuse and lock bits ;***************************** ;* Global Register Variables * ;***************************** .def savedSREG=r0 ; Used by interrupt handler .def device = r16 ; Device code .def temp1 = r17 .def temp2 = r18 .def s_data = r19 ; SPI data .def u_data = r20 ; UART data .def addrl = r1 ; Low order byte of address .def addrh = r2 ; High order byte of address .def bit_cnt = r21 ; Bit count used by UART routine .def u_stat = r22 ; Status byte used by UART routine .def cmd = r3 ; Serial programming command .def count = r23 ; Time out variable for "enter programming mode" .def param1 = r24 .def cmd1 = r4 .def cmd2 = r5 .def cmd3 = r6 .def tempi = r25 ; For use only in the interrupt handler .def TOIenable=r26 ; Constant 1<= 0x20) && (device <= 0x7F) ) brlo s2 tst device brmi s2 s0b: ; { ldi count,32 ; count = 32; s1: ; do { rcall rdser ; if (rdser == 0x53) // SPI read (byte 3) cpi s_data,0x53 breq s3 ; break; ldi s_data,0x00 ; wrser(0x00); // SPI write (byte 4) rcall wrser pulse_sck ; pulse SCK ldi s_data,0xac ; wrser(0xac); // SPI write (byte 1) rcall wrser ldi s_data,0x53 ; wrser(0x53); // SPI write (byte 2) rcall wrser dec count ; } while(--count); brne s1 rjmp s3 ; } ; else s2: ; { ldi s_data,0x00 ; wrser(0x00); // SPI write (byte 3) rcall wrser s3: ; } ldi s_data,0x00 ; wrser(0x00); // SPI write (byte 4) rcall wrser ldi temp1,0x10 ; delay(0x10); rcall delay ret ;*************************************************************************** ;* ;* FUNCTION ;* show_id ;* ;* DESCRIPTION ;* Show our ID ("AVR DCK") on the serial line. ;* ;*************************************************************************** show_id:ldi u_data,0x41 ; 'A' rcall putc ldi u_data,0x56 ; 'V' rcall putc ldi u_data,0x52 ; 'R' rcall putc ldi u_data,0x20 ; ' ' rcall putc ldi u_data,0x44 ; 'D' ;My initials rcall putc ldi u_data,0x43 ; 'C' rcall putc ldi u_data,0x4b ; 'K' rcall putc ret ;*************************************************************************** ;* ;* RESET ;* ;* DESCRIPTION ;* Initialization ;* ;*************************************************************************** RESET: ldi TOIenable,1< main ;* ;* DESCRIPTION ;* Wait for and execute commands. ;* ;*************************************************************************** waitcmd:rcall getc ; while (getc() == ESC) {}; cpi u_data,0x1b breq waitcmd ;**** Device Type **** cpi u_data,0x54 ; 'T' Device type brne w0 rcall getc ; getc(); // dummy mov device,u_data ; putc(device); rjmp put_ret ;**** Return Software Identifier **** w0: cpi u_data,0x53 ; 'S' Return software identifier brne w1 rcall show_id ; show_id(); rjmp waitcmd ;**** Return Software Version **** w1: cpi u_data,0x56 ;'V' Return software version brne w2 ldi u_data,0x30+SW_MAJOR ; putc(0x30+SW_MAJOR); rcall putc ldi u_data,0x30+SW_MINOR ; putc(0x30+SW_MINOR); rcall putc rjmp waitcmd ;**** Return Hardware Version **** w2: cpi u_data,0x76 ;'v' Return hardware version brne w3 ldi u_data,0x30+HW_MAJOR ; putc(0x30+HW_MAJOR); rcall putc ldi u_data,0x30+HW_MINOR ; putc(0x30+HW_MINOR); rcall putc rjmp waitcmd ;**** Show Supported Devices **** w3: cpi u_data,0x74 ; 't' Show supported devices brne w4 ldi u_data,S1200C ; putc(S1200C); rcall putc ldi u_data,S1200D ; putc(S1200D); rcall putc ldi u_data,S2313A ; putc(S2313A); rcall putc ldi u_data,S4414A ; putc(S4414A); rcall putc ldi u_data,S8515A ; putc(S8515A); rcall putc ldi u_data,S2323A ; putc(S2323A); rcall putc ldi u_data,0x00 ; putc(0x00); // end of device list rcall putc rjmp waitcmd ;**** Return Programmer Type **** w4: cpi u_data,0x70 ; 'p' Return programmer type brne w5 ldi u_data,0x50 ; putc('P'); // parallel programmer rcall putc rjmp waitcmd ;**** Set LED **** w5: cpi u_data,0x78 ; 'x' Set LED (ignored) brne w6 rjmp put_ret ;**** Clear LED **** w6: cpi u_data,0x79 ; 'y' Clear LED (ignored) brne w7 rjmp put_ret ;**** Check if device code selected **** ; We require that the device code be selected before any of the other commands w7: cpi device,S1200C ; if ((device != S1200C) && breq w72 cpi device,S1200D ; (device != S1200D) && breq w72 cpi device,S8515A ; (device != S8515A) && breq w72 cpi device,S4414A ; (device != S4414A) && breq w72 cpi device,S2313A ; (device != S2313A) && breq w72 cpi device,S01838C ; (device != S01838C) && breq w72 cpi device,S01838D ; (device != S01838D) && breq w72 cpi device,S2323A ; (device != S2323A)) breq w72 rjmp put_err ; goto put_err(); ;**** Load Address **** w72: cpi u_data,0x41 ; 'A' Load address brne w8 rcall getc ; addrh = getc(); mov addrh,u_data rcall getc ; addrl = getc(); mov addrl,u_data rjmp put_ret ; goto reply(); ;**** Write Program Memory, High Byte **** w8: cpi u_data,0x43 ; 'C' Write program memory, high byte brne w9 rcall getc ldi s_data,0x48 ; wrser(0x48); // SPI write (byte 1) rcall wrser mov s_data,addrh ; wrser(addrh); // SPI write (byte 2) rcall wrser mov s_data,addrl ; wrser(addrl); // SPI write (byte 3) rcall wrser cpi device,S01838C ; invert data on 01838 rev C! brne w81b com u_data w81b: mov s_data,u_data ; wrser(u_data); // SPI write (byte 4) rcall wrser cpi device,S01838C ; no delay for S01838 (Uses Page Write Mode instead) breq w82 cpi device,S01838D breq w82 ldi temp1,0x20 ; delay(0x20); // 24585 cycles delay rcall delay ; // Page mode requires no delay! w82: ldi temp1,0x01 ; Auto increment address !!!! clr temp2 add addrl,temp1 adc addrh,temp2 rjmp put_ret ; goto reply(); ;**** Write Program Memory, Low Byte **** w9: cpi u_data,0x63 ; 'c' Write program memory, low byte brne w11 rcall getc ldi s_data,0x40 ; wrser(0x40); // SPI write (byte 1) rcall wrser mov s_data,addrh ; s_data = addrh; rcall wrser ; wrser(s_data); // SPI write (byte 2) mov s_data,addrl ; wrser(addrl); // SPI write (byte 3) rcall wrser cpi device,S01838C ; invert data on 01838 rev C! brne w91b com u_data w91b: mov s_data,u_data ; wrser(u_data); // SPI write (byte 4) rcall wrser cpi device,S01838C ; no delay for S01838 (Uses Page Write Mode instead) breq w92 cpi device,S01838D breq w92 ldi temp1,0x20 ; delay(0x20); // 24585 cycles delay rcall delay ; // Page mode requires no delay! w92: rjmp put_ret ; goto reply(); ;**** Write Data Memory **** w11: cpi u_data,0x44 ; 'D' Write data memory brne w16 rcall getc ldi s_data,0xc0 rcall wrser mov s_data,addrh rcall wrser mov s_data,addrl rcall wrser mov s_data,u_data rcall wrser ldi temp1,0x20 rcall delay rjmp put_ret ;**** Write Lock Bits **** w16: cpi u_data,0x6c ; 'l' Write lock bits brne w20 rcall getc ldi s_data,0xac rcall wrser mov s_data,u_data andi s_data,0x06 ori s_data,0xe0 rcall wrser ldi s_data,0x00 rcall wrser ldi s_data,0x00 rcall wrser ldi temp1,0x30 rcall delay ;Do parallel write lock bits too, just in case serial programming or ; RC oscillator are disabled. rcall ENTER_PAR_PROG_MODE rcall PAR_WR_LOCK_BITS rcall LEAVE_PAR_PROG_MODE rjmp put_ret ;**** Write Fuse Bits **** w20: cpi u_data,0x66 ; 'f' Write fuse bits brne w12 rcall getc ;The only way to write fuse bits is in parallel mode rcall ENTER_PAR_PROG_MODE rcall WR_FUSE_BITS rcall LEAVE_PAR_PROG_MODE rjmp put_ret ;**** Read Program Memory **** w12: cpi u_data,0x52 ; 'R' Read program memory brne w13 ; ldi s_data,0x28 ; wrser(0x28); // SPI write (byte 1) rcall wrser mov s_data,addrh ; s_data = addrh; rcall wrser ; wrser(s_data); // SPI write (byte 2) mov s_data,addrl ; wrser(addrl); // SPI write (byte 3) rcall wrser rcall rdser ; putc(rdser()); // Send data (byte 4) mov u_data,s_data rcall putc ldi s_data,0x20 ; wrser(0x20); // SPI write (byte 1) rcall wrser mov s_data,addrh ; wrser(addrh); // SPI write (byte 2) rcall wrser mov s_data,addrl ; wrser(addrl); // SPI write (byte 3) rcall wrser rcall rdser ; putc(rdser()); // Send data (byte 4) mov u_data,s_data rcall putc ldi temp1,0x01 ; Auto increment address !!!! clr temp2 add addrl,temp1 adc addrh,temp2 rjmp waitcmd ; goto waitcmd(); ;;**** Read Data Memory **** w13: cpi u_data,0x64 ; 'd' Read data memory brne w10 ldi s_data,0xa0 ; wrser(0xA0); // SPI write (byte 1) rcall wrser mov s_data,addrh ; s_data = addrh; rcall wrser ; wrser(s_data); // SPI write (byte 2) mov s_data,addrl ; wrser(addrl); // SPI write (byte 3) rcall wrser rcall rdser ; putc(rdser()); // Send data (byte 4) mov u_data,s_data rcall putc rjmp waitcmd ; goto waitcmd(); ;**** Enter Programming Mode **** w10: cpi u_data,0x50 ; 'P' Enter programming mode brne w14 rcall spiinit ; spiinit(); rjmp put_ret ;**** Leave Programming Mode **** w14: cpi u_data,0x4c ; 'L' Leave programming mode brne w15 out DDRB,zero ; Set Port B as all inputs sbi PORTD,NOT_RST_PDN ; set RESET = 1 rjmp put_ret ;**** Chip Erase **** w15: cpi u_data,0x65 ; 'e' Chip erase brne w17 ldi s_data,0xac rcall wrser ldi s_data,0x80 rcall wrser ldi s_data,0x04 rcall wrser ldi s_data,0x00 rcall wrser ldi temp1,0x30 rcall delay ;Do parallel chip erase too, just in case serial programming or ; RC oscillator are disabled. rcall ENTER_PAR_PROG_MODE rcall PAR_CHIP_ERASE rcall LEAVE_PAR_PROG_MODE rjmp put_ret ;**** Read Signature Bytes **** w17: cpi u_data,0x73 ; 's' Read signature bytes brne w18 ldi param1,0x02 rcall w17call ldi param1,0x01 rcall w17call ldi param1,0x00 rcall w17call rjmp waitcmd w17call:ldi s_data,0x30 rcall wrser ldi s_data,0x00 rcall wrser mov s_data,param1 rcall wrser rcall rdser mov u_data,s_data rcall putc ret ;**** Write Program Memory Page **** w18: cpi u_data,0x6D ; 'm' Write Program Memory Page brne w21 ldi s_data,0x4c ; wrser(0x4c); // SPI write (byte 1) rcall wrser mov s_data,addrh ; wrser(addrh); // SPI write (byte 2) rcall wrser mov s_data,addrl ; wrser(addrl); // SPI write (byte 3) rcall wrser ldi s_data,0x00 ; wrser(0x00); // SPI write (byte 4) rcall wrser ldi temp1,0xff ; delay(0xFF); // 0x20 = 24585 cycles delay rcall delay rjmp put_ret ;**** Universal Command **** Commented out to fit parallel programming code ;w19: cpi u_data,0x3A ; ':' Universal Command ; brne w20 ; rcall getc ; mov cmd1,u_data ; rcall getc ; mov cmd2,u_data ; rcall getc ; mov cmd3,u_data ; rcall universal ; ldi temp1,0xff ; delay(0xFF); // 0x20 = 24585 cycles delay ; rcall delay ; rjmp put_ret ;universal: ; mov s_data,cmd1 ; rcall wrser ; mov s_data,cmd2 ; rcall wrser ; mov s_data,cmd3 ; rcall wrser ; rcall rdser ; mov u_data,s_data ; rcall putc ; ret ;**** Read Fuse and Lock Bits **** w21: cpi u_data,0x46 ; 'F' Read fuse and lock bits brne w99 ;The only way to read fuse and lock bits is in parallel mode rcall ENTER_PAR_PROG_MODE rcall RD_FUSE_LOCK_BITS rcall LEAVE_PAR_PROG_MODE rcall putc rjmp waitcmd ;If we had the code space: ;We could use $80 as a command for correcting the UART bit rate, N. ;It may need correcting since we are using the RC oscillator ;whose frequency is temperature and supply voltage dependent. ;N would need to be stored in EEPROM. ;If $80 is received as $00 then incr N by 4 ;If $80 is received as $CO or greater then decr N by 3. ;If received as $80, do nothing. ;One might be able to send an $80 from a terminal program ;by setting it to 7 bits and odd parity and typing Ctrl-@, ;or maybe by typing Alt 200. w99: ;**** Command Error **** put_err:ldi u_data,0x3f ; putc('?'); \\ send '?' rcall putc rjmp waitcmd ;**** Reply Command **** put_ret:ldi u_data,0x0d ; putc(0x0D); \\ send CR rcall putc rjmp waitcmd ;**** End of File **** .ESEG .db "http://users.bigpond.net.au/d.keenan"