由 WT_0213 贡献,Ai-Thinker 进行整理

目前aithinker_Ai-M6X_SDK 使用I2C 不是很友好。所以参考其他封装代码修改成适配SDK的封装代码。
Wire.h
📜 点击展开 Wire.h / Wire.c 完整代码
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;
}代码可以直接放到项目里面引用。
目前arduino 平台已经支持 I2C 的常用功能能。这里多了一种选择。
遇到问题?
如有其他问题,请到统一的提问与讨论区:Ai-Thinker Discussions

