ESP32 IDF iic通信( 已验证) C语言
关于iic原理建议B站自己看视频去,
然后本文主要实现了esp32的初始化, 写地址, 写数据, 读数据的功能, 从机的代码因为展示不需要,没写.
园子里面有个兄弟写了iic的代码.但是里面有点毒,多发了次地址验证,所以才有这篇文章;
代码注释比较多, 愿君少走弯路❀
以下是头文件主要参数代码:
#include "driver/i2c.h"
#include "freertos/portmacro.h"
#define I2C_NUM I2C_NUM_0
#define I2C_MATER_READ (0x1) // 主机进行读操作,从机进行写操作
#define I2C_MATER_WRITE (0x0) // 主机进行写操作,从机进行读操作
#define ACK_VAL (0x0) // 主机读取时的应答信号(应答)
#define NACK_VAL (0x1) // 主机读取时的应答信号(不应答)
#define portTICK_RATE_MS portTICK_PERIOD_MS //防止报错
以下为iic初始化函数代码:
/*
* @brief 初始化I2C配置;
* @param I2C_SCL_Frequency 为i2c的时钟频率 ,一般模式为100 000(100k);
* @param SCL & SDA 为I2C配置的引脚,允许任意IO口,可直接输入对应的IO口序号,如 19 ,20
* @return err 为esp特有的报错指南?还没搞得很懂,反正是为了方便调试的;
*/
esp_err_t esp32_i2c_init(char SCL , char SDA , int I2C_SCL_Frequency)
{
// 初始化I2C配置
i2c_config_t i2c_config = {
.mode = I2C_MODE_MASTER, // 设置i2c模式
.sda_io_num = SDA, // 设置SDA引脚
.scl_io_num = SCL, // 设置SCL引脚
.sda_pullup_en = GPIO_PULLUP_ENABLE, // 设置上拉使能
.scl_pullup_en = GPIO_PULLUP_ENABLE, // 设置上拉使能
.master.clk_speed = I2C_SCL_Frequency, // 设置时钟频率xxxbit
// .clk_flags = 0,
};
// 设置I2C
i2c_param_config(
I2C_NUM,
//配置参数初始化,此函数内部就是将i2c_config 中的相关参数 填入到 "I2c_NUM "对应的结构体中。
&i2c_config);
// 注册I2C服务及使能 (安装 I2C 驱动程序后, ESP32 即可与其他 I2C 设备通信。)
esp_err_t err = i2c_driver_install(
I2C_NUM, i2c_config.mode, 0, 0,
0); //初始化配置以外的所有相关参数,将配置写入寄存器
return err; //可以直接访问esp32_i2c_init的返回值 即可知道,驱动是否安全完成.
}
以下是写地址函数::
/*
* @brief 进行I2C第一个Byte的写操作(指主机在公屏call对应的从机);
* @param SlaveAddr 为i2c从机的地址,代码未做自动移位处理;
* @param endbit 为写入的最后一个bit,肩负着告诉从机接下来是发送数据还是接收数据(I2C_MATER_READ || I2C_MATER_WRITE)
* @return 0; 暂时没想到有什么需要返回的
* @property 有已经封装好的函数有对应每8bit进行应答的操作,所以后续开发不需要在意iic协议本身
* @exception vscode 可能会提示portTICK_RATE_MS 为未定义标识符,可以别管能编译通过
*/
char i2c_write_addr(char SlaveAddr, char end_bit)
{
//创建i2c_cmd_handle_t对象
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
//添加各种子数据帧
i2c_master_start(cmd); //向cmd对象添加起始信号
i2c_master_write_byte(
cmd, (SlaveAddr) | end_bit,
true); //向cmd对象添加从机r地址及读写位 ack(true)为检测slave答应
//i2c_master_write(cmd, bytes, datalen, true); //向cmd对象添加数据位(数组)
i2c_master_stop(cmd); //向cmd对象添加终止信号
//向I2C_NUM 发送这个数据帧cmd,
i2c_master_cmd_begin(I2C_NUM, cmd, 1000 / portTICK_RATE_MS);
//删除i2c_cmd_handle_t对象,释放资源
i2c_cmd_link_delete(cmd);
return 0;
}
以下是写数据函数(8bits):
/*
* @brief 进行I2C第一个Byte的写操作(指主机在公屏call对应的从机);
* @param SlaveAddr 为i2c从机的地址,代码未做自动移位处理;
* @param endbit 为写入的最后一个bit,肩负着告诉从机接下来是发送数据还是接收数据(I2C_MATER_READ || I2C_MATER_WRITE)
* @return 0; 暂时没想到有什么需要返回的
* @property 有已经封装好的函数有对应每8bit进行应答的操作,所以后续开发不需要在意iic协议本身
* @exception vscode 可能会提示portTICK_RATE_MS 为未定义标识符,可以别管能编译通过
*/
char i2c_write_to_slave(char SlaveAddr, char endbit, char data)
{
//创建i2c_cmd_handle_t对象
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
//添加各种子数据帧
i2c_master_start(cmd); //向cmd对象添加起始信号
i2c_master_write_byte(
cmd, (SlaveAddr) | endbit,
true); //向cmd对象添加从机r地址及读写位 ack(true)为检测slave答应
i2c_master_write_byte(cmd, data, true); //向cmd对象添加数据位(数组)
i2c_master_stop(cmd); //向cmd对象添加终止信号
//向I2C_NUM 发送这个数据帧cmd,
i2c_master_cmd_begin(I2C_NUM, cmd, 1000 / portTICK_RATE_MS);
//删除i2c_cmd_handle_t对象,释放资源
i2c_cmd_link_delete(cmd);
return 0;
}
以下是读数据函数:
/*
* @brief 进行I2C两个Byte的读操作;
* @param SlaveAddr 为i2c从机的地址,代码已做自动移位处理;
* @param endbit 为写入的最后一个bit,肩负着告诉从机接下来是发送数据还是接收数据(I2C_MATER_READ || I2C_MATER_WRITE)
* @return readvalue 为读取的16bit数据,(正常顺序)
* @property 有已经封装好的函数有对应每8bit进行应答的操作,所以后续开发不需要在意iic协议本身
* @exception vscode 可能会提示portTICK_RATE_MS 为未定义标识符,可以别管能编译通过
*/
uint16_t i2c_read_slave(char SlaveAddr, char endbit)
{
static uint8_t readvalue1 = 0; //切记搞成static的,不然会灵异读取
static uint8_t readvalue2 = 0; //切记搞成static的,不然会灵异读取
static uint16_t readvalue = 0;
esp_err_t err = ESP_OK;
//创建i2c_cmd_handle_t对象
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd); //向cmd对象添加起始信号
i2c_master_write_byte(
cmd, (SlaveAddr << 1) | endbit,
true); //向cmd对象添加从机地址及读写位 ack(true)为检测slave答应
//******开始读数据操作:总共读16bit*****//
err = i2c_master_read_byte(cmd, &readvalue1, ACK_VAL);
err = i2c_master_read_byte(cmd, &readvalue2, NACK_VAL);
if (err != ESP_OK) {
goto end;
}
i2c_master_stop(cmd); //向cmd对象添加终止信号
//向I2C_NUM 发送这个数据帧cmd,
i2c_master_cmd_begin(I2C_NUM, cmd, 1000 / portTICK_RATE_MS);
//删除i2c_cmd_handle_t对象,释放资源
end:
i2c_cmd_link_delete(cmd);
readvalue = readvalue1 << 8 | readvalue2;
return readvalue;
}