@@ -0,0 +1,325 @@
# 程序流程与函数说明
## 1. 文档范围
本文按当前源码(2026-07-19)说明 `apps/chrg/applications/` 的自研函数、运行流程和函数级调用关系,并补充 `apps/boot/` 的启动主链。`rt-thread/` 、`libs/` 、CubeMX 生成代码、HAL 中断模板以及调试 shell 命令的内部库调用不展开。调用关系由源码静态分析得到;通过 `INIT_*_EXPORT` 、线程入口、回调和函数指针发生的调用会单独标明。
## 2. 上电与初始化流程
``` mermaid
flowchart TD
R["芯片复位"] --> B["Boot main()"]
B --> SI["System_Init()"]
SI --> AI["APP_Init()"]
AI --> AR["APP_Running()"]
AR --> CK["boot_CheckAndHandle()"]
CK -->|APP 有效| JA["boot_JumpToAPP()"]
CK -->|需要升级| RX["recvDataHandler()"]
RX --> YH["ymodemHandler()"]
YH --> BP["boot_ProtocolDataHandle()"]
BP --> FM["FM_* 擦写/校验函数"]
FM --> JA
JA --> RTT["RT-Thread 启动"]
RTT --> INIT["INIT_PREV/DEVICE/ENV/APP_EXPORT"]
INIT --> TI["chrg_thread_init()"]
TI --> TC["chrg_thread_create() x 6"]
TC --> MAIN["main() 空闲循环"]
```
`main()` 不是业务入口。GPIO、FAL、定时器、串口、继电器和看门狗由 RT-Thread 自动初始化段调用;`chrg_thread_init()` 再创建六个常驻线程。初始化失败目前不会阻止后续线程继续创建,排障时必须查看启动日志。
## 3. 运行期线程与数据流
| 线程入口 | 职责 | 主要输入 | 主要输出 |
|---|---|---|---|
| `chrg_north_thread_entry` | 接收协议板/Sink/Source 北向帧 | 北向 UART ringbuffer | 更新 `chrgnorth.sw[]` ,投递帧 |
| `chrg_south_thread_entry` | 接收功率板 Modbus 响应 | 南向 UART ringbuffer | 投递响应到轮询/通信模块 |
| `chrg_comm_thread_entry` | 上位机 Modbus、寄存器映射和业务命令 | mailbox、Modbus 回调 | 修改目标状态,生成南北向命令 |
| `chrg_lcd_thread_entry` | HMI 报文解析和状态显示 | LCD 串口 | 修改配置、刷新页面 |
| `chrg_roll_nor_thread_entry` | 分时驱动四路 Sink/Source 协议板 | event、通道状态 | 查询/设置协议板,运行 Sink 状态机 |
| `chrg_roll_sou_thread_entry` | 串行执行功率板寄存器读写 | event、批命令队列 | Modbus RTU 请求与完成状态 |
``` mermaid
flowchart LR
PC["上位机"] --> MB["Modbus 回调 func* / mb_reg_*_cb"]
LCD["HMI"] --> LP["chrg_lcd_parse()"]
MB --> CM["chrg_comm_parse_msg()"]
LP --> ST["chrgnorth.sw[] 目标状态"]
CM --> ST
RN["chrg_roll_nor_thread_entry()"] --> SET["chrg_roll_nor_set()"]
ST --> SET
SET --> SK["sink/source 设置函数"]
SK --> NTX["chrg_north_send()"]
DEV["协议板"] --> NRX["chrg_north_thread_entry()"]
NRX --> FD["chrg_north_frame_dispatch()"]
FD --> SP["chrg_sink_parse() / Source 解析"]
SP --> SW["chrg_sink_work()"]
SW --> SR["chrg_set_sou_reg()"]
CM --> SR
SR --> RS["chrg_roll_sou_thread_entry()"]
RS --> STX["chrg_south_send()"]
PWR["功率板"] --> SRX["chrg_south_thread_entry()"]
SRX --> SD["chrg_south_frame_dispatch()"]
SD --> RS
```
## 4. Sink 工作状态机
``` mermaid
stateDiagram-v2
[*] --> INIT
INIT --> WAIT: 下发停止/安全继电器状态
WAIT --> RUNING: 电压正常且接近目标
RUNING --> WAIT: 电压正常但电流连续偏低
RUNING --> LOW: 电压连续低于 WORK_MIN_VOLT
WAIT --> LOW: 电压连续过低
LOW --> WAIT: 电压恢复并重新准备
RUNING --> OFF: 停止工作
WAIT --> OFF: 停止工作
```
函数级主链:`chrg_roll_nor_thread_entry()` → `chrg_roll_nor_set()` → `sink_get_vc()` → 北向响应 → `chrg_north_frame_dispatch()` → `chrg_sink_parse()` → `chrg_sink_work()` → `chrg_sink_work_init()` / `chrg_sink_work_volt_runing()` / `chrg_sink_nor_work_low()` / `chrg_sink_nor_work_off()` 。继电器动作由 `chrg_sink_change_ON()` 、`chrg_sink_SHOUT_ON()` 、`chrg_nor_sw_rel()` 完成,功率板命令最终经 `chrg_set_sou_reg()` 交给南向轮询线程。
## 5. 函数说明:入口与线程基础设施
### `main.c`
| 函数 | 说明 |
|---|---|
| `show_app_version()` | 打印 RT-Thread 版本和固件编译时间,无输入和返回值。 |
| `main()` | 设置 shell 提示符后进入 1 秒延时循环;业务线程由自动初始化段提前创建。 |
### `thread/chrg_thread.c`
| 函数 | 说明 |
|---|---|
| `chrg_thread_evt_send()` | 向指定线程发送事件位;线程或事件对象无效时返回错误。 |
| `chrg_thread_mb_free()` | 释放邮箱消息及其 `payload` ,仅供本文件失败路径使用。 |
| `chrg_thread_mb_send()` | 向指定线程邮箱转移消息所有权;发送失败时自动释放消息。 |
| `chrg_thread_mq_send()` | 向指定线程消息队列复制定长消息。 |
| `chrg_thread_create()` | 按 `chrgthr[]` 配置创建 IPC 对象、线程并启动线程。 |
| `chrg_thread_init()` | 遍历六个线程配置并创建;由 `INIT_APP_EXPORT` 隐式调用。 |
## 6. 函数说明:通信线程
### `thread/chrg_north.c`
| 函数 | 说明 |
|---|---|
| `chrg_compare()` | 计算两个有符号量的绝对差值。 |
| `chrg_source_stop()` | 按步骤停止 Source 输出并处理继电器/状态复位。 |
| `chrg_source_standard_output()` | 执行 Source 标准输出控制流程。 |
| `chrg_source_fast_output()` | 执行 Source 快充协议输出流程。 |
| `chrg_source_setting()` | 根据 Source 当前配置选择和推进设置过程。 |
| `chrg_sink_parse()` | 解析 Sink 电压、电流及状态,做低压/无流判定并调用 `chrg_sink_work()` 。 |
| `chrg_north_handle_Seting_Volt()` | 解析北向设置电压类响应并更新通道状态。 |
| `chrg_north_switch()` | 选择当前北向物理通道。 |
| `chrg_north_send()` | 通过北向 TTY 发送一帧数据。 |
| `chrg_north_frame_head()` | 从 ringbuffer 搜索合法帧头。 |
| `chrg_north_frame_reset()` | 重置组帧上下文,并按参数决定是否释放缓存。 |
| `chrg_north_frame_append()` | 将接收字节追加到当前帧缓存。 |
| `chrg_north_frame_dispatch()` | 校验完整帧并分派到 Source/Sink 业务解析。 |
| `chrg_north_pickup_frame()` | 驱动“找帧头—收帧体—分派”状态机。 |
| `chrg_north_thread_entry()` | 北向接收线程主循环,从 UART ringbuffer 持续提取帧。 |
| `north_ch()` / `north_diag()` | FinSH 调试命令:切换通道、查看诊断计数。 |
### `thread/chrg_south.c`
| 函数 | 说明 |
|---|---|
| `chrg_south_switch_lv1()` / `chrg_south_switch_lv2_1()` / `chrg_south_switch_lv2_2()` | 控制级联选择线,完成南向通道硬件选通。 |
| `chrg_south_switch()` | 校验通道并组合调用三级选择函数。 |
| `chrg_south_send()` | 通过南向 TTY 发送 Modbus RTU 帧。 |
| `chrg_south_frame_head()` | 从 ringbuffer 识别目标地址和功能码。 |
| `chrg_south_frame_reset()` | 清理南向组帧状态和动态缓存。 |
| `chrg_south_frame_append()` | 追加南向接收字节。 |
| `chrg_south_frame_dispatch()` | 校验 CRC 后将响应投递给消费者。 |
| `chrg_south_pickup_frame()` | 推进南向组帧状态机。 |
| `chrg_south_thread_entry()` | 南向接收线程主循环。 |
| `south_ch()` | FinSH 南向通道调试命令。 |
### `thread/chrg_roll_nor.c`
| 函数 | 说明 |
|---|---|
| `sink_fill_pd_buf()` | 按协议类型把组号、电压、电流编码为 Sink 参数字节。 |
| `chrg_nor_source_stop()` | 分步骤停止 Source,关闭继电器并清理切换标志。 |
| `chrg_roll_nor_sw_uart()` | 在指定通道选择 Sink 或 Source UART。 |
| `chrg_roll_nor_sink_pro_set()` | 组装并发送 Sink 快充协议设置。 |
| `chrg_roll_nor_source_pro_set()` | 按设置来源选择 Source 协议下发函数。 |
| `chrg_roll_nor_parse()` | 根据角色和子命令生成一次北向查询/设置动作。 |
| `chrg_roll_nor_cmd()` | 北向命令参数检查接口;当前仅做空指针检查。 |
| `chrg_roll_nor_set()` | 北向轮询核心状态机,推进每通道 Source/Sink 子步骤。 |
| `chrg_roll_nor_thread_entry()` | 轮询四个北向通道,等待响应并控制超时/重试。 |
| `sink()` / `source()` | FinSH 手工配置 Sink/Source 的调试入口。 |
| `chrg_trim_crc()` | 计算校准数据 CRC。 |
| `chrg_trim_set_volt()` / `chrg_trim_set_curr()` / `chrg_trim_set_source_curr()` | 写入电压、电流及 Source 电流校准值。 |
| `chrg_trim_set_zero()` | 执行零点校准设置。 |
| `chrg_roll_trim_set()` | 按校准类型分派具体校准函数。 |
### `thread/chrg_roll_sou.c`
| 函数 | 说明 |
|---|---|
| `chrg_sou_com_batch_submit()` | 提交一批南向连续写寄存器命令。 |
| `chrg_sou_com_batch_add_reg()` | 向当前批次追加通道、寄存器和值。 |
| `chrg_set_sou_reg()` | 提交单个异步寄存器写命令。 |
| `chrg_set_sou_reg_com()` | 将寄存器写入合并批次。 |
| `sou_com_batch_next()` | 在锁保护下取下一条批命令。 |
| `sou_com_batch_complete()` | 标记批命令完成并推进队列。 |
| `sou_com_batch_cancel()` | 取消指定通道批命令。 |
| `sou_com_batch_pending_locked()` | 判断锁内是否还有待处理批命令。 |
| `sou_single_command_finish()` | 结束单命令,恢复期望运行状态。 |
| `sou_com_response_matches()` | 判断响应是否匹配当前请求。 |
| `sou_msg_free()` | 释放南向邮箱消息。 |
| `sou_com_command_failed()` | 统一处理超时/协议错误和批次回滚。 |
| `chrg_roll_sou_thread_entry()` | 串行发送南向命令、等待响应并处理重试。 |
| `south_send()` / `south_run()` | FinSH 发送寄存器和启停调试命令。 |
## 7. 函数说明:业务与协议
### `utils/chrg_sink.c`
| 函数 | 说明 |
|---|---|
| `sink_get_vc()` / `sink_get_pd()` | 生成 Sink 电压电流/协议查询帧。 |
| `sink_set_cc_line()` / `sink_set_cc_lvl()` | 设置 CC 线选择和电平。 |
| `sink_set_pd()` | 编码并发送 Sink 快充协议参数。 |
| `chrg_sink_work_init()` | 初始化工作步骤并下发停止/安全继电器命令。 |
| `chrg_sink_work_volt_runing()` | 在电压满足条件时推进 RUNING 动作。 |
| `chrg_sink_nor_work_low()` | 处理持续低压状态并复位运行条件。 |
| `chrg_sink_nor_work_off()` | 执行 Sink 停机收尾。 |
| `chrg_sink_change_ON()` / `chrg_sink_SHOUT_ON()` | 控制换向和短接相关继电器步骤。 |
| `chrg_sink_work()` | 根据 `work_mode` 分派 Sink 工作状态。 |
### `utils/chrg_source.c`
| 函数组 | 说明 |
|---|---|
| `source_get_vc()` / `source_get_pd()` | 生成 Source 电参/协议查询帧。 |
| `source_set_cc_lvl()` / `source_set_cc_choose()` | 配置 Source CC 电平和选择方式。 |
| `parse_mulit_gear_One()` / `parse_power_single_gear()` | 解析单组固定档快充能力。 |
| `parse_pd_11group_pdo()` / `parse_power_pdo_gear()` | 解析 PDO/PPS 多档能力。 |
| `parse_mulit_gear_more()` / `parse_multi_gear_more()` | 解析多组多档协议能力。 |
| `parse_ufcs_gear_more()` / `parse_power_ufcs()` | 解析 UFCS 档位。 |
| `pd_fill_single_group()` | 通用单组协议参数编码。 |
| `source_set_pd()` | 按协议、电压和电流生成 Source 设置帧。 |
| `pd_fill_single_group_com()` | 按通信寄存器档位编码单组参数。 |
| `pd_fill_single_group_need_src_com()` / `pd_fill_single_group_no_src_com()` | 分别编码需要/不需要 Source 参数的协议。 |
| `pd_fill_single_group_FCP_AFC()` / `pd_fill_single_group_UFCS()` | 编码 FCP/AFC 和 UFCS 专用参数。 |
| `source_set_pd_com()` / `source_set_pd_new_com()` | 根据寄存器配置或完整通道状态下发 Source 协议。 |
### `utils/chrg_eload.c`
| 函数组 | 说明 |
|---|---|
| `chrg_eload_set_ymodem_resp()` / `chrg_eload_get_ymodem_resp()` / `chrg_eload_clear_ymodem_resp()` | 设置、读取和清除电子负载升级响应标志。 |
| `memcmp_8bytes()` | 比较固定 8 字节标识。 |
| `chrg_eload_read_regs()` / `chrg_eload_write_reg()` / `chrg_eload_write_More_reg()` | 生成电子负载 Modbus 读、单写和多写请求。 |
| `chrg_eload_send_ymodem_data()` | 转发 YModem 升级数据。 |
| `chrg_write_cv()` | 写入电子负载 CV 参数。 |
| `chrg_eload_send_relay()` | 下发电子负载继电器动作。 |
| `chrg_eload_refresh()` | 刷新电子负载测量状态。 |
| `chrg_eload_parse()` | 解析电子负载响应并更新通道数据。 |
| `snp_ch()` | 选择电子负载通道。 |
| `Show_Volt()` / `Show_Curr()` / `Show_Ripple()` / `Show_DZF()` | 计算或刷新对应测量显示项。 |
| `Sta_compute()` | 汇总测量状态。 |
| `LCD_SHOW()` | 将电子负载测量结果推送到 LCD。 |
### `utils/chrg_north_pkg.c` 与 `utils/chrg_utils.c`
| 函数 | 说明 |
|---|---|
| `chrg_north_pkg_encode()` / `chrg_north_pkg_decode()` | 编码和解码北向协议包,并校验长度/校验值。 |
| `mb_crc16()` | 计算 Modbus CRC16。 |
| `_crc8()` / `calc_crc8()` | 单字节推进及整段 CRC8 计算。 |
| `u16_to_u8v()` / `u16_to_u8v_com()` / `u8v_to_u16()` | 16 位整数与字节序列互转。 |
| `u32_to_u8v()` / `u8v_to_u32()` | 32 位整数与字节序列互转。 |
| `swap_u16()` / `swap_u32()` | 交换 16/32 位整数端序。 |
## 8. 函数说明:上位机、LCD 与板级封装
### `thread/chrg_comm.c`
| 函数组 | 说明 |
|---|---|
| `chrg_shadow_sync()` / `chrg_shadow_init()` | 同步/初始化 Modbus 寄存器影子数据。 |
| `mb_set_bits()` / `mb_get_bits()` | 在 Modbus 位缓冲区写入/读取指定位段。 |
| `mb_reg_input_cb()` / `mb_reg_holding_cb()` / `mb_reg_test_cb()` | 输入、保持和测试寄存器回调。 |
| `mb_ymodem_updata_cb()` | 处理升级寄存器数据。 |
| `mb_reg_coils_cb()` / `mb_reg_discrete_cb()` | 线圈与离散输入回调。 |
| `funcReadCoils()` / `funcReadDiscreteInputs()` / `funcReadHoldingRegister()` / `funcReadInputRegister()` | 实现对应 Modbus 读功能。 |
| `funcWriteCoil()` / `funcWriteHoldingRegister()` / `funcWriteMultipleCoils()` / `funcWriteMultipleHoldingRegister()` | 校验并执行对应写功能。 |
| `sink_proto_lookup()` / `src_proto_lookup()` / `src_type_lookup()` | 将寄存器枚举映射为内部协议/Source 类型。 |
| `parse_load_no_group()` / `parse_load_with_group()` | 解析无组号/带组号的负载协议参数。 |
| `chrg_com_source_on()` | 根据通信配置启动 Source 流程。 |
| `funcWriteProtoCal()` / `funcSinkTest()` / `funcTrimELoad()` | 处理协议校准、Sink 测试和电子负载校准命令。 |
| `funYmodemUpadata()` | 推进固件升级命令。 |
| `chrg_comm_parse_msg()` | 解析通信线程邮箱消息并分派功能码。 |
| `chrg_comm_updata_north()` | 将实时北向状态同步到寄存器区。 |
| `chrg_comm_thread_entry()` | 初始化 Modbus 从站并循环处理消息/状态同步。 |
| `chrg_mb_write_sink()` / `chrg_mb_write_src()` / `chrg_mb_write_sys()` | 将保持寄存器变化写入 Sink、Source 和系统状态。 |
| `chrg_sink_get_proto_diff()` | 比较目标与实际 Sink 协议参数。 |
| `chrg_sink_diff_send_reg()` | 仅发送存在差异的 Sink 寄存器。 |
| `set_ymodem_update_channel()` / `mb_error()` / `rs485snd()` | 分别设置升级通道、转换错误码和提供 RS485 调试命令。 |
### `thread/chrg_lcd.c`
| 函数组 | 说明 |
|---|---|
| `chrg_sou_work()` / `chrg_sou_stop()` | 启动/停止指定功率通道。 |
| `chrg_pro_set()` | 应用 LCD 输入的快充协议配置。 |
| `chrg_set_eload_sink()` / `chrg_set_eload_source()` | 配置电子负载的 Sink/Source 角色。 |
| `chrg_set_CV()` / `chrg_set_CC()` / `chrg_roll_set_cv()` | 设置 CV、CC 及轮询相关目标值。 |
| `chrg_lcd_parse()` | 解析 HMI 命令并修改对应通道配置。 |
| `chrg_lcd_send()` | 向 HMI 发送数据。 |
| `chrg_lcd_thread_entry()` | LCD 串口收发与周期刷新主循环。 |
| `lcdsend()` | FinSH LCD 原始发送调试命令。 |
### `bsp/`
| 模块 | 函数说明 |
|---|---|
| `chrg_fal.c` | `chrg_fal_read()` 、`chrg_fal_write()` 、`chrg_fal_erase()` 、`chrg_fal_erase_all()` 封装分区访问;`chrg_fal_init()` 初始化 FAL。 |
| `chrg_gpio.c` | `chrg_gpio_mode_set()` 、`chrg_gpio_get()` 、`chrg_gpio_set()` 封装 GPIO; `chrg_gpios_init()` 初始化引脚。 |
| `chrg_rel.c` | `chrg_nor_sw_rel()` 控制北向继电器;`chrg_nor_rel_init()` 初始化;`rel_test()` 用于 shell 测试。 |
| `chrg_switch.c` | `chrg_switch_ch()` 驱动通道译码选择线。 |
| `chrg_vcom.c` | `chrg_vcom_init()` 初始化 USB 虚拟串口;`chrg_vcom_console_init()` 注册控制台。 |
| `chrg_wdt.c` | `chrg_wdt_feed()` 喂狗;`chrg_wdt_init()` 初始化并启动看门狗。 |
| `chrg_tmr.c` | `chrg_tmr_get_count()` 、`chrg_tmr_start()` 、`chrg_tmr_stop()` 、`chrg_tmr_release()` 、`chrg_tmr_open()` 管理软件秒定时器;`chrg_tmr_1s_callback()` 计时;`chrg_tmrs_init()` 初始化;`uptime()` 、`sec_tmr()` 为 shell 命令。 |
| `chrg_led.c` | `chrg_led_get()` 、`chrg_led_set()` 、`chrg_led_toggle()` 、`chrg_led_pulsing()` 、`chrg_led_flashing()` 控制 LED; `chrg_led_timer_set_on()` 、`chrg_led_timer_set_off()` 、`chrg_led_timer_start()` 、`chrg_led_timer_delete()` 、`chrg_led_timer_timeout_callback()` 、`chrg_led_show()` 管理闪烁时序;`chrg_leds_init()` 初始化。 |
| `chrg_tty.c` | `chrg_tty_send_comp_hook()` 、`chrg_tty_recv_ind_hook()` 为驱动回调;`chrg_tty_cal_byte_tmo()` 、`chrg_tty_mode_set()` 、`chrg_tty_dev_open()` 、`chrg_tty_config()` 配置串口;`chrg_tty_connect()` 、`chrg_tty_disconn()` 、`chrg_tty_destory()` 管理连接;`chrg_tty_set_recv_tmo()` 、`chrg_tty_set_byte_tmo()` 设置超时;`chrg_tty_recv()` 、`chrg_tty_send()` 、`chrg_tty_break_recv()` 、`chrg_tty_send_then_recv()` 完成阻塞式收发。 |
## 9. Bootloader 函数级主链
``` mermaid
flowchart TD
M["main()"] --> SI["System_Init()"]
M --> GPIO["MX_GPIO_Init() / MX_DMA_Init() / MX_USART_Init()"]
M --> AI["APP_Init()"]
AI --> DTI["DT_Init()"]
AI --> YI["ymodem_init()"]
AI --> FI["FM_Init()"]
M --> AR["APP_Running()"]
AR --> RD["recvDataHandler()"]
RD --> DPR["DT_PollingReceive()"]
RD --> YH["ymodemHandler()"]
YH --> BPD["boot_ProtocolDataHandle()"]
AR --> BCH["boot_CheckAndHandle()"]
BCH --> BCF["boot_CheckFirmware()"]
BCH --> BAU["boot_AutoUpdate()"]
BAU --> FMR["FM_ReadFirmwareHead()"]
BAU --> FMV["FM_VerifyFirmware()"]
BAU --> FMU["FM_UpdateToAPP()"]
BCH --> J["boot_JumpToAPP()"]
```
Boot 辅助模块职责:`firmware.c` 的 `FM_*` 函数负责分区头、擦写、CRC 校验、版本和搬运;`ymodem.c` 的 `ymodemHandler()` 、`ymodemConfig()` 负责升级协议状态机;`transfer.c` 的 `DT_Init()` 、`DT_Send()` 、`DT_PollingReceive()` 负责串口帧传输;`drv_timer.c` 提供启动、重启、暂停、注销、处理和用户数据绑定;`bsp_uart.c` /`bsp_uart_port.c` 提供 UART、DMA、中断及接收完成回调。
## 10. 阅读与调试顺序
1. 启动异常先看 `chrg_thread_init()` 和各 `INIT_*_EXPORT` 初始化返回值。
2. 北向无数据依次跟踪 `chrg_north_thread_entry()` 、`chrg_north_frame_head()` 、`chrg_north_pickup_frame()` 、`chrg_north_frame_dispatch()` 。
3. Sink 不启动依次检查 `chrg_sink_parse()` 的电压/电流判定、`work_mode` 、`chrg_sink_work()` 和 `chrg_set_sou_reg()` 。
4. 功率板命令未生效依次检查 `chrg_set_sou_reg()` 、`chrg_roll_sou_thread_entry()` 、`chrg_south_send()` 和南向响应匹配。
5. 上位机写入未生效依次检查 Modbus `funcWrite*()` 、`chrg_mb_write_*()` 、共享状态和两个轮询线程。