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