I2C Slave Pseudo code and notes Copyright, Conor O'Rourke, 2013 I2C Slave: 7 bit Address = 5B Read: Single status bit = xxxx xxxx Bit 0: LED#1 is on/off Bit 1: LED#2 is on/off Bit 2: Motor is on/off Bit 3: Key #1 pressed - read status resets Write: Command = xxxx xxxx Bit 0: Turn LED#1 on/off Bit 1: Turn LED#2 on/off Bit 2: Turn Motor on/off That's it for the moment. RB1 = SDA RB4 = SCL RB0 = LED out RA* = debug output Init ---- Set rb1 and rb4 to inputs (default) BSF TRISB, 1 BSF TRISB, 4 RA* to digital, and output banksel PORTA clrf PORTA banksel ANSEL clrf ANSEL clrf TRISA SSPADD is 8 bit, bit 0 is 0. sspadd = 0x5b << 1 Set sspcon = b'00110110' = i2c slave, 7 bit, sspen, ckp(1),wcol(0),sspov(0) IRQ: Clear SSPIF, set SSPIE, PEIE, GIE Setup complete ISR Notes --------- SSPSTAT: D/A#, P, S, R/W# - only valid from address to start/stop/ack# BF - buffer full on receieve, transmit in progress on transmit SSPCON: WCOL - write to sspbuf fail SSPOV - byte received while sspbuf full (transmit is don't care) CKP - Clock running (1), Clock stretched (0) SSPEN, CKP, SSPM - R/W# is only valid from address match to next NACK/Start/Stop. Save R/W# after address. http://www.piclist.com/techref/microchip/i2c77x.htm - SDA->SCL hold time given by SSPBUF load to CKP release. Must be > 250ns - BF is set after address on receive only. At least it is on this PIC. No harm in clearing BF on transmit. - No ACKSTAT flag on PIC16F88. So R/W# reflects NACK status on transmit - Clock is held on ACK from master. Not held on NACK from master. - P isn't useful to detect NACK (IRQ race) - SSPOV on transmit is "don't care". SSPOV can arrive at ANY point in the ISR up to and including the read from SSPBUF. Safe for one i2c cycle but only after BF is cleared. - SSPOV races: detecting SSPOV and clearing it results in a pending interrupt with nothing to do and garbage in SSPBUF. Clear the interrupt flag. Checking BF might be an idea. ISR on SSPIF ------------ Overflow() { /* I2C cycle has ended with no slave ACK */ clear BF (in case) clear SSPOV clear WCOL (in case) Set CKP (for write collision) clear SSPIF /* There will be a pending irq */ exit isr } Start: Clear SSPIF if sspstat.d/a# == address(0) { isxmit = sspstat.r/w# (1 if read) isfirst = 1 movf SSPBUF, W if (!isxmit) { if sspcon.sspov Overflow(). End of cycle. exit isr /* done */ } } if (!isxmit) { /* implies d/a# == data */ /* This seems unlikely unless you don't clear SSPIF on SSPOV: */ if (sspstat.bf == 0) { Overflow() /* Clears and exits */ /* done */ } read sspbuf into W, clearing BF i2c_in_data = W if sspcon.sspov Overflow(). End of cycle. exit isr /* done */ } /* Transmit: CKP is 0 on master ACK D/A# can be A or D */ if (sspstat.r/w# == 0 || sspcon.ckp == 1) { /* Master NACK. Reset. */ clear sspov clear wcol set CKP clear BF if you want. exit isr /* Done */ } clear sspov clear wcol sspbuf = i2c_out_data (sets bf) if sspcon, WCOL { /* write collision */ Overflow() /* This routine will handle it */ /* Done */ } /* Wait for setup time. The check for wcol should cover that */ /* Start clock */ bsf sspcon, CKP ; start clock exit isr /* done */