shuichan_old/V1.4-Chinese/STM32F103_App1/HardWare/SYSINIT/reset_log.c

222 lines
7.8 KiB
C
Raw Normal View History

/* reset_log.c - 重启记录(原因 + 上次运行时长)
*
* W25Q64 0x100000 4KB 10
* 寿
* BOOT_SHARE RAM/
* RAM
* PA0 <10% 12V 30s
* RCC->CSR BL BOOT_SHARE APPBL
* reset_log_mark()
*/
#include "reset_log.h"
#include "boot_share.h"
#include "log.h"
#include "main.h"
#include "app_loop.h"
#include "../W25Q64/w25q64.h"
#include "string.h"
#define RST_LOG_ADDR 0x100000u /* 避开 OTA 区(0~0x81FFF) */
#define RST_LOG_SLOTS 10
#define RST_REC_MAGIC 0x5253544Cu /* 'RSTL' */
#define RST_REC_SIZE 32u
typedef struct {
uint32_t magic; /* RST_REC_MAGIC */
uint32_t seq; /* 递增序号,大者新 */
uint8_t reason; /* RSN_x */
uint8_t boot_seen; /* 0xFF=新记录;断电预警记录被下次开机消费后置 0x00位翻转免擦除 */
uint8_t rsvd[2];
uint32_t uptime_sec; /* 上次运行时长0xFFFFFFFF=未知(冷启动 RAM 丢失) */
uint16_t crc16; /* 前 14 字节累加和 */
uint8_t pad[16];
} rst_rec_t;
static const char *reason_str(uint8_t r)
{
switch (r) {
case RSN_POWER_ON: return "上电/意外掉电";
case RSN_PIN_RESET: return "按键复位";
case RSN_WATCHDOG: return "卡死看门狗溢出";
case RSN_KICKED: return "被平台踢下线";
case RSN_PING_TIMEOUT: return "心跳超时(服务器无响应)";
case RSN_NO_SERVICE: return "4G无网络服务";
case RSN_NO_SIM: return "无SIM卡";
case RSN_OTA_UPDATE: return "OTA升级完成";
case RSN_OTA_HEALTH: return "OTA健康检查超时";
case RSN_4G_FAIL: return "4G初始化失败";
case RSN_CMD_RESET: return "串口RESET命令";
case RSN_CMD_OTA: return "串口OTA命令";
case RSN_CMD_RECOVERY: return "串口RECOVERY命令";
case RSN_POWER_LOSS: return "外部断电(法拉电容记录)";
case RSN_4G_MODULE: return "4G模组异常自重启";
case RSN_SOFT_GENERIC: return "软件复位(未标记)";
default: return "未知";
}
}
static uint16_t rec_sum(const rst_rec_t *r)
{
const uint8_t *p = (const uint8_t *)r;
uint16_t s = 0;
for (int i = 0; i < 14; i++) s += p[i]; /* magic+seq+reason+boot_seen+rsvd+uptime */
return s;
}
static int rec_valid(const rst_rec_t *r)
{
return r->magic == RST_REC_MAGIC && r->crc16 == rec_sum(r);
}
static void rec_read(uint8_t slot, rst_rec_t *r)
{
W25Q64_Read(RST_LOG_ADDR + slot * RST_REC_SIZE, (uint8_t *)r, RST_REC_SIZE);
}
static void rec_write(uint8_t slot, rst_rec_t *r)
{
r->crc16 = rec_sum(r);
W25Q64_WriteBuffer(RST_LOG_ADDR + slot * RST_REC_SIZE, (const uint8_t *)r, RST_REC_SIZE);
}
/* 找最新记录:返回槽位;无有效记录返回 -1 */
static int find_newest(uint32_t *seq_out)
{
rst_rec_t r;
int best = -1;
uint32_t best_seq = 0;
for (uint8_t i = 0; i < RST_LOG_SLOTS; i++) {
rec_read(i, &r);
if (rec_valid(&r) && (best < 0 || (int32_t)(r.seq - best_seq) > 0)) {
best = i;
best_seq = r.seq;
}
}
if (seq_out) *seq_out = best_seq;
return best;
}
/* 追加一条记录(自动环形覆盖最旧) */
static void append_record(uint8_t reason, uint32_t uptime_sec)
{
uint32_t seq = 0;
int newest = find_newest(&seq);
int slot = (newest < 0) ? 0 : (newest + 1) % RST_LOG_SLOTS;
if (slot == 0) {
/* 回到起点:整扇区擦除重写 */
W25Q64_EraseRegion(RST_LOG_ADDR, W25Q64_SECTOR_SIZE);
}
rst_rec_t r;
memset(&r, 0xFF, sizeof(r));
r.magic = RST_REC_MAGIC;
r.seq = seq + 1;
r.reason = reason;
r.boot_seen = 0xFF;
r.uptime_sec = uptime_sec;
rec_write((uint8_t)slot, &r);
}
/* 主动复位前标记细分原因(共享 RAM热复位后 APP 开机读取) */
void reset_log_mark(uint8_t reason)
{
BOOT_SHARE->pending_reason = reason;
boot_share_store();
}
/* 每秒更新运行时长到共享 RAM热复位不丢 */
void reset_log_tick(uint32_t uptime_s)
{
BOOT_SHARE->uptime_sec = uptime_s;
boot_share_store();
}
/* 外部断电预警PA0 低电量 = 12V 断开,法拉电容续航中):立即写断电记录 */
void reset_log_power_loss(void)
{
uint32_t up = boot_share_valid() ? BOOT_SHARE->uptime_sec : 0xFFFFFFFFu;
uint32_t m = up / 60;
log_warn("> 断电预警:外部 12V 断开,记录运行时间 %u天%02u小时%02u分%02u秒",
(unsigned)(m / 1440), (unsigned)((m % 1440) / 60), (unsigned)(m % 60), (unsigned)(up % 60));
append_record(RSN_POWER_LOSS, up);
}
/*==== 开机:根据 BL 传来的 CSR + 预留原因码生成本次记录,并打印最近 10 条 ====*/
void reset_log_boot(void)
{
uint8_t reason = RSN_UNKNOWN;
uint32_t up = 0xFFFFFFFFu;
int skip_new = 0;
if (boot_share_valid()) {
uint32_t csr = BOOT_SHARE->csr_flags;
up = BOOT_SHARE->uptime_sec; /* 热复位时这就是上轮运行时长 */
if ((csr & RCC_CSR_SFTRSTF) && BOOT_SHARE->pending_reason != 0) {
reason = (uint8_t)BOOT_SHARE->pending_reason;
} else if (csr & RCC_CSR_SFTRSTF) {
reason = RSN_SOFT_GENERIC;
} else if (csr & RCC_CSR_IWDGRSTF) {
reason = RSN_WATCHDOG;
} else if (csr & RCC_CSR_WWDGRSTF) {
reason = RSN_WATCHDOG;
} else if (csr & RCC_CSR_PINRSTF) {
reason = RSN_PIN_RESET;
} else if (csr & RCC_CSR_PORRSTF) {
reason = RSN_POWER_ON;
}
}
/* 断电预警记录消费(不限复位类型):断电时已预写记录,本次开机直接消费它,
* +BL */
{
uint32_t seq;
int newest = find_newest(&seq);
if (newest >= 0) {
rst_rec_t last;
rec_read((uint8_t)newest, &last);
if (last.reason == RSN_POWER_LOSS && last.boot_seen == 0xFF) {
last.boot_seen = 0x00;
rec_write((uint8_t)newest, &last); /* 位翻转 1->0 免擦除 */
skip_new = 1;
log_info("> 上次为断电关机(已记录)");
}
}
}
if (!skip_new) {
append_record(reason, up);
}
/* 本次开机初始化共享 RAM 状态(清预留原因,运行时长重新计) */
BOOT_SHARE->pending_reason = 0;
BOOT_SHARE->uptime_sec = 0;
BOOT_SHARE->magic = BOOT_SHARE_MAGIC;
boot_share_store();
/* 打印最近 10 条(新→旧) */
log_info("> 历史重启记录(新→旧):");
uint32_t newest_seq;
int newest = find_newest(&newest_seq);
if (newest < 0) {
log_info(" (无记录)");
return;
}
for (int i = 0; i < RST_LOG_SLOTS; i++) {
int slot = (newest - i + RST_LOG_SLOTS) % RST_LOG_SLOTS;
rst_rec_t r;
rec_read((uint8_t)slot, &r);
if (!rec_valid(&r)) continue;
if (r.uptime_sec == 0xFFFFFFFFu) {
log_info(" [%u] 重启原因: %s 运行时间: 未知",
(unsigned)r.seq, reason_str(r.reason));
} else {
uint32_t m = r.uptime_sec / 60;
log_info(" [%u] 重启原因: %s 运行时间: %u天%02u小时%02u分",
(unsigned)r.seq, reason_str(r.reason),
(unsigned)(m / 1440), (unsigned)((m % 1440) / 60), (unsigned)(m % 60));
}
}
}