;==========================================================================
; I2C Interface
;
; Three examples Reads & Writes are for PCF8583 256 Byte RAM w/
; Clock/Calendar. The technique for other chips is similar.
;
; I2C Bus is connected to RB.1 (Data) and RB.2 (Clock) with 10K Pullups.
; CB is used as a direction control shadow register so other lines of RB
; can also be made bidirectional. If you have no need of this, calls to
; i2cbit can be replace w/ direct bit manipulation, provided the proper
; delays are inserted inline (I2C maximum clock rate is 100Khz).
;
; This examples were extracted from proprietary code and have not been
; tested. They are provided as guidance for using the i2c routines, which
; have been tested. The PIC16C54 running this routine was orginally clocked
; at 6Mhz. Higher clock speeds may required additional delay to avoid
; exceeding the 100Khz clock speed as specified by the I2C specs.

;==========================================================================
; Chip & I/O Definitions

		device	pic16c54,hs_osc,wdt_off,protect_off
		reset	main

					; Bit Mask into RB Register
SDA		=	1		; I2C Data (RB.1)
SCL		=	2		; I2C Clock (RB.2)

;==========================================================================
; Registers Allocation

		org	10h

TRDR		ds	1		; Transmit/Receive Data Register
BITS		ds	1		; Shift Register Bit Count
CB		ds	1		; Control Shadow for TRISB
FLAGS		ds	1		; General Purpose User Flags
I2CACK		=	FLAGS.0		; I2C Acknowledge Bit

COUNT		ds	1		; Byte Counter used in Examples

;==========================================================================
; I2C Interface

		org	000h

; I2CBIT : Output Bits & Delay

i2cbit		and	rb,#0F9h	; 2	Assure I2C Bits are 0
		mov	!rb,CB		; 2	Output Bit Pattern
		nop			; 1
		ret			; 2	Done

; I2CSEND : Send TRDR Contents to I2C Bus

i2csend		mov	BITS,#08h	;	Set Bit Counter
:next		movb	CB.SDA,TRDR.7	;	Output Next Bit
		rl	TRDR		;	Prepare Next Bit
		call	i2cbit
		setb	CB.SCL		;	Clock Out Bit
		call	i2cbit
:wait0		jnb	rb.SCL,:wait0	;	Wait On Slower Clocks
		clrb	CB.SCL		;	Lower Clock to Complete Bit
		call	i2cbit
		djnz	BITS,:next	;	Continue til All Bytes Sent
		setb	CB.SDA		;	Prepare for Acknowledge
		call	i2cbit
		setb	CB.SCL		;	Clock In Acknowledge
		call	i2cbit
:wait1		jnb	rb.SCL,:wait1	;	Wait On Slower Clocks
		movb	I2CACK,rb.SDA	;	Get Acnknowledge
		clrb	CB.SCL		;	Clear Clock to End Ack
		jmp	i2cbit

; I2CREAD : Read Contents of I2C Bus

i2cread		mov	BITS,#08h	;	Set Bit Count
		setb	CB.SDA		;	Raise SDA to Allow Receive
		call	i2cbit
:next		setb	CB.SCL		;	Clock in Next Bit
		call	i2cbit
:wait0		jnb	rb.SCL,:wait0	;	Wait on Slower Clocks
		rl	TRDR		;	Shift In Bit
		movb	TRDR.0,rb.SDA
		clrb	CB.SCL		;	Finish Clock In
		call	i2cbit
		djnz	BITS,:next	;	Clock in All Bits
		movb	CB.SDA,I2CACK	;	Send Acknowledge
		call	i2cbit
		setb	CB.SCL		;	Clock Out Ack Bit
		call	i2cbit
:wait1		jnb	rb.SCL,:wait1	;	Wait on Slower Clocks
		clrb	CB.SCL		;	Finish Clock Out
		jmp	i2cbit

; I2CSTART : Send Start Condition

i2cstart	clrb	CB.SDA		; 1	Lower Data for Start
		call	i2cbit
		clrb	CB.SCL		; 1	Set Clock for Active Bus
		jmp	i2cbit

; I2CSTOP : Send Stop Condition

i2cstop		setb	CB.SCL		; 1	Raise Clock for Inactive Bus
		call	i2cbit
:wait		jnb	rb.SCL,:wait	; 2/3	Wait on Slower Clocks
		setb	CB.SDA		; 1	Raise Data for Stop
		jmp	i2cbit

;==========================================================================
; Program (Pin Setup Example)

		org	100h

main		mov	rb,#00h		;	Initialize I2C/LED Bits
		mov	CB,#006h
		mov	!rb,CB

;==========================================================================
; Read Entire RAM Space of PCF8583 (Bytes 010h - 0FFh)

ram		call	i2cstop		;	End Reset
		call	i2cstart	;	Send Start
		mov	TRDR,#01h	;	Reset I2C Bus
		call	i2csend
		call	i2cstop		;	End Reset

		call	i2cstart	;	Send Sr
		mov	TRDR,#0A0h	;	Send Write Address
		call	i2csend
		jb	I2CACK,ram	;	Restart if Error
		mov	TRDR,#00Fh	;	Address = 0x0F
		call	i2csend
		jb	I2CACK,ram	;	Restart if Error
		call	i2cstop		;	Send Stop

		call	i2cstart	;	Send Start
		mov	TRDR,#0A1h	;	Send Read Address
		call	i2csend
		jb	I2CACK,ram	;	Restart if Error

		clrb	I2CACK		;	Clear Ack to Get Next Byte
		mov	COUNT,#240	;	Set Read Byte Count
:next		csne	COUNT,#01h	;	Drop Ack on Last Byte
		setb	I2CACK
		call	i2cread		;	Get Next Byte
		djnz	COUNT,:next	;	Read Next Byte
		call	i2cstop		;	Notify I2C Bus of End

;==========================================================================
; Read PCF8583 Clock (Bytes 000h - 00Fh)

read		call	i2cstop		;	Normalize Bus/Recover Errors
		call	i2cstart	;	Send Start
		mov	TRDR,#0A0h	;	Address Specified Node
		call	i2csend
		jb	I2CACK,read	;	Restart if Error
		mov	TRDR,#000h	;	SubAddress Specified
		call	i2csend
		jb	I2CACK,read

:reread		call	i2cstop		;	Send Stop/Correct Errors
		call	i2cstart	;	Send Start
		mov	TRDR,#0A1h	;	Send Read Address
		call	i2csend
		jb	I2CACK,:reread	;	Reread if Error
		clrb	I2CACK		;	Clear Ack to Get Next Byte
		mov	COUNT,#10h	;	Set Read Byte Count
:next		csne	COUNT,#1	;	Set Ack on All But Last Byte
		setb	I2CACK
		call	i2cread		;	Read Next Byte
		djnz	COUNT,:next	;	Continue til Done
		call	i2cstop		;	Normalize Bus

;===========================================================================
; Write PCF8583 Clock (Bytes 000h - 010h)

write		call	i2cstop		;	Normalize Bus/Recover Errors
		call	i2cstart	;	Send Start
		mov	TRDR,#0A0h	;	Address Specified Node
		call	i2csend
		jb	I2CACK,write	;	Restart if Error
		mov	COUNT,#10h	;	Set Write Byte Count
:next		mov	TRDR,#000h	;	Get Next Byte to Write
		call	i2csend		;	NO ERROR RECOVERY HERE
		djnz	COUNT,:next	;	Continue til Done
		call	i2cstop		;	Normalize Bus

; === END OF PROGRAM ===