Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement I²C Clock Stretching on the Stellaris / Tiva C #336

Closed
rei-vilo opened this issue Mar 7, 2014 · 8 comments
Closed

Implement I²C Clock Stretching on the Stellaris / Tiva C #336

rei-vilo opened this issue Mar 7, 2014 · 8 comments

Comments

@rei-vilo
Copy link
Member

rei-vilo commented Mar 7, 2014

The I²C specifications (NXP, I²C-bus.org) allow the slave to set the SCL clock line low to make the master wait. This is called clock streching.

An I2C slave is allowed to hold down the clock if it needs to reduce the bus speed. The master on the other hand is required to read back the clock signal after releasing it to high state and wait until the line has actually gone high.

How to implement clock stretching on the Stellaris LM4F120 / Tiva C TM4C123?

For example, the twi.h library for the MSP430 includes two functions that are used this purpose:

void twi_stop(void);
void twi_releaseBus(void);

Thank you!

@rei-vilo
Copy link
Member Author

rei-vilo commented Mar 8, 2014

The E2E forum suggested me to use void I2CMasterTimeoutSet(uint32_t ui32Base, uint32_t ui32Value), as explained in

  • paragraph 16.3.1.6 - Clock Low Timeout page 999
  • and Register 10: I2C Master Clock Low Timeout Count (I2CMCLKOCNT), offset 0x024 page 1030

of the Tiva™ C Series TM4C123GH6PM Microcontroller Data Sheet (Rev. D).

The I2C slave can extend the transaction by pulling the clock low periodically to create a slow bit transfer rate. The I2C module has a 12-bit programmable counter that is used to track how long the clock has been held low. The upper 8 bits of the count value are software programmable through the I2C Master Clock Low Timeout Count (I2CMCLKOCNT) register. The lower four bits are not user visible and are 0x0. The CNTL value programmed in the I2CMCLKOCNT register has to be greater than 0x01. The application can program the eight most significant bits of the counter to reflect the acceptable cumulative low period in transaction. The count is loaded at the START condition and counts down on each falling edge of the internal bus clock of the Master. Note that the internal bus clock generated for this counter keeps running at the programmed I2C speed even if SCL is held low on the bus. Upon reaching terminal count, the master state machine forces ABORT on the bus by issuing a STOP condition at the instance of SCL and SDA release.

Now, how to implement it on Energia, as the I²C bus is managed by interrupts, ReceiveEvent(int howMany) and RequestEvent()?

@rei-vilo
Copy link
Member Author

Here's the solution that I've tested on port I²C (3) of the Stellaris LM4F120 and Tiva C TM4C123 LaunchPads.

Libraries to include with #include "Wire.h"

#include "driverlib/i2c.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"

This routine initialise the SCL pin. Not sure it is really useful.

void stretchInit()
{
    SysCtlPeripheralEnable(I2C_SCL3_PERIPH);
}

This routine configures the SCL line as a standard output and sets it LOW. Not sure about the 4mA parameter.

void stretchPause()
{
    // Standard output
    GPIOPadConfigSet(I2C_SCL3_BASE, I2C_SCL3_PIN, GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD);
    GPIODirModeSet(I2C_SCL3_BASE, I2C_SCL3_PIN, GPIO_DIR_MODE_OUT);
    // Set LOW
    GPIOPinWrite(I2C_SCL3_BASE, I2C_SCL3_PIN, 0);
}

This routine configures the SCL line back to normal. I tried another option but it didn't work.

void stretchResume()
{
    // Reconfigure for I2C
    ROM_GPIOPinConfigure(GPIO_PD0_I2C3SCL);
    ROM_GPIOPinTypeI2CSCL(I2C_SCL3_BASE, I2C_SCL3_PIN);

    // Doesn't work
    // HIGH for floating?
    //    GPIOPinWrite(I2C_SCL3_BASE, I2C_SCL3_PIN, I2C_SCL3_PIN);
    // Open drain
    //    GPIOPadConfigSet(I2C_SCL3_BASE, I2C_SCL3_PIN, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_OD);
    //    GPIODirModeSet(I2C_SCL3_BASE, I2C_SCL3_PIN, GPIO_DIR_MODE_HW);
}

Typical usage requires

  • adding stretchInit(); in setup()
  • including stretchPause(); on the first line of void ReceiveEvent(int howMany) as the I²C sequence is buffered
  • placing stretchResume(); on the last line of the routine in charge of executing orders from I²C.

Questions:

  • How to make it universal for the 4 I²C ports available in the LM4F and TM4C MCUs?
  • How to integrate the routines directly into the Wire.h library?
  • How to check the whole process is safe —which values for pull-up resistors, which parameter for mA, no shorts, no damage for the MCU?

Thanks!

@rei-vilo
Copy link
Member Author

Integration into Wire library was easier than expected.
See pull request #375
Tested on LM4F120- and TM4C123-based LaunchPads
Please proceed with other tests and commit!

@rei-vilo
Copy link
Member Author

capture 2014-04-21 a 14 11 51

@spirilis
Copy link

Slick! I like it.

@rei-vilo
Copy link
Member Author

I removed the digitalWrite(GREEN_LED, HIGH); on line 613 from Wire.cpp.

See 263e48c

@rei-vilo
Copy link
Member Author

See #375

@robertinant
Copy link
Member

This issue was moved to energia/tivac-core#28

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants