/********************************************************************** * Copyright: (C)2024 LingYun IoT System Studio * Author: GuoWenxue * 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 #include /* 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; }