NAME Control_AT24Cxx ; This collection of routines allows an AT89C2051 microcontroller to read ; and write the AT24Cxx family of serial CMOS EEPROMS. This version of the ; code is compatible only with the AT89C2051 due to the location of the ; data buffer and stack in RAM. The code may be modified to work with the ; AT89C1051 by relocating or resizing the buffer and stack to fit into the ; smaller amount of RAM available in the AT89C1051. Note that the minimum ; size of the buffer is determined by the page size of the AT24Cxx. ; ; All five AT24Cxx device operations are supported. Byte Write, Current ; Address Read and Random Read are implemented by the functions WRITE_BYTE, ; READ_CURRENT and READ_RANDOM, respectively. Page Write and Sequential Read ; are special cases of functions WRITE_BLOCK and READ_BLOCK, respectively. ; WRITE_BLOCK and READ_BLOCK process from one byte to the number of bytes ; in a page and transfer data between the AT24Cxx and the RAM buffer. ; ; The code supports multiple AT24Cxx devices per bus, each with a unique ; address determined by the wiring of the address pins (A0, A1, A2). The ; three-bit programmable address is passed to the WRITE_BYTE, READ_CURRENT, ; READ_RANDOM, WRITE_BLOCK and READ_BLOCK functions, where it is combined ; with the AT24Cxx fixed address (FADDR) and used to address a device on the ; bus. Refer to the AT24Cxx family data sheets for additional information on ; device addressing. ; ; Functions BYTE_FILL, VERIFY_BYTE_FILL, PAGE_FILL and VERIFY_PAGE_FILL are ; artifacts from the debug process and serve to illustrate the use of the ; device read and write functions with an AT24C64. To modify the code for a ; different member of the AT24Cxx family, simply redefine the values of SIZE ; (the number of bytes per device) and PSIZE (the number of bytes per page). ; To change the fill value, redefine FILL. To change the programmable portion ; of the device address, redefine PADDR to a value from zero to seven. ; ; The code meets all AT24Cxx family timing requirements when executed by an ; AT89Cx051 microcontroller with a 12 MHz clock. Code modifications may be ; required if a faster clock is substituted. FADDR EQU 0a0h ; fixed address for AT24Cxx EEPROMs PADDR EQU 0 ; programmable address (0..7) SIZE EQU 2000h ; bytes per AT24C64 PSIZE EQU 32 ; bytes per page for AT24C64 FILL EQU 55h ; example fill value ; Register definitions. index EQU r0 ; buffer pointer kount EQU r1 ; byte count register zdata EQU r1 ; data register addr_lo EQU r2 ; 2-byte address register addr_hi EQU r3 ; ; Microcontroller connections to AT24Cxx serial bus lines. SCL BIT p1.2 ; serial clock SDA BIT p1.3 ; serial data DSEG AT 20H ORG 40H buffer: DS PSIZE ; storage for read/write data ORG 60H ; stack origin stack: DS 20H ; stack depth CSEG ORG 0000H ; power on/reset vector jmp on_reset ORG 0003H ; external interrupt 0 vector reti ; undefined ORG 000BH ; timer 0 overflow vector reti ; undefined ORG 0013H ; external interrupt 1 vector reti ; undefined ORG 001BH ; timer 1 overflow vector reti ; undefined ORG 0023H ; serial I/O interrupt vector reti ; undefined ORG 0080H ; begin code space USING 0 ; register bank zero on_reset: mov sp, #(stack-1) ; initialize stack pointer ; Initialize AT24Cxx serial bus lines. setb SDA ; high setb SCL ; high call byte_fill jc fault call verify_byte_fill jc fault call page_fill jc fault call verify_page_fill jc fault fault: jmp $ byte_fill: ; Fill every byte in an AT24Cxx with the same value. ; Writes one address at a time (page mode is not used). ; Returns CY set to indicate write timeout. ; Destroys A, B, DPTR, XDATA, ADDR_HI:ADDR_LO. mov zdata, #FILL ; set up fill data mov dptr, #0 ; initialize address pointer x51: mov addr_lo, dpl ; set up address mov addr_hi, dph ; mov b, #120 ; retry counter x52: mov a, #PADDR ; programmable address call write_byte ; try to write jnc x53 ; jump if write OK djnz b, x52 ; try again setb c ; set timeout error flag jmp x54 ; exit x53: inc dptr ; advance address pointer ; mov a, dpl ; check low byte ; cjne a, #(LOW SIZE), x51 ; jump if not last mov a, dph ; check high byte cjne a, #(HIGH SIZE), x51 ; jump if not last clr c ; clear error flag x54: ret verify_byte_fill: ; Verify that all bytes in an AT24Cxx match a fill value. ; Reads and verifies one byte at a time (page mode is not used). ; Performs a Random Read function to initialize the internal ; address counter and checks the contents of the first address. ; Then performs multiple Current Address Read functions to step ; through the remaining addressess. ; Returns CY set to indicate read timeout or compare fail. ; Destroys A, B, DPTR. mov dptr, #0 ; initialize address pointer/counter mov addr_lo, dpl ; set up address mov addr_hi, dph ; mov b, #120 ; retry counter x81: mov a, #PADDR ; programmable address call read_random ; try to read jnc x82 ; jump if read OK djnz b, x81 ; try again jmp x86 ; set error flag and exit x82: cjne a, #FILL, x86 ; jump if compare error jmp x85 ; do remaining addresses x83: mov a, #PADDR call read_current jc x87 ; jump if read fails cjne a, #FILL, x86 ; jump if compare error x85: inc dptr ; advance address pointer mov a, dph ; check high byte cjne a, #(HIGH SIZE), x83 ; jump if not last clr c ; clear error flag jmp x87 ; exit x86: setb c ; set error flag x87: ret page_fill: ; Fill every byte in an AT24Cxx with the same value. ; Writes one page at a time. ; Returns CY set to indicate write timeout. ; Destroys A, B, DPTR, KOUNT, INDEX, ADDR_HI:ADDR_LO. ; First fill buffer. mov b, #PSIZE ; bytes per page mov index, #buffer ; point to buffer x61: mov @index, #FILL ; put fill value in buffer inc index ; advance pointer djnz b, x61 ; next byte ; Copy buffer to device, one page at a time. mov dptr, #0 ; initialize address pointer x62: mov addr_lo, dpl ; set up address mov addr_hi, dph ; mov kount, #PSIZE ; bytes per page mov b, #120 ; retry counter x63: mov a, #PADDR ; programmable address call write_block ; try to write jnc x64 ; jump if write OK djnz b, x63 ; try again setb c ; set timeout error flag jmp x66 ; exit x64: ; Add page size to address pointer. mov a, dpl ; get low byte add a, #PSIZE ; add page size mov dpl, a ; save low byte jnc x65 ; jump if high byte not affected inc dph ; increment high byte x65: ; cjne a, #(LOW SIZE), x62 ; jump if low byte not last mov a, dph ; check high byte cjne a, #(HIGH SIZE), x62 ; jump if not last clr c ; clear error flag x66: ret verify_page_fill: ; Verify that all bytes in an AT24Cxx match a fill value. ; Reads and verifies one page at a time. ; Returns CY set to indicate read timeout or compare fail. ; Destroys A, B, DPTR, KOUNT, INDEX, ADDR_HI:ADDR_LO. ; Copy device page to buffer. mov dptr, #0 ; initialize address pointer x71: mov addr_lo, dpl ; set up address mov addr_hi, dph ; mov kount, #PSIZE ; bytes per page mov b, #120 ; retry counter x72: mov a, #PADDR ; programmable address call read_block ; try to read jnc x74 ; jump if read OK djnz b, x72 ; try again x73: setb c ; set error flag jmp x77 ; exit x74: ; Verify buffer contents. mov b, #PSIZE ; bytes per page mov index, #buffer ; point to buffer x75: cjne @index, #FILL, x73 ; jump if compare fails inc index ; advance pointer djnz b, x75 ; next byte ; Add page size to address pointer. mov a, dpl ; get low byte add a, #PSIZE ; add page size mov dpl, a ; save low byte jnc x76 ; jump if high byte not affected inc dph ; increment high byte x76: ; cjne a, #(LOW SIZE), x71 ; jump if low byte not last mov a, dph ; check high byte cjne a, #(HIGH SIZE), x71 ; jump if not last clr c ; clear error flag x77: ret write_block: ; Write from one byte to one page of data to an AT24Cxx. ; Called with programmable address in A, address of first byte ; in register pair ADDR_HI:ADDR_LO, data in BUFFER, byte count ; in register KOUNT. ; Does not wait for write cycle to complete. ; Returns CY set to indicate that the bus is not available ; or that the addressed device failed to acknowledge. ; Destroys A, KOUNT, INDEX. call start jc x38 ; abort if bus not available rl a ; programmable address to bits 3:1 orl a, #FADDR ; add fixed address clr acc.0 ; specify write operation call shout ; send device address jc x37 ; abort if no acknowledge mov a, addr_hi ; send high byte of address call shout ; jc x37 ; abort if no acknowledge mov a, addr_lo ; send low byte of address call shout ; jc x37 ; abort if no acknowledge mov index, #buffer ; point to buffer x36: mov a, @index ; get data call shout ; send data jc x37 ; abort if no acknowledge inc index ; advance buffer pointer djnz kount, x36 ; next byte clr c ; clear error flag x37: call stop x38: ret read_block: ; Read from one byte to one page of data from an AT24Cxx. ; Performs a Random Read which is extended into a Sequential Read ; when more than one byte is read. Called with programmable address ; in A, address of first byte in register pair ADDR_HI:ADDR_LO, ; byte count in register KOUNT. ; Returns data in BUFFER. Returns CY set to indicate that the bus is ; not available or that the addressed device failed to acknowledge. ; Destroys A, KOUNT, INDEX. ; Send dummy write command to address first byte. call start jc x35 ; abort if bus not available rl a ; programmable address to bits 3:1 orl a, #FADDR ; add fixed address mov index, a ; save copy of device address clr acc.0 ; specify write operation call shout ; send device address jc x34 ; abort if no acknowledge mov a, addr_hi ; send high byte of address call shout ; jc x34 ; abort if no acknowledge mov a, addr_lo ; send low byte of address call shout ; jc x34 ; abort if no acknowledge ; Send read command and receive data. call start ; second start for read jc x34 ; abort if bus not available mov a, index ; get device address setb acc.0 ; specify read operation call shout ; send device address jc x34 ; abort if no acknowledge mov index, #buffer ; point to buffer x31: call shin ; receive data byte mov @index, a ; save data cjne kount, #1, x32 ; jump if not last byte call NAK ; do not acknowledge last byte jmp x33 ; done x32: call ACK ; acknowledge byte inc index ; advance buffer pointer djnz kount, x31 ; next byte x33: clr c ; clear error flag x34: call stop x35: ret write_byte: ; AT24Cxx Byte Write function. ; Called with programmable address in A, byte address in ; register pair ADDR_HI:ADDR_LO, data in register XDATA. ; Does not wait for write cycle to complete. ; Returns CY set to indicate that the bus is not available ; or that the addressed device failed to acknowledge. ; Destroys A. call start jc x49 ; abort if bus not available rl a ; programmable address to bits 3:1 orl a, #FADDR ; add fixed address clr acc.0 ; specify write operation call shout ; send device address jc x48 ; abort if no acknowledge mov a, addr_hi ; send high byte of address call shout ; jc x48 ; abort if no acknowledge mov a, addr_lo ; send low byte of address call shout ; jc x48 ; abort if no acknowledge mov a, zdata ; get data call shout ; send data jc x48 ; abort if no acknowledge clr c ; clear error flag x48: call stop x49: ret read_current: ; AT24Cxx Current Address Read function. ; Called with programmable address in A. Returns data in A. ; Returns CY set to indicate that the bus is not available ; or that the addressed device failed to acknowledge. call start jc x45 ; abort if bus not available rl a ; programmable address to bits 3:1 orl a, #FADDR ; add fixed address setb acc.0 ; specify read operation call shout ; send device address jc x44 ; abort if no acknowledge call shin ; receive data byte call NAK ; do not acknowledge byte clr c ; clear error flag x44: call stop x45: ret read_random: ; AT24Cxx Random Read function. ; Called with programmable address in A, byte address in ; register pair ADDR_HI:ADDR_LO. Returns data in A. ; Returns CY set to indicate that the bus is not available ; or that the addressed device failed to acknowledge. push b mov b, a ; save copy of programmable address ; Send dummy write command to set internal address. call start jc x47 ; abort if bus not available rl a ; programmable address to bits 3:1 orl a, #FADDR ; add fixed address clr acc.0 ; specify write operation call shout ; send device address jc x46 ; abort if no acknowledge mov a, addr_hi ; send high byte of address call shout ; jc x46 ; abort if no acknowledge mov a, addr_lo ; send low byte of address call shout ; jc x46 ; abort if no acknowledge ; Call Current Address Read function. mov a, b ; get programmable address call read_current jmp x47 ; exit x46: call stop x47: pop b ret start: ; Send START, defined as high-to-low SDA with SCL high. ; Return with SCL, SDA low. ; Returns CY set if bus is not available. setb SDA setb SCL ; Verify bus available. jnb SDA, x40 ; jump if not high jnb SCL, x40 ; jump if not high nop ; enforce setup delay and cycle delay clr SDA nop ; enforce hold delay nop ; nop ; nop ; nop ; clr SCL clr c ; clear error flag jmp x41 x40: setb c ; set error flag x41: ret stop: ; Send STOP, defined as low-to-high SDA with SCL high. ; SCL expected low on entry. Return with SCL, SDA high. clr SDA nop ; enforce SCL low and data setup nop setb SCL nop ; enforce setup delay nop ; nop ; nop ; nop ; setb SDA ret shout: ; Shift out a byte to the AT24Cxx, most significant bit first. ; SCL, SDA expected low on entry. Return with SCL low. ; Called with data to send in A. ; Returns CY set to indicate failure by slave to acknowledge. ; Destroys A. push b mov b, #8 ; bit counter x42: rlc a ; move bit into CY mov SDA, c ; output bit nop ; enforce SCL low and data setup setb SCL ; raise clock nop ; enforce SCL high nop ; nop ; nop ; clr SCL ; drop clock djnz b, x42 ; next bit setb SDA ; release SDA for ACK nop ; enforce SCL low and tAA nop ; setb SCL ; raise ACK clock nop ; enforce SCL high nop ; nop ; nop ; mov c, SDA ; get ACK bit clr SCL ; drop ACK clock pop b ret shin: ; Shift in a byte from the AT24Cxx, most significant bit first. ; SCL expected low on entry. Return with SCL low. ; Returns received data byte in A. setb SDA ; make SDA an input push b mov b, #8 ; bit count x43: nop ; enforce SCL low and data setup nop ; nop ; setb SCL ; raise clock nop ; enforce SCL high nop ; mov c, SDA ; input bit rlc a ; move bit into byte clr SCL ; drop clock djnz b, x43 ; next bit pop b ret ACK: ; Clock out an acknowledge bit (low). ; SCL expected low on entry. Return with SCL, SDA low. clr SDA ; ACK bit nop ; enforce SCL low and data setup nop ; setb SCL ; raise clock nop ; enforce SCL high nop ; nop ; nop ; clr SCL ; drop clock ret NAK: ; Clock out a negative acknowledge bit (high). ; SCL expected low on entry. Return with SCL low, SDA high. setb SDA ; NAK bit nop ; enforce SCL low and data setup nop ; setb SCL ; raise clock nop ; enforce SCL high nop ; nop ; nop ; clr SCL ; drop clock ret END