1 ;######################################################################
2 ; i2clib.inc - I2C LIBRARY - IMPLEMENTATION
4 ; See i2clib.asm for documentation of the interface to this file.
6 include /usr/share/gputils/header/p18f458.inc
8 include ../iwjpictest/insn-aliases.inc
11 include ../iwjpictest/clockvaries.inc
13 include morse+auto.inc
15 ;======================================================================
20 ; m_... routines used by master only
21 ; s_... routines used by slave only
22 ; <any other name> routines used by both
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
29 ; [sm]_event_bad[_...] event handler which panics; called when i2c
30 ; controller did something unexpected
32 ; m_improper_... panics; called when main program
33 ; does something wrong
35 ; [ms]_<anything else> routines or labels of some other kind
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 `;...',
44 ; bt_f_if0 st, st_something
48 ; m_event_several_including_spong
49 ; bs_f st, st_sponging
50 ; bra metasyntacticing
55 ;============================================================
56 ; COMMON ADMINISTRATIVE ROUTINES and VARIABLES
62 sspcon2 res 1 ; master only
63 slave res 1 ; master only
64 slave_next res 1 ; master only
69 ; st is a bitmask, bit set in visible states:
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
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
93 improper_read_done_data
96 ;======================================================================
105 mov_lw 0x08 ; !SSPEN, Master mode
107 clr_f SSPCON2 ; nothing going
108 mov_lw 0x80 ; SMP(noslew), !CKE(!smbus)
110 bc_f IPR1, SSPIP ; low priority
117 ; We have an interrupt:
119 mov_ff SSPSTAT, sspstat
120 mov_ff SSPCON1, sspcon1
121 mov_ff SSPCON2, sspcon2
125 mov_lw (1<<WCOL) | (1<<SSPOV)
129 ; No ? Well, then the I2C should be idle now:
131 and_lw ~((1<<ACKSTAT) | (1<<ACKDT)) ; those two are ok if set
135 bt_f_if1 sspstat, R_W
138 bt_f_if1 st, st_stopping
139 bra m_event_done_stopping
141 bt_f_if1 st, st_starting
142 bra m_event_done_starting
145 bt_f_if1 st, st_addressing
146 bra m_event_done_addressing
148 bt_f_if1 st, st_writing
149 bra m_event_done_writing
151 bt_f_if1 st, st_acking
152 bra m_event_done_acking
154 bt_f_if1 st, st_reading
155 bra m_event_done_reading
160 ;========================================
161 ; MASTER - STARTING, ADDRESSING, STOPPING
165 ; st checked for busyness correct
166 ; st_reading/writing set unchanged
167 ; st_starting clear set
169 ; slave any slave_number
170 ; expects to return directly to main program (caller)
172 bra_z m_improper_slave
176 bra_nz m_improper_slave
181 m_event_done_starting
185 bt_f_if1 st, st_reading
186 bs_w 0 ; address bottom bit means read
190 bs_f st, st_addressing
194 m_event_done_addressing
195 bt_f_if1 sspcon2, ACKSTAT
196 bra m_bad_address_ack
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
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
214 m_event_done_stopping
227 ;========================================
233 ; State Idle Writing-Setup
236 bra m_improper_write_start
243 ; Did slave ack our byte ? It had better have done !
244 bt_f_if1 sspcon2, ACKSTAT
247 bs_f st, st_subsequent
250 m_event_done_addressing_write
252 ; st_addressing cleared
253 call i2cmu_write_next_byte
254 bra_z m_event_write_mustfinish
255 ; OK, we have the next byte:
261 m_event_write_mustfinish
262 bt_f_if0 st, st_subsequent
263 bra m_improper_write_finish
268 m_improper_write_start
272 m_improper_write_finish
275 ;========================================
281 ; State Idle Reading-Busy
284 bra m_read_start_busy
291 bt_f_if1 st, st_awaiting
297 ; Main program would like to address another slave.
299 bra_z m_improper_slave
303 m_event_done_addressing_read
304 m_event_done_acking_readmore
306 ; st_addressing/acking cleared
318 goto i2cmu_read_got_byte
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:
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
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
343 ; State Reading-Wait Stopping
346 bt_f_if0 st, st_awaiting
347 bra improper_read_done_data
356 bt_f_if1 st, st_reading
357 bra m_event_done_acking_readmore
361 ; ok, we want to read another:
366 bra m_start_or_restart
369 m_improper_read_another
372 ;======================================================================
377 ; W slave number undefined
381 mov_lw 0x16 ; !SSPEN, CKP(release), I2C 7-bit slave no-SP-int
383 mov_lw 0x01 ; !GCEN, SEN
385 mov_lw 0x80 ; SMP(noslew), !CKE(!smbus)
387 bs_f IPR1, SSPIP ; high priority
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
410 ;========================================
411 ; SLAVE - INTERRUPT HANDLING
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)
420 ; Labels of the form s_event_* are branches of the interrupt
421 ; handler and are supposed to finish with retfie_r.
425 chkvals_start macro what
429 chkval macro lastval, value, label
430 xor_lw value ^ lastval
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
438 chkvals_addrrecv_lastval equ 0x89
441 i2cs_interrupt ; 4cy interrupt latency + 3cy until branch to here
444 ; We have an interrupt:
446 ; Firstly, clear the interrupt flag so that if something else happens
447 ; while we faff, the interrupt will be regenerated:
452 mov_lw (1<<WCOL) | (1<<SSPOV)
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
460 bt_f_if1 st, st_writing
464 chkvals_start SSPSTAT
465 chkvals_addrrecv 0 ; 23cy to 1st insn of addrrecvread
467 panic morse_SS ; slave, interrupt, controller in bad state
470 panic morse_IH ; unknown high-priority interrupt
472 ;========================================
476 s_event_idle_addrrecvread
478 call QQ_i2csu_read_begin ; 26cy until 1st insn of read_begin
483 bs_f st, st_awaiting ; (probably)
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
494 s_event_reading_not_another
495 ; Whatever is happening, we're done reading now !
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
508 ; W byte for master any
509 ; State Transmit-Wait Transmit-Busy
513 bt_f_if0 st, st_awaiting
514 bra improper_read_done_data
519 ;========================================
523 s_event_idle_addrrecvwrite
524 bs_f SSPCON1, 3 ; we'll need the Stop interrupt
526 ; well, this is all fine so far, so do carry on:
529 ; W any byte from master
530 ; i2c controller waiting due to SEN etc continuing with next byte
537 chkvals_start SSPSTAT
538 chkval 0, 0xa9, s_event_writing_datarecv ; D,!P, S,W,BF
540 ; Well, we're done writing now in any case
542 bc_f SSPCON1, 3 ; no Start and Stop interrupts any more
543 call i2csu_write_done
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
550 ; First, the nice cases:
551 chkvals_start SSPSTAT
556 and_lw 0xc7 ; ?D_A, ?P; ?S
557 xor_lw 0x80 ; SMP, !CKE, !R_W, !UA, !BF
562 s_event_writing_datarecv
563 rcall s_write_slurpbyte
565 bt_f_if1 st, st_subsequent
566 bra s_event_writing_datarecv_subsequent
568 bs_f st, st_subsequent
569 call i2csu_write_begin
572 s_event_writing_datarecv_subsequent
573 rcall i2csu_write_another
577 ;======================================================================