Compare commits

...

2 Commits

4 changed files with 175 additions and 125 deletions

126
README.md
View File

@@ -1,70 +1,112 @@
# Driver_M24Cxx可移植 EEPROM 驱动) # EEPROM24xx 通用驱动模块说明
这是一个面向 M24Cxx / AT24Cxx 系列 EEPROM 的可移植驱动模块 本 README 对当前 EEPROM 模块代码做统一说明,包含:适用范围、文件职责、移植步骤
核心特性: ## 1. 适用范围
- 不依赖特定 MCU/SDK不绑定 STM32 HAL 该模块适用于 I2C 接口的 24xx 系列 EEPROM如 M24Cxx / AT24Cxx并支持
- 支持页写自动拆分
- 通过回调适配底层总线
## 目录结构 - 1 字节或 2 字节存储地址(`mem_addr_size = 1/2`
- 页写自动拆分(避免跨页写导致写入异常)
- 平台无关移植(通过用户传入读/写/延时函数)
- `module/Int_EEPROM24xx.h`:驱动接口定义 不适用于:
- `module/Int_EEPROM24xx.c`:驱动实现
- `examples/adapter_template.c`:适配层模板
## 你需要实现的 3 个函数 - 非 I2C EEPROM
- 需要 DMA/异步回调流程的复杂驱动(本模块为同步阻塞接口)
移植时,你只需要实现这三个底层函数,然后在配置结构体里挂接: ## 2. 文件说明
1. `bus_read`:从 EEPROM 读数据 ### `interface/Int_EEPROM24xx.h`
2. `bus_write`:向 EEPROM 写数据
3. `delay_ms`:毫秒延时
> 这也是本仓库提交信息里的重点:**需要自己实现读、写、延时这三个函数**。 对外头文件,定义:
## 快速接入 - 返回码:
- `INT_EEPROM_OK`
- `INT_EEPROM_ERR_ARG`
- `INT_EEPROM_ERR_IO`
- 用户需提供的函数指针类型:
- `IntEEPROM_I2CRead_t`
- `IntEEPROM_I2CWrite_t`
- `IntEEPROM_DelayMs_t`
- 配置结构体 `IntEEPROM24xx_Config_t`
- 对外 API
- `Int_EEPROM24xx_Init()`
- `Int_EEPROM24xx_Read()`
- `Int_EEPROM24xx_Write()`
### 1) 将模块加入工程 ### `interface/Int_EEPROM24xx.c`
把以下文件加入你的工程 核心实现文件,主要逻辑
- `module/Int_EEPROM24xx.h` - `Init`:校验参数、保存配置和用户回调
- `module/Int_EEPROM24xx.c` - `Read`:参数检查后调用用户读函数
- `Write`:按页拆分写入 + 每页写后延时(写周期等待)
### 2) 实现底层适配函数 ## 3. API 使用流程
参考 `examples/adapter_template.c`,按你的平台替换 I2C 读写与延时实现。
### 3) 初始化设备描述并调用 API
```c ```c
static const IntEEPROM24xx_t g_eeprom = { // 1) 实现底层 3 个函数
.ctx = your_bus_context, static IntEEPROM_Result_t my_i2c_read(uint16_t dev_addr,
uint32_t mem_addr,
uint8_t mem_addr_size,
uint8_t *buf,
uint16_t len,
uint32_t timeout_ms);
static IntEEPROM_Result_t my_i2c_write(uint16_t dev_addr,
uint32_t mem_addr,
uint8_t mem_addr_size,
const uint8_t *buf,
uint16_t len,
uint32_t timeout_ms);
static void my_delay_ms(uint32_t ms);
// 2) 填写配置并初始化
IntEEPROM24xx_Config_t cfg = {
.dev_addr = (0x50u << 1), .dev_addr = (0x50u << 1),
.page_size = 8u, .page_size = 8u,
.mem_addr_size = 1u, .mem_addr_size = 1u,
.write_cycle_ms = 6u, .write_cycle_ms = 6u,
.timeout_ms = 100u, .timeout_ms = 100u,
.bus_read = your_bus_read,
.bus_write = your_bus_write,
.delay_ms = your_delay_ms,
}; };
Int_EEPROM24xx_Read(&g_eeprom, 0x00, rx_buf, sizeof(rx_buf)); Int_EEPROM24xx_Init(&cfg, my_i2c_read, my_i2c_write, my_delay_ms);
Int_EEPROM24xx_Write(&g_eeprom, 0x00, tx_buf, sizeof(tx_buf));
// 3) 读写
Int_EEPROM24xx_Write(0x00, tx_buf, tx_len);
Int_EEPROM24xx_Read(0x00, rx_buf, rx_len);
``` ```
## 参数说明 ## 4. 如何移植到新 MCU
- `dev_addr`设备地址7bit 或左移后地址,取决于你的底层驱动规范) 只需要做 4 步:
- `page_size`:页大小(必须与芯片手册一致)
- `mem_addr_size`地址字节数1 或 2
- `write_cycle_ms`:每次页写后的写周期等待
## 常见问题 1. 拷贝 `Int_EEPROM24xx.h/.c` 到你的工程。
2. 用目标平台 SDK 实现 3 个函数(读、写、毫秒延时)。
3. 按芯片手册填写配置参数:
- `dev_addr`
- `page_size`
- `mem_addr_size`
- `write_cycle_ms`
4. 调用 `Int_EEPROM24xx_Init` 一次后,即可直接调用读写函数。
- 写入失败:优先检查 `page_size``write_cycle_ms` ## 5. 关键参数建议
- 读写偏移错乱:检查 `mem_addr_size`1 字节 / 2 字节)
- 跨平台报错:说明底层回调未按目标 SDK 正确实现。 - `page_size`:必须与 EEPROM 手册一致。
- `mem_addr_size`
- 小容量常见 1
- 大容量常见 2
- `write_cycle_ms`:建议按 datasheet通常 5~10ms。
- `dev_addr`:根据你的 I2C 驱动要求使用 7bit 地址或左移地址。
## 6. 常见问题排查
- 写后读不一致:优先检查 `write_cycle_ms` 是否太小。
- 连续写入中间数据错位:优先检查 `page_size` 是否配置错误。
- 初始化失败:检查回调函数是否传空、`mem_addr_size` 是否为 1/2。
## 7. 当前工程中的使用点
本项目在 `interface/W5500/Int_NetCfg.c` 中调用该模块,用于网络参数 EEPROM 持久化。

View File

@@ -1,18 +1,16 @@
#include "Int_EEPROM24xx.h" #include "Int_EEPROM24xx.h"
/* /*
* 这是移植模板:请把这 3 个函数替换为你的平台实现 * 移植模板:你只需要实现这 3 个函数。
*/ */
static IntEEPROM_Result_t your_bus_read(void *ctx, static IntEEPROM_Result_t your_i2c_read(uint16_t dev_addr,
uint16_t dev_addr,
uint32_t mem_addr, uint32_t mem_addr,
uint8_t mem_addr_size, uint8_t mem_addr_size,
uint8_t *buf, uint8_t *buf,
uint16_t len, uint16_t len,
uint32_t timeout_ms) uint32_t timeout_ms)
{ {
(void)ctx;
(void)dev_addr; (void)dev_addr;
(void)mem_addr; (void)mem_addr;
(void)mem_addr_size; (void)mem_addr_size;
@@ -20,19 +18,17 @@ static IntEEPROM_Result_t your_bus_read(void *ctx,
(void)len; (void)len;
(void)timeout_ms; (void)timeout_ms;
/* TODO: 替换为平台 I2C 读函数 */ /* TODO: 替换为你的平台 I2C 读实现 */
return INT_EEPROM_ERR_IO; return INT_EEPROM_ERR_IO;
} }
static IntEEPROM_Result_t your_bus_write(void *ctx, static IntEEPROM_Result_t your_i2c_write(uint16_t dev_addr,
uint16_t dev_addr,
uint32_t mem_addr, uint32_t mem_addr,
uint8_t mem_addr_size, uint8_t mem_addr_size,
const uint8_t *buf, const uint8_t *buf,
uint16_t len, uint16_t len,
uint32_t timeout_ms) uint32_t timeout_ms)
{ {
(void)ctx;
(void)dev_addr; (void)dev_addr;
(void)mem_addr; (void)mem_addr;
(void)mem_addr_size; (void)mem_addr_size;
@@ -40,36 +36,34 @@ static IntEEPROM_Result_t your_bus_write(void *ctx,
(void)len; (void)len;
(void)timeout_ms; (void)timeout_ms;
/* TODO: 替换为平台 I2C 写函数 */ /* TODO: 替换为你的平台 I2C 写实现 */
return INT_EEPROM_ERR_IO; return INT_EEPROM_ERR_IO;
} }
static void your_delay_ms(void *ctx, uint32_t delay_ms) static void your_delay_ms(uint32_t delay_ms)
{ {
(void)ctx;
(void)delay_ms; (void)delay_ms;
/* TODO: 替换为你的平台 delay 实现 */
/* TODO: 替换为平台 delay 实现 */
} }
/* 示例:设备描述 */ void eeprom_example(void)
static const IntEEPROM24xx_t g_eeprom = { {
.ctx = 0, IntEEPROM24xx_Config_t cfg = {
.dev_addr = (0x50u << 1), .dev_addr = (0x50u << 1),
.page_size = 8u, .page_size = 8u,
.mem_addr_size = 1u, .mem_addr_size = 1u,
.write_cycle_ms = 6u, .write_cycle_ms = 6u,
.timeout_ms = 100u, .timeout_ms = 100u,
.bus_read = your_bus_read, };
.bus_write = your_bus_write,
.delay_ms = your_delay_ms,
};
void eeprom_example(void) uint8_t tx[8] = {1, 2, 3, 4, 5, 6, 7, 8};
{
uint8_t tx[8] = {1,2,3,4,5,6,7,8};
uint8_t rx[8] = {0}; uint8_t rx[8] = {0};
(void)Int_EEPROM24xx_Write(&g_eeprom, 0x00, tx, sizeof(tx)); if (Int_EEPROM24xx_Init(&cfg, your_i2c_read, your_i2c_write, your_delay_ms) != INT_EEPROM_OK)
(void)Int_EEPROM24xx_Read(&g_eeprom, 0x00, rx, sizeof(rx)); {
return;
}
(void)Int_EEPROM24xx_Write(0x00, tx, sizeof(tx));
(void)Int_EEPROM24xx_Read(0x00, rx, sizeof(rx));
} }

View File

@@ -1,18 +1,47 @@
#include "Int_EEPROM24xx.h" #include "Int_EEPROM24xx.h"
/* 直接透传到用户提供的总线读回调。 */ static IntEEPROM24xx_Config_t g_cfg;
IntEEPROM_Result_t Int_EEPROM24xx_Read(const IntEEPROM24xx_t *dev, uint32_t mem_addr, uint8_t *buf, uint16_t len) static IntEEPROM_I2CRead_t g_read_fn;
static IntEEPROM_I2CWrite_t g_write_fn;
static IntEEPROM_DelayMs_t g_delay_fn;
static uint8_t g_inited;
IntEEPROM_Result_t Int_EEPROM24xx_Init(const IntEEPROM24xx_Config_t *cfg,
IntEEPROM_I2CRead_t read_fn,
IntEEPROM_I2CWrite_t write_fn,
IntEEPROM_DelayMs_t delay_fn)
{ {
if (dev == NULL || buf == NULL || len == 0u || dev->bus_read == NULL) if (cfg == NULL || read_fn == 0 || write_fn == 0)
{ {
return INT_EEPROM_ERR_ARG; return INT_EEPROM_ERR_ARG;
} }
return dev->bus_read(dev->ctx, dev->dev_addr, mem_addr, dev->mem_addr_size, buf, len, dev->timeout_ms); if (cfg->page_size == 0u || (cfg->mem_addr_size != 1u && cfg->mem_addr_size != 2u))
{
return INT_EEPROM_ERR_ARG;
}
g_cfg = *cfg;
g_read_fn = read_fn;
g_write_fn = write_fn;
g_delay_fn = delay_fn;
g_inited = 1u;
return INT_EEPROM_OK;
}
/* 直接透传到用户提供的总线读回调。 */
IntEEPROM_Result_t Int_EEPROM24xx_Read(uint32_t mem_addr, uint8_t *buf, uint16_t len)
{
if (g_inited == 0u || buf == 0 || len == 0u)
{
return INT_EEPROM_ERR_ARG;
}
return g_read_fn(g_cfg.dev_addr, mem_addr, g_cfg.mem_addr_size, buf, len, g_cfg.timeout_ms);
} }
/* 按页拆分写入,避免跨页写导致 EEPROM 数据回卷或写失败。 */ /* 按页拆分写入,避免跨页写导致 EEPROM 数据回卷或写失败。 */
IntEEPROM_Result_t Int_EEPROM24xx_Write(const IntEEPROM24xx_t *dev, uint32_t mem_addr, const uint8_t *buf, uint16_t len) IntEEPROM_Result_t Int_EEPROM24xx_Write(uint32_t mem_addr, const uint8_t *buf, uint16_t len)
{ {
uint16_t written = 0; uint16_t written = 0;
uint16_t chunk; uint16_t chunk;
@@ -21,7 +50,7 @@ IntEEPROM_Result_t Int_EEPROM24xx_Write(const IntEEPROM24xx_t *dev, uint32_t mem
uint16_t room; uint16_t room;
IntEEPROM_Result_t st; IntEEPROM_Result_t st;
if (dev == NULL || buf == NULL || len == 0u || dev->page_size == 0u || dev->bus_write == NULL) if (g_inited == 0u || buf == 0 || len == 0u)
{ {
return INT_EEPROM_ERR_ARG; return INT_EEPROM_ERR_ARG;
} }
@@ -30,27 +59,26 @@ IntEEPROM_Result_t Int_EEPROM24xx_Write(const IntEEPROM24xx_t *dev, uint32_t mem
{ {
/* 计算当前页内偏移和本次最多可写长度。 */ /* 计算当前页内偏移和本次最多可写长度。 */
cur_addr = mem_addr + written; cur_addr = mem_addr + written;
page_off = (uint16_t)(cur_addr % dev->page_size); page_off = (uint16_t)(cur_addr % g_cfg.page_size);
room = (uint16_t)(dev->page_size - page_off); room = (uint16_t)(g_cfg.page_size - page_off);
chunk = (uint16_t)(((len - written) < room) ? (len - written) : room); chunk = (uint16_t)(((len - written) < room) ? (len - written) : room);
/* 实际总线写由平台适配层完成。 */ /* 实际总线写由平台适配层完成。 */
st = dev->bus_write(dev->ctx, st = g_write_fn(g_cfg.dev_addr,
dev->dev_addr,
cur_addr, cur_addr,
dev->mem_addr_size, g_cfg.mem_addr_size,
(const uint8_t *)(buf + written), (const uint8_t *)(buf + written),
chunk, chunk,
dev->timeout_ms); g_cfg.timeout_ms);
if (st != INT_EEPROM_OK) if (st != INT_EEPROM_OK)
{ {
return st; return st;
} }
if (dev->delay_ms != NULL && dev->write_cycle_ms > 0u) if (g_delay_fn != 0 && g_cfg.write_cycle_ms > 0u)
{ {
/* EEPROM 页写后通常需要写周期时间。 */ /* EEPROM 页写后通常需要写周期时间。 */
dev->delay_ms(dev->ctx, dev->write_cycle_ms); g_delay_fn(g_cfg.write_cycle_ms);
} }
written = (uint16_t)(written + chunk); written = (uint16_t)(written + chunk);
} }

View File

@@ -2,7 +2,6 @@
#define INT_EEPROM24XX_H #define INT_EEPROM24XX_H
#include <stdint.h> #include <stdint.h>
#include <stddef.h>
typedef int32_t IntEEPROM_Result_t; typedef int32_t IntEEPROM_Result_t;
@@ -10,51 +9,38 @@ typedef int32_t IntEEPROM_Result_t;
#define INT_EEPROM_ERR_ARG -1 #define INT_EEPROM_ERR_ARG -1
#define INT_EEPROM_ERR_IO -2 #define INT_EEPROM_ERR_IO -2
/* 底层总线读回调: typedef IntEEPROM_Result_t (*IntEEPROM_I2CRead_t)(uint16_t dev_addr,
* ctx : 用户上下文(可传 I2C 句柄、驱动实例等)
* dev_addr : 器件地址(格式由你的平台驱动决定)
* mem_addr : EEPROM 内存地址
* mem_addr_size : 地址字节数1 或 2
* timeout_ms : 访问超时(毫秒)
*/
typedef IntEEPROM_Result_t (*IntEEPROM_BusRead_t)(void *ctx,
uint16_t dev_addr,
uint32_t mem_addr, uint32_t mem_addr,
uint8_t mem_addr_size, uint8_t mem_addr_size,
uint8_t *buf, uint8_t *buf,
uint16_t len, uint16_t len,
uint32_t timeout_ms); uint32_t timeout_ms);
/* 底层总线写回调,参数含义同读回调 */ typedef IntEEPROM_Result_t (*IntEEPROM_I2CWrite_t)(uint16_t dev_addr,
typedef IntEEPROM_Result_t (*IntEEPROM_BusWrite_t)(void *ctx,
uint16_t dev_addr,
uint32_t mem_addr, uint32_t mem_addr,
uint8_t mem_addr_size, uint8_t mem_addr_size,
const uint8_t *buf, const uint8_t *buf,
uint16_t len, uint16_t len,
uint32_t timeout_ms); uint32_t timeout_ms);
typedef void (*IntEEPROM_DelayMs_t)(void *ctx, uint32_t delay_ms); typedef void (*IntEEPROM_DelayMs_t)(uint32_t delay_ms);
typedef struct typedef struct
{ {
void *ctx; /* 用户上下文 */ uint16_t dev_addr;
uint16_t dev_addr; /* 器件地址 */ uint16_t page_size;
uint16_t page_size; /* 页大小(字节) */ uint8_t mem_addr_size; /* 1 or 2 */
uint8_t mem_addr_size; /* 地址字节数1 或 2 */ uint32_t write_cycle_ms;
uint32_t write_cycle_ms; /* 每次页写后等待时间ms */ uint32_t timeout_ms;
uint32_t timeout_ms; /* 总线访问超时ms */ } IntEEPROM24xx_Config_t;
IntEEPROM_BusRead_t bus_read;
IntEEPROM_BusWrite_t bus_write;
IntEEPROM_DelayMs_t delay_ms;
} IntEEPROM24xx_t;
/* 读取 EEPROM。 /* 初始化模块:用户传入 I2C 读、写和毫秒延时函数后,即可直接调用读写 API。 */
* 返回 INT_EEPROM_OK 表示成功。 IntEEPROM_Result_t Int_EEPROM24xx_Init(const IntEEPROM24xx_Config_t *cfg,
*/ IntEEPROM_I2CRead_t read_fn,
IntEEPROM_Result_t Int_EEPROM24xx_Read(const IntEEPROM24xx_t *dev, uint32_t mem_addr, uint8_t *buf, uint16_t len); IntEEPROM_I2CWrite_t write_fn,
IntEEPROM_DelayMs_t delay_fn);
/* 写入 EEPROM内部会自动按页拆分写入。 */ IntEEPROM_Result_t Int_EEPROM24xx_Read(uint32_t mem_addr, uint8_t *buf, uint16_t len);
IntEEPROM_Result_t Int_EEPROM24xx_Write(const IntEEPROM24xx_t *dev, uint32_t mem_addr, const uint8_t *buf, uint16_t len); IntEEPROM_Result_t Int_EEPROM24xx_Write(uint32_t mem_addr, const uint8_t *buf, uint16_t len);
#endif #endif