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