;
; See i2clib.asm for documentation of the interface to this file.
- include /usr/share/gputils/header/p18f458.inc
- radix dec
- include ../iwjpictest/insn-aliases.inc
- include panic.inc
+ include /usr/share/gputils/header/p18f458.inc
+ radix dec
+ include ../iwjpictest/insn-aliases.inc
+
+ include ../iwjpictest/clockvaries.inc
+ include panic.inc
+ include morse+auto.inc
+ include i2clib.incm
;======================================================================
; NOTATION
;
; ;----------
; m_event_spong
-; bt_f_if0 i2c_st, st_something
+; bt_f_if0 st, st_something
; bra m_event_bad
; ;...
;
; m_event_several_including_spong
-; bs_f i2c_st, st_sponging
+; bs_f st, st_sponging
; bra metasyntacticing
;
; ;----------
udata_acs
-i2c_sspstat res 1
-i2c_sspcon1 res 1
-i2c_sspcon2 res 1 ; master only
-i2c_slave res 1 ; master only
-
-i2c_st res 1
-
-; i2c_st is a bitmask, bit set in visible states:
- ; master slave
-st_starting equ 7 ; Writing-Setup?,Reading-Busy?
-st_addressing equ 6 ; Writing-Setup?,Reading-Busy?
-st_writing equ 5 ; Writing-* [Idle-going-]Receiving
-st_subsequent equ 4 ; Writing? Receiving
-st_reading equ 3 ; Reading-* Transmitting
+sspstat res 1 ; master only
+sspcon1 res 1 ; master only
+sspcon2 res 1 ; master only
+slave res 1 ; master only
+slave_next res 1 ; master only
+
+st res 1
+st_orig res 1
+
+; st is a bitmask, bit set in visible states:
+ ; master
+st_starting equ 7 ; Writing-Setup?, Reading-Busy?
+st_addressing equ 6 ; Writing-Setup?, Reading-Busy?
+st_writing equ 5 ; Writing-*, Stopping(after Reading-Wait:write_start)
+st_subsequent equ 4 ; Writing?
+st_reading equ 3 ; Reading-*
st_awaiting equ 2 ; Reading-Wait
-st_acking equ 1 ; Reading-Busy?,Stopping(from read)
+st_acking equ 1 ; Reading-Busy?, Stopping(from read)
st_stopping equ 0 ; Stopping
; ...? means not always set in that state
rlc_w
return
+;----------
+improper_read_done_data
+ i2cpanic morse_SD
+
;======================================================================
; MASTER
;----------
i2cm_init
- mov_lw 100-1 ; baud rate = Fosc/(4*(SSPADD+1))
- mov_wf SSPADD ; Fosc=20MHz, so SSPADD==99 means 50kbit/s
+ mov_lw i2c_sspadd
+ mov_wf SSPADD
+ clr_f st
+ clr_f slave_next
mov_lw 0x08 ; !SSPEN, Master mode
mov_wf SSPCON1
clr_f SSPCON2 ; nothing going
mov_lw 0x80 ; SMP(noslew), !CKE(!smbus)
+ mov_wf SSPSTAT
+ bc_f IPR1, SSPIP ; low priority
bra init_enable
;----------
bt_f_if0 PIR1, SSPIF
return
; We have an interrupt:
+;...
+i2cm_interrupt_definite
+ mov_ff SSPSTAT, sspstat
+ mov_ff SSPCON1, sspcon1
+ mov_ff SSPCON2, sspcon2
- mov_ff SSPSTAT, i2c_sspstat
- mov_ff SSPCON1, i2c_sspcon1
- mov_ff SSPCON2, i2c_sspcon2
+ bc_f PIR1, SSPIF
- bt_f_if1 i2c_sspcon1, WCOL
- bra_z m_event_bad
- bt_f_if1 i2c_sspcon1, SSPOV
- bra_z m_event_bad
+ mov_lw (1<<WCOL) | (1<<SSPOV)
+ and_wfw sspcon1
+ bra_nz m_event_bad
; No ? Well, then the I2C should be idle now:
- mov_fw i2c_sspcon2
- and_lw ~0x60 ; ACKSTAT,ACKDT
+ mov_fw sspcon2
+ and_lw ~((1<<ACKSTAT) | (1<<ACKDT)) ; those two are ok if set
bra_nz m_event_bad
; OK...
- bt_f_if1 i2c_sspstat, R_W
+ bt_f_if1 sspstat, R_W
bra_nz m_event_bad
- bt_f_if1 i2c_st, st_stopping
+ bt_f_if1 st, st_stopping
bra m_event_done_stopping
- bt_f_if1 i2c_st, st_starting
+ bt_f_if1 st, st_starting
bra m_event_done_starting
; not just done SEN
- bt_f_if1 i2c_st, st_addressing
+ bt_f_if1 st, st_addressing
bra m_event_done_addressing
- bt_f_if1 i2c_st, st_writing
- bra m_event_done_writing
-
- bt_f_if1 i2c_st, st_acking
+ bt_f_if1 st, st_acking
bra m_event_done_acking
- bt_f_if1 i2c_st, st_reading
+ bt_f_if1 st, st_writing
+ bra m_event_done_writing
+
+ bt_f_if1 st, st_reading
bra m_event_done_reading
m_event_bad
- panic morse_SM
+ i2cpanic morse_SM
;========================================
; MASTER - STARTING, ADDRESSING, STOPPING
;----------
m_start
-; i2c_st checked for busyness correct
-; st_reading/writing set unchanged
+; st checked for busyness correct
+; st_reading/writing one set, one clear unchanged
; st_starting clear set
; W slave number any
-; i2c_slave any slave_number
+; slave any slave_number
; expects to return directly to main program (caller)
- mov_wf i2c_slave
- and_lw 31
- bra_nz m_improper_slave
- bs_f i2c_st, st_starting
+ mov_wf slave
bs_f SSPCON2, SEN
+m_start_or_restart
+ and_lw ~31
+ bra_nz m_improper_slave
+ bs_f st, st_starting
+ tst_f_ifnz slave
return
+ ; oops:
+;...
+
+m_improper_slave
+; slave slave number
+ i2cpanic morse_SN
+
;----------
m_event_done_starting
- mov_fw i2c_slave
+ mov_fw slave
rcall slave2addr
- bt_f_if1 i2c_st, st_reading
+ bt_f_if1 st, st_reading
bs_w 0 ; address bottom bit means read
mov_wf SSPBUF
- bc_f i2c_st, st_starting
- bs_f i2c_st, st_addressing
+ bc_f st, st_starting
+ bs_f st, st_addressing
return
;----------
m_event_done_addressing
- bt_f_if1 i2c_sspcon2, ACKSTAT
- bra m_bad_address_ack
+ bt_f_if1 sspcon2, ACKSTAT
+ bra m_no_address_ack
; OK, we got ack.
- bc_f i2c_st, st_addressing
- bt_f_if1 i2c_st, st_reading
+ bc_f st, st_addressing
+ bt_f_if1 st, st_reading
bra m_event_done_addressing_read
bra m_event_done_addressing_write
; st_stopping clear set
; st_reading/acking/writing any unchanged
; expects to return directly to main program or to end interrupt handler
- bs_f i2c_st, st_stopping
+ bs_f st, st_stopping
bs_f SSPCON2, PEN
return
;----------
m_event_done_stopping
- clr_f i2c_st
+ clr_f st
goto i2cmu_done
;----------
-m_bad_address_ack
- panic morse_SK
+m_no_address_ack
+ bt_f_if0 st, st_reading
+ bra m_bad_no_address_ack_write
+ clr_f st
+ rcall m_stop
+ goto i2cmu_slave_no_ack
-;----------
-m_improper_slave
-; i2c_slave slave number
- panic morse_SN
+m_bad_no_address_ack_write
+ panic morse_SW
;========================================
; MASTER - WRITING
;----------
i2cm_write_start
; At call On return
-; State Idle Writing-Setup
+; State Idle/Reading-Wait Writing-Setup
; W slave number any
- tst_f_ifnz i2c_st
- bra m_improper_write_start
+ tst_f_ifnz st
+ bra m_write_start_busy
- bs_f i2c_st, st_writing
+ bs_f st, st_writing
bra m_start
;----------
m_event_done_writing
; Did slave ack our byte ? It had better have done !
- bt_f_if1 i2c_sspcon2, ACKSTAT
+ bt_f_if1 sspcon2, ACKSTAT
bra m_event_bad
- bs_f i2c_st, st_subsequent
+ bs_f st, st_subsequent
;...
m_event_done_addressing_write
;----------
m_event_write_mustfinish
- bt_f_if0 i2c_st, st_subsequent
+ bt_f_if0 st, st_subsequent
bra m_improper_write_finish
bra m_stop
-;----------
-m_improper_write_start
- panic morse_SW
-
;----------
m_improper_write_finish
- panic morse_SF
+ i2cpanic morse_SF
;========================================
; MASTER - READING
; At call On return
; State Idle Reading-Busy
; W slave number any
- tst_f_ifnz i2c_st
- bra m_improper_read_start
+ tst_f_ifnz st
+ bra m_read_start_busy
- bs_f i2c_st, st_reading
+ bs_f st, st_reading
bra m_start
+;----------
+m_write_start_busy
+ bs_f st, st_writing
+m_read_start_busy
+ bt_f_if1 st, st_awaiting
+ bra m_address_different
+ i2cpanic morse_SB
+
+;----------
+m_address_different
+; Main program would like to address another slave for reading.
+ mov_wf slave_next
+ tst_f_ifnz slave_next
+ bra i2cm_read_done
+ panic morse_SO
+
;----------
m_event_done_addressing_read
m_event_done_acking_readmore
;----------
m_event_done_reading
- bt_f_if0 i2c_sspstat, BF
+ bt_f_if0 sspstat, BF
bra m_event_bad
mov_fw SSPBUF
- bs_f i2c_st, st_awaiting
+ bs_f st, st_awaiting
goto i2cmu_read_got_byte
;----------
i2cm_read_another
; State Reading-Wait Reading-Busy
- bt_f_if0 i2c_st, st_awaiting
+ bt_f_if0 st, st_awaiting
bra m_improper_read_another
; OK, we're fine to read another:
;...
; st_awaiting still set cleared
; st_acking clear set
; expects to return directly to main program or to end interrupt handler
- bc_f i2c_st, st_awaiting
- bs_f i2c_st, st_acking
+ bc_f st, st_awaiting
+ bs_f st, st_acking
bc_f SSPCON2, ACKDT ; ACKDT=0 means to acknowledge
+ bt_f_if0 st, st_reading
+ bs_f SSPCON2, ACKDT ; don't ack last byte
bs_f SSPCON2, ACKEN
return
;----------
i2cm_read_done
; State Reading-Wait Stopping
- bc_f i2c_st, st_reading
+ bc_f st, st_reading
- bt_f_if0 i2c_st, st_awaiting
- bra m_improper_read_done
+ bt_f_if0 st, st_awaiting
+ bra improper_read_done_data
; OK:
bra m_read_ack
;----------
m_event_done_acking
- bc_f i2c_st, st_acking
+ bc_f st, st_acking
- bt_f_if1 i2c_st, st_reading
+ bt_f_if1 st, st_reading
bra m_event_done_acking_readmore
- bra m_stop
-
-;----------
-m_improper_read_start
- panic morse_SR
+ mov_fw slave_next
+ bra_z m_stop
+; ok, we want to read another:
+ mov_wf slave
+ clr_f slave_next
+ bt_f_if0 st, st_writing ; because of i2cm_write_start ?
+ bs_f st, st_reading ; no, then we will want to read
+ bs_f SSPCON2, RSEN
+ bra m_start_or_restart
;----------
m_improper_read_another
- panic morse_SA
-
-;----------
-m_improper_read_done
- panic morse_SD
+ i2cpanic morse_SA
;======================================================================
; SLAVE
; W slave number undefined
rcall slave2addr
mov_wf SSPADD
- clr_f i2c_st
+ clr_f st
mov_lw 0x16 ; !SSPEN, CKP(release), I2C 7-bit slave no-SP-int
mov_wf SSPCON1
mov_lw 0x01 ; !GCEN, SEN
mov_wf SSPCON2
- mov_lw 0x8 ; SMP(noslew), !CKE(!smbus)
+ mov_lw 0x80 ; SMP(noslew), !CKE(!smbus)
mov_wf SSPSTAT
+ bs_f IPR1, SSPIP ; high priority
init_enable
; Actually engages the I2C controller, which must already have
; been set up (all but SSPEN):
; SSPSTAT configured correctly unchanged, except:
; SSPSTAT<SSPEN> 0 (disabled) 1 (enabled)
; SSPIE 0 (disabled) 1 (enabled)
+; SSPIF configured correctly unchanged
; TRISB<1,0> any configured for I2C
; SSPIP any configured correctly
; GIEL 0 (disabled) 0 (disabled)
; ssp* shadows any all bits set
- set_f i2c_sspstat
- set_f i2c_sspcon1
- set_f i2c_sspcon2
- bs_f TRISB, 0
- bs_f TRISB, 1
- bc_f IPR1, SSPIP
+ set_f sspstat
+ set_f sspcon1
+ set_f sspcon2
+ set_f st_orig
+ bs_f TRISC, 3
+ bs_f TRISC, 4
bs_f SSPCON1, SSPEN
bs_f PIE1, SSPIE
return
;========================================
-; SLAVE - INTERRUPT HANDLING
-
+; SLAVE
+;
; In general, we figure out our state and then see what kind of events
; we were expecting. Bits we want to check:
-; 80 60 20 10 08 04 02 01
+; 80 40 20 10 08 04 02 01
; SMP CKE D_A P S R_W UA BF
; set clr data? stop start read? clr full?
; (we don't usually mention SMP, CKE and UA below)
+;
+; Labels of the form s_event_* are branches of the interrupt
+; handler and are supposed to finish with return.
-; Some macros:
+;----------
+; Macros: chkvals_start and chkval
-chkvals_start_sspstat macro
- mov_fw i2c_sspstat
+chkvals_start macro chvals_what
+ mov_fw chvals_what
endm
-chkval macro lastval, value, label
- xor_lw value ^ lastval
- bra_z label
+chkval macro chkval_lastval, chkval_value, chkval_label
+ xor_lw chkval_value ^ chkval_lastval
+ bra_z chkval_label
endm
-chkvals_addrrecv macro lastval
- chkval lastval, 0x8c, s_event_idle_addrrecvread ; A,!P, S,R,!BF
- chkval 0x8c, 0x89, s_event_idle_addrrecvwrite ; A,!P, S,W,BF
- endm
-chkvals_addrrecv_lastval equ 0x89
+near_i2csu code
;----------
-i2cs_interrupt
- bt_f_if0 PIR1, SSPIF
- return
- ; We have an interrupt:
-
- bt_f_if1 i2c_sspcon1, WCOL
- bra_z s_event_bad
- bt_f_if1 i2c_sspcon1, SSPOV
- bra_z s_event_bad
-
-; Firstly, clear the interrupt flag so that if something else happens
-; while we faff, the interrupt will be regenerated:
- bc_f PIR1, SSPIF
-
- mov_ff SSPSTAT, i2c_sspstat
- mov_ff SSPCON1, i2c_sspcon1
-
- bt_f_if0 i2c_st, st_reading
- bra s_event_reading
-
- bt_f_if0 i2c_st, st_writing
- bra s_event_writing
-
-s_event_idle
- chkvals_start_sspstat
- chkvals_addrrecv 0
-s_event_bad
- panic morse_SS ; slave, interrupt, controller in bad state
+s_write_slurpbyte macro
+; W any byte from master
+; i2c controller waiting due to SEN etc continuing with next byte
+ mov_fw SSPBUF
+ bs_f SSPCON1, CKP
+ endm
-;========================================
-; SLAVE - READING
+;----------------------------------------
+i2cs_read_data
+ i2cs_read_data_macro
+ return
-;----------
-s_event_idle_addrrecvread
- bs_f i2c_st, st_reading
- call i2csu_read_begin
- bra s_events_reading_datasend
+;----------------------------------------
+; branches from the ISR
;----------
-s_event_reading
- chkvals_start_sspstat
- chkval 0, 0xac, s_event_reading_datasent ; D,!P, S,R,!BF
-
- ; Whatever is happening, we're done reading now !
- clr_f i2c_st
- call i2csu_read_done
-
- chkvals_start_sspstat
- chkval 0, 0xa8, s_event_reading_datanack ; D,!P, S,!R,!BF
- ; Or, maybe it was nack and then we were reselected:
- chkvals_addrrecv 0xa8
-
- bra s_event_bad
+s_event_addrrecvwrite
+ s_write_slurpbyte
+ goto i2csu_write_begin
;----------
-s_event_reading_datasent
- call i2csu_read_another
-s_events_reading_datasend
- mov_wf SSPBUF
- bs_f SSPCON1, CKP
s_event_reading_datanack
return
-;========================================
-; SLAVE - WRITING
-
;----------
-s_event_idle_addrrecvwrite
- bs_f SSPCON1, 3 ; we'll need the Stop interrupt
- bs_f i2c_st, st_writing
- ; well, this is all fine so far, so do carry on:
-
-s_write_slurpbyte
-; W any byte from master
-; i2c controller waiting due to SEN etc continuing with next byte
- mov_fw SSPBUF
- bs_f SSPCON1, CKP
- return
+s_event_writing_datarecv
+ s_write_slurpbyte
+ goto i2csu_write_data
;----------
-s_event_writing
- chkvals_start_sspstat
- chkval 0, 0xa9, s_event_writing_datarecv ; D,!P, S,W,BF
-
- ; Well, we're done writing now in any case
- clr_f i2c_st
- bc_f SSPCON1, 3 ; no Start and Stop interrupts any more
- call i2csu_write_done
-
- ; Who knows what might have happened. We may have
- ; missed a number of S and P due to delay between
- ; clearing SSPIF and SSPM3(s&p-intrs) so we can't be
- ; too picky.
-
- ; First, the nice cases:
- chkvals_start_sspstat
- chkvals_addrrecv 0
-
- ; Then random junk:
- mov_fw i2c_sspstat
- and_lw 0xc7 ; ?D_A, ?P; ?S
- xor_lw 0x80 ; SMP, !CKE, !R_W, !UA, !BF
- bt_f_if1 STATUS, Z
- return
- ; no good
+s_event_bad_intr
+ i2cpanic morse_IH ; unknown high-priority interrupt
- bra s_event_bad
+;----------------------------------------
+i2cs_interrupt ; 4cy interrupt latency + 3cy until branch to here
+ bt_f_if0 PIR1, SSPIF
+ bra s_event_bad_intr
+ ; We have an interrupt:
-;----------
-s_event_writing_datarecv
- rcall s_write_slurpbyte
+ mov_lw (1<<WCOL) | (1<<SSPOV)
+ and_wfw SSPCON1
+ bra_nz s_event_bad
- bt_f_if1 i2c_st, st_subsequent
- goto i2csu_write_another
- ; not subsequent (yet):
+; Firstly, clear the interrupt flag so that if something else happens
+; while we faff, the interrupt will be regenerated:
+ bc_f PIR1, SSPIF
- bs_f i2c_st, st_subsequent
- goto i2csu_write_begin
+ chkvals_start SSPSTAT
+ chkval 0, 0x8c, i2csu_read_begin ;A,!P, S,R,!BF
+ chkval 0x8c,0xac, i2csu_read_another ;D,!P, S,R,!BF
+ chkval 0xac,0x89, s_event_addrrecvwrite ;A,!P, S,W,BF
+ chkval 0x89,0xa9, s_event_writing_datarecv ;D,!P, S,W,BF
+ chkval 0xa9,0xa8, s_event_reading_datanack ;D,!P, S,!R,!BF
+s_event_bad
+ i2cpanic morse_SS
;======================================================================
- include panic.fin
- include i2clib.inc
+ include program+externs.fin
+ include i2clib.inc
- end
+ end