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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; 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 | |