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*/
}
}