1. I2C 地址
- I2C 地址:设备的 I2C 地址为
0x50
(7 位地址),在写和读操作中使用时会左移一位并添加读/写标志。- 写操作:
0xA0
- 读操作:
0xA1
- 写操作:
2. 基本指令
a. 写入指令
- 写命令(Write Command):
- 格式:
(设备地址 << 1) | 0
(例如0xA0
) - 步骤:
- 发送 START。
- 发送控制字节(写命令)。
- 发送 16 位内存地址(高字节和低字节)。
- 发送要写入的数据字节。
- 发送 STOP。
- 格式:
b. 读取指令
- 读取命令(Read Command):
- 格式:
(设备地址 << 1) | 1
(例如0xA1
) - 步骤:
- 发送 START。
- 发送控制字节(写命令)。
- 发送 16 位内存地址(高字节和低字节)。
- 发送 START。
- 发送控制字节(读命令)。
- 读取数据字节。
- 发送 NACK(如果不再读取数据)。
- 发送 STOP。
- 格式:
3. 页面写入
- 页面写入(Page Write):
- 每个页面的大小为 64 字节,写入时不能跨越页面边界。
- 格式和步骤与写入命令相同,写入的数据可以是 1 至 64 字节。
- 24C256 具有 512 个页面,每个页面大小为 64 字节
- 在使用 24C256 EEPROM 进行读写时,地址通常需要手动管理。每次写入或读取数据时,您需要指定开始地址。24C256 是一个 256Kb 的 EEPROM,其中每个地址存储 1 字节数据,地址范围从 0 到 32767(0x0000 到 0x7FFF)。
- esp32 写页函数为:i2c_master_write_to_device()。
4. 状态和控制指令
- 写保护(WP):
- WP 引脚:用于启用或禁用写保护功能。
- 连接到 Vcc:写保护开启。
- 连接到 GND:写保护关闭。
- WP 引脚:用于启用或禁用写保护功能。
#include "driver/i2c.h"
#include <stdio.h>
#include <string.h>
#define I2C_MASTER_SCL_IO 15 // SCL 引脚
#define I2C_MASTER_SDA_IO 21 // SDA 引脚
#define I2C_MASTER_FREQ_HZ 100000 // I2C 时钟频率
#define I2C_MASTER_PORT I2C_NUM_0 // I2C 端口
#define EEPROM_I2C_ADDRESS 0x50 // 24C256 I2C 地址
// I2C 初始化函数
void i2c_master_init() {
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_MASTER_SDA_IO,
.scl_io_num = I2C_MASTER_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ
};
i2c_param_config(I2C_MASTER_PORT, &conf);
i2c_driver_install(I2C_MASTER_PORT, conf.mode, 0, 0, 0);
}
// 向 24C256 写入一个字节
esp_err_t eeprom_write_byte(uint16_t mem_address, uint8_t data) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (EEPROM_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true);
// 发送内存地址的高字节和低字节
i2c_master_write_byte(cmd, (mem_address >> 8) & 0xFF, true);
i2c_master_write_byte(cmd, mem_address & 0xFF, true);
// 发送要写入的数据
i2c_master_write_byte(cmd, data, true);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_PORT, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
// EEPROM 写入完成后通常需要 5ms 延迟
vTaskDelay(5 / portTICK_PERIOD_MS);
return ret;
}
// 从 24C256 读取一个字节
esp_err_t eeprom_read_byte(uint16_t mem_address, uint8_t *data) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
// 发送写命令,设置内存地址指针
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (EEPROM_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, (mem_address >> 8) & 0xFF, true);
i2c_master_write_byte(cmd, mem_address & 0xFF, true);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_PORT, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
return ret;
}
// 读取数据
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (EEPROM_I2C_ADDRESS << 1) | I2C_MASTER_READ, true);
i2c_master_read_byte(cmd, data, I2C_MASTER_NACK);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(I2C_MASTER_PORT, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
return ret;
}
#define PAGE_SIZE 64 // 每页大小
// 写入一页数据到 EEPROM
void eeprom_write_page(uint16_t addr, uint8_t* data, size_t len) {
// 确保写入长度不超过页面大小
if (len > PAGE_SIZE) {
len = PAGE_SIZE; // 截断长度
}
uint8_t buffer[PAGE_SIZE + 2]; // 额外的两个字节用于存储地址
buffer[0] = (addr >> 8) & 0xFF; // 地址高字节
buffer[1] = addr & 0xFF; // 地址低字节
memcpy(&buffer[2], data, len); // 将数据拷贝到缓冲区
// 发送写入命令到 EEPROM
esp_err_t ret = i2c_master_write_to_device(I2C_MASTER_PORT,EEPROM_I2C_ADDRESS, buffer, len + 2, 1000 / portTICK_PERIOD_MS);
if (ret == ESP_OK) {
// 写入成功,等待写入完成(大约 5 毫秒)
vTaskDelay(5 / portTICK_PERIOD_MS);
} else {
// 处理写入错误
}
}
void app_main() {
uint8_t data1[PAGE_SIZE] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
// 初始化 I2C
i2c_master_init();
uint8_t write_data = 0x5A; // 要写入的数据
uint16_t address = 0x65; // 24C256 的地址 0x88
// 向 EEPROM 写入数据
esp_err_t ret = eeprom_write_byte(address, write_data);
if (ret == ESP_OK) {
printf("Data 0x%X written to EEPROM at address 0x%X\n", write_data, address);
} else {
printf("Error writing data to EEPROM\n");
}
vTaskDelay(10 / portTICK_PERIOD_MS);
// 读取 EEPROM 中的数据
uint8_t read_data = 0;
ret = eeprom_read_byte(address, &read_data);
if (ret == ESP_OK) {
printf("Data 0x%X read from EEPROM at address 0x%X\n", read_data, address);
} else {
printf("Error reading data from EEPROM\n");
}
//写页
eeprom_write_page(0, data1, PAGE_SIZE); //从地址0x0开始
vTaskDelay(10 / portTICK_PERIOD_MS);
ret = eeprom_read_byte(7, &read_data); //读地址0x7
if (ret == ESP_OK) {
printf("Data 0x%X read from EEPROM at address 0x%X\n", read_data,7);
} else {
printf("Error reading data from EEPROM\n");
}
}