i2c: increase the default clock strech timeout to 250msec.
This also redefines the timeout in FreeRTOS clock ticks, and implements a two stage wait: firstly spinning sampling frequently, and then falling back to a longer wait while sampling less frequently and yielding.
This commit is contained in:
		
							parent
							
								
									d9c5f2eaa2
								
							
						
					
					
						commit
						c8c7f97290
					
				
					 2 changed files with 42 additions and 23 deletions
				
			
		| 
						 | 
				
			
			@ -66,7 +66,7 @@ typedef struct i2c_bus_description
 | 
			
		|||
  bool started;
 | 
			
		||||
  bool flag;
 | 
			
		||||
  bool force;
 | 
			
		||||
  uint32_t clk_stretch;
 | 
			
		||||
  TickType_t clk_stretch;
 | 
			
		||||
} i2c_bus_description_t;
 | 
			
		||||
 | 
			
		||||
static i2c_bus_description_t i2c_bus[I2C_MAX_BUS];
 | 
			
		||||
| 
						 | 
				
			
			@ -183,7 +183,7 @@ int i2c_set_frequency_hz(uint8_t bus, uint32_t freq)
 | 
			
		|||
    return not_ok ? -EINVAL : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void i2c_set_clock_stretch(uint8_t bus, uint32_t clk_stretch)
 | 
			
		||||
void i2c_set_clock_stretch(uint8_t bus, TickType_t clk_stretch)
 | 
			
		||||
{
 | 
			
		||||
    i2c_bus[bus].clk_stretch = clk_stretch;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -235,13 +235,46 @@ static inline void clear_sda(uint8_t bus)
 | 
			
		|||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void set_scl(uint8_t bus)
 | 
			
		||||
#define I2C_CLK_STRETCH_SPIN 1024
 | 
			
		||||
 | 
			
		||||
static void set_scl(uint8_t bus)
 | 
			
		||||
{
 | 
			
		||||
#if I2C_USE_GPIO16 == 1
 | 
			
		||||
    gpio_write(i2c_bus[bus].g_scl_pin, 1);
 | 
			
		||||
#else
 | 
			
		||||
    GPIO.OUT_SET = i2c_bus[bus].g_scl_mask;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    // Clock stretching.
 | 
			
		||||
 | 
			
		||||
    // Spin sampling frequently.
 | 
			
		||||
    uint32_t clk_stretch_spin = I2C_CLK_STRETCH_SPIN;
 | 
			
		||||
    do {
 | 
			
		||||
        if (read_scl(bus)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        clk_stretch_spin--;
 | 
			
		||||
    } while (clk_stretch_spin);
 | 
			
		||||
 | 
			
		||||
    // Fall back to a longer wait, sampling less frequently.
 | 
			
		||||
    TickType_t clk_stretch = i2c_bus[bus].clk_stretch;
 | 
			
		||||
    TickType_t start = xTaskGetTickCount();
 | 
			
		||||
 | 
			
		||||
    do {
 | 
			
		||||
        vTaskDelay(20 / portTICK_PERIOD_MS);
 | 
			
		||||
 | 
			
		||||
        if (read_scl(bus)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        TickType_t elapsed = xTaskGetTickCount() - start;
 | 
			
		||||
        if (elapsed > clk_stretch) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    } while (1);
 | 
			
		||||
 | 
			
		||||
    debug("bus %u clock stretch timeout", bus);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void set_sda(uint8_t bus)
 | 
			
		||||
| 
						 | 
				
			
			@ -265,10 +298,7 @@ void i2c_start(uint8_t bus)
 | 
			
		|||
        // Set SDA to 1
 | 
			
		||||
        set_sda(bus);
 | 
			
		||||
        i2c_delay(bus);
 | 
			
		||||
        uint32_t clk_stretch = i2c_bus[bus].clk_stretch;
 | 
			
		||||
        set_scl(bus);
 | 
			
		||||
        while (read_scl(bus) == 0 && clk_stretch--)
 | 
			
		||||
            ;
 | 
			
		||||
        // Repeated start setup time, minimum 4.7us
 | 
			
		||||
        i2c_delay(bus);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -286,14 +316,10 @@ void i2c_start(uint8_t bus)
 | 
			
		|||
// Output stop condition
 | 
			
		||||
bool i2c_stop(uint8_t bus)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t clk_stretch = i2c_bus[bus].clk_stretch;
 | 
			
		||||
    // Set SDA to 0
 | 
			
		||||
    clear_sda(bus);
 | 
			
		||||
    i2c_delay(bus);
 | 
			
		||||
    // Clock stretching
 | 
			
		||||
    set_scl(bus);
 | 
			
		||||
    while (read_scl(bus) == 0 && clk_stretch--)
 | 
			
		||||
        ;
 | 
			
		||||
    // Stop bit setup time, minimum 4us
 | 
			
		||||
    i2c_delay(bus);
 | 
			
		||||
    // SCL is high, set SDA from 0 to 1
 | 
			
		||||
| 
						 | 
				
			
			@ -315,17 +341,13 @@ bool i2c_stop(uint8_t bus)
 | 
			
		|||
// Write a bit to I2C bus
 | 
			
		||||
static void i2c_write_bit(uint8_t bus, bool bit)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t clk_stretch = i2c_bus[bus].clk_stretch;
 | 
			
		||||
    if (bit) {
 | 
			
		||||
        set_sda(bus);
 | 
			
		||||
    } else {
 | 
			
		||||
        clear_sda(bus);
 | 
			
		||||
    }
 | 
			
		||||
    i2c_delay(bus);
 | 
			
		||||
    // Clock stretching
 | 
			
		||||
    set_scl(bus);
 | 
			
		||||
    while (read_scl(bus) == 0 && clk_stretch--)
 | 
			
		||||
        ;
 | 
			
		||||
    // SCL is high, now data is valid
 | 
			
		||||
    // If SDA is high, check that nobody else is driving SDA
 | 
			
		||||
    if (bit && read_sda(bus) == 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -338,15 +360,11 @@ static void i2c_write_bit(uint8_t bus, bool bit)
 | 
			
		|||
// Read a bit from I2C bus
 | 
			
		||||
static bool i2c_read_bit(uint8_t bus)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t clk_stretch = i2c_bus[bus].clk_stretch;
 | 
			
		||||
    bool bit;
 | 
			
		||||
    // Let the slave drive data
 | 
			
		||||
    set_sda(bus);
 | 
			
		||||
    i2c_delay(bus);
 | 
			
		||||
    set_scl(bus);
 | 
			
		||||
    // Clock stretching
 | 
			
		||||
    while (read_scl(bus) == 0 && clk_stretch--)
 | 
			
		||||
        ;
 | 
			
		||||
    // SCL is high, now data is valid
 | 
			
		||||
    bit = read_sda(bus);
 | 
			
		||||
    i2c_delay(bus);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,6 +32,8 @@
 | 
			
		|||
#include <stdint.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <FreeRTOS.h>
 | 
			
		||||
#include <task.h>
 | 
			
		||||
 | 
			
		||||
#ifdef	__cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
| 
						 | 
				
			
			@ -51,7 +53,8 @@ extern "C" {
 | 
			
		|||
    #define I2C_USE_GPIO16 0
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define I2C_DEFAULT_CLK_STRETCH (10)
 | 
			
		||||
/* Default clock strech waiting time, 250 msec. */
 | 
			
		||||
#define I2C_DEFAULT_CLK_STRETCH (250 / portTICK_PERIOD_MS)
 | 
			
		||||
 | 
			
		||||
/* SCL speed settings. 160 MHz sysclk frequency will result in improved
 | 
			
		||||
 * timing accuracy. Greater bitrates will have poorer accuracy. 1000K is the
 | 
			
		||||
| 
						 | 
				
			
			@ -86,7 +89,6 @@ typedef struct i2c_dev
 | 
			
		|||
 * @param scl_pin SCL pin for I2C
 | 
			
		||||
 * @param sda_pin SDA pin for I2C
 | 
			
		||||
 * @param freq frequency of bus (ex : I2C_FREQ_400K)
 | 
			
		||||
 * @param clk_stretch I2C clock stretch. I2C_DEFAULT_CLK_STRETCH would be good in most cases
 | 
			
		||||
 * @return Non-zero if error occured
 | 
			
		||||
 */
 | 
			
		||||
int i2c_init(uint8_t bus, uint8_t scl_pin, uint8_t sda_pin, i2c_freq_t freq);
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +99,6 @@ int i2c_init(uint8_t bus, uint8_t scl_pin, uint8_t sda_pin, i2c_freq_t freq);
 | 
			
		|||
 * @param scl_pin SCL pin for I2C
 | 
			
		||||
 * @param sda_pin SDA pin for I2C
 | 
			
		||||
 * @param freq frequency of bus in hertz
 | 
			
		||||
 * @param clk_stretch I2C clock stretch. I2C_DEFAULT_CLK_STRETCH would be good in most cases
 | 
			
		||||
 * @return Non-zero if error occured
 | 
			
		||||
 */
 | 
			
		||||
int i2c_init_hz(uint8_t bus, uint8_t scl_pin, uint8_t sda_pin, uint32_t freq);
 | 
			
		||||
| 
						 | 
				
			
			@ -119,9 +120,9 @@ int i2c_set_frequency_hz(uint8_t bus, uint32_t freq);
 | 
			
		|||
/**
 | 
			
		||||
 * Change clock stretch
 | 
			
		||||
 * @param bus I2C bus
 | 
			
		||||
 * @param clk_stretch I2C clock stretch. I2C_DEFAULT_CLK_STRETCH by default
 | 
			
		||||
 * @param clk_stretch I2C clock stretch, in ticks. I2C_DEFAULT_CLK_STRETCH by default
 | 
			
		||||
 */
 | 
			
		||||
void i2c_set_clock_stretch(uint8_t bus, uint32_t clk_stretch);
 | 
			
		||||
void i2c_set_clock_stretch(uint8_t bus, TickType_t clk_stretch);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Write a byte to I2C bus.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue