Skip to content

概念先知道

  • 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 扩展)。

操作步骤

1
硬件准备

准备一块 AT24Cxx 系列 EEPROM 模块(例程默认地址 0x50)。接线:模块 SDA 接开发板 I2C0 的 SDA 引脚、SCL 接 I2C0 的 SCL 引脚(引脚见 board_i2c0_gpio_init()),VCC 接 3.3V、GND 接 GND;A0/A1/A2 接地保持地址 0x50。

2
进入例程目录

在终端进入 SDK 的 I2C 例程目录(前提:已按快速开始(Linux)Windows搭建好环境):

cd examples/peripherals/i2c/i2c_eeprom
3
编译工程

执行编译命令。Ai-M62(BL616)与 Ai-M61(BL618)同属一个系列,统一填写引脚最少的 bl616 即可:

make CHIP=bl616 BOARD=bl616dk
4
烧录固件

用 USB 线连接开发板,按住 BOOT 键(Ai-M61-32S-Kit 为 IO2)不放、短按 EN/RST 进入下载模式,然后执行烧录(把串口号换成实际值):

make flash CHIP=bl616 COMX=/dev/ttyUSB0
5
运行验证

打开串口助手(波特率 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 设备句柄
  • msgsstruct bflb_i2c_msg_s 数组,每项包含:
    • addr:从机地址,如 0x50
    • flags:标志,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 完整代码
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

Released under the MIT License. Build Time 2026-09-11 14:52:23