chiark / gitweb /
i2c finished we think but not yet compiled
[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
10 ;======================================================================
11 ; NOTATION
12
13 ; Naming conventions
14 ;
15 ;  m_...                        routines used by master only
16 ;  s_...                        routines used by slave only
17 ;  <any other name>             routines used by both
18 ;
19 ;  [ms]_event_...               event handler, branched to from interrupt
20 ;                                handler; conditions are as in the name;
21 ;                                should `return' at end which will return
22 ;                                from i2c[ms]_interrupt
23 ;
24 ;  [sm]_event_bad[_...]         event handler which panics; called when i2c
25 ;                                controller did something unexpected
26 ;
27 ;  m_improper_...               panics; called when main program
28 ;                                does something wrong
29 ;
30 ;  [ms]_<anything else>         routines or labels of some other kind
31
32 ; Whenever flow does not pass past the end of some code, we
33 ; have a boundary `;----------', and when flow passes past
34 ; an important label we sometimes mark it specially with `;...',
35 ; like this:
36 ;
37 ;               ;----------
38 ;               m_event_spong
39 ;                               bt_f_if0 i2c_st, st_something
40 ;                               bra     m_event_bad
41 ;               ;...
42 ;
43 ;               m_event_several_including_spong
44 ;                               bs_f    i2c_st, st_sponging
45 ;                               bra     metasyntacticing
46 ;
47 ;               ;----------
48 ;               m_event_wombat
49
50 ;============================================================
51 ; COMMON ADMINISTRATIVE ROUTINES and VARIABLES
52
53                 udata_acs
54
55 i2c_sspstat             res     1
56 i2c_sspcon1             res     1
57 i2c_sspcon2             res     1       ; master only
58 i2c_slave               res     1       ; master only
59
60 i2c_st                  res     1
61
62 ; i2c_st is a bitmask, bit set in visible states:
63                   ;    master                          slave
64 st_starting     equ 7 ; Writing-Setup?,Reading-Busy?
65 st_addressing   equ 6 ; Writing-Setup?,Reading-Busy?
66 st_writing      equ 5 ; Writing-*                       [Idle-going-]Receiving
67 st_subsequent   equ 4 ; Writing?                        Receiving
68 st_reading      equ 3 ; Reading-*                       Transmitting
69 st_awaiting     equ 2 ; Reading-Wait
70 st_acking       equ 1 ; Reading-Busy?,Stopping(from read)
71 st_stopping     equ 0 ; Stopping
72                   ; ...? means not always set in that state
73
74                 code
75
76 ;----------
77 slave2addr
78 ; computes slave address in form suitable for use in i2c controller
79 ; actual i2c slave address is (slave number) + 0b0001000
80 ;       W               slave number            i2c address * 2
81                 add_lw  0b0001000
82                 rlc_w
83                 return
84
85 ;======================================================================
86 ; MASTER
87
88 ;----------
89 i2cm_init
90                 mov_lw  100-1   ; baud rate = Fosc/(4*(SSPADD+1))
91                 mov_wf  SSPADD  ; Fosc=20MHz, so SSPADD==99 means 50kbit/s
92                 mov_lw  0x08    ; !SSPEN, Master mode
93                 mov_wf  SSPCON1
94                 clr_f   SSPCON2 ; nothing going
95                 mov_lw  0x80    ; SMP(noslew), !CKE(!smbus)
96                 bra     init_enable
97
98 ;----------
99 i2cm_interrupt
100                 bt_f_if0 PIR1, SSPIF
101                 return
102                 ; We have an interrupt:
103
104                 mov_ff  SSPSTAT, i2c_sspstat
105                 mov_ff  SSPCON1, i2c_sspcon1
106                 mov_ff  SSPCON2, i2c_sspcon2
107
108                 bt_f_if1 i2c_sspcon1, WCOL
109                 bra_z   m_event_bad
110                 bt_f_if1 i2c_sspcon1, SSPOV
111                 bra_z   m_event_bad
112
113                 ; No ?  Well, then the I2C should be idle now:
114                 mov_fw  i2c_sspcon2
115                 and_lw  ~0x60 ; ACKSTAT,ACKDT
116                 bra_nz  m_event_bad
117                 ; OK...
118
119                 bt_f_if1 i2c_sspstat, R_W
120                 bra_nz  m_event_bad
121
122                 bt_f_if1 i2c_st, st_stopping
123                 bra     m_event_done_stopping
124
125                 bt_f_if1 i2c_st, st_starting
126                 bra     m_event_done_starting
127                 ; not just done SEN
128
129                 bt_f_if1 i2c_st, st_addressing
130                 bra     m_event_done_addressing
131
132                 bt_f_if1 i2c_st, st_writing
133                 bra     m_event_done_writing
134
135                 bt_f_if1 i2c_st, st_acking
136                 bra     m_event_done_acking
137
138                 bt_f_if1 i2c_st, st_reading
139                 bra     m_event_done_reading
140
141 m_event_bad
142                 panic   morse_SM ; master, interrupt, controller in bad state
143
144 ;========================================
145 ; MASTER - STARTING, ADDRESSING, STOPPING
146
147 ;----------
148 m_start
149 ;       i2c_st                  checked for busyness    correct
150 ;       st_reading/writing      set                     unchanged
151 ;       st_starting             clear                   set
152 ;       W                       slave number            any
153 ;       i2c_slave               any                     slave_number
154 ; expects to return directly to main program (caller)
155                 mov_wf  i2c_slave
156                 and_lw  31
157                 bra_nz  m_improper_slave
158                 bs_f    i2c_st, st_starting
159                 bs_f    i2c_st, st_working
160                 bs_f    SSPCON2, SEN
161                 return
162
163 ;----------
164 m_event_done_starting
165                 mov_fw  i2c_slave
166                 bsr     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_do_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_do_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   slave2addr2
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 ms_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 chkval_lastvalue equ 0
392                 endm
393
394 chkval macro value, label
395                 xor_lw  value ^ chkval_lastvalue
396  chkval_lastvalue equ value
397                 bra_z   label
398                 endm
399
400 chkvals_addrrecv macro
401                 chkval  0x8c, s_event_idle_addrrecvread ; A,!P, S,R,!BF
402                 chkval  0x89, s_event_idle_addrrecvwrite ; A,!P, S,W,BF
403                 endm
404
405 ;----------
406 i2cs_interrupt
407                 bt_f_if0 PIR1, SSPIF
408                 return
409                 ; We have an interrupt:
410
411                 bt_f_if1 i2c_sspcon1, WCOL
412                 bra_z   s_event_bad
413                 bt_f_if1 i2c_sspcon1, SSPOV
414                 bra_z   s_event_bad
415
416 ; Firstly, clear the interrupt flag so that if something else happens
417 ; while we faff, the interrupt will be regenerated:
418                 bc_f    PIR1, SSPIF
419
420                 mov_ff  SSPSTAT, i2c_sspstat
421                 mov_ff  SSPCON1, i2c_sspcon1
422 ; Check that nothing obvious is wrong:
423                 bsr     check_wcolsspov
424
425                 bt_f_if0 i2c_st, st_reading
426                 bra     s_event_reading
427
428                 bt_f_if0 i2c_st, st_writing
429                 bra     s_event_writing
430
431 s_event_idle
432                 chkvals_start
433                 chkvals_addrrecv
434 s_event_bad
435                 panic   morse_SS ; slave, interrupt, controller in bad state
436
437 ;========================================
438 ; SLAVE - READING
439
440 ;----------
441 s_event_idle_addrrecvread
442                 bs_f    i2c_st, st_reading
443                 call    i2csu_read_begin
444                 bra     s_events_read_datasend
445
446 ;----------
447 s_event_reading
448                 chkvals_start_sspstat
449                 chkval  0xac, s_event_reading_datasent ; D,!P, S,R,!BF
450
451                 ; Whatever is happening, we're done reading now !
452                 clr_f   i2c_st
453                 call    i2csu_read_done
454
455                 chkval  0xa8, s_event_reading_datanack ; D,!P, S,!R,!BF
456                 ; Or, maybe it was nack and then we were reselected:
457                 chkvals_addrrecv
458
459                 bra     s_event_bad
460
461 ;----------
462 s_event_reading_datasent
463                 call    i2csu_read_another
464 s_events_reading_datasend
465                 mov_wf  SSPBUF
466                 bs_f    SSPCON1, CKP
467 s_event_reading_datanack
468                 return
469
470 ;========================================
471 ; SLAVE - WRITING
472
473 ;----------
474 s_event_idle_addrrecvwrite
475                 bs_f    SSPCON, 3 ; we'll need the Stop interrupt
476                 bs_f    i2c_st, st_writing
477                 ; well, this is all fine so far, so do carry on:
478
479 s_write_slurpbyte
480 ;       W               any                     byte from master
481 ;       i2c controller  waiting due to SEN etc  continuing with next byte
482                 mov_fw  SSPBUF
483                 bs_f    SSPCON1, CKP
484                 return
485
486 ;----------
487 s_event_writing
488                 chkvals_start_sspstat
489                 chkval  0xa9, s_event_writing_datarecv ; D,!P, S,W,BF
490
491                 ; Well, we're done writing now in any case
492                 clr_f   i2c_st
493                 bc_f    SSPCON1, 3 ; no Start and Stop interrupts any more
494                 call    i2csu_write_done
495
496                 ; Who knows what might have happened.  We may have
497                 ; missed a number of S and P due to delay between
498                 ; clearing SSPIF and SSPM3(s&p-intrs) so we can't be
499                 ; too picky.
500
501                 ; First, the nice cases:
502                 chkvals_start_sspstat
503                 chkvals_addrrecv
504
505                 ; Then random junk:
506                 mov_fw  i2c_sspstat
507                 and_lw  0xc7 ; ?D_A, ?P; ?S
508                 xor_lw  0x80 ; SMP, !CKE, !R_W, !UA, !BF
509                 bt_f_if1 STATUS, Z
510                 return
511                 ; no good
512
513                 bra     s_event_bad
514
515 ;----------
516 s_event_writing_datarecv
517                 rcall   s_write_slurpbyte
518
519                 bt_f_if1 i2c_st, st_subsequent
520                 goto    i2csu_write_another
521                 ; not subsequent (yet):
522
523                 bs_f    i2c_st, st_subsequent
524                 goto    i2csu_write_begin
525
526 ;======================================================================
527
528  include i2clib.inc
529  end