Files
chrg/docs/程序流程与函数说明.md
T
yhf b68ffe3bf3 chore: 优化批量下发缓存与测试模式逻辑
解决问题:南向队列容量的溢出风险,以及状态机的错别字还有快速启动加动态测试的导致没有继电器开启的问题
1.  apps/chrg/applications/thread/chrg_roll_sou.h
   - 调整SOU_COM_BATCH_REG_MAX为32并补充注释说明预留空间
2.  apps/chrg/applications/utils/chrg_sink.h
   - 移除废弃的SINK_WORK_RUNING_2枚举项
   - 重命名测试模式枚举项为更清晰的名称
3.  apps/chrg/applications/thread/chrg_comm.c
   - 更新测试模式匹配逻辑为新枚举项
4.  apps/chrg/applications/thread/chrg_roll_sou.c
   - 新增批量缓存空间回收与异常校验逻辑
   - 优化批量满日志输出信息
5.  apps/chrg/applications/utils/chrg_sink.c
   - 重构测试启动函数为更语义化的名称
   - 调整动态测试命令寄存器数量与内容
   - 更新工作状态分派逻辑适配新枚举项
6.  新增编译日志与程序流程说明文档
2026-07-19 11:49:38 +08:00

326 lines
19 KiB
Markdown
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.
# 程序流程与函数说明
## 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_*()`、共享状态和两个轮询线程。