8c72698666
新增 App/algo/run_par.c/h(「数据可信」卡 10,lead 四裁落地): - [run_par] cfg 段:遥测死区 9 键 int32 毫单位(db_ua…db_freq),工厂默认 = 卡 6 额定 0.5% 档(500mV×4 / 5mA×4 / 500mHz);注册走 adc_cal 同款内核前 契约(app_main_init),不挂回调 = 引擎副本即生效值;键数 9≤16 不拆段 - 真源收编(裁③):pts 层 s_yc_db 运行时副本删除,COV 每拍实时读 run_par (协议→算法向下依赖,同 meas 先例;无投递无缓存 = 顺序/幂等平凡成立); iec104_pts_yc_set_deadband()/deadband() 保留为转发壳,壳内保留"成功清 半截确认计数"语义并由 TC-IEC104-027 追加断言钉住(裁⑤) - 104 地址映射(裁①):0x8030~0x8038 死区 9 点 / 0x8040~0x8047 k 覆盖 / 0x8048~0x804F b 覆盖(逐项地址本仓自定——复现规划 §5.4 权威表不在仓, 送检点表到位后替换 run_par.c 顶部常量段一处;已在 IEC104 §3.4 表头挂账); 路由顺序 = run_par 表先、未命中落 prot_set——0x8027~0x802A 属 prot_set 告警段永不覆盖,TC-IEC104-029 双向往返钉死;空隙地址自然 COT14/PN - 通道系数覆盖 = adc_cal_set 代理(裁②a):合法带/越带 [ERR] 留痕全复用, run_par 不存第二份系数(防双真源);单侧写 RMW 用另一侧当前生效值; 回显 = 生效值视图(文件坏值被哨兵回退 ROM 时主站看到的就是下游吃到的); adc_cal 配套:+adc_cal_b_codes() 读取口(回显/RMW 用)+ set() 授权写者 注记扩至 iec104Task 直通路径(单字原子契约同「数据可信」§4.4) - 持久化(裁②b):新事件位 SYS_EVT_RUN_PAR_SAVE(1<<8);iec104Task 主循环 peek 投递 → defaultTask run_par_store_save() 一条事件一趟 cfg_file_save 整档外科(覆盖 [run_par] 9 键 + 联动 [adc_cal] 两段);失败脏保留 = 天然 重试(B7 prot_set 同款)。C_SE 现行无"暂停/测试态"门控——文档现行如此 不造新语义,送检若要求接线点已写进 §3.4 注(裁④) 测试:新建 test/host/run_par(TC-RUNPAR-001~008,67 断言,真实 cfg 引擎 + 真实 adc_cal + 内存虚拟盘同 adc_cal 套件先例;[run_par] 注册契约/落盘往返/ 外改文件即副本生效/两段式不提交不置脏/合法带拒+RMW+生效值回显/init 幂等 重注册留痕);iec104 套扩真链 TU(cfg_file+adc_cal+run_par 非桩,141 断言: +TC-IEC104-029 路由 + 壳清确认计数 3 断言);meas 套静态更名 s_snap*→ s_ms_snap*(与 cfg_file.c s_snap 同 TU 相撞,D1 同款改名法,226 断言不变); iec104_serv 套补 run_par_dirty_peek 假件。id_registry 建族 TC-HOST-RUNPAR (游标 008)+ TC-HOST-IEC104 游标 028→029;新钩子 run-par-unit-test + iec104 钩子触发面随真链扩;uvprojx +run_par.c(合法入库)+ SIM_EXEMPT 登记 (模拟器不编 104 协议,已核实 CMake 面)。 文档:IEC104 §3.4 运行参数段表(含送检替换账/门控未来条件/代理声明)、§7、 §8 第 4 条、变更记录;网络配置说明新增 §3.5 [run_par] 键表;复现规划 §5.4 补运行参数段落实进度。**遗留(交 lead)**:数据可信 §4.4 "引擎 RAM 副本" 一节当时的措辞对象含 pts 侧副本,s_yc_db 删除后该句过时(裁③b 点名,§4.4 在写域外未动)。 gate:@build 0/0(Code=219620 RO=22036 RW=1280 ZI=132864);vs 0cbae7d: Flash +1912B、RAM(RW+ZI) +8B(37B 新静态被链接器并洞吸收);iec104Task 栈 1456B/margin 43%、defaultTask 3032B 不变;id-check/doc-links/cppcheck/ coding-standard 实跑过;--all-files 全量 45 钩子实跑(43 绿 + 仅 mem-budget / mem-numbers 两红 = 台账受管区 vs .map 漂移,回填属 lead 单写者区,批 1/道D-1 /D2 同口径):gate: only mem-budget/mem-numbers pending lead ledger backfill BOM/CR/U+FFFD/字面量 ASCII 逐文件自查通过。
334 lines
16 KiB
C
334 lines
16 KiB
C
/*
|
||
* 模块名称:Application Main Entry
|
||
* 模块功能:应用初始化流程编排、各模块启动、主循环
|
||
* 适用平台:STM32F407ZGTx
|
||
* 作者:王建锋
|
||
* 创建日期:2026-07-19
|
||
* 修改记录:
|
||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||
* v1.1 2026-08-29 王建锋 将原 main.c 中各外设初始化统一收归 app_main_init,
|
||
* 并按"时间子系统优先"的顺序编排启动流程
|
||
* v1.2 2026-09-14 王建锋 新增自检埋点(RTC/NAND/ADC 失败 → sys_log 自检记录)+ syslog_mcu_init()
|
||
*/
|
||
|
||
#include "app_main.h"
|
||
|
||
#include "main.h" /* ST_DIR4_GPIO_Port / ST_DIR4_Pin 等 CubeMX 宏 */
|
||
#include "stm32f4xx_it.h" /* fault_handler_enable() */
|
||
#include "usart.h" /* huart5 */
|
||
#include "tim.h" /* htim3:1 kHz 采样节拍(TIM3 更新中断触发 CONVST + TRGO 触发 ADC1) */
|
||
#include "rs485.h"
|
||
#include "sd2506.h"
|
||
#include "sys_clock.h"
|
||
#include "gd5f2gq5ue.h"
|
||
#include "tpafe5160.h"
|
||
#include "adc_input.h" /* L1 采集层:adc_input_init()(语义映射 + STM32 ADC1 扫描) */
|
||
#include "adc_cal.h" /* L1.5 标定层:adc_cal_init()(ROM 系数 + cfg 两段注册,道A 卡2/4) */
|
||
#include "run_par.h" /* 运行参数段:run_par_init()([run_par] 注册 + 104 地址映射真源,道D 卡10) */
|
||
#include "phasor.h" /* L2 相量层:phasor_init()(系数表 + 实例 + 双区交换) */
|
||
#include "freq.h" /* L2 派生量:freq_init()(相位解缠绕测频,B8) */
|
||
#include "prot_set.h" /* L3 定值模型:prot_set_init()(B5,内核前封存) */
|
||
#include "prot_elm.h" /* L3 元件框架:prot_elm_init()(B5,解析相量索引) */
|
||
#include "sys_log.h"
|
||
#include "task_watchdog.h" /* task_wd_seg_report():round 8 复位不丢"进段打点"上报 */
|
||
#include "test_config.h" /* 套件开关:身份戳的构建变体串(FW_BUILD_VARIANT)依赖本头,
|
||
* 改 TEST_SUITE_* / ENABLE_DESTRUCTIVE_TESTS 必重编本 TU →
|
||
* __DATE__/__TIME__ 随之刷新,见 app_main_init() 的戳注释 */
|
||
|
||
#define DBG_TAG "[APP]"
|
||
#include "dbg_log.h"
|
||
|
||
/* g_rs485 由 rs485.c 定义,经 rs485.h 的 extern 引用;接收缓冲内聚于句柄 rx_buf */
|
||
|
||
/* 复位原因锁存值(Src/main.c 在 HAL_Init() 后立即写入并清除 RCC_CSR)。
|
||
* 放这里而非 main.c,是因为需要与其它应用级全局量同层、且日志侧更好引用。 */
|
||
volatile uint32_t g_rcc_csr_latched;
|
||
|
||
/*
|
||
* 构建变体串(2026-10-11):把"测试套件开关"编进固件身份戳,见 app_main_init() 的戳注释。
|
||
* TEST_SUITE_NAME 由 test/board/test_config.h 定义(产品构建 = "NONE");
|
||
* 模拟器等未纳入该头的编译单元退化为空串,不影响戳的可搜索性。
|
||
*/
|
||
#if defined(TEST_SUITE_NAME)
|
||
#define FW_BUILD_SUITE " suite=" TEST_SUITE_NAME
|
||
#else
|
||
#define FW_BUILD_SUITE ""
|
||
#endif
|
||
|
||
#if defined(ENABLE_DESTRUCTIVE_TESTS)
|
||
#define FW_BUILD_DSTR " dstr=on"
|
||
#else
|
||
#define FW_BUILD_DSTR " dstr=off"
|
||
#endif
|
||
|
||
/* 身份戳后缀:一段连续 ASCII,保证 .hex 里可原样搜到(run_boot_test.py 抠戳用) */
|
||
#define FW_BUILD_VARIANT FW_BUILD_SUITE FW_BUILD_DSTR
|
||
|
||
/*
|
||
* 函数功能:把 RCC_CSR 锁存值翻译为可读的复位原因串(见手册 RM0090 §6.3.22)
|
||
* 入口参数:csr - 锁存的 RCC_CSR 值
|
||
* buf - 输出缓冲
|
||
* len - 缓冲容量
|
||
* 返回值:无
|
||
* 函数说明:按"最可信归因"的优先级组织——IWDG/WWDG 优先于软件复位
|
||
* (看门狗复位是我们真正关心的自动恢复事件;软件复位多为崩溃钩子
|
||
* 兜底,两者同时置位时报告看门狗更有价值)。纯 ASCII,防编码坑。
|
||
*/
|
||
static void reset_cause_str(uint32_t csr, char *buf, unsigned int len) {
|
||
const char *cause;
|
||
|
||
if ((csr & RCC_CSR_IWDGRSTF) != 0u) {
|
||
cause = "IWDG (independent watchdog)";
|
||
} else if ((csr & RCC_CSR_WWDGRSTF) != 0u) {
|
||
cause = "WWDG (window watchdog)";
|
||
} else if ((csr & RCC_CSR_SFTRSTF) != 0u) {
|
||
cause = "software (NVIC_SystemReset)";
|
||
} else if ((csr & RCC_CSR_PORRSTF) != 0u) {
|
||
cause = "power-on (POR)";
|
||
} else if ((csr & RCC_CSR_PINRSTF) != 0u) {
|
||
cause = "external pin (NRST)";
|
||
} else if ((csr & RCC_CSR_BORRSTF) != 0u) {
|
||
cause = "brown-out (BOR)";
|
||
} else {
|
||
cause = "unknown";
|
||
}
|
||
|
||
/* 简单有界拼接(不引 snprintf,避免依赖与栈开销) */
|
||
if (buf == NULL || len == 0u) {
|
||
return;
|
||
}
|
||
{
|
||
unsigned int i = 0u;
|
||
while ((i < (len - 1u)) && (cause[i] != '\0')) {
|
||
buf[i] = cause[i];
|
||
i++;
|
||
}
|
||
buf[i] = '\0';
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 函数功能:应用层初始化
|
||
* 入口参数:无
|
||
* 返 回 值:0 - 成功,-1 - 失败
|
||
* 限定条件:CubeMX 外设初始化已完成
|
||
* 函数说明:在 CubeMX 外设初始化之后、FreeRTOS 调度器启动之前调用。
|
||
* 启动顺序以"时间子系统优先"为原则,使后续所有日志均带时间戳:
|
||
* 1) 时间子系统(SD2506 RTC → 墙钟基准)
|
||
* 2) RS-485
|
||
* 3) GD5F NAND
|
||
* 4) TPAFE5160 ADC → ADC 输入层(L1,语义映射 + STM32 ADC1)
|
||
* 5) SD2506 诊断信息
|
||
* 6) 启动 TIM3 1 kHz 采样节拍(ISR 内脉冲 CONVST;同一更新事件经 TRGO 硬件触发 ADC1 + DMA)
|
||
*/
|
||
int app_main_init(void) {
|
||
/* 0) 使能 MemManage / BusFault / UsageFault(复位后默认只有 HardFault 使能)。
|
||
* 必须尽早——放在所有业务初始化**之前**,这样从第一行业务代码起,
|
||
* 任何内存/总线/非法指令类故障都会被独立捕获并打出故障名,
|
||
* 而不是被升级成 HardFault 后只看到笼统的 "HARDFAULT"。
|
||
* 纯寄存器写(SCB->SHCSR),不依赖日志/时钟,故放在最前无副作用。 */
|
||
fault_handler_enable();
|
||
|
||
/* 日志引擎初始化(创建待发队列;须先于任何 syslog_post) */
|
||
(void)syslog_mcu_init();
|
||
/* 启动锚点(2026-10-02 S10):本函数在内核启动前执行、不创建任何 RTOS 对象,
|
||
* 恒成功且无副作用;本行是"启动进度轨迹"的起点——之后的每一步都有结果日志,
|
||
* 故某次启动卡死时,最后一行日志即可指向卡住的步骤。 */
|
||
DBG_INFO("boot: syslog init OK");
|
||
|
||
/* 固件身份戳(2026-10-10):__DATE__/__TIME__ 由编译器展开,与前面的字面量做
|
||
* C 字符串拼接 → .rodata 里是一段连续 ASCII,可直接在 .hex 里原样搜到。
|
||
* 用途:@test 把"芯片里正在跑的镜像"和"刚编出来的镜像"对上号。此前开机标记
|
||
* 全是固定串(App init OK / Network Init / ...),旧镜像也照样全中,于是
|
||
* "反复重下载、芯片一直是旧镜像"这类事故在自动闭环里完全不可见
|
||
* (AGENTS.md 烧录铁律记的正是它)。代价:1 条 DBG ≈ 90 B Flash + 30 B 常量。
|
||
* ⚠ 戳只在**本 TU 被重编**时才刷新(增量构建),故本文件必须 #include
|
||
* "test_config.h":否则只切套件宏(TEST_SUITE_* / ENABLE_DESTRUCTIVE_TESTS)
|
||
* 不重编本 TU,产品镜像与测试镜像顶着同一个戳,戳校验对"两种构建互换"失明
|
||
* (2026-10-11 实测:切宏后的测试构建与产品构建的戳一模一样,只有回读校验
|
||
* 才发现镜像不同)。两条防线:① 头依赖 → 改宏必重编、戳必刷新;
|
||
* ② FW_BUILD_VARIANT 把套件名与破坏性开关编进戳内容 → 即便戳时间相同,
|
||
* 产品串与测试串也不同。代价:约 20 B 常量。 */
|
||
DBG_INFO("fw build: " __DATE__ " " __TIME__ FW_BUILD_VARIANT);
|
||
|
||
/* 0) 复位原因上报(锁存值由 Src/main.c 在 HAL_Init() 后立即采得)。
|
||
* 放在日志引擎之后、业务初始化之前:任何一次非上电复位都值得在
|
||
* 开机第一时间看到——尤其 IWDG,它意味着上一轮运行有任务卡死。
|
||
* 注意:RCC_CSR 已被 main.c 清零,本处只读锁存副本。 */
|
||
{
|
||
char cause[40];
|
||
uint32_t csr = g_rcc_csr_latched;
|
||
reset_cause_str(csr, cause, sizeof(cause));
|
||
DBG_INFO("reset cause: %s (CSR=0x%08lX)", cause, (unsigned long)csr);
|
||
/* 看门狗复位记入自检记录(只记载故障,符合既有约定) */
|
||
if ((csr & (RCC_CSR_IWDGRSTF | RCC_CSR_WWDGRSTF)) != 0u) {
|
||
syslog_post(SYSLOG_SELFTEST, "watchdog reset");
|
||
}
|
||
}
|
||
|
||
/* 0b) round 8 取证:上报上一轮 defaultTask 主循环的**最后进段**。
|
||
* 轨迹槽在 .noinit(scatter UNINIT_CCM),IWDG/引脚复位不清 SRAM →
|
||
* 误复位后开机第一时间可见 `PREVRUN seg=N`,直接指向主循环阻塞点
|
||
* (round 7 观察"判定分支未执行"经 round 8b 更正:真因是 IWDG 超时被静默
|
||
* 配成复位默认值,见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)。
|
||
* 裸 UART 直发,且一次复位只报一次(内部清 magic)。 */
|
||
task_wd_seg_report();
|
||
|
||
/*
|
||
* 1) 时间子系统初始化(尽早完成,后续所有日志带时间戳)
|
||
* sd2506_init() 从 RTC 读取初始时间
|
||
* sys_clock_init() 将 RTC 时间转为 Unix epoch,建立墙钟基准
|
||
* S10(2026-10-02):sd2506_init 原先所有失败路径静默返回,是启动段
|
||
* 唯一无日志的初始化;现内部已逐路径打 DBG_ERROR,此处再补一行
|
||
* 带错误码的结果回执,使 I2C 不通时能看到"在哪一步、什么错"。
|
||
*/
|
||
{
|
||
bsp_err_t rtc_ret = sd2506_init();
|
||
DBG_INFO("SD2506 init: %s (ret=%d)",
|
||
(rtc_ret == BSP_OK) ? "OK" : "FAILED", rtc_ret);
|
||
}
|
||
if (sys_clock_init() != 0) {
|
||
syslog_post(SYSLOG_SELFTEST, "clock chip fault");
|
||
}
|
||
|
||
/* 2) RS-485 初始化(UART4 + PD0 方向控制),初始化即自动开启接收 */
|
||
rs485_init(&g_rs485, &huart4, ST_DIR4_GPIO_Port, ST_DIR4_Pin);
|
||
DBG_INFO("RS485 init: OK (UART4, DE=PA15)");
|
||
|
||
/* 3) GD5F 初始化测试(BBT 全片扫描,启动段最耗时的一步) */
|
||
int ret = gd5f2gq5ue_init();
|
||
DBG_INFO("NAND init: %s",
|
||
(ret == GD5F_OK) ? "OK" :
|
||
(ret == GD5F_ERROR) ? "ERROR" :
|
||
(ret == GD5F_WAIT_EXPIRED) ? "BUSY TIMEOUT" :
|
||
(ret == GD5F_ECC_ERROR) ? "ECC ERROR" :
|
||
(ret == GD5F_PROGRAM_FAIL) ? "PROGRAM FAIL" :
|
||
(ret == GD5F_ERASE_FAIL) ? "ERASE FAIL" :
|
||
(ret == GD5F_ID_MISMATCH) ? "ID MISMATCH" : "UNKNOWN");
|
||
if (ret != GD5F_OK) {
|
||
syslog_post(SYSLOG_SELFTEST, "NAND fault");
|
||
}
|
||
|
||
/* 4) TPAFE5160 ADC 测试 */
|
||
bsp_err_t tp_ret = tpafe5160_init();
|
||
DBG_INFO("ADC init: %s",
|
||
(tp_ret == BSP_OK) ? "OK" :
|
||
(tp_ret == BSP_ERR_TIMEOUT) ? "TIMEOUT" :
|
||
(tp_ret == BSP_ERR_NO_DEVICE) ? "NO CHIP" : "FAIL");
|
||
if (tp_ret != BSP_OK) {
|
||
syslog_post(SYSLOG_SELFTEST, "ADC fault");
|
||
}
|
||
|
||
/* 4b) ADC 输入层(L1)初始化:清原始码值缓冲与统计 + **语义映射表自洽性自检**
|
||
* (映射指向了不会被扫描的通道 → 返回 -1,避免"读到恒为陈旧值"的静默错)
|
||
* + 挂载一次 STM32 ADC1 的 DMA(失败返回 -2)。
|
||
* ADC1 外设已由 MX_ADC1_Init()(main.c)配好 9 通道 / 144 cycles /
|
||
* **TIM3 TRGO 外部触发 + DMA 连续请求**,本层不重复配置、也不触发转换。
|
||
* 须在启动采样节拍(步骤 6)之前——先挂 DMA 再放节拍,才不会漏第一拍。 */
|
||
{
|
||
int adc_rc = adc_input_init();
|
||
|
||
if (adc_rc == -1) {
|
||
DBG_ERROR("ADC input layer: map self-check FAILED");
|
||
syslog_post(SYSLOG_SELFTEST, "ADC map fault");
|
||
} else if (adc_rc != 0) {
|
||
DBG_ERROR("ADC input layer: DMA arm FAILED (rc=%d)", adc_rc);
|
||
syslog_post(SYSLOG_SELFTEST, "ADC dma fault");
|
||
} else {
|
||
DBG_INFO("ADC input layer init: OK");
|
||
}
|
||
}
|
||
|
||
/* 4c) 相量层(L2)初始化:清双区结果与有效标志 + 复位采样环缓冲。
|
||
* 纯算法层(无外设 / 无 RTOS 依赖,见 docs/算法/继电保护功能复现规划.md
|
||
* §3.2),须在启动采样节拍(步骤 6)之前。幂等、当前无失败路径
|
||
* (返回值保留供后续自检扩展)。 */
|
||
(void)phasor_init();
|
||
DBG_INFO("phasor layer init: OK (inst=%u)", (unsigned)phasor_inst_count());
|
||
|
||
/* 4c-2) 测频层(L2 派生量,B8)初始化:清平均窗/相位基准/有效标志。
|
||
* 纯算法(输入 = phasor 使用区),消费在 adcTask(freq_run 每拍)
|
||
* 与 protectTask(OF/UF 判据经 prot_elm_run 取数)。 */
|
||
(void)freq_init();
|
||
DBG_INFO("freq layer init: OK");
|
||
|
||
/* 4c-3) 通道标定层(L1.5,「数据可信」卡 2/卡 4)初始化:RAM 系数副本置
|
||
* ROM 默认(卡 1 定死的引脚侧 LSB,docs/硬件平台/ADC通道映射说明.md §7.1)
|
||
* + 向配置引擎注册 [adc_cal] / [adc_cal_ofs] 两段。注册必须先于
|
||
* defaultTask 的 cfg_file_load(「先注册后 load」契约)→ 只能落内核前;
|
||
* 折算消费在 adcTask 每拍(adc_cal_apply),标定文件值随 load/poll 原地
|
||
* 生效,无回调。 */
|
||
adc_cal_init();
|
||
DBG_INFO("adc cal init: OK (k_q16 tpafe=%ld stm32=%ld)",
|
||
(long)ADC_CAL_K_Q16_TPAFE, (long)ADC_CAL_K_Q16_STM32);
|
||
|
||
/* 4c2) 运行参数段初始化(「数据可信」卡 10):死区 9 点工厂默认 + 注册
|
||
* [run_par] 段。同 adc_cal 的「先注册后 load」契约 → 必须内核前;
|
||
* 104 消费(COV 实时读 / 0x8020 段读写映射)在 iec104Task 启动后进行。 */
|
||
run_par_init();
|
||
DBG_INFO("run par init: OK (db9 k/b via adc_cal, evt=1<<8)");
|
||
|
||
/* 4d) 保护定值 + 元件层初始化(B5):出厂默认定值封存校验和 + 元件状态
|
||
* 复位并解析相量索引(须在 phasor_init 之后)。内核前完成 → iec104
|
||
* 定值读/protectTask 首拍前定值必已装载。判据消费在 protectTask。 */
|
||
prot_set_init();
|
||
prot_elm_init();
|
||
DBG_INFO("protection init: OK (set=%u elm=%u)",
|
||
(unsigned)PS_ID_COUNT, (unsigned)PE_COUNT);
|
||
|
||
/* 5) SD2506 RTC 诊断信息 */
|
||
{
|
||
/* 读取 ID */
|
||
uint8_t sd_id[8];
|
||
sd2506_get_id(sd_id);
|
||
DBG_INFO("SD2506 ID: %02X%02X%02X%02X%02X%02X%02X%02X",
|
||
sd_id[0], sd_id[1], sd_id[2], sd_id[3],
|
||
sd_id[4], sd_id[5], sd_id[6], sd_id[7]);
|
||
|
||
/* 读取当前时间 */
|
||
sd2506_time_t sd_time;
|
||
sd2506_get_time(&sd_time);
|
||
DBG_INFO("SD2506 time: %04d-%02d-%02d %02d:%02d:%02d (week %d)",
|
||
sd_time.year, sd_time.month, sd_time.day,
|
||
sd_time.hour, sd_time.minute, sd_time.second,
|
||
sd_time.week);
|
||
|
||
/* 读取温度 */
|
||
int8_t sd_temp;
|
||
sd2506_get_temperature(&sd_temp);
|
||
DBG_INFO("SD2506 temp: %d C", sd_temp);
|
||
|
||
/* 读取电池电压 */
|
||
uint16_t sd_vbat;
|
||
sd2506_get_battery_voltage(&sd_vbat);
|
||
DBG_INFO("SD2506 battery: %d.%02d V",
|
||
sd_vbat / 1000, (sd_vbat % 1000) / 10);
|
||
}
|
||
|
||
/* 6) 启动 1 kHz 采样节拍:TIM3 更新事件 → ① ISR 内脉冲 CONVST(Src/main.c)
|
||
* ② 同一事件经 TRGO 硬件触发 STM32 ADC1 一轮 9 通道转换(DMA 自动搬运)。
|
||
* ⚠ 必须在 tpafe5160_init() 成功之后启动,否则 ISR 会在驱动首帧自检
|
||
* (E-44 有效性探针)期间乱发 CONVST。消费端 adcTask 退化为纯消费者。
|
||
* ⚠ 节拍源必须能触发 ADC1,故用 **TIM3**:F4 上 ADC1 的外部触发只认
|
||
* TIM1/TIM2/TIM3/TIM4/TIM5/TIM8 的 CC 或 TRGO,**TIM6/TIM7 的 TRGO 只接
|
||
* DAC**(查 CubeMX DB 与本仓库 stm32f4xx_hal_adc.h 的 EXTSEL 表证实)
|
||
* → 要让两片转换器由同一个硬件事件驱动,节拍源必须换成 TIM3。 */
|
||
(void)HAL_TIM_Base_Start_IT(&htim3);
|
||
|
||
DBG_INFO("App init OK");
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:应用主循环
|
||
* 入口参数:无
|
||
* 返 回 值:无
|
||
* 限定条件:FreeRTOS 调度器启动后不再返回
|
||
* 函数说明:空闲任务可调用的应用层处理入口
|
||
*/
|
||
void app_main_run(void) {
|
||
for (;;) {
|
||
/* 应用层空闲处理 */
|
||
}
|
||
}
|