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;
}
 
posted @ 2024-01-05 08:48  Atul-8  阅读(115)  评论(0编辑  收藏  举报