This tutorial shows how to use the MCP23017 with an 8051 chip (C8051F410 Silicon Labs) with I2C protocol.
I was wondering how the adafruit I2C LCD (https://www.adafruit.com/product/772) worked with only 2 pins. They use an MCP23017 chip. Looked it up, its an IO port expander.
Looked pretty useful so I ordered a couple. Works great with pin hungry devices! I liked this project, gave a chance to learn:
1) I2C
2) MCP23017 port expander
3) 7-segment display
4) Special indexed addressing mode on ‘8051: MOVC A, @A+PC for pulling data from table!
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 MCP23017 I/O Port expander | |
; I used LAoE bitflip.a51 as a starting point from Tom C. Hayes, Learning the art of electronics | |
; 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 P2.0 ; clock | |
SDA EQU P2.1 ; data | |
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 SCL ; initialize with SCL, SDA high | |
SETB SDA | |
MAIN: | |
MOV R1, #00h ; R1 holds value | |
MOV R2, #00h ; R2 holds the address (DIRA) | |
ACALL EXPANDER_WRITE | |
LCALL DELAY | |
LCALL DELAY | |
LCALL DELAY | |
MOV R1, #00h ; R1 holds value | |
MOV R2, #12h ; R2 holds the address (GPIOA) | |
ACALL EXPANDER_WRITE ; Call the write function | |
LCALL DELAY | |
LCALL DELAY | |
LCALL DELAY | |
MOV R1, #00h ; R1 holds value | |
MOV R2, #01h ; R2 holds the address (DIRB) | |
ACALL EXPANDER_WRITE | |
LCALL DELAY | |
LCALL DELAY | |
LCALL DELAY | |
MOV R1, #00h ; R1 holds value | |
MOV R2, #13h ; R2 holds the address (GPIOB) | |
ACALL EXPANDER_WRITE ; Call the write function | |
LCALL DELAY | |
LCALL DELAY | |
LCALL DELAY | |
MOV R1, #00h ; R1 holds value | |
MOV R2, #00h ; R2 holds the address (DIRA) | |
ACALL EXPANDER_WRITE_2 | |
LCALL DELAY | |
LCALL DELAY | |
LCALL DELAY | |
MOV R1, #00h ; R1 holds value | |
MOV R2, #12h ; R2 holds the address (GPIOA) | |
ACALL EXPANDER_WRITE_2 ; Call the write function | |
LCALL DELAY | |
LCALL DELAY | |
LCALL DELAY | |
MOV R1, #00h ; R1 holds value | |
MOV R2, #01h ; R2 holds the address (DIRB) | |
ACALL EXPANDER_WRITE_2 | |
LCALL DELAY | |
LCALL DELAY | |
LCALL DELAY | |
MOV R1, #00h ; R1 holds value | |
MOV R2, #13h ; R2 holds the address (GPIOB) | |
ACALL EXPANDER_WRITE_2 ; Call the write function | |
LCALL DELAY | |
LCALL DELAY | |
LCALL DELAY | |
CLR A | |
MOV R3, #00h | |
MAIN_LOOP: | |
ACALL GETCODE | |
MOV R1, A;//#01011110b | |
MOV R2, #12h | |
ACALL EXPANDER_WRITE | |
LCALL DELAY | |
LCALL DELAY | |
ACALL GETCODE | |
MOV R1, A;//#01011110b | |
MOV R2, #12h | |
ACALL EXPANDER_WRITE_2 | |
LCALL DELAY | |
LCALL DELAY | |
ACALL GETCODE | |
MOV R1, A;//#01011110b | |
MOV R2, #13h | |
ACALL EXPANDER_WRITE | |
LCALL DELAY | |
LCALL DELAY | |
ACALL GETCODE | |
MOV R1, A;//#01011110b | |
MOV R2, #13h | |
ACALL EXPANDER_WRITE_2 | |
LCALL DELAY | |
LCALL DELAY | |
LCALL LONG_DELAY | |
INC R3 | |
CJNE R3, #10h, MAIN_LOOP | |
MOV R3, #00h | |
SJMP MAIN_LOOP | |
; EEPROM WRITE AND READ FUNCTIONS | |
EXPANDER_WRITE: | |
ACALL I2C_START | |
MOV A, #40h | |
ACALL I2C_SEND_BYTE | |
MOV A, R2 | |
ACALL I2C_SEND_BYTE | |
MOV A, R1 | |
ACALL I2C_SEND_BYTE | |
LCALL I2C_STOP | |
LCALL DELAY | |
RET | |
EXPANDER_WRITE_2: | |
ACALL I2C_START | |
MOV A, #42h | |
ACALL I2C_SEND_BYTE | |
MOV A, R2 | |
ACALL I2C_SEND_BYTE | |
MOV A, R1 | |
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 | |
; 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 | |
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 | |
DELAY: | |
MOV R7, #00h | |
WAIT: | |
DJNZ R7, WAIT | |
RET | |
LONG_DELAY: | |
MOV R6, #50h | |
INNER: | |
MOV R7, #00 | |
LONG_WAIT: | |
DJNZ R7, LONG_WAIT | |
DJNZ R6, INNER | |
RET | |
GETCODE: MOV A, R3 | |
ADD A, #01 | |
MOVC A, @A+PC ; that we must read beyond | |
RET | |
DB 00111111b ; 3F | |
DB 00000110b ; 06 | |
DB 01011011b ; 5B | |
DB 01001111b ; 4F | |
DB 01100110b | |
DB 01101101b | |
DB 01111101b | |
DB 00000111b | |
DB 01111111b | |
DB 01101111b | |
DB 01110111b | |
DB 01111100b | |
DB 00111001b | |
DB 01011110b | |
DB 01111001b | |
DB 01110001b | |
END |