chiark / gitweb /
i2clib compiles
[trains.git] / detpic / i2clib.asm
1 ;######################################################################
2 ; i2clib.inc - I2C LIBRARY - IMPLEMENTATION
3 ;
4 ; See i2clib.asm for documentation of the interface to this file.
5
6  include /usr/share/gputils/header/p18f458.inc
7  radix dec
8  include ../iwjpictest/insn-aliases.inc
9  include panic.inc
10
11 ;======================================================================
12 ; NOTATION
13
14 ; Naming conventions
15 ;
16 ;  m_...                        routines used by master only
17 ;  s_...                        routines used by slave only
18 ;  <any other name>             routines used by both
19 ;
20 ;  [ms]_event_...               event handler, branched to from interrupt
21 ;                                handler; conditions are as in the name;
22 ;                                should `return' at end which will return
23 ;                                from i2c[ms]_interrupt
24 ;
25 ;  [sm]_event_bad[_...]         event handler which panics; called when i2c
26 ;                                controller did something unexpected
27 ;
28 ;  m_improper_...               panics; called when main program
29 ;                                does something wrong
30 ;
31 ;  [ms]_<anything else>         routines or labels of some other kind
32
33 ; Whenever flow does not pass past the end of some code, we
34 ; have a boundary `;----------', and when flow passes past
35 ; an important label we sometimes mark it specially with `;...',
36 ; like this:
37 ;
38 ;               ;----------
39 ;               m_event_spong
40 ;                               bt_f_if0 i2c_st, st_something
41 ;                               bra     m_event_bad
42 ;               ;...
43 ;
44 ;               m_event_several_including_spong
45 ;                               bs_f    i2c_st, st_sponging
46 ;                               bra     metasyntacticing
47 ;
48 ;               ;----------
49 ;               m_event_wombat
50
51 ;============================================================
52 ; COMMON ADMINISTRATIVE ROUTINES and VARIABLES
53
54                 udata_acs
55
56 i2c_sspstat             res     1
57 i2c_sspcon1             res     1
58 i2c_sspcon2             res     1       ; master only
59 i2c_slave               res     1       ; master only
60
61 i2c_st                  res     1
62
63 ; i2c_st is a bitmask, bit set in visible states:
64                   ;    master                          slave
65 st_starting     equ 7 ; Writing-Setup?,Reading-Busy?
66 st_addressing   equ 6 ; Writing-Setup?,Reading-Busy?
67 st_writing      equ 5 ; Writing-*                       [Idle-going-]Receiving
68 st_subsequent   equ 4 ; Writing?                        Receiving
69 st_reading      equ 3 ; Reading-*                       Transmitting
70 st_awaiting     equ 2 ; Reading-Wait
71 st_acking       equ 1 ; Reading-Busy?,Stopping(from read)
72 st_stopping     equ 0 ; Stopping
73                   ; ...? means not always set in that state
74
75                 code
76
77 ;----------
78 slave2addr
79 ; computes slave address in form suitable for use in i2c controller
80 ; actual i2c slave address is (slave number) + 0b0001000
81 ;       W               slave number            i2c address * 2
82                 add_lw  b'0001000'
83                 rlc_w
84                 return
85
86 ;======================================================================
87 ; MASTER
88
89 ;----------
90 i2cm_init
91                 mov_lw  100-1   ; baud rate = Fosc/(4*(SSPADD+1))
92                 mov_wf  SSPADD  ; Fosc=20MHz, so SSPADD==99 means 50kbit/s
93                 mov_lw  0x08    ; !SSPEN, Master mode
94                 mov_wf  SSPCON1
95                 clr_f   SSPCON2 ; nothing going
96                 mov_lw  0x80    ; SMP(noslew), !CKE(!smbus)
97                 bra     init_enable
98
99 ;----------
100 i2cm_interrupt
101                 bt_f_if0 PIR1, SSPIF
102                 return
103                 ; We have an interrupt:
104
105                 mov_ff  SSPSTAT, i2c_sspstat
106                 mov_ff  SSPCON1, i2c_sspcon1
107                 mov_ff  SSPCON2, i2c_sspcon2
108
109                 bt_f_if1 i2c_sspcon1, WCOL
110                 bra_z   m_event_bad
111                 bt_f_if1 i2c_sspcon1, SSPOV
112                 bra_z   m_event_bad
113
114                 ; No ?  Well, then the I2C should be idle now:
115                 mov_fw  i2c_sspcon2
116                 and_lw  ~0x60 ; ACKSTAT,ACKDT
117                 bra_nz  m_event_bad
118                 ; OK...
119
120                 bt_f_if1 i2c_sspstat, R_W
121                 bra_nz  m_event_bad
122
123                 bt_f_if1 i2c_st, st_stopping
124                 bra     m_event_done_stopping
125
126                 bt_f_if1 i2c_st, st_starting
127                 bra     m_event_done_starting
128                 ; not just done SEN
129
130                 bt_f_if1 i2c_st, st_addressing
131                 bra     m_event_done_addressing
132
133                 bt_f_if1 i2c_st, st_writing
134                 bra     m_event_done_writing
135
136                 bt_f_if1 i2c_st, st_acking
137                 bra     m_event_done_acking
138
139                 bt_f_if1 i2c_st, st_reading
140                 bra     m_event_done_reading
141
142 m_event_bad
143                 panic   morse_SM
144
145 ;========================================
146 ; MASTER - STARTING, ADDRESSING, STOPPING
147
148 ;----------
149 m_start
150 ;       i2c_st                  checked for busyness    correct
151 ;       st_reading/writing      set                     unchanged
152 ;       st_starting             clear                   set
153 ;       W                       slave number            any
154 ;       i2c_slave               any                     slave_number
155 ; expects to return directly to main program (caller)
156                 mov_wf  i2c_slave
157                 and_lw  31
158                 bra_nz  m_improper_slave
159                 bs_f    i2c_st, st_starting
160                 bs_f    SSPCON2, SEN
161                 return
162
163 ;----------
164 m_event_done_starting
165                 mov_fw  i2c_slave
166                 rcall   slave2addr
167
168                 bt_f_if1 i2c_st, st_reading
169                 bs_w    0       ; address bottom bit means read
170
171                 mov_wf  SSPBUF
172                 bc_f    i2c_st, st_starting
173                 bs_f    i2c_st, st_addressing
174                 return
175
176 ;----------
177 m_event_done_addressing
178                 bt_f_if1 i2c_sspcon2, ACKSTAT
179                 bra     m_bad_address_ack
180                 ; OK, we got ack.
181
182                 bc_f    i2c_st, st_addressing
183                 bt_f_if1 i2c_st, st_reading
184                 bra     m_event_done_addressing_read
185                 bra     m_event_done_addressing_write
186
187 ;----------
188 m_stop
189 ;       st_stopping                     clear           set
190 ;       st_reading/acking/writing       any             unchanged
191 ; expects to return directly to main program or to end interrupt handler
192                 bs_f    i2c_st, st_stopping
193                 bs_f    SSPCON2, PEN
194                 return
195
196 ;----------
197 m_event_done_stopping
198                 clr_f   i2c_st
199                 goto    i2cmu_done
200
201 ;----------
202 m_bad_address_ack
203                 panic   morse_SK
204
205 ;----------
206 m_improper_slave
207 ;       i2c_slave               slave number
208                 panic   morse_SN
209
210 ;========================================
211 ; MASTER - WRITING
212
213 ;----------
214 i2cm_write_start
215 ;                               At call         On return
216 ;   State                       Idle            Writing-Setup
217 ;   W                           slave number    any
218                 tst_f_ifnz i2c_st
219                 bra     m_improper_write_start
220
221                 bs_f    i2c_st, st_writing
222                 bra     m_start
223
224 ;----------
225 m_event_done_writing
226                 ; Did slave ack our byte ?  It had better have done !
227                 bt_f_if1 i2c_sspcon2, ACKSTAT
228                 bra     m_event_bad
229
230                 bs_f    i2c_st, st_subsequent
231 ;...
232
233 m_event_done_addressing_write
234 ;       ACKSTAT         checked
235 ;       st_addressing   cleared
236                 call    i2cmu_write_next_byte
237                 bra_z   m_event_write_mustfinish
238                 ; OK, we have the next byte:
239
240                 mov_wf  SSPBUF
241                 return
242
243 ;----------
244 m_event_write_mustfinish
245                 bt_f_if0 i2c_st, st_subsequent
246                 bra     m_improper_write_finish
247
248                 bra     m_stop
249
250 ;----------
251 m_improper_write_start
252                 panic   morse_SW
253
254 ;----------
255 m_improper_write_finish
256                 panic   morse_SF
257
258 ;========================================
259 ; MASTER - READING
260
261 ;----------
262 i2cm_read_start
263 ;                               At call         On return
264 ;       State                   Idle            Reading-Busy
265 ;       W                       slave number    any
266                 tst_f_ifnz i2c_st
267                 bra     m_improper_read_start
268
269                 bs_f    i2c_st, st_reading
270                 bra     m_start
271
272 ;----------
273 m_event_done_addressing_read
274 m_event_done_acking_readmore
275 ;       ACKSTAT                 checked
276 ;       st_addressing/acking    cleared
277                 bs_f    SSPCON2, RCEN
278                 return
279
280 ;----------
281 m_event_done_reading
282                 bt_f_if0 i2c_sspstat, BF
283                 bra     m_event_bad
284
285                 mov_fw  SSPBUF
286
287                 bs_f    i2c_st, st_awaiting
288                 goto    i2cmu_read_got_byte
289
290 ;----------
291 i2cm_read_another
292 ;   State                       Reading-Wait    Reading-Busy
293                 bt_f_if0 i2c_st, st_awaiting
294                 bra     m_improper_read_another
295                 ; OK, we're fine to read another:
296 ;...
297
298 m_read_ack
299 ;       st_reading              1 iff not done          unchanged
300 ;       st_awaiting             still set               cleared
301 ;       st_acking               clear                   set
302 ; expects to return directly to main program or to end interrupt handler
303                 bc_f    i2c_st, st_awaiting
304                 bs_f    i2c_st, st_acking
305                 bc_f    SSPCON2, ACKDT ; ACKDT=0 means to acknowledge
306                 bs_f    SSPCON2, ACKEN
307                 return
308
309 ;----------
310 i2cm_read_done
311 ;   State                       Reading-Wait    Stopping
312                 bc_f    i2c_st, st_reading
313                 
314                 bt_f_if0 i2c_st, st_awaiting
315                 bra     m_improper_read_done
316                 ; OK:
317
318                 bra     m_read_ack
319
320 ;----------
321 m_event_done_acking
322                 bc_f    i2c_st, st_acking
323
324                 bt_f_if1 i2c_st, st_reading
325                 bra     m_event_done_acking_readmore
326
327                 bra     m_stop
328
329 ;----------
330 m_improper_read_start
331                 panic   morse_SR
332
333 ;----------
334 m_improper_read_another
335                 panic   morse_SA
336
337 ;----------
338 m_improper_read_done
339                 panic   morse_SD
340
341 ;======================================================================
342 ; SLAVE
343
344 ;----------
345 i2cs_init
346 ;       W               slave number            undefined
347                 rcall   slave2addr
348                 mov_wf  SSPADD
349                 clr_f   i2c_st
350                 mov_lw  0x16 ; !SSPEN, CKP(release), I2C 7-bit slave no-SP-int
351                 mov_wf  SSPCON1
352                 mov_lw  0x01 ; !GCEN, SEN
353                 mov_wf  SSPCON2
354                 mov_lw  0x8 ; SMP(noslew), !CKE(!smbus)
355                 mov_wf  SSPSTAT
356 init_enable
357 ; Actually engages the I2C controller, which must already have
358 ; been set up (all but SSPEN):
359 ;  SSPADD,SSPCON1,SSPCON2       configured correctly    unchanged
360 ;       SSPSTAT                 configured correctly    unchanged, except:
361 ;       SSPSTAT<SSPEN>          0 (disabled)            1 (enabled)
362 ;       SSPIE                   0 (disabled)            1 (enabled)
363 ;       TRISB<1,0>              any                     configured for I2C
364 ;       SSPIP                   any                     configured correctly
365 ;       GIEL                    0 (disabled)            0 (disabled)
366 ;       ssp* shadows            any                     all bits set
367                 set_f   i2c_sspstat
368                 set_f   i2c_sspcon1
369                 set_f   i2c_sspcon2
370                 bs_f    TRISB, 0
371                 bs_f    TRISB, 1
372                 bc_f    IPR1, SSPIP
373                 bs_f    SSPCON1, SSPEN
374                 bs_f    PIE1, SSPIE
375                 return
376
377 ;========================================
378 ; SLAVE - INTERRUPT HANDLING
379
380 ; In general, we figure out our state and then see what kind of events
381 ; we were expecting.  Bits we want to check:
382 ;       80    60    20    10            08    04    02    01
383 ;       SMP   CKE   D_A   P             S     R_W   UA    BF
384 ;       set   clr   data? stop          start read? clr   full?
385 ; (we don't usually mention SMP, CKE and UA below)
386
387 ; Some macros:
388
389 chkvals_start_sspstat macro
390                 mov_fw  i2c_sspstat
391                 endm
392
393 chkval macro lastval, value, label
394                 xor_lw  value ^ lastval
395                 bra_z   label
396                 endm
397
398 chkvals_addrrecv macro lastval
399         chkval  lastval, 0x8c, s_event_idle_addrrecvread ; A,!P, S,R,!BF
400         chkval  0x8c,    0x89, s_event_idle_addrrecvwrite ; A,!P, S,W,BF
401         endm
402 chkvals_addrrecv_lastval equ 0x89
403
404 ;----------
405 i2cs_interrupt
406                 bt_f_if0 PIR1, SSPIF
407                 return
408                 ; We have an interrupt:
409
410                 bt_f_if1 i2c_sspcon1, WCOL
411                 bra_z   s_event_bad
412                 bt_f_if1 i2c_sspcon1, SSPOV
413                 bra_z   s_event_bad
414
415 ; Firstly, clear the interrupt flag so that if something else happens
416 ; while we faff, the interrupt will be regenerated:
417                 bc_f    PIR1, SSPIF
418
419                 mov_ff  SSPSTAT, i2c_sspstat
420                 mov_ff  SSPCON1, i2c_sspcon1
421
422                 bt_f_if0 i2c_st, st_reading
423                 bra     s_event_reading
424
425                 bt_f_if0 i2c_st, st_writing
426                 bra     s_event_writing
427
428 s_event_idle
429                 chkvals_start_sspstat
430                 chkvals_addrrecv 0
431 s_event_bad
432                 panic   morse_SS ; slave, interrupt, controller in bad state
433
434 ;========================================
435 ; SLAVE - READING
436
437 ;----------
438 s_event_idle_addrrecvread
439                 bs_f    i2c_st, st_reading
440                 call    i2csu_read_begin
441                 bra     s_events_reading_datasend
442
443 ;----------
444 s_event_reading
445                 chkvals_start_sspstat
446                 chkval  0, 0xac, s_event_reading_datasent ; D,!P, S,R,!BF
447
448                 ; Whatever is happening, we're done reading now !
449                 clr_f   i2c_st
450                 call    i2csu_read_done
451
452                 chkvals_start_sspstat
453                 chkval  0, 0xa8, s_event_reading_datanack ; D,!P, S,!R,!BF
454                 ; Or, maybe it was nack and then we were reselected:
455                 chkvals_addrrecv 0xa8
456
457                 bra     s_event_bad
458
459 ;----------
460 s_event_reading_datasent
461                 call    i2csu_read_another
462 s_events_reading_datasend
463                 mov_wf  SSPBUF
464                 bs_f    SSPCON1, CKP
465 s_event_reading_datanack
466                 return
467
468 ;========================================
469 ; SLAVE - WRITING
470
471 ;----------
472 s_event_idle_addrrecvwrite
473                 bs_f    SSPCON1, 3 ; we'll need the Stop interrupt
474                 bs_f    i2c_st, st_writing
475                 ; well, this is all fine so far, so do carry on:
476
477 s_write_slurpbyte
478 ;       W               any                     byte from master
479 ;       i2c controller  waiting due to SEN etc  continuing with next byte
480                 mov_fw  SSPBUF
481                 bs_f    SSPCON1, CKP
482                 return
483
484 ;----------
485 s_event_writing
486                 chkvals_start_sspstat
487                 chkval  0, 0xa9, s_event_writing_datarecv ; D,!P, S,W,BF
488
489                 ; Well, we're done writing now in any case
490                 clr_f   i2c_st
491                 bc_f    SSPCON1, 3 ; no Start and Stop interrupts any more
492                 call    i2csu_write_done
493
494                 ; Who knows what might have happened.  We may have
495                 ; missed a number of S and P due to delay between
496                 ; clearing SSPIF and SSPM3(s&p-intrs) so we can't be
497                 ; too picky.
498
499                 ; First, the nice cases:
500                 chkvals_start_sspstat
501                 chkvals_addrrecv 0
502
503                 ; Then random junk:
504                 mov_fw  i2c_sspstat
505                 and_lw  0xc7 ; ?D_A, ?P; ?S
506                 xor_lw  0x80 ; SMP, !CKE, !R_W, !UA, !BF
507                 bt_f_if1 STATUS, Z
508                 return
509                 ; no good
510
511                 bra     s_event_bad
512
513 ;----------
514 s_event_writing_datarecv
515                 rcall   s_write_slurpbyte
516
517                 bt_f_if1 i2c_st, st_subsequent
518                 goto    i2csu_write_another
519                 ; not subsequent (yet):
520
521                 bs_f    i2c_st, st_subsequent
522                 goto    i2csu_write_begin
523
524 ;======================================================================
525
526  include panic.fin
527  include i2clib.inc
528
529  end