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