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
This commit is contained in:
parent
fb3b5e08f3
commit
b40c72828d
@ -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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* --- 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
|
||||
|
||||
|
@ -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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* --- 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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**@}*/
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**@}*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user