Introduction to I2C with the C8051F410

For my electronics class, my project required communicating with an I2C peripheral (MCP4725 DAC, adafruit breakout board).

I got stuck trying to use the ‘410 SMBus; example code from the application note is very complex and I could not get this to work. I was able to communicate with the DAC using an Arduino. After observing the Arduino’s I2C implementation using the Saleae logic analyzer, and reading a great tutorial from TI:

http://www.ti.com/lit/an/slva704/slva704.pdf?&ts=1589762906285

I was able to write a very simple I2C protocol for the 8051:

; simple I2C protocol for C8051F410 used LAoE bitflip.a51 as a starting point
; https://learningtheartofelectronics.com/program-listings/silabs-code/lab-c1/
$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
ACALL SET_VOLTAGE_TO_5V ; Comment/uncomment for testing
//ACALL SET_VOLTAGE_TO_1V
//ACALL SET_VOLTAGE_TO_0V
SJMP MAIN
; These small set_voltage functions demonstrate a how to send a complete I2C frame
; Start
; Load accumulator with value, then send byte
; repeat if needed
; Stop
SET_VOLTAGE_TO_5V: ; as advertised, sets the DAC to max 5V
ACALL I2C_START
MOV A, #0C4h
ACALL I2C_SEND_BYTE
MOV A, #040h
ACALL I2C_SEND_BYTE
MOV A, #0FAh
ACALL I2C_SEND_BYTE
MOV A, #00h
ACALL I2C_SEND_BYTE
LCALL I2C_STOP
LCALL DELAY
RET
SET_VOLTAGE_TO_0V:
ACALL I2C_START
MOV A, #0C4h
ACALL I2C_SEND_BYTE
MOV A, #040h
ACALL I2C_SEND_BYTE
MOV A, #00h
ACALL I2C_SEND_BYTE
MOV A, #00h
ACALL I2C_SEND_BYTE
LCALL I2C_STOP
LCALL DELAY
RET
SET_VOLTAGE_TO_1V:
ACALL I2C_START
MOV A, #0C4h
ACALL I2C_SEND_BYTE
MOV A, #040h
ACALL I2C_SEND_BYTE
MOV A, #32h
ACALL I2C_SEND_BYTE
MOV A, #00h
ACALL I2C_SEND_BYTE
LCALL I2C_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
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
view raw I2C 8051.a51 hosted with ❤ by GitHub

Here is the code communicating with the DAC:

(TOP) SCL, (BOTTOM) SDA

I wrote up a quick tutorial: