/* * 模块名称:AT24C02 EEPROM 驱动 * 模块功能:提供AT24C02的读写操作接口,基于STM32 HAL I2C * 适用平台:STM32F103C8T6 * 作者:王建锋 * 创建日期:2026-07-14 * 修改记录: * 2026-07-14 王建锋 创建初始版本 */ #include "at24c02.h" #include /* 引用CubeMX生成的I2C句柄 */ extern I2C_HandleTypeDef hi2c1; /* * 函数功能:AT24C02字节写入 * 入口参数:addr - 目标地址(0~255) * data - 要写入的数据 * 返回值: 0=成功,负值=错误码 * 函数说明:先发送写命令,再等待内部写周期完成 */ int at24c02_write_byte(uint8_t addr, uint8_t data) { uint8_t buf[2]; HAL_StatusTypeDef hal_ret; buf[0] = addr; buf[1] = data; hal_ret = HAL_I2C_Master_Transmit(&hi2c1, AT24C02_ADDR_WRITE, buf, 2, AT24C02_I2C_TIMEOUT); if (hal_ret != HAL_OK) { return AT24C02_ERR_I2C; } /* 等待内部写周期完成 */ return at24c02_wait_ready(); } /* * 函数功能:AT24C02随机读取 * 入口参数:addr - 目标地址(0~255) * 返回值:>=0=读取的数据,负值=错误码 * 函数说明:假写操作设定地址,再重新发送读命令读取 */ int at24c02_read_byte(uint8_t addr) { uint8_t data; HAL_StatusTypeDef hal_ret; /* 假写:发送设备地址 + 字地址 */ hal_ret = HAL_I2C_Master_Transmit(&hi2c1, AT24C02_ADDR_WRITE, &addr, 1, AT24C02_I2C_TIMEOUT); if (hal_ret != HAL_OK) { return AT24C02_ERR_I2C; } /* 重发起始条件 + 读 */ hal_ret = HAL_I2C_Master_Receive(&hi2c1, AT24C02_ADDR_READ, &data, 1, AT24C02_I2C_TIMEOUT); if (hal_ret != HAL_OK) { return AT24C02_ERR_I2C; } return data; } /* * 函数功能:AT24C02页写入 * 入口参数:addr - 起始地址(0~255) * p_data - 数据缓冲区指针 * len - 写入长度(≤8) * 返回值: 0=成功,负值=错误码 * 函数说明:页写入,自动等待写周期完成 */ int at24c02_write_page(uint8_t addr, const uint8_t *p_data, uint8_t len) { uint8_t buf[AT24C02_PAGE_SIZE + 1]; HAL_StatusTypeDef hal_ret; uint8_t i; /* 参数校验 */ if ((p_data == NULL) || (len == 0) || (len > AT24C02_PAGE_SIZE)) { return AT24C02_ERR_PARAM; } if (addr + len > AT24C02_SIZE) { return AT24C02_ERR_PARAM; } buf[0] = addr; for (i = 0; i < len; i++) { buf[i + 1] = p_data[i]; } hal_ret = HAL_I2C_Master_Transmit(&hi2c1, AT24C02_ADDR_WRITE, buf, len + 1, AT24C02_I2C_TIMEOUT); if (hal_ret != HAL_OK) { return AT24C02_ERR_I2C; } /* 等待内部写周期完成 */ return at24c02_wait_ready(); } /* * 函数功能:AT24C02连续写入(跨页自动拆分) * 入口参数:addr - 起始地址(0~255) * p_data - 数据缓冲区指针 * len - 写入长度 * 返回值: 0=成功,负值=错误码 * 函数说明:自动处理跨页边界,逐页写入 */ int at24c02_write_buf(uint8_t addr, const uint8_t *p_data, uint16_t len) { uint8_t page_remaining; int ret; if ((p_data == NULL) || (len == 0)) { return AT24C02_ERR_PARAM; } if (addr + len > AT24C02_SIZE) { return AT24C02_ERR_PARAM; } while (len > 0) { /* 当前页剩余空间 */ page_remaining = AT24C02_PAGE_SIZE - (addr % AT24C02_PAGE_SIZE); if (page_remaining > len) { page_remaining = (uint8_t)len; } ret = at24c02_write_page(addr, p_data, page_remaining); if (ret != AT24C02_OK) { return ret; } addr += page_remaining; p_data += page_remaining; len -= page_remaining; } return AT24C02_OK; } /* * 函数功能:AT24C02连续读取 * 入口参数:addr - 起始地址(0~255) * p_buf - 接收缓冲区指针 * len - 读取长度 * 返回值: 0=成功,负值=错误码 * 函数说明:从指定地址开始顺序读取多个字节 */ int at24c02_read_buf(uint8_t addr, uint8_t *p_buf, uint16_t len) { HAL_StatusTypeDef hal_ret; if ((p_buf == NULL) || (len == 0)) { return AT24C02_ERR_PARAM; } if (addr + len > AT24C02_SIZE) { return AT24C02_ERR_PARAM; } /* 假写:设定读起始地址 */ hal_ret = HAL_I2C_Master_Transmit(&hi2c1, AT24C02_ADDR_WRITE, &addr, 1, AT24C02_I2C_TIMEOUT); if (hal_ret != HAL_OK) { return AT24C02_ERR_I2C; } /* 顺序读 */ hal_ret = HAL_I2C_Master_Receive(&hi2c1, AT24C02_ADDR_READ, p_buf, len, AT24C02_I2C_TIMEOUT); if (hal_ret != HAL_OK) { return AT24C02_ERR_I2C; } return AT24C02_OK; } /* * 函数功能:检测AT24C02设备是否存在 * 入口参数:无 * 返回值: 0=检测到设备,-1=未响应 * 函数说明:通过I2C发送设备地址,检查是否有ACK应答 */ int at24c02_scan(void) { HAL_StatusTypeDef hal_ret; hal_ret = HAL_I2C_IsDeviceReady(&hi2c1, AT24C02_ADDR_WRITE, 3, AT24C02_I2C_TIMEOUT); if (hal_ret != HAL_OK) { printf("AT24C02: no device found\r\n"); return AT24C02_ERR_DEVICE; } printf("AT24C02: device detected\r\n"); return AT24C02_OK; } /* * 函数功能:等待内部写周期完成(应答查询) * 入口参数:无 * 返回值: 0=完成,-3=超时 * 函数说明:发送设备地址(写模式),直到器件应答 */ int at24c02_wait_ready(void) { HAL_StatusTypeDef hal_ret; uint16_t trials = 50; while (trials--) { hal_ret = HAL_I2C_IsDeviceReady(&hi2c1, AT24C02_ADDR_WRITE, 1, 1); if (hal_ret == HAL_OK) { return AT24C02_OK; } HAL_Delay(1); } return AT24C02_ERR_TIMEOUT; }