1
2	list	p=16f88
3        include <p16f88.inc>
4        include <coff.inc>
5
6        __CONFIG  _CONFIG1, _CP_OFF & _WDT_OFF &  _INTRC_IO & _PWRTE_ON & _LVP_OFF & _BODEN_OFF & _MCLR_OFF
7        __CONFIG    _CONFIG2, _IESO_OFF & _FCMEN_OFF
8
9	;; The purpose of this program is to test gpsim's ability to simulate a pic 16F88.
10	;; Specifically, I2C
11
12        errorlevel -302
13
14; Printf Command
15.command macro x
16  .direct "C", x
17  endm
18
19_ClkIn          equ     8000000        ; Input Clock Frequency
20
21_ClkOut         equ     (_ClkIn >> 2)
22
23;
24; Compute the delay constants for setup & hold times
25;
26_40uS_Delay     set     (_ClkOut/250000)
27_47uS_Delay     set     (_ClkOut/212766)
28_50uS_Delay     set     (_ClkOut/200000)
29
30
31TRUE    equ     1
32FALSE   equ     0
33
34LSB     equ     0
35MSB     equ     7
36
37#define SCL_PIN	2
38#define SDA_PIN 3
39#define I2CPORT	PORTB
40#define _SCL_D	I2CPORT,SCL_PIN
41#define _SCL	TRISB,SCL_PIN
42#define _SDA	TRISB,SDA_PIN
43
44#define T0IE	TMR0IE
45#define T0IF	TMR0IF
46
47#define _ENABLE_BUS_FREE_TIME   TRUE
48#define _CLOCK_STRETCH_CHECK    TRUE
49#define _OPTION_INIT (0xC0 | 0x02) ; Prescaler to TMR0 for Appox 1 mSec timeout
50
51;*****************************************************************************
52;                       I2C Bus Status Reg Bit Definitions
53;*****************************************************************************
54
55#define _Bus_Busy       Bus_Status,0
56#define _Abort          Bus_Status,1
57#define _Txmt_Progress  Bus_Status,2
58#define _Rcv_Progress   Bus_Status,3
59
60#define _Txmt_Success   Bus_Status,4
61#define _Rcv_Success    Bus_Status,5
62#define _Fatal_Error    Bus_Status,6
63#define _ACK_Error      Bus_Status,7
64
65;*****************************************************************************
66;                       I2C Bus Contro Register
67;*****************************************************************************
68#define _10BitAddr      Bus_Control,0
69#define _Slave_RW       Bus_Control,1
70#define _Last_Byte_Rcv  Bus_Control,2
71
72#define _SlaveActive    Bus_Control,6
73#define _TIME_OUT_      Bus_Control,7
74
75
76
77RELEASE_BUS     MACRO
78                        bsf     STATUS,RP0              ; select page 1
79                        bsf     _SDA            ; tristate SDA
80                        bsf     _SCL            ; tristate SCL
81;                       bcf     _Bus_Busy       ; Bus Not Busy, TEMP ????, set/clear on Start & Stop
82                ENDM
83
84
85;****************************************************************************
86;  A MACRO To Load 8 OR 10 Bit Address To The Address Registers
87;
88;  SLAVE_ADDRESS is a constant and is loaded into the SlaveAddress Register(s)
89;      depending  on 8 or 10 bit addressing modes
90;****************************************************************************
91
92LOAD_ADDR_10    MACRO   SLAVE_ADDRESS
93
94	bsf     _10BitAddr      ; Slave has 10 bit address
95	movlw   (SLAVE_ADDRESS & 0xff)
96	movwf   SlaveAddr               ; load low byte of address
97        movlw   (((SLAVE_ADDRESS >> 7) & 0x06) | 0xF0)  ; 10 bit addr 11110AA0
98	movwf   SlaveAddr+1     ; hi order  address
99
100	ENDM
101
102LOAD_ADDR_8     MACRO   SLAVE_ADDRESS
103
104	bcf     _10BitAddr      ; Set for 8 Bit Address Mode
105	movlw   (SLAVE_ADDRESS & 0xff)
106	movwf   SlaveAddr
107
108	ENDM
109
110;****************************************************************************
111;                            I2C_WRITE_SUB
112;
113;  Writes a message just like I2C_WRITE, except that the data is preceeded
114;  by a sub-address to a slave device.
115;        Eg. : A serial EEPROM would need an address of memory location for
116;	       Random Writes
117;
118;  Parameters :
119;        _BYTES_ #of bytes starting from RAM pointer _SourcePointer_ (constant)
120;        _SourcePointer_     Data Start Buffer pointer in RAM (file Registers)
121;        _Sub_Address_       Sub-address of Slave (constant)
122;
123;   Sequence :
124;               S-SlvAW-A-SubA-A-D[0]-A.....A-D[N-1]-A-P
125;
126;  If an error occurs then the routine simply returns and user should check for
127;       flags in Bus_Status Reg (for eg. _Txmt_Success flag
128;
129;       Returns :       WREG = 1 on success, else WREG = 0
130;
131;  NOTE : The address of the slave must be loaded into SlaveAddress Registers,
132;         and 10 or 8 bit mode addressing must be set
133;
134;  COMMENTS :
135;       I2C_WR may prove to be more efficient than this macro in most situations
136;       Advantages will be found for Random Address Block Writes for Slaves with
137;       Auto Increment Sub-Addresses (like Microchip's 24CXX series Serial
138;	    EEPROMS)
139;
140;****************************************************************************
141
142I2C_WR_SUB      MACRO   _BYTES_, _SourcePointer_, _Sub_Address_
143
144	movlw   (_BYTES_ + 1)
145	movwf   tempCount
146
147	movlw   (_SourcePointer_ - 1)
148	movwf   FSR
149
150	movf    INDF,W
151	movwf   StoreTemp_1     ; temporarily store contents of (_SourcePointer_ -1)
152	movlw   _Sub_Address_
153	movwf   INDF           ; store temporarily the sub-address at (_SourcePointer_ -1)
154
155	call    _i2c_block_write        ; write _BYTES_+1 block of data
156
157	movf    StoreTemp_1,W
158	movwf   (_SourcePointer_ - 1) ; restore contents of (_SourcePointer_ - 1)
159
160;	call    TxmtStopBit     ; Issue a stop bit to end transmission
161
162	ENDM
163I2C_WR_SUB2      MACRO   _BYTES_, _SourcePointer_, _Sub_Address_, _Sub_Address2_
164
165	movlw   (_BYTES_ + 2)
166	movwf   tempCount
167
168	movlw   (_SourcePointer_ - 2)
169	movwf   FSR
170
171	movf    INDF,W
172	movwf   StoreTemp_1     ; temporarily store contents of (_SourcePointer_ -2)
173	movlw   _Sub_Address_
174	movwf   INDF           ; store temporarily the sub-address at (_SourcePointer_ -2)
175	movlw   (_SourcePointer_ - 1)
176	movwf   FSR
177	movlw	_Sub_Address2_
178	movwf	INDF
179	movlw   (_SourcePointer_ - 2)
180	movwf   FSR
181
182	call    _i2c_block_write        ; write _BYTES_+1 block of data
183
184	movf    StoreTemp_1,W
185;	movwf   (_SourcePointer_ - 2) ; restore contents of (_SourcePointer_ - 1)
186
187;	call    TxmtStopBit     ; Issue a stop bit to end transmission
188
189	ENDM
190
191
192;****************************************************************************
193;                               I2C_WRITE
194;
195;  A basic macro for writing a block of data to a slave
196;
197;  Parameters :
198;       _BYTES_            #of bytes starting from RAM pointer _SourcePointer_
199;      _SourcePointer_     Data Start Buffer pointer in RAM (file Registers)
200;
201;   Sequence :
202;               S-SlvAW-A-D[0]-A.....A-D[N-1]-A-P
203;
204;  If an error occurs then the routine simply returns and user should check for
205;       flags in Bus_Status Reg (for eg. _Txmt_Success flag)
206;
207;  NOTE : The address of the slave must be loaded into SlaveAddress Registers,
208;        and 10 or 8 bit mode addressing must be set
209;****************************************************************************
210
211
212I2C_WR          MACRO   _BYTES_, _SourcePointer_
213
214	movlw   _BYTES_
215	movwf   tempCount
216	movlw   _SourcePointer_
217	movwf   FSR
218
219	call    _i2c_block_write
220	call    TxmtStopBit  ; Issue a stop bit for slave to end transmission
221
222	ENDM
223
224;*****************************************************************************
225;
226;                               I2C_READ
227;
228; The basic MACRO/procedure to read a block message from a slave device
229;
230;   Parameters :
231;               _BYTES_         :  constant : #of bytes to receive
232;               _DestPointer_   :  destination pointer of RAM (File Registers)
233;
234;   Sequence :
235;               S-SlvAR-A-D[0]-A-.....-A-D[N-1]-N-P
236;
237;   If last byte, then Master will NOT Acknowledge (send NACK)
238;
239;  NOTE : The address of the slave must be loaded into SlaveAddress Registers,
240;     and 10 or 8 bit mode addressing must be set
241;
242;*****************************************************************************
243
244I2C_READ        MACRO   _BYTES_, _DestPointer_
245
246
247	movlw   (_BYTES_ -1)
248	movwf   tempCount       ; -1 because, the last byte is used out of loop
249	movlw   _DestPointer_
250	movwf   FSR             ; FIFO destination address pointer
251
252	call    _i2c_block_read
253
254	ENDM
255
256;***************************************************************************
257;
258;                               I2C_READ_SUB
259;  This MACRO/Subroutine reads a message from a slave device preceeded by
260;  a write of the sub-address.
261;  Between the sub-addrers write & the following reads, a STOP condition
262;  is not issued and a "REPEATED START" condition is used so that an other
263;  master will not take over the bus, and also that no other master will
264;  overwrite the sub-address of the same salve.
265;
266;   This function is very commonly used in accessing Random/Sequential reads
267;   from a memory device (e.g : 24Cxx serial of Serial EEPROMs from Microchip).
268;
269;  Parameters :
270;               _BYTES_         # of bytes to read
271;               _DestPointer_   The destination pointer of data to be received.
272;               _BubAddress_    The sub-address of the slave
273;
274;  Sequence :
275;		S-SlvAW-A-SubAddr-A-S-SlvAR-A-D[0]-A-.....-A-D[N-1]-N-P
276;
277;
278;***************************************************************************
279
280I2C_READ_SUB    MACRO   _BYTES_, _DestPointer_, _SubAddress_
281
282	bcf     _Slave_RW       ; set for write operation
283	call    TxmtStartBit    ; send START bit
284	call    Txmt_Slave_Addr ; if successful, then _Txmt_Success bit is set
285
286
287	movlw   _SubAddress_
288	movwf   DataByte        ; START address of EEPROM(slave 1)
289	call    SendData        ; write sub address
290;
291; do not send STOP after this, use REPEATED START condition
292;
293
294	I2C_READ _BYTES_, _DestPointer_
295
296	ENDM
297I2C_READ_SUB2    MACRO   _BYTES_, _DestPointer_, _SubAddress_, _SubAddress2_
298
299	bcf     _Slave_RW       ; set for write operation
300	call    TxmtStartBit    ; send START bit
301	call    Txmt_Slave_Addr ; if successful, then _Txmt_Success bit is set
302
303
304	movlw   _SubAddress_
305	movwf   DataByte        ; START address of EEPROM(slave 1)
306	call    SendData        ; write sub address
307	movlw   _SubAddress2_
308	movwf   DataByte        ; START address of EEPROM(slave 1)
309	call    SendData        ; write sub address
310;
311; do not send STOP after this, use REPEATED START condition
312;
313
314	I2C_READ _BYTES_, _DestPointer_
315
316	ENDM
317;----------------------------------------------------------------------
318;----------------------------------------------------------------------
319INT_VAR        UDATA
320
321IO_buf  RES 10
322IN_buf  RES 10
323
324GPR_DATA                UDATA_SHR
325
326w_temp RES  1
327status_temp RES  1
328SlaveAddr RES 1              ; Slave Addr must be loader into this reg
329SlaveAddrHi RES 1            ; for 10 bit addressing mode
330DataByte RES 1               ; load this reg with the data to be transmitted
331BitCount RES 1               ; The bit number (0:7) transmitted or received
332Bus_Status RES 1             ; Status Reg of I2C Bus for both TXMT & RCVE
333Bus_Control RES 1            ; control Register of I2C Bus
334DelayCount RES 1
335DataByteCopy RES 1           ; copy of DataByte for Left Shifts (destructive)
336
337SubAddr RES 1                ; sub-address of slave (used in I2C_HIGH.ASM)
338SrcPtr RES 1                 ; source pointer for data to be transmitted
339
340tempCount RES 1              ; a temp variable for scratch RAM
341StoreTemp_1 RES 1            ; a temp variable for scratch RAM, do not disturb contents
342
343_End_I2C_Ram RES 1           ; unused, only for ref of end of RAM allocation
344
345
346
347;----------------------------------------------------------------------
348;   ********************* RESET VECTOR LOCATION  ********************
349;----------------------------------------------------------------------
350RESET_VECTOR  CODE    0x000              ; processor reset vector
351        movlw  high  start               ; load upper byte of 'start' label
352        movwf  PCLATH                    ; initialize PCLATH
353        goto   start                     ; go to beginning of program
354
355	;;
356	;; Interrupt
357	;;
358INT_VECTOR   CODE    0x004               ; interrupt vector location
359	movwf	w_temp
360	swapf	STATUS,W
361	movwf	status_temp
362	bcf	STATUS,RP0	;adcon0 is in bank 0
363
364 if _CLOCK_STRETCH_CHECK                ; TMR0 Interrupts enabled only if Clock Stretching is Used
365        btfss   INTCON,T0IF
366        goto    check	                ; other Interrupts
367        bsf     _TIME_OUT_              ; MUST set this Flag
368        bcf     INTCON,T0IF
369	goto	int_ret
370 endif
371
372
373check:
374	btfss	PIR1,SSPIF
375	goto	int_ret
376	bcf	PIR1,SSPIF
377	call	sspint
378int_ret:
379	swapf	status_temp,w
380	movwf	STATUS
381	swapf	w_temp,F
382	swapf	w_temp,W
383	retfie
384
385
386
387;----------------------------------------------------------------------
388;   ******************* MAIN CODE START LOCATION  ******************
389;----------------------------------------------------------------------
390MAIN    CODE
391start:
392
393  .sim "module lib libgpsim_modules"
394   .sim "p16f88.xpos = 96"
395   .sim "p16f88.ypos = 144"
396
397   .sim "module load pu pu1"
398   .sim "pu1.xpos = 276"
399   .sim "pu1.ypos = 72"
400
401   .sim "module load pu pu2"
402   .sim "pu2.xpos = 96"
403   .sim "pu2.ypos = 48"
404
405   .sim "node n1"
406   .sim "attach n1 portb2 pu1.pin portb4" ; ee.SCL"
407   .sim "node n2"
408   .sim "attach n2 portb3 pu2.pin portb1" ; ee.SDA"
409   .sim "node n3"
410   ;.sim "attach n3 porta0 ee.WP"
411   .sim "node n4"
412   ;.sim "attach n4 porta1 ee.A0"
413   .sim "node n5"
414   ;.sim "attach n5 porta2 ee.A1"
415   .sim "node n6"
416   ;.sim "attach n6 porta3 ee.A2"
417   .sim "scope.ch0 = 'portb2'"
418   .sim "scope.ch1 = 'portb3'"
419
420
421    bsf  STATUS,RP0	; bank 1
422    movlw	0xf6	; set internal RC to 8 Mhz
423    movwf	OSCCON
424    clrf        TRISA
425    bsf		PIE1,SSPIE	; allow SSP interrupts
426    bsf		INTCON,GIE	; allow interrupts
427    bsf		INTCON,PEIE	; allow interrupts
428    bcf  STATUS,RP0	; bank 0
429;    bsf		PORTA,0	; Write protect
430    bsf		PORTA,1	; A0
431    movlw	0x10
432    movwf	tempCount
433    movlw	IO_buf
434    movwf	FSR
435
436Fill_loop:
437    movf	FSR,W
438    movwf	INDF
439    incf	FSR,F
440    decfsz	tempCount,F
441    goto	Fill_loop
442
443    movlw	0x36
444    movwf	SSPCON
445    bsf  STATUS,RP0	; bank 1
446    movlw	0xa2
447    movwf	SSPADD
448    bcf  STATUS,RP0	; bank 0
449
450    call InitI2CBus_Master
451
452    LOAD_ADDR_8 0xa2
453    call IsSlaveActive
454    btfss _SlaveActive
455  .assert "'Slave not active'"
456    nop
457    LOAD_ADDR_8 0xa2
458;    I2C_WR_SUB 8, IO_buf, 0x0c
459    I2C_WR_SUB2 8, IO_buf, 0x0c, 0x0c
460    call    TxmtStopBit     ; Issue a stop bit to end transmission
461    movf    Bus_Status,W
462  .assert "W == 0x10, '*** FAILED I2C write status'"
463    nop
464
465poll_ready:
466    LOAD_ADDR_8 0xa2
467    call IsSlaveActive
468    btfss _SlaveActive
469    goto poll_ready	; slave not active yet
470    nop
471
472;
473;	write 0xa2 0x0c 0x0c to set an address
474;	write RSTART 0xa3 to initiate read
475;	read 8 bytes of data into ram starting at IN_buf
476;
477    LOAD_ADDR_8 0xa2
478    I2C_READ_SUB2 8, IN_buf, 0x0c, 0x0c
479    nop
480    bcf  STATUS,RP0	; bank 0
481    movf	IN_buf,W
482  .assert "W == 0xf5, '*** FAILED read data'"
483    nop
484    movf	Bus_Status,W
485  .assert "W == 0x30, '*** FAILED 8 bit read status'"
486    nop
487
488;
489;	write 8 bytes of data in slave 10bit mode
490;
491    bcf  STATUS,RP0	; bank 0
492    movlw	0x37
493    movwf	SSPCON
494    bsf  STATUS,RP0	; bank 1
495    movlw	0xf0
496    movwf	SSPADD
497    bcf  STATUS,RP0	; bank 0
498    LOAD_ADDR_10 0x0c
499    I2C_WR 8, IO_buf
500
501  .assert "'*** PASSED p16f88 I2C test'"
502    nop
503
504    goto $
505
506IsSlaveActive
507                bcf     _Slave_RW       ; set for write operation
508                call    TxmtStartBit    ; send START bit
509                call    Txmt_Slave_Addr ; if successful, then _Txmt_Success bit is set
510;
511                bcf     _SlaveActive
512                btfss   _ACK_Error      ; skip if NACK, device is not present or not responding
513                bsf     _SlaveActive    ; ACK received, device present & listening
514                call    TxmtStopBit
515                return
516
517include "i2c_low.inc"
518
519
520_i2c_block_write:
521	call    TxmtStartBit    ; send START bit
522	bcf     _Slave_RW       ; set for write operation
523	call    Txmt_Slave_Addr ; if successful, then _Txmt_Success bit is set
524;
525_block_wr1_loop:
526	btfss   _Txmt_Success
527	return
528	movf    INDF,W
529	movwf   DataByte  ; start from the first byte starting at _DataPointer_
530	incf    FSR, F
531	call    SendData  ; send next byte, bus is our's !
532	decfsz  tempCount, F
533	goto    _block_wr1_loop  ; loop until desired bytes of data
534				 ;  transmitted to slave
535	return
536;
537;****************************************************************************
538
539_i2c_block_read:
540	call    TxmtStartBit    ; send START bit
541	bsf     _Slave_RW       ; set for read operation
542	bcf     _Last_Byte_Rcv  ; not a last byte to rcv
543	call    Txmt_Slave_Addr ; if successful, then _Txmt_Success bit is set
544	btfsc   _Txmt_Success
545	goto    _block_rd1_loop ; end
546	call    TxmtStopBit     ; Issue a stop bit for slave to end transmission
547	retlw   FALSE           ; Error : may be device not responding
548;
549_block_rd1_loop:
550	call    GetData
551	movf    DataByte,W
552	movwf   INDF      ;start receiving data, starting at Destination Pointer
553	incf    FSR, F
554	decfsz  tempCount, F
555	goto    _block_rd1_loop  ; loop until desired bytes of data transmitted to slave
556	bsf     _Last_Byte_Rcv          ; last byte to rcv, so send NACK
557	call    GetData
558	movf    DataByte,W
559	movwf   INDF
560	call    TxmtStopBit   ; Issue a stop bit for slave to end transmission
561	retlw   TRUE
562
563sspint:
564    	bsf  STATUS,RP0	; bank 1
565	btfsc	SSPSTAT,R_W	; write test ?
566	goto	sspint_wr
567	btfsc	SSPSTAT,UA	; UA bit set
568	goto	sspint_ua
569
570    	bcf  STATUS,RP0	; bank 0
571	movf	SSPBUF,W
572        return
573
574sspint_wr:
575    	bcf  STATUS,RP0	; bank 0
576	movlw	0xf5	; byte to send
577	movwf	SSPBUF
578	bsf	SSPCON,CKP	; turn off clock stretch
579	return
580
581sspint_ua:
582	movlw	0x0c	; second byte address
583	movwf	SSPADD
584    	bcf  STATUS,RP0	; bank 0
585	movf	SSPBUF,W
586	return
587
588	end
589