$TITLE(INTEL_I2C_SOFTWARE EMULATION_MASTER CONTROLLER) $INCLUDE(A:MACRO.PDF) ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; INTEL MCS-51 MASTER CONTROLLER MODULE ; ; ; This ASM51 program demonstrates I2C Bus communication between ; the Intel MCS-51 product line and I2C compatible ICs located on the Philips ; OM1016 I2C Evaluation Board. ; ; This program writes the numeric data that represents the following display "_I2C" ; to an I2C compatible IC (PCF8570 RAM), reads the values back into a buffer and ; transmits this buffer out to the Philips I2C SAA1064 LED driver to display the sequence. ; ; Note: ASM51 macro file MACRO.PDF is referenced for use with this program. ; ; Written By: Sabrina Quarles ; Intel Corporation ; EMD 8-Bit Applications Engineering Rev 1.0 ; Date: December 21, 1992 ; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; DEFINITIONS ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ;~~~~~~~~~~ I2C Philips Address of compatible devices on I2C Eval Board ~~~ I2C_RAM EQU 0AEh ;Slave address for PCF8570 RAM chip. I2C_IO EQU 4Eh ;Slave address for PCF8574 I/O expandor. I2C_LED EQU 76h ;Slave address for SAA1064 LED driver. ;~~~~~~~~ RAM DATA STORAGE BUFFERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BIT_CNT DATA 8h ;Bit counter for I2C routines BYTE_CNT DATA 9h ;Byte counter for I2C routines. SLV_ADDR DATA 0Ah ;Slave address for I2C routines. XMT_DAT DATA 0Ch ;I2C transmit buffer, 12 bytes max. RCV_DAT DATA 18h ;I2C receive buffer, 8 bytes max. ALT_XMT DATA 20h ;Alternate I2C transmit buffer, 8 bytes max. FLAGS DATA 28h ;Location for bit flags NO_ACK BIT FLAGS.0 ;I2C no acknowledge flag. BUS_FAULT BIT FLAGS.1 ;I2C bus fault flag. I2C_BUSY BIT FLAGS.2 ;I2C busy flag. ;~~~~~~~~~ I2C DECLATIONS ON PORT 0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SINK BIT P0.0 ;Sink pin for oscope triggering SCL_PIN BIT P0.6 ;I2C serial clock line. SDA_PIN BIT P0.7 ;I2C serial data line. ;~~~~~~~~~~~~~~~ RESET ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ORG 0 AJMP I2C_RESET ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; ; SUBROUTINES ; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ORG 30h ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; ; SEND STOP Subroutine ; ; This program sends an I2C STOP condition to release the bus. ; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SEND_STOP: CLR SDA_PIN ;Get SDA ready for stop. %RELEASE_SCL_HIGH ;Set clock for stop. %DELAY_3_CYCLES ;Delay SETB SDA_PIN ;Send I2C STOP ;Delay satisfied via software CLR I2C_BUSY ;Clear I2C busy status. RET ;Bus should now be released. ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; ; SEND_MSG Subroutine ; ; This subroutine sends a message across the I2C bus using the ; information stored in the XMT_DAT Buffer in the following format: ; ; Buffer @R0 = SlvAddr, # of Bytes to be Transferred, Data Bytes ; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SEND_MSG: MOV SLV_ADDR, @R0 ;Initializes Slave Address INC R0 ;Next address MOV BYTE_CNT,@R0 ;Initializes BYTE_CNT INC R0 ;Next address ACALL SEND_DATA ;Send Data RET ;Return from Subroutine ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; ; MASTER CONTROLLER Subroutine ; ; This subroutine sends an I2C START condition and Slave Address to ; begin I2C communications. ; ; SDA = Receive/Transmit Data ; SCL = Generate/Control Clock Line ; ; ; Slv_Addr = Slave Address ; ; Verification ; Issues before MASTER TRANSMIT ; * No Bus Fault = Bus Not Busy = SCL & SDA HIGH ; ; ; Issues during MASTER TRANSMIT ; * ACK Received after every Byte Transmission ; ; ; SUBROUTINES Used ; Send_Byte ; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MASTER_CONTROLLER: SETB I2C_BUSY ;Indicate that I2C frame is in progress. CLR NO_ACK ;Clear error status flags. CLR BUS_FAULT JNB SCL_PIN, FAULT ;Check for bus clear. JNB SDA_PIN, FAULT CLR SDA_PIN ;Begin I2C start. %DELAY_3_CYCLES ;Delay CLR SCL_PIN ;Complete I2C start. %DELAY_3_CYCLES ;Delay MOV A,SLV_ADDR ;Get slave address. ACALL SEND_BYTE ;Send slave address. RET FAULT: SETB BUS_FAULT ;Set fault status RET ; and return. ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; MASTER TRANSMIT ~ SEND_BYTE Subroutine ; ; This subroutine sends 1 byte of information located in the ACCumulator ; ACC = Byte to be Transmitted ; ; Verification Issues ; * ACK Received after transmission of Byte ; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SEND_BYTE: MOV BIT_CNT, #8 ;Set bit count value. SB_LOOP: RLC A ;Send one data bit. MOV SDA_PIN,C ;Put data bit on pin. %RELEASE_SCL_HIGH ;Drive SCL HIGH %DELAY_3_CYCLES ;Delay CLR SCL_PIN ;Clear SCL %DELAY_3_CYCLES ;Delay DJNZ BIT_CNT,SB_LOOP ;Repeat until all bits sent. SETB SDA_PIN ;Release data line for acknowledge. %RELEASE_SCL_HIGH ;Send clock for acknowledge. %DELAY_4_CYCLES ;Delay JNB SDA_PIN, SB_EX ;Check for valid acknowledge bit. SETB NO_ACK ;Set status for no acknowledge. SB_EX: CLR SCL_PIN ;Finish acknowledge bit. %DELAY_3_CYCLES ;Delay RET ;Return ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; ; MASTER TRANSMIT ~ SEND DATA Subroutine ; ; This subroutine transmits multiple data bytes over the SDA line. ; The following locations must be initialized before the transmission. ; ; Byte_Cntr = # of bytes to be transmitted ; Slv_Adr = Slave Address ; @R0 = Data to be Transmitted ; ~ includes any addition subaddresses, control, etc ; specific to certain devices ; ; SUBROUTINES Used ; MASTER_XMIT ; Send_Byte ; Send_Byte ; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SEND_DATA: ACALL MASTER_CONTROLLER ;Acquire bus and send slave address. JB NO_ACK,SDEX ;Check for slave not responding. SD_LOOP: MOV A,@R0 ;Get data byte from buffer. ACALL SEND_BYTE ;Send next data byte. INC R0 ;Advance buffer pointer. JB NO_ACK,SDEX ;Check for slave not responding. DJNZ BYTE_CNT, SD_LOOP ;All bytes sent? SDEX: ACALL SEND_STOP ;Done, send an I2C stop. RET ;Return ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; ; TRANSFER Subroutine ; ; This subroutine copies data from the EPROM referenced by DPTR into a ; Buffer referenced by R1. ; ; DPTR = String stored into EPROM ; R1 = Buffer in which data shall be stored ; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TRANSFER: CLR A ;Clears ACC MOVC A, @A+DPTR ;Moves contents of DPTR into A MOV @R1, A ;Copies A into Buffer INC R1 ;Next address INC DPTR ;Next location CLR A ;Clears ACC MOVC A, @A+DPTR ;Moves contents of DPTR into A MOV @R1, A ;Copies A into Buffer MOV R0, A ;Copies A into R0 (# of bytes) INC R1 ;Next address INC DPTR ;Next location CLR A ;Clears A NEXT: MOVC A, @A+DPTR ;Moves contents of DPTR into A DEC R0 ;Decrease # of remaining bytes MOV @R1, A ;Copies A into Buffer INC R1 ;Next address INC DPTR ;Next location CLR A ;Clears A CJNE R0, #0, NEXT ;Compare # of bytes remaining RET ;If all bytes copied, return ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; RECV_MSG Subroutine ; ; This subroutine receives a message from the I2C bus using SLV_ADDR ; and BYTE_CNT as indicators as to what Slave will be sending info and ; how many bytes to expect to receive, and places the data into the ; RCV_DAT buffer. The RCV_DAT Buffer is configured to receive a max. 8 bytes. ; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ RECV_MSG: MOV SLV_ADDR, @R1 ;Moves SLV_ADDR from Buffer R0 points to INC R1 ;Next buffer location MOV BYTE_CNT, @R1 ;Moves BYTE_CNT value into memory location ACALL RCV_DATA ;Calls Rcv Data Subroutine RET ;Returns from Receive Msg subroutine ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; ; MASTER RECEIVE ~ RECEIVE BYTE Subroutine ; ; This subroutine receives a byte from an addressed I2C slave ; device and places into the ACC register. ; ; ACC = Data Byte Received ; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ RECV_BYTE: MOV BIT_CNT,#8 ;Set bit count. RB_LOOP: %RELEASE_SCL_HIGH ;Read one data bit. %DELAY_3_CYCLES ;Delay MOV C, SDA_PIN ;Get data bit from pin. RLC A ;Rotate bit into result byte. CLR SCL_PIN ;Clear SCL pin %DELAY_3_CYCLES ;Delay DJNZ BIT_CNT, RB_LOOP ;Repeat until all bits received. PUSH ACC ;Save accumulator MOV A, BYTE_CNT ;Copies byte count into A CJNE A, #1, RB_ACK ;Check for last byte of frame. SETB SDA_PIN ;Send no acknowledge on last byte. SJMP RB_ACLK ;No ACK on last byte; jump to RB_ACLK RB_ACK: CLR SDA_PIN ;Send acknowledge bit. RB_ACLK: %RELEASE_SCL_HIGH ;Send acknowledge clock. POP ACC ;Restore accumulator %DELAY_3_CYCLES ;Delay CLR SCL_PIN ;Clear SCL pin SETB SDA_PIN ;Clear acknowledge bit. %DELAY_4_CYCLES ;Delay RET ;Return from RecvByte ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; ; MASTER RECEIVE ~ RECEIVE DATA BYTES Subroutine ; ; This subroutine receives multiple data bytes from an addressed ; I2C slave device into the buffer pointed to by R0. ; ; BYTE_CNT = # of bytes to be received ; SLV_ADDR = Slave addres ; ; @R0 = location of received data ; ; SUBROUTINES Used ; MASTER_XMIT ; RCV_BYTE ; ; Note: To receive with a subaddress, use Send Data to set the ; subaddress first (no provision for repeated start). ; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ RCV_DATA: INC SLV_ADDR ;Set for READ of slave. ACALL MASTER_CONTROLLER ;Acquire bus and send slave address. JB NO_ACK,RDEX ;Check for slave not responding. RDLoop: ACALL RECV_BYTE ;Recieve next data byte. MOV @R0,A ;Save data byte in buffer. INC R0 ;Advance buffer pointer. DJNZ BYTE_CNT,RDLoop ;Repeat untill all bytes received. RDEX: ACALL SEND_STOP ;Done, send an I2C stop. RET ;Return from RCV_DATa Subroutine ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; Main Program ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ I2C_RESET: MOV SP,#2Fh ;Set stack to start at 30h. MOV DPTR, #RAM_LED ;Points to RAM_LED string MOV R1, #XMT_DAT ;Points to the XMT_DAT Buffer ACALL TRANSFER ;Transfers RAM_LED into XMT_DAT MOV DPTR, #RAM_SLC ;Points to RAM_SLC string to select RAM MOV R1, #ALT_XMT ;Buffer to transfer string to ACALL TRANSFER ;Transfer RAM_SLC into ALT_XMT TEST_LOOP: CLR SINK ;Trigger point for oscope SETB SINK MOV R0, #XMT_DAT ;Points to XMT_DAT Buffer ACALL SEND_MSG ;Calls Send Msg Subroutine ;Writes Data to I2C RAM ;(1 Subaddr + 8 data bytes) MOV R0, #ALT_XMT ;Points to ALTXMT Buffer ACALL SEND_MSG ;Calls Send Msg Subroutin ;Writes Subaddress to Select RAM MOV R0, #RCV_DAT ;Points to RECEIVE Buffer MOV R1, #XMT_DAT ;Points to XMTDAT Buffer ACALL RECV_MSG ;Calls Recv Msg Subroutine ;Receives data from I2C RAM into ;Intel MCS-51 Device MOV R0, #RCV_DAT ;Points to RECEIVE Buffer ACALL SEND_MSG ;Calls Send Msg Subroutine ;Transfers RcvDat Buffer to LED ;(info encoded into string) AJMP TEST_LOOP ;Repeat operation for oscope monitoring ;~~~~~~~~~~~~~~~~~~~~~~ I2C STRINGS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ RAM_SLC: DB I2C_RAM, 1, 0 RAM_LED: DB I2C_RAM, 9, 0, I2C_LED, 6, 0, 37H, 0H, 48H, 3EH, 35H END