I2C Tools 示例

[English]

示例说明

本示例展示了如何初始化和配置 I2C 接口,并通过 Console REPL 对 I2C 总线进行操作:在命令行环境下向模拟从机发送指令,例如读取指定寄存器的值,从而验证 I2C 总线通信和外设交互的正确性。

示例由两个文件组成:

  • i2ctools_example_main.c:包含示例入口,用于初始化 I2C 总线,启动 Console REPL等。

  • cmd_i2ctools:包含命令注册,用于实现具体的 I2C 命令操作,包括读取和写入模拟从机寄存器。

Console REPL

Console REPL (Read–Eval–Print Loop) 是一种面向嵌入式系统的实时交互调试与控制接口,可以实时执行输入的指令并返回结果。在 ESP-IDF 示例中,Console REPL 的专业特点包括:

  • 交互式执行:允许用户在终端中输入命令,系统即时解析并执行。

  • 实时反馈:命令执行结果即时输出,便于开发者观察外设状态和调试程序。

  • 可扩展命令注册:支持注册自定义命令接口,例如 I2C、SPI、GPIO 操作指令,实现对硬件外设的灵活控制。

  • 硬件验证与调试:提供直接访问外设寄存器和总线接口的手段,便于快速验证硬件连接和功能实现。

但需要注意,命令行操作不会自动识别设备类型或寄存器意义,需要用户自行确认寄存器地址和数据格式。更多说明可参考 控制台终端

运行方法

示例完整代码见 I2C Tools 示例。运行前的配置说明、对应 GPIO 引脚、相关指令及构建与烧录流程详见示例目录下的 README.md 文件。

如需自定义 FAT 文件系统挂载路径,可查看 宏定义/全局变量说明 部分并修改相关定义。

头文件说明

本示例所使用的头文件涵盖了系统工具、文件系统、命令行解析、I2C 驱动及日志记录等模块,为控制台工具开发、命令解析、I2C 通信及文件操作提供支持。

各头文件按功能分类如下:

  1. 系统工具 :提供标准输入输出、字符串处理、内存管理、配置宏支持、FAT 文件系统操作以及日志打印功能。

#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "esp_vfs_fat.h"
#include "esp_log.h"

备注

  • sdkconfig.h 用于读取构建系统生成的配置宏,仅在 i2ctools_example_main.c 文件中使用。

  • esp_vfs_fat.h 提供 FAT 文件系统挂载及文件操作接口,仅在 i2ctools_example_main.c 文件中使用。

  1. 命令行解析与控制台:提供命令解析、注册以及交互式控制台功能。

#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "cmd_system.h"
#include "cmd_i2ctools.h"

备注

  • argtable3/argtable3.h 用于提供命令行参数解析功能,仅在 cmd_i2ctools.c 文件中使用。

  • cmd_system.h 用于封装系统命令注册,仅在 i2ctools_example_main.c 文件中使用。

  • cmd_i2ctools.h 用于封装 I2C 工具命令注册,仅在 i2ctools_example_main.c 文件中使用。

  1. I2C 驱动 :提供 I2C 主机模式下的初始化、读写操作及资源释放功能。

#include "driver/i2c_master.h"

宏定义/全局变量说明

本示例中的宏定义和全局变量主要用于配置 I2C 主机的引脚、端口号、通信频率及超时时间,并提供总线句柄以便后续读写操作。

按功能分类如下:

  1. I2C 主机配置:定义于 i2ctools_example_main.c

  • 使用配置宏定义 I2C 时钟线 (SCL) 与数据线 (SDA) 所使用的 GPIO 引脚编号。示例中时钟线对应 GPIO 4;数据线对应 GPIO 5。

static gpio_num_t i2c_gpio_sda = CONFIG_EXAMPLE_I2C_MASTER_SDA;
static gpio_num_t i2c_gpio_scl = CONFIG_EXAMPLE_I2C_MASTER_SCL;
  • 定义 I2C 使用的端口号,本示例使用 I2C_NUM_0,即 I2C0 硬件外设。

static i2c_port_t i2c_port = I2C_NUM_0;
  1. 历史记录存储:定义于 i2ctools_example_main.c

  • 当启动 CONFIG_EXAMPLE_STORE_HISTORY 配置项时,示例会挂载文件系统并将命令历史保存到指定文件。

#if CONFIG_EXAMPLE_STORE_HISTORY
#define MOUNT_PATH "/data"                        // 挂载路径
#define HISTORY_PATH MOUNT_PATH "/history.txt"    // 历史记录文件路径
  1. I2C 通信参数:定义于 cmd_i2ctools.c

  • 定义 I2C 操作的超时时间 (ms),在规定时间内未完成操作则返回超时错误。

#define I2C_TOOL_TIMEOUT_VALUE_MS (50)
  • 定义 I2C 通信频率,示例中设置为 100 kHz。

static uint32_t i2c_frequency = 100 * 1000;
  1. I2C 总线句柄:定义于 cmd_i2ctools.c

  • 定义 I2C 主机总线句柄,用于保存成功创建总线后返回的有效句柄,便于后续操作。

i2c_master_bus_handle_t tool_bus_handle;

备注

  • 配置宏以 CONFIG_ 开头,由 ESP-IDF 构建系统根据 menuconfig 设置自动生成,保存在 sdkconfig 文件中。

  • 定义 GPIO 引脚时使用配置宏而非直接写入引脚数字,可以在编译时灵活修改引脚而无需修改源代码,便于项目扩展和管理。

  • 如果想修改使用的引脚,只需修改 menuconfig 并再次构建/编译示例后会自动同步更新,无需手动更改代码。

任务函数说明

本示例为了实现通过命令行与 I2C 总线设备进行交互调试,主要包含 命令注册与实现 以及 辅助初始化函数 两类。

备注

以下函数均定义于 cmd_i2ctools.c,用于实现具体 I2C 操作命令(如配置 I2C、扫描设备、读取寄存器、写入寄存器、转储寄存器等)并注册到控制台,用户可通过命令行直接执行这些操作。

命令注册与实现

命令注册函数:用于将控制台命令注册到 ESP-IDF 控制台系统。

  1. 构建命令行参数结构体,用于设定用户在控制台输入命令时需要提供哪些信息以及提供信息的格式。

  • 调用 arg_int0() 定义 可选 整数参数,该参数在命令中可出现 0 次或 1 次。

  • 调用 arg_int1() 定义 必选 整数参数,该参数在命令中必须且只能出现 1 次。

  • 调用 arg_intn() 定义 指定次数 整数参数,用户可自定义该参数在命令中最少和最多出现次数。

  • 调用 arg_end() 定义参数结束标志,传参表示最多允许记录的解析错误的数量。

arg_intn() 参数定义如下:

参数解释

传入参数

参数功能

参数说明

shortopts

短选项字符串

单字符形式的命令行选项,可为 NULL 表示不使用短选项。

longopts

长选项字符串

多字符形式的命令行选项,可为 NULL 表示不使用长选项。

datatype

数据类型说明

用于生成帮助信息的字符串

mincount

最小出现次数

命令行中该参数至少出现的次数。通常为 0 或 1。

maxcount

最大出现次数

命令行中该参数最多出现的次数。

glossary

参数说明文字

用于生成帮助信息的描述文字,说明参数的作用。

备注

arg_int0()arg_int1() 都是基于 arg_intn() 封装实现的,分别固定了最小和最大出现次数,即调用时无需传入 mincountmaxcount 参数,以便简化常用场景的调用。

  1. 构建命令注册结构体 esp_console_cmd_t,用于指定命令名称、回调函数、帮助信息和参数结构。

结构体成员

结构体成员

成员功能

成员说明

command

命令名称

用户在控制台输入的命令名。

help

命令信息

简短描述命令用途,在控制台输入 help 即可显示。

hint

命令提示(可选)

用于提供参数格式提示。

func

回调函数

命令触发时调用的函数指针。

argtable

命令参数结构

指向参数结构体,用于解析用户输入的参数。

func_w_context

带上下文的回调函数

当回调函数需要访问外部上下文时使用,例如需要传入额外结构体或状态。

context

上下文指针

指向任意用户自定义数据结构,传入 func_w_context,在回调函数中可访问该数据。

备注

  • funcfunc_w_context 互斥使用,只需要选择其一注册回调。

  • context 仅在使用 func_w_context 时有效,可携带命令执行所需的额外数据或状态。

  • 通过这种机制,命令回调函数既可以独立执行,也可以访问外部状态,实现更灵活的命令处理。

  1. 调用 esp_console_cmd_register() 注册命令,使其可在控制台使用。进一步说明及传参可参考 控制台终端 API

注册回调函数:在用户输入命令时被调用,读取参数结构体中的值并执行相应操作。

参数解释

传入参数

参数功能

参数说明

argc

命令行参数个数

由系统传入,表示 argv 数组中参数的数量。

argv

命令行参数数组

由系统传入,包含命令名称及用户输入的各个选项和值。

更改 I2C 总线配置

定义参数结构体

i2cconfig_args 是一个命令行参数结构体,用于定义和保存用户在控制台输入 i2cconfig 命令时提供的参数。

结构体成员

传入参数

参数功能

参数说明

port

I2C 端口号

用于解析 --port 选项,表示要配置的 I2C 端口。

freq

I2C 通信频率

用于解析 --freq 选项,表示配置的通信频率,单位为 Hz。

sda

I2C 数据线引脚

用于解析 --sda 选项,表示 SDA 所使用的 GPIO 编号。

scl

I2C 时钟线引脚

用于解析 --scl 选项,表示 SCL 所使用的 GPIO 编号。

end

参数结束标志

用于标记参数列表结束并保存解析错误信息。

注册命令

register_i2cconfig() 用于在 ESP-IDF 控制台中注册 i2cconfig 命令,使用户可以通过命令行动态修改 I2C 总线参数配置(端口号、频率以及 SDA/SCL 引脚)。

  1. 定义 命令行参数结构体,即命令的输入结构:

  • 可选 参数: portfreq

  • 必选 参数: sdascl

  • 最多允许记录 2 个解析错误。

  1. 构建 esp_console_cmd_t 结构体

  2. 注册命令

实现命令

do_i2cconfig_cmd()回调处理函数,用于更改 I2C 总线配置,包括端口号、通信频率、SDA/SCL 引脚,并重新初始化 I2C 主机总线,适用于需要在运行过程中调整 I2C 端口或引脚的场景。

该函数的执行逻辑如下:

  1. 解析命令行参数:

  • 调用 arg_parse() 解析命令行参数,并将参数存入 i2cconfig_args 结构体

  • 若解析命令行参数出错,调用 arg_print_errors() 输出错误信息并直接返回。

  1. 定义变量:

  • 定义变量 i2c_portI2C_NUM_0,表示默认使用 I2C 控制器 0。

  • 定义变量 i2c_gpio_sdai2c_gpio_scl,用于保存 I2C 数据线和时钟线使用的 GPIO 编号。初始值为 0,后续根据命令行参数或默认配置更新为实际引脚编号。

  1. 判断/更新参数

  • 判断用户是否指定 port 参数。如果指定,则调用 i2c_get_port() 验证端口编号 有效性并更新 i2c_port

  • 判断用户是否指定 freq 参数。如果指定,则更新全局 I2C 通信频率 i2c_frequency 为指定频率。

  • 根据用户指定的 sdascl 参数更新变量 i2c_gpio_sdai2c_gpio_scl

  1. 更新总线

  • 调用 i2c_del_master_bus() 删除已存在的 I2C 总线,保证重新初始化时不会冲突。进一步介绍及传参说明可参考 I2C 接口

  • 根据指定的端口号、SDA/SCL 引脚等参数重新 配置并初始化 I2C 总线

验证 I2C 端口

i2c_get_port() 用于验证用户提供的 I2C 端口编号是否有效,并返回对应的 i2c_port_t 类型端口句柄,保证后续 I2C 操作使用正确的端口。

参数解释

传入参数

参数功能

参数说明

port

用户指定的 I2C 端口编号

用于选择要操作的 I2C 控制器。

i2c_port

输出端口句柄指针

用于保存验证后的 I2C 端口编号,后续 I2C 操作将使用该句柄。

该函数的执行逻辑如下:

  1. 检查传入的 I2C 端口编号是否在正确范围内,如果端口号无效,打印错误日志并返回 ESP_FAIL

  2. 如果端口号有效,将传入的指针 i2c_port 指向对应的端口编号,并返回 ESP_OK 表示端口验证通过。

检测从机设备

注册命令

register_i2cdetect() 用于在 ESP-IDF 控制台中注册 i2cdetect 命令。

  1. 构建 esp_console_cmd_t 结构体

  2. 注册命令

备注

该命令无需输入额外参数,所以不需要定义参数结构体。

实现命令

do_i2cdetect_cmd()回调处理函数,用于扫描指定 I2C 总线上的所有设备,帮助用户快速检测总线上连接了哪些从机设备。

该函数的执行逻辑如下:

  1. 定义变量 address 用于保存当前扫描的 I2C 从机地址。

  2. 构建从机设备地址表格:

  • 打印表头,用于显示 16 列的地址偏移。

  • 外层循环按 16 个地址为一行遍历 I2C 地址空间,并打印当前行的起始地址。

  • 内层循环遍历每行中的 16 个地址:

    • 调用 i2c_master_probe() 检测对应地址是否有设备响应。进一步介绍及传参说明可参考 I2C 接口

    • 根据返回值打印结果:

      • ESP_OK:从机设备响应,打印设备地址。

      • ESP_ERR_TIMEOUT:总线被占用或未响应,打印 UU

      • 其他,例如未检测到设备,打印 --

获取 I2C 从机数据

定义参数结构体

i2cget_args 是一个命令行参数结构体,用于定义和保存用户在控制台输入 i2cget 命令时提供的参数。

结构体成员

传入参数

参数功能

参数说明

chip_address

设备地址

需要获取数据的从机设备地址。

register_address

寄存器地址

指定获取特地寄存器的数据。

data_length

数据长度

需要获取的数据长度。

end

参数结束标志

用于标记参数列表结束并保存解析错误信息。

注册命令

register_i2cget() 用于在 ESP-IDF 控制台中注册 i2cget 命令,使用户可以通过命令行动态获取指定设备的指定寄存器中的数据。

  1. 定义命令行参数结构,即命令的输入结构:

  • 可选 参数: register_addressdata_length

  • 必选 参数: chip_address

  • 最多允许记录 1 个解析错误。

  1. 构建 esp_console_cmd_t 结构体

  2. 注册命令

实现命令

do_i2cget_cmd()回调处理函数,用于获取指定设备的指定寄存器中的数据。

该函数的执行逻辑如下:

  1. 解析命令行参数:

  • 调用 arg_parse() 解析命令行参数,并将参数存入 i2cget_args 结构体

  • 若解析命令行参数出错,调用 arg_print_errors() 输出错误信息并直接返回。

  1. 判断/更新参数

  • 保存用户指定的 chip_address 参数至 chip_addr

  • 判断用户是否指定 register_address 参数。如果指定,则更新寄存器地址 data_addr 为指定地址,否则默认为 -1。

  • 判断用户是否指定 data_length 参数。如果指定,则更新数据长度 len 为指定长度,否则默认为 1。

  1. 添加设备至总线

  2. 执行读操作

  • 为即将接收的数据申请长度为 len 的动态内存缓冲区 data

  • I2C 主机写入后读取i2c_master_transmit_receive() 的参数说明可参考 I2C 接口

  • 根据返回值打印结果:

    • ESP_OK:循环打印读取到的数据,每行 16 个字节。

    • ESP_ERR_TIMEOUT:表示总线繁忙,打印 Bus is busy

    • 其他,打印 Read failed

  1. 释放资源

  • 释放接收缓冲区。

  • 调用 i2c_master_bus_rm_device() 移除设备,并返回执行结果。进一步介绍及传参说明可参考 I2C 接口

写入数据至 I2C 从机

定义参数结构体

i2cset_args 是一个命令行参数结构体,用于定义和保存用户在控制台输入 i2cset 命令时提供的参数。

结构体成员

传入参数

参数功能

参数说明

chip_address

设备地址

需要获取数据的从机设备地址。

register_address

寄存器地址

指定获取特地寄存器的数据。

data

写入数据

需要写入从机的数据。

end

参数结束标志

用于标记参数列表结束并保存解析错误信息。

注册命令

register_i2cset() 用于在 ESP-IDF 控制台中注册 i2cset 命令,使用户可以通过命令行动态将数据写入指定设备的指定寄存器中。

  1. 定义命令行参数结构,即命令的输入结构:

  • 可选 参数: register_address

  • 必选 参数: chip_address

  • 指定次数 参数:data,最少写入 0 个数据,最多写入 256 个数据。

  • 最多允许记录 2 个解析错误。

  1. 构建 esp_console_cmd_t 结构体

  2. 注册命令

实现命令

do_i2cset_cmd()回调处理函数,用于将数据写入指定设备的指定寄存器中。

该函数的执行逻辑如下:

  1. 解析命令行参数:

  • 调用 arg_parse() 解析命令行参数,并将参数存入 i2cset_args 结构体

  • 若解析命令行参数出错,调用 arg_print_errors() 输出错误信息并直接返回。

  1. 判断/更新参数

  • 保存用户指定的 chip_address 参数至 chip_addr

  • 判断用户是否指定 register_address 参数。如果指定,则更新寄存器地址 data_addr 为指定地址,否则默认为 0。

  • 获取用户需要写入从机的数据的长度,保存至 len

  1. 添加设备至总线

  2. 执行写操作

  • 为即将写入的数据申请长度为 len + 1 的动态内存缓冲区 data

  • 将目标寄存器地址 data_addr 存入缓冲区的第 0 个字节,作为要发送的第一个数据。

  • 通过循环将用户传入的写入数据依次复制到缓冲区,从 data[1] 开始填充,

  • I2C 主机写入i2c_master_transmit() 的参数说明可参考 I2C 接口

  • 根据返回值打印结果:

    • ESP_OK:表示写入成功,打印 Write OK

    • ESP_ERR_TIMEOUT:表示总线繁忙,打印 Bus is busy

    • 其他,打印 Write failed

  1. 释放资源

  • 释放接收缓冲区。

  • 调用 i2c_master_bus_rm_device() 移除设备,并返回执行结果。

扫描 I2C 从机寄存器

定义参数结构体

i2cdump_args 是一个命令行参数结构体,用于定义和保存用户在控制台输入 i2cdump 命令时提供的参数。

结构体成员

传入参数

参数功能

参数说明

chip_address

设备地址

需要获取数据的从机设备地址。

size

读取数据宽度

表示一次读取多少字节,支持 1、2 或 4 字节。

end

参数结束标志

用于标记参数列表结束并保存解析错误信息。

注册命令

do_i2cdump_cmd() 用于在 ESP-IDF 控制台中注册 i2cdump 命令,使用户可以通过命令行动态扫描并打印 I2C 从机设备内部寄存器的值,帮助用户快速检查设备寄存器状态。

  1. 定义命令行参数结构,即命令的输入结构:

  • 可选 参数: size

  • 必选 参数: chip_address

  • 最多允许记录 1 个解析错误。

  1. 构建 esp_console_cmd_t 结构体

  2. 注册命令

实现命令

do_i2cdump_cmd()回调处理函数,用于扫描并打印 I2C 从机设备内部寄存器的值。

该函数的执行逻辑如下:

  1. 解析命令行参数:

  • 调用 arg_parse() 解析命令行参数,并将参数存入 i2cset_args 结构体

  • 若解析命令行参数出错,调用 arg_print_errors() 输出错误信息并直接返回。

  1. 判断/更新参数

  • 保存用户指定的 chip_address 参数至 chip_addr

  • 判断用户是否写入 size 参数。如果写入,则更新读取的数据宽度 size 为指定宽度,否则默认为 1。

  • 判断用户指定的数据宽度是否有效(只接受1、2 和 4字节)。

  1. 添加设备至总线

  2. 扫描寄存器

  • 定义缓存变量:

    • data_addr:临时保存当前要访问的寄存器地址。

    • data[4]:存放从设备读回的数据,支持最大 4 字节。

    • block[16]:存放一行(16 个寄存器)的数据,用于后续 ASCII 可视化打印。

  • 打印表头,包括十六进制数据列和 ASCII 字符显示列。

  • 外层循环按 16 个寄存器为一行扫描从机设备的寄存器,并打印当前行的起始地址。

  • 内层循环遍历每行中的 16 个寄存器:

    • 计算当前寄存器地址。

    • I2C 主机写入后读取 寄存器数据。i2c_master_transmit_receive() 的参数说明可参考 I2C 接口

    • 根据返回值打印结果:

      • ESP_OK:打印数据的十六进制值,并将其保存到 block[]

      • 其他,例如获取数据失败或当前寄存器无数据,打印 xx,并将 block[] 置 -1 表示无效。

  • 遍历 block[16] 完成 ASCII 可视化输出。

  1. 移除从机设备

  • 调用 i2c_master_bus_rm_device() 移除设备,并返回执行结果。

集中注册所有命令

register_i2ctools() 是一个总入口函数,通过调用这个函数将 I2C 的所有命令一次性注册到 ESP-IDF 控制台中,无需单独注册。

备注

以下函数定义于 i2ctools_example_main.c,当启动 CONFIG_EXAMPLE_STORE_HISTORY 配置项时有效。

存储命令历史

initialize_filesystem() 用于初始化并挂载 SPI Flash 上的 FAT 文件系统,以便在示例中存储命令历史或其他文件。

  1. 定义句柄变量 wl_handle,用于管理 SPI Flash 上的文件系统访问。

  2. 构建挂载配置结构体 esp_vfs_fat_mount_config_t。结构体成员说明可参考 FAT 文件系统

  • 允许最多同时打开 4 个文件。

  • 如果挂载失败,自动格式化 SPI Flash 并创建文件系统。

  1. 调用 esp_vfs_fat_spiflash_mount_rw_wl() 将 FAT 文件系统挂载到 MOUNT_PATH 目录,并使用 storage 分区。进一步介绍及传参说明可参考 FAT 文件系统

  • 如果挂载成功,保存文件系统句柄,供后续访问使用。

  • 如果挂载失败,打印错误日志并返回,不继续执行后续操作。

主函数说明

本示例演示了如何在 ESP-IDF 控制台环境下使用 I2C 工具命令与 I2C 总线设备进行交互,包括 I2C 总线初始化、命令注册、控制台 REPL 环境配置。

其执行逻辑如下:

  1. 配置 REPL:

  • 定义控制台 REPL 句柄 repl

  • 使用默认配置 ESP_CONSOLE_REPL_CONFIG_DEFAULT() 初始化 esp_console_repl_config_t 结构体。结构体成员说明可参考 控制台终端

  • 设置提示符 prompt,用于提示用户输入命令。

  1. 初始化文件系统:

  • CONFIG_EXAMPLE_STORE_HISTORY 启用时,调用 initialize_filesystem() 挂载 FAT 文件系统,用于保存控制台历史命令。

  • 设置历史记录保存路径为 HISTORY_PATH

  1. 通过条件编译判断并 初始化 REPL 环境

  2. 配置并初始化 I2C 总线

  3. 调用 register_i2ctools() 注册 I2C 工具命令。

  4. 打印命令使用步骤,指导用户如何通过控制台命令操作 I2C 总线和设备。

  5. 调用 esp_console_start_repl(),进入交互式命令行,等待用户输入命令。进一步介绍及传参说明可参考 控制台终端