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