Full Version : Read & Write EEPROM DIRVERS (ASM)
avr >>ASSMBLER ROUTINES >>Read & Write EEPROM DIRVERS (ASM)


AVR_Admin- 04-28-2006
Simple EEPROM Driver for AVR Processors with RAM Stack by Dick Cappel


I had been putting off writing a driver for some 24LC64 and 24LC256 EEPROMS I was thinking about using, when I came across a temperature logger application written by Seal Ellis. The Temperature logger was posted on on www.avrfreaks.net on January 10, 2002, project ID 49. The original application was written for the ATtiny15, whichhas a small hardware stack, so when extracting the I2C code, I massaged it a little bit to make it more compact when running on processors equipped with a RAM stack while making it a little easier to customize for new applications. The block write was omitted. If you need higher writing throughput, have a look at the original code on www.avrfreaks.net.

The resulting code was run in a small test program in an AT90S2313.

Customiztion and use Pull-up resistors are required on SDA and SCL lines. See the I2C spec. for details.

The subroutine, I2CInit, needs to be called during initialization, preferable after the I/O ports are initialized but before calling any I2C read or write routines. I2CInit will set up the I2C I/O pins and initialize the bus.

The EEPROM code needs to be customized to deal with the I/O pins being used and the processor clock speed in each particular application. .All constants and registers that need to be modified are found in the I2C Memory Driver section. Check and if necessary, change the I/O assignments to I2CPORT, I2CDDR, I2CPIN, bSDA, and bSCL in the I2C Memory Driver sect

Delays to accommodate various bus speed and processor clock rates are controlled by the constant I2CDelayConstant.

The device address constant (ADDR24LC64 in this case) needs to match that of the EEPROM being used. The 24LC64, 24LC128. and 24LC256 use address A0 when their address pins are tied low.

There are two main subroutines of interest.

WriteI2Mem writes the byte contained in register "I2WData" into the EEPROM location pointed to by ZH,ZL.

ReadI2Mem reads the byte at the EEPROM location pointed to by ZH,ZL and returns the byte in register "temp".

There are no provision within the subroutines to check for invalid addresses.

The code , as far as I know, is in the public domain.

AVR_Admin- 04-28-2006
CODE

.include "2313def.inc"

;Pull-up resistors are required on SDA and SCL lines. See the I2C spec. for details.

;The subroutine, I2CInit, needs to be called during initialization, preferable after the I/O
;ports are initialized. I2CInit will set up the I2C I/O pins and initialize the bus.

;The EEPROM code needs to be customized to deal with the I/O pins being used and the
;processor clock speed. All constants and registers that need to be modified are found
;in the I2C Memory Driver section. Check and if necessary, change usage of I2CPORT, I2CDDR,
;and I2CPIN in the I2C routines if necessary. Also change the values of bSDA and bSCL to
;agree with the I/O bits used for SDA and SCL respectively.

;Delays to accommodate various bus speed and processor clock rates are controlled by
;the constant I2CDelayConstant.

;The device address constant (ADDR24LC64 in this case) needs to match that of the
;EEPROM being used. The 24LC64 and 24LC256 use address A0 when their address pins are tied low.



;There are two main subroutines of interest.

;WriteI2Mem writes the byte contained in register "I2WData" into the EEPROM location
;pointed to by ZH,ZL.

;ReadI2Mem reads the byte at the EEPROM location pointed to by ZH,ZL and returns the byte in
;register "temp".




;**************Start I2C Memory Driver Constants and Registers
;I2C routines written  by sellin (Sean Ellis)
;temp and I2WDtata must be high registers
.def DCounter = r2; Used for counting loops. Can be used for other things in other routines
.def temp  = r16; Return value from functions. Can be used for other things in other rotuines
.def I2WData  = r17; Write data for EEPROM reoutines


; I2C Device addressing
.equ ADDR24LC64 = 0xA0; 'LC64, 'LC128, and 'LC256 address byte (1010 000R)
.equ I2CREAD  = 0x01; read/write bit = read
.equ I2CWRITE = 0x00; read/write bit = write
.equ I2CDelayConstant=$04; Constant for delay loop- change as function of uC clock frequency

; IO Port Bits
.equ bSDA  = 6;* SET THIS The bit on the I/O Port used for SDA
.equ bSCL  = 5;* SET THIS The bit on the I/O Port used for SCL
.equ mSDA  = (1<<bSDA);Calculate binary value corresponding to I/O port bit for masking
.equ mSCL  = (1<<bSCL);Calculate binary value corresponding to I/O port bit for masking

.equ I2CPORT  = PORTD;*Set to correspond to I/O Port used for I2C
.equ I2CDDR   = DDRD;*Set to correspond to I/O Port used for I2C
.equ I2CPIN   = PIND;*Set to correspond to I/O Port used for I2C

;**************End I2C Memory Driver Constants and Registers


.equ clock = 4000000;* Set for uC clock frequency
.equ baudrate = 9600 ;choose a baudrate
.equ baudconstant = (clock/(16*baudrate))-1




;D0 USED FOR UART RECEIVE
;D1 USED FOR UART TRANSMIT
;D2
;D3
;D4
;D5 SCA via firmware
;D6 SDA via firmware
;D7 The AT90S2313 does not have a D7 I/O port.

;UART baud rate calculation


.cseg  
.ORG $0000   ;Reset
rjmp start

start:
 
ldi r16,RAMEND ;Init Stack Pointer
out spl,r16

ldi temp,$FF ;All I/O inputs with pasive pullups
out PORTB,temp
out PORTD,temp
ldi temp,$00
out DDRB,temp
out DDRD,temp


 ldi     temp,baudconstant    
 out     ubrr,temp      ;Load baud rate for UART.
 sbi ucr,rxen ;Enable WAUT receive
 sbi ucr,txen ;Enable UART Transmit


rcall I2CInit  ; Initialize I2C bus


rjmp main


recvchar:     ;Receive a byte from the terminal into temp        
sbis usr,rxc  ;Wait for byte to be received
rjmp  recvchar
in temp,udr ;Read byte
ret

emitchar:;Send character contained in temp
 sbis usr,udre   ;wait until the register is cleared.
 rjmp emitchar    
 out udr,temp ;send the character.
 ret

;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
;-----------------------------------------------------------------
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
;  START I2C ROUTINES
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
;-----------------------------------------------------------------
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

I2CDelay:
push temp
ldi temp,I2CDelayConstant
delaymore:
dec temp
brne delaymore
pop temp
ret
;-----------------------------------------------------------------
; I2CInit
; Initialize I2C interface, ports, etc.
I2CInit:
; Set up port assignments
ldi temp,mSCL ; SCL only output by default
out I2CDDR,temp  
out I2CPORT,temp ; All outputs high, input pullups disabled

cbi I2CPORT,bSDA; Data line always drives low if driven
cbi I2CDDR,bSDA; SDA as input to start with - floats high
cbi I2CPORT,bSCL; Set SCL low to try and avoid...
sbi I2CDDR,bSCL; ...false start transitions
rcall I2CDelay
rjmp I2CStop ; Stop any erroneous transfer
;-----------------------------------------------------------------
; I2CStart
; Send I2C start condition
; Assumes SCL and SDA are high to start with.
; Leaves SCL and SDA low
I2CStart:
rcall I2CDelay
sbi I2CDDR,bSDA; Drive data line low (start bit)
rcall I2CDelay
cbi I2CPORT,bSCL; Clock line low (ready to start)
ret
;-----------------------------------------------------------------
; I2CStop
; Send I2C stop condition
; Assumes SCL is low to start with, SDA may be in either state
; Leaves SCL and SDA high
I2CStop:
sbi I2CDDR,bSDA; SDA driven low
rcall I2CDelay
sbi I2CPORT,bSCL; Clock line high (ready to stop)
rcall I2CDelay
cbi I2CDDR,bSDA; Data line floats high (stop bit)
rcall I2CDelay
ret
;-----------------------------------------------------------------
ReadI2Mem:; Reads external memory at Z to temp
rcall I2CStart
ldi I2WData,(ADDR24LC64|I2CWRITE)
rcall I2CSendAddress
brne ReadI2MemError; No ack!
mov I2WData,ZH
rcall I2CSendByte
mov I2WData,ZL
rcall I2CSendByte
rcall I2CStop
rcall I2CDelay
rcall I2CStart
ldi I2WData,(ADDR24LC64|I2CREAD)
rcall I2CSendAddress
brne ReadI2MemError; No ack!
ldi I2WData,1; Don't send ack
rcall I2CReadByte
ret  
ReadI2MemError:
rcall I2CStop
clr DCounter
ReadI2MemErrorDelay:
dec DCounter
brne ReadI2MemErrorDelay
rjmp ReadI2Mem; and try again
;-----------------------------------------------------------------
WriteI2Mem:;  Writes I2WData to external memory at Z
mov temp,I2WData; save value
rcall I2CStart
ldi I2WData,(ADDR24LC64|I2CWRITE)
rcall I2CSendAddress
brne WriteI2MemError; No ack!
mov I2WData,ZH
rcall I2CSendByte
mov I2WData,ZL
rcall I2CSendByte
mov I2WData,temp
rcall I2CSendByte
rcall I2CStop
ret

WriteI2MemError:
rcall I2CStop
clr DCounter
WriteI2MemErrorDelay:
dec DCounter
brne WriteI2MemErrorDelay
mov I2WData,temp ; restore data value
rjmp WriteI2Mem; and try again
;-----------------------------------------------------------------
; I2CReadByte  Read a byte from the I2C bus and acknowledge
; If I2WData is zero, acknowledge is sent, otherwise not.
; Byte is returned in temp
; Leaves SCL low, I2C bus not driven.
I2CReadByte:
ldi temp,8
mov DCounter,temp
clr temp
cbi I2CDDR,bSDA; release bus (floats high)
I2CReadBits:
sbi I2CPORT,bSCL; clock high
rcall I2CDelay
add temp,temp
sbic I2CPIN,bSDA; if SDA clear, skip increment
inc temp
cbi I2CPORT,bSCL; clock low
rcall I2CDelay
dec DCounter
brne I2CReadBits
tst I2WData ; Send an ACK...?
brne I2CReadBitsNack
sbi I2CDDR,bSDA; yes... drive data bus low
I2CReadBitsNack:
sbi I2CPORT,bSCL; clock high
rcall I2CDelay
cbi I2CPORT,bSCL; clock low
rcall I2CDelay
cbi I2CDDR,bSDA; release bus (floats high)
ret
;-----------------------------------------------------------------------------------------------------------------
; I2CSendByte
; Sends 8 bits of data from I2WData and listens for an ACK at the
; end. Returns Z if ack received, else NZ.
; Assumes a start condition has already been sent, and SDA and SCL
; are low Leaves SCL low, SDA not driven. Uses DCounter and I2WData.
I2CSendAddress:
I2CSendByte:
push temp
ldi temp,8
mov DCounter,temp
pop temp
I2CSendBits:
add I2WData,I2WData
brcc I2CSendBits0
cbi I2CDDR,bSDA; send 1 (floats high)
rjmp I2CSendBits1
I2CSendBits0:
sbi I2CDDR,bSDA; send 0 (drives low)
I2CSendBits1:
rcall I2CDelay
sbi I2CPORT,bSCL; clock high
rcall I2CDelay
cbi I2CPORT,bSCL; clock low
dec DCounter
brne I2CSendBits
cbi I2CDDR,bSDA; stop driving data bus
rcall I2CDelay
sbi I2CPORT,bSCL; clock high
rcall I2CDelay
in I2WData,I2CPIN; sample data bus
cbi I2CPORT,bSCL; clock low
rcall I2CDelay
andi I2WData,mSDA; check ack bit (z set if OK)
ret
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
;-----------------------------------------------------------------
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
;  END I2C ROUTINES
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
;-----------------------------------------------------------------
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx



;DEMONSTRATION PROGRAM
;Writes carriage return, linefeed, and asterisk to terminal,
;Then echos characters back to terminal as it writes each character to the
;EEPROM. When a dollar sign is encountered, it is treated as an end of file
;indicator and stored in memory, then the contents of the memory is sent to the
;terminal until the dollar sign is reached, at which time the program startrs over.
;If the first character received from the terminal at the start of the program is
;a dollar sign, the contents of the EEPROM will be dumped up to the first dollar sign
;stored in EEPROM.


Main:  
ldi temp,$0A ;Send return, linefeed, asterisk
rcall emitchar
ldi temp,$0D
rcall emitchar
ldi temp,$2A
rcall emitchar

clr ZH  ;Clear Z register (EEPROM pointer)
clr ZL

MainLoop:

rcall recvchar ;Get char from terminal

cpi temp,$24   ;dump if its a dollar sign $
breq dumpsome

push temp  ;Save char on return stack

mov I2WData,temp ;Write char to EEPROM
rcall WriteI2Mem
adiw ZL,1  ;Increment ZH,ZL for chips that support adiw instruction

pop temp  ;Retrieve char
rcall emitchar ;Echo char to the terminal

rjmp MainLoop ;Go back and do it again

dumpsome:
cpi ZH,0  ;If ZH,ZL = 00 00, then dont' write dollar sign
brne zwrite
cpi ZL,0
breq nowriteds ;Don't write a dollar sign if this is EEPROM location 00 00

zwrite:
mov I2WData,temp ;Write the dollar sign $ in temp that brought us here
rcall WriteI2Mem
adiw ZL,1  ;Increment ZH,ZL for chips that support adiw instruction
nowriteds:

clr ZH  ;Dump EEPROM contents until dollar sigsn is encountered
clr ZL

dumpsomemore:
rcall ReadI2Mem
adiw ZL,1  ;Increment ZH,ZL for chips that support adiw instruction
rcall emitchar
cpi temp,$24
breq Main
rjmp dumpsomemore

.exit


Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.