shuichan_old/V1.3/STM32F103_App1/HardWare/24C02/24c02.c

225 lines
8.3 KiB
C
Raw Normal View History

#include "24c02.h"
#include "iic.h"
#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 read init 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 write err @%u, retry=%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;
ptr = data;
return m24c02ReadSequential(addr,ptr,len);
}
/*-------------------------------------------------*/
/*函数名:向 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 page write err @%u, retry=%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;
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 write fail @%u, err=0x%02X", addr, e);
}
err |= e;
addr += chunk;
ptr += chunk;
len -= chunk;
}
if (err) {
log_warn("> EEPROM write total ACK err=0x%02X", err);
} else {
log_info("> EEPROM write OK");
}
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 probe: wr=0x%02X, rd=0x%02X", probe_wr, probe_rd);
/* 从EEPROM读取配置 */
memset(&MqttInfoStr, 0, MQTT_STRUCT_LEN);
read_err = eepromReadData(0, &MqttInfoStr, MQTT_STRUCT_LEN);
log_info("> EEPROM read 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 config valid, 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 write done, Magic=0x%08X", MqttInfoStr.Magic);
/* 立即回读验证 */
memset(&tmp_read, 0, MQTT_STRUCT_LEN);
read_err = eepromReadData(0, &tmp_read, MQTT_STRUCT_LEN);
log_info("> EEPROM verify err=%d, Magic=0x%08X, Ver=%u",
read_err, tmp_read.Magic, tmp_read.Ver);
log_info("EEPROM repaired, Ver=%u", MqttInfoStr.Ver);
}
}