#include "24c02.h" #include "iic.h" #include "app_loop.h" /* storage_lock/unlock:跨任务访问互斥 */ #include #include /* MQTT 配置参数 — 首次上电自动写入 EEPROM * 所有默认值来自 main.h 的 MQTT_xxx 宏,保证全局唯一配置源 */ MqttInfo_Str MqttInfoStr = { .ClientID = MQTT_CLIENT_ID, .Username = MQTT_USERNAME, .Passward = MQTT_PASSWORD, .ServerIP = MQTT_SERVER_IP, .Topic = MQTT_TOPIC, .ServerPort = MQTT_SERVER_PORT, .Relay_State = {0}, .Magic = 0, /* 首次上电由 eepromReadInfo() 写入 EEPROM_MAGIC */ }; /*-------------------------------------------------*/ /*读取AT24C02指定地址的一个字节数据 */ /*输入参数:ReadAddr:需要读取数据的地址 */ /*返回值:读取到的数据 */ /*-------------------------------------------------*/ uint8_t m24c02ReadOneByte(uint8_t ReadAddr) { uint8_t receive; uint8_t err = 0; iicStart(); err |= iicSendByteAck(M24C02_WADDR); err |= iicSendByteAck(ReadAddr); iicStart(); err |= iicSendByteAck(M24C02_RADDR); if (err) { iicStop(); log_warn("> EE 读初始化错误 err=0x%02X", err); return 0xFF; } receive=iicReadByte(0); iicStop(); HAL_Delay(5); return receive; } /*-------------------------------------------------*/ /*函数名:向指定地址写入一个数据 */ /*输入参数:WriteAddr :写入数据的目的地址 */ /*输入参数:DataToWrite:要写入的数据 */ /*返回值:0=成功, 1=无ACK(设备未响应) */ /*-------------------------------------------------*/ uint8_t m24c02WriteOneByte(uint8_t WriteAddr,uint8_t DataToWrite) { uint8_t ack_err = 0; uint8_t retry = 3; while (retry--) { ack_err = 0; iicStart(); ack_err |= iicSendByteAck(M24C02_WADDR); ack_err |= iicSendByteAck(WriteAddr); ack_err |= iicSendByteAck(DataToWrite); iicStop(); HAL_Delay(10); if (ack_err == 0) break; log_warn("> EE 写入错误 @%u, 重试=%u", WriteAddr, retry); HAL_Delay(5); } return ack_err; } /*-------------------------------------------------*/ /*函数名:从指定的地址读取指定长度的数据 */ /*输入参数:ReadAddr:需要读取数据的地址 */ /*输入参数:Redata:保存数据的缓冲区 */ /*输入参数:Redatalen:读取数据的长度 */ /*返回值:0=成功, 1=设备无ACK */ /*-------------------------------------------------*/ uint8_t m24c02ReadSequential(uint8_t ReadAddr,uint8_t *Redata,uint16_t Redatalen) { uint16_t i; uint8_t ack_err = 0; iicStart(); ack_err |= iicSendByteAck(M24C02_WADDR); ack_err |= iicSendByteAck(ReadAddr); iicStart(); ack_err |= iicSendByteAck(M24C02_RADDR); for(i=0; i<(Redatalen-1); i++) { Redata[i]=iicReadByte(1); } Redata[Redatalen-1]=iicReadByte(0); iicStop(); HAL_Delay(5); return ack_err; } /*-------------------------------------------------*/ /*函数名:从EEprom指定的地址读取数据 */ /*输入参数:addr:需要读取数据的地址 */ /*输入参数:data:读取数据缓冲区 */ /*输入参数:len:要读取的数据长度 */ /*返回值:0=成功, 1=设备无ACK */ /*-------------------------------------------------*/ uint8_t eepromReadData(uint8_t addr,void *data, uint16_t len) { uint8_t *ptr; uint8_t ret; ptr = data; storage_lock(); ret = m24c02ReadSequential(addr,ptr,len); storage_unlock(); return ret; } /*-------------------------------------------------*/ /*函数名:向 24C02 写入同一页内的若干字节 */ /*输入参数:WriteAddr:起始地址 */ /* data :数据缓冲区 */ /* len :长度(不得跨页, 页大小 8 字节) */ /*返回值:0=成功, 1=无ACK(设备未响应) */ /*-------------------------------------------------*/ #define M24C02_PAGE_SIZE 8u static uint8_t m24c02WritePage(uint8_t WriteAddr, const uint8_t *data, uint8_t len) { uint8_t ack_err = 0; uint8_t retry = 3; uint8_t i; while (retry--) { ack_err = 0; iicStart(); ack_err |= iicSendByteAck(M24C02_WADDR); ack_err |= iicSendByteAck(WriteAddr); for (i = 0; i < len && ack_err == 0; i++) { ack_err |= iicSendByteAck(data[i]); } iicStop(); HAL_Delay(10); if (ack_err == 0) break; log_warn("> EE 页写入错误 @%u, 重试=%u", WriteAddr, retry); HAL_Delay(5); } return ack_err; } /*-------------------------------------------------*/ /*函数名:EEprom指定地址写入指定字节的数据 */ /* 按页边界切分, 页写入, 每页一个写周期 */ /*输入参数:addr:地址 */ /*输入参数:data:写入的数据 */ /*输入参数:len:写入数据的长度 */ /*返回值:0=成功, 1=有页写入失败 */ /*-------------------------------------------------*/ uint8_t eepromWriteData(uint8_t addr, void *data, uint16_t len) { uint8_t *ptr = data; uint8_t err = 0; storage_lock(); while (len > 0) { /* 当前地址到页边界的距离, 避免跨页回卷 */ uint8_t chunk = M24C02_PAGE_SIZE - (addr % M24C02_PAGE_SIZE); if (chunk > len) chunk = (uint8_t)len; uint8_t e = m24c02WritePage(addr, ptr, chunk); if (e) { log_warn("> EE 写入失败 @%u, err=0x%02X", addr, e); } err |= e; addr += chunk; ptr += chunk; len -= chunk; } storage_unlock(); if (err) { log_warn("> EEPROM 写入总 ACK 错误=0x%02X", err); } else { log_info("> EEPROM 写入成功"); } return err; } /*函数名:从EEprom读取或验证MQTT配置信息 */ /*输入参数:无 */ /*返回值:无 */ /*-------------------------------------------------*/ void eepromReadInfo(void) { MqttInfo_Str tmp_read; uint8_t read_err; /* 先做一次单字节读写探测,确认IIC基本通信正常 */ uint8_t probe_wr = 0xA5; m24c02WriteOneByte(250, probe_wr); uint8_t probe_rd = m24c02ReadOneByte(250); log_info("> EEPROM 探测: 写=0x%02X, 读=0x%02X", probe_wr, probe_rd); /* 从EEPROM读取配置 */ memset(&MqttInfoStr, 0, MQTT_STRUCT_LEN); read_err = eepromReadData(0, &MqttInfoStr, MQTT_STRUCT_LEN); log_info("> EEPROM 读取 err=%d, Magic=0x%08X, Ver=%u", read_err, MqttInfoStr.Magic, MqttInfoStr.Ver); /* MQTT字段与编译时常量比对 (Ver由OTA管理, 不参与校验) */ if (MqttInfoStr.Magic == EEPROM_MAGIC && strcmp(MqttInfoStr.ClientID, MQTT_CLIENT_ID) == 0 && strcmp(MqttInfoStr.Username, MQTT_USERNAME) == 0 && strcmp(MqttInfoStr.Passward, MQTT_PASSWORD) == 0 && strcmp(MqttInfoStr.ServerIP, MQTT_SERVER_IP) == 0 && strcmp(MqttInfoStr.Topic, MQTT_TOPIC) == 0 && MqttInfoStr.ServerPort == MQTT_SERVER_PORT) { log_info("EEPROM 配置有效, Ver=%u", MqttInfoStr.Ver); } else { /* 配置损坏/变更/未初始化 */ uint16_t savedVer = MqttInfoStr.Ver; int magic_ok = (MqttInfoStr.Magic == EEPROM_MAGIC); memset(&MqttInfoStr, 0, MQTT_STRUCT_LEN); strcpy(MqttInfoStr.ClientID, MQTT_CLIENT_ID); strcpy(MqttInfoStr.Username, MQTT_USERNAME); strcpy(MqttInfoStr.Passward, MQTT_PASSWORD); strcpy(MqttInfoStr.ServerIP, MQTT_SERVER_IP); strcpy(MqttInfoStr.Topic, MQTT_TOPIC); MqttInfoStr.ServerPort = MQTT_SERVER_PORT; /* 魔数匹配→保留EEPROM版本号; 魔数不匹配→归0(新板/重置) */ MqttInfoStr.Ver = magic_ok ? savedVer : 0; /* 新板/配置重置时,继电器默认全关(0),不保留空白EEPROM的0xFF */ memset(MqttInfoStr.Relay_State, 0, sizeof(MqttInfoStr.Relay_State)); MqttInfoStr.Magic = EEPROM_MAGIC; eepromWriteData(0, &MqttInfoStr, MQTT_STRUCT_LEN); log_info("> EEPROM 写入完成, Magic=0x%08X", MqttInfoStr.Magic); /* 立即回读验证 */ memset(&tmp_read, 0, MQTT_STRUCT_LEN); read_err = eepromReadData(0, &tmp_read, MQTT_STRUCT_LEN); log_info("> EEPROM 回读校验 err=%d, Magic=0x%08X, Ver=%u", read_err, tmp_read.Magic, tmp_read.Ver); log_info("EEPROM 配置已修复, Ver=%u", MqttInfoStr.Ver); } }