Merge pull request #642 from ourairquality/i2c-clock-stretch-slow
i2c: increase the default clock strech timeout to 250msec.
This commit is contained in:
commit
6db65e8a7a
2 changed files with 42 additions and 23 deletions
|
@ -67,7 +67,7 @@ typedef struct i2c_bus_description
|
||||||
bool started;
|
bool started;
|
||||||
bool flag;
|
bool flag;
|
||||||
bool force;
|
bool force;
|
||||||
uint32_t clk_stretch;
|
TickType_t clk_stretch;
|
||||||
} i2c_bus_description_t;
|
} i2c_bus_description_t;
|
||||||
|
|
||||||
static i2c_bus_description_t i2c_bus[I2C_MAX_BUS];
|
static i2c_bus_description_t i2c_bus[I2C_MAX_BUS];
|
||||||
|
@ -184,7 +184,7 @@ int i2c_set_frequency_hz(uint8_t bus, uint32_t freq)
|
||||||
return not_ok ? -EINVAL : 0;
|
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;
|
i2c_bus[bus].clk_stretch = clk_stretch;
|
||||||
}
|
}
|
||||||
|
@ -236,13 +236,46 @@ static inline void clear_sda(uint8_t bus)
|
||||||
#endif
|
#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
|
#if I2C_USE_GPIO16 == 1
|
||||||
gpio_write(i2c_bus[bus].g_scl_pin, 1);
|
gpio_write(i2c_bus[bus].g_scl_pin, 1);
|
||||||
#else
|
#else
|
||||||
GPIO.OUT_SET = i2c_bus[bus].g_scl_mask;
|
GPIO.OUT_SET = i2c_bus[bus].g_scl_mask;
|
||||||
#endif
|
#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)
|
static inline void set_sda(uint8_t bus)
|
||||||
|
@ -266,10 +299,7 @@ void i2c_start(uint8_t bus)
|
||||||
// Set SDA to 1
|
// Set SDA to 1
|
||||||
set_sda(bus);
|
set_sda(bus);
|
||||||
i2c_delay(bus);
|
i2c_delay(bus);
|
||||||
uint32_t clk_stretch = i2c_bus[bus].clk_stretch;
|
|
||||||
set_scl(bus);
|
set_scl(bus);
|
||||||
while (read_scl(bus) == 0 && clk_stretch--)
|
|
||||||
;
|
|
||||||
// Repeated start setup time, minimum 4.7us
|
// Repeated start setup time, minimum 4.7us
|
||||||
i2c_delay(bus);
|
i2c_delay(bus);
|
||||||
}
|
}
|
||||||
|
@ -287,14 +317,10 @@ void i2c_start(uint8_t bus)
|
||||||
// Output stop condition
|
// Output stop condition
|
||||||
bool i2c_stop(uint8_t bus)
|
bool i2c_stop(uint8_t bus)
|
||||||
{
|
{
|
||||||
uint32_t clk_stretch = i2c_bus[bus].clk_stretch;
|
|
||||||
// Set SDA to 0
|
// Set SDA to 0
|
||||||
clear_sda(bus);
|
clear_sda(bus);
|
||||||
i2c_delay(bus);
|
i2c_delay(bus);
|
||||||
// Clock stretching
|
|
||||||
set_scl(bus);
|
set_scl(bus);
|
||||||
while (read_scl(bus) == 0 && clk_stretch--)
|
|
||||||
;
|
|
||||||
// Stop bit setup time, minimum 4us
|
// Stop bit setup time, minimum 4us
|
||||||
i2c_delay(bus);
|
i2c_delay(bus);
|
||||||
// SCL is high, set SDA from 0 to 1
|
// SCL is high, set SDA from 0 to 1
|
||||||
|
@ -316,17 +342,13 @@ bool i2c_stop(uint8_t bus)
|
||||||
// Write a bit to I2C bus
|
// Write a bit to I2C bus
|
||||||
static void i2c_write_bit(uint8_t bus, bool bit)
|
static void i2c_write_bit(uint8_t bus, bool bit)
|
||||||
{
|
{
|
||||||
uint32_t clk_stretch = i2c_bus[bus].clk_stretch;
|
|
||||||
if (bit) {
|
if (bit) {
|
||||||
set_sda(bus);
|
set_sda(bus);
|
||||||
} else {
|
} else {
|
||||||
clear_sda(bus);
|
clear_sda(bus);
|
||||||
}
|
}
|
||||||
i2c_delay(bus);
|
i2c_delay(bus);
|
||||||
// Clock stretching
|
|
||||||
set_scl(bus);
|
set_scl(bus);
|
||||||
while (read_scl(bus) == 0 && clk_stretch--)
|
|
||||||
;
|
|
||||||
// SCL is high, now data is valid
|
// SCL is high, now data is valid
|
||||||
// If SDA is high, check that nobody else is driving SDA
|
// If SDA is high, check that nobody else is driving SDA
|
||||||
if (bit && read_sda(bus) == 0) {
|
if (bit && read_sda(bus) == 0) {
|
||||||
|
@ -339,15 +361,11 @@ static void i2c_write_bit(uint8_t bus, bool bit)
|
||||||
// Read a bit from I2C bus
|
// Read a bit from I2C bus
|
||||||
static bool i2c_read_bit(uint8_t bus)
|
static bool i2c_read_bit(uint8_t bus)
|
||||||
{
|
{
|
||||||
uint32_t clk_stretch = i2c_bus[bus].clk_stretch;
|
|
||||||
bool bit;
|
bool bit;
|
||||||
// Let the slave drive data
|
// Let the slave drive data
|
||||||
set_sda(bus);
|
set_sda(bus);
|
||||||
i2c_delay(bus);
|
i2c_delay(bus);
|
||||||
set_scl(bus);
|
set_scl(bus);
|
||||||
// Clock stretching
|
|
||||||
while (read_scl(bus) == 0 && clk_stretch--)
|
|
||||||
;
|
|
||||||
// SCL is high, now data is valid
|
// SCL is high, now data is valid
|
||||||
bit = read_sda(bus);
|
bit = read_sda(bus);
|
||||||
i2c_delay(bus);
|
i2c_delay(bus);
|
||||||
|
|
|
@ -32,6 +32,8 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <FreeRTOS.h>
|
||||||
|
#include <task.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -51,7 +53,8 @@ extern "C" {
|
||||||
#define I2C_USE_GPIO16 0
|
#define I2C_USE_GPIO16 0
|
||||||
#endif
|
#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
|
/* SCL speed settings. 160 MHz sysclk frequency will result in improved
|
||||||
* timing accuracy. Greater bitrates will have poorer accuracy. 1000K is the
|
* 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 scl_pin SCL pin for I2C
|
||||||
* @param sda_pin SDA pin for I2C
|
* @param sda_pin SDA pin for I2C
|
||||||
* @param freq frequency of bus (ex : I2C_FREQ_400K)
|
* @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
|
* @return Non-zero if error occured
|
||||||
*/
|
*/
|
||||||
int i2c_init(uint8_t bus, uint8_t scl_pin, uint8_t sda_pin, i2c_freq_t freq);
|
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 scl_pin SCL pin for I2C
|
||||||
* @param sda_pin SDA pin for I2C
|
* @param sda_pin SDA pin for I2C
|
||||||
* @param freq frequency of bus in hertz
|
* @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
|
* @return Non-zero if error occured
|
||||||
*/
|
*/
|
||||||
int i2c_init_hz(uint8_t bus, uint8_t scl_pin, uint8_t sda_pin, uint32_t freq);
|
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
|
* Change clock stretch
|
||||||
* @param bus I2C bus
|
* @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.
|
* Write a byte to I2C bus.
|
||||||
|
|
Loading…
Reference in a new issue