From 52d130a832039a636f5570913791aa17eed43115 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Mon, 23 Aug 2021 17:33:47 +0200 Subject: [PATCH] softi2c_master: add timeout to prevent infinite loop --- softi2c_master.c | 45 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/softi2c_master.c b/softi2c_master.c index 0fd5df0..24d85f3 100644 --- a/softi2c_master.c +++ b/softi2c_master.c @@ -19,10 +19,16 @@ // half period to wait for I²C clock static uint16_t period = 0; +// port for data line #define SDA_PORT GPIO_PB +// pin for data line #define SDA_PIN PB5 +// port for clock line #define SCL_PORT GPIO_PB +// pin for clock line #define SCL_PIN PB4 +// operation timeout (in half period) +#define TIMEOUT 10U // delay for half a period static void I2C_delay(void) @@ -84,7 +90,7 @@ bool softi2c_master_setup(uint16_t freq_khz) SDA_PORT->CR1.reg &= ~SDA_PIN; // use in open-drain mode I2C_delay(); // give time to get high - return (read_SCL() && read_SDA()); + return (read_SCL() && read_SDA()); // line is ready when the two lines are high } void softi2c_master_release(void) @@ -104,7 +110,14 @@ bool softi2c_master_start(void) set_SDA(); I2C_delay(); set_SCL(); - while (read_SCL() == 0); // Clock stretching + uint8_t timeout = TIMEOUT; + while (read_SCL() == 0 && timeout) { // Clock stretching + I2C_delay(); + timeout--; + } + if (0 == timeout) { + return false; + } // Repeated start setup time, minimum 4.7us I2C_delay(); @@ -130,7 +143,14 @@ bool softi2c_master_stop(void) I2C_delay(); set_SCL(); - while (read_SCL() == 0); // Clock stretching + uint8_t timeout = TIMEOUT; + while (read_SCL() == 0 && timeout) { // Clock stretching + I2C_delay(); + timeout--; + } + if (0 == timeout) { + return false; + } I2C_delay(); // Stop bit setup time, minimum 4us @@ -158,7 +178,15 @@ static bool softi2c_master_write_bit(bool bit) { I2C_delay(); // SDA change propagation delay set_SCL(); // Set SCL high to indicate a new valid SDA value is available I2C_delay(); // Wait for SDA value to be read by slave, minimum of 4us for standard mode - while (read_SCL() == 0); // Clock stretching + uint8_t timeout = TIMEOUT; + while (read_SCL() == 0 && timeout) { // Clock stretching + I2C_delay(); + timeout--; + } + if (0 == timeout) { + return false; + } + // SCL is high, now data is valid if (bit && (read_SDA() == 0)) { // If SDA is high, check that nobody else is driving SDA return false; @@ -173,7 +201,14 @@ static bool softi2c_master_read_bit(void) { set_SDA(); // Let the slave drive data I2C_delay(); // Wait for SDA value to be written by slave, minimum of 4us for standard mode set_SCL(); // Set SCL high to indicate a new valid SDA value is available - while (read_SCL() == 0); // Clock stretching + uint8_t timeout = TIMEOUT; + while (read_SCL() == 0 && timeout) { // Clock stretching + I2C_delay(); + timeout--; + } + if (0 == timeout) { + return false; + } I2C_delay(); // Wait for SDA value to be written by slave, minimum of 4us for standard mode const bool bit = read_SDA(); // SCL is high, read out bit clear_SCL(); // Set SCL low in preparation for next operation