;###################################################################### ; i2clib.inc - I2C LIBRARY - IMPLEMENTATION ; ; 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 ;============================================================ ; COMMON ADMINISTRATIVE ROUTINES and VARIABLES udata_acs sspstat res 1 sspcon1 res 1 sspcon2 res 1 st res 1 ; bitmask, bit set in visible states: ; master slave st_reading equ 0 ; Reading-* Transmitting st_writing equ 1 ; Writing-* [Idle-going-]Receiving st_subsequent equ 2 ; Receiving st_working equ 3 ; Writing-*,Reading-Busy,Stopping st_starting equ 4 ; Writing-Setup,Reading-Busy code ;---------- slave2addr ; computes slave address in form suitable for use in i2c controller ; actual i2c slave address is (slave number) + 0b0001000 ; W slave number i2c address * 2 add_lw 0b0001000 rlc_w return ;---------- check_wcolsspov mov_fw SSPCON1 mov_wf ssp and_lw 0xc0 ; WCOL,SSPOV bra_z intr_wcolsspov return ;---------- intr_wcolsspov panic morse_SV ;====================================================================== ; 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 0x08 ; !SSPEN, Master mode mov_wf SSPCON1 clr_f SSPCON2 ; nothing going mov_lw 0x80 ; SMP(noslew), !CKE(!smbus) bra init_enable ;---------- i2cm_write_start ; At call On return ; State Idle Writing-Setup ; W slave number any tst_f_ifnz st bra m_bad_write_start bs_f st, st_writing m_start bs_f st, st_starting bs_f SSPCON2, SEN return ;---------- m_bad_write_start panic morse_SPW ;---------- i2cm_interrupt ; Do we have a doomy clash ? bsr check_wcolsspov ; No ? Well, then the I2C should be idle now: mov_fw SSPCON2 mov_wf ssp and_lw ~0x60 ; ACKSTAT,ACKDT bra_nz m_bad_sspcon2 ; OK, what's happening; bt_f_if0 PIR1, SSPIF return ; We have an interrupt: bt_f_if1 st, st_starting bra m_event_starting ; not just done SEN fixme ;---------- m_bad_sspcon2 panic morse_SM ;---------- m_event_starting mov_lw 0x88 bsr m_check_sspstat m_check_sspstat ; W expected bit values any ; Panics if (SSPSTAT ^ W) & mask != 0 mov_ff SSPSTAT, ssp ;====================================================================== ; SLAVE ;---------- i2cs_init ; W slave number undefined rcall slave2addr2 mov_wf SSPADD 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_wf SSPSTAT ms_init_enable ; Actually engages the I2C controller, which must already have ; been set up (all but SSPEN): ; SSPADD,SSPCON1,SSPCON2 configured correctly unchanged ; SSPSTAT configured correctly unchanged, except: ; SSPSTAT 0 (disabled) 1 (enabled) ; SSPIE 0 (disabled) 1 (enabled) ; TRISB<1,0> any configured for I2C ; SSPIP any configured correctly ; GIEL 0 (disabled) 0 (disabled) bs_f TRISB, 0 bs_f TRISB, 1 bc_f IPR1, SSPIP bs_f SSPCON1, SSPEN bs_f PIE1, SSPIE return ;======================================== ; SLAVE - INTERRUPT HANDLING ; 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 ; 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) ; Some macros: chkvals_start macro mov_fw ssp chkval_lastvalue equ 0 endm chkval macro value, label xor_lw value ^ chkval_lastvalue chkval_lastvalue equ value bra_z label endm chkvals_addrrecv macro chkval 0x8c, s_event_idle_addrrecvread ; A,!P, S,R,!BF chkval 0x89, s_event_idle_addrrecvwrite ; A,!P, S,W,BF endm ;---------- i2cs_interrupt bt_f_if0 PIR1, SSPIF return ; We have an interrupt: ; Firstly, clear the interrupt flag so that if something else happens ; while we faff, the interrupt will be regenerated: bc_f PIR1, SSPIF ; Check that nothing obvious is wrong: bsr check_wcolsspov 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 chkvals_addrrecv panic morse_SI ;======================================== ; SLAVE - READING ;---------- s_event_idle_addrrecvread bs_f st, st_reading call i2csu_read_begin bra s_events_read_datasend ;---------- s_event_reading chkvals_start chkval 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 chkval 0xa8, s_event_reading_datanack ; D,!P, S,!R,!BF ; Or, maybe it was nack and then we were reselected: chkvals_addrrecv panic morse_SR ;---------- 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 SSPCON, 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 chkvals_start chkval 0xa9, s_event_writing_datarecv ; D,!P, S,W,BF ; 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 chkvals_addrrecv ; Then random junk: mov_fw ssp and_lw 0xc7 ; ?D_A, ?P; ?S xor_lw 0x80 ; SMP, !CKE, !R_W, !UA, !BF bt_f_if1 STATUS, Z return panic morse_SW ;---------- s_event_writing_datarecv rcall s_write_slurpbyte bt_f_if1 st, st_subsequent goto i2csu_write_another ; not subsequent (yet): bs_f st, st_subsequent goto i2csu_write_begin ;====================================================================== include i2clib.inc end