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