Files
vd_960/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/offlog.h
T
wangfq eadc67584f fix(vd960DBN): 复位原因位定义错位修正 — RST_REASON=0x08000000=PORRSTF
现场: RST_REASON 打印 0x08000000, 对照 WCH ch32v20x.h 发现
offlog.h 位定义整体错位 (IWDG=bit31实为bit29, SFT=bit24实为bit28,
POR=bit25实为bit27, LPWR=bit29实为bit31)
结论: 0x08000000=PORRSTF=上电/掉电复位 → 电源跌落方向 (SPI擦除电流脉冲?)
修正:
- offlog.h 位定义对齐 WCH 库, 新增 OFFLOG_RST_RMVF=bit24
- peripheral_main.c RMVF 清除改 bit24 (原写 bit27=PORRSTF 只读位,
  复位标志从未清除, 历史累积污染诊断)
- 协议文档 boot 事件位解析同步
测试回归全过
2026-08-12 18:58:16 +08:00

169 lines
9.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
******************************************************************************
* @file offlog.h
* @author wfq
* @version V1.0
* @date 2026-08-04
* @brief DBN 脱机事件日志 (W25Qxx 外部 SPI NOR, 环形)
*
* 目的: 解决"平台重复上线/异常重启但现场无日志"的取证问题。
* 区分「设备真复位」vs「MQTT 断连重连」(两者都会导致平台重收 initialize):
* - 真复位: 会有 OFFLOG_EVT_BOOT 事件, boot_seq 递增
* - 断连重连: boot_seq 不变, 只有 IOT_* 网络事件
*
* 分区 (2026-08-12 ROADMAP 新规划):
* | 参数区 0x000000 64KB (固定)
* | OTA 区 0x010000 512KB (固定)
* | 事件区 0x090000 动态: W25Q32=512KB / Q64=1MB / Q128=2MB / Q256=4MB
* | 快照区 事件区后 剩余容量 (snapshot.c, 2026-08-12 已实现)
* 上电读 JEDEC ID (0x9F) 决定事件区容量 (g_offlog_part 运行时分区表)
*
* 环形实现:
* - 扇区 0 (区内) = 头扇区, 存元数据 (boot_seq/写位置/条数), 扇区切换时更新
* - 扇区 1..63 = 数据扇区, 每扇区 128 条 x 32B 定长记录
* - 顺序写, 写满擦下一扇区 (环形覆盖), 天然磨损均衡
* - 上电从头部记录的写位置向后扫描, 找第一条无效记录 = 真实写位置 (掉电恢复)
*
* 记录格式 (32B 定长):
* magic(1) type(1) len(1) flags(1) seq(4) boot_seq(2) rsvd(2)
* ts_ms(4) unix_ts(4) payload(14)
******************************************************************************
*/
#ifndef _OFFLOG_H__
#define _OFFLOG_H__
#include <stdint.h>
/*===========================================================================
* 分区与容量
*===========================================================================*/
/* 固定分区 (ROADMAP 2026-08-12): 参数区 64KB + OTA 512KB, 事件区紧随其后 */
#define OFFLOG_PARAM_SIZE 0x10000UL /* 参数区 64KB (固定) */
#define OFFLOG_OTA_SIZE 0x80000UL /* OTA 镜像暂存 512KB (固定) */
#define OFFLOG_AREA_BASE (OFFLOG_PARAM_SIZE + OFFLOG_OTA_SIZE) /* 0x090000 */
#define OFFLOG_HEAD_SECTOR 0 /* 区内扇区号: 头扇区 */
#define OFFLOG_EVT_PER_SECTOR 128 /* 4096 / 32 */
/* 芯片 JEDEC ID (0x9F 命令第 3 字节, EF 40 xx) */
#define OFFLOG_CHIP_W25Q32 0x16
#define OFFLOG_CHIP_W25Q64 0x17
#define OFFLOG_CHIP_W25Q128 0x18
#define OFFLOG_CHIP_W25Q256 0x19
/* 运行时分区表 (offlog_init 由 JEDEC ID 填充; 默认 W25Q32) */
typedef struct {
uint8_t chip; /* OFFLOG_CHIP_xxx */
uint32_t area_size; /* 事件日志区大小 */
uint32_t data_sectors; /* 数据扇区数 (不含头扇区) */
uint32_t max_records; /* 最大记录数 = data_sectors * 128 */
} OfflogPart;
extern OfflogPart g_offlog_part;
/* 兼容宏: 展开为运行时值 (offlog.c 主体引用不变) */
#define OFFLOG_AREA_SIZE (g_offlog_part.area_size)
#define OFFLOG_DATA_SECTOR_CNT (g_offlog_part.data_sectors)
#define OFFLOG_DATA_SIZE ((g_offlog_part.data_sectors) * 4096UL)
#define OFFLOG_MAX_RECORDS (g_offlog_part.max_records)
#define OFFLOG_DATA_BASE (OFFLOG_AREA_BASE + 4096UL)
/* 头扇区记录 (32B, 扇区切换时更新) */
typedef struct {
uint8_t magic[4]; /* "OFFL" */
uint32_t boot_seq; /* 上次上电序号 (本 boot 由 init 时 +1) */
uint32_t wr_sector; /* 上次写扇区号 (区内 1..63) */
uint32_t wr_off; /* 上次写偏移 (相对 OFFLOG_AREA_BASE) */
uint32_t wr_seq; /* 下一条记录全局 seq (掉电恢复锚点) */
uint32_t count; /* 有效记录数 (0..max_records) */
uint32_t rsvd[2];
} OfflogHead; /* 28B, 按 32B 对齐使用 */
#define OFFLOG_HEAD_MAGIC0 'O'
#define OFFLOG_HEAD_MAGIC1 'F'
#define OFFLOG_HEAD_MAGIC2 'L'
#define OFFLOG_HEAD_MAGIC3 'L'
/* 事件记录 (32B 定长, 字段重排保证自然对齐无 padding:
4×uint8 → uint32×3 → uint16×2 → payload[12] = 4+12+4+12 = 32) */
typedef struct {
uint8_t magic; /* 0xA5 */
uint8_t type; /* OFFLOG_EVT_xxx */
uint8_t len; /* payload 有效字节数 (0..12) */
uint8_t flags; /* bit0: unix_ts 有效 */
uint32_t seq; /* 全局序号 (跨 boot 递增) */
uint32_t ts_ms; /* boot 内相对时间 mstick() */
uint32_t unix_ts; /* 已同步 Unix 秒 (flags.bit0=1 时有效) */
uint16_t boot_seq; /* 启动序号 */
uint16_t rsvd;
uint8_t payload[12]; /* 事件参数 */
} OfflogEvt; /* 32B, 无 padding */
#define OFFLOG_EVT_MAGIC 0xA5
#define OFFLOG_EVT_SIZE sizeof(OfflogEvt)
#define OFFLOG_UNIX_VALID_FLAG 0x01
/*===========================================================================
* 事件类型
*===========================================================================*/
enum {
OFFLOG_EVT_BOOT = 0x01, /* 上电/复位; payload[0..3]=复位原因寄存器 RCC_RSTSCKR */
OFFLOG_EVT_IOT_CONNECT = 0x10, /* MQTT TCP 连接成功 (socket 中断) */
OFFLOG_EVT_IOT_READY = 0x11, /* MQTT SUBACK → 发 initialize (重复上线直接原因!) */
OFFLOG_EVT_IOT_DISCONN = 0x12, /* MQTT 断连; payload[0]=原因(1=断开 2=超时 3=CONNACK拒绝 4=连接超时) */
OFFLOG_EVT_IOT_RECONN = 0x13, /* 重连退避; payload[0..3]=退避 ms */
OFFLOG_EVT_EVT_RETRY = 0x30, /* event_report ACK 超时重发; payload[0..3]=msg_id, payload[4]=retry */
OFFLOG_EVT_EVT_GIVEUP = 0x31, /* event_report 重试耗尽挂起; payload[0..3]=msg_id */
OFFLOG_EVT_COIL = 0x40, /* 线圈事件; payload[0]=sub(1=进 2=出 3=断开 4=恢复), payload[1]=ch, payload[2..5]=value(50ms) */
OFFLOG_EVT_TIME_ANCHOR = 0x50, /* 时钟同步锚点; unix_ts=同步值 */
OFFLOG_EVT_LOG_CLEAR = 0x70, /* 日志清除 (审计) */
};
/* 复位原因寄存器 (CH32V20x RCC 基址 0x40021000, RSTSCKR 偏移 0x24,
STM32F1 兼容布局; 若实际硬件布局不同, 读出的原因位会异常, 待板上验证) */
#define OFFLOG_RCC_BASE 0x40021000UL
#define OFFLOG_RCC_RSTSCKR (*(volatile uint32_t *)(OFFLOG_RCC_BASE + 0x24UL))
/* 复位原因位 (RCC_RSTSCKR, CH32V20x 布局 — 2026-08-12 对照 WCH ch32v20x.h 修正,
原定义错位: IWDG/SFT/POR 位置全错, 且 RMVF 清标志写错位导致标志从未清除) */
#define OFFLOG_RST_LPWR (1UL << 31) /* 低功耗复位 */
#define OFFLOG_RST_WWDG (1UL << 30) /* 窗口看门狗复位 */
#define OFFLOG_RST_IWDG (1UL << 29) /* 独立看门狗复位 (IWDG 超时 → 主循环卡死!) */
#define OFFLOG_RST_SFT (1UL << 28) /* 软件复位 NVIC_SystemReset */
#define OFFLOG_RST_POR (1UL << 27) /* 上电/掉电复位 (真断电!) */
#define OFFLOG_RST_PIN (1UL << 26) /* NRST 引脚复位 */
#define OFFLOG_RST_RMVF (1UL << 24) /* 清除复位标志 (写 1 清全部, WCH: RCC_RMVF) */
/*===========================================================================
* API
*===========================================================================*/
void offlog_init(void); /* 上电初始化 (扫描恢复) + 内部准备 BOOT 事件 */
void offlog_boot(uint32_t reset_reason); /* 记录 BOOT 事件 (带复位原因) */
void offlog_evt(uint8_t type, const uint8_t *payload, uint8_t len);
void offlog_iot_connect(void);
void offlog_iot_ready(void);
void offlog_iot_disconnect(uint8_t reason);
void offlog_iot_reconn(uint32_t backoff_ms);
void offlog_evt_retry(uint32_t msg_id, uint8_t retry);
void offlog_evt_giveup(uint32_t msg_id);
void offlog_coil(uint8_t sub, uint8_t ch, uint32_t value);
void offlog_time_anchor(uint32_t unix_ts);
uint32_t offlog_boot_seq(void); /* 当前 boot 序号 */
uint16_t offlog_count(void); /* 有效记录数 */
int offlog_read_idx(uint16_t idx, OfflogEvt *out); /* 按逻辑序号读, 0=成功 -1=越界 */
void offlog_clear(void); /* 清空事件区 (清空后写 LOG_CLEAR 审计) */
/*===========================================================================
* 协议导出 (MQTT V1.06 / TCP JSON V1.02)
*===========================================================================*/
uint8_t offlog_enabled(void); /* 日志功能是否启用 (Flash 初始化成功) */
uint32_t offlog_seq_last(void); /* 最新一条记录全局序号 (0=空) */
const char *offlog_type_str(uint8_t type); /* 事件类型数字 → 协议字符串名 */
int offlog_evt_to_json(const OfflogEvt *e, char *buf, int buf_len); /* 记录 → JSON 对象 */
#define OFFLOG_MAX_QUERY_RECORDS 4 /* log_query 分页上限 (MQTT ≤500B 发布限制) */
#endif /* _OFFLOG_H__ */