From b68ffe3bf3b15debd81bc3c8655029f003812ef1 Mon Sep 17 00:00:00 2001 From: yhf Date: Sun, 19 Jul 2026 11:49:38 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E4=BC=98=E5=8C=96=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E4=B8=8B=E5=8F=91=E7=BC=93=E5=AD=98=E4=B8=8E=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E9=80=BB=E8=BE=91=20=E8=A7=A3=E5=86=B3?= =?UTF-8?q?=E9=97=AE=E9=A2=98=EF=BC=9A=E5=8D=97=E5=90=91=E9=98=9F=E5=88=97?= =?UTF-8?q?=E5=AE=B9=E9=87=8F=E7=9A=84=E6=BA=A2=E5=87=BA=E9=A3=8E=E9=99=A9?= =?UTF-8?q?=EF=BC=8C=E4=BB=A5=E5=8F=8A=E7=8A=B6=E6=80=81=E6=9C=BA=E7=9A=84?= =?UTF-8?q?=E9=94=99=E5=88=AB=E5=AD=97=E8=BF=98=E6=9C=89=E5=BF=AB=E9=80=9F?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E5=8A=A0=E5=8A=A8=E6=80=81=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=9A=84=E5=AF=BC=E8=87=B4=E6=B2=A1=E6=9C=89=E7=BB=A7=E7=94=B5?= =?UTF-8?q?=E5=99=A8=E5=BC=80=E5=90=AF=E7=9A=84=E9=97=AE=E9=A2=98=201.=20?= =?UTF-8?q?=20apps/chrg/applications/thread/chrg=5Froll=5Fsou.h=20=20=20?= =?UTF-8?q?=20-=20=E8=B0=83=E6=95=B4SOU=5FCOM=5FBATCH=5FREG=5FMAX=E4=B8=BA?= =?UTF-8?q?32=E5=B9=B6=E8=A1=A5=E5=85=85=E6=B3=A8=E9=87=8A=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=E9=A2=84=E7=95=99=E7=A9=BA=E9=97=B4=202.=20=20apps/ch?= =?UTF-8?q?rg/applications/utils/chrg=5Fsink.h=20=20=20=20-=20=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=E5=BA=9F=E5=BC=83=E7=9A=84SINK=5FWORK=5FRUNING=5F2?= =?UTF-8?q?=E6=9E=9A=E4=B8=BE=E9=A1=B9=20=20=20=20-=20=E9=87=8D=E5=91=BD?= =?UTF-8?q?=E5=90=8D=E6=B5=8B=E8=AF=95=E6=A8=A1=E5=BC=8F=E6=9E=9A=E4=B8=BE?= =?UTF-8?q?=E9=A1=B9=E4=B8=BA=E6=9B=B4=E6=B8=85=E6=99=B0=E7=9A=84=E5=90=8D?= =?UTF-8?q?=E7=A7=B0=203.=20=20apps/chrg/applications/thread/chrg=5Fcomm.c?= =?UTF-8?q?=20=20=20=20-=20=E6=9B=B4=E6=96=B0=E6=B5=8B=E8=AF=95=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E5=8C=B9=E9=85=8D=E9=80=BB=E8=BE=91=E4=B8=BA=E6=96=B0?= =?UTF-8?q?=E6=9E=9A=E4=B8=BE=E9=A1=B9=204.=20=20apps/chrg/applications/th?= =?UTF-8?q?read/chrg=5Froll=5Fsou.c=20=20=20=20-=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=89=B9=E9=87=8F=E7=BC=93=E5=AD=98=E7=A9=BA=E9=97=B4=E5=9B=9E?= =?UTF-8?q?=E6=94=B6=E4=B8=8E=E5=BC=82=E5=B8=B8=E6=A0=A1=E9=AA=8C=E9=80=BB?= =?UTF-8?q?=E8=BE=91=20=20=20=20-=20=E4=BC=98=E5=8C=96=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E6=BB=A1=E6=97=A5=E5=BF=97=E8=BE=93=E5=87=BA=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=205.=20=20apps/chrg/applications/utils/chrg=5Fsink.c=20=20=20?= =?UTF-8?q?=20-=20=E9=87=8D=E6=9E=84=E6=B5=8B=E8=AF=95=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E4=B8=BA=E6=9B=B4=E8=AF=AD=E4=B9=89=E5=8C=96?= =?UTF-8?q?=E7=9A=84=E5=90=8D=E7=A7=B0=20=20=20=20-=20=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E5=8A=A8=E6=80=81=E6=B5=8B=E8=AF=95=E5=91=BD=E4=BB=A4=E5=AF=84?= =?UTF-8?q?=E5=AD=98=E5=99=A8=E6=95=B0=E9=87=8F=E4=B8=8E=E5=86=85=E5=AE=B9?= =?UTF-8?q?=20=20=20=20-=20=E6=9B=B4=E6=96=B0=E5=B7=A5=E4=BD=9C=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E5=88=86=E6=B4=BE=E9=80=BB=E8=BE=91=E9=80=82=E9=85=8D?= =?UTF-8?q?=E6=96=B0=E6=9E=9A=E4=B8=BE=E9=A1=B9=206.=20=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E7=BC=96=E8=AF=91=E6=97=A5=E5=BF=97=E4=B8=8E=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E6=B5=81=E7=A8=8B=E8=AF=B4=E6=98=8E=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/chrg/applications/thread/chrg_comm.c | 2 +- apps/chrg/applications/thread/chrg_roll_sou.c | 29 +- apps/chrg/applications/thread/chrg_roll_sou.h | 3 +- apps/chrg/applications/utils/chrg_sink.c | 30 +- apps/chrg/applications/utils/chrg_sink.h | 5 +- apps/chrg/keil_pd_dynamic_build.log | 18 + docs/程序流程与函数说明.md | 325 ++++++++++++++++++ 7 files changed, 393 insertions(+), 19 deletions(-) create mode 100644 apps/chrg/keil_pd_dynamic_build.log create mode 100644 docs/程序流程与函数说明.md diff --git a/apps/chrg/applications/thread/chrg_comm.c b/apps/chrg/applications/thread/chrg_comm.c index 3cc4c1f..e266dbe 100644 --- a/apps/chrg/applications/thread/chrg_comm.c +++ b/apps/chrg/applications/thread/chrg_comm.c @@ -2280,7 +2280,7 @@ static eMBException chrg_mb_write_sys(rt_uint8_t ch, rt_uint16_t reg_offset, rt_ if (curr_test_old == 0) { // 从正常模式进入测试模式 - pReal->sink.work_mode = (curr_test == 3) ? SINK_WORK_ON_TEST_CHANGE : SINK_WORK_ON_TEST_SHOUT; + pReal->sink.work_mode = (curr_test == 3) ? SINK_WORK_ON_TEST_DYNA : SINK_WORK_ON_TEST_SHORT; pReal->sink.test.test_mode_old = curr_test; } else diff --git a/apps/chrg/applications/thread/chrg_roll_sou.c b/apps/chrg/applications/thread/chrg_roll_sou.c index 23f36cd..33700bf 100644 --- a/apps/chrg/applications/thread/chrg_roll_sou.c +++ b/apps/chrg/applications/thread/chrg_roll_sou.c @@ -409,6 +409,7 @@ int chrg_sou_com_batch_submit(eIDX_SOU_CH ch, const rt_uint16_t *regs, struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU]; struct chrg_rollsou_t *pROLL = &chrgrollsou; sou_com_batch_ch_t *pBatch; + rt_uint16_t remaining; rt_uint8_t i; if ((ch >= TOTAL_SOU_CHS) || (RT_NULL == regs) || (RT_NULL == vals) || @@ -421,9 +422,35 @@ int chrg_sou_com_batch_submit(eIDX_SOU_CH ch, const rt_uint16_t *regs, } pBatch = &g_sou_com_batch.ch[ch]; + if (pBatch->cur_idx > pBatch->reg_cnt) { + LOG_E("sou com batch index invalid: ch=%d cur=%d cnt=%d", + ch, pBatch->cur_idx, pBatch->reg_cnt); + pBatch->pending = RT_FALSE; + pBatch->cur_idx = 0; + pBatch->reg_cnt = 0; + } + + /* + * cur_idx 之前的命令均已收到有效响应,追加新事务前先回收这些空间。 + * 否则快速连续写入时,即使大部分命令已经执行完成,也可能因为 + * reg_cnt 未清零而误报批队列已满。 + */ + if (pBatch->cur_idx > 0) { + remaining = pBatch->reg_cnt - pBatch->cur_idx; + if (remaining > 0) { + memmove(pBatch->reg_list, &pBatch->reg_list[pBatch->cur_idx], + remaining * sizeof(pBatch->reg_list[0])); + memmove(pBatch->val_list, &pBatch->val_list[pBatch->cur_idx], + remaining * sizeof(pBatch->val_list[0])); + } + pBatch->reg_cnt = remaining; + pBatch->cur_idx = 0; + } + if ((pBatch->reg_cnt > SOU_COM_BATCH_REG_MAX) || (count > (SOU_COM_BATCH_REG_MAX - pBatch->reg_cnt))) { - LOG_E("sou com batch reg full"); + LOG_E("sou com batch reg full: ch=%d queued=%d append=%d max=%d", + ch, pBatch->reg_cnt, count, SOU_COM_BATCH_REG_MAX); rt_mutex_release(pTHR->mutex); return -2; } diff --git a/apps/chrg/applications/thread/chrg_roll_sou.h b/apps/chrg/applications/thread/chrg_roll_sou.h index 8115eab..af2c1e0 100644 --- a/apps/chrg/applications/thread/chrg_roll_sou.h +++ b/apps/chrg/applications/thread/chrg_roll_sou.h @@ -16,7 +16,8 @@ #define THR_NAME_ROLL_SOU "thr.rollsou" // 单通道批量下发缓存:保存当前需要同步的寄存器列表 -#define SOU_COM_BATCH_REG_MAX 16 +/* 为连续提交初始化、普通启动和动态启动命令预留足够空间。 */ +#define SOU_COM_BATCH_REG_MAX 32 typedef struct { rt_uint16_t reg_cnt; // 当前待下发寄存器数量 diff --git a/apps/chrg/applications/utils/chrg_sink.c b/apps/chrg/applications/utils/chrg_sink.c index 2729aea..9d3d32f 100644 --- a/apps/chrg/applications/utils/chrg_sink.c +++ b/apps/chrg/applications/utils/chrg_sink.c @@ -524,20 +524,27 @@ static int chrg_sink_nor_work_off(eIDX_SOU_CH idx, struct chrg_switch_t *pSW) } //动态测试启动 -static int chrg_sink_change_ON(rt_uint8_t ch, struct chrg_switch_t *pSW) +static int chrg_sink_TEST_DYNA_ON(rt_uint8_t ch, struct chrg_switch_t *pSW) { struct test *pTest = &pSW->sink.test; - rt_uint16_t regs[6] = {ModbusRTU_DYNA_L1, ModbusRTU_DYNA_T1, - ModbusRTU_DYNA_L2, ModbusRTU_DYNA_T2, ModbusRTU_DYNA_CMD_MODE_FLAG, REG_WORK}; - rt_uint16_t vals[6] = {pTest->change_test.curr1_set, pTest->change_test.time1_set, + rt_uint16_t regs[8] = {REG_REL_ON, REG_REL_ON, + ModbusRTU_DYNA_L1, ModbusRTU_DYNA_T1, ModbusRTU_DYNA_L2, + ModbusRTU_DYNA_T2, ModbusRTU_DYNA_CMD_MODE_FLAG, REG_WORK}; + rt_uint16_t vals[8] = {0x02, 0x03, + pTest->change_test.curr1_set, pTest->change_test.time1_set, pTest->change_test.curr2_set, pTest->change_test.time2_set, ModbusRTU_DYNA_CMD_MODE_RUN, WORK_START}; - return chrg_sou_com_batch_submit((eIDX_SOU_CH)ch, regs, vals, 6); + /* + * 动态测试可能在普通 RUNING 状态打开功率板继电器之前进入。 + * 因此动态启动流程必须自行打开两路继电器,避免快速从 PD 设置 + * 切换到动态测试时,负载已经启动但功率板继电器仍处于关闭状态。 + */ + return chrg_sou_com_batch_submit((eIDX_SOU_CH)ch, regs, vals, 8); } //短路测试启动 -static int chrg_sink_SHOUT_ON(rt_uint8_t ch, struct chrg_switch_t *pSW) +static int chrg_sink_TEST_SHORT_ON(rt_uint8_t ch, struct chrg_switch_t *pSW) { rt_uint16_t regs[2] = {ModbusRTU_ShortTest_CMD_MODE_FLAG, REG_WORK}; rt_uint16_t vals[2] = {ModbusRTU_DYNA_CMD_MODE_RUN_ALWAYS_KEPP, WORK_START}; @@ -572,14 +579,11 @@ int chrg_sink_work(eIDX_SOU_CH idx, struct chrg_switch_t *pSW) case SINK_WORK_RUNING: ret = chrg_sink_work_volt_runing(idx,pSW); break; - case SINK_WORK_RUNING_2: - ret = chrg_sink_work_volt_runing(idx,pSW); - break; - case SINK_WORK_ON_TEST_CHANGE: - ret = chrg_sink_change_ON(idx,pSW); + case SINK_WORK_ON_TEST_DYNA: + ret = chrg_sink_TEST_DYNA_ON(idx,pSW); break; - case SINK_WORK_ON_TEST_SHOUT: - ret = chrg_sink_SHOUT_ON(idx,pSW); + case SINK_WORK_ON_TEST_SHORT: + ret = chrg_sink_TEST_SHORT_ON(idx,pSW); break; case SINK_WORK_LOW: ret = chrg_sink_nor_work_low(idx,pSW); diff --git a/apps/chrg/applications/utils/chrg_sink.h b/apps/chrg/applications/utils/chrg_sink.h index 1025ce4..6c6dda6 100644 --- a/apps/chrg/applications/utils/chrg_sink.h +++ b/apps/chrg/applications/utils/chrg_sink.h @@ -206,11 +206,10 @@ enum { SINK_WORK_WAIT, SINK_WORK_VOLT_OK, SINK_WORK_RUNING, - SINK_WORK_RUNING_2, SINK_WORK_LOW, SINK_WORK_STOP, - SINK_WORK_ON_TEST_CHANGE, - SINK_WORK_ON_TEST_SHOUT, + SINK_WORK_ON_TEST_DYNA, + SINK_WORK_ON_TEST_SHORT, }; // sink 参数结构 diff --git a/apps/chrg/keil_pd_dynamic_build.log b/apps/chrg/keil_pd_dynamic_build.log new file mode 100644 index 0000000..13afe79 --- /dev/null +++ b/apps/chrg/keil_pd_dynamic_build.log @@ -0,0 +1,18 @@ +*** Using Compiler 'V6.16', folder: 'D:\Program Files\Keil_v5\ARM\ARMCLANG\Bin' +Build target 'chrg' +compiling chrg_roll_nor.c... +compiling chrg_north.c... +compiling chrg_south.c... +compiling chrg_comm.c... +compiling chrg_roll_sou.c... +compiling chrg_lcd.c... +compiling chrg_thread.c... +compiling chrg_sink.c... +compiling chrg_north_pkg.c... +compiling chrg_eload.c... +linking... +Program Size: Code=141040 RO-data=15212 RW-data=8544 ZI-data=13968 +FromELF: creating hex file... +After Build - User command #1: fromelf --bin .\build\keil\Obj\chrg.axf -o build/chrg.bin +".\build\keil\Obj\chrg.axf" - 0 Error(s), 0 Warning(s). +Build Time Elapsed: 00:00:06 diff --git a/docs/程序流程与函数说明.md b/docs/程序流程与函数说明.md new file mode 100644 index 0000000..32df2a4 --- /dev/null +++ b/docs/程序流程与函数说明.md @@ -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_*()`、共享状态和两个轮询线程。