chiark / gitweb /
qq stuff - write to a buffer we can read
[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                 bc_f    IPR1, SSPIP ; low priority
111                 bra     init_enable
112
113 ;----------
114 i2cm_interrupt
115                 bt_f_if0 PIR1, SSPIF
116                 return
117                 ; We have an interrupt:
118
119                 mov_ff  SSPSTAT, sspstat
120                 mov_ff  SSPCON1, sspcon1
121                 mov_ff  SSPCON2, sspcon2
122
123                 bc_f    PIR1, SSPIF
124
125                 mov_lw  (1<<WCOL) | (1<<SSPOV)
126                 and_wfw sspcon1
127                 bra_nz  m_event_bad
128
129                 ; No ?  Well, then the I2C should be idle now:
130                 mov_fw  sspcon2
131                 and_lw  ~((1<<ACKSTAT) | (1<<ACKDT)) ; those two are ok if set
132                 bra_nz  m_event_bad
133                 ; OK...
134
135                 bt_f_if1 sspstat, R_W
136                 bra_nz  m_event_bad
137
138                 bt_f_if1 st, st_stopping
139                 bra     m_event_done_stopping
140
141                 bt_f_if1 st, st_starting
142                 bra     m_event_done_starting
143                 ; not just done SEN
144
145                 bt_f_if1 st, st_addressing
146                 bra     m_event_done_addressing
147
148                 bt_f_if1 st, st_writing
149                 bra     m_event_done_writing
150
151                 bt_f_if1 st, st_acking
152                 bra     m_event_done_acking
153
154                 bt_f_if1 st, st_reading
155                 bra     m_event_done_reading
156
157 m_event_bad
158                 panic   morse_SM
159
160 ;========================================
161 ; MASTER - STARTING, ADDRESSING, STOPPING
162
163 ;----------
164 m_start
165 ;       st                      checked for busyness    correct
166 ;       st_reading/writing      set                     unchanged
167 ;       st_starting             clear                   set
168 ;       W                       slave number            any
169 ;       slave                   any                     slave_number
170 ; expects to return directly to main program (caller)
171                 mov_wf  slave
172                 bra_z   m_improper_slave
173                 bs_f    SSPCON2, SEN
174 m_start_or_restart
175                 and_lw  ~31
176                 bra_nz  m_improper_slave
177                 bs_f    st, st_starting
178                 return
179
180 ;----------
181 m_event_done_starting
182                 mov_fw  slave
183                 rcall   slave2addr
184
185                 bt_f_if1 st, st_reading
186                 bs_w    0       ; address bottom bit means read
187
188                 mov_wf  SSPBUF
189                 bc_f    st, st_starting
190                 bs_f    st, st_addressing
191                 return
192
193 ;----------
194 m_event_done_addressing
195                 bt_f_if1 sspcon2, ACKSTAT
196                 bra     m_bad_address_ack
197                 ; OK, we got ack.
198
199                 bc_f    st, st_addressing
200                 bt_f_if1 st, st_reading
201                 bra     m_event_done_addressing_read
202                 bra     m_event_done_addressing_write
203
204 ;----------
205 m_stop
206 ;       st_stopping                     clear           set
207 ;       st_reading/acking/writing       any             unchanged
208 ; expects to return directly to main program or to end interrupt handler
209                 bs_f    st, st_stopping
210                 bs_f    SSPCON2, PEN
211                 return
212
213 ;----------
214 m_event_done_stopping
215                 clr_f   st
216                 goto    i2cmu_done
217
218 ;----------
219 m_bad_address_ack
220                 panic   morse_SK
221
222 ;----------
223 m_improper_slave
224 ;       slave                   slave number
225                 panic   morse_SN
226
227 ;========================================
228 ; MASTER - WRITING
229
230 ;----------
231 i2cm_write_start
232 ;                               At call         On return
233 ;   State                       Idle            Writing-Setup
234 ;   W                           slave number    any
235                 tst_f_ifnz st
236                 bra     m_improper_write_start
237
238                 bs_f    st, st_writing
239                 bra     m_start
240
241 ;----------
242 m_event_done_writing
243                 ; Did slave ack our byte ?  It had better have done !
244                 bt_f_if1 sspcon2, ACKSTAT
245                 bra     m_event_bad
246
247                 bs_f    st, st_subsequent
248 ;...
249
250 m_event_done_addressing_write
251 ;       ACKSTAT         checked
252 ;       st_addressing   cleared
253                 call    i2cmu_write_next_byte
254                 bra_z   m_event_write_mustfinish
255                 ; OK, we have the next byte:
256
257                 mov_wf  SSPBUF
258                 return
259
260 ;----------
261 m_event_write_mustfinish
262                 bt_f_if0 st, st_subsequent
263                 bra     m_improper_write_finish
264
265                 bra     m_stop
266
267 ;----------
268 m_improper_write_start
269                 panic   morse_SW
270
271 ;----------
272 m_improper_write_finish
273                 panic   morse_SF
274
275 ;========================================
276 ; MASTER - READING
277
278 ;----------
279 i2cm_read_start
280 ;                               At call         On return
281 ;       State                   Idle            Reading-Busy
282 ;       W                       slave number    any
283                 tst_f_ifnz st
284                 bra     m_read_start_busy
285
286                 bs_f    st, st_reading
287                 bra     m_start
288
289 ;----------
290 m_read_start_busy
291                 bt_f_if1 st, st_awaiting
292                 bra     m_read_different
293                 panic   morse_SR
294
295 ;----------
296 m_read_different
297 ; Main program would like to address another slave.
298                 mov_wf  slave_next
299                 bra_z   m_improper_slave
300                 bra     i2cm_read_done
301
302 ;----------
303 m_event_done_addressing_read
304 m_event_done_acking_readmore
305 ;       ACKSTAT                 checked
306 ;       st_addressing/acking    cleared
307                 bs_f    SSPCON2, RCEN
308                 return
309
310 ;----------
311 m_event_done_reading
312                 bt_f_if0 sspstat, BF
313                 bra     m_event_bad
314
315                 mov_fw  SSPBUF
316
317                 bs_f    st, st_awaiting
318                 goto    i2cmu_read_got_byte
319
320 ;----------
321 i2cm_read_another
322 ;   State                       Reading-Wait    Reading-Busy
323                 bt_f_if0 st, st_awaiting
324                 bra     m_improper_read_another
325                 ; OK, we're fine to read another:
326 ;...
327
328 m_read_ack
329 ;       st_reading              1 iff not done          unchanged
330 ;       st_awaiting             still set               cleared
331 ;       st_acking               clear                   set
332 ; expects to return directly to main program or to end interrupt handler
333                 bc_f    st, st_awaiting
334                 bs_f    st, st_acking
335                 bc_f    SSPCON2, ACKDT ; ACKDT=0 means to acknowledge
336                 bt_f_if0 st, st_reading
337                 bs_f    SSPCON2, ACKDT ; don't ack last byte
338                 bs_f    SSPCON2, ACKEN
339                 return
340
341 ;----------
342 i2cm_read_done
343 ;   State                       Reading-Wait    Stopping
344                 bc_f    st, st_reading
345                 
346                 bt_f_if0 st, st_awaiting
347                 bra     improper_read_done_data
348                 ; OK:
349
350                 bra     m_read_ack
351
352 ;----------
353 m_event_done_acking
354                 bc_f    st, st_acking
355
356                 bt_f_if1 st, st_reading
357                 bra     m_event_done_acking_readmore
358
359                 mov_fw  slave_next
360                 bra_z   m_stop
361 ; ok, we want to read another:
362                 mov_wf  slave
363                 clr_f   slave_next
364                 bs_f    st, st_reading
365                 bs_f    SSPCON2, RSEN
366                 bra     m_start_or_restart
367
368 ;----------
369 m_improper_read_another
370                 panic   morse_SA
371
372 ;======================================================================
373 ; SLAVE
374
375 ;----------
376 i2cs_init
377 ;       W               slave number            undefined
378                 rcall   slave2addr
379                 mov_wf  SSPADD
380                 clr_f   st
381                 mov_lw  0x16 ; !SSPEN, CKP(release), I2C 7-bit slave no-SP-int
382                 mov_wf  SSPCON1
383                 mov_lw  0x01 ; !GCEN, SEN
384                 mov_wf  SSPCON2
385                 mov_lw  0x80 ; SMP(noslew), !CKE(!smbus)
386                 mov_wf  SSPSTAT
387                 bs_f    IPR1, SSPIP ; high priority
388 init_enable
389 ; Actually engages the I2C controller, which must already have
390 ; been set up (all but SSPEN):
391 ;  SSPADD,SSPCON1,SSPCON2       configured correctly    unchanged
392 ;       SSPSTAT                 configured correctly    unchanged, except:
393 ;       SSPSTAT<SSPEN>          0 (disabled)            1 (enabled)
394 ;       SSPIE                   0 (disabled)            1 (enabled)
395 ;       SSPIF                   configured correctly    unchanged
396 ;       TRISB<1,0>              any                     configured for I2C
397 ;       SSPIP                   any                     configured correctly
398 ;       GIEL                    0 (disabled)            0 (disabled)
399 ;       ssp* shadows            any                     all bits set
400                 set_f   sspstat
401                 set_f   sspcon1
402                 set_f   sspcon2
403                 set_f   st_orig
404                 bs_f    TRISB, 0
405                 bs_f    TRISB, 1
406                 bs_f    SSPCON1, SSPEN
407                 bs_f    PIE1, SSPIE
408                 return
409
410 ;========================================
411 ; SLAVE - INTERRUPT HANDLING
412
413 ; In general, we figure out our state and then see what kind of events
414 ; we were expecting.  Bits we want to check:
415 ;       80    60    20    10            08    04    02    01
416 ;       SMP   CKE   D_A   P             S     R_W   UA    BF
417 ;       set   clr   data? stop          start read? clr   full?
418 ; (we don't usually mention SMP, CKE and UA below)
419
420 ; Labels of the form s_event_* are branches of the interrupt
421 ; handler and are supposed to finish with retfie_r.
422
423 ; Some macros:
424
425 chkvals_start macro what
426                 mov_fw  what
427                 endm
428
429 chkval macro lastval, value, label
430                 xor_lw  value ^ lastval
431                 bra_z   label
432                 endm
433
434 chkvals_addrrecv macro lastval
435         chkval  lastval, 0x8c, s_event_idle_addrrecvread ; A,!P, S,R,!BF
436         chkval  0x8c,    0x89, s_event_idle_addrrecvwrite ; A,!P, S,W,BF
437         endm
438 chkvals_addrrecv_lastval equ 0x89
439
440 ;----------
441 i2cs_interrupt  ; 4cy interrupt latency + 3cy until branch to here
442                 bt_f_if0 PIR1, SSPIF
443                 bra     s_event_bad_intr
444                 ; We have an interrupt:
445
446 ; Firstly, clear the interrupt flag so that if something else happens
447 ; while we faff, the interrupt will be regenerated:
448                 bc_f    PIR1, SSPIF
449
450                 mov_ff  st, st_orig
451
452                 mov_lw  (1<<WCOL) | (1<<SSPOV)
453                 and_wfw SSPCON1
454                 bra_nz  s_event_bad
455
456                 ; 8cy from entry to here, so total of 15cy
457                 bt_f_if1 st, st_reading
458                 bra     s_event_reading ; 18cy to 1st insn of event_reading
459
460                 bt_f_if1 st, st_writing
461                 bra     s_event_writing
462
463 s_event_idle
464                 chkvals_start SSPSTAT
465                 chkvals_addrrecv 0 ; 23cy to 1st insn of addrrecvread
466 s_event_bad
467                 panic   morse_SS ; slave, interrupt, controller in bad state
468
469 s_event_bad_intr
470                 panic   morse_IH ; unknown high-priority interrupt
471
472 ;========================================
473 ; SLAVE - READING
474
475 ;----------
476 s_event_idle_addrrecvread
477                 bs_f    st, st_awaiting
478                 call    QQ_i2csu_read_begin ; 26cy until 1st insn of read_begin
479                 retfie_r
480
481 ;----------
482 s_event_reading
483                 bs_f    st, st_awaiting ; (probably)
484
485                 mov_fw  SSPSTAT
486                 xor_lw  0xac ; D,!P, S,R,!BF
487                 bra_nz  s_event_reading_not_another
488                 call    QQ_i2csu_read_another
489                                 ; 24cy until 1st insn of i2csu_read_another
490 s_event_reading_datanack
491                 retfie_r
492
493 ;...
494 s_event_reading_not_another
495                 ; Whatever is happening, we're done reading now !
496                 clr_f   st
497                 call    i2csu_read_done
498
499                 chkvals_start SSPSTAT
500                 chkval  0, 0xa8, s_event_reading_datanack ; D,!P, S,!R,!BF
501                 ; Or, maybe it was nack and then we were reselected:
502                 chkvals_addrrecv 0xa8
503
504                 bra     s_event_bad
505
506 ;----------
507 i2cs_read_data
508 ;       W               byte for master         any
509 ;       State           Transmit-Wait           Transmit-Busy
510                 mov_wf  SSPBUF
511                 bs_f    SSPCON1, CKP
512                 
513                 bt_f_if0 st, st_awaiting
514                 bra     improper_read_done_data
515                 bc_f    st, st_awaiting
516                 bs_f    st, st_reading
517                 return
518
519 ;========================================
520 ; SLAVE - WRITING
521
522 ;----------
523 s_event_idle_addrrecvwrite
524                 bs_f    SSPCON1, 3 ; we'll need the Stop interrupt
525                 bs_f    st, st_writing
526                 ; well, this is all fine so far, so do carry on:
527
528 s_write_slurpbyte
529 ;       W               any                     byte from master
530 ;       i2c controller  waiting due to SEN etc  continuing with next byte
531                 mov_fw  SSPBUF
532                 bs_f    SSPCON1, CKP
533                 retfie_r
534
535 ;----------
536 s_event_writing
537                 chkvals_start SSPSTAT
538                 chkval  0, 0xa9, s_event_writing_datarecv ; D,!P, S,W,BF
539
540                 ; Well, we're done writing now in any case
541                 clr_f   st
542                 bc_f    SSPCON1, 3 ; no Start and Stop interrupts any more
543                 call    i2csu_write_done
544
545                 ; Who knows what might have happened.  We may have
546                 ; missed a number of S and P due to delay between
547                 ; clearing SSPIF and SSPM3(s&p-intrs) so we can't be
548                 ; too picky.
549
550                 ; First, the nice cases:
551                 chkvals_start SSPSTAT
552                 chkvals_addrrecv 0
553
554                 ; Then random junk:
555                 mov_fw  SSPSTAT
556                 and_lw  0xc7 ; ?D_A, ?P; ?S
557                 xor_lw  0x80 ; SMP, !CKE, !R_W, !UA, !BF
558                 bra_nz  s_event_bad
559                 retfie_r
560
561 ;----------
562 s_event_writing_datarecv
563                 rcall   s_write_slurpbyte
564
565                 bt_f_if1 st, st_subsequent
566                 bra     s_event_writing_datarecv_subsequent
567
568                 bs_f    st, st_subsequent
569                 call    i2csu_write_begin
570                 retfie_r
571
572 s_event_writing_datarecv_subsequent
573                 rcall   i2csu_write_another
574                 retfie_r
575
576
577 ;======================================================================
578
579  include panic.fin
580  include i2clib.inc
581
582  end