#include "24c02.h" #include "iic.h" #include "app_loop.h" /* storage_lock/unlock:跨任务访问互斥 */ #include #include /* MQTT 配置参数 — 设备身份只存 EEPROM(由蓝牙 SETMQTT 灌入),固件不含身份。 * 服务器地址/端口/主题默认取自 main.h 宏,也可用蓝牙 SETIP 修改 */ uint8_t g_mqtt_configured = 0; /* 1=EEPROM 中已有有效设备身份 */ MqttInfo_Str MqttInfoStr = { .ClientID = "", .Username = "", .Passward = "", .ServerIP = MQTT_SERVER_IP, .Topic = MQTT_TOPIC, .ServerPort = MQTT_SERVER_PORT, .Relay_State = {0}, .Magic = 0, /* 身份写入(SETMQTT)时置 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) { uint8_t read_err; /* 先做一次单字节读写探测,确认IIC基本通信正常(不打印,异常时才报) */ uint8_t probe_wr = 0xA5; m24c02WriteOneByte(250, probe_wr); uint8_t probe_rd = m24c02ReadOneByte(250); if (probe_rd != probe_wr) { log_warn("> EEPROM 探测异常: 写=0x%02X, 读=0x%02X", probe_wr, probe_rd); } /* 从EEPROM读取配置(不打印,err 异常时才报) */ memset(&MqttInfoStr, 0, MQTT_STRUCT_LEN); read_err = eepromReadData(0, &MqttInfoStr, MQTT_STRUCT_LEN); if (read_err != 0) { log_warn("> EEPROM 读取异常 err=%d", read_err); } /* 配置有效 = 魔数匹配 且 客户端ID非空(身份只认 EEPROM,不再与编译宏比对) */ if (MqttInfoStr.Magic == EEPROM_MAGIC && MqttInfoStr.ClientID[0] != 0) { g_mqtt_configured = 1; log_info("EEPROM 配置有效, Ver=%u", MqttInfoStr.Ver); return; } /* 未配置:等待蓝牙 SETMQTT 灌入(不写 EEPROM) */ g_mqtt_configured = 0; if (MqttInfoStr.Magic != EEPROM_MAGIC) { /* 全新板/整片擦除(全0xFF):全部回编译默认值,版本号/继电器状态归 0 */ memset(&MqttInfoStr, 0, MQTT_STRUCT_LEN); strcpy(MqttInfoStr.ServerIP, MQTT_SERVER_IP); strcpy(MqttInfoStr.Topic, MQTT_TOPIC); MqttInfoStr.ServerPort = MQTT_SERVER_PORT; } else { /* 有 Magic 但无身份(如只 SETIP 过):保留 EEPROM 服务器配置,身份留空; * 服务器字段防御:首字符非数字视为脏数据,回默认值 */ MqttInfoStr.ClientID[0] = 0; MqttInfoStr.Username[0] = 0; MqttInfoStr.Passward[0] = 0; if (MqttInfoStr.ServerIP[0] < '0' || MqttInfoStr.ServerIP[0] > '9') { strcpy(MqttInfoStr.ServerIP, MQTT_SERVER_IP); strcpy(MqttInfoStr.Topic, MQTT_TOPIC); MqttInfoStr.ServerPort = MQTT_SERVER_PORT; } } log_warn("> EEPROM 无设备身份,进入待配置模式(蓝牙 SETMQTT 灌入)"); }