232 lines
8.0 KiB
C
232 lines
8.0 KiB
C
#include "24c02.h"
|
||
#include "iic.h"
|
||
#include "app_loop.h" /* storage_lock/unlock:跨任务访问互斥 */
|
||
#include <string.h>
|
||
#include <stdio.h>
|
||
|
||
/* 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);
|
||
}
|
||
}
|