From 1cadae03df0dfdfcf366fdf54fdda144143b2f60 Mon Sep 17 00:00:00 2001 From: sys64738 Date: Tue, 15 Jun 2021 03:25:29 +0200 Subject: [PATCH] i2c actually working a bit more, but device scans/address probes are not --- bsp/rp2040/i2c_tinyusb.c | 218 ++++++++++++++++++++++++++++++++++++++- src/main.c | 15 +++ src/util.h | 4 +- src/vnd_i2ctinyusb.c | 8 +- 4 files changed, 240 insertions(+), 5 deletions(-) diff --git a/bsp/rp2040/i2c_tinyusb.c b/bsp/rp2040/i2c_tinyusb.c index 9e5da57..e5bc7e8 100644 --- a/bsp/rp2040/i2c_tinyusb.c +++ b/bsp/rp2040/i2c_tinyusb.c @@ -2,11 +2,225 @@ #include #include #include +#include +#include +#include #include "protocfg.h" #include "pinout.h" #include "i2ctinyusb.h" +// replicating/rewriting some SDK functions because they don't do what I want +// except better this time + +static int i2cex_probe_address(uint16_t addr, bool a10bit) { + // TODO: bitbang the whole thing because the synopsys stuff sucks + return PICO_ERROR_GENERIC; +} + +static void i2cex_abort_xfer(void) { + // now do the abort + i2c->hw->enable = 1 /*| (1<<2)*/ | (1<<1); + // wait for M_TX_ABRT irq + do { + /*if (timeout_check) { + timeout = timeout_check(ts); + abort |= timeout; + }*/ + tight_loop_contents(); + } while (/*!timeout &&*/ !(i2c->hw->raw_intr_stat & I2C_IC_RAW_INTR_STAT_TX_ABRT_BITS)); + // reset irq + //if (!timeout) + (void)i2c->hw->clr_tx_abrt; +} + +static int i2cex_write_blocking_until(i2c_inst_t* i2c, uint16_t addr, bool a10bit, + const uint8_t* src, size_t len, bool nostop, absolute_time_t until) { + timeout_state_t ts_; + struct timeout_state* ts = &ts_; + check_timeout_fn timeout_check = init_single_timeout_until(&ts_, until); + + if ((int)len < 0) return PICO_ERROR_GENERIC; + if (a10bit) { // addr too high + if (addr & ~(uint16_t)((1<<10)-1)) return PICO_ERROR_GENERIC; + } else if (addr & 0x80) + return PICO_ERROR_GENERIC; + + if (len == 0) return i2cex_probe_address(addr, a10bit); + + bool abort = false, timeout = false; + uint32_t abort_reason = 0; + int byte_ctr; + + i2c->hw->enable = 0; + // enable 10bit mode if requested + hw_write_masked(&i2c->hw->con, I2C_IC_CON_IC_10BITADDR_MASTER_BITS, (a10bit + ? I2C_IC_CON_IC_10BITADDR_MASTER_VALUE_ADDR_10BITS + : I2C_IC_CON_IC_10BITADDR_MASTER_VALUE_ADDR_7BITS ) << I2C_IC_CON_IC_10BITADDR_MASTER_LSB); + i2c->hw->tar = addr; + i2c->hw->enable = 1; + + for (byte_ctr = 0; byte_ctr < (int)len; ++byte_ctr) { + bool first = byte_ctr == 0, + last = byte_ctr == (int)len - 1; + + i2c->hw->data_cmd = + bool_to_bit(first && i2c->restart_on_next) << I2C_IC_DATA_CMD_RESTART_LSB | + bool_to_bit(last && !nostop) << I2C_IC_DATA_CMD_STOP_LSB | + *src++; + + do { + if (timeout_check) { + timeout = timeout_check(ts); + abort |= timeout; + } + tight_loop_contents(); + } while (!timeout && !(i2c->hw->raw_intr_stat & I2C_IC_RAW_INTR_STAT_TX_EMPTY_BITS)); + + if (!timeout) { + abort_reason = i2c->hw->tx_abrt_source; + if (abort_reason) { + (void)i2c->hw->clr_tx_abrt; + abort = true; + } + + if (abort || (last && !nostop)) { + do { + if (timeout_check) { + timeout = timeout_check(ts); + abort |= timeout; + } + tight_loop_contents(); + } while (!timeout && !(i2c->hw->raw_intr_stat & I2C_IC_RAW_INTR_STAT_STOP_DET_BITS)); + + if (!timeout) (void)i2c->hw->clr_stop_det; + } else { + // if we had a timeout, send an abort request to the hardware, + // so that the bus gets released + i2cex_abort_xfer(); + } + } + + if (abort) break; + } + + int rval; + + if (abort) { + const int addr_noack = I2C_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK_BITS + | I2C_IC_TX_ABRT_SOURCE_ABRT_10ADDR1_NOACK_BITS + | I2C_IC_TX_ABRT_SOURCE_ABRT_10ADDR2_NOACK_BITS; + + if (timeout) rval = PICO_ERROR_TIMEOUT; + else if (!abort_reason || (abort_reason & addr_noack)) + rval = PICO_ERROR_GENERIC; + else if (abort_reason & I2C_IC_TX_ABRT_SOURCE_ABRT_TXDATA_NOACK_BITS) + rval = byte_ctr; + else rval = PICO_ERROR_GENERIC; + } else rval = byte_ctr; + + i2c->restart_on_next = nostop; + return rval; +} +static int i2cex_read_blocking_until(i2c_inst_t* i2c, uint16_t addr, bool a10bit, + uint8_t* dst, size_t len, bool nostop, absolute_time_t until) { + timeout_state_t ts_; + struct timeout_state* ts = &ts_; + check_timeout_fn timeout_check = init_single_timeout_until(&ts_, until); + + if ((int)len < 0) return PICO_ERROR_GENERIC; + if (a10bit) { // addr too high + if (addr & ~(uint16_t)((1<<10)-1)) return PICO_ERROR_GENERIC; + } else if (addr & 0x80) + return PICO_ERROR_GENERIC; + + i2c->hw->enable = 0; + // enable 10bit mode if requested + hw_write_masked(&i2c->hw->con, I2C_IC_CON_IC_10BITADDR_MASTER_BITS, (a10bit + ? I2C_IC_CON_IC_10BITADDR_MASTER_VALUE_ADDR_10BITS + : I2C_IC_CON_IC_10BITADDR_MASTER_VALUE_ADDR_7BITS ) << I2C_IC_CON_IC_10BITADDR_MASTER_LSB); + i2c->hw->tar = addr; + i2c->hw->enable = 1; + + if (len == 0) return i2cex_probe_address(addr, a10bit); + + bool abort = false, timeout = false; + uint32_t abort_reason = 0; + int byte_ctr; + + for (byte_ctr = 0; byte_ctr < (int)len; ++byte_ctr) { + bool first = byte_ctr == 0; + bool last = byte_ctr == (int)len - 1; + + while (!i2c_get_write_available(i2c) && !abort) { + tight_loop_contents(); + // ? + if (timeout_check) { + timeout = timeout_check(ts); + abort |= timeout; + } + } + + if (timeout) { + // if we had a timeout, send an abort request to the hardware, + // so that the bus gets released + i2cex_abort_xfer(); + } + if (abort) break; + + i2c->hw->data_cmd = + bool_to_bit(first && i2c->restart_on_next) << I2C_IC_DATA_CMD_RESTART_LSB | + bool_to_bit(last && !nostop) << I2C_IC_DATA_CMD_STOP_LSB | + I2C_IC_DATA_CMD_CMD_BITS; // -> 1 for read + + do { + abort_reason = i2c->hw->tx_abrt_source; + abort = (bool)i2c->hw->clr_tx_abrt; + + if (timeout_check) { + timeout = timeout_check(ts); + abort |= timeout; + } + tight_loop_contents(); // ? + } while (!abort && !i2c_get_read_available(i2c)); + + if (timeout) { + // if we had a timeout, send an abort request to the hardware, + // so that the bus gets released + i2cex_abort_xfer(); + } + if (abort) break; + + *dst++ = (uint8_t)i2c->hw->data_cmd; + } + + int rval; + + if (abort) { + const int addr_noack = I2C_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK_BITS + | I2C_IC_TX_ABRT_SOURCE_ABRT_10ADDR1_NOACK_BITS + | I2C_IC_TX_ABRT_SOURCE_ABRT_10ADDR2_NOACK_BITS; + + if (timeout) rval = PICO_ERROR_TIMEOUT; + else if (!abort_reason || (abort_reason & addr_noack)) + rval = PICO_ERROR_GENERIC; + else rval = PICO_ERROR_GENERIC; + } else rval = byte_ctr; + + i2c->restart_on_next = nostop; + return rval; +} +static inline int i2cex_write_timeout_us(i2c_inst_t* i2c, uint16_t addr, bool a10bit, + const uint8_t* src, size_t len, bool nostop, uint32_t timeout_us) { + absolute_time_t t = make_timeout_time_us(timeout_us); + return i2cex_write_blocking_until(i2c, addr, a10bit, src, len, nostop, t); +} +static inline int i2cex_read_timeout_us(i2c_inst_t* i2c, uint16_t addr, bool a10bit, + uint8_t* dst, size_t len, bool nostop, uint32_t timeout_us) { + absolute_time_t t = make_timeout_time_us(timeout_us); + return i2cex_read_blocking_until(i2c, addr, a10bit, dst, len, nostop, t); +} + __attribute__((__const__)) enum ki2c_funcs i2ctu_get_func(void) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; @@ -30,14 +244,14 @@ uint32_t i2ctu_set_freq(uint32_t freq) { enum itu_status i2ctu_write(enum ki2c_flags flags, enum itu_command startstopflags, uint16_t addr, const uint8_t* buf, size_t len) { - int rv = i2c_write_timeout_us(PINOUT_I2C_DEV, addr, buf, len, + int rv = i2cex_write_timeout_us(PINOUT_I2C_DEV, addr, false, buf, len, !(startstopflags & ITU_CMD_I2C_IO_END), 1000*1000); if (rv < 0) return ITU_STATUS_ADDR_NAK; return ITU_STATUS_ADDR_ACK; } enum itu_status i2ctu_read(enum ki2c_flags flags, enum itu_command startstopflags, uint16_t addr, uint8_t* buf, size_t len) { - int rv = i2c_read_timeout_us(PINOUT_I2C_DEV, addr, buf, len, + int rv = i2cex_read_timeout_us(PINOUT_I2C_DEV, addr, false, buf, len, !(startstopflags & ITU_CMD_I2C_IO_END), 1000*1000); if (rv < 0) return ITU_STATUS_ADDR_NAK; return ITU_STATUS_ADDR_ACK; diff --git a/src/main.c b/src/main.c index 66e3c4f..111ca6c 100644 --- a/src/main.c +++ b/src/main.c @@ -42,6 +42,8 @@ #ifdef PICO_BOARD #include +/*#include +#include "pinout.h"*/ #endif static cothread_t mainthread; @@ -93,6 +95,14 @@ int main(void) { #if defined(PICO_BOARD) && !defined(USE_USBCDC_FOR_STDIO) // use hardcoded values from TinyUSB board.h bi_decl(bi_2pins_with_func(0, 1, GPIO_FUNC_UART)); + /*i2c_init(PINOUT_I2C_DEV, 100*1000); + + gpio_set_function(PINOUT_I2C_SCL, GPIO_FUNC_I2C); + gpio_set_function(PINOUT_I2C_SDA, GPIO_FUNC_I2C); + gpio_pull_up(PINOUT_I2C_SCL); + gpio_pull_up(PINOUT_I2C_SDA); + + bi_decl(bi_2pins_with_func(PINOUT_I2C_SCL, PINOUT_I2C_SDA, GPIO_FUNC_I2C));*/ #endif board_init(); @@ -115,11 +125,16 @@ int main(void) { #endif while (1) { + /*uint8_t val = 0x12; + i2c_write_timeout_us(PINOUT_I2C_DEV, 0x13, &val, 1, false, 1000*1000);*/ + tud_task(); // tinyusb device task #ifdef DBOARD_HAS_UART co_switch(uartthread); #endif + //i2c_write_timeout_us(PINOUT_I2C_DEV, 0x13, &val, 1, false, 1000*1000); + tud_task(); // tinyusb device task #ifdef DBOARD_HAS_SERPROG co_switch(serprogthread); diff --git a/src/util.h b/src/util.h index c52468c..31e29e6 100644 --- a/src/util.h +++ b/src/util.h @@ -3,8 +3,8 @@ #define UTIL_H_ static inline char nyb2hex(int x) { - if (x < 0xa) return '0'+x; - else return 'A'+x; + if (x < 0xa) return '0'+(x-0); + else return 'A'+(x-0xa); } void thread_yield(void); diff --git a/src/vnd_i2ctinyusb.c b/src/vnd_i2ctinyusb.c index 9f25e2e..15d062d 100644 --- a/src/vnd_i2ctinyusb.c +++ b/src/vnd_i2ctinyusb.c @@ -12,6 +12,9 @@ #include "i2ctinyusb.h" +#include "pinout.h" +#include + static uint8_t itf_num; static enum itu_status state; @@ -71,7 +74,7 @@ static bool iub_ctl_req(uint8_t rhport, uint8_t stage, tusb_control_request_t co } break; case ITU_CMD_GET_FUNC: { // flags unused, addr unused, len=4 - if (req->wLength != 0) return false; + if (req->wLength != 4) return false; const uint32_t func = i2ctu_get_func(); uint8_t rv[4]; @@ -125,6 +128,9 @@ static bool iub_ctl_req(uint8_t rhport, uint8_t stage, tusb_control_request_t co } else { // write bool rv = tud_control_xfer(rhport, req, buf, cmd.len); if (rv) { + uint8_t val = cmd.cmd; + i2c_write_timeout_us(PINOUT_I2C_DEV, cmd.len, &val, 1, false, 1000*1000); + state = i2ctu_write(cmd.flags, cmd.cmd & ITU_CMD_I2C_IO_DIR_MASK, cmd.addr, buf, sizeof buf); }