EMS 知识库
首页 / knowledge/代码解析/user

main.c

源文件路径:user/main.c 所属模块:user(主程序入口 / 系统启动与任务编排) 代码行数:370

1. 文件职责

本文件是整个 EMS 3.6 固件的唯一入口与任务总编排点。main() 只做三件事:把 CPU 全中断打开、把时钟/延时/板级外设初始化好、调用 OSInit() 初始化 uCOS-II,然后创建一个最低优先级的 start_task 并 OSStart(),从此再不回到 main()。真正的"业务启动"全部由 start_task 完成——它在临界区内一次性 OSTaskCreateExt() 出 15 个应用任务(数据管理、辅助功能、电表、逆变器、BMS、逻辑调度、接口解析、以太网监控、逆变器 CAN、云通信、以太网、采样、Modbus RTU、并机 SCI、HMI),再由 event_init() 统一创建 8 个串口/采样/掉电信号量和 1 个快调度事件标志组,最后把自己挂起。它处在数据流的最上游:所有模块的任务都是它"派生"出来的,任务优先级决定了下游各模块被调度的相对实时性;本文件也是全局任务计数结构 g_t_task_run_status 的定义者。本文件不含任何储能业务算法。

2. 关键数据结构与宏

2.1 本文件定义/使用的全局对象

名称类型说明
g_t_task_run_status结构体(SYSTEM_TASK_CNT_T)定义于本文件 main.c:31,类型定义在 UCOSII/uCOS-CONFIG/includes.h:29-47,extern 声明于 includes.h:49。含 14 个任务计数 + 1 个 u32_irq_dma_cnt,各任务在自己的 DEBUG_xxx == 1 分支里自增,用于看门狗式任务存活诊断(实际诊断代码在 midware/auxiliary/system_aux.c:1746-1801 已被整段注释掉)
TASK_START_STK[256]OS_STK 数组main.c:33,start_task 任务栈
TASK_DATA_STK[512]OS_STK 数组main.c:35
TASK_AUXILIARY_STK[512]OS_STK 数组main.c:36
TASK_METER_STK[256]OS_STK 数组main.c:37
TASK_INVERTER_STK[512]OS_STK 数组main.c:38
TASK_BMS_STK[256]OS_STK 数组main.c:39
TASK_LOGIC_STK[512]OS_STK 数组main.c:40
TASK_INTERFACE_STK[512]OS_STK 数组main.c:41
TASK_ETHERNET_MONITOR_STK[256]OS_STK 数组main.c:42
TASK_INVERTER_CAN_STK[256]OS_STK 数组main.c:43
TASK_NET_STK[512]OS_STK 数组main.c:44
TASK_ETHERNET_STK[1024]OS_STK 数组main.c:45,全工程最大任务栈(W5500 + 无线 + DHCP 协议栈)
TASK_SAMPLING_STK[256]OS_STK 数组main.c:46
TASK_MODBUS_RTU_STK[256]OS_STK 数组main.c:47
TASK_PARALLEL_SCI_STK[256]OS_STK 数组main.c:48
TASK_HMI_STK[512]OS_STK 数组main.c:49
TASK_USMART_STK[512]OS_STK 数组main.c:51,被 #if DEBUG_SUMART 包裹
g_t_uart0_rcv_sem … g_t_uart5_rcv_semOS_EVENT *main.c:53-58,6 路串口接收完成信号量
g_t_ct_sample_semOS_EVENT *main.c:59,CT 采样 DMA+定时器触发信号量
g_t_power_down_semOS_EVENT *main.c:60,掉电检测 EXTI 中断投递
g_t_fast_dispatch_flagOS_FLAG_GRP *main.c:62,逻辑-逆变器"快调度"流水线事件标志组

2.2 本文件使用的宏(均在别处定义)

名称类型说明
START_TASK_PRIO宏 = 30uUCOSII/uCOS-CONFIG/app_cfg.h:56,启动任务优先级,见备注 4
TASK_SYSTEM_DATA_PRIO宏 = 7uapp_cfg.h:58
TASK_SYSTEM_AUX_PRIO宏 = 8uapp_cfg.h:59
TASK_METER_PRIO宏 = 9uapp_cfg.h:60
TASK_INVERTER_PRIO宏 = 10uapp_cfg.h:61;logic.c:2954 用 OSTimeDlyResume(TASK_INVERTER_PRIO) 主动唤醒它
TASK_BMS_PRIO宏 = 11uapp_cfg.h:62
TASK_LOGIC_CONTROL_PRIO宏 = 12uapp_cfg.h:63
TASK_INTERFACE_PRIO宏 = 13uapp_cfg.h:64
TASK_ETHERNET_MONITOR_PRIO宏 = 14uapp_cfg.h:65
TASK_INVERTER_CAN_PRIO宏 = 15uapp_cfg.h:66
TASK_NET_PRIO宏 = 16uapp_cfg.h:67
TASK_ETHERNET_PRIO宏 = 17uapp_cfg.h:68
TASK_SAMPLING_PRIO宏 = 18uapp_cfg.h:69
TASK_MODBUS_RTU_PRIO宏 = 19uapp_cfg.h:70
TASK_PARALLEL_SCI_PRIO宏 = 20uapp_cfg.h:71
TASK_HMI_PRIO宏 = 21uapp_cfg.h:72
TASK_USMART_PRIO宏 = 22uapp_cfg.h:73(当前未编译)
TASK_*_STK_SIZE宏app_cfg.h:83-100,各任务栈深度(OS_STK 条目数)
DEBUG_OS_EVENT宏 = 0本工程定义于 user/main.h:51;为 1 时打印每个事件对象指针
DEBUG_SUMART宏 = 0定义于 midware/debug/shell_usmart.h:13;为 1 时才编译 USMART 调试任务
OS_TASK_OPT_STK_CHK / OS_TASK_OPT_STK_CLR / OS_TASK_OPT_SAVE_FP宏uCOS-II 任务创建选项:栈检查、栈清零、保存浮点寄存器
OS_TASK_STAT_EN / OS_CPU_CFG_INT_DIS_MEAS_EN宏控制 OSStatInit() 与中断关断时间测量是否编译
OS_LOWEST_PRIO宏 = 63uos_cfg.h:39;OS_TASK_STAT_PRIO = 62、OS_TASK_TMR_PRIO = 53
OS_MAX_TASKS宏 = 20uos_cfg.h:46,任务上限(见备注 4)
OS_MAX_EVENTS宏 = 30uos_cfg.h:42
OS_MAX_FLAGS宏 = 5uos_cfg.h:43
OS_TICKS_PER_SEC宏 = 1000uos_cfg.h:51,即 1 tick = 1 ms
OS_CRITICAL_METHOD宏 = 3uUCOSII/uCOS-II/Ports/ARM-Cortex-M4/Generic/RealView/os_cpu.h:138,临界区采用"保存-恢复 CPU_SR"方式,可安全嵌套(见备注 5)

3. 函数清单

函数名作用域说明
event_initstatic(main.c:74)创建 8 个信号量 + 1 个事件标志组,并给每个对象设置名字
start_taskstatic(main.c:140)启动任务:统计初始化 → 临界区内批量创建 15 个应用任务 → event_init() → 退出临界区 → 挂起自身
main全局(main.c:348)中断/时钟/板级初始化 → OSInit() → 创建 start_task → OSStart(),不再返回

本文件无函数指针、无回调注册;全工程唯一被 main() 直接调用的入口就是 start_task。

4. 核心逻辑解析

4.1 main() 启动时序(main.c:348-369)

步骤行号动作说明
1350INTX_ENABLE()汇编 cpsie i 打开全局中断(system/sys/chip.c:68)
2351systick_config()配置 SysTick 为 1000 Hz(SysTick_Config(SystemCoreClock/1000)),并设 NVIC 优先级 0(system/sys/chip.c:24-33)。uCOS-II 的 tick 由该中断提供,因此 OS_TICKS_PER_SEC 必须与之一致
3352delay_init(SystemCoreClock / 1000000U)以 us 为单位的忙等延时基数
4353Board_init()板级初始化:NVIC → IWDG → GPIO → usart0(115200 调试)、usart1(9600 电表 485)、usart2(9600 逆变器 MDSP)、uart4(9600 BMS 485)、usart5(115200 WiFi)、flash、SDRAM、PCF8563、WS2812、CAN0/CAN1(250 Kbps)、uart3(外部 485,按 g_t_modbus_config.u8_modbus_baud_rate);每步之间 IWDG_Feed(),最后 sys_info_restore 与上电原因记录(system/board_init/board_init.c:217-275)
5355OSInit()初始化 uCOS-II:创建空闲任务(prio 63)、统计任务(prio 62,因 OS_TASK_STAT_EN=1);OS_TMR_EN=0 故不创建定时器任务
6358-366OSTaskCreateExt(start_task, …)优先级 30,栈 256,任务 ID 与优先级相同,选项 `STK_CHK\STK_CLR\SAVE_FP`。注意此时还没有开调度器,所以创建动作是顺序执行的
7368OSStart()启动调度器,从此进入多任务世界,main() 的栈帧永久保留

为何 INTX_ENABLE() 在最前:systick_config() 里 SysTick_Config 会立刻使能 SysTick 中断与异常,若全局中断未开则计数不会走;同时 Board_init() 中的中断驱动外设(串口接收、EXTI 掉电中断)也要求中断已开。

4.2 start_task() 的任务创建表(main.c:140-339)

进入后先做统计初始化,再进临界区批量建任务:

任务创建明细(优先级/栈大小均引用自 app_cfg.h):

行号任务函数优先级栈(字)任务名定义位置周期/触发
156task_system_data7512task_system_datadata/data.c:251OSSemPend(g_t_power_down_sem, 500),500 ms 超时轮询
167task_system_aux8512task_system_auxmidware/auxiliary/system_aux.c:2056OSTimeDlyHMSM(0,0,0,400) + IWDG_Feed()
178task_meter9256task_metermidware/meter/meter.c:5710300 ms 级轮询
189task_inverter10512task_invertermidware/inverter/inverter.c:1045OSFlagPend(PEND_LOGIC_CALCULATE_DONE, 20ms) 阻塞等待逻辑算完
200task_bms11256task_bmsmidware/bms/bms.c:2731100 ms 级 CAN 轮询
211task_system_logic_control12512task_system_logic_controllogic/logic.c:2902OSFlagPend(POST_PEND_FAST_ENABLE, 20ms) + OSTimeDlyHMSM(0,0,0,280)
222task_interface_parse13512task_interface_parsemidware/interface/interface.c:211500 ms(DRM / 干接点 / EPO)
233task_ethernet_monitor14256task_ethernet_monitormidware/ethernt/ethernet.c:1538500 ms
244task_inverter_can15256task_inverter_canmidware/inverter/inverter.c:1199OSFlagPend(PEND_INV_CALCULATE_DONE, 20ms, CONSUME)
255task_net_app16512task_net_appnet/ems.c:7250OSTimeDlyHMSM(0,0,0,25)
266task_ethernet171024task_ethernetmidware/ethernt/ethernet.c:986OSQPend(g_pEthernetQ, 10)
277task_sampling18256task_samplingmidware/meter/ct.c:844OSSemPend(g_t_ct_sample_sem, 100)
288task_modbus_rtu19256task_modbus_rtumidware/interface/interface.c:298100 ms 级 / 场景切换
299task_parallel_sci20256task_modbus_rtu(名字写错,见备注 1)midware/inverter/inverter.c:1296OSTimeDlyHMSM(0,0,0,100)
310task_hmi_parse21512task_hmi_parsehmi/key.c:888810 ms 级(按键/ LED / LCD)
322task_usmart22512task_usmart_parsemidware/debug/shell_usmart.c:54#if DEBUG_SUMART,当前不编译

任务总数核对:应用任务 15 个 + start_task 自身 1 个 + uCOS-II 统计任务(prio 62)1 个 + 空闲任务(prio 63)1 个 = 18 个 TCB,上限 OS_MAX_TASKS = 20;若打开 DEBUG_SUMART 则为 19 个。

优先级设计要点:数字越小优先级越高。task_system_data(7) 优先级最高——它承担掉电保存,必须在断电电容能量耗尽前抢到 CPU;task_ethernet(17) 虽然只排第 11 位,但因为 OSQPend 超时只有 10 tick,实际跑得最勤;task_hmi_parse(21) 排在应用任务末位,人机界面响应可以被任何测量/控制任务抢占。START_TASK_PRIO = 30 高于所有应用任务,保证启动阶段不被抢占。

4.3 event_init() 事件对象创建(main.c:74-131)

全部创建在 start_task 的临界区内按固定顺序执行:

行号对象初值名字生产者(OSSemPost/OSFlagPost)消费者(OSSemPend/OSFlagPend)
78g_t_uart0_rcv_sem0uart0_rcv_semhardware/usart0/usart0.c:92(接收中断)hardware/usart0/usart0.c:212(调试口收帧,500 tick 超时)
84g_t_uart1_rcv_sem0uart1_rcv_semhardware/usart1/usart1.c:303(帧结束 + 总线静默 3.5T)hardware/usart1/usart1.c:256(电表 RS485)
90g_t_uart2_rcv_sem0uart2_rcv_semhardware/usart2/usart2.c:94hardware/usart2/usart2.c:213(逆变器 MDSP)
96g_t_uart3_rcv_sem0uart3_rcv_semhardware/usart3/usart3.c:123hardware/usart3/usart3.c:277、midware/interface/interface.c 的 task_modbus_rtu(外部 485 / Modbus RTU)
102g_t_uart4_rcv_sem0uart4_rcv_semhardware/usart4/usart4.c:120hardware/usart4/usart4.c:325(BMS RS485)
108g_t_uart5_rcv_sem0uart5_rcv_semhardware/usart5/usart5.c:99midware/ethernt/wireless/elink_parse.c:108(WiFi 模组 AT 通路)
114g_t_ct_sample_sem0ct_sample_semmidware/meter/ct.c:834(定时器/DMA 中断)midware/meter/ct.c:883 的 task_sampling(100 tick 超时)
120g_t_power_down_sem0g_t_power_down_semhardware/exti/exti.c:95(掉电检测 EXTI)data/data.c:263 的 task_system_data(500 tick 超时)
126g_t_fast_dispatch_flag0(FAST_DISPATCH_IDLE)fast_dispatch_flag见 4.4见 4.4

所有信号量初值均为 0,即"上电即阻塞",必须由中断或其它任务显式投递。

4.4 快调度事件标志流水线 g_t_fast_dispatch_flag

这是一条典型的三任务流水线握手,标志位定义在 logic/logic.h:121-130:

枚举值(二进制)置位方说明
FAST_DISPATCH_IDLE0—初值,全清
POST_PEND_FAST_ENABLE0b0001net/server.c:838云端下发快速调度请求 → bit0 置位,作为整条流水线的"门控"位
POST_LOGIC_CALCULATE_DONE0b0010logic/logic.c:2953逻辑算完 → bit1 置位
PEND_LOGIC_CALCULATE_DONE0b0011—逆变器等待掩码:需要 bit0+bit1 同时置位
POST_INV_CALCULATE_DONE0b0100midware/inverter/inverter.c:1185逆变器算完 → bit2 置位
PEND_INV_CALCULATE_DONE0b0111—CAN 任务等待掩码:bit0+bit1+bit2,且 `OS_FLAG_WAIT_SET_ALL \OS_FLAG_CONSUME`

数据流:server.c 置 bit0 → task_system_logic_control(logic.c:2912,OS_FLAG_WAIT_SET_ALL 掩码 1、不消费、20 tick 超时)醒来,把 5 个软定时 tick 清零(logic.c:2916-2920,即强制跳过所有节流间隔立刻算一次)→ 算完后置 bit1 并 OSTimeDlyResume(TASK_INVERTER_PRIO)(logic.c:2951-2955)→ task_inverter 的 OSFlagPend(掩码 3) 满足,清零自己的查询 tick 后写逆变器控制功率 → 置 bit2 → task_inverter_can 以掩码 7 + CONSUME 收尾,一次性把 bit0/bit1/bit2 全部清掉,标志组回到 FAST_DISPATCH_IDLE。因此整条链是"一次外部触发 = 一次完整下发",如果某个环节超时(20 tick = 20 ms),对应任务仍会按普通周期节流继续运行,只是失去了"立即响应"的加速效果。

4.5 定时/调度时序小结

4.6 关键全局变量在本文件中的读写

本文件只定义、几乎不修改任何跨模块变量:

5. 对外接口

本文件导出(供外部使用)

名称类型使用方
g_t_task_run_status全局变量各任务的调试计数自增;extern 声明在 UCOSII/uCOS-CONFIG/includes.h:49。midware/auxiliary/system_aux.c:1746-1801 有整段被注释的"任务存活检测"消费者
g_t_uart0_rcv_sem … g_t_uart5_rcv_sem全局变量hardware/usart0…usart5/*.c、midware/ethernt/wireless/elink_parse.c、midware/interface/interface.c(经 user/main.h 的 extern)
g_t_ct_sample_sem全局变量midware/meter/ct.c
g_t_power_down_sem全局变量hardware/exti/exti.c:95(投递)、data/data.c:263(等待)
g_t_fast_dispatch_flag全局变量net/server.c:838、logic/logic.c:2912/2953、midware/inverter/inverter.c:1054/1185/1207
main函数由启动代码 user/startup_gd32f407.s 的 Reset_Handler 调用
event_init、start_taskstatic不导出,仅本文件内使用

本文件调用(外部符号)

调用来源模块
INTX_ENABLE、systick_configsystem/sys/chip.c
delay_initsystem/delay/delay.c
Board_initsystem/board_init/board_init.c
OSInit、OSTaskCreateExt、OSTaskNameSet、OSSemCreate、OSEventNameSet、OSFlagCreate、OSFlagNameSet、OSTaskSuspend、OSStart、OSStatInit、CPU_IntDisMeasMaxCurReset、OS_ENTER_CRITICAL / OS_EXIT_CRITICALUCOSII/uCOS-II + UCOSII/uCOS-II/Ports/ARM-Cortex-M4/Generic/RealView
15 个任务函数data/data.c、midware/auxiliary/system_aux.c、midware/meter/meter.c、midware/meter/ct.c、midware/inverter/inverter.c、midware/bms/bms.c、logic/logic.c、midware/interface/interface.c、midware/ethernt/ethernet.c、net/ems.c、hmi/key.c、midware/debug/shell_usmart.c

6. 依赖关系

依赖(本文件 #include):chip.h、delay.h、board_init.h、ethernet.h、key.h、logic.h、ems.h、meter.h、bms.h、inverter.h、data.h、interface.h、system_aux.h、ct.h、shell_usmart.h(main.c:13-27)。其中 logic.h 再 #include "includes.h"(logic.h:13),SYSTEM_TASK_CNT_T 与 g_t_task_run_status 的 extern 正是从 includes.h:29-49 得到的——除此以外 chip.h 只引入 gd32f4xx.h,并不提供 includes.h 链。

被依赖(谁 include user/main.h,共 20 个文件):

user/main.c 本身不被任何文件 include;它由工程文件 user/EMS_3_0.uvprojx(<FileName>main.c</FileName>,路径 .\main.c)直接编译。

7. 备注与风险点

  1. 任务名复制粘贴错误:main.c:308 把并机 SCI 任务命名为 "task_modbus_rtu",与 main.c:297 的 Modbus 任务重名。两个任务共享同一 OSTaskNameGet/调试视图名,任何按名字定位任务的调试工具都会认错;而该任务的优先级 TASK_PARALLEL_SCI_PRIO = 20 与名字也不匹配。应改为 "task_parallel_sci"。
  2. 启动任务永不删除:main.c:338 用 OSTaskSuspend(OS_PRIO_SELF) 而非 OSTaskDel(OS_PRIO_SELF),start_task 永久占用 1 个 TCB。当前 18/20 已用(开启 DEBUG_SUMART 为 19/20),余量仅 1~2 个;后续若再增加任务会撞到 OS_MAX_TASKS 上限,OSTaskCreateExt 将返回 OS_ERR_TASK_NO_MORE_TCB 而本文件完全不检查返回值,失败时静默无声,只表现为"某个功能永远不跑"。
  3. 所有 OSTaskCreateExt 返回值均未校验:main.c:156-332 共 16 处创建调用,OSTaskNameSet 的错误码也只是丢弃在 err 里。创建失败时任务不会运行,也不会打印任何信息(DEBUG_OS_EVENT 只打印事件对象,不打印任务)。
  4. START_TASK_PRIO 与 OS_TASK_TMR_PRIO 同域但无序依赖:START_TASK_PRIO = 30,而 OS_TASK_TMR_PRIO = OS_LOWEST_PRIO - 10 = 53。当前 OS_TMR_EN = 0,不冲突;但如果哪天打开定时器管理,需要重新核对优先级合法性(uCOS-II 要求 0..63 且不与系统任务重叠,30 仍在合法范围,只是语义上"启动任务比定时器任务优先级高")。
  5. 关中断窗口偏长:main.c:154 到 main.c:336 之间约 180 行、16 次任务创建 + 8 次 OSSemCreate + 9 次 OSEventNameSet/OSFlagNameSet 全部在中断关闭状态下执行。得益于 OS_CRITICAL_METHOD == 3(save/restore CPU_SR,可嵌套),功能正确;但这段时间内 SysTick 与所有串口/CAN 中断都被屏蔽,若某一帧在此时到达会丢失。这是上电一次性的行为,风险可接受,但把 event_init() 移到临界区外(OSFlagCreate/OSSemCreate 自身是原子操作)可以显著缩短关中断时间。
  6. p_arg = p_arg;(main.c:144):自赋值语句,实际作用是压制"未使用参数"告警。uCOS-II 的惯例写法是 (void)p_arg;(data.c:253 等文件就是这么写的),本文件与工程其余部分风格不一致,可读性差。
  7. OS_CPU_SR cpu_sr = 0;(main.c:142):仅在 OS_CRITICAL_METHOD==3 时被宏使用;若切换到 method 1/2,该变量会变成未使用变量。属于对 uCOS 配置的隐式耦合。
  8. 事件名带 g_t_ 前缀的不一致:8 个信号量里 7 个名字与变量名一致,唯独 g_t_power_down_sem 的名字是 "g_t_power_down_sem"(main.c:124)——即故意带上了 g_t_ 前缀(其余为 "uart0_rcv_sem" 这种去掉前缀的写法)。无功能影响,仅名字风格混乱。
  9. OSEventNameSet 的 &err 未检查:main.c:82-130 共 9 处,err 反复覆盖。若 OS_EVENT_NAME_EN 被关掉,这些调用会返回错误但无人知晓(当前 os_cfg.h:37 为 1,安全)。
  10. main() 中的 delay_init(SystemCoreClock / 1000000U)(main.c:352):SystemCoreClock 由 SystemInit() 在启动文件中设置,若时钟树未按预期配置(例如外部 HXTAL 起振失败回落),这里的除法基数是错的,会导致 us 级延时全部失准。当前工程依赖 system_gd32f4xx.c 的默认配置,属隐含假设。
  11. g_t_task_run_status 的消费者已全部被注释:midware/auxiliary/system_aux.c:1746-1801 的"任务死掉检测"整段被注释掉,而这个结构体的一半写操作本身又被 DEBUG_* 宏(当前为 0)包裹,导致该诊断机制实际上是完全失效的死代码:既不采集也不上报。
  12. DEBUG_SUMART 全局为 0:midware/debug/shell_usmart.h:13 定义为 0,因此 TASK_USMART_STK(main.c:51)和 task_usmart 创建(main.c:321-332)都不编译。USMART shell 在量产固件中不可用,现场调试只能靠 usart0 的 printf。