From b40c72828df0aab74224df6fd18205ef9623a084 Mon Sep 17 00:00:00 2001 From: Karl Palsson Date: Fri, 24 Mar 2017 00:10:44 +0000 Subject: [PATCH] stm32: i2c: provide "transfer" level helper routines For both v1 and v2, provide routines to help do arbitrary length write/read transfers. Tested with multiple byte writes and reads, for both 100khz and 400khz, with repeated starts and stop/starts. However, only tested (presently) with a single i2c target device, a Sensiron SHT21 sensor. Extended testing against eeproms and alternative devices would be useful --- .../libopencm3/stm32/common/i2c_common_v1.h | 4 + .../libopencm3/stm32/common/i2c_common_v2.h | 4 + lib/stm32/common/i2c_common_v1.c | 77 +++++++++++++++++++ lib/stm32/common/i2c_common_v2.c | 61 +++++++++++++++ 4 files changed, 146 insertions(+) diff --git a/include/libopencm3/stm32/common/i2c_common_v1.h b/include/libopencm3/stm32/common/i2c_common_v1.h index bded08b5..b13bffc5 100644 --- a/include/libopencm3/stm32/common/i2c_common_v1.h +++ b/include/libopencm3/stm32/common/i2c_common_v1.h @@ -34,6 +34,9 @@ specific memorymap.h header before including this header file.*/ #ifndef LIBOPENCM3_I2C_COMMON_V1_H #define LIBOPENCM3_I2C_COMMON_V1_H +#include +#include + /* --- Convenience macros -------------------------------------------------- */ /* I2C register base addresses (for convenience) */ @@ -391,6 +394,7 @@ void i2c_enable_dma(uint32_t i2c); void i2c_disable_dma(uint32_t i2c); void i2c_set_dma_last_transfer(uint32_t i2c); void i2c_clear_dma_last_transfer(uint32_t i2c); +void i2c_transfer7(uint32_t i2c, uint8_t addr, uint8_t *w, size_t wn, uint8_t *r, size_t rn); END_DECLS diff --git a/include/libopencm3/stm32/common/i2c_common_v2.h b/include/libopencm3/stm32/common/i2c_common_v2.h index 01ec643b..3012fd7f 100644 --- a/include/libopencm3/stm32/common/i2c_common_v2.h +++ b/include/libopencm3/stm32/common/i2c_common_v2.h @@ -31,6 +31,9 @@ specific memorymap.h header before including this header file.*/ #ifndef LIBOPENCM3_I2C_COMMON_V2_H #define LIBOPENCM3_I2C_COMMON_V2_H +#include +#include + /* --- Convenience macros -------------------------------------------------- */ /* I2C register base addresses (for convenience) */ @@ -427,6 +430,7 @@ void i2c_enable_rxdma(uint32_t i2c); void i2c_disable_rxdma(uint32_t i2c); void i2c_enable_txdma(uint32_t i2c); void i2c_disable_txdma(uint32_t i2c); +void i2c_transfer7(uint32_t i2c, uint8_t addr, uint8_t *w, size_t wn, uint8_t *r, size_t rn); END_DECLS diff --git a/lib/stm32/common/i2c_common_v1.c b/lib/stm32/common/i2c_common_v1.c index d8e53d1b..dff41272 100644 --- a/lib/stm32/common/i2c_common_v1.c +++ b/lib/stm32/common/i2c_common_v1.c @@ -463,4 +463,81 @@ void i2c_clear_dma_last_transfer(uint32_t i2c) I2C_CR2(i2c) &= ~I2C_CR2_LAST; } +static void i2c_write7_v1(uint32_t i2c, int addr, uint8_t *data, size_t n) +{ + while ((I2C_SR2(i2c) & I2C_SR2_BUSY)) { + } + + i2c_send_start(i2c); + + /* Wait for master mode selected */ + while (!((I2C_SR1(i2c) & I2C_SR1_SB) + & (I2C_SR2(i2c) & (I2C_SR2_MSL | I2C_SR2_BUSY)))); + + i2c_send_7bit_address(i2c, addr, I2C_WRITE); + + /* Waiting for address is transferred. */ + while (!(I2C_SR1(i2c) & I2C_SR1_ADDR)); + + /* Clearing ADDR condition sequence. */ + (void)I2C_SR2(i2c); + + for (size_t i = 0; i < n; i++) { + i2c_send_data(i2c, data[i]); + while (!(I2C_SR1(i2c) & (I2C_SR1_BTF))); + } +} + +static void i2c_read7_v1(uint32_t i2c, int addr, uint8_t *res, size_t n) +{ + i2c_send_start(i2c); + i2c_enable_ack(i2c); + + /* Wait for master mode selected */ + while (!((I2C_SR1(i2c) & I2C_SR1_SB) + & (I2C_SR2(i2c) & (I2C_SR2_MSL | I2C_SR2_BUSY)))); + + i2c_send_7bit_address(i2c, addr, I2C_READ); + + /* Waiting for address is transferred. */ + while (!(I2C_SR1(i2c) & I2C_SR1_ADDR)); + /* Clearing ADDR condition sequence. */ + (void)I2C_SR2(i2c); + + for (size_t i = 0; i < n; ++i) { + if (i == n - 1) { + i2c_disable_ack(i2c); + } + while (!(I2C_SR1(i2c) & I2C_SR1_RxNE)); + res[i] = i2c_get_data(i2c); + } + i2c_send_stop(i2c); + + return; +} + +/** + * Run a write/read transaction to a given 7bit i2c address + * If both write & read are provided, the read will use repeated start. + * Both write and read are optional + * There are likely still issues with repeated start/stop condtions! + * @param i2c peripheral of choice, eg I2C1 + * @param addr 7 bit i2c device address + * @param w buffer of data to write + * @param wn length of w + * @param r destination buffer to read into + * @param rn number of bytes to read (r should be at least this long) + */ +void i2c_transfer7(uint32_t i2c, uint8_t addr, uint8_t *w, size_t wn, uint8_t *r, size_t rn) { + if (wn) { + i2c_write7_v1(i2c, addr, w, wn); + } + if (rn) { + i2c_read7_v1(i2c, addr, r, rn); + } else { + i2c_send_stop(i2c); + } +} + + /**@}*/ diff --git a/lib/stm32/common/i2c_common_v2.c b/lib/stm32/common/i2c_common_v2.c index ca393a96..2dd5db8e 100644 --- a/lib/stm32/common/i2c_common_v2.c +++ b/lib/stm32/common/i2c_common_v2.c @@ -375,4 +375,65 @@ void i2c_disable_txdma(uint32_t i2c) I2C_CR1(i2c) &= ~I2C_CR1_TXDMAEN; } +/** + * Run a write/read transaction to a given 7bit i2c address + * If both write & read are provided, the read will use repeated start. + * Both write and read are optional + * @param i2c peripheral of choice, eg I2C1 + * @param addr 7 bit i2c device address + * @param w buffer of data to write + * @param wn length of w + * @param r destination buffer to read into + * @param rn number of bytes to read (r should be at least this long) + */ +void i2c_transfer7(uint32_t i2c, uint8_t addr, uint8_t *w, size_t wn, uint8_t *r, size_t rn) +{ + /* waiting for busy is unnecessary. read the RM */ + if (wn) { + i2c_set_7bit_address(i2c, addr); + i2c_set_write_transfer_dir(i2c); + i2c_set_bytes_to_transfer(i2c, wn); + if (rn) { + i2c_disable_autoend(i2c); + } else { + i2c_enable_autoend(i2c); + } + i2c_send_start(i2c); + + while (wn--) { + bool wait = true; + while (wait) { + if (i2c_transmit_int_status(i2c)) { + wait = false; + } + while (i2c_nack(i2c)); /* FIXME Some error */ + } + i2c_send_data(i2c, *w++); + } + /* not entirely sure this is really necessary. + * RM implies it will stall until it can write out the later bits + */ + if (rn) { + while (!i2c_transfer_complete(i2c)); + } + } + + if (rn) { + /* Setting transfer properties */ + i2c_set_7bit_address(i2c, addr); + i2c_set_read_transfer_dir(i2c); + i2c_set_bytes_to_transfer(i2c, rn); + /* start transfer */ + i2c_send_start(i2c); + /* important to do it afterwards to do a proper repeated start! */ + i2c_enable_autoend(i2c); + + for (size_t i = 0; i < rn; i++) { + while (i2c_received_data(i2c) == 0); + r[i] = i2c_get_data(i2c); + } + } +} + + /**@}*/