A simple I2C protocol for 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 | |
Here is the code communicating with the DAC:
I wrote up a quick tutorial: