Category Archives: Microchip

RPI I2C interface to Microchip embedded uP – Clock stretching

If you are going to implement I2C to an embedded uP read this first http://www.slate.com/blogs/bad_astronomy/2016/03/28/psychedelic_stroboscopic_easter_eggs.html

If you are using a Microchip uP you might want to continue reading.

Here is the problem, Raspberry Pi’s I2C software can’t cope with slave clock stretching, not a problem if you can service the data request quickly, well you would have thought not anyway. Unfortunately Microchip’s hardware (using a PIC18F14K50 and many others)  always puts in a small clock stretch for a slave transmit, even if you disable clock stretching with the SEN bit.   A thorough read of the data sheet gives you a clue “The ACK pulse will be sent on the ninth bit and pin SCK/SCL is held low regardless of SEN“.

So data transmission TO the PIC works fine (if you can deal with the I2C data quickly enough), but for Slave data transmit TO the RPI you get errors. The problem is, they are random and depend on the exact timing of the I2C bus and uP speed.

This clock stretch only happens after the address byte and before the transmission data. It causes a short or missing clock pulse (as the RPI ignores the presence of the clock held low by the slave) as in the transition of bytes 3 &4  of data in the oscilloscope capture below.

DS1Z_QuickPrint7

So how do we work around this, well to be honest there isn’t a total solution. (that I’ve found) the best that I have achieved is to change the I2C clock speed  so the timing of a clock stretch happens when the clock is already low and tweak the uP interrupt software.

The clock speed change is unfortunately a trial and error process whilst monitoring the data (and probably watching with a scope). To change the I2C bus speed with a newer PI distro, you need to edit the /boot/config.txt and edit or add the line “dtparam=i2c1_baudrate=clockspeed” try clock speeds from 20000 to 400000 and follow by a reboot of the PI to make the changes active.

The bus clock speed that will work will vary with the microprocessor speed,  so there is no single solution. With my setup 200Khz and 50Khz works well (uP clock @32Mhz) where 400khz and 100khz is a total wright off.

I have also changed the I2C code in the PIC18F14K50 to give a fast set of the  BF flag after data transmission which helps minimise the clock hold time.

if (PIR1bits.SSPIF) {
      
        if (!SSPSTATbits.D_NOT_A) {
            //
            // Slave Address
            //
            i2c_byte_count = 0;

            if (SSPSTATbits.BF) {
                // Discard slave address
                sspBuf = SSPBUF;    // Clear BF
                SSPSTATbits.BF=1;
            }

            if (SSPSTATbits.R_NOT_W) {
                // Reading - read from register map
                SSPCON1bits.WCOL = 0;
                SSPBUF           = i2c_reg_map[i2c_reg_addr++];
              
            }

        } else {
            //
            // Data bytes
            //
            i2c_byte_count++;

            if (SSPSTATbits.BF) {
                sspBuf = SSPBUF;    // Clear BF
                
            }

            if (SSPSTATbits.R_NOT_W) {
                // Multi-byte read - advance to next address
                SSPCON1bits.WCOL = 0;
                SSPBUF           = i2c_reg_map[i2c_reg_addr++];
                SSPSTATbits.BF=1;
               
            } else {

                if (i2c_byte_count == 1) {
                    // First write byte is register address
                    i2c_reg_addr = sspBuf;
                } else {
                    // Write to register address - auto advance
                    //   to allow multiple bytes to be written
                    i2c_reg_map[i2c_reg_addr++] = sspBuf;
                }
            }
           
        }//else
        
        SSPCON1bits.CKP = 1;            // Release clock
        // Limit address to size of register map
        i2c_reg_addr %= sizeof(i2c_reg_map);

        // Finally
        PIR1bits.SSPIF  = 0;            // Clear MSSP interrupt flag
      
    }   

With these changes, I have got (as the adverts say) up to 100% TX from a slave without error. This is about the best we can do until either Microchip change their hardware, or RPI write a I2C handler that supports clock stretch.