;###################################################################### ; 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 udata_acs ssp res 1 st res 1 ; bitmask: st_writing equ 0 st_subsequent equ 0 code ;-------------------- i2cs_init ; W slave number undefined rcall slave2addr2 mov_wf SSPADD clr_f st mov_lw 0x1e ; !SSPEN, CKP(release), I2C 7-bit slave S&P mov_wf SSPCON1 mov_lw 0x01 ; !GCEN, SEN mov_wf SSPCON2 mov_lw 0x8 ; SMP(noslew), !CKE, !BF(empty) mov_wf SSPSTAT bs_f TRISB, 0 bs_f TRISB, 1 bc_f IPR1, SSPIP bs_f SSPCON1, SSPEN bs_f PIE1, SSPIE return ;-------------------- 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: mov_fw SSPCON1 mov_wf ssp and_lw 0xc0 bra_nz i2cs_interrupt_wcolsspov_endif panic morse_SV i2cs_interrupt_wcolsspov_endif ; Find out what's just happened: mov_ff SSPSTAT, ssp ; 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? mov_fw ssp chkval_lastvalue equ 0 chkval macro value, label xor_lw value ^ chkval_lastvalue chkval_lastvalue equ value bra_z label endm chkval 0x89, s_case_addr_recv_write chkval 0x8d, s_case_addr_recv_read chkval 0xa9, s_case_write_data_recv bt_f_if0 st, st_reading bra s_ifnot_reading ; only check this if we're reading; otherwise ; this will be handled by s_case_uninteresting_start chkval 0xac, s_case_read_data_sent chkval 0xa8, s_case_read_data_nack s_ifnot_reading chkvalm macro mask, value, label mov_fw ssp xor_lw value bra_z label endm chkvalm 0xdf, 0x90, s_case_something_stop chkvalm 0xdb, 0x88, s_case_uninteresting_start mov_ff ssp, WREG2 ; fixme panic morse_SS ;---------- s_case_something_stop s_case_something_start s_ensure_idle mov_fw st ; were we doing something ? bt_f_if1 STATUS,Z return ; we were, it seems: bc_f SSPCON, 3 clr_f st ; now we're not (but W still has old st) bt_f_if1 WREG, st_writing goto i2csu_write_done bt_f_if1 WREG, st_reading goto i2csu_read_done mov_wf st ; put it back and then ... s_panic_st_unexpected panic morse_ST ;---------- s_case_addr_recv_write rcall s_ensure_idle bs_f SSPCON, 3; we'll need the Stop interrupt bs_f st, st_writing ; well, now this is all fine 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_case_write_data_recv bt_f_if0 st, st_writing bra s_panic_st_unexpected ; ok, we are writing: 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 ;---------- s_case_addr_recv_read rcall s_ensure_idle bs_f st, st_reading call i2csu_read_begin bra s_cases_read_data_send ;---------- s_case_read_data_sent call i2csu_read_another s_cases_read_data_send mov_wf SSPBUF bs_f SSPCON1, CKP return ;---------- s_case_read_data_nack rcall s_ensure_idle goto i2csu_read_done s_cases_write_alliswell bt_f_if1 ssp, I2C_START bra si_if_start si_if_notstart ; So it should be stop mov_fw ssp and_lw 0xdf ; ?D_A xor_lw 0x90 ; SMP, !CKE, P; !S, !R_W, !UA, !BF bra_nz si_if_bad si_if_start bt_f_if1 ssp, BF bra si_if_bufferfull si_if_bufferempty bt_f_if1 ssp, R_W ;read? bra si_if_bufferempty_reading si_if_bufferempty_notreading ; So we think this is just a START (which we want to ignore) mov_fw ssp and_lw 0xdf ; ?D_A xor_lw 0x88 ; SMP, !CKE, !P; S, !R_W, !UA, !BF bra_nz si_if_bad ; OK, ignore it return ;---------- s_case_unknown_stop s_case_unknown_start return ;---------- s_case_got_write_addr mov_fw SSPBUF and_lw 0xfe bra_nz nonzero mov_wf ssp mov_fw SSPSTAT and_lw 0xe7 ; all except P and S xor_lw 0x80 ; bits which might sensibly be set chkval_last equ 0 chkval macro value, label xor_lw value ^ chkval_last bra_z label chkval_last equ value endm chkval 0x80 ; addr dunno mov_lw 0x8 bt_f_if0 DATA_ADDRESS, SSPSTAT bra data 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 include i2clib.inc end