;**************************************************************************** ; 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. ; *********************************************************************** ; *********************************************************************** ; *** *** ; *** Please note: Purchase of Philips' I2C components conveys a *** ; *** license under the Philips' I2C patent to use the components *** ; *** in the I2C-system provided the system conforms to the I2C *** ; *** specifications defined by Philips. (U.S. Patent Number *** ; *** 4,689,740) *** ; *** *** ; *********************************************************************** ; *********************************************************************** ; Written by G.Goodhue, Philips Semiconductors ;**************************************************************************** $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