softi2c_master: add timeout to prevent infinite loop

This commit is contained in:
King Kévin 2021-08-23 17:33:47 +02:00
parent 04901a6ce1
commit 52d130a832
1 changed files with 40 additions and 5 deletions

View File

@ -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