概念先知道
- I2C:两根线(SDA 数据、SCL 时钟)挂多个设备的串行总线,靠 7 位地址区分谁在通信。
- 主从模式:主机(开发板)发起通信并产生时钟,从机(EEPROM 等)响应;例程开发板是主机,EEPROM 地址 0x50。
- 组合传输:一次通信可以拼多段消息(如“先发子地址,再读写数据”),例程用两个
bflb_i2c_msg_s组合完成。
例程功能简介
本页对应博流官方 SDK 的 i2c_eeprom 例程(examples/peripherals/i2c/i2c_eeprom),演示 I2C 主机与 EEPROM 的读写:
- I2C 波特率 400kHz,地址 0x50(AT24Cxx 模块,A0/A1/A2 接地);
- 通过
bflb_i2c_transfer组合消息:先发 2 字节子地址(页 0),再写 32 字节数据; - 延时后以相同子地址读回 32 字节,逐字节与写入数据比对并打印结果。
- 同族例程(
examples/peripherals/i2c/):i2c_10_bit(10 位地址)、i2c_eeprom_dma(DMA 读写 EEPROM)、i2c_eeprom_interrupt(中断读写)、i2c_ds3231(RTC 芯片)、i2c_lsm6ds3(IMU 芯片)、i2c_pca8553(IO 扩展)。
操作步骤
准备一块 AT24Cxx 系列 EEPROM 模块(例程默认地址 0x50)。接线:模块 SDA 接开发板 I2C0 的 SDA 引脚、SCL 接 I2C0 的 SCL 引脚(引脚见 board_i2c0_gpio_init()),VCC 接 3.3V、GND 接 GND;A0/A1/A2 接地保持地址 0x50。
在终端进入 SDK 的 I2C 例程目录(前提:已按快速开始(Linux)或Windows搭建好环境):
cd examples/peripherals/i2c/i2c_eeprom执行编译命令。Ai-M62(BL616)与 Ai-M61(BL618)同属一个系列,统一填写引脚最少的 bl616 即可:
make CHIP=bl616 BOARD=bl616dk用 USB 线连接开发板,按住 BOOT 键(Ai-M61-32S-Kit 为 IO2)不放、短按 EN/RST 进入下载模式,然后执行烧录(把串口号换成实际值):
make flash CHIP=bl616 COMX=/dev/ttyUSB0打开串口助手(波特率 2000000),应依次看到 write over(写入 32 字节)→ read over(读回)→ check over(逐字节校验一致)→ end。如果校验失败,串口会打印具体出错位置。
代码执行流程
例程从启动到运行的完整流程如下(图中的循环箭头表示反复执行):
例程调用的 API 介绍
bflb_i2c_init(i2c, 400000)
初始化 I2C 主机,第二个参数为波特率(400kHz)。
参数:
i2c:I2C 设备句柄(bflb_device_get_by_name("i2c0"))baudrate:时钟频率,例程400000
返回值:成功返回 0;失败返回负值错误码
bflb_i2c_transfer(i2c, msgs, count)
按消息数组执行一组 I2C 传输(可组合“先写子地址再读写数据”的完整时序)。
参数:
i2c:I2C 设备句柄msgs:struct bflb_i2c_msg_s数组,每项包含:addr:从机地址,如0x50flags:标志,I2C_M_NOSTOP(不产生停止条件,用于拼接下一段)、I2C_M_READ(读方向)buffer:数据缓冲区length:数据长度
count:消息条数,例程2
返回值:成功返回 0;失败返回负值错误码
完整代码
以下为 i2c_eeprom/main.c 完整源码,与官方示例(examples/peripherals/i2c/i2c_eeprom)一致;点灯引脚已适配 Ai-M61/62-32S-Kit 板载 RGB 灯,默认折叠,点击展开:
📜 点击展开 i2c_eeprom/main.c 完整代码
#include "bflb_mtimer.h"
#include "bflb_i2c.h"
#include "board.h"
#define EEPROM_TRANSFER_LENGTH 32
#define EEPROM_SELECT_PAGE0 (0 << 5)
static struct bflb_device_s *i2c0;
int main(void)
{
struct bflb_i2c_msg_s msgs[2];
uint8_t subaddr[2] = { 0x00, EEPROM_SELECT_PAGE0};
uint8_t write_data[256];
uint8_t read_data[256];
board_init();
board_i2c0_gpio_init();
i2c0 = bflb_device_get_by_name("i2c0");
bflb_i2c_init(i2c0, 400000);
/* Write and read buffer init */
for (size_t i = 0; i < 256; i++) {
write_data[i] = i;
read_data[i] = 0;
}
/* Write page 0 */
msgs[0].addr = 0x50;
msgs[0].flags = I2C_M_NOSTOP;
msgs[0].buffer = subaddr;
msgs[0].length = 2;
msgs[1].addr = 0x50;
msgs[1].flags = 0;
msgs[1].buffer = write_data;
msgs[1].length = EEPROM_TRANSFER_LENGTH;
bflb_i2c_transfer(i2c0, msgs, 2);
printf("write over\r\n");
bflb_mtimer_delay_ms(100);
/* Read page 0 */
msgs[1].addr = 0x50;
msgs[1].flags = I2C_M_READ;
msgs[1].buffer = read_data;
msgs[1].length = EEPROM_TRANSFER_LENGTH;
bflb_i2c_transfer(i2c0, msgs, 2);
printf("read over\r\n");
bflb_mtimer_delay_ms(100);
/* Check read data */
for (uint8_t i = 0; i < EEPROM_TRANSFER_LENGTH; i++) {
if (write_data[i] != read_data[i]) {
printf("check fail, %d write: %02x, read: %02x\r\n", i, write_data[i], read_data[i]);
}
}
printf("check over\r\n");
printf("end\r\n");
while(1){
}
}FAQ
串口打印 write over 后没有 read over
大概率是 EEPROM 地址不对或接线问题:确认模块 A0/A1/A2 接地(地址 0x50),SDA/SCL 接对且共地、供电 3.3V。
check fail,读回数据与写入不一致
EEPROM 写操作后需要写入周期(例程已延时 100ms);如果换用其他型号 EEPROM,注意页大小与写周期时间;确认 SCL/SDA 上拉电阻正常(模块自带则无需处理)。
例程没有输出
确认波特率 2000000;I2C0 引脚是否被其他外设占用;如果模块带地址跳线,确认实际地址与 msgs[0].addr 一致。
相关最佳实践
遇到问题?
如有其他问题,请到统一的提问与讨论区:Ai-Thinker Discussions

