From 9fbc9d503ef4e5d63a1ff782a24d880873022665 Mon Sep 17 00:00:00 2001 From: Rogerio Goncalves Date: Tue, 29 Oct 2024 11:13:04 +0000 Subject: [PATCH] I2C Error handling (#6689) (#400) * i2ccmds: abstract i2c dev from bus implementation Added wrapper around sw/hw bus API, pins.py code will ensure that pins will not mix between HW/SW buses. Signed-off-by: Timofey Titovets * i2c: handle errors at i2ccmds Signed-off-by: Timofey Titovets * i2c_software: forward errors to i2ccmd Signed-off-by: Timofey Titovets * linux: forward i2c errors to i2ccmd Signed-off-by: Timofey Titovets * rp2040: forward i2c errors to i2ccmd Signed-off-by: Timofey Titovets * stm32: forward i2c errors to i2ccmd Signed-off-by: Timofey Titovets * i2ccmds: move status checks to function Signed-off-by: Timofey Titovets * ldc1612: shutdown on i2c errors Signed-off-by: Timofey Titovets * mpu: shutdown on i2c errors Signed-off-by: Timofey Titovets --------- Signed-off-by: Timofey Titovets Co-authored-by: Timofey Titovets --- src/atsam/gpio.h | 6 ++--- src/atsam/i2c.c | 9 +++++-- src/atsamd/gpio.h | 6 ++--- src/atsamd/i2c.c | 9 +++++-- src/avr/gpio.h | 4 +-- src/avr/i2c.c | 9 +++++-- src/hc32f460/gpio.h | 6 ++--- src/i2c_software.c | 57 +++++++++++++++++++++++++++++------------ src/i2c_software.h | 10 ++++---- src/i2ccmds.c | 53 ++++++++++++++++++++++++++++---------- src/i2ccmds.h | 19 ++++++++++++-- src/linux/gpio.h | 4 +-- src/linux/i2c.c | 31 +++++++++++++++------- src/lpc176x/gpio.h | 6 ++--- src/lpc176x/i2c.c | 9 +++++-- src/rp2040/gpio.h | 6 ++--- src/rp2040/i2c.c | 45 ++++++++++++++++++++------------ src/sensor_ldc1612.c | 4 +-- src/sensor_mpu9250.c | 19 +++++++++----- src/stm32/gpio.h | 6 ++--- src/stm32/i2c.c | 9 +++++-- src/stm32/stm32f0_i2c.c | 37 ++++++++++++++++++-------- 22 files changed, 249 insertions(+), 115 deletions(-) diff --git a/src/atsam/gpio.h b/src/atsam/gpio.h index e9fc4c3ed..638d02da4 100644 --- a/src/atsam/gpio.h +++ b/src/atsam/gpio.h @@ -50,8 +50,8 @@ struct i2c_config { }; struct i2c_config i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr); -void i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write); -void i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg - , uint8_t read_len, uint8_t *read); +int i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write); +int i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg + , uint8_t read_len, uint8_t *read); #endif // gpio.h diff --git a/src/atsam/i2c.c b/src/atsam/i2c.c index 0661727c2..8c3dc8ad2 100644 --- a/src/atsam/i2c.c +++ b/src/atsam/i2c.c @@ -10,6 +10,7 @@ #include "gpio.h" // i2c_setup #include "internal.h" // gpio_peripheral #include "sched.h" // sched_shutdown +#include "i2ccmds.h" // I2C_BUS_SUCCESS #if CONFIG_MACH_SAME70 #include "same70_i2c.h" // Fixes for upstream header changes @@ -126,7 +127,7 @@ i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr) return (struct i2c_config){ .twi=p_twi, .addr=addr}; } -void +int i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write) { Twi *p_twi = config.twi; @@ -150,9 +151,11 @@ i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write) p_twi->TWI_CR = TWI_CR_STOP; while (!(p_twi->TWI_SR & TWI_SR_TXCOMP)) ; + + return I2C_BUS_SUCCESS; } -void +int i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg , uint8_t read_len, uint8_t *read) { @@ -192,4 +195,6 @@ i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg while (!(p_twi->TWI_SR & TWI_SR_TXCOMP)) ; (void)p_twi->TWI_SR; + + return I2C_BUS_SUCCESS; } diff --git a/src/atsamd/gpio.h b/src/atsamd/gpio.h index b8cb3e861..d29071dc6 100644 --- a/src/atsamd/gpio.h +++ b/src/atsamd/gpio.h @@ -51,8 +51,8 @@ struct i2c_config { }; struct i2c_config i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr); -void i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write); -void i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg - , uint8_t read_len, uint8_t *read); +int i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write); +int i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg + , uint8_t read_len, uint8_t *read); #endif // gpio.h diff --git a/src/atsamd/i2c.c b/src/atsamd/i2c.c index 3c316142c..18506a27c 100644 --- a/src/atsamd/i2c.c +++ b/src/atsamd/i2c.c @@ -9,6 +9,7 @@ #include "command.h" // shutdown #include "gpio.h" // i2c_setup #include "sched.h" // sched_shutdown +#include "i2ccmds.h" // I2C_BUS_SUCCESS #define TIME_RISE 125ULL // 125 nanoseconds #define I2C_FREQ 100000 @@ -86,7 +87,7 @@ i2c_stop(SercomI2cm *si) si->CTRLB.reg = SERCOM_I2CM_CTRLB_CMD(3); } -void +int i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write) { SercomI2cm *si = (SercomI2cm *)config.si; @@ -94,9 +95,11 @@ i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write) while (write_len--) i2c_send_byte(si, *write++); i2c_stop(si); + + return I2C_BUS_SUCCESS; } -void +int i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg , uint8_t read_len, uint8_t *read) { @@ -143,4 +146,6 @@ i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg // read received data byte *read++ = si->DATA.reg; } + + return I2C_BUS_SUCCESS; } diff --git a/src/avr/gpio.h b/src/avr/gpio.h index 9d98ee709..9f28a30ac 100644 --- a/src/avr/gpio.h +++ b/src/avr/gpio.h @@ -50,8 +50,8 @@ struct i2c_config { }; struct i2c_config i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr); -void i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write); -void i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg +int i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write); +int i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg , uint8_t read_len, uint8_t *read); #endif // gpio.h diff --git a/src/avr/i2c.c b/src/avr/i2c.c index 658a30a12..e8febfba9 100644 --- a/src/avr/i2c.c +++ b/src/avr/i2c.c @@ -11,6 +11,7 @@ #include "gpio.h" // i2c_setup #include "internal.h" // GPIO #include "sched.h" // sched_shutdown +#include "i2ccmds.h" // I2C_BUS_SUCCESS DECL_ENUMERATION("i2c_bus", "twi", 0); @@ -94,7 +95,7 @@ i2c_stop(uint32_t timeout) TWCR = (1<ticks); gpio_in_reset(is->sda_in, 1); gpio_in_reset(is->scl_in, 1); @@ -137,7 +139,10 @@ i2c_software_start(struct i2c_software *is, uint8_t addr) i2c_delay(is->ticks); gpio_out_reset(is->scl_out, 0); - i2c_software_send_byte(is, addr); + ret = i2c_software_send_byte(is, addr); + if (ret == I2C_BUS_NACK) + return I2C_BUS_START_NACK; + return ret; } static void @@ -150,32 +155,52 @@ i2c_software_stop(struct i2c_software *is) gpio_in_reset(is->sda_in, 1); } -void +int i2c_software_write(struct i2c_software *is, uint8_t write_len, uint8_t *write) { - i2c_software_start(is, is->addr); - while (write_len--) - i2c_software_send_byte(is, *write++); + int ret = i2c_software_start(is, is->addr); + if (ret != I2C_BUS_SUCCESS) + goto out; + + while (write_len--) { + ret = i2c_software_send_byte(is, *write++); + if (ret != I2C_BUS_SUCCESS) + break; + } + +out: i2c_software_stop(is); + return ret; } -void +int i2c_software_read(struct i2c_software *is, uint8_t reg_len, uint8_t *reg , uint8_t read_len, uint8_t *read) { - uint8_t addr = is->addr | 0x01; - + int ret; if (reg_len) { // write the register - i2c_software_start(is, is->addr); - while(reg_len--) - i2c_software_send_byte(is, *reg++); + ret = i2c_software_start(is, is->addr); + if (ret != I2C_BUS_SUCCESS) + goto out; + while(reg_len--) { + ret = i2c_software_send_byte(is, *reg++); + if (ret != I2C_BUS_SUCCESS) + goto out; + } + } // start/re-start and read data - i2c_software_start(is, addr); + ret = i2c_software_start(is, is->addr | 0x01); + if (ret != I2C_BUS_SUCCESS) { + ret = I2C_BUS_START_READ_NACK; + goto out; + } while(read_len--) { *read = i2c_software_read_byte(is, read_len); read++; } +out: i2c_software_stop(is); + return ret; } diff --git a/src/i2c_software.h b/src/i2c_software.h index 9bd54f29a..843ab461a 100644 --- a/src/i2c_software.h +++ b/src/i2c_software.h @@ -4,10 +4,10 @@ #include // uint8_t struct i2c_software *i2c_software_oid_lookup(uint8_t oid); -void i2c_software_write(struct i2c_software *sw_i2c - , uint8_t write_len, uint8_t *write); -void i2c_software_read(struct i2c_software *sw_i2c - , uint8_t reg_len, uint8_t *reg - , uint8_t read_len, uint8_t *read); +int i2c_software_write(struct i2c_software *sw_i2c + , uint8_t write_len, uint8_t *write); +int i2c_software_read(struct i2c_software *sw_i2c + , uint8_t reg_len, uint8_t *reg + , uint8_t read_len, uint8_t *read); #endif // i2c_software.h diff --git a/src/i2ccmds.c b/src/i2ccmds.c index 8a508615f..c51772f1d 100644 --- a/src/i2ccmds.c +++ b/src/i2ccmds.c @@ -36,7 +36,7 @@ command_i2c_set_bus(uint32_t *args) { uint8_t addr = args[3] & 0x7f; struct i2cdev_s *i2c = i2cdev_oid_lookup(args[0]); - i2c->i2c_config = i2c_setup(args[1], args[2], addr); + i2c->i2c_hw = i2c_setup(args[1], args[2], addr); i2c->flags |= IF_HARDWARE; } DECL_COMMAND(command_i2c_set_bus, @@ -45,27 +45,55 @@ DECL_COMMAND(command_i2c_set_bus, void i2cdev_set_software_bus(struct i2cdev_s *i2c, struct i2c_software *is) { - i2c->i2c_software = is; + i2c->i2c_sw = is; i2c->flags |= IF_SOFTWARE; } -void -command_i2c_write(uint32_t *args) +void i2c_shutdown_on_err(int ret) +{ + switch (ret) { + case I2C_BUS_NACK: + shutdown("I2C NACK"); + case I2C_BUS_START_NACK: + shutdown("I2C START NACK"); + case I2C_BUS_START_READ_NACK: + shutdown("I2C START READ NACK"); + case I2C_BUS_TIMEOUT: + shutdown("I2C Timeout"); + } +} + +int i2c_dev_write(struct i2cdev_s *i2c, uint8_t write_len, uint8_t *data) +{ + uint_fast8_t flags = i2c->flags; + if (CONFIG_WANT_SOFTWARE_I2C && flags & IF_SOFTWARE) + return i2c_software_write(i2c->i2c_sw, write_len, data); + else + return i2c_write(i2c->i2c_hw, write_len, data); +} + +void command_i2c_write(uint32_t *args) { uint8_t oid = args[0]; struct i2cdev_s *i2c = oid_lookup(oid, command_config_i2c); uint8_t data_len = args[1]; uint8_t *data = command_decode_ptr(args[2]); + int ret = i2c_dev_write(i2c, data_len, data); + i2c_shutdown_on_err(ret); +} +DECL_COMMAND(command_i2c_write, "i2c_write oid=%c data=%*s"); + +int i2c_dev_read(struct i2cdev_s *i2c, uint8_t reg_len, uint8_t *reg + , uint8_t read_len, uint8_t *read) +{ uint_fast8_t flags = i2c->flags; if (CONFIG_WANT_SOFTWARE_I2C && flags & IF_SOFTWARE) - i2c_software_write(i2c->i2c_software, data_len, data); + return i2c_software_read(i2c->i2c_sw, reg_len, reg, read_len, read); else - i2c_write(i2c->i2c_config, data_len, data); + return i2c_read(i2c->i2c_hw, reg_len, reg, read_len, read); } -DECL_COMMAND(command_i2c_write, "i2c_write oid=%c data=%*s"); -void -command_i2c_read(uint32_t * args) +void command_i2c_read(uint32_t *args) { uint8_t oid = args[0]; struct i2cdev_s *i2c = oid_lookup(oid, command_config_i2c); @@ -73,11 +101,8 @@ command_i2c_read(uint32_t * args) uint8_t *reg = command_decode_ptr(args[2]); uint8_t data_len = args[3]; uint8_t data[data_len]; - uint_fast8_t flags = i2c->flags; - if (CONFIG_WANT_SOFTWARE_I2C && flags & IF_SOFTWARE) - i2c_software_read(i2c->i2c_software, reg_len, reg, data_len, data); - else - i2c_read(i2c->i2c_config, reg_len, reg, data_len, data); + int ret = i2c_dev_read(i2c, reg_len, reg, data_len, data); + i2c_shutdown_on_err(ret); sendf("i2c_read_response oid=%c response=%*s", oid, data_len, data); } DECL_COMMAND(command_i2c_read, "i2c_read oid=%c reg=%*s read_len=%u"); diff --git a/src/i2ccmds.h b/src/i2ccmds.h index 9ce54aa06..55c97b6b5 100644 --- a/src/i2ccmds.h +++ b/src/i2ccmds.h @@ -4,13 +4,28 @@ #include #include "board/gpio.h" // i2c_config +// I2C ERROR Codes +enum { + I2C_BUS_SUCCESS, + I2C_BUS_NACK, + I2C_BUS_TIMEOUT, + I2C_BUS_START_NACK, + I2C_BUS_START_READ_NACK, +}; + struct i2cdev_s { - struct i2c_config i2c_config; - struct i2c_software *i2c_software; + union { + struct i2c_config i2c_hw; + struct i2c_software *i2c_sw; + }; uint8_t flags; }; struct i2cdev_s *i2cdev_oid_lookup(uint8_t oid); void i2cdev_set_software_bus(struct i2cdev_s *i2c, struct i2c_software *is); +int i2c_dev_read(struct i2cdev_s *i2c, uint8_t reg_len, uint8_t *reg + , uint8_t read_len, uint8_t *read); +int i2c_dev_write(struct i2cdev_s *i2c, uint8_t write_len, uint8_t *data); +void i2c_shutdown_on_err(int ret); #endif diff --git a/src/linux/gpio.h b/src/linux/gpio.h index df9de7525..d72007c2c 100644 --- a/src/linux/gpio.h +++ b/src/linux/gpio.h @@ -49,8 +49,8 @@ struct i2c_config { }; struct i2c_config i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr); -void i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write); -void i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg +int i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write); +int i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg , uint8_t read_len, uint8_t *read); #endif // gpio.h diff --git a/src/linux/i2c.c b/src/linux/i2c.c index b328dc56e..a728752aa 100644 --- a/src/linux/i2c.c +++ b/src/linux/i2c.c @@ -13,6 +13,7 @@ #include "command.h" // shutdown #include "internal.h" // report_errno #include "sched.h" // sched_shutdown +#include "i2ccmds.h" // I2C_BUS_SUCCESS DECL_ENUMERATION_RANGE("i2c_bus", "i2c.0", 0, 15); @@ -84,18 +85,28 @@ i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr) return (struct i2c_config){.fd=fd, .addr=addr}; } -void +int i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *data) { - int ret = write(config.fd, data, write_len); - if (ret != write_len) { - if (ret < 0) - report_errno("write value i2c", ret); - try_shutdown("Unable write i2c device"); + struct i2c_rdwr_ioctl_data i2c_data; + struct i2c_msg msgs[1]; + msgs[0].addr = config.addr; + msgs[0].flags = 0x0; + msgs[0].len = write_len; + msgs[0].buf = data; + i2c_data.nmsgs = 1; + i2c_data.msgs = &msgs[0]; + + int ret = ioctl(config.fd, I2C_RDWR, &i2c_data); + + if (ret < 0) { + return I2C_BUS_NACK; } + + return I2C_BUS_SUCCESS; } -void +int i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg , uint8_t read_len, uint8_t *data) { @@ -121,7 +132,9 @@ i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg int ret = ioctl(config.fd, I2C_RDWR, &i2c_data); - if(ret < 0) { - try_shutdown("Unable to read i2c device"); + if (ret < 0) { + return I2C_BUS_NACK; } + + return I2C_BUS_SUCCESS; } diff --git a/src/lpc176x/gpio.h b/src/lpc176x/gpio.h index e03afbb44..c1507c64c 100644 --- a/src/lpc176x/gpio.h +++ b/src/lpc176x/gpio.h @@ -51,8 +51,8 @@ struct i2c_config { }; struct i2c_config i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr); -void i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write); -void i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg - , uint8_t read_len, uint8_t *read); +int i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write); +int i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg + , uint8_t read_len, uint8_t *read); #endif // gpio.h diff --git a/src/lpc176x/i2c.c b/src/lpc176x/i2c.c index 7ed631af1..519dd504c 100644 --- a/src/lpc176x/i2c.c +++ b/src/lpc176x/i2c.c @@ -9,6 +9,7 @@ #include "gpio.h" // i2c_setup #include "internal.h" // gpio_peripheral #include "sched.h" // sched_shutdown +#include "i2ccmds.h" // I2C_BUS_SUCCESS struct i2c_info { LPC_I2C_TypeDef *i2c; @@ -122,7 +123,7 @@ i2c_stop(LPC_I2C_TypeDef *i2c, uint32_t timeout) i2c_wait(i2c, IF_STOP, timeout); } -void +int i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write) { LPC_I2C_TypeDef *i2c = config.i2c; @@ -133,9 +134,11 @@ i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write) while (write_len--) i2c_send_byte(i2c, *write++, timeout); i2c_stop(i2c, timeout); + + return I2C_BUS_SUCCESS; } -void +int i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg , uint8_t read_len, uint8_t *read) { @@ -159,4 +162,6 @@ i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg read++; } i2c_stop(i2c, timeout); + + return I2C_BUS_SUCCESS; } diff --git a/src/rp2040/gpio.h b/src/rp2040/gpio.h index ae6083780..0dd393bfe 100644 --- a/src/rp2040/gpio.h +++ b/src/rp2040/gpio.h @@ -50,8 +50,8 @@ struct i2c_config { }; struct i2c_config i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr); -void i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write); -void i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg - , uint8_t read_len, uint8_t *read); +int i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write); +int i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg + , uint8_t read_len, uint8_t *read); #endif // gpio.h diff --git a/src/rp2040/i2c.c b/src/rp2040/i2c.c index 84b627ce8..e4d51c053 100644 --- a/src/rp2040/i2c.c +++ b/src/rp2040/i2c.c @@ -11,6 +11,7 @@ #include "internal.h" // pclock, gpio_peripheral #include "hardware/regs/resets.h" // RESETS_RESET_I2C*_BITS #include "hardware/structs/i2c.h" +#include "i2ccmds.h" // I2C_BUS_SUCCESS struct i2c_info { i2c_hw_t *i2c; @@ -132,8 +133,8 @@ i2c_stop(i2c_hw_t *i2c) i2c->enable = 0; } -static void -i2c_do_write(i2c_hw_t *i2c, uint8_t addr, uint8_t write_len, uint8_t *write +static int +i2c_do_write(i2c_hw_t *i2c, uint8_t write_len, uint8_t *write , uint8_t send_stop, uint32_t timeout) { for (int i = 0; i < write_len; i++) { @@ -143,7 +144,7 @@ i2c_do_write(i2c_hw_t *i2c, uint8_t addr, uint8_t write_len, uint8_t *write // Wait until there's a spot in the TX FIFO while (i2c->txflr == 16) { if (!timer_is_before(timer_read_time(), timeout)) - shutdown("i2c timeout"); + return I2C_BUS_TIMEOUT; } i2c->data_cmd = first << I2C_IC_DATA_CMD_RESTART_LSB @@ -152,12 +153,12 @@ i2c_do_write(i2c_hw_t *i2c, uint8_t addr, uint8_t write_len, uint8_t *write } if (!send_stop) - return; + return I2C_BUS_SUCCESS; // Drain the transmit buffer while (i2c->txflr != 0) { if (!timer_is_before(timer_read_time(), timeout)) - shutdown("i2c timeout"); + return I2C_BUS_TIMEOUT; } if (i2c->raw_intr_stat & I2C_IC_RAW_INTR_STAT_TX_ABRT_BITS) { @@ -165,25 +166,26 @@ i2c_do_write(i2c_hw_t *i2c, uint8_t addr, uint8_t write_len, uint8_t *write if (abort_source & I2C_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK_BITS) { i2c->clr_tx_abrt; - shutdown("i2c Start NACK"); + return I2C_BUS_START_NACK; } if (abort_source & I2C_IC_TX_ABRT_SOURCE_ABRT_TXDATA_NOACK_BITS) { i2c->clr_tx_abrt; - shutdown("i2c NACK"); + return I2C_BUS_NACK; } } + return I2C_BUS_SUCCESS; } -static void -i2c_do_read(i2c_hw_t *i2c, uint8_t addr, uint8_t read_len, uint8_t *read +static int +i2c_do_read(i2c_hw_t *i2c, uint8_t read_len, uint8_t *read , uint32_t timeout) { int have_read = 0; int to_send = read_len; while (have_read < read_len) { if (!timer_is_before(timer_read_time(), timeout)) - shutdown("i2c timeout"); + return I2C_BUS_TIMEOUT; if (to_send > 0 && i2c->txflr < 16) { int first = to_send == read_len; @@ -205,33 +207,42 @@ i2c_do_read(i2c_hw_t *i2c, uint8_t addr, uint8_t read_len, uint8_t *read uint32_t abort_source = i2c->tx_abrt_source; if (abort_source & I2C_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK_BITS) { i2c->clr_tx_abrt; - shutdown("i2c Start Read NACK"); + return I2C_BUS_START_READ_NACK; } } } + + return I2C_BUS_SUCCESS; } -void +int i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write) { i2c_hw_t *i2c = (i2c_hw_t*)config.i2c; uint32_t timeout = timer_read_time() + timer_from_us(5000); + int ret; i2c_start(i2c, config.addr); - i2c_do_write(i2c, config.addr, write_len, write, 1, timeout); + ret = i2c_do_write(i2c, write_len, write, 1, timeout); i2c_stop(i2c); + return ret; } -void +int i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg , uint8_t read_len, uint8_t *read) { i2c_hw_t *i2c = (i2c_hw_t*)config.i2c; uint32_t timeout = timer_read_time() + timer_from_us(5000); + int ret = I2C_BUS_SUCCESS; i2c_start(i2c, config.addr); if (reg_len != 0) - i2c_do_write(i2c, config.addr, reg_len, reg, 0, timeout); - i2c_do_read(i2c, config.addr, read_len, read, timeout); + ret = i2c_do_write(i2c, reg_len, reg, 0, timeout); + if (ret != I2C_BUS_SUCCESS) + goto out; + ret = i2c_do_read(i2c, read_len, read, timeout); +out: i2c_stop(i2c); -} + return ret; +} \ No newline at end of file diff --git a/src/sensor_ldc1612.c b/src/sensor_ldc1612.c index 01cf3ee04..219583329 100644 --- a/src/sensor_ldc1612.c +++ b/src/sensor_ldc1612.c @@ -7,7 +7,6 @@ #include // memcpy #include "basecmd.h" // oid_alloc -#include "board/gpio.h" // i2c_read #include "board/irq.h" // irq_disable #include "board/misc.h" // timer_read_time #include "command.h" // DECL_COMMAND @@ -147,7 +146,8 @@ check_home(struct ldc1612 *ld, uint32_t data) static void read_reg(struct ldc1612 *ld, uint8_t reg, uint8_t *res) { - i2c_read(ld->i2c->i2c_config, sizeof(reg), ®, 2, res); + int ret = i2c_dev_read(ld->i2c, sizeof(reg), ®, 2, res); + i2c_shutdown_on_err(ret); } // Read the status register on the ldc1612 diff --git a/src/sensor_mpu9250.c b/src/sensor_mpu9250.c index 23c029211..7476734c4 100644 --- a/src/sensor_mpu9250.c +++ b/src/sensor_mpu9250.c @@ -13,7 +13,6 @@ #include "command.h" // DECL_COMMAND #include "sched.h" // DECL_TASK #include "sensor_bulk.h" // sensor_bulk_report -#include "board/gpio.h" // i2c_read #include "i2ccmds.h" // i2cdev_oid_lookup // Chip registers @@ -71,13 +70,21 @@ mp9250_reschedule_timer(struct mpu9250 *mp) irq_enable(); } +static void +read_mpu(struct i2cdev_s *i2c, uint8_t reg_len, uint8_t *reg + , uint8_t read_len, uint8_t *read) +{ + int ret = i2c_dev_read(i2c, reg_len, reg, read_len, read); + i2c_shutdown_on_err(ret); +} + // Reads the fifo byte count from the device. static uint16_t get_fifo_status(struct mpu9250 *mp) { uint8_t reg[] = {AR_FIFO_COUNT_H}; uint8_t msg[2]; - i2c_read(mp->i2c->i2c_config, sizeof(reg), reg, sizeof(msg), msg); + read_mpu(mp->i2c, sizeof(reg), reg, sizeof(msg), msg); uint16_t fifo_bytes = ((msg[0] & 0x1f) << 8) | msg[1]; if (fifo_bytes > mp->fifo_max) mp->fifo_max = fifo_bytes; @@ -95,8 +102,7 @@ mp9250_query(struct mpu9250 *mp, uint8_t oid) // If we have enough bytes to fill the buffer do it and send report if (mp->fifo_pkts_bytes >= BYTES_PER_BLOCK) { uint8_t reg = AR_FIFO; - i2c_read(mp->i2c->i2c_config, sizeof(reg), ® - , BYTES_PER_BLOCK, &mp->sb.data[0]); + read_mpu(mp->i2c, sizeof(reg), ®, BYTES_PER_BLOCK, &mp->sb.data[0]); mp->sb.data_count = BYTES_PER_BLOCK; mp->fifo_pkts_bytes -= BYTES_PER_BLOCK; sensor_bulk_report(&mp->sb, oid); @@ -144,8 +150,7 @@ command_query_mpu9250_status(uint32_t *args) // Detect if a FIFO overrun occurred uint8_t int_reg[] = {AR_INT_STATUS}; uint8_t int_msg; - i2c_read(mp->i2c->i2c_config, sizeof(int_reg), int_reg, sizeof(int_msg), - &int_msg); + read_mpu(mp->i2c, sizeof(int_reg), int_reg, sizeof(int_msg), &int_msg); if (int_msg & FIFO_OVERFLOW_INT) mp->sb.possible_overflows++; @@ -153,7 +158,7 @@ command_query_mpu9250_status(uint32_t *args) uint8_t reg[] = {AR_FIFO_COUNT_H}; uint8_t msg[2]; uint32_t time1 = timer_read_time(); - i2c_read(mp->i2c->i2c_config, sizeof(reg), reg, sizeof(msg), msg); + read_mpu(mp->i2c, sizeof(reg), reg, sizeof(msg), msg); uint32_t time2 = timer_read_time(); uint16_t fifo_bytes = ((msg[0] & 0x1f) << 8) | msg[1]; diff --git a/src/stm32/gpio.h b/src/stm32/gpio.h index 281e3a8db..78f567b81 100644 --- a/src/stm32/gpio.h +++ b/src/stm32/gpio.h @@ -51,8 +51,8 @@ struct i2c_config { }; struct i2c_config i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr); -void i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write); -void i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg - , uint8_t read_len, uint8_t *read); +int i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write); +int i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg + , uint8_t read_len, uint8_t *read); #endif // gpio.h diff --git a/src/stm32/i2c.c b/src/stm32/i2c.c index 4bb8b105f..441f611a8 100644 --- a/src/stm32/i2c.c +++ b/src/stm32/i2c.c @@ -11,6 +11,7 @@ #include "internal.h" // GPIO #include "sched.h" // sched_shutdown #include "board/irq.h" //irq_disable +#include "i2ccmds.h" // I2C_BUS_SUCCESS struct i2c_info { I2C_TypeDef *i2c; @@ -149,7 +150,7 @@ i2c_stop(I2C_TypeDef *i2c, uint32_t timeout) i2c_wait(i2c, 0, I2C_SR1_TXE, timeout); } -void +int i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write) { I2C_TypeDef *i2c = config.i2c; @@ -159,9 +160,11 @@ i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write) while (write_len--) i2c_send_byte(i2c, *write++, timeout); i2c_stop(i2c, timeout); + + return I2C_BUS_SUCCESS; } -void +int i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg , uint8_t read_len, uint8_t *read) { @@ -182,4 +185,6 @@ i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg read++; } i2c_wait(i2c, 0, I2C_SR1_RXNE, timeout); + + return I2C_BUS_SUCCESS; } diff --git a/src/stm32/stm32f0_i2c.c b/src/stm32/stm32f0_i2c.c index b83ceb0fc..ef12145f7 100644 --- a/src/stm32/stm32f0_i2c.c +++ b/src/stm32/stm32f0_i2c.c @@ -9,6 +9,7 @@ #include "gpio.h" // i2c_setup #include "internal.h" // GPIO #include "sched.h" // sched_shutdown +#include "i2ccmds.h" // I2C_BUS_SUCCESS struct i2c_info { I2C_TypeDef *i2c; @@ -174,48 +175,57 @@ i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr) return (struct i2c_config){ .i2c=i2c, .addr=addr<<1 }; } -static uint32_t +static int i2c_wait(I2C_TypeDef *i2c, uint32_t set, uint32_t timeout) { for (;;) { uint32_t isr = i2c->ISR; if (isr & set) - return isr; + return I2C_BUS_SUCCESS; if (isr & I2C_ISR_NACKF) - shutdown("I2C NACK error encountered"); + return I2C_BUS_NACK; if (!timer_is_before(timer_read_time(), timeout)) - shutdown("i2c timeout"); + return I2C_BUS_TIMEOUT; } } -void +int i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write) { I2C_TypeDef *i2c = config.i2c; uint32_t timeout = timer_read_time() + timer_from_us(5000); + int ret = I2C_BUS_SUCCESS; // Send start and address i2c->CR2 = (I2C_CR2_START | config.addr | (write_len << I2C_CR2_NBYTES_Pos) | I2C_CR2_AUTOEND); while (write_len--) { - i2c_wait(i2c, I2C_ISR_TXIS, timeout); + ret = i2c_wait(i2c, I2C_ISR_TXIS, timeout); + if (ret != I2C_BUS_SUCCESS) + goto abrt; i2c->TXDR = *write++; } - i2c_wait(i2c, I2C_ISR_TXE, timeout); + return i2c_wait(i2c, I2C_ISR_TXE, timeout); +abrt: + i2c->CR2 |= I2C_CR2_STOP; + return ret; } -void +int i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg , uint8_t read_len, uint8_t *read) { I2C_TypeDef *i2c = config.i2c; uint32_t timeout = timer_read_time() + timer_from_us(5000); + int ret = I2C_BUS_SUCCESS; // Send start, address, reg i2c->CR2 = (I2C_CR2_START | config.addr | (reg_len << I2C_CR2_NBYTES_Pos)); while (reg_len--) { - i2c_wait(i2c, I2C_ISR_TXIS, timeout); + ret = i2c_wait(i2c, I2C_ISR_TXIS, timeout); + if (ret != I2C_BUS_SUCCESS) + goto abrt; i2c->TXDR = *reg++; } i2c_wait(i2c, I2C_ISR_TC, timeout); @@ -224,8 +234,13 @@ i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg i2c->CR2 = (I2C_CR2_START | I2C_CR2_RD_WRN | config.addr | (read_len << I2C_CR2_NBYTES_Pos) | I2C_CR2_AUTOEND); while (read_len--) { - i2c_wait(i2c, I2C_ISR_RXNE, timeout); + ret = i2c_wait(i2c, I2C_ISR_RXNE, timeout); + if (ret != I2C_BUS_SUCCESS) + goto abrt; *read++ = i2c->RXDR; } - i2c_wait(i2c, I2C_ISR_STOPF, timeout); + return i2c_wait(i2c, I2C_ISR_STOPF, timeout); +abrt: + i2c->CR2 |= I2C_CR2_STOP; + return ret; }