;****************************************************************************

;

;				Low Level I2C Routines

;

;	Single Master Transmitter & Single Master Receiver Routines

; 	These routines can very easily be converted to Multi-Master System

;	   when PIC16C6X with on chip I2C Slave Hardware, Start & Stop Bit

;	   detection is available.

;

;   The generic high level routines are given in I2C_HIGH.ASM

;

;

;       Program:          I2C_LOW.ASM 

;       Revision Date:   

;                         1-16-97      Compatibility with MPASMWIN 1.40

;

;***************************************************************************






;**************************************************************************

;				I2C Bus Initialization

;	

;**************************************************************************

InitI2CBus_Master:
	
	bcf	STATUS,RP0
	movlw	~(1<<SCL_PIN | 1<<SDA_PIN)
	andwf   PORTB,F         ; must not use BSF, BCF on portb

	; set SDA & SCL to zero. From Now on, simply play with tris

	RELEASE_BUS
	clrf	Bus_Status	; reset status reg

	clrf	Bus_Control	; clear the Bus_Control Reg, reset to 8 bit addressing	

	return
;

;**************************************************************************

;					Send Start Bit

;

;**************************************************************************


TxmtStartBit:
		bsf	STATUS,RP0		; select page 1

		bsf	_SDA		; set SDA high

		bsf	_SCL		; clock to high

;

; Setup time for a REPEATED START condition (4.7 uS)

;

                call	Delay40uSec	; only necesry for setup time

;

		bcf	_SDA		; give a falling edge on SDA while clock is high

;

		call	Delay47uSec	; only necessary for START HOLD time

;

		bsf	_Bus_Busy	; on a start condition bus is busy

;

		return


;*********************************************************************************************************

;					Send Stop Bit

;

;*********************************************************************************************************


TxmtStopBit:
		bsf	STATUS,RP0		; select page 1

		bcf	_SCL		
		bcf	_SDA		; set SDA low

		bsf	_SCL		; Clock is pulled up

		call	Delay40uSec	; Setup Time For STOP Condition 

		bsf	_SDA		; give a rising edge on SDA while CLOCK is high

;

  if _ENABLE_BUS_FREE_TIME
; delay to make sure a START bit is not sent immediately after a STOP, ensure BUS Free Time tBUF

;

		call	Delay47uSec	
  endif
;

		bcf	_Bus_Busy	; on a stop condition bus is considered Free

;

		return	


;*********************************************************************************************************

;					Abort Transmission

;

;   Send STOP Bit & set Abort Flag

;*********************************************************************************************************


AbortTransmission:

		call	TxmtStopBit
		bsf	_Abort
		return	

;*********************************************************************************************************

;				Transmit Address (1st Byte)& Put in Read/Write Operation

;

;  Transmits Slave Addr On the 1st byte and set LSB to R/W operation

;  Slave Address must be loaded into SlaveAddr reg

;  The R/W operation must be set in Bus_Status Reg (bit _SLAVE_RW): 0 for Write & 1 for Read

;

;  On Success, return TRUE in WREG, else FALSE in WREG

;

;   If desired, the failure may tested by the bits in Bus Status Reg

;

;*********************************************************************************************************


Txmt_Slave_Addr:
	bcf	_ACK_Error		; reset Acknowledge error bit

	btfss	_10BitAddr
	goto	SevenBitAddr
;

	btfss	_Slave_RW
	goto	TenBitAddrWR		; For 10 Bit WR simply send 10 bit addr

;

; Required to READ a 10 bit slave, so first send 10 Bit for WR & Then Repeated Start

; and then Hi Byte Only for read opreation

;

TenBitAddrRd:

	bcf	_Slave_RW		; temporarily set for WR operation

	call	TenBitAddrWR
	btfss	_Txmt_Success		; skip if successful

	retlw	FALSE

	call    TxmtStartBit    	; send A REPEATED START condition

	bsf	_Slave_RW		; For 10 bit slave Read


	movf	SlaveAddr+1,W
	movwf	DataByte
	bsf	DataByte,LSB		; Read Operation

	call	SendData		; send ONLY high byte of 10 bit addr slave

        goto	_AddrSendTest		; 10 Bit Addr Send For Slave Read Over

;

; if successfully transmitted, expect an ACK bit

;

	btfss	_Txmt_Success		; if not successful, generate STOP & abort transfer

        goto	_AddrSendFail
;


TenBitAddrWR:

	movf	SlaveAddr+1,W
	movwf	DataByte
	bcf	DataByte,LSB		; WR Operation

;

; Ready to transmit data : If Interrupt Driven (i.e if Clock Stretched LOW Enabled)

; then save RETURN Address Pointer

;

	call	SendData		; send high byte of 10 bit addr slave

;

; if successfully transmitted, expect an ACK bit

;

	btfss	_Txmt_Success		; if not successful, generate STOP & abort transfer

        goto	_AddrSendFail
;

	movf	SlaveAddr,W
	movwf	DataByte		; load addr to DatByte for transmission

	goto	EndTxmtAddr

SevenBitAddr:
	movf	SlaveAddr,W
	movwf	DataByte		; load addr to DatByte for transmission

	bcf	DataByte,LSB
	btfsc	_Slave_RW		; if skip then write operation

	bsf	DataByte,LSB		; Read Operation


EndTxmtAddr:
	call	SendData		; send 8 bits of address, bus is our's

;

; if successfully transmitted, expect an ACK bit

;

_AddrSendTest:
	btfss	_Txmt_Success		; skip if successful

	goto	_AddrSendFail
	clrwdt
	retlw	TRUE
;

_AddrSendFail:
	clrwdt
	btfss	_ACK_Error
	retlw	FALSE			; Addr Txmt Unsuccessful, so return 0

;

; Address Not Acknowledged, so send STOP bit

;					

	call	TxmtStopBit
	retlw	FALSE			; Addr Txmt Unsuccessful, so return 0

;

;*********************************************************************************************************

;				Transmit A Byte Of Data

;

; The data to be transmitted must be loaded into DataByte Reg

; Clock stretching is allowed by slave. If the slave pulls the clock low, then, the stretch is detected

; and INT Interrupt on Rising edge is enabled and also TMR0 timeout interrupt is enabled

;	The clock stretching slows down the transmit rate because all checking is done in

;	software. However, if the system has fast slaves and needs no clock stretching, then

;	this feature can be disabled during Assembly time by setting

;	_CLOCK_STRETCH_ENABLED must be set to FALSE.

;

;*********************************************************************************************************

SendData:

;

; TXmtByte & Send Data are same, Can check errors here before calling TxmtByte

; For future compatibility, the user MUST call SendData & NOT TxmtByte

;

	goto	TxmtByte
			
;

TxmtByte:
		movf	DataByte,W
                movwf	DataByteCopy	; make copy of DataByte 

		bsf	_Txmt_Progress	; set Bus status for txmt progress

		bcf	_Txmt_Success	; reset status bit

		movlw	0x08
		movwf	BitCount
		bsf	STATUS,RP0
    if _CLOCK_STRETCH_CHECK
;	 set TMR0 to INT CLK timeout for 1 mSec 

;	 do not disturb user's selection of RPUB in OPTION Register

;

		movf	OPTION_REG,W
		andlw	_OPTION_INIT	; defined in I2C.H header file

		movwf	OPTION_REG
    endif

TxmtNextBit:
		clrwdt			; clear WDT, set for 18 mSec

		bcf	_SCL
		rlf     DataByteCopy, F	; MSB first, Note DataByte Is Lost

                bcf	_SDA
		btfsc	STATUS,C
		bsf	_SDA
		call	Delay47uSec	; guareentee min LOW TIME tLOW & Setup time

		bsf	_SCL		; set clock high , check if clock is high, else clock being stretched

		call	Delay40uSec	; guareentee min HIGH TIME tHIGH

	if _CLOCK_STRETCH_CHECK
		bcf	STATUS,RP0
		clrf	TMR0		; clear TMR0

		bcf	INTCON,T0IF		; clear any pending flags

		bsf	INTCON,T0IE		; elable TMR0 Interrupt

		bcf	_TIME_OUT_	; reset timeout error flag

Check_SCL_1:
		btfsc	_TIME_OUT_	; if TMR0 timeout or Error then Abort & return

		goto	Bus_Fatal_Error	; Possible FATAL Error on Bus

		bcf	STATUS,RP0 
		btfss	_SCL_D		; if clock not being stretched, it must be high

		goto	Check_SCL_1	; loop until SCL high or TMR0 timeout interrupt

		bcf	INTCON,T0IE		; Clock good, diable TMR0 interrupts

                bsf	STATUS,RP0
	endif
		decfsz	BitCount, F
		goto	TxmtNextBit
;

; Check For Acknowledge

;

		bcf	_SCL		; reset clock

		bsf	_SDA		; Release SDA line for Slave to pull down

		call	Delay47uSec	; guareentee min LOW TIME tLOW & Setup time

		bsf	_SCL		; clock for slave to ACK

		call	Delay40uSec	; guareentee min HIGH TIME tHIGH	

		bcf	STATUS,RP0		; select PAGE 0 to test PortB pin SDA

		btfsc	_SDA		; SDA should be pulled low by slave if OK

                goto	_TxmtErrorAck
;

		bsf	STATUS,RP0
		bcf	_SCL		; reset clock


		bcf	_Txmt_Progress	; reset TXMT bit in Bus Status

		bsf	_Txmt_Success	; transmission successful

		bcf	_ACK_Error	; ACK OK

		return
_TxmtErrorAck:
		RELEASE_BUS
		bcf	_Txmt_Progress	; reset TXMT bit in Bus Status

		bcf	_Txmt_Success	; transmission NOT successful

		bsf	_ACK_Error	; No ACK From Slave

		return
;

;*********************************************************************************************************

;

;				Receive  A Byte Of Data From Slave

;

;  assume address is already sent

;  if last byte to be received, do not acknowledge slave (last byte is testted from

;  _Last_Byte_Rcv bit of control reg)

;  Data Received on successful reception is in DataReg register

;

;

;*********************************************************************************************************

;


GetData:
		goto	RcvByte
;

RcvByte:
		
		bsf	_Rcv_Progress	; set Bus status for txmt progress

		bcf	_Rcv_Success	; reset status bit


		movlw	0x08
		movwf	BitCount
	if _CLOCK_STRETCH_CHECK
		bsf	STATUS,RP0
;	 set TMR0 to INT CLK timeout for 1 mSec

;	 do not disturb user's selection of RPUB in OPTION Register

;

		movf	OPTION_REG,W
		andlw	_OPTION_INIT	; defined in I2C.H header file

		movwf	OPTION_REG
	endif

RcvNextBit:
		clrwdt			; clear WDT, set for 18 mSec

		bsf	STATUS,RP0		; page 1 for TRIS manipulation

		bcf	_SCL
		bsf	_SDA		; can be removed from loop

		call	Delay47uSec	; guareentee min LOW TIME tLOW & Setup time

		bsf	_SCL		; clock high, data sent by slave

		call	Delay40uSec	; guareentee min HIGH TIME tHIGH

	if _CLOCK_STRETCH_CHECK
		bcf	STATUS,RP0
		clrf	TMR0		; clear TMR0

		bcf	INTCON,T0IF           ; clear any pending flags

		bsf	INTCON,T0IE		; elable TMR0 Interrupt

		bcf	_TIME_OUT_	; reset timeout error flag

Check_SCL_2:
		btfsc	_TIME_OUT_	; if TMR0 timeout or Error then Abort & return

		goto	Bus_Fatal_Error	; Possible FATAL Error on Bus

		bcf	STATUS,RP0
		btfss	_SCL_D		; if clock not being stretched, it must be high

		goto	Check_SCL_2	; loop until SCL high or TMR0 timeout interrupt

		bcf	INTCON,T0IE		; Clock good, diable TMR0 interrupts

                bsf	STATUS,RP0
	endif
		bcf	STATUS,RP0		; select page 0 to read Ports

		bcf	STATUS,C
		btfsc	_SDA
		bsf	STATUS,C
;					; TEMP ???? DO 2 out of 3 Majority detect 

                rlf	DataByte, F	; left shift data ( MSB first)

		decfsz	BitCount, F
		goto	RcvNextBit
;

; Generate ACK bit if not last byte to be read,

; if last byte Gennerate NACK ; do not send ACK on last byte, main routine will send a STOP bit

;

		bsf	STATUS,RP0
		bcf	_SCL
		bcf	_SDA		; ACK by pulling SDA low

		btfsc	_Last_Byte_Rcv
		bsf	_SDA		; if last byte, send NACK by setting SDA high

		call	Delay47uSec	; guareentee min LOW TIME tLOW & Setup time

		bsf	_SCL
		call	Delay40uSec	; guareentee min HIGH TIME tHIGH

RcvEnd:
		bcf	_SCL		; reset clock


		bcf	_Rcv_Progress	; reset TXMT bit in Bus Status

		bsf	_Rcv_Success	; transmission successful

		bcf	_ACK_Error	; ACK OK


		return

  if _CLOCK_STRETCH_CHECK
;*********************************************************************************************************

;				Fatal Error On I2C Bus

;

;  Slave pulling clock for too long or if SCL Line is stuck low.

;  This occurs if during Transmission, SCL is stuck low for period longer than appox 1mS

;   and TMR0 times out ( appox 4096 cycles : 256 * 16 -- prescaler of 16).

;

;*********************************************************************************************************


Bus_Fatal_Error:

; diable TMR0 Interrupt

;

	bcf	INTCON,T0IE			; disable TMR0 interrupts, until next TXMT try


	RELEASE_BUS
;

;  Set the Bus_Status Bits appropriately

;

  .assert "\"FAILED IC2 Clock stretch timeout\""
	nop
	bsf	_Abort			; transmission was aborted

	bsf	_Fatal_Error		; FATAL Error occured

	bcf	_Txmt_Progress		; Transmission Is Not in Progress 

	bcf	_Txmt_Success		; Transmission Unsuccesful

;

	call	TxmtStopBit		; Try sending a STOP bit, may be not successful

;

	return
;

;*********************************************************************************************************

  endif


;*********************************************************************************************************

;			General Purpose Delay Routines

;

;  Delay4uS	is wait loop for 4.0 uSec

;  Delay47uS	is wait loop for 4.7 uSec

;  Delay50uS	is wait loop for 5.0 uSec

;

;*********************************************************************************************************

;


Delay50uSec:
	movlw	((_50uS_Delay-5)/3 + 1)
DlyK
	movwf	DelayCount
	decfsz	DelayCount, F
	goto	$-1
	return		
;

Delay47uSec:
	movlw	((_47uS_Delay-8)/3 + 1)
        goto	DlyK
;

Delay40uSec:
	movlw	((_40uS_Delay-8)/3 + 1)
	goto	DlyK
;

;*********************************************************************************************************



syntax highlighted by Code2HTML, v. 0.9.1