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