;****************************************************************************
; Software Implemented I2C Drivers
; These routines allow an 80C51 based microcontroller to drive the I2C bus
; as a single master. The main program at the end demonstrates writing and
; reading several types of devices:
; PCF8570 256 byte static RAM.
; PCF8574 8-bit I/O expander.
; SAA1064 4 digit LED display driver.
; Written by G.Goodhue, Philips Components-Signetics
;****************************************************************************
$Title(I2C Routines for 80C51)
$Date(08/14/90)
$MOD51
$DEBUG
;****************************************************************************
; Definitions
;****************************************************************************
; Addresses of several I2C devices as connected on the Signetics I2C
; Evaluation Board.
I2CRAM EQU 0AEh ;Slave address for PCF8570 RAM chip.
I2CIO EQU 4Eh ;Slave address for PCF8574 I/O expandor.
I2CLED EQU 76h ;Slave address for SAA1064 LED driver.
; Data storage locations
BitCnt DATA 8h ;Bit counter for I2C routines.
ByteCnt DATA 9h ;Byte counter for I2C routines.
SlvAdr DATA 0Ah ;Slave address for I2C routines.
XmtDat DATA 10h ;I2C transmit buffer, 8 bytes max.
RcvDat DATA 18h ;I2C receive buffer, 8 bytes max.
AltRcv DATA 20h ;Alternate I2C receive buffer, 8 bytes max.
Flags DATA 28h ;Location for bit flags
NoAck BIT Flags.0 ;I2C no acknowledge flag.
BusFault BIT Flags.1 ;I2C bus fault flag.
I2CBusy BIT Flags.2 ;I2C busy flag.
; I2C connections.
SCLPin BIT P0.0 ;I2C serial clock line.
SDAPin BIT P0.1 ;I2C serial data line.
;****************************************************************************
; Reset and Interrupt Vectors
;****************************************************************************
ORG 0
AJMP Reset
;****************************************************************************
; Subroutines
;****************************************************************************
ORG 30h
; BitDly - insures minimum high and low clock times on I2C bus.
; This routine must be tuned for the actual oscilator frequency used, shown
; here tuned for a 12MHz clock. Note that the CALL instruction that invokes
; BitDly already uses 2 machine cycles.
BitDly: NOP ;NOPs to delay 5 microseconds (minus 4
; machine cycles for CALL and RET).
RET
; SCLHigh - sends SCL pin high and waits for any clock stretching peripherals.
SCLHigh: SETB SCLPin ;Set SCL from our end.
JNB SCLPin,$ ;Wait for pin to actually go high.
RET
; SendStop - sends an I2C stop, releasing the bus.
SendStop: CLR SDAPin ;Get SDA ready for stop.
ACALL SCLHigh ;Set clock for stop.
ACALL BitDly
SETB SDAPin ;Send I2C stop.
ACALL BitDly
CLR I2CBusy ;Clear I2C busy status.
RET ;Bus should now be released.
; SendByte - sends one byte of data to an I2C slave device.
; Enter with:
; ACC = data byte to be sent.
SendByte: MOV BitCnt,#8 ;Set bit count.
SBLoop: RLC A ;Send one data bit.
MOV SDAPin,C ;Put data bit on pin.
ACALL SCLHigh ;Send clock.
ACALL BitDly
CLR SCLPin
ACALL BitDly
DJNZ BitCnt,SBloop ;Repeat until all bits sent.
SETB SDAPin ;Release data line for acknowledge.
ACALL SCLHigh ;Send clock for acknowledge.
ACALL BitDly
JNB SDAPin,SBEX ;Check for valid acknowledge bit.
SETB NoAck ;Set status for no acknowledge.
SBEX: CLR SCLPin ;Finish acknowledge bit.
ACALL BitDly
RET
; GoMaster - sends an I2C start and slave address.
; Enter with:
; SlvAdr = slave address.
GoMaster: SETB I2CBusy ;Indicate that I2C frame is in progress.
CLR NoAck ;Clear error status flags.
CLR BusFault
JNB SCLPin,Fault ;Check for bus clear.
JNB SDAPin,Fault
CLR SDAPin ;Begin I2C start.
ACALL BitDly
CLR SCLPin
ACALL BitDly ;Complete I2C start.
MOV A,SlvAdr ;Get slave address.
ACALL SendByte ;Send slave address.
RET
Fault: SETB BusFault ;Set fault status
RET ; and exit.
; SendData - sends one or more bytes of data to an I2C slave device.
; Enter with:
; ByteCnt = count of bytes to be sent.
; SlvAdr = slave address.
; @R0 = data to be sent (the first data byte will be the
; subaddress, if the I2C device expects one).
SendData: ACALL GoMaster ;Acquire bus and send slave address.
JB NoAck,SDEX ;Check for slave not responding.
SDLoop: MOV A,@R0 ;Get data byte from buffer.
ACALL SendByte ;Send next data byte.
INC R0 ;Advance buffer pointer.
JB NoAck,SDEX ;Check for slave not responding.
DJNZ ByteCnt,SDLoop ;All bytes sent?
SDEX: ACALL SendStop ;Done, send an I2C stop.
RET
;RcvByte - receives one byte of data from an I2C slave device.
; Returns:
; ACC = data byte received.
RcvByte: MOV BitCnt,#8 ;Set bit count.
RBLoop: ACALL SCLHigh ;Read one data bit.
ACALL BitDly
MOV C,SDAPin ;Get data bit from pin.
RLC A ;Rotate bit into result byte.
CLR SCLPin
ACALL BitDly
DJNZ BitCnt,RBLoop ;Repeat until all bits received.
PUSH ACC ;Save accumulator
MOV A,ByteCnt
CJNE A,#1,RBAck ;Check for last byte of frame.
SETB SDAPin ;Send no acknowledge on last byte.
SJMP RBAClk
RBAck: CLR SDAPin ;Send acknowledge bit.
RBAClk: ACALL SCLHigh ;Send acknowledge clock.
POP ACC ;Restore accumulator
ACALL BitDly
CLR SCLPin
SETB SDAPin ;Clear acknowledge bit.
ACALL BitDly
RET
;RcvData - receives sends one or more bytes of data from an I2C slave device.
; Enter with:
; ByteCnt = count of bytes to be sent.
; SlvAdr = slave address.
; Returns:
; @R0 = data received.
; Note: to receive with a subaddress, use SendData to set the subaddress
; first (no provision for repeated start).
RcvData: INC SlvAdr ;Set for READ of slave.
ACALL GoMaster ;Acquire bus and send slave address.
JB NoAck,RDEX ;Check for slave not responding.
RDLoop: ACALL RcvByte ;Recieve next data byte.
MOV @R0,A ;Save data byte in buffer.
INC R0 ;Advance buffer pointer.
DJNZ ByteCnt,RDLoop ;Repeat untill all bytes received.
RDEX: ACALL SendStop ;Done, send an I2C stop.
RET
;****************************************************************************
; Main Program
;****************************************************************************
Reset: MOV SP,#2Fh ;Set stack to start at 30h.
MOV XmtDat,#0 ;Initialize transmit data area.
MOV XmtDat+1,#37h
MOV XmtDat+2,#0AAh
MOV XmtDat+3,#055h
MOV XmtDat+4,#33h
MOV XmtDat+5,#0CCh
MOV XmtDat+6,#0FFh
MOV XmtDat+7,#0BBh
TestLoop: MOV SlvAdr,#I2CIO ;Write data to PCF8574 I/O expandor.
MOV R0,#XmtDat+2 ;Start of data.
MOV ByteCnt,#1 ;Send one data byte.
ACALL SendData
MOV SlvAdr,#I2CIO ;Read back data from PCF8574 I/O expandor.
MOV R0,#AltRcv ;Start of data.
MOV ByteCnt,#1 ;Read one data byte.
ACALL RcvData
INC XmtDat+2 ;Advance data to next value.
MOV SlvAdr,#I2CLED ;Write data to SAA1064 LED driver.
MOV R0,#XmtDat ;Start of data.
MOV ByteCnt,#6 ;Send 6 bytes (subaddress, control, data).
ACALL SendData
MOV SlvAdr,#I2CRAM ;Write data to PCF8570 RAM.
MOV R0,#XmtDat ;Start of data.
MOV ByteCnt,#8 ;Send 8 bytes (subaddress + 7 data bytes).
ACALL SendData
MOV SlvAdr,#I2CRAM ;Write subaddress to PCF8570 RAM.
MOV R0,#XmtDat ;Start of data.
MOV ByteCnt,#1 ;Send one byte (subaddress).
ACALL SendData
MOV SlvAdr,#I2CRAM ;Read back data from PCF8570 RAM.
MOV R0,#RcvDat ;Start of data.
MOV ByteCnt,#7 ;Read 7 data bytes.
ACALL RcvData
AJMP TestLoop ;Repeat operation for scope watchers.
END
|