chiark / gitweb /
Send HELLO as a result of all slaves being online. Do not crash if slave is slow...
[trains.git] / detpic / i2clib.asm
index d90803f1f9fa9cb60228cad0126723e952140a5b..09d995dbe56fcf6b69827b98ec76c71701b68525 100644 (file)
@@ -3,10 +3,14 @@
 ;
 ; 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
 
                udata_acs
 
-sspstat                res     1
-sspcon1                res     1
+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                          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
+                 ;    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
 
@@ -83,17 +89,25 @@ slave2addr
                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
 
 ;----------
@@ -101,19 +115,21 @@ i2cm_interrupt
                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
 
-               bt_f_if1 sspcon1, WCOL
-               bra_z   m_event_bad
-               bt_f_if1 sspcon1, SSPOV
-               bra_z   m_event_bad
+               bc_f    PIR1, SSPIF
+
+               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  sspcon2
-               and_lw  ~0x60 ; ACKSTAT,ACKDT
+               and_lw  ~((1<<ACKSTAT) | (1<<ACKDT)) ; those two are ok if set
                bra_nz  m_event_bad
                ; OK...
 
@@ -130,17 +146,17 @@ i2cm_interrupt
                bt_f_if1 st, st_addressing
                bra     m_event_done_addressing
 
-               bt_f_if1 st, st_writing
-               bra     m_event_done_writing
-
                bt_f_if1 st, st_acking
                bra     m_event_done_acking
 
+               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
@@ -148,17 +164,26 @@ m_event_bad
 ;----------
 m_start
 ;      st                      checked for busyness    correct
-;      st_reading/writing      set                     unchanged
+;      st_reading/writing      one set, one clear      unchanged
 ;      st_starting             clear                   set
 ;      W                       slave number            any
 ;      slave                   any                     slave_number
 ; expects to return directly to main program (caller)
                mov_wf  slave
-               and_lw  31
+               bs_f    SSPCON2, SEN
+m_start_or_restart
+               and_lw  ~31
                bra_nz  m_improper_slave
                bs_f    st, st_starting
-               bs_f    SSPCON2, SEN
+               tst_f_ifnz slave
                return
+               ; oops:
+;...
+
+m_improper_slave
+;      slave                   slave number
+               i2cpanic morse_SN
+
 
 ;----------
 m_event_done_starting
@@ -176,7 +201,7 @@ m_event_done_starting
 ;----------
 m_event_done_addressing
                bt_f_if1 sspcon2, ACKSTAT
-               bra     m_bad_address_ack
+               bra     m_no_address_ack
                ; OK, we got ack.
 
                bc_f    st, st_addressing
@@ -199,13 +224,15 @@ m_event_done_stopping
                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
-;      slave                   slave number
-               panic   morse_SN
+m_bad_no_address_ack_write
+               panic   morse_SW
 
 ;========================================
 ; MASTER - WRITING
@@ -213,10 +240,10 @@ m_improper_slave
 ;----------
 i2cm_write_start
 ;                              At call         On return
-;   State                      Idle            Writing-Setup
+;   State                  Idle/Reading-Wait   Writing-Setup
 ;   W                          slave number    any
                tst_f_ifnz st
-               bra     m_improper_write_start
+               bra     m_write_start_busy
 
                bs_f    st, st_writing
                bra     m_start
@@ -247,13 +274,9 @@ m_event_write_mustfinish
 
                bra     m_stop
 
-;----------
-m_improper_write_start
-               panic   morse_SW
-
 ;----------
 m_improper_write_finish
-               panic   morse_SF
+               i2cpanic morse_SF
 
 ;========================================
 ; MASTER - READING
@@ -264,11 +287,27 @@ i2cm_read_start
 ;      State                   Idle            Reading-Busy
 ;      W                       slave number    any
                tst_f_ifnz st
-               bra     m_improper_read_start
+               bra     m_read_start_busy
 
                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
@@ -303,6 +342,8 @@ m_read_ack
                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
 
@@ -312,7 +353,7 @@ i2cm_read_done
                bc_f    st, st_reading
                
                bt_f_if0 st, st_awaiting
-               bra     m_improper_read_done
+               bra     improper_read_done_data
                ; OK:
 
                bra     m_read_ack
@@ -324,19 +365,19 @@ m_event_done_acking
                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
@@ -351,8 +392,9 @@ i2cs_init
                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):
@@ -360,6 +402,7 @@ init_enable
 ;      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)
@@ -367,163 +410,100 @@ init_enable
                set_f   sspstat
                set_f   sspcon1
                set_f   sspcon2
-               bs_f    TRISB, 0
-               bs_f    TRISB, 1
-               bc_f    IPR1, SSPIP
+               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  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 sspcon1, WCOL
-               bra_z   s_event_bad
-               bt_f_if1 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, sspstat
-               mov_ff  SSPCON1, sspcon1
-
-               bt_f_if0 st, st_reading
-               bra     s_event_reading
-
-               bt_f_if0 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    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   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    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
+s_event_bad_intr
+               i2cpanic morse_IH ; unknown high-priority interrupt
 
-               ; Well, we're done writing now in any case
-               clr_f   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  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
-
-               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 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    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