guowenxue
4 days ago 53de825c4c4d8325c02341177f62ab84897eb5ee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
/**********************************************************************
* Copyright: (C)2024 LingYun IoT System Studio
* Author: GuoWenxue<guowenxue@gmail.com>
* Description: I2C EEPROM device driver for ISKBoard (Internal Lock Version)
*
* ChangeLog:
* Version    Date       Author            Description
* V1.5.0  2026.07.08    GuoWenxue         Use goto cleanup for unified lock releasing
***********************************************************************/
 
#include "eeprom.h"
#include "miscdev.h" /* 提供 mdelay() / udelay() */
#include <stdio.h>
#include <string.h>
 
 
/* EEPROM 型号表:未来若有新芯片,只需在此处追加一行,其余代码不用更改 */
static const eeprom_info_t eeprom_ids[] = {
    { EEPROM_INFO("AT24C02",  256,   8,  1) },
    { EEPROM_INFO("AT24C04",  512,   16, 1) },
    { EEPROM_INFO("AT24C08",  1024,  16, 1) },
    { EEPROM_INFO("AT24C16",  2048,  16, 1) },
    { EEPROM_INFO("AT24C32",  4096,  32, 2) },
    { EEPROM_INFO("AT24C64",  8192,  32, 2) },
    { EEPROM_INFO("AT24C128", 16384, 64, 2) },
    { EEPROM_INFO("AT24C256", 32768, 64, 2) },
};
#define EEPROM_IDS_NUM  (sizeof(eeprom_ids) / sizeof(eeprom_ids[0]))
 
/**
 * @brief  根据芯片型号名称内部查找匹配的型号
 */
static const eeprom_info_t *eeprom_find_info_by_name(const char *name)
{
    uint32_t i;
    for (i = 0; i < EEPROM_IDS_NUM; i++)
    {
        if (strcmp(eeprom_ids[i].name, name) == 0)
        {
            return &eeprom_ids[i];
        }
    }
    return NULL;
}
 
/**
 * @brief  初始化 EEPROM 设备参数(通过型号名称自动查表匹配)
 */
int eeprom_init(eeprom_dev_t *eeprom, i2c_bus_t *bus, uint8_t dev_addr, const char *chip_name)
{
    const eeprom_info_t *info = eeprom_find_info_by_name(chip_name);
 
    if (!info || !eeprom || !bus)
    {
        printf("EEPROM: Named device [%s] not supported!\r\n", chip_name ? chip_name : "NULL");
        return -1;
    }
 
    eeprom->bus = bus;
    eeprom->dev_addr = dev_addr;
    eeprom->info = info;
 
    printf("Initialized EEPROM(%s), Capacity: %ld Bytes, Page Size: %ld\r\n",
            info->name, info->capacity, info->page_size);
    return 0;
}
 
/**
 * @brief  EEPROM 测试函数
 */
int eeprom_test(i2c_bus_t *bus)
{
    eeprom_dev_t my_at24c256;
    uint8_t tx_buf[100];
    uint8_t rx_buf[100];
    int i;
    int rv = -1;
 
    /* 1. 初始化 EEPROM 参数 */
    if (eeprom_init(&my_at24c256, bus, 0x50, "AT24C256") != 0)
    {
        return -1;
    }
 
    /* 2. 填充测试数据 */
    for (i = 0; i < sizeof(tx_buf); i++)
    {
        tx_buf[i] = i;
    }
 
    /* 3. 测试跨页连续写 */
    if (eeprom_write(&my_at24c256, 0x00A0, tx_buf, sizeof(tx_buf)) != I2C_OK)
    {
        goto cleanup;
    }
 
    /* 4. 测试任意地址连续读 */
    if (eeprom_read(&my_at24c256, 0x00A0, rx_buf, sizeof(rx_buf)) != I2C_OK)
    {
        goto cleanup;
    }
 
    /* 5. 数据一致性校验 */
    if (memcmp(tx_buf, rx_buf, sizeof(tx_buf)) == 0)
    {
        rv = 0;
    }
 
cleanup:
    printf("AT24C256 Read/Write Test %s!\r\n", rv == 0 ? "Passed" : "Failed");
    return rv;
}
 
/**
 * @brief  24C16仅支持单字节地址(可寻址256字节),需借用I2C设备地址的低3位充当片内地址的高3位。
 */
static inline uint8_t eeprom_get_actual_addr(eeprom_dev_t *eeprom, uint16_t reg_addr)
{
    uint8_t actual_dev_addr = eeprom->dev_addr;
    if (eeprom->info->addr_bytes == 1 && eeprom->info->capacity > 256)
    {
        actual_dev_addr |= (uint8_t)((reg_addr >> 8) & 0x07);
    }
    return actual_dev_addr;
}
 
/**
 * @brief  从 EEPROM 任意地址读取指定长度数据
 */
i2c_status_t eeprom_read(eeprom_dev_t *eeprom, uint16_t reg_addr, uint8_t *buf, uint16_t len)
{
    uint8_t addr_buf[2];
    uint8_t addr_len = 0;
    i2c_msg_t msgs[2];
    i2c_status_t status;
 
    if ( len == 0 || !buf || !eeprom || !eeprom->bus || !eeprom->info )
    {
        return I2C_OK;
    }
 
    /* 1. 准备发送数据 */
    if (eeprom->info->addr_bytes == 2)
    {
        addr_buf[addr_len++] = (uint8_t)(reg_addr >> 8);
    }
    addr_buf[addr_len++] = (uint8_t)(reg_addr & 0xFF);
 
    /* 2. 封装 I2C 消息 */
    msgs[0].addr  = eeprom_get_actual_addr(eeprom, reg_addr);
    msgs[0].flags = I2C_M_WR;
    msgs[0].len   = addr_len;
    msgs[0].buf   = addr_buf;
 
    msgs[1].addr  = msgs[0].addr;
    msgs[1].flags = I2C_M_RD;
    msgs[1].len   = len;
    msgs[1].buf   = buf;
 
    /* 3. 锁住 I2C 总线  */
    if (i2c_lock(eeprom->bus) != I2C_OK)
    {
        return I2C_BUSY;
    }
 
    /* 4. 调用 i2c_transfer 发送消息 */
    status = i2c_transfer(eeprom->bus, msgs, 2);
 
    /* 5. 释放 I2C 总线 */
    i2c_unlock(eeprom->bus);
 
    return status;
}
 
/**
 * @brief  轮询 EEPROM 硬件内部擦写就绪状态
 */
static void eeprom_wait_standby(eeprom_dev_t *eeprom, uint16_t reg_addr)
{
    i2c_msg_t msg;
    uint32_t timeout = 2000;
 
    msg.addr  = eeprom_get_actual_addr(eeprom, reg_addr);
    msg.flags = I2C_M_WR;
    msg.len   = 0;
    msg.buf   = NULL;
 
    while (timeout--)
    {
        if (i2c_transfer(eeprom->bus, &msg, 1) == I2C_OK)
        {
            break; /* 芯片返回 ACK,擦写完成,退出轮询 */
        }
        udelay(50);
    }
}
 
/**
 * @brief  向 EEPROM 写入单页数据
 */
static i2c_status_t eeprom_write_page(eeprom_dev_t *eeprom, uint16_t reg_addr, uint8_t *buf, uint16_t len)
{
    uint8_t tx_payload[128];
    uint8_t addr_len = 0;
    i2c_msg_t msg;
    i2c_status_t status = I2C_OK;
 
    /* 1. 准备发送数据 */
    if (eeprom->info->addr_bytes == 2)
    {
        tx_payload[addr_len++] = (uint8_t)(reg_addr >> 8);
    }
    tx_payload[addr_len++] = (uint8_t)(reg_addr & 0xFF);
    memcpy(&tx_payload[addr_len], buf, len);
 
    msg.addr  = eeprom_get_actual_addr(eeprom, reg_addr);
    msg.flags = I2C_M_WR;
    msg.len   = addr_len + len;
    msg.buf   = tx_payload;
 
    /* 2. 锁住 I2C 总线 */
    if (i2c_lock(eeprom->bus) != I2C_OK)
    {
        return I2C_BUSY;
    }
 
    /* 3. 调用 i2c_transfer 发送消息 */
    status = i2c_transfer(eeprom->bus, &msg, 1);
    if (status != I2C_OK)
    {
        status = I2C_ERROR;
        goto cleanup;
    }
 
    /* 4. 轮询等待 EEPROM 内部擦写完成 */
    eeprom_wait_standby(eeprom, reg_addr);
 
cleanup:
    /* 5. 释放 I2C 总线 */
    i2c_unlock(eeprom->bus);
 
    return status;
}
 
/**
 * @brief  大批量数据动态跨页分包连续写入
 */
i2c_status_t eeprom_write(eeprom_dev_t *eeprom, uint16_t reg_addr, uint8_t *buf, uint16_t len)
{
    i2c_status_t status = I2C_OK;
    uint16_t count = 0;
    uint16_t page_offset;
    uint16_t bytes_to_write;
 
    if ( len == 0 || !buf || !eeprom || !eeprom->bus || !eeprom->info )
    {
        return I2C_OK;
    }
 
    while (len > 0)
    {
        page_offset = reg_addr % eeprom->info->page_size;
 
        bytes_to_write = eeprom->info->page_size - page_offset;
        if (bytes_to_write > len)
        {
            bytes_to_write = len;
        }
 
        /* 内部单页级连写带等就绪的一体化锁定 */
        status = eeprom_write_page(eeprom, reg_addr, &buf[count], bytes_to_write);
        if (status != I2C_OK)
        {
            goto cleanup;
        }
 
        reg_addr += bytes_to_write;
        count    += bytes_to_write;
        len      -= bytes_to_write;
    }
 
cleanup:
    return status;
}