I2C EEPROM Project with the C8051F410

Trying to learn more about I2C communication. An EEPROM chip is a great way to learn as it supports both read and write functions. The SMBus App Note for the C8051F410 was overly complex to implement so I worked out a bit bang protocol in assembly. The App Note details working with a small EEPROM from Microchip – 24LC02B:

A link to the datasheet:

http://ww1.microchip.com/downloads/en/DeviceDoc/24AA02-24LC02B-24FC02-Data-Sheet-20001709L.pdf

; simple I2C protocol for C8051F410 with EEPROM 24LC02B-I/P-ND from Microchip
; I used LAoE bitflip.a51 as a starting point from
; https://learningtheartofelectronics.com/program-listings/silabs-code/lab-c1/
; demonstrates simple read and write
$NOSYMBOLS ; keeps listing short..
$INCLUDE (C:\MICRO\8051\RAISON\INC\c8051f410.inc)
SCL EQU P0.0 ; clock
SDA EQU P0.1 ; data
BLUE_LED EQU P0.4 ; an LED is always nice
ORG 0 ; tells assembler the address at which to place this code
SJMP STARTUP ; here code begins--with just a jump to start of
; real program. ALL our programs will start thus
ORG 80h ; ...and here the program starts
STARTUP:
ACALL USUAL_SETUP
ACALL IO_SETUP
SETB BLUE_LED
SETB SCL ; initialize with SCL, SDA high
SETB SDA
MAIN: CPL BLUE_LED ; flips LED
MOV R1, #0E1h ; R1 holds value
MOV R2, #00h ; R2 holds the address
ACALL EEPROM_WRITE ; Call the write function
ACALL DELAY ; Need sufficient delay between write cycles!
ACALL DELAY ; Polling would be more efficient
ACALL DELAY ; See data sheet for example!
ACALL DELAY
ACALL DELAY
ACALL DELAY
ACALL DELAY
ACALL DELAY
ACALL DELAY
MOV R1, #23h
MOV R2, #01h
ACALL EEPROM_WRITE
ACALL DELAY
ACALL DELAY
ACALL DELAY
ACALL DELAY
ACALL DELAY
ACALL DELAY
ACALL DELAY
ACALL DELAY
ACALL DELAY
MOV R2, #00h ; R2 holds address
ACALL EEPROM_READ ; Read this address
MOV R7, ACC ; When return, accumultor holds read value
MOV R2, #01h
ACALL EEPROM_READ
MOV R6, ACC
STUCK:
SJMP STUCK ; Stop here - good idea not to repeat writes to wear out EEPROM!
; EEPROM WRITE AND READ FUNCTIONS
EEPROM_WRITE:
ACALL I2C_START
MOV A, #0A0h
ACALL I2C_SEND_BYTE
MOV A, R2
ACALL I2C_SEND_BYTE
MOV A, R1
ACALL I2C_SEND_BYTE
LCALL I2C_STOP
LCALL DELAY
RET
EEPROM_READ:
ACALL I2C_START ; START I2C
MOV A, #0A0h ; SEND WRITE COMMAND
ACALL I2C_SEND_BYTE
MOV A, R2 ; WRITE THE ADDRESS TO THE ADDRESS REG.
ACALL I2C_SEND_BYTE
SETB SCL ; NEED TO SEND ANOTHER START FOR THE SLAVE so reinitialize with SCL, SDA high
SETB SDA
ACALL I2C_START ; 2nd start
MOV A, #0A1h; This is READ
ACALL I2C_SEND_BYTE ; Sends READ command
ACALL I2C_READ_BYTE ; Reads the byte
LCALL I2C_STOP ; STOP
LCALL DELAY
RET
I2C_START: ; start condition
CLR SDA
CLR SCL
RET
I2C_STOP: ; stop condition
CLR SDA
CLR SCL
SETB SCL
SETB SDA
RET
;Send Byte assumess accumulator loaded with byte you want to send.
;Basically, just reads bit 7 and pulses SDA appropriately, along with a clock pulse.
;Then, all bits are shifted and the process is repeated until all 8 bits are sent.
;After the 8 bits, an acknowledgement bit is sent.
I2C_SEND_BYTE:
MOV R0, #08
I2C_SEND_LOOP:
JB ACC.7, GO_HIGH
GO_LOW:
ACALL I2C_LOW
SJMP CONTINUE
GO_HIGH:
ACALL I2C_HIGH
SJMP CONTINUE
CONTINUE:
RL A
DJNZ R0, I2C_SEND_LOOP
I2C_SEND_BYTE_DONE:
ACALL I2C_HIGH //ACK
RET
I2C_HIGH:
SETB SDA ; data high
SETB SCL ; Clock hi edge
SETB SDA ;stay high
CLR SCL ; clear clock
RET
I2C_LOW:
CLR SDA ; data low
SETB SCL ; clock edge hi
CLR SDA ; stay low
CLR SCL ; clear clock
RET
; I2C READ BYTE
; Assumes slave is ready to send data.
; Basically, sends clock pulses and polls SDA and sets/clears ACC.0 depending on value.
; Then, the ACC is left shifted until the byte is read.
; Read byte is left in accumulator upon return from subroutine
I2C_READ_BYTE:
MOV R0, #08
MOV ACC, #00h
I2C_READ_LOOP:
RL A
SETB SCL; clock edge hi
JB SDA, READ_HIGH
READ_LOW:
CLR ACC.0
SJMP CONTINUE_READ
READ_HIGH:
SETB ACC.0
SJMP CONTINUE_READ
CONTINUE_READ:
CLR SCL; clock low
DJNZ R0, I2C_READ_LOOP
I2C_SEND_BYTE_DONE_READ:
ACALL I2C_HIGH //ACK
RET
DELAY:
MOV R0, #20h
WAIT:
DJNZ R0, WAIT
RET
USUAL_SETUP: ; Disable the WDT.
anl PCA0MD, #NOT(040h) ; Clear Watchdog Enable bit
; Enable the Port I/O Crossbar
mov XBR1, #40h ; Enable Crossbar
ret
IO_SETUP: orl P0MDOUT, #01h ; enable P0.0 as push-pull output
RET
END