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