当前位置: 首页 > news >正文

ESP32-S3学习笔记<4>:I2C的应用

ESP32-S3学习笔记<4>:UART的应用

  • 1. 头文件包含
  • 2. I2C的配置
    • 2.1 i2c_num 的选择
    • 2.2 i2c_conf 的设定
      • 2.2.1 mode/设置模式
      • 2.2.2 sda_io_num、scl_io_num/设置SDA和SCL的GPIO
      • 2.2.3 sda_pullup_en、scl_pullup_en/设置SDA和SCL的上拉使能
      • 2.2.4 clk_speed/设置I2C速率
      • 2.2.5 clk_flags/标记位
  • 3. 驱动的安装
  • 4. 读或写
    • 4.1 创建命令序列/链
    • 4.2 添加start/start-repeated命令
    • 4.3 添加单个字节写入
    • 4.4 添加多个字节写入
    • 4.5 读取单个字节数据
    • 4.6 读取多个字节数据
    • 4.7 添加stop命令
    • 4.8 执行命令序列
    • 4.9 删除命令序列
  • 5. 示例

1. 头文件包含

#include "driver/i2c.h"
#include "driver/gpio.h"

2. I2C的配置

使用如下函数配置I2C外设:

esp_err_t i2c_param_config(i2c_port_t i2c_num, const i2c_config_t *i2c_conf);

各个参数的含义:

2.1 i2c_num 的选择

参数 i2c_num 用于指定要配置的I2C外设。ESP32-S3有2个I2C外设。选择 I2C_NUM_0I2C_NUM_1

typedef enum {I2C_NUM_0 = 0,              /*!< I2C port 0 */
#if SOC_HP_I2C_NUM >= 2I2C_NUM_1,                  /*!< I2C port 1 */
#endif /* SOC_HP_I2C_NUM >= 2 */
#if SOC_LP_I2C_NUM >= 1LP_I2C_NUM_0,               /*< LP_I2C port 0 */
#endif /* SOC_LP_I2C_NUM >= 1 */I2C_NUM_MAX,                /*!< I2C port max */
} i2c_port_t;

2.2 i2c_conf 的设定

第二个参数 i2c_conf 用于配置I2C的工作参数。结构体 i2c_config_t 的定义为:

typedef struct {i2c_mode_t mode;     /*!< I2C mode */int sda_io_num;      /*!< GPIO number for I2C sda signal */int scl_io_num;      /*!< GPIO number for I2C scl signal */bool sda_pullup_en;  /*!< Internal GPIO pull mode for I2C sda signal*/bool scl_pullup_en;  /*!< Internal GPIO pull mode for I2C scl signal*/union {struct {uint32_t clk_speed;      /*!< I2C clock frequency for master mode, (no higher than 1MHz for now) */} master;                    /*!< I2C master config */
#if SOC_I2C_SUPPORT_SLAVEstruct {uint8_t addr_10bit_en;   /*!< I2C 10bit address mode enable for slave mode */uint16_t slave_addr;     /*!< I2C address for slave mode */uint32_t maximum_speed;  /*!< I2C expected clock speed from SCL. */} slave;                     /*!< I2C slave config */
#endif // SOC_I2C_SUPPORT_SLAVE};uint32_t clk_flags;              /*!< Bitwise of ``I2C_SCLK_SRC_FLAG_**FOR_DFS**`` for clk source choice*/
} i2c_config_t;typedef void *i2c_cmd_handle_t;    /*!< I2C command handle  */

2.2.1 mode/设置模式

参数 mode 用于设置I2C的模式。一般是使用 I2C_MODE_MASTER

typedef enum{
#if SOC_I2C_SUPPORT_SLAVEI2C_MODE_SLAVE = 0,   /*!< I2C slave mode */
#endifI2C_MODE_MASTER,      /*!< I2C master mode */I2C_MODE_MAX,
} i2c_mode_t;

2.2.2 sda_io_num、scl_io_num/设置SDA和SCL的GPIO

2.2.3 sda_pullup_en、scl_pullup_en/设置SDA和SCL的上拉使能

硬件设计上,I2C的两个信号线一般在远端使用电阻上拉。这里就不需要使能了。如果外面没有上拉电阻,这里也可以使能内部上拉以补救。

2.2.4 clk_speed/设置I2C速率

如设置为100000,则表示100Kbps的I2C速率。

2.2.5 clk_flags/标记位

设置为 I2C_SCLK_SRC_FLAG_FOR_NOMAL 即可。

3. 驱动的安装

使用如下函数安装I2C驱动:

esp_err_t i2c_driver_install(i2c_port_t i2c_num, i2c_mode_t mode, size_t slv_rx_buf_len, size_t slv_tx_buf_len, int intr_alloc_flags);
  1. i2c_num:指定I2C外设,I2C_NUM_0 或者 I2C_NUM_1
  2. mode:指定I2C模式,I2C_MODE_MASTER
  3. slv_rx_buf_lenslv_tx_buf_len:用于指定slave模式时的接收和发送缓存大小。对于master模式,设置为0即可。
  4. intr_alloc_flags:设定中断优先级之类的标记。定义在 esp_intr_alloc.h 文件中。

这一步完成之后,I2C就可以准备读写其他设备了。

4. 读或写

ESP32-S3的I2C驱动,使用一种链接的方式来实现读写。程序首先要创建一个链接,然后将I2C访问过程中的每一个步骤,按类型不同,使用不同的函数压入到链中,最后在命令驱动按照链去执行每一个步骤。使用起来还是很方便的。

4.1 创建命令序列/链

使用如下命令创建一个命令序列。返回值要保留,后面压入命令和执行命令都要用到。

i2c_cmd_handle_t i2c_cmd_link_create(void);

4.2 添加start/start-repeated命令

I2C访问的开始,不论是读还是写,都是以一个start条件开始的。因此读写序列的第一个命令,一定是添加一个start命令。
另外I2C读取数据的时候,在总线地址和目标地址发送完成后,需要重新发送一个start(start-repeated)命令。
这两种情况都可以通过如下函数添加。

esp_err_t i2c_master_start(i2c_cmd_handle_t cmd_handle);

其中,第一个参数 cmd_handle,即是由函数 i2c_cmd_link_create 创建返回的句柄。

4.3 添加单个字节写入

I2C主设备,需要向从设备写入总线地址、寄存器地址、或者单个字节的数据时,都可以添加单个字节写入命令。通过如下函数实现该功能:

esp_err_t i2c_master_write_byte(i2c_cmd_handle_t cmd_handle, uint8_t data, bool ack_en);

其中,第二个参数 data 是要写入的数据。第三个参数 ack_en ,用于设定是否需要检查从机的ACK。正常应该是需要的。

4.4 添加多个字节写入

也可以一次添加多个数据写入,例如写入EEPROM数据等。通过如下函数来实现该功能:

esp_err_t i2c_master_write(i2c_cmd_handle_t cmd_handle, const uint8_t *data, size_t data_len, bool ack_en);

第二个参数 data ,指向一个保存多个数据的缓存。data_len 用于指定写入数据量。

4.5 读取单个字节数据

使用如下函数向命令序列中添加单个字节读取命令。

esp_err_t i2c_master_read_byte(i2c_cmd_handle_t cmd_handle, uint8_t *data, i2c_ack_type_t ack);

这个命令和单个字节写入有点类似,但是第三个参数,即是否需要ACK不同。其定义为:

typedef enum {I2C_MASTER_ACK = 0x0,        /*!< I2C ack for each byte read */I2C_MASTER_NACK = 0x1,       /*!< I2C nack for each byte read */I2C_MASTER_LAST_NACK = 0x2,   /*!< I2C nack for the last byte*/I2C_MASTER_ACK_MAX,
} i2c_ack_type_t;

如何设置,需要根据应用而定。它控制读取数据时,每个字节接收完成后,是否需要向从机发送ACK响应。一般来说,连续读取一串数据,都需要发送ACK,但是最后一个数据读完后应该发送NACK。

4.6 读取多个字节数据

使用如下函数向命令序列中添加多个字节的读取命令。

esp_err_t i2c_master_read(i2c_cmd_handle_t cmd_handle, uint8_t *data, size_t data_len, i2c_ack_type_t ack);

这种情况下,最后一个参数ack,一般应设置为 I2C_MASTER_LAST_NACK ,以指示最后一个字节接收完成后,不发送ACK。除非这条读命令添加后,还需要再添加读取命令。

4.7 添加stop命令

使用如下函数添加stop命令,以结束I2C传输:

esp_err_t i2c_master_stop(i2c_cmd_handle_t cmd_handle);

4.8 执行命令序列

使用如下命令执行已经构建好的命令序列:

esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle, TickType_t ticks_to_wait);

ticks_to_wait 用于设置超时时间。以FreeRTOS的systick跳动为单位。

4.9 删除命令序列

序列执行完成后,需要删除命令序列。因为命令序列是动态分配的。

void i2c_cmd_link_delete(i2c_cmd_handle_t cmd_handle);

5. 示例

以下示例,先向24C02 EEPROM写入一些字节,然后读取这些数据并打印。

test_i2c.h文件:

#define TEST_I2C_GPIO_SCL        (GPIO_NUM_42)
#define TEST_I2C_GPIO_SDA        (GPIO_NUM_41)void TEST_I2C_I2CConfig(void) ;
void TEST_I2C_eeprom_wr_test(void) ;
void TEST_I2C_eeprom_rd_test(void) ;

test_i2c.c文件:

#include "driver/i2c.h"#include "test_i2c.h"void TEST_I2C_I2CConfig(void)
{esp_err_t        iErrCode ;i2c_config_t     stI2CConfigInfo ;stI2CConfigInfo.mode             = I2C_MODE_MASTER ;stI2CConfigInfo.sda_io_num       = TEST_I2C_GPIO_SDA ;stI2CConfigInfo.scl_io_num       = TEST_I2C_GPIO_SCL ;stI2CConfigInfo.sda_pullup_en    = true ;stI2CConfigInfo.scl_pullup_en    = true ;stI2CConfigInfo.master.clk_speed = 100000 ;stI2CConfigInfo.clk_flags        = I2C_SCLK_SRC_FLAG_FOR_NOMAL ;iErrCode = i2c_param_config(I2C_NUM_0, &stI2CConfigInfo) ;if(ESP_OK != iErrCode){printf("i2c_param_config error. Return value is %d.\n", iErrCode) ;}iErrCode = i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0) ;if(ESP_OK != iErrCode){printf("i2c_driver_install error. Return value : %d.\n ", iErrCode) ;}return ;
}void TEST_I2C_eeprom_wr_test(void)
{esp_err_t        iErrCode ;i2c_cmd_handle_t pvCmd ;unsigned char    aucSendBuf[8] = {0xAA,0x11,0xBB,0x22,0xCC,0x33,0xDD,0x44} ;/* 创建I2C命令序列 */pvCmd = i2c_cmd_link_create() ;/* 添加I2C起始命令 */i2c_master_start(pvCmd) ;/* 添加I2C单字节发送:设备总线地址 */i2c_master_write_byte(pvCmd, 0xA0, true) ;/* 添加I2C单字节发送:寄存器地址 */i2c_master_write_byte(pvCmd, 0x10, true) ;/* 添加I2C多字节发送:数据 */i2c_master_write(pvCmd, aucSendBuf, sizeof(aucSendBuf), true) ;/* 添加I2C停止命令 */i2c_master_stop(pvCmd) ;/* 启动命令序列,并等待完成 */iErrCode = i2c_master_cmd_begin(I2C_NUM_0, pvCmd, 500) ;if(ESP_OK != iErrCode){printf("i2c_master_cmd_begin error. Return value : %d. \n ", iErrCode) ;while(1) ;}/* 命令执行完成,删除序列 */i2c_cmd_link_delete(pvCmd) ;printf("24C02 Write done.\n") ;return ;
}void TEST_I2C_eeprom_rd_test(void)
{esp_err_t        iErrCode ;i2c_cmd_handle_t pvCmd ;unsigned char    aucRecvBuf[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} ;/* 创建I2C命令序列 */pvCmd = i2c_cmd_link_create() ;/* 添加I2C起始命令 */i2c_master_start(pvCmd) ;/* 添加I2C单字节发送:设备总线地址(写入) */i2c_master_write_byte(pvCmd, 0xA0, true) ;/* 添加I2C单字节发送:寄存器地址 */i2c_master_write_byte(pvCmd, 0x10, true) ;/* 添加I2C重复起始命令 */i2c_master_start(pvCmd) ;/* 添加I2C单字节发送:设备总线地址(读出) */i2c_master_write_byte(pvCmd, 0xA1, true) ;/* 添加I2C多字节接收:数据 */i2c_master_read(pvCmd, aucRecvBuf, sizeof(aucRecvBuf), I2C_MASTER_LAST_NACK) ;/* 添加I2C停止命令 */i2c_master_stop(pvCmd) ;/* 启动命令序列,并等待完成 */iErrCode = i2c_master_cmd_begin(I2C_NUM_0, pvCmd, 1000) ;if(ESP_OK != iErrCode){printf("i2c_master_cmd_begin failed. Return value : %d. \n", iErrCode) ;while(1) ;}/* 命令执行完成,删除序列 */i2c_cmd_link_delete(pvCmd) ;printf("24C02 read done. Value is %02x %02x %02x %02x %02x %02x %02x %02x.\n", aucRecvBuf[0], aucRecvBuf[1], aucRecvBuf[2], aucRecvBuf[3], aucRecvBuf[4], aucRecvBuf[5], aucRecvBuf[6], aucRecvBuf[7]) ;return ;
}

main.c文件:

#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"#include "test_i2c.h"void app_main(void)
{TEST_I2C_I2CConfig() ;TEST_I2C_eeprom_wr_test() ;vTaskDelay(50) ;TEST_I2C_eeprom_rd_test() ;while (true){vTaskDelay(50) ;}
}

http://www.lryc.cn/news/597146.html

相关文章:

  • DeepSeek 助力 Vue3 开发:打造丝滑的日历(Calendar),日历_家庭维护示例(CalendarView01_31)
  • WebGIS 中常用空间数据格式
  • 2025暑期—06神经网络-常见网络3
  • 2025暑期—06神经网络-常见网络2
  • 2026 拼多多秋招内推码(提前批)
  • 为什么设置 git commit签名是公钥而不是私钥?
  • yo easy-ui5生成项目,ui5版本降级处理
  • Tang Prime 20K板I2S输入输出例程
  • Hexo - 免费搭建个人博客01 - 安装软件工具
  • Java应用程序内存占用分析
  • 大致自定义文件I/O库函数的实现详解(了解即可)
  • 软件开发、项目开发基本步骤
  • Java从入门到精通!第十二天(泛型)
  • 搭建 Android 开发环境JAVA+AS
  • 阿里云ODPS十五周年重磅升级发布:为AI而生的数据平台
  • HTTP性能优化终极指南:从协议原理到企业级实践
  • k8s pvc是否可绑定在多个pod上
  • 【Kubernetes】集群启动nginx,观察端口映射,work节点使用kubectl配置
  • 优化 Elasticsearch JVM 参数配置指南
  • 每日一算:华为-批萨分配问题
  • 谷粒商城篇章13--P340-P360--k8s/KubeSphere【高可用集群篇一】
  • 常用的正则表达式
  • 代码随想录算法训练营第五十二天|图论part3
  • 图论的题目整合(Dijkstra)
  • 【图论,拓扑排序】P1347 排序
  • 算法竞赛备赛——【图论】最小生成树
  • Modbus协议详解与c#应用
  • 算法竞赛备赛——【图论】拓扑排序
  • CI/CD与DevOps集成方法
  • python在windows电脑找回WiFi密码