4 |
pfowler |
1 |
;---------------------------------------------------------------------
|
|
|
2 |
; File: an734_PIC16.asm
|
|
|
3 |
;
|
|
|
4 |
; Written By: Stephen Bowling, Microchip Technology
|
|
|
5 |
;
|
|
|
6 |
; Version: 1.00
|
|
|
7 |
;
|
|
|
8 |
; Assembled using Microchip Assembler
|
|
|
9 |
;
|
|
|
10 |
; Functionality:
|
|
|
11 |
;
|
|
|
12 |
; This code implements the basic functions for an I2C slave device
|
|
|
13 |
; using the SSP module. All I2C functions are handled in an ISR.
|
|
|
14 |
; Bytes written to the slave are stored in a buffer. After a number
|
|
|
15 |
; of bytes have been written, the master device can then read the
|
|
|
16 |
; bytes back from the buffer.
|
|
|
17 |
;
|
|
|
18 |
; Variables and Constants used in the program:
|
|
|
19 |
;
|
|
|
20 |
; The start address for the receive buffer is stored in the variable
|
|
|
21 |
; 'RXBuffer'. The length of the buffer is denoted by the constant
|
|
|
22 |
; value 'RX_BUF_LEN'. The current buffer index is stored in the
|
|
|
23 |
; variable 'Index'.
|
|
|
24 |
;
|
|
|
25 |
;--------------------------------------------------------------------
|
|
|
26 |
;
|
|
|
27 |
; The following files should be included in the MPLAB project:
|
|
|
28 |
;
|
|
|
29 |
; an734_PIC16.asm-- Main source code file
|
|
|
30 |
;
|
|
|
31 |
; 16f877a.lkr-- Linker script file
|
|
|
32 |
; (change this file for the device you are using)
|
|
|
33 |
;
|
|
|
34 |
;---------------------------------------------------------------------
|
|
|
35 |
;---------------------------------------------------------------------
|
|
|
36 |
; Include Files
|
|
|
37 |
;---------------------------------------------------------------------
|
|
|
38 |
list p=16F88
|
|
|
39 |
#include <p16f88.inc> ; Change to device that you are using.
|
|
|
40 |
__CONFIG _CONFIG1, _CP_OFF & _CCP1_RB3 & _CCPMX_RB3 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_ON & _PWRTE_ON & _WDT_ON & _HS_OSC
|
|
|
41 |
ERRORLEVEL -302
|
|
|
42 |
;---------------------------------------------------------------------
|
|
|
43 |
;Constant Definitions
|
|
|
44 |
;---------------------------------------------------------------------
|
|
|
45 |
#define NODE_ADDR 0x4e ; I2C address of slave 2
|
|
|
46 |
|
|
|
47 |
#define LED0 PORTA,0
|
|
|
48 |
#define LED1 PORTA,1
|
|
|
49 |
#define LED2 PORTA,2
|
|
|
50 |
#define LED3 PORTA,3
|
|
|
51 |
#define LED4 PORTA,4
|
|
|
52 |
#define LED5 PORTB,2
|
|
|
53 |
#define LED6 PORTB,0
|
|
|
54 |
#define LED7 PORTB,5
|
|
|
55 |
; Change this value to address that
|
|
|
56 |
; you wish to use.
|
|
|
57 |
;---------------------------------------------------------------------
|
|
|
58 |
; Buffer Length Definition
|
|
|
59 |
;---------------------------------------------------------------------
|
|
|
60 |
#define RX_BUF_LEN 8 ; Length of receive buffer
|
|
|
61 |
;---------------------------------------------------------------------
|
|
|
62 |
; Variable declarations
|
|
|
63 |
;---------------------------------------------------------------------
|
|
|
64 |
udata_shr
|
|
|
65 |
WREGsave res 1
|
|
|
66 |
RXBuffer res RX_BUF_LEN
|
|
|
67 |
|
|
|
68 |
|
|
|
69 |
udata
|
|
|
70 |
STATUSsave res 1
|
|
|
71 |
FSRsave res 1
|
|
|
72 |
PCLATHsave res 1
|
|
|
73 |
Index res 1 ; Index to receive buffer
|
|
|
74 |
Temp res 1 ;
|
|
|
75 |
|
|
|
76 |
|
|
|
77 |
; device.
|
|
|
78 |
;---------------------------------------------------------------------
|
|
|
79 |
; Vectors
|
|
|
80 |
;---------------------------------------------------------------------
|
|
|
81 |
STARTUP code 0x00
|
|
|
82 |
nop
|
|
|
83 |
goto Startup ;
|
|
|
84 |
nop ; 0x0002
|
|
|
85 |
nop ; 0x0003
|
|
|
86 |
goto ISR ; 0x0004
|
|
|
87 |
PROG code
|
|
|
88 |
;---------------------------------------------------------------------
|
|
|
89 |
; Macros
|
|
|
90 |
;---------------------------------------------------------------------
|
|
|
91 |
memset macro Buf_addr,Value,Length
|
|
|
92 |
movlw Length ; This macro loads a range of data memory
|
|
|
93 |
movwf Temp ; with a specified value. The starting
|
|
|
94 |
movlw Buf_addr ; address and number of bytes are also
|
|
|
95 |
movwf FSR ; specified.
|
|
|
96 |
SetNext movlw Value
|
|
|
97 |
movwf INDF
|
|
|
98 |
incf FSR,F
|
|
|
99 |
decfsz Temp,F
|
|
|
100 |
goto SetNext
|
|
|
101 |
endm
|
|
|
102 |
LFSR macro Address,Offset ; This macro loads the correct value
|
|
|
103 |
movlw Address ; into the FSR given an initial data
|
|
|
104 |
movwf FSR ; memory address and offset value.
|
|
|
105 |
movf Offset,W
|
|
|
106 |
addwf FSR,F
|
|
|
107 |
endm
|
|
|
108 |
;---------------------------------------------------------------------
|
|
|
109 |
; Main Code
|
|
|
110 |
;---------------------------------------------------------------------
|
|
|
111 |
Startup
|
|
|
112 |
bcf STATUS,RP1
|
|
|
113 |
bsf STATUS,RP0
|
|
|
114 |
call Setup
|
|
|
115 |
banksel WREGsave
|
|
|
116 |
banksel RXBuffer
|
|
|
117 |
clrf RXBuffer
|
|
|
118 |
bcf LED0
|
|
|
119 |
bcf LED1
|
|
|
120 |
bcf LED2
|
|
|
121 |
bcf LED3
|
|
|
122 |
bcf LED4
|
|
|
123 |
bcf LED5
|
|
|
124 |
bcf LED6
|
|
|
125 |
bcf LED7
|
|
|
126 |
|
|
|
127 |
Main clrwdt ; Clear the watchdog timer.
|
|
|
128 |
banksel PORTB
|
|
|
129 |
btfsc RXBuffer,0
|
|
|
130 |
bsf LED0
|
|
|
131 |
btfss RXBuffer,0
|
|
|
132 |
bcf LED0
|
|
|
133 |
|
|
|
134 |
btfsc RXBuffer,1
|
|
|
135 |
bsf LED1
|
|
|
136 |
btfss RXBuffer,1
|
|
|
137 |
bcf LED1
|
|
|
138 |
|
|
|
139 |
btfsc RXBuffer,2
|
|
|
140 |
bsf LED2
|
|
|
141 |
btfss RXBuffer,2
|
|
|
142 |
bcf LED2
|
|
|
143 |
|
|
|
144 |
btfsc RXBuffer,3
|
|
|
145 |
bsf LED3
|
|
|
146 |
btfss RXBuffer,3
|
|
|
147 |
bcf LED3
|
|
|
148 |
|
|
|
149 |
btfsc RXBuffer,4
|
|
|
150 |
bsf LED4
|
|
|
151 |
btfss RXBuffer,4
|
|
|
152 |
bcf LED4
|
|
|
153 |
|
|
|
154 |
btfsc RXBuffer,5
|
|
|
155 |
bsf LED5
|
|
|
156 |
btfss RXBuffer,5
|
|
|
157 |
bcf LED5
|
|
|
158 |
|
|
|
159 |
btfsc RXBuffer,6
|
|
|
160 |
bsf LED6
|
|
|
161 |
btfss RXBuffer,6
|
|
|
162 |
bcf LED6
|
|
|
163 |
|
|
|
164 |
btfsc RXBuffer,7
|
|
|
165 |
bsf LED7
|
|
|
166 |
btfss RXBuffer,7
|
|
|
167 |
bcf LED7
|
|
|
168 |
|
|
|
169 |
goto Main ; Loop forever.
|
|
|
170 |
;---------------------------------------------------------------------
|
|
|
171 |
; Interrupt Code
|
|
|
172 |
;---------------------------------------------------------------------
|
|
|
173 |
ISR
|
|
|
174 |
movwf WREGsave ; Save WREG
|
|
|
175 |
movf STATUS,W ; Get STATUS register
|
|
|
176 |
banksel STATUSsave ; Switch banks, if needed.
|
|
|
177 |
movwf STATUSsave ; Save the STATUS register
|
|
|
178 |
movf PCLATH,W ;
|
|
|
179 |
movwf PCLATHsave ; Save PCLATH
|
|
|
180 |
movf FSR,W ;
|
|
|
181 |
movwf FSRsave ; Save FSR
|
|
|
182 |
banksel PIR1
|
|
|
183 |
btfss PIR1,SSPIF ; Is this a SSP interrupt?
|
|
|
184 |
goto $ ; No, just trap here.
|
|
|
185 |
bcf PIR1,SSPIF
|
|
|
186 |
call SSP_Handler ; Yes, service SSP interrupt.
|
|
|
187 |
banksel FSRsave
|
|
|
188 |
movf FSRsave,W ;
|
|
|
189 |
movwf FSR ; Restore FSR
|
|
|
190 |
movf PCLATHsave,W;
|
|
|
191 |
movwf PCLATH ; Restore PCLATH
|
|
|
192 |
movf STATUSsave,W;
|
|
|
193 |
movwf STATUS ; Restore STATUS
|
|
|
194 |
swapf WREGsave,F ;
|
|
|
195 |
swapf WREGsave,W ; Restore WREG
|
|
|
196 |
retfie ; Return from interrupt.
|
|
|
197 |
;---------------------------------------------------------------------
|
|
|
198 |
Setup
|
|
|
199 |
;
|
|
|
200 |
; Initializes program variables and peripheral registers.
|
|
|
201 |
;---------------------------------------------------------------------
|
|
|
202 |
banksel PCON
|
|
|
203 |
bsf PCON,NOT_POR
|
|
|
204 |
bsf PCON,NOT_BOR
|
|
|
205 |
banksel ANSEL
|
|
|
206 |
movlw 0x00
|
|
|
207 |
movwf ANSEL
|
|
|
208 |
banksel Index ; Clear various program variables
|
|
|
209 |
clrf Index
|
|
|
210 |
clrf PORTB
|
|
|
211 |
clrf PIR1
|
|
|
212 |
banksel TRISB
|
|
|
213 |
clrf TRISB
|
|
|
214 |
bsf TRISB,4
|
|
|
215 |
bsf TRISB,1
|
|
|
216 |
|
|
|
217 |
banksel TRISA
|
|
|
218 |
clrf TRISA
|
|
|
219 |
movlw 0x36 ; Setup SSP module for 7-bit
|
|
|
220 |
banksel SSPCON
|
|
|
221 |
movwf SSPCON ; address, slave mode
|
|
|
222 |
movlw NODE_ADDR
|
|
|
223 |
banksel SSPADD
|
|
|
224 |
movwf SSPADD
|
|
|
225 |
clrf SSPSTAT
|
|
|
226 |
banksel PIE1 ; Enable interrupts
|
|
|
227 |
bsf PIE1,SSPIE
|
|
|
228 |
bsf INTCON,PEIE ; Enable all peripheral interrupts
|
|
|
229 |
bsf INTCON,GIE ; Enable global interrupts
|
|
|
230 |
bcf STATUS,RP0
|
|
|
231 |
return
|
|
|
232 |
;---------------------------------------------------------------------
|
|
|
233 |
SSP_Handler
|
|
|
234 |
|
|
|
235 |
;---------------------------------------------------------------------
|
|
|
236 |
; The I2C code below checks for 5 states:
|
|
|
237 |
;---------------------------------------------------------------------
|
|
|
238 |
; State 1: I2C write operation, last byte was an address byte.
|
|
|
239 |
; SSPSTAT bits: S = 1, D_A = 0, R_W = 0, BF = 1
|
|
|
240 |
;
|
|
|
241 |
; State 2: I2C write operation, last byte was a data byte.
|
|
|
242 |
; SSPSTAT bits: S = 1, D_A = 1, R_W = 0, BF = 1
|
|
|
243 |
;
|
|
|
244 |
; State 3: I2C read operation, last byte was an address byte.
|
|
|
245 |
; SSPSTAT bits: S = 1, D_A = 0, R_W = 1 (see Appendix C for more information)
|
|
|
246 |
;
|
|
|
247 |
; State 4: I2C read operation, last byte was a data byte.
|
|
|
248 |
; SSPSTAT bits: S = 1, D_A = 1, R_W = 1, BF = 0
|
|
|
249 |
;
|
|
|
250 |
; State 5: Slave I2C logic reset by NACK from master.
|
|
|
251 |
; SSPSTAT bits: S = 1, D_A = 1, BF = 0 (see Appendix C for more information)
|
|
|
252 |
;
|
|
|
253 |
; For convenience, WriteI2C and ReadI2C functions have been used.
|
|
|
254 |
;----------------------------------------------------------------------
|
|
|
255 |
banksel SSPSTAT
|
|
|
256 |
movf SSPSTAT,W ; Get the value of SSPSTAT
|
|
|
257 |
andlw b'00101101' ; Mask out unimportant bits in SSPSTAT.
|
|
|
258 |
banksel Temp ; Put masked value in Temp
|
|
|
259 |
movwf Temp ; for comparision checking.
|
|
|
260 |
State1: ; Write operation, last byte was an
|
|
|
261 |
movlw b'00001001' ; address, buffer is full.
|
|
|
262 |
xorwf Temp,W ;
|
|
|
263 |
btfss STATUS,Z ; Are we in State1?
|
|
|
264 |
goto State2 ; No, check for next state.....
|
|
|
265 |
memset RXBuffer,0,RX_BUF_LEN ; Clear the receive buffer.
|
|
|
266 |
clrf Index ; Clear the buffer index.
|
|
|
267 |
banksel SSPBUF ; Do a dummy read of the SSPBUF.
|
|
|
268 |
movf SSPBUF,W
|
|
|
269 |
return
|
|
|
270 |
State2: ; Write operation, last byte was data,
|
|
|
271 |
movlw b'00101001' ; buffer is full.
|
|
|
272 |
xorwf Temp,W
|
|
|
273 |
btfss STATUS,Z ; Are we in State2?
|
|
|
274 |
goto State3 ; No, check for next state.....
|
|
|
275 |
LFSR RXBuffer,Index ; Point to the buffer.
|
|
|
276 |
banksel SSPBUF ; Get the byte from the SSP.
|
|
|
277 |
movf SSPBUF,W
|
|
|
278 |
movwf INDF ; Put it in the buffer.
|
|
|
279 |
incf Index,F ; Increment the buffer pointer.
|
|
|
280 |
movf Index,W ; Get the current buffer index.
|
|
|
281 |
sublw RX_BUF_LEN ; Subtract the buffer length.
|
|
|
282 |
btfsc STATUS,Z ; Has the index exceeded the buffer length?
|
|
|
283 |
clrf Index ; Yes, clear the buffer index.
|
|
|
284 |
return
|
|
|
285 |
State3: ; Read operation, last byte was an address,
|
|
|
286 |
movf Temp,W ;
|
|
|
287 |
andlw b'00101100' ; Mask BF bit in SSPSTAT
|
|
|
288 |
xorlw b'00001100'
|
|
|
289 |
btfss STATUS,Z ; Are we in State3?
|
|
|
290 |
goto State4 ; No, check for next state.....
|
|
|
291 |
clrf Index ; Clear the buffer index.
|
|
|
292 |
LFSR RXBuffer,Index ; Point to the buffer
|
|
|
293 |
movf INDF,W ; Get the byte from buffer.
|
|
|
294 |
call WriteI2C ; Write the byte to SSPBUF
|
|
|
295 |
incf Index,F ; Increment the buffer index.
|
|
|
296 |
return
|
|
|
297 |
State4: ; Read operation, last byte was data,
|
|
|
298 |
banksel SSPCON ; buffer is empty.
|
|
|
299 |
btfsc SSPCON, CKP
|
|
|
300 |
goto State5
|
|
|
301 |
|
|
|
302 |
movlw b'00101100'
|
|
|
303 |
xorwf Temp,W
|
|
|
304 |
btfss STATUS,Z ; Are we in State4?
|
|
|
305 |
goto State5 ; No, check for next state....
|
|
|
306 |
movf Index,W ; Get the current buffer index.
|
|
|
307 |
sublw RX_BUF_LEN ; Subtract the buffer length.
|
|
|
308 |
btfsc STATUS,Z ; Has the index exceeded the buffer length?
|
|
|
309 |
clrf Index ; Yes, clear the buffer index.
|
|
|
310 |
LFSR RXBuffer,Index ; Point to the buffer
|
|
|
311 |
movf INDF,W ; Get the byte
|
|
|
312 |
call WriteI2C ; Write to SSPBUF
|
|
|
313 |
incf Index,F ; Increment the buffer index.
|
|
|
314 |
return
|
|
|
315 |
|
|
|
316 |
State5:
|
|
|
317 |
movf Temp,W ; NACK received when sending data to the master
|
|
|
318 |
andlw b'00101000' ; Mask RW bit in SSPSTAT
|
|
|
319 |
xorlw b'00101000' ;
|
|
|
320 |
btfss STATUS,Z ;
|
|
|
321 |
goto I2CErr ;
|
|
|
322 |
return ; If we aren?t in State5, then something is
|
|
|
323 |
; wrong.
|
|
|
324 |
I2CErr nop
|
|
|
325 |
banksel PORTB ; Something went wrong! Set LED
|
|
|
326 |
bsf PORTB,2 ; and loop forever. WDT will reset
|
|
|
327 |
goto $ ; device, if enabled.
|
|
|
328 |
return
|
|
|
329 |
;---------------------------------------------------------------------
|
|
|
330 |
; WriteI2C
|
|
|
331 |
;---------------------------------------------------------------------
|
|
|
332 |
WriteI2C
|
|
|
333 |
banksel SSPSTAT
|
|
|
334 |
btfsc SSPSTAT,BF ; Is the buffer full?
|
|
|
335 |
goto WriteI2C ; Yes, keep waiting.
|
|
|
336 |
banksel SSPCON ; No, continue.
|
|
|
337 |
DoI2CWrite
|
|
|
338 |
bcf SSPCON,WCOL ; Clear the WCOL flag.
|
|
|
339 |
movwf SSPBUF ; Write the byte in WREG
|
|
|
340 |
btfsc SSPCON,WCOL ; Was there a write collision?
|
|
|
341 |
goto DoI2CWrite
|
|
|
342 |
bsf SSPCON,CKP ; Release the clock.
|
|
|
343 |
return
|
|
|
344 |
|
|
|
345 |
end
|