Skip to content

Commit

Permalink
[stm32] I2C: Improve stability in case of the bus stuck
Browse files Browse the repository at this point in the history
  • Loading branch information
softsr authored and flixr committed Jul 28, 2015
1 parent 000bd52 commit cd508f9
Showing 1 changed file with 64 additions and 18 deletions.
82 changes: 64 additions & 18 deletions sw/airborne/arch/stm32/mcu_periph/i2c_arch.c
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ static inline void PPRZ_I2C_SEND_START(struct i2c_periph *periph)

// Reset the buffer pointer to the first byte
periph->idx_buf = 0;
periph->watchdog = 0;

#ifdef I2C_DEBUG_LED
LED_SHOW_ACTIVE_BITS(regs);
Expand Down Expand Up @@ -976,7 +977,7 @@ void i2c1_ev_isr(void)
uint32_t i2c = (uint32_t) i2c1.reg_addr;
i2c_disable_interrupt(i2c, I2C_CR2_ITERREN);
i2c1.watchdog = 0; // restart watchdog
i2c_irq(&i2c1);;
i2c_irq(&i2c1);
i2c_enable_interrupt(i2c, I2C_CR2_ITERREN);
}

Expand Down Expand Up @@ -1129,7 +1130,6 @@ void i2c3_er_isr(void)
{
uint32_t i2c = (uint32_t) i2c3.reg_addr;
i2c_disable_interrupt(i2c, I2C_CR2_ITEVTEN);
I2C_CR2(i2c) &= ~I2C_CR2_ITEVTEN;
i2c3.watchdog = 0; // restart watchdog
i2c_irq(&i2c3);
i2c_enable_interrupt(i2c, I2C_CR2_ITEVTEN);
Expand Down Expand Up @@ -1241,6 +1241,9 @@ void i2c_setbitrate(struct i2c_periph *periph, int bitrate)
}
}

#define WD_DELAY 20 // number of ticks with 2ms - 40ms delay before resetting the bus
#define WD_RECOVERY_TICKS 18 // number of generated SCL clocking pulses

#if USE_I2C1 || USE_I2C2 || USE_I2C3
static inline void i2c_scl_set(uint32_t i2c)
{
Expand All @@ -1261,27 +1264,45 @@ static inline void i2c_scl_set(uint32_t i2c)
#endif
}

static inline void i2c_scl_clear(uint32_t i2c)
static inline void i2c_scl_toggle(uint32_t i2c)
{
#if USE_I2C1
if (i2c == I2C1) {
gpio_clear(I2C1_GPIO_PORT, I2C1_GPIO_SCL);
gpio_toggle(I2C1_GPIO_PORT, I2C1_GPIO_SCL);
}
#endif
#if USE_I2C2
if (i2c == I2C2) {
gpio_clear(I2C2_GPIO_PORT, I2C2_GPIO_SCL);
gpio_toggle(I2C2_GPIO_PORT, I2C2_GPIO_SCL);
}
#endif
#if USE_I2C3
if (i2c == I2C3) {
gpio_clear(I2C3_GPIO_PORT_SCL, I2C3_GPIO_SCL);
gpio_toggle(I2C3_GPIO_PORT_SCL, I2C3_GPIO_SCL);
}
#endif
}

#define WD_DELAY 20 // number of ticks with 2ms - 40ms delay before resetting the bus
#define WD_RECOVERY_TICKS 10 // number of generated SCL clocking pulses
static inline bool_t i2c_sda_get(uint32_t i2c)
{
bool_t res = FALSE;
#if USE_I2C1
if (i2c == I2C1) {
res = gpio_get(I2C1_GPIO_PORT, I2C1_GPIO_SDA);
}
#endif
#if USE_I2C2
if (i2c == I2C2) {
res = gpio_get(I2C2_GPIO_PORT, I2C2_GPIO_SDA);
}
#endif
#if USE_I2C3
if (i2c == I2C3) {
res = gpio_get(I2C3_GPIO_PORT_SDA, I2C3_GPIO_SDA);
}
#endif
return res;
}

static void i2c_wd_check(struct i2c_periph *periph)
{
Expand All @@ -1293,6 +1314,15 @@ static void i2c_wd_check(struct i2c_periph *periph)
i2c_disable_interrupt(i2c, I2C_CR2_ITEVTEN);
i2c_disable_interrupt(i2c, I2C_CR2_ITERREN);

periph->trans_insert_idx = 0;
periph->trans_extract_idx = 0;
periph->status = I2CIdle;
struct i2c_transaction *trans;
for (uint8_t i = 0; i < I2C_TRANSACTION_QUEUE_LEN; i++) {
trans = periph->trans[i];
trans->status = I2CTransFailed;
}

i2c_peripheral_disable(i2c);

#if USE_I2C1
Expand All @@ -1314,27 +1344,40 @@ static void i2c_wd_check(struct i2c_periph *periph)
}
#endif

i2c_scl_clear(i2c);
i2c_scl_set(i2c);
} else if (periph->watchdog < WD_DELAY + WD_RECOVERY_TICKS) {
if ((periph->watchdog - WD_DELAY) % 2) {
i2c_scl_clear(i2c);
} else {
i2c_scl_set(i2c);
if (i2c_sda_get(i2c)) {
periph->watchdog = WD_DELAY + WD_RECOVERY_TICKS;
}
i2c_scl_toggle(i2c);
} else {
i2c_scl_set(i2c);

/* setup gpios for normal i2c operation again */
i2c_setup_gpio(i2c);

periph->trans_insert_idx = 0;
periph->trans_extract_idx = 0;
periph->status = I2CIdle;
i2c_reset(i2c);

i2c_enable_interrupt(i2c, I2C_CR2_ITEVTEN);
i2c_peripheral_enable(i2c);
i2c_set_own_7bit_slave_address(i2c, 0);
i2c_enable_interrupt(i2c, I2C_CR2_ITERREN);

i2c_peripheral_enable(i2c);
#if USE_I2C1
if (i2c == I2C1) {
i2c_setbitrate(periph, I2C1_CLOCK_SPEED);
}
#endif
#if USE_I2C2
if (i2c == I2C2) {
i2c_setbitrate(periph, I2C2_CLOCK_SPEED);
}
#endif
#if USE_I2C3
if (i2c == I2C3) {
i2c_setbitrate(periph, I2C3_CLOCK_SPEED);
}
#endif

periph->watchdog = 0; // restart watchdog

periph->errors->timeout_tlow_cnt++;
Expand Down Expand Up @@ -1374,6 +1417,9 @@ void i2c_event(void)

bool_t i2c_submit(struct i2c_periph *periph, struct i2c_transaction *t)
{
if (periph->watchdog > WD_DELAY) {
return FALSE;
}

uint8_t temp;
temp = periph->trans_insert_idx + 1;
Expand Down

0 comments on commit cd508f9

Please sign in to comment.