Contributed by WT_0213, curated by Ai-Thinker

Using I2C with aithinker_Ai-M6X_SDK is currently not very friendly, so I referred to other wrapper code and modified it into a wrapper that fits this SDK.
Wire.h
📜 Click to expand the full Wire.h / Wire.c code
cpp
#pragma once
// #include "bouffalo_sdk.h"
#include "bflb_gpio.h"
#include "bl616_gpio.h"
#include "bl616_glb.h"
#include "bl616_glb_gpio.h"
#include "../../drivers/lhal/include/hardware/i2c_reg.h"
#include "bflb_i2c.h"
#define lowByte(w) ((uint8_t) ((w) & 0xff))
#define highByte(w) ((uint8_t) ((w) >> 8))
bool getWireTimeoutFlag();
bool clearWireTimeoutFlag();
void setWireTimeout(int timeout, bool reset_on_timeout);
void onRequest(void (*callback)());
void onReceive(void (*callback)(int));
void setClock(int clockFrequency);
int readI2c();
int available();
int write_len(uint8_t *str, int len);
int write_str(uint8_t *str);
int write_char(unsigned char value);
void endTransmission_stop(bool stop);
void endTransmission();
void beginTransmission(unsigned char addr);
int requestFrom_stop(unsigned char addr, int quantity, bool stop);
int requestFrom(unsigned char addr, int quantity);
void end();
void begin_addr(unsigned char addr);
void begin();
Wire.c
#include "Wire.h"
#define PUT_UINT32_LE(field, value) \
do { \
(field)[0] = (uint8_t)((value) >> 0); \
(field)[1] = (uint8_t)((value) >> 8); \
(field)[2] = (uint8_t)((value) >> 16); \
(field)[3] = (uint8_t)((value) >> 24); \
} while (0)
struct bflb_device_s *i2c0;
uint8_t rbuf[128];
int available_count;
int indexi2c;
int wire_timeout;
bool wire_timeout_flag;
void board_i2c_pinmux_init(void)
{
GLB_GPIO_Type pinlist[] = {
GLB_GPIO_PIN_30,
GLB_GPIO_PIN_31
};
GLB_GPIO_Func_Init(GPIO_FUN_I2C0, pinlist, 2);
}
bool bflb_i2c_isend(struct bflb_device_s *dev)
{
uint32_t regval;
uint32_t reg_base;
reg_base = dev->reg_base;
regval = getreg32(reg_base + I2C_INT_STS_OFFSET);
if (regval & I2C_END_INT) {
return true;
}
return false;
}
bool bflb_i2c_isnak(struct bflb_device_s *dev)
{
uint32_t regval;
uint32_t reg_base;
reg_base = dev->reg_base;
regval = getreg32(reg_base + I2C_INT_STS_OFFSET);
if (regval & I2C_NAK_INT) {
return true;
}
return false;
}
bool bflb_i2c_isbusy(struct bflb_device_s *dev)
{
uint32_t regval;
uint32_t reg_base;
reg_base = dev->reg_base;
regval = getreg32(reg_base + I2C_BUS_BUSY_OFFSET);
if (regval & I2C_STS_I2C_BUS_BUSY) {
return true;
}
return false;
}
void bflb_i2c_enable(struct bflb_device_s *dev)
{
uint32_t regval;
uint32_t reg_base;
reg_base = dev->reg_base;
regval = getreg32(reg_base + I2C_CONFIG_OFFSET);
regval |= I2C_CR_I2C_M_EN;
putreg32(regval, reg_base + I2C_CONFIG_OFFSET);
}
bool bflb_i2c_isenable(struct bflb_device_s *dev)
{
uint32_t regval;
uint32_t reg_base;
reg_base = dev->reg_base;
regval = getreg32(reg_base + I2C_CONFIG_OFFSET);
if (regval & I2C_CR_I2C_M_EN) {
return true;
}
return false;
}
void bflb_i2c_disable(struct bflb_device_s *dev)
{
uint32_t regval;
uint32_t reg_base;
reg_base = dev->reg_base;
regval = getreg32(reg_base + I2C_CONFIG_OFFSET);
regval &= ~I2C_CR_I2C_M_EN;
putreg32(regval, reg_base + I2C_CONFIG_OFFSET);
/* Clear I2C fifo */
regval = getreg32(reg_base + I2C_FIFO_CONFIG_0_OFFSET);
regval |= I2C_TX_FIFO_CLR;
regval |= I2C_RX_FIFO_CLR;
putreg32(regval, reg_base + I2C_FIFO_CONFIG_0_OFFSET);
/* Clear I2C interrupt status */
regval = getreg32(reg_base + I2C_INT_STS_OFFSET);
regval |= I2C_CR_I2C_END_CLR;
regval |= I2C_CR_I2C_NAK_CLR;
regval |= I2C_CR_I2C_ARB_CLR;
putreg32(regval, reg_base + I2C_INT_STS_OFFSET);
}
void bflb_i2c_addr_config(struct bflb_device_s *dev, uint16_t slaveaddr, uint16_t subaddr, uint8_t subaddr_size, bool is_addr_10bit)
{
uint32_t regval;
uint32_t reg_base;
reg_base = dev->reg_base;
regval = getreg32(reg_base + I2C_CONFIG_OFFSET);
if (subaddr_size > 0) {
regval |= I2C_CR_I2C_SUB_ADDR_EN;
regval &= ~I2C_CR_I2C_SUB_ADDR_BC_MASK;
regval |= ((subaddr_size - 1) << I2C_CR_I2C_SUB_ADDR_BC_SHIFT);
} else {
regval &= ~I2C_CR_I2C_SUB_ADDR_EN;
}
regval &= ~I2C_CR_I2C_SLV_ADDR_MASK;
regval |= (slaveaddr << I2C_CR_I2C_SLV_ADDR_SHIFT);
#if !defined(BL602) && !defined(BL702)
if (is_addr_10bit) {
regval |= I2C_CR_I2C_10B_ADDR_EN;
} else {
regval &= ~I2C_CR_I2C_10B_ADDR_EN;
}
#endif
putreg32(subaddr, reg_base + I2C_SUB_ADDR_OFFSET);
putreg32(regval, reg_base + I2C_CONFIG_OFFSET);
}
void bflb_i2c_set_datalen(struct bflb_device_s *dev, uint16_t data_len)
{
uint32_t regval;
uint32_t reg_base;
reg_base = dev->reg_base;
regval = getreg32(reg_base + I2C_CONFIG_OFFSET);
regval &= ~I2C_CR_I2C_PKT_LEN_MASK;
regval |= ((data_len - 1) << I2C_CR_I2C_PKT_LEN_SHIFT) & I2C_CR_I2C_PKT_LEN_MASK;
putreg32(regval, reg_base + I2C_CONFIG_OFFSET);
}
void bflb_i2c_set_dir(struct bflb_device_s *dev, bool is_in)
{
uint32_t regval;
uint32_t reg_base;
reg_base = dev->reg_base;
regval = getreg32(reg_base + I2C_CONFIG_OFFSET);
if (is_in) {
regval |= I2C_CR_I2C_PKT_DIR;
} else {
regval &= ~I2C_CR_I2C_PKT_DIR;
}
putreg32(regval, reg_base + I2C_CONFIG_OFFSET);
}
int bflb_i2c_write_bytes(struct bflb_device_s *dev, uint8_t *data, uint32_t len, uint32_t timeout)
{
uint32_t reg_base;
uint32_t temp = 0;
uint8_t *tmp_buf;
uint64_t start_time;
reg_base = dev->reg_base;
tmp_buf = data;
while (len >= 4) {
for (uint8_t i = 0; i < 4; i++) {
temp += (tmp_buf << ((i % 4) * 8));
}
tmp_buf += 4;
len -= 4;
start_time = bflb_mtimer_get_time_ms();
while ((getreg32(reg_base + I2C_FIFO_CONFIG_1_OFFSET) & I2C_TX_FIFO_CNT_MASK) == 0) {
if ((bflb_mtimer_get_time_ms() - start_time) > timeout) {
return -ETIMEDOUT;
}
}
putreg32(temp, reg_base + I2C_FIFO_WDATA_OFFSET);
if (!bflb_i2c_isenable(dev)) {
bflb_i2c_enable(dev);
}
temp = 0;
}
if (len > 0) {
for (uint8_t i = 0; i < len; i++) {
temp += (tmp_buf << ((i % 4) * 8));
}
start_time = bflb_mtimer_get_time_ms();
while ((getreg32(reg_base + I2C_FIFO_CONFIG_1_OFFSET) & I2C_TX_FIFO_CNT_MASK) == 0) {
if ((bflb_mtimer_get_time_ms() - start_time) > timeout) {
return -ETIMEDOUT;
}
}
putreg32(temp, reg_base + I2C_FIFO_WDATA_OFFSET);
if (!bflb_i2c_isenable(dev)) {
bflb_i2c_enable(dev);
}
}
start_time = bflb_mtimer_get_time_ms();
while (bflb_i2c_isbusy(dev) || !bflb_i2c_isend(dev) || bflb_i2c_isnak(dev)) {
if ((bflb_mtimer_get_time_ms() - start_time) > timeout) {
return -ETIMEDOUT;
}
}
bflb_i2c_disable(dev);
return 0;
}
int bflb_i2c_read_bytes(struct bflb_device_s *dev, uint8_t *data, uint32_t len, uint32_t timeout)
{
uint32_t reg_base;
uint32_t temp = 0;
uint8_t *tmp_buf;
uint64_t start_time;
reg_base = dev->reg_base;
tmp_buf = data;
bflb_i2c_enable(dev);
while (len >= 4) {
start_time = bflb_mtimer_get_time_ms();
while ((getreg32(reg_base + I2C_FIFO_CONFIG_1_OFFSET) & I2C_RX_FIFO_CNT_MASK) == 0) {
if ((bflb_mtimer_get_time_ms() - start_time) > timeout) {
return -ETIMEDOUT;
}
}
temp = getreg32(reg_base + I2C_FIFO_RDATA_OFFSET);
PUT_UINT32_LE(tmp_buf, temp);
tmp_buf += 4;
len -= 4;
}
if (len > 0) {
start_time = bflb_mtimer_get_time_ms();
while ((getreg32(reg_base + I2C_FIFO_CONFIG_1_OFFSET) & I2C_RX_FIFO_CNT_MASK) == 0) {
if ((bflb_mtimer_get_time_ms() - start_time) > timeout) {
return -ETIMEDOUT;
}
}
temp = getreg32(reg_base + I2C_FIFO_RDATA_OFFSET);
for (uint8_t i = 0; i < len; i++) {
tmp_buf = (temp >> (i * 8)) & 0xff;
}
}
start_time = bflb_mtimer_get_time_ms();
while (bflb_i2c_isbusy(dev) || !bflb_i2c_isend(dev)) {
if ((bflb_mtimer_get_time_ms() - start_time) > timeout) {
return -ETIMEDOUT;
}
}
bflb_i2c_disable(dev);
return 0;
}
/*
* address: the 7-bit slave address (optional); if not specified, join the bus as a controller device.
*/
void begin_addr(unsigned char addr) {
wire_timeout = 100;
wire_timeout_flag = false;
board_i2c_pinmux_init();
i2c0 = bflb_device_get_by_name("i2c0");
bflb_i2c_init(i2c0, 50000);
}
void begin() {
wire_timeout = 100;
wire_timeout_flag = false;
board_i2c_pinmux_init();
i2c0 = bflb_device_get_by_name("i2c0");
bflb_i2c_init(i2c0, 50000);
}
void end() {
bflb_i2c_deinit(i2c0);
}
/*
* address: the 7-bit slave address of the device to request bytes from.
*
* quantity: the number of bytes to request.
*
* stop: true or false. true will send a stop message after the request, releasing the bus.
* False will continually send a restart after the request, keeping the connection active.
*/
int requestFrom_stop(unsigned char addr, int quantity, bool stop) {
indexi2c = 0;
bflb_i2c_disable(i2c0);
bflb_i2c_enable(i2c0);
bflb_i2c_addr_config(i2c0, addr, 0, 0, false);
bflb_i2c_set_datalen(i2c0, quantity);
bflb_i2c_set_dir(i2c0, 1);
bflb_i2c_read_bytes(i2c0, rbuf, quantity, wire_timeout);
available_count = quantity;
if (true == stop){
bflb_i2c_disable(i2c0);
}
return 0;
}
int requestFrom(unsigned char addr, int quantity) {
indexi2c = 0;
bflb_i2c_disable(i2c0);
bflb_i2c_enable(i2c0);
bflb_i2c_addr_config(i2c0, addr, 0, 0, false);
bflb_i2c_set_datalen(i2c0, quantity);
bflb_i2c_set_dir(i2c0, 1);
bflb_i2c_read_bytes(i2c0, rbuf, quantity, wire_timeout);
available_count = quantity;
return 0;
}
/*
* address: the 7-bit address of the device to transmit to.
*/
void beginTransmission(unsigned char addr) {
//bflb_i2c_enable(i2c0);
bflb_i2c_addr_config(i2c0, addr, 0, 0, false);
bflb_i2c_set_dir(i2c0, 0);
}
/*
* stop: true or false. True will send a stop message, releasing the bus after transmission.
* False will send a restart, keeping the connection active.
*
* Returns
* 0: success.
* 1: data too long to fit in transmit buffer.
* 2: received NACK on transmit of address.
* 3: received NACK on transmit of data.
* 4: other error.
* 5: timeout
*/
void endTransmission_stop(bool stop) {
bflb_i2c_disable(i2c0);
}
void endTransmission() {
bflb_i2c_disable(i2c0);
}
/*
* Description
* This function writes data from a peripheral device in response to a request from
* a controller device, or queues bytes for transmission from a controller to
* peripheral device (in-between calls to beginTransmission() and endTransmission()).
* Syntax
* Wire.write(value) Wire.write(string) Wire.write(data, length)
* Parameters
* value: a value to send as a single byte.
* string: a string to send as a series of bytes.
* data: an array of data to send as bytes.
* length: the number of bytes to transmit.
* Returns
* The number of bytes written (reading this number is optional).
*/
int write_char(unsigned char value) {
bflb_i2c_set_datalen(i2c0, 1);
bflb_i2c_write_bytes(i2c0, &value, 1, wire_timeout);
return 0;
}
int write_str(uint8_t *str) {
bflb_i2c_set_datalen(i2c0, strlen((const char*)str));
bflb_i2c_write_bytes(i2c0, str, strlen((const char*)str), wire_timeout);
return 0;
}
int write_len(uint8_t *str, int len) {
bflb_i2c_set_datalen(i2c0, len);
int ret = bflb_i2c_write_bytes(i2c0, str, len, wire_timeout);
return ret;
}
/*
* Description
* This function returns the number of bytes available for retrieval with read().
* This function should be called on a controller device after a call to
* requestFrom() or on a peripheral inside the onReceive() handler.
* available() inherits from the Stream utility class.
*/
int available() {
return available_count;
}
/*
* Description
* This function reads a byte that was transmitted from a peripheral device to
* a controller device after a call to requestFrom() or was transmitted from a
* controller device to a peripheral device. read() inherits from the Stream utility class.
* Syntax
* Wire.read()
* Parameters
* None.
* Returns
* The next byte received.
*/
int readI2c() {
unsigned char ret;
if (available_count){
available_count--;
ret = rbuf[indexi2c];
indexi2c++;
return ret;
}
return 0;
}
/*
* Description
* This function modifies the clock frequency for I2C communication.
* I2C peripheral devices have no minimum working clock frequency,
* however 100KHz is usually the baseline.
* Syntax
* Wire.setClock(clockFrequency)
* Parameters
* clockFrequency: the value (in Hertz) of the desired communication clock.
* Accepted values are 100000 (standard mode) and 400000 (fast mode).
* Some processors also support 10000 (low speed mode), 1000000 (fast mode plus)
* and 3400000 (high speed mode). Please refer to the specific processor documentation
* to make sure the desired mode is supported.
* Returns
* None.
*/
void setClock(int clockFrequency) {
bflb_i2c_deinit(i2c0);
bflb_i2c_init(i2c0, clockFrequency);
}
/*
* Description
* This function registers a function to be called when a peripheral device receives
* a transmission from a controller device.
* Syntax
* Wire.onReceive(handler)
* Parameters
* handler: the function to be called when the peripheral device receives data;
* this should take a single int parameter (the number of bytes read from the controller
* device) and return nothing.
* Returns
* None.
*/
void onReceive(void (*callback)(int)) {
//we not support slave mode yet
}
/*
* Description
* This function registers a function to be called when a controller device requests data from a peripheral device.
* Syntax
* Wire.onRequest(handler)
* Parameters
* handler: the function to be called, takes no parameters and returns nothing.
* Returns
* None.
*/
void onRequest(void (*callback)()) {
//we not support slave mode yet
}
/*
* Description
* Sets the timeout for Wire transmissions in master mode.
* Syntax
* Wire.setWireTimeout(timeout, reset_on_timeout)
* Wire.setWireTimeout()
* Parameters
* timeout a timeout: timeout in microseconds, if zero then timeout checking is disabled
* reset_on_timeout: if true then Wire hardware will be automatically reset on timeout
* When this function is called without parameters, a default timeout is configured that
* should be sufficient to prevent lockups in a typical single-master configuration.
* Returns
* None.
*/
void setWireTimeout(int timeout, bool reset_on_timeout) {
wire_timeout = timeout;
wire_timeout_flag = true;
}
/* Description
* Clears the timeout flag.
* Timeouts might not be enabled by default. See the documentation for Wire.setWireTimeout()
* for more information on how to configure timeouts and how they work.
* Syntax
* Wire.clearTimeout()
* Parameters
* None.
* Returns
* bool: The current value of the flag
*/
bool clearWireTimeoutFlag() {
wire_timeout_flag = false;
return true;
}
/*
* Description
* Checks whether a timeout has occured since the last time the flag was cleared.
* This flag is set is set whenever a timeout occurs and cleared when Wire.clearWireTimeoutFlag()
* is called, or when the timeout is changed using Wire.setWireTimeout().
* Syntax
* Wire.getWireTimeoutFlag()
* Parameters
* None.
* Returns
* bool: The current value of the flag
*/
bool getWireTimeoutFlag() {
return wire_timeout_flag;
}The code can be directly referenced in your project.
The Arduino platform already supports the common I2C functions. This provides another option.
Have questions?
For other questions, please visit the unified discussion area: Ai-Thinker Discussions

