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