1;****************************************************************************
2;
3;				Low Level I2C Routines
4;
5;	Single Master Transmitter & Single Master Receiver Routines
6; 	These routines can very easily be converted to Multi-Master System
7;	   when PIC16C6X with on chip I2C Slave Hardware, Start & Stop Bit
8;	   detection is available.
9;
10;   The generic high level routines are given in I2C_HIGH.ASM
11;
12;
13;       Program:          I2C_LOW.ASM
14;       Revision Date:
15;                         1-16-97      Compatibility with MPASMWIN 1.40
16;
17;***************************************************************************
18
19
20
21
22
23;**************************************************************************
24;				I2C Bus Initialization
25;
26;**************************************************************************
27InitI2CBus_Master:
28
29	bcf	STATUS,RP0
30	movlw	~(1<<SCL_PIN | 1<<SDA_PIN)
31	andwf   PORTB,F         ; must not use BSF, BCF on portb
32	; set SDA & SCL to zero. From Now on, simply play with tris
33	RELEASE_BUS
34	clrf	Bus_Status	; reset status reg
35	clrf	Bus_Control	; clear the Bus_Control Reg, reset to 8 bit addressing
36	return
37;
38;**************************************************************************
39;					Send Start Bit
40;
41;**************************************************************************
42
43TxmtStartBit:
44		bsf	STATUS,RP0		; select page 1
45		bsf	_SDA		; set SDA high
46		bsf	_SCL		; clock to high
47;
48; Setup time for a REPEATED START condition (4.7 uS)
49;
50                call	Delay40uSec	; only necesry for setup time
51;
52		bcf	_SDA		; give a falling edge on SDA while clock is high
53;
54		call	Delay47uSec	; only necessary for START HOLD time
55;
56		bsf	_Bus_Busy	; on a start condition bus is busy
57;
58		return
59
60
61;*********************************************************************************************************
62;					Send Stop Bit
63;
64;*********************************************************************************************************
65
66TxmtStopBit:
67		bsf	STATUS,RP0		; select page 1
68		bcf	_SCL
69		bcf	_SDA		; set SDA low
70		bsf	_SCL		; Clock is pulled up
71		call	Delay40uSec	; Setup Time For STOP Condition
72		bsf	_SDA		; give a rising edge on SDA while CLOCK is high
73;
74  if _ENABLE_BUS_FREE_TIME
75; delay to make sure a START bit is not sent immediately after a STOP, ensure BUS Free Time tBUF
76;
77		call	Delay47uSec
78  endif
79;
80		bcf	_Bus_Busy	; on a stop condition bus is considered Free
81;
82		return
83
84
85;*********************************************************************************************************
86;					Abort Transmission
87;
88;   Send STOP Bit & set Abort Flag
89;*********************************************************************************************************
90
91AbortTransmission:
92
93		call	TxmtStopBit
94		bsf	_Abort
95		return
96
97;*********************************************************************************************************
98;				Transmit Address (1st Byte)& Put in Read/Write Operation
99;
100;  Transmits Slave Addr On the 1st byte and set LSB to R/W operation
101;  Slave Address must be loaded into SlaveAddr reg
102;  The R/W operation must be set in Bus_Status Reg (bit _SLAVE_RW): 0 for Write & 1 for Read
103;
104;  On Success, return TRUE in WREG, else FALSE in WREG
105;
106;   If desired, the failure may tested by the bits in Bus Status Reg
107;
108;*********************************************************************************************************
109
110Txmt_Slave_Addr:
111	bcf	_ACK_Error		; reset Acknowledge error bit
112	btfss	_10BitAddr
113	goto	SevenBitAddr
114;
115	btfss	_Slave_RW
116	goto	TenBitAddrWR		; For 10 Bit WR simply send 10 bit addr
117;
118; Required to READ a 10 bit slave, so first send 10 Bit for WR & Then Repeated Start
119; and then Hi Byte Only for read opreation
120;
121TenBitAddrRd:
122
123	bcf	_Slave_RW		; temporarily set for WR operation
124	call	TenBitAddrWR
125	btfss	_Txmt_Success		; skip if successful
126	retlw	FALSE
127
128	call    TxmtStartBit    	; send A REPEATED START condition
129	bsf	_Slave_RW		; For 10 bit slave Read
130
131	movf	SlaveAddr+1,W
132	movwf	DataByte
133	bsf	DataByte,LSB		; Read Operation
134	call	SendData		; send ONLY high byte of 10 bit addr slave
135        goto	_AddrSendTest		; 10 Bit Addr Send For Slave Read Over
136;
137; if successfully transmitted, expect an ACK bit
138;
139	btfss	_Txmt_Success		; if not successful, generate STOP & abort transfer
140        goto	_AddrSendFail
141;
142
143TenBitAddrWR:
144
145	movf	SlaveAddr+1,W
146	movwf	DataByte
147	bcf	DataByte,LSB		; WR Operation
148;
149; Ready to transmit data : If Interrupt Driven (i.e if Clock Stretched LOW Enabled)
150; then save RETURN Address Pointer
151;
152	call	SendData		; send high byte of 10 bit addr slave
153;
154; if successfully transmitted, expect an ACK bit
155;
156	btfss	_Txmt_Success		; if not successful, generate STOP & abort transfer
157        goto	_AddrSendFail
158;
159	movf	SlaveAddr,W
160	movwf	DataByte		; load addr to DatByte for transmission
161	goto	EndTxmtAddr
162
163SevenBitAddr:
164	movf	SlaveAddr,W
165	movwf	DataByte		; load addr to DatByte for transmission
166	bcf	DataByte,LSB
167	btfsc	_Slave_RW		; if skip then write operation
168	bsf	DataByte,LSB		; Read Operation
169
170EndTxmtAddr:
171	call	SendData		; send 8 bits of address, bus is our's
172;
173; if successfully transmitted, expect an ACK bit
174;
175_AddrSendTest:
176	btfss	_Txmt_Success		; skip if successful
177	goto	_AddrSendFail
178	clrwdt
179	retlw	TRUE
180;
181_AddrSendFail:
182	clrwdt
183	btfss	_ACK_Error
184	retlw	FALSE			; Addr Txmt Unsuccessful, so return 0
185;
186; Address Not Acknowledged, so send STOP bit
187;
188	call	TxmtStopBit
189	retlw	FALSE			; Addr Txmt Unsuccessful, so return 0
190;
191;*********************************************************************************************************
192;				Transmit A Byte Of Data
193;
194; The data to be transmitted must be loaded into DataByte Reg
195; Clock stretching is allowed by slave. If the slave pulls the clock low, then, the stretch is detected
196; and INT Interrupt on Rising edge is enabled and also TMR0 timeout interrupt is enabled
197;	The clock stretching slows down the transmit rate because all checking is done in
198;	software. However, if the system has fast slaves and needs no clock stretching, then
199;	this feature can be disabled during Assembly time by setting
200;	_CLOCK_STRETCH_ENABLED must be set to FALSE.
201;
202;*********************************************************************************************************
203SendData:
204
205;
206; TXmtByte & Send Data are same, Can check errors here before calling TxmtByte
207; For future compatibility, the user MUST call SendData & NOT TxmtByte
208;
209	goto	TxmtByte
210
211;
212TxmtByte:
213		movf	DataByte,W
214                movwf	DataByteCopy	; make copy of DataByte
215		bsf	_Txmt_Progress	; set Bus status for txmt progress
216		bcf	_Txmt_Success	; reset status bit
217		movlw	0x08
218		movwf	BitCount
219		bsf	STATUS,RP0
220    if _CLOCK_STRETCH_CHECK
221;	 set TMR0 to INT CLK timeout for 1 mSec
222;	 do not disturb user's selection of RPUB in OPTION Register
223;
224		movf	OPTION_REG,W
225		andlw	_OPTION_INIT	; defined in I2C.H header file
226		movwf	OPTION_REG
227    endif
228
229TxmtNextBit:
230		clrwdt			; clear WDT, set for 18 mSec
231		bcf	_SCL
232		rlf     DataByteCopy, F	; MSB first, Note DataByte Is Lost
233                bcf	_SDA
234		btfsc	STATUS,C
235		bsf	_SDA
236		call	Delay47uSec	; guareentee min LOW TIME tLOW & Setup time
237		bsf	_SCL		; set clock high , check if clock is high, else clock being stretched
238		call	Delay40uSec	; guareentee min HIGH TIME tHIGH
239	if _CLOCK_STRETCH_CHECK
240		bcf	STATUS,RP0
241		clrf	TMR0		; clear TMR0
242		bcf	INTCON,T0IF		; clear any pending flags
243		bsf	INTCON,T0IE		; elable TMR0 Interrupt
244		bcf	_TIME_OUT_	; reset timeout error flag
245Check_SCL_1:
246		btfsc	_TIME_OUT_	; if TMR0 timeout or Error then Abort & return
247		goto	Bus_Fatal_Error	; Possible FATAL Error on Bus
248		bcf	STATUS,RP0
249		btfss	_SCL_D		; if clock not being stretched, it must be high
250		goto	Check_SCL_1	; loop until SCL high or TMR0 timeout interrupt
251		bcf	INTCON,T0IE		; Clock good, diable TMR0 interrupts
252                bsf	STATUS,RP0
253	endif
254		decfsz	BitCount, F
255		goto	TxmtNextBit
256;
257; Check For Acknowledge
258;
259		bcf	_SCL		; reset clock
260		bsf	_SDA		; Release SDA line for Slave to pull down
261		call	Delay47uSec	; guareentee min LOW TIME tLOW & Setup time
262		bsf	_SCL		; clock for slave to ACK
263		call	Delay40uSec	; guareentee min HIGH TIME tHIGH
264		bcf	STATUS,RP0		; select PAGE 0 to test PortB pin SDA
265		btfsc	_SDA		; SDA should be pulled low by slave if OK
266                goto	_TxmtErrorAck
267;
268		bsf	STATUS,RP0
269		bcf	_SCL		; reset clock
270
271		bcf	_Txmt_Progress	; reset TXMT bit in Bus Status
272		bsf	_Txmt_Success	; transmission successful
273		bcf	_ACK_Error	; ACK OK
274		return
275_TxmtErrorAck:
276		RELEASE_BUS
277		bcf	_Txmt_Progress	; reset TXMT bit in Bus Status
278		bcf	_Txmt_Success	; transmission NOT successful
279		bsf	_ACK_Error	; No ACK From Slave
280		return
281;
282;*********************************************************************************************************
283;
284;				Receive  A Byte Of Data From Slave
285;
286;  assume address is already sent
287;  if last byte to be received, do not acknowledge slave (last byte is testted from
288;  _Last_Byte_Rcv bit of control reg)
289;  Data Received on successful reception is in DataReg register
290;
291;
292;*********************************************************************************************************
293;
294
295GetData:
296		goto	RcvByte
297;
298RcvByte:
299
300		bsf	_Rcv_Progress	; set Bus status for txmt progress
301		bcf	_Rcv_Success	; reset status bit
302
303		movlw	0x08
304		movwf	BitCount
305	if _CLOCK_STRETCH_CHECK
306		bsf	STATUS,RP0
307;	 set TMR0 to INT CLK timeout for 1 mSec
308;	 do not disturb user's selection of RPUB in OPTION Register
309;
310		movf	OPTION_REG,W
311		andlw	_OPTION_INIT	; defined in I2C.H header file
312		movwf	OPTION_REG
313	endif
314
315RcvNextBit:
316		clrwdt			; clear WDT, set for 18 mSec
317		bsf	STATUS,RP0		; page 1 for TRIS manipulation
318		bcf	_SCL
319		bsf	_SDA		; can be removed from loop
320		call	Delay47uSec	; guareentee min LOW TIME tLOW & Setup time
321		bsf	_SCL		; clock high, data sent by slave
322		call	Delay40uSec	; guareentee min HIGH TIME tHIGH
323	if _CLOCK_STRETCH_CHECK
324		bcf	STATUS,RP0
325		clrf	TMR0		; clear TMR0
326		bcf	INTCON,T0IF           ; clear any pending flags
327		bsf	INTCON,T0IE		; elable TMR0 Interrupt
328		bcf	_TIME_OUT_	; reset timeout error flag
329Check_SCL_2:
330		btfsc	_TIME_OUT_	; if TMR0 timeout or Error then Abort & return
331		goto	Bus_Fatal_Error	; Possible FATAL Error on Bus
332		bcf	STATUS,RP0
333		btfss	_SCL_D		; if clock not being stretched, it must be high
334		goto	Check_SCL_2	; loop until SCL high or TMR0 timeout interrupt
335		bcf	INTCON,T0IE		; Clock good, diable TMR0 interrupts
336                bsf	STATUS,RP0
337	endif
338		bcf	STATUS,RP0		; select page 0 to read Ports
339		bcf	STATUS,C
340		btfsc	_SDA
341		bsf	STATUS,C
342;					; TEMP ???? DO 2 out of 3 Majority detect
343                rlf	DataByte, F	; left shift data ( MSB first)
344		decfsz	BitCount, F
345		goto	RcvNextBit
346;
347; Generate ACK bit if not last byte to be read,
348; if last byte Gennerate NACK ; do not send ACK on last byte, main routine will send a STOP bit
349;
350		bsf	STATUS,RP0
351		bcf	_SCL
352		bcf	_SDA		; ACK by pulling SDA low
353		btfsc	_Last_Byte_Rcv
354		bsf	_SDA		; if last byte, send NACK by setting SDA high
355		call	Delay47uSec	; guareentee min LOW TIME tLOW & Setup time
356		bsf	_SCL
357		call	Delay40uSec	; guareentee min HIGH TIME tHIGH
358RcvEnd:
359		bcf	_SCL		; reset clock
360
361		bcf	_Rcv_Progress	; reset TXMT bit in Bus Status
362		bsf	_Rcv_Success	; transmission successful
363		bcf	_ACK_Error	; ACK OK
364
365		return
366
367  if _CLOCK_STRETCH_CHECK
368;*********************************************************************************************************
369;				Fatal Error On I2C Bus
370;
371;  Slave pulling clock for too long or if SCL Line is stuck low.
372;  This occurs if during Transmission, SCL is stuck low for period longer than appox 1mS
373;   and TMR0 times out ( appox 4096 cycles : 256 * 16 -- prescaler of 16).
374;
375;*********************************************************************************************************
376
377Bus_Fatal_Error:
378
379; diable TMR0 Interrupt
380;
381	bcf	INTCON,T0IE			; disable TMR0 interrupts, until next TXMT try
382
383	RELEASE_BUS
384;
385;  Set the Bus_Status Bits appropriately
386;
387  .assert "'FAILED IC2 Clock stretch timeout'"
388	nop
389	bsf	_Abort			; transmission was aborted
390	bsf	_Fatal_Error		; FATAL Error occured
391	bcf	_Txmt_Progress		; Transmission Is Not in Progress
392	bcf	_Txmt_Success		; Transmission Unsuccesful
393;
394	call	TxmtStopBit		; Try sending a STOP bit, may be not successful
395;
396	return
397;
398;*********************************************************************************************************
399  endif
400
401
402;*********************************************************************************************************
403;			General Purpose Delay Routines
404;
405;  Delay4uS	is wait loop for 4.0 uSec
406;  Delay47uS	is wait loop for 4.7 uSec
407;  Delay50uS	is wait loop for 5.0 uSec
408;
409;*********************************************************************************************************
410;
411
412Delay50uSec:
413	movlw	((_50uS_Delay-5)/3 + 1)
414DlyK
415	movwf	DelayCount
416	decfsz	DelayCount, F
417	goto	$-1
418	return
419;
420Delay47uSec:
421	movlw	((_47uS_Delay-8)/3 + 1)
422        goto	DlyK
423;
424Delay40uSec:
425	movlw	((_40uS_Delay-8)/3 + 1)
426	goto	DlyK
427;
428;*********************************************************************************************************
429