extmod/machine_i2c: Add clock stretching support.

When the clock is too fast for the i2c slave, it can temporarily hold
down the scl line to signal to the master that it needs to wait. The
master should check the scl line when it is releasing it after
transmitting data, and wait for it to be released.

This change has been tested with a logic analyzer and an i2c slace
implemented on an atmega328p using its twi peripheral, clocked at 8Mhz.
Without the change, the i2c communication works up to aboy 150kHz
frequency, and above that results in the slave stuck in an unresponsive
state. With this change, communication has been tested to work up to
400kHz.
This commit is contained in:
Radomir Dopieralski 2016-09-15 01:08:03 +02:00 committed by Damien George
parent 1f69b16d3f
commit ec078af985
1 changed files with 8 additions and 6 deletions

View File

@ -34,6 +34,9 @@
#if MICROPY_PY_MACHINE_I2C
// Clock stretching limit, so that we don't get stuck.
#define I2C_STRETCH_LIMIT 255
typedef struct _machine_i2c_obj_t {
mp_obj_base_t base;
uint32_t us_delay;
@ -53,6 +56,11 @@ STATIC void mp_hal_i2c_scl_low(machine_i2c_obj_t *self) {
STATIC void mp_hal_i2c_scl_release(machine_i2c_obj_t *self) {
mp_hal_pin_od_high(self->scl);
mp_hal_i2c_delay(self);
// For clock stretching, wait for the SCL pin to be released, with timeout.
for (int count = I2C_STRETCH_LIMIT; mp_hal_pin_read(self->scl) == 0 && count; --count) {
mp_hal_delay_us_fast(1);
}
}
STATIC void mp_hal_i2c_sda_low(machine_i2c_obj_t *self) {
@ -71,7 +79,6 @@ STATIC void mp_hal_i2c_start(machine_i2c_obj_t *self) {
mp_hal_i2c_sda_release(self);
mp_hal_i2c_delay(self);
mp_hal_i2c_scl_release(self);
mp_hal_i2c_delay(self);
mp_hal_i2c_sda_low(self);
mp_hal_i2c_delay(self);
}
@ -81,7 +88,6 @@ STATIC void mp_hal_i2c_stop(machine_i2c_obj_t *self) {
mp_hal_i2c_sda_low(self);
mp_hal_i2c_delay(self);
mp_hal_i2c_scl_release(self);
mp_hal_i2c_delay(self);
mp_hal_i2c_sda_release(self);
mp_hal_i2c_delay(self);
}
@ -108,14 +114,12 @@ STATIC int mp_hal_i2c_write_byte(machine_i2c_obj_t *self, uint8_t val) {
}
mp_hal_i2c_delay(self);
mp_hal_i2c_scl_release(self);
mp_hal_i2c_delay(self);
mp_hal_i2c_scl_low(self);
}
mp_hal_i2c_sda_release(self);
mp_hal_i2c_delay(self);
mp_hal_i2c_scl_release(self);
mp_hal_i2c_delay(self);
int ret = mp_hal_i2c_sda_read(self);
mp_hal_i2c_delay(self);
@ -150,7 +154,6 @@ STATIC int mp_hal_i2c_read_byte(machine_i2c_obj_t *self, uint8_t *val, int nack)
uint8_t data = 0;
for (int i = 7; i >= 0; i--) {
mp_hal_i2c_scl_release(self);
mp_hal_i2c_delay(self);
data = (data << 1) | mp_hal_i2c_sda_read(self);
mp_hal_i2c_scl_low(self);
mp_hal_i2c_delay(self);
@ -163,7 +166,6 @@ STATIC int mp_hal_i2c_read_byte(machine_i2c_obj_t *self, uint8_t *val, int nack)
}
mp_hal_i2c_delay(self);
mp_hal_i2c_scl_release(self);
mp_hal_i2c_delay(self);
mp_hal_i2c_scl_low(self);
mp_hal_i2c_sda_release(self);