ESP32开发之路(10)— ESP32的I2C通信读写AT24C02
一、前言
之前我们将ESP32的基本功能—WiFi都差不多试过了,进行了就来使用一下他的其他功能,比如I2C通信,I2C是一种典型的低速硬件通信协议,我想很多人的I2C通信都是从AT24C02开始的,所以我们ESP32的I2C通信也从AT24C02开始。
因为某种原因,我手边没有单独的AT24C02模块,所以就把小熊派E53_SC1扩展板拿来用一下,因为他上面有一个AT24C02,如下所示:
通过其地址设置引脚的接线我们知道,其地址应该为0xA2
(左移后),可实际上,三个地址设置引脚都接到了地,不知道是不是因为改版的原因,所以在使用的时候最好从实际硬件去确认一下其器件地址。
二、参数配置
在使用IIC各种需要使用到一些参数,我们使用宏定义的方式去设置,首先,是IIC的读写控制:
#define WRITE_BIT 0x00 /*!< I2C master write */
#define READ_BIT 0x01 /*!< I2C master read */
然后是IIC的应答机制的设置:
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
#define ACK_VAL 0x0 /*!< I2C ack value */
#define NACK_VAL 0x1 /*!< I2C nack value */
接下来是IIC引脚的设置,ESP32由两个IIC控制器,分别对应不同的引脚,我们设置为如下,可以通过定义来选择使用哪一个IIC控制器:
#if 1
#define IIC_CTRL_NUM I2C_NUM_1 /*!< I2C port number */
#define SDA_PIN_NUM 25 /*!< gpio number for I2C data */
#define SCL_PIN_NUM 26 /*!< gpio number for I2C clock */
#else
#define IIC_CTRL_NUM I2C_NUM_0 /*!< I2C port number */
#define SDA_PIN_NUM 18 /*!< gpio number for I2C data */
#define SCL_PIN_NUM 19 /*!< gpio number for I2C clock */
#endif
还有最重要的AT24C02的器件地址
#define AT24C02_DeviceAddr 0xA0 /* AT24C02的器件地址 */
三、编写AT24C02控制函数
首先我们需要初始化IIC控制器,设置为主模式,
/**
* @brief i2c master initialization
*/
static esp_err_t i2c_master_init(void)
{
int i2c_master_port = IIC_CTRL_NUM;
i2c_config_t conf;
conf.mode = I2C_MODE_MASTER;
conf.sda_io_num = SDA_PIN_NUM;
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
conf.scl_io_num = SCL_PIN_NUM;
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
conf.master.clk_speed = 100000; /* 标准模式(100 kbit/s) */
i2c_param_config(i2c_master_port, &conf);
return i2c_driver_install(i2c_master_port, conf.mode, 0, 0, 0);
}
然后是对AT24C02的写入一个字节函数:
/* AT24C02写入一个字节函数,第一个参数为要写入的值,第二个参数为要写入的地址*/
static esp_err_t at24c02_write(uint8_t data_wr, uint16_t WriteAddr)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, AT24C02_DeviceAddr | WRITE_BIT, ACK_CHECK_EN);
i2c_master_write_byte(cmd, WriteAddr % 256, ACK_CHECK_EN);
i2c_master_write_byte(cmd, data_wr, ACK_CHECK_EN);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(IIC_CTRL_NUM, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
return ret;
}
然后是读取一个字节的函数:
/* AT24C02读取一个字节函数,第一个参数为要读出值的存放指针,第二个参数为要读出的地址*/
static esp_err_t at24c02_read(uint8_t* data_rd, uint16_t ReadAddr)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, AT24C02_DeviceAddr | WRITE_BIT, ACK_CHECK_EN);
i2c_master_write_byte(cmd, ReadAddr % 256, ACK_CHECK_EN);
i2c_master_start(cmd);
i2c_master_write_byte(cmd, 0xA0 | READ_BIT, ACK_CHECK_EN);
i2c_master_read_byte(cmd, data_rd, NACK_VAL);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(IIC_CTRL_NUM, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
return ret;
}
四、读写AT24C02
接下来,编写程序对AT24C02进行读取
void app_main(void)
{
uint8_t data_buf = 0xff;
/* 打印Hello world! */
printf("Hello world!\n");
/* 初始化IIC控制器 */
ESP_ERROR_CHECK(i2c_master_init());
/* 读取AT24C02里的数据 */
if(at24c02_read(&data_buf, 0x02) == ESP_OK)
printf("Read at24c02 in 0x02, data = 0x%x \n",data_buf);
else
printf("Read at24c02 err \n");
/* 向AT24C02写入数据 */
if(at24c02_write(0x55, 0x02) == ESP_OK)
printf("Write 0x55 to at24c02 in 0x02 \n");
else
printf("Write at24c02 err \n");
/* 读取AT24C02里的数据 */
if(at24c02_read(&data_buf, 0x02) == ESP_OK)
printf("Read at24c02 in 0x02, data = 0x%x \n",data_buf);
else
printf("Read at24c02 err \n");
}
编译下载运行,结果如下,可以看到,读写数据成功
替换为另一组IIC控制器,
编译下载运行,读写数据依然成功
五、代码
下面贴上完整的app_main.c的代码
#include <string.h>
#include <sys/param.h>
#include "driver/i2c.h"
#define GPIO_LED_NUM 2 /* LED引脚编号 */
#define WRITE_BIT 0x00 /*!< I2C master write */
#define READ_BIT 0x01 /*!< I2C master read */
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave */
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
#define ACK_VAL 0x0 /*!< I2C ack value */
#define NACK_VAL 0x1 /*!< I2C nack value */
#if 0
#define IIC_CTRL_NUM I2C_NUM_1 /*!< I2C port number */
#define SDA_PIN_NUM 25 /*!< gpio number for I2C data */
#define SCL_PIN_NUM 26 /*!< gpio number for I2C clock */
#else
#define IIC_CTRL_NUM I2C_NUM_0 /*!< I2C port number */
#define SDA_PIN_NUM 18 /*!< gpio number for I2C data */
#define SCL_PIN_NUM 19 /*!< gpio number for I2C clock */
#endif
#define AT24C02_DeviceAddr 0xA0 /* AT24C02的器件地址 */
/**
* @brief i2c master initialization
*/
static esp_err_t i2c_master_init(void)
{
int i2c_master_port = IIC_CTRL_NUM;
i2c_config_t conf;
conf.mode = I2C_MODE_MASTER;
conf.sda_io_num = SDA_PIN_NUM;
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
conf.scl_io_num = SCL_PIN_NUM;
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
conf.master.clk_speed = 100000; /* 标准模式(100 kbit/s) */
i2c_param_config(i2c_master_port, &conf);
return i2c_driver_install(i2c_master_port, conf.mode, 0, 0, 0);
}
/* AT24C02读取一个字节函数,第一个参数为要读出值的存放指针,第二个参数为要读出的地址*/
static esp_err_t at24c02_read(uint8_t* data_rd, uint16_t ReadAddr)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, AT24C02_DeviceAddr | WRITE_BIT, ACK_CHECK_EN);
i2c_master_write_byte(cmd, ReadAddr % 256, ACK_CHECK_EN);
i2c_master_start(cmd);
i2c_master_write_byte(cmd, 0xA0 | READ_BIT, ACK_CHECK_EN);
i2c_master_read_byte(cmd, data_rd, NACK_VAL);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(IIC_CTRL_NUM, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
return ret;
}
/* AT24C02写入一个字节函数,第一个参数为要写入的值,第二个参数为要写入的地址*/
static esp_err_t at24c02_write(uint8_t data_wr, uint16_t WriteAddr)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, AT24C02_DeviceAddr | WRITE_BIT, ACK_CHECK_EN);
i2c_master_write_byte(cmd, WriteAddr % 256, ACK_CHECK_EN);
i2c_master_write_byte(cmd, data_wr, ACK_CHECK_EN);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(IIC_CTRL_NUM, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
return ret;
}
void app_main(void)
{
uint8_t data_buf = 0x00;
/* 打印Hello world! */
printf("Hello world!\n");
/* 初始化IIC控制器 */
ESP_ERROR_CHECK(i2c_master_init());
/* 读取AT24C02里的数据 */
if(at24c02_read(&data_buf, 0x03) == ESP_OK)
printf("Read at24c02 in 0x03, data = 0x%x \n",data_buf);
else
printf("Read at24c02 err \n");
/* 向AT24C02写入数据 */
if(at24c02_write(0x55, 0x03) == ESP_OK)
printf("Write 0x55 to at24c02 in 0x03 \n");
else
printf("Write at24c02 err \n");
/* 读取AT24C02里的数据 */
if(at24c02_read(&data_buf, 0x03) == ESP_OK)
printf("Read at24c02 in 0x03, data = 0x%x \n",data_buf);
else
printf("Read at24c02 err \n");
/* 定义一个gpio配置结构体 初始化LED */
gpio_config_t gpio_config_structure;
/* 初始化gpio配置结构体*/
gpio_config_structure.pin_bit_mask = (1ULL << GPIO_LED_NUM);/* 选择gpio2 */
gpio_config_structure.mode = GPIO_MODE_OUTPUT; /* 输出模式 */
gpio_config_structure.pull_up_en = 0; /* 不上拉 */
gpio_config_structure.pull_down_en = 0; /* 不下拉 */
gpio_config_structure.intr_type = GPIO_PIN_INTR_DISABLE; /* 禁止中断 */
/* 根据设定参数初始化并使能 */
gpio_config(&gpio_config_structure);
/* 默认熄灭LED */
gpio_set_level(GPIO_LED_NUM, 0); /* 熄灭 */
while(1)
{
gpio_set_level(GPIO_LED_NUM, 0); /* 熄灭 */
vTaskDelay(500 / portTICK_PERIOD_MS); /* 延时500ms*/
gpio_set_level(GPIO_LED_NUM, 1); /* 点亮 */
vTaskDelay(500 / portTICK_PERIOD_MS); /* 延时500ms*/
}
}