STM32从零到量产开发:四路继电器工业控制模块开发 - 系统应用层(system_app)设计说明

1. 模块概述

本模块为工业控制模块的系统应用层,负责运行时间统计、复位计数、看门狗喂狗等系统级服务。向上为 Modbus 协议层和 RS232 Shell 提供统一的系统状态读数接口,向下封装 HAL 层与 Flash 参数模块的依赖,实现关注点分离。

核心功能:

  • 运行时间统计:本次上电运行秒数(16 位回绕,对应输入寄存器 0x0000)
  • 累计运行秒/小时:跨重启连续累计,掉电不丢失,用于设备寿命评估(#4 设备履历)
  • 累计复位次数:对应输入寄存器 0x0001,由 FlashParam 模块上电自动 +1
  • 看门狗喂狗:封装 HAL_IWDG_Refresh,主循环单圈调用防止 IWDG 超时复位(6.5 秒超时)

设计约束:

  • 文件名使用 system_app 避免与 CubeMX 生成的 system_stm32f1xx.c 冲突
  • 板载看门狗 IWDG 超时配置为 6.5 秒

2. 数据流

数据源 接口函数 目标/用途
HAL_GetTick() SystemApp_GetUptime() 本次上电运行秒(16 位回绕)
flash_param SystemApp_GetRunSeconds() 跨重启累计运行秒(#4 设备履历)
flash_param SystemApp_GetRunHours() 累计运行小时(秒 / 3600)
flash_param SystemApp_GetResetCount() 累计复位次数(低 16 位)
主循环 SystemApp_FeedWatchdog() 刷新 IWDG 防止超时复位

3. 存储架构

3.1 与 flash_param 的依赖关系

本模块不自行维护任何静态变量,所有累计数据实时向 flash_param 模块获取:

SystemApp_GetRunSeconds()  →  FlashParam_GetRunSeconds()
                              ├─ run_sec_base(掉电保存基准)
                              └─ 本次上电 HAL_GetTick()/1000(增量)

SystemApp_GetResetCount()  →  FlashParam_Get()->reset_count(低16位)

解耦说明:

  1. 本层不自行维护任何静态变量,全部读数实时向 HAL / flash_param 取,不存在缓存与真实值不一致的风险
  2. 累计运行时间统一走 FlashParam_GetRunSeconds(),上层无需感知"Flash 保存基准 + 本次上电增量"的拼接细节
  3. 看门狗句柄 hiwdg 的依赖收敛在本文件,上层业务不直接接触 HAL IWDG 接口

4. 对外公共接口

函数 返回值类型 说明
SystemApp_Init() void 系统应用层初始化(在所有外设初始化后调用)
SystemApp_Tick1ms() void 系统 1ms 心跳(预留扩展点,当前为空实现)
SystemApp_GetUptime() uint16_t 获取运行时间(秒,16 位回绕,约 18.2 小时回绕,对应输入寄存器 0x0000)
SystemApp_GetRunSeconds() uint32_t 获取累计运行秒数(掉电保存基准 + 本次上电增量,#4 设备履历)
SystemApp_GetRunHours() uint32_t 获取累计运行小时数(运行秒数 / 3600,#4 设备履历)
SystemApp_GetResetCount() uint16_t 获取累计复位次数(低 16 位,对应输入寄存器 0x0001)
SystemApp_FeedWatchdog() void 喂看门狗(封装 HAL_IWDG_Refresh,主循环单圈调用)

5. 关键设计亮点

5.1 零静态变量设计

本模块不维护任何静态变量,所有读数实时从 HAL 层或 FlashParam 模块获取:

  • Uptime:直接调用 HAL_GetTick() / 1000,无缓存
  • RunSeconds / RunHours:委托 FlashParam_GetRunSeconds() 计算,避免直接读 run_sec_base 漏掉本次上电增量
  • ResetCount:从 FlashParam_Get()->reset_count 读取,由 FlashParam_Init 在每次上电 +1

优势:不存在缓存与真实值不一致的风险,代码简洁且可维护性高。

5.2 运行秒跨重启连续累计

  • run_sec_base 由 flash_param 模块在 Flash 中掉电保存
  • 每次上电后,FlashParam_GetRunSeconds() = run_sec_base + HAL_GetTick()/1000
  • SystemApp_GetRunHours() = SystemApp_GetRunSeconds() / 3600
  • 确保设备运行时间统计准确且断电不丢失,用于设备寿命评估

5.3 16 位回绕处理

  • Uptime 返回 uint16_t,约 18.2 小时回绕一次(65535 秒),符合规格书 4.3 定义
  • ResetCount 返回 uint16_t(低 16 位),内部 reset_count 为 uint32_t,取低 16 位对应输入寄存器

5.4 看门狗喂狗封装

  • 封装 HAL_IWDG_Refresh(&hiwdg),统一管理系统看门狗
  • 板载看门狗 IWDG 超时 6.5 秒,主循环每圈调用一次 SystemApp_FeedWatchdog() 防止超时复位
  • 上层业务不直接接触 HAL IWDG 接口,依赖收敛在本文件

5.5 1ms 心跳预留扩展点

  • SystemApp_Tick1ms() 当前为空实现,预留了 1ms 心跳扩展能力
  • 如需扩展软件定时器调度等 1ms 级任务,可在此实现,无需修改上层调用逻辑

6. 与 Modbus 协议协作

输入寄存器地址 对应接口 数据来源
0x0000 SystemApp_GetUptime() HAL_GetTick() / 1000(本次上电秒,16 位回绕)
0x0001 SystemApp_GetResetCount() FlashParam_Get()->reset_count(低 16 位)
0x0002 SystemApp_GetRunSeconds() FlashParam_GetRunSeconds()(跨重启累计秒)
0x0003 SystemApp_GetRunHours() SystemApp_GetRunSeconds() / 3600(累计小时)

7. 依赖关系

依赖模块 依赖方式 说明
main.h #include HAL 层基础定义
app_main.h #include 应用层公共定义
bsp_storage.h #include FlashParam 接口(flash_param 模块)
iwdg.h #include IWDG 句柄 hiwdg 定义
HAL 库 函数调用 HAL_GetTick()、HAL_IWDG_Refresh()

8. 设计总结

设计维度 具体措施
数据完整性 零静态变量,实时从 HAL/FlashParam 获取,无缓存不一致风险
运行统计 跨重启连续累计(秒 + 小时),掉电不丢失,用于设备寿命评估
看门狗管理 封装喂狗接口,主循环单圈调用,IWDG 超时 6.5 秒
关注点分离 上层不直接接触 HAL IWDG,依赖收敛在本文件
可扩展性 1ms 心跳预留扩展点,便于后续添加软件定时器调度
协议对接 与 Modbus 输入寄存器 0x0000~0x0003 一一对应

该模块设计遵循"零静态变量"原则,所有读数实时从底层获取,消除了缓存与真实值不一致的风险。运行统计功能通过委托 flash_param 模块实现跨重启连续累计,看门狗喂狗封装统一了系统级看门狗管理,整体架构清晰、解耦充分,符合工业控制软件的可靠性设计要求。

/**
  ******************************************************************************
  * @file    system_app.h
  * @brief   系统应用层 - 运行时间统计、复位计数、看门狗喂狗等
  *
  * 对应规格书:
  *   - 4.3 输入寄存器: Uptime / ResetCount / FrameCount / CRCErrorCount
  *   - 2. 板载看门狗 IWDG 超时 6.5 秒
  *
  * 注: 文件名使用 system_app 避免与 CubeMX 生成的 system_stm32f1xx.c 冲突
  ******************************************************************************
  */
#ifndef __SYSTEM_APP_H
#define __SYSTEM_APP_H

#ifdef __cplusplus
extern "C" {
#endif

#include "main.h"

/**
  * @brief  系统应用层初始化 (在所有外设初始化后调用)
  */
void SystemApp_Init(void);

/**
  * @brief  系统 1ms 心跳 (在 SysTick 中断或主循环调用)
  * @note   累计运行秒数 (Uptime, 16位回绕)
  */
void SystemApp_Tick1ms(void);

/**
  * @brief  获取运行时间 (秒, 16位回绕, 对应输入寄存器 0x0000)
  */
uint16_t SystemApp_GetUptime(void);

/**
  * @brief  获取累计运行秒数 (掉电保存基准 + 本次上电秒数, #4)
  * @note   运行秒数 = Flash 中保存的 run_sec_base(掉电不丢) + 本次上电 HAL_GetTick()/1000
  *         该值跨重启连续累计, 用于计算运行小时/设备寿命评估。
  */
uint32_t SystemApp_GetRunSeconds(void);

/**
  * @brief  获取累计运行小时数 (#4 设备履历)
  * @note   运行小时 = 累计运行秒数 / 3600, 掉电保存、跨重启连续。
  */
uint32_t SystemApp_GetRunHours(void);

/**
  * @brief  获取累计复位次数 (对应输入寄存器 0x0001)
  */
uint16_t SystemApp_GetResetCount(void);

/**
  * @brief  喂看门狗
  * @note   封装 HAL_IWDG_Refresh, 便于统一管理
  */
void SystemApp_FeedWatchdog(void);

#ifdef __cplusplus
}
#endif
#endif /* __SYSTEM_APP_H */

/**
  ******************************************************************************
  * @file    system_app.c
  * @brief   系统应用层实现(运行时间统计、复位计数、看门狗喂狗)
  *
  * 模块功能:
  *   封装与产品规格书对应的运行履历接口,向上为 Modbus / RS232 Shell 提供统一读数。
  *   运行秒 / 小时来自 flash_param(掉电保存基准 + 本次上电增量,#4 运行小时统计)。
  *
  * 数据流:
  *   HAL_GetTick()      -> SystemApp_GetUptime()      本次上电运行秒 (16 位回绕)
  *   flash_param        -> SystemApp_GetRunSeconds()  跨重启累计运行秒
  *                      -> SystemApp_GetRunHours()    累计运行小时 (秒 / 3600)
  *   flash_param        -> SystemApp_GetResetCount()  累计复位次数
  *   主循环             -> SystemApp_FeedWatchdog()   刷新 IWDG 防止超时复位
  *
  * 解耦说明:
  *   1. 本层不自行维护任何静态变量,全部读数实时向 HAL / flash_param 取,
  *      不存在缓存与真实值不一致的风险。
  *   2. 累计运行时间统一走 FlashParam_GetRunSeconds(),上层无需感知
  *      "Flash 保存基准 + 本次上电增量" 的拼接细节。
  *   3. 看门狗句柄 hiwdg 的依赖收敛在本文件,上层业务不直接接触 HAL IWDG 接口。
  ******************************************************************************
  */
#include "app_main.h"
#include "bsp_storage.h"
#include "iwdg.h"

/* -------------------------- API 快速导航 --------------------------
 * void     SystemApp_Init(void);             系统应用层初始化 (外设初始化后调用)
 * uint16_t SystemApp_GetUptime(void);        本次上电运行秒数 (16 位回绕)
 * uint32_t SystemApp_GetRunSeconds(void);    跨重启累计运行秒数 (#4)
 * uint32_t SystemApp_GetRunHours(void);      跨重启累计运行小时数 (#4)
 * uint16_t SystemApp_GetResetCount(void);    累计复位次数 (低 16 位)
 * void     SystemApp_FeedWatchdog(void);     喂看门狗, 主循环单圈调用
 * void     SystemApp_Tick1ms(void);          1ms 心跳扩展点 (当前为空实现)
 * ------------------------------------------------------------------ */

/* -------------------------- 区块 4:对外公共业务 API -------------------------- */
/**
  * @brief  系统应用层初始化(在所有外设初始化后调用)
  * @retval 无
  * @note   运行时间基于 HAL_GetTick() 系统毫秒时基,无需额外变量初始化
  */
void SystemApp_Init(void)
{
    /* 运行时间基于 HAL_GetTick(), 无需额外初始化 */
}

/**
  * @brief  获取运行时间(秒,16位回绕,对应输入寄存器 0x0000)
  * @retval 上电后累计秒数(约18.2小时回绕,符合规格书 4.3)
  * @note   HAL_GetTick() 返回 ms, /1000 得到秒, 取低16位
  * @note   本值仅表示本次上电时长;需要跨重启累计值请用 SystemApp_GetRunSeconds()
  */
uint16_t SystemApp_GetUptime(void)
{
    /* HAL_GetTick() 返回 ms, /1000 得到秒, 取低16位 (约18.2小时回绕, 符合规格书 4.3) */
    return (uint16_t)(HAL_GetTick() / 1000u);
}

/**
  * @brief  获取累计运行秒数 (#4 设备履历)
  * @retval 自设备首次运行以来的累计秒数(掉电保存基准 + 本次上电增量)
  * @note   【关联指引】直接委托 flash_param 的 FlashParam_GetRunSeconds() 计算,
  *         避免直接读 run_sec_base 漏掉本次上电增量,保证实时准确且断电不丢失
  */
uint32_t SystemApp_GetRunSeconds(void)
{
    /* #4 累计运行秒数委托 flash_param 计算 (已保存基准 + 本次上电增量),
     * 实时准确且断电不丢失。直接读取 run_sec_base 会漏掉本次上电增量,
     * 故统一走 FlashParam_GetRunSeconds()。 */
    return FlashParam_GetRunSeconds();
}

/**
  * @brief  获取累计运行小时数 (#4 设备履历)
  * @retval 累计运行小时(运行秒数 / 3600)
  * @note   【关联指引】依赖 SystemApp_GetRunSeconds(),跨重启连续累计,用于设备寿命评估
  */
uint32_t SystemApp_GetRunHours(void)
{
    /* #4 运行小时 = 累计秒数 / 3600 */
    return SystemApp_GetRunSeconds() / 3600u;
}

/**
  * @brief  获取累计复位次数(对应输入寄存器 0x0001)
  * @retval 上电累计复位次数(低16位)
  * @note   【关联指引】数据来自 FlashParam_Get()->reset_count,由 FlashParam_Init 在每次上电 +1
  */
uint16_t SystemApp_GetResetCount(void)
{
    /* 从 Flash 参数获取累计复位次数 (低16位) */
    return (uint16_t)(FlashParam_Get()->reset_count & 0xFFFFu);
}

/**
  * @brief  喂看门狗
  * @retval 无
  * @note   封装 HAL_IWDG_Refresh,便于统一管理;主循环单圈调用防止 IWDG 超时复位
  */
void SystemApp_FeedWatchdog(void)
{
    HAL_IWDG_Refresh(&hiwdg);
}

/**
  * @brief  系统 1ms 心跳(预留扩展点)
  * @retval 无
  * @note   当前实现直接基于 HAL_GetTick 计算 uptime,此函数为空;
  *         如需扩展 1ms 心跳任务(如软件定时器调度),可在此实现
  */
void SystemApp_Tick1ms(void)
{
    /* 预留: 当前实现直接基于 HAL_GetTick 计算 uptime, 此函数为空 */
    /* 如需扩展 1ms 心跳任务 (如软件定时器调度), 可在此实现 */
}

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐