From ed1ece23c34f0b90decc24c58934302d16e30874 Mon Sep 17 00:00:00 2001 From: xiao Date: Wed, 10 Jun 2026 10:18:35 +0800 Subject: [PATCH] docs: consolidate project documentation --- AT固件使用手册.md | 5 +- Keil工程配置说明.txt | 195 --------- MDK-ARM/keil-build-viewer-record.txt | 64 --- uart-ch390-debug-handoff.md | 586 --------------------------- 工程调试指南.md | 22 +- 项目技术实现.md | 18 +- 项目文档索引.md | 45 ++ 7 files changed, 69 insertions(+), 866 deletions(-) delete mode 100644 Keil工程配置说明.txt delete mode 100644 MDK-ARM/keil-build-viewer-record.txt delete mode 100644 uart-ch390-debug-handoff.md create mode 100644 项目文档索引.md diff --git a/AT固件使用手册.md b/AT固件使用手册.md index 8721c6b..5b55b39 100644 --- a/AT固件使用手册.md +++ b/AT固件使用手册.md @@ -30,7 +30,7 @@ 1. `MUX`:全局数据承载模式开关 2. `NET`:全局静态网络配置记录 -3. `LINK[ROLE]`:按角色名组织的链路配置记录(`S1/S2/C1/C2`) +3. `LINK`:按角色名组织的链路配置记录(`S1/S2/C1/C2`) 约束如下: @@ -452,4 +452,5 @@ AT+RESET\r\n - AT 命令实现:[config.c](/D:/code/STM32Project/TCP2UART/App/config.c) - 配置结构与默认值:[config.h](/D:/code/STM32Project/TCP2UART/App/config.h) -- 调试与测试记录:[uart-ch390-debug-handoff.md](/D:/code/STM32Project/TCP2UART/uart-ch390-debug-handoff.md) +- 调试指导:[工程调试指南.md](/D:/code/STM32Project/TCP2UART/工程调试指南.md) +- 文档索引:[项目文档索引.md](/D:/code/STM32Project/TCP2UART/项目文档索引.md) diff --git a/Keil工程配置说明.txt b/Keil工程配置说明.txt deleted file mode 100644 index 4a337df..0000000 --- a/Keil工程配置说明.txt +++ /dev/null @@ -1,195 +0,0 @@ -======================================== -TCP2UART Keil 工程配置说明 -======================================== - -由于 Keil 工程文件格式复杂,建议在 Keil uVision 中手动添加以下配置。 - -======================================== -一、添加包含路径 (Include Paths) -======================================== - -打开 Keil -> Project -> Options for Target -> C/C++ -> Include Paths - -添加以下路径(用分号分隔): -../Drivers/CH390 -../Drivers/LwIP/src/include -../Drivers/LwIP/src/include/lwip -../Drivers/LwIP/src/include/netif -../Drivers/LwIP/src/include/arch -../Drivers/LwIP/port -../App - -完整的 Include Paths 应该是: -../Core/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy;../Drivers/CMSIS/Device/ST/STM32F1xx/Include;../Drivers/CMSIS/Include;../Middlewares/Third_Party/FreeRTOS/Source/include;../Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2;../Middlewares/Third_Party/FreeRTOS/Source/portable/RVDS/ARM_CM3;../Drivers/CH390;../Drivers/LwIP/src/include;../Drivers/LwIP/src/include/lwip;../Drivers/LwIP/src/include/netif;../Drivers/LwIP/src/include/arch;../Drivers/LwIP/port;../App - -======================================== -二、添加源文件分组 (Source Groups) -======================================== - -在 Project 窗口中右键 -> Add Group,创建以下分组并添加文件: - -【1】Drivers/CH390 - 添加文件: - - ../Drivers/CH390/CH390.c - - ../Drivers/CH390/CH390_Interface.c - -【2】Drivers/LwIP/core - 添加文件: - - ../Drivers/LwIP/src/core/def.c - - ../Drivers/LwIP/src/core/dns.c - - ../Drivers/LwIP/src/core/inet_chksum.c - - ../Drivers/LwIP/src/core/init.c - - ../Drivers/LwIP/src/core/ip.c - - ../Drivers/LwIP/src/core/mem.c - - ../Drivers/LwIP/src/core/memp.c - - ../Drivers/LwIP/src/core/netif.c - - ../Drivers/LwIP/src/core/pbuf.c - - ../Drivers/LwIP/src/core/raw.c - - ../Drivers/LwIP/src/core/stats.c - - ../Drivers/LwIP/src/core/sys.c - - ../Drivers/LwIP/src/core/tcp.c - - ../Drivers/LwIP/src/core/tcp_in.c - - ../Drivers/LwIP/src/core/tcp_out.c - - ../Drivers/LwIP/src/core/timeouts.c - - ../Drivers/LwIP/src/core/udp.c - - IPv4 支持(在 core/ipv4 子目录): - - ../Drivers/LwIP/src/core/ipv4/autoip.c - - ../Drivers/LwIP/src/core/ipv4/dhcp.c - - ../Drivers/LwIP/src/core/ipv4/etharp.c - - ../Drivers/LwIP/src/core/ipv4/icmp.c - - ../Drivers/LwIP/src/core/ipv4/igmp.c - - ../Drivers/LwIP/src/core/ipv4/ip4.c - - ../Drivers/LwIP/src/core/ipv4/ip4_addr.c - - ../Drivers/LwIP/src/core/ipv4/ip4_frag.c - -【3】Drivers/LwIP/api - 添加文件: - - ../Drivers/LwIP/src/api/api_lib.c - - ../Drivers/LwIP/src/api/api_msg.c - - ../Drivers/LwIP/src/api/err.c - - ../Drivers/LwIP/src/api/netbuf.c - - ../Drivers/LwIP/src/api/netdb.c - - ../Drivers/LwIP/src/api/netifapi.c - - ../Drivers/LwIP/src/api/sockets.c - - ../Drivers/LwIP/src/api/tcpip.c - -【4】Drivers/LwIP/netif - 添加文件: - - ../Drivers/LwIP/src/netif/ethernetif.c - -【5】Drivers/LwIP/port - 添加文件: - - ../Drivers/LwIP/port/sys_arch.c - -【6】App - 添加文件: - - ../App/tcp_server.c - - ../App/tcp_client.c - - ../App/uart_trans.c - - ../App/config.c - - ../App/flash_param.c - -======================================== -三、预处理器宏定义 (Preprocessor Defines) -======================================== - -打开 Keil -> Project -> Options for Target -> C/C++ -> Define - -保持现有定义,不需要额外添加: -USE_HAL_DRIVER,STM32F103xB - -======================================== -四、编译优化设置 -======================================== - -建议设置: -- Optimization: Level 2 (-O2) -- 勾选 "One ELF Section per Function" -- Warning Level: All Warnings - -======================================== -五、目标内存配置 -======================================== - -确认 ROM 和 RAM 配置正确: -- IROM1: 0x08000000, Size: 0x10000 (64KB) -- IRAM1: 0x20000000, Size: 0x5000 (20KB) - -======================================== -六、编译验证 -======================================== - -配置完成后: -1. 按 F7 编译整个工程 -2. 检查是否有编译错误 -3. 常见问题: - - "file not found" -> 检查包含路径 - - "undefined reference" -> 检查是否添加了所有源文件 - - 链接错误 -> 检查 ROM/RAM 大小配置 - -======================================== -七、烧录配置 -======================================== - -Debug 选项卡: -- 选择正确的调试器(ST-Link/J-Link) -- 勾选 "Reset and Run" - -Utilities 选项卡: -- 选择正确的 Flash 算法 -- STM32F10x Med-density Flash (64KB) - -======================================== -快速添加方法(可选) -======================================== - -如果源文件太多手动添加麻烦,可以: - -1. 在 Keil 中右键分组 -> Add Existing Files -2. 选择 "All Files (*.*)" -3. 导航到对应目录 -4. 按住 Ctrl 多选所有 .c 文件 -5. 点击 Add - -======================================== -文件结构参考 -======================================== - -TCP2UART/ -├── App/ -│ ├── tcp_server.c/h -│ ├── tcp_client.c/h -│ ├── uart_trans.c/h -│ ├── config.c/h -│ └── flash_param.c/h -├── Core/ -│ ├── Inc/ -│ └── Src/ -├── Drivers/ -│ ├── CH390/ -│ │ ├── CH390.c/h -│ │ └── CH390_Interface.c/h -│ ├── LwIP/ -│ │ ├── port/ -│ │ │ └── sys_arch.c -│ │ └── src/ -│ │ ├── api/ -│ │ ├── core/ -│ │ │ └── ipv4/ -│ │ ├── include/ -│ │ │ ├── arch/ -│ │ │ │ ├── cc.h -│ │ │ │ ├── lwipopts.h -│ │ │ │ └── sys_arch.h -│ │ │ ├── lwip/ -│ │ │ └── netif/ -│ │ └── netif/ -│ │ └── ethernetif.c/h -│ └── STM32F1xx_HAL_Driver/ -├── MDK-ARM/ -│ └── TCP2UART.uvprojx -└── Middlewares/ - └── Third_Party/FreeRTOS/ - -======================================== diff --git a/MDK-ARM/keil-build-viewer-record.txt b/MDK-ARM/keil-build-viewer-record.txt deleted file mode 100644 index 803f061..0000000 --- a/MDK-ARM/keil-build-viewer-record.txt +++ /dev/null @@ -1,64 +0,0 @@ - Code (inc. data) RO Data RW Data ZI Data Debug Object Name - 794 0 0 0 0 0 ch390.o - 618 0 64 0 0 0 ch390_interface.o - 2584 0 85 6 136 0 ch390_runtime.o - 3958 0 591 8 1240 0 config.o - 8 0 0 0 0 0 def.o - 124 0 0 0 0 0 dma.o - 1816 0 0 1 240 0 etharp.o - 238 0 12 0 0 0 ethernet.o - 178 0 0 0 48 0 ethernetif.o - 246 0 0 0 0 0 flash_param.o - 240 0 0 0 0 0 gpio.o - 452 0 0 0 0 0 icmp.o - 334 0 0 0 0 0 inet_chksum.o - 26 0 0 0 0 0 init.o - 0 0 0 0 24 0 ip.o - 778 0 0 2 0 0 ip4.o - 46 0 4 0 0 0 ip4_addr.o - 44 0 0 0 12 0 iwdg.o - 3532 0 300 20 272 0 main.o - 828 0 0 12 4115 0 mem.o - 196 0 244 32 6464 0 memp.o - 582 0 0 12 0 0 netif.o - 1118 0 0 0 0 0 pbuf.o - 248 0 0 4 0 0 raw.o - 214 0 9 168 272 0 segger_rtt.o - 64 0 0 0 0 0 segger_rtt_printf.o - 216 0 0 0 88 0 spi.o - 60 0 236 0 1024 0 startup_stm32f103xb.o - 128 0 0 12 0 0 stm32f1xx_hal.o - 198 0 0 0 0 0 stm32f1xx_hal_cortex.o - 808 0 0 0 0 0 stm32f1xx_hal_dma.o - 392 0 0 0 32 0 stm32f1xx_hal_flash.o - 240 0 0 0 0 0 stm32f1xx_hal_flash_ex.o - 516 0 0 0 0 0 stm32f1xx_hal_gpio.o - 106 0 0 0 0 0 stm32f1xx_hal_iwdg.o - 60 0 0 0 0 0 stm32f1xx_hal_msp.o - 1240 0 18 0 0 0 stm32f1xx_hal_rcc.o - 1510 0 0 0 0 0 stm32f1xx_hal_spi.o - 936 0 0 0 0 0 stm32f1xx_hal_tim.o - 108 0 0 0 0 0 stm32f1xx_hal_tim_ex.o - 2300 0 0 0 0 0 stm32f1xx_hal_uart.o - 490 0 0 0 0 0 stm32f1xx_it.o - 2 0 24 4 0 0 system_stm32f1xx.o - 3474 0 193 32 0 0 tcp.o - 1734 0 0 0 1088 0 tcp_client.o - 3684 0 0 36 20 0 tcp_in.o - 3862 0 0 0 0 0 tcp_out.o - 1364 0 0 0 1048 0 tcp_server.o - 164 0 0 0 72 0 tim.o - 374 0 16 12 0 0 timeouts.o - 1590 0 0 0 2936 0 uart_trans.o - 816 0 0 0 624 0 usart.o -Object Totals - -Memory Map of the image - - Load Region LR_IROM1 - - Execution Region ER_IROM1 (Exec base: 0x08000000, Size: 0x0000E0B8, Max: 0x00010000, END) - - Execution Region RW_IRAM1 (Exec base: 0x20000000, Size: 0x00004FE0, Max: 0x00005000, END) - -Image component sizes \ No newline at end of file diff --git a/uart-ch390-debug-handoff.md b/uart-ch390-debug-handoff.md deleted file mode 100644 index dcda41b..0000000 --- a/uart-ch390-debug-handoff.md +++ /dev/null @@ -1,586 +0,0 @@ -# UART CH390 Debug Handoff - -## 2026-03-31 Config UART Test Session - -### Goal - -- Exhaustively test the `USART1` config command interface. -- Verify that Flash-backed parameters survive `AT+SAVE` plus reset. -- Record concrete bench procedure, failures, fixes, and evidence paths. - -### Bench Baseline - -- Workspace: `D:\code\STM32Project\TCP2UART` -- Target MCU: `STM32F103R8T6` -- Config UART: `USART1` on `PA9/PA10` -- Host visible COM ports during this session: `COM1`, `COM9` -- Debug probe visible during this session: `STLink V2` -- Flash parameter page: `0x0800FC00` -- Firmware image used for test bring-up: `MDK-ARM\TCP2UART\TCP2UART.axf` - -### Source-of-Truth Command Surface - -From `App/config.c`, the tested command surface is: - -- `AT` -- `AT+?` -- `AT+QUERY` -- `AT+SAVE` -- `AT+RESET` -- `AT+DEFAULT` -- `AT+IP=...` -- `AT+MASK=...` -- `AT+GW=...` -- `AT+RIP=...` -- `AT+MAC=...` -- `AT+PORT=...` -- `AT+RPORT=...` -- `AT+BAUD1=...` -- `AT+BAUD2=...` -- `AT+DHCP=0/1` - -### Test Procedure - -1. Flash the current `axf` image with `probe-rs download --chip STM32F103R8`. -2. Connect to the config UART at `115200 8N1`. -3. Run `python tools/uart_config_test.py --port COM9 --scenario inventory`. -4. Run `python tools/uart_config_test.py --port COM9 --scenario persistence`. -5. Read back Flash words at `0x0800FC00` and compare them before and after reset. -6. Save all transcripts into `artifacts/uart-config/`. - -### Important Expectations - -- Setter commands update RAM immediately and return `OK` plus the reboot hint. -- Persistence is not proven until the sequence `set -> query -> save -> reset -> query` passes. -- `AT+DEFAULT` only resets RAM state and still requires `AT+SAVE` to persist. -- `AT+DHCP=1` must fail in this build by design. - -### Session Findings - -- `probe-rs download` succeeded against `STM32F103R8`. -- `pyserial` had to be installed on the host before scripted UART testing. -- The requested handoff filename did not exist in the repo, so this file was created as the dedicated config-UART handoff log. -- Raw command transcripts and Flash comparisons should be attached from `artifacts/uart-config/` for any future regression analysis. - -### 2026-03-31 Live Test Evidence - -- Host-side strict inventory run on `COM9` failed with `non_empty_responses = 0`. -- Artifact: `artifacts/uart-config/inventory-20260331-185333.json` -- Direct host probe on `COM9` with `AT\r\n` returned `b''`. -- Direct host probe on `COM1` with `AT\r\n` also returned `b''`. -- Strict persistence run was intentionally not accepted as valid after the script was corrected, because all command responses were empty. -- Flash page `0x0800FC00` remained all `0xFFFFFFFF`, proving `AT+SAVE` never actually executed on target. - -### Board/Debugger Findings That Narrow The Fault - -- The target firmware is present in Flash and `probe-rs download` completes successfully. -- After a clean reset and short run window, the MCU executes from Flash and initializes clocks and `USART1` registers. -- `USART1` register block was observed in an initialized state after boot, not all-zero. -- Firmware was patched to explicitly start `HAL_UART_Receive_IT(&huart1, &g_uart1_rx_probe_byte, 1)` during `App_Init()`. -- Even after that fix, repeated `AT` frames from the host produced no visible UART response. -- Debug readout of software-side config RX state showed: - - `g_pending_cmd_ready = 0` - - `g_pending_cmd_len = 0` - - `g_uart_cmd_len = 0` - - `g_uart_cmd_buffer` remained zero-filled - - `g_pending_cmd_buffer` remained zero-filled -- This means the config parser never received any bytes from the live UART path during the test window. - -### Current Best Conclusion - -- The immediate blocker is no longer the parser itself. -- Current evidence points to a board-level `USART1_RX` path problem or wrong host wiring/port assumption, because the firmware is alive, `USART1` is initialized, but no command bytes enter `config_uart_rx_byte()`. -- Until the physical config-UART path is proven, it is not meaningful to claim that every config command or Flash persistence path has passed on real hardware. - -### Corrected Final Conclusion - -- The earlier "no response" conclusion was wrong because the host was sending `\r\n` terminated commands. -- This firmware expects the config command to be terminated by `\n` to complete the frame in the real bench setup. -- After switching the host sender to `AT...\n`, `COM9` immediately returned `OK\r\n` for `AT` and the complete config command set became testable. -- Therefore the key bench rule is: every config command must end with `\n`. - -### Final Verified Results - -- `AT` returns `OK`. -- `AT+?` and `AT+QUERY` return the full current configuration snapshot. -- Setter commands `AT+IP`, `AT+MASK`, `AT+GW`, `AT+RIP`, `AT+MAC`, `AT+PORT`, `AT+RPORT`, `AT+BAUD1`, `AT+BAUD2`, `AT+DHCP=0` all return `OK` plus the reboot hint. -- `AT+SAVE` returns `OK: Configuration saved`. -- `AT+RESET` returns `OK: Resetting...` and the board comes back responding to `AT`. -- `AT+DEFAULT` returns `OK: Defaults restored`. -- Negative cases were verified: - - `AT+UNKNOWN` -> `ERROR: Unknown command` - - `AT+PORT=0` / `AT+PORT=65536` -> `ERROR: Invalid port` - - `AT+BAUD1=1199` / `AT+BAUD1=921601` -> `ERROR: Invalid baudrate` - - `AT+DHCP=1` -> `ERROR: DHCP disabled in this build` - - `AT+IP=999.1.1.1` -> `ERROR: Invalid IP format` - - `AT+MAC=GG:11:22:33:44:55` -> `ERROR: Invalid MAC format` -- Non-AT input `BT` produced no response, which matches the parser gate. - -### Final Flash Persistence Evidence - -- Tested persisted values: - - `IP=192.168.1.123` - - `MASK=255.255.255.0` - - `GW=192.168.1.1` - - `RIP=192.168.1.201` - - `MAC=02:12:34:56:78:9A` - - `PORT=10001` - - `RPORT=10002` - - `BAUD1=57600` - - `BAUD2=38400` -- Sequence used: - 1. set values with `\n`-terminated AT commands - 2. query with `AT+?` - 3. `AT+SAVE` - 4. `AT+RESET` - 5. query again with `AT+?` - 6. read raw Flash words at `0x0800FC00` -- Query values before and after reset matched exactly. -- Raw Flash read before and after reset also matched exactly. -- Factory default restoration was also proven with `AT+DEFAULT -> AT+SAVE -> AT+RESET -> AT+?`. - -### Evidence Files - -- Inventory transcript: `artifacts/uart-config/inventory-20260331-185752.json` -- Inventory raw text: `artifacts/uart-config/inventory-20260331-185752.txt` -- Persistence transcript: `artifacts/uart-config/persistence-20260331-190039.json` -- Persistence raw text: `artifacts/uart-config/persistence-20260331-190039.txt` - -### Firmware Adjustment Made During This Session - -- Added explicit `HAL_UART_Receive_IT(&huart1, &g_uart1_rx_probe_byte, 1u)` arming in `App_Init()` so the `USART1` interrupt receive path is definitely started after boot. -- This is a safe, minimal bring-up fix and should remain in place. - -### Practical Lessons - -- Do not treat a script exit code alone as proof of UART success; require at least one non-empty response in the captured transcript. -- Do not treat `probe-rs read 0x0800FC00` returning all `0xFFFFFFFF` as a flash-driver failure until you first prove that `AT+SAVE` was actually accepted by the parser. -- In this project, the fastest truth test is: - 1. prove target is executing - 2. prove `USART1` is initialized - 3. prove bytes reach `config_uart_rx_byte` - 4. only then evaluate parser responses and flash persistence -- Most important bench lesson: if the config UART appears dead, first retry with commands ending in `\n` instead of `\r\n`. - -### Open Items - -- Confirm whether `COM9` is the real `USART1` config port by live command-response evidence. -- If command-response is unstable, inspect whether host wiring/USB-UART level shifting is the cause before changing parser logic. -- If persistence fails after a clean `AT+SAVE`, inspect `App/flash_param.c` and raw Flash contents at `0x0800FC00` before changing higher-level config logic. - -## 2026-03-31 CH390D Bring-up Debug Session - -### Goal - -- Determine why `CH390D` does not return valid register values during boot. -- Find a software-side root cause if one exists and attempt a minimal fix. - -### Baseline Symptom - -- MCU boots normally and RTT works. -- CH390 boot diagnostics originally reported: - -```text -TCP2UART boot -CH390 VID=0x0000 PID=0x0000 REV=0x00 NSR=0x00 LINK=0 -CH390 NCR=0x00 RCR=0x00 IMR=0x00 INTCR=0x00 GPR=0x00 ISR=0x00 -CH390 WRCHK NCR:0x00->0x00 INTCR:0x00->0x00 -``` - -- This showed that CH390 register reads and write-back checks were not producing valid values. - -### Board-Side Evidence Already Collected - -- `RST` line was observed released high. -- `CS` line idle state was high. -- `INT` line was observed low and mapped to EXTI. -- `SPI1` was enabled and configured for `Mode 3` in the active firmware. -- These observations did not by themselves restore valid CH390 responses. - -### What Was Tried - -1. Added richer RTT startup diagnostics in `BootDiag_ReportCh390()`. -2. Lowered `SPI1` speed from `/8` to `/64`. -3. Added stage markers around `low_level_init()` to localize the hang. -4. Step-debugged and breakpoint-debugged `ch390_default_config()` and `ch390_write_phy()`. -5. Added timeout protection to `ch390_read_phy()` / `ch390_write_phy()` so `EPCR` polling cannot hang forever. -6. Temporarily skipped `ch390_set_phy_mode(CH390_AUTO)` to isolate non-PHY register access. -7. Compared current driver against `Reference/EVT/EXAM/PUB/CH390.c` and `Reference/EVT/EXAM/PUB/CH390_Interface.c`. -8. Tried multiple SPI register transaction shapes: - - original two-byte exchange style - - split `Transmit` then read phase - - explicit dummy-byte read phase - - single-frame two-byte full-duplex read -9. Scanned all four SPI modes (`mode0`..`mode3`) during startup. -10. Added small `CS` setup/hold delays. -11. Increased hardware reset release wait to `50ms`. -12. Restored EVT-style init order and PHY setup path to see whether EVT sequence alone fixes the problem. - -### Key Intermediate Findings - -- Lowering SPI speed changed behavior, but did not recover valid CH390 IDs. -- Stage markers showed that low-speed SPI could stall during `ETH init: default`. -- Step/RTT evidence localized the original stall to PHY access during `ch390_default_config()`. -- The PHY access loop in `ch390_read_phy()` / `ch390_write_phy()` had no timeout and could hang indefinitely. This is a real software bug and should stay fixed. -- After adding PHY timeouts and temporarily skipping PHY setup, the init path completed, but all CH390 reads became `0xFF` rather than valid IDs. -- SPI mode scan result under that condition was: - -```text -CH390 SPI mode0 [FF FF FF FF FF] -CH390 SPI mode1 [FF FF FF FF FF] -CH390 SPI mode2 [FF FF FF FF FF] -CH390 SPI mode3 [FF FF FF FF FF] -``` - -- This ruled out a simple `CPOL/CPHA` mismatch. -- External code comparison did not reveal an `opcode` or register-address mismatch. Public CH390 implementations use the same `OPC_REG_R=0x00`, `OPC_REG_W=0x80`, and the same register map. -- One experimental split transaction path produced repeatable but obviously bogus values like `0x03`, `0xAC`, `0xAE`, which strongly suggests transaction artifacts rather than real CH390 data. -- A debug read of `SPI1->SR` showed `OVR=1` during one of the experimental transaction variants, indicating the SPI transaction layer was not trustworthy in that configuration. - -### EVT Comparison Outcome - -- `Reference/EVT` is useful as a baseline, but it is not a drop-in fix for this project. -- The broad init order in the live project already matches EVT closely through the lwIP glue path. -- The most important EVT-specific difference is that EVT performs `ch390_set_phy_mode(CH390_AUTO)` at the start of `ch390_default_config()`. -- Restoring the EVT-style `PHY` setup path in this project caused boot to hang again at: - -```text -TCP2UART boot -ETH init: gpio -ETH init: spi -ETH init: reset -ETH init: default -``` - -- That confirms the `PHY` path is a real trigger for the hang, but EVT order alone does not solve the underlying communication problem. - -### Current Best Technical Conclusion - -- A real software defect was found and fixed: `EPCR` polling in PHY access had no timeout. -- That fix prevents the firmware from hanging forever, but it does **not** restore valid CH390 register communication. -- The core unresolved problem remains: the SPI register-access path still does not yield believable CH390 register data. -- At this point, the following common explanations have already been tested and are **not** sufficient by themselves: - - SPI mode selection - - adding dummy bytes - - `CS` setup/hold delays - - changing reset wait from `10ms` to `50ms` - - reverting to EVT transaction style - - restoring EVT initialization order - - public `opcode` / register-map mismatch - -### Recommended Next Debug Step - -- The next high-value experiment is a temporary GPIO bit-bang read of `VIDL/VIDH/CHIPR` with a fully controlled continuous command+clock sequence. -- If bit-bang returns valid IDs while HAL-SPI paths do not, the remaining fault is in the SPI transaction implementation rather than CH390 higher-level init order. -- If bit-bang still returns invalid data, the investigation must move back to board-level bus behavior even if static continuity checks look correct. - -### Additional 2026-03-31 Finding: HAL SPI Re-init And Bit-Bang Side Effects - -- A valid concern was raised about calling `HAL_SPI_Init()` after temporarily changing SPI pins to GPIO mode. -- Code review of `stm32f1xx_hal_spi.c` showed that `HAL_SPI_MspInit()` only runs when `hspi->State == HAL_SPI_STATE_RESET`. -- Therefore, simply calling `HAL_SPI_Init()` after bit-bang mode does **not** automatically restore `PA5/PA7` to SPI alternate-function output mode. -- This was a real software-side risk in the temporary bit-bang probe and was corrected by explicitly restoring: - - `PA5` -> `AF_PP` - - `PA7` -> `AF_PP` - - `PA6` -> input - before calling `HAL_SPI_Init()` again. -- After that correction, the observed behavior changed again: boot output stopped at `ETH init: reset`, and a short halt showed execution inside `HAL_SPI_TransmitReceive()` called from the CH390 SPI exchange path. -- This means the earlier bit-bang experiments could have polluted later SPI results, but after the GPIO restore fix, the active blocker is again a live SPI transaction stall rather than a missing-GPIO-restore artifact. - -### Additional 2026-03-31 Finding: Reset Exists In Runtime Path - -- The project does **not** lack a CH390 reset process. -- The actual runtime order is: - 1. `App_Init()` - 2. `lwip_netif_init()` - 3. `ethernetif_init()` - 4. `low_level_init()` - 5. `ch390_gpio_init()` - 6. `ch390_spi_init()` - 7. `ch390_hardware_reset()` - 8. `ch390_default_config()` -- The reset process is therefore present and executed, but it lives in the lwIP/netif bring-up path instead of being written inline in `main.c` as in the EVT sample. -- The current unresolved problem is not "missing reset"; it is that SPI transactions after reset still do not produce valid CH390 register responses. - -## 2026-03-31 Manual Reset Sensitivity Analysis - -### Observed Symptom - -- An extra `ch390_hardware_reset()` was temporarily inserted into `App_Init()` before `lwip_init()`. -- With that extra reset in place, a manual board reset could lead to the firmware appearing stuck and the LED heartbeat not behaving normally. -- The same image could still look more normal when observed after a `probe-rs` flash-and-run cycle. - -### Code-Level Finding - -- The inserted extra reset sat here in `Core/Src/main.c`: - -```c -SEGGER_RTT_Init(); -SEGGER_RTT_WriteString(0, "\r\nTCP2UART boot\r\n"); -... -ch390_hardware_reset(); -lwip_init(); -lwip_netif_init(...); -``` - -- But the normal bring-up path already performs a reset later inside `ch390_runtime_init()` / `low_level_init()` before `ch390_default_config()`. -- That means the temporary line created a redundant early reset in a different initialization phase than the normal driver-owned reset. - -### Interpretation - -- This pattern is much more consistent with a reset-sequencing / startup-state issue than with compiler optimization level. -- The Keil target uses one fixed optimization configuration, so a plain manual reset does not change code generation. -- In contrast, an extra CH390 reset inserted before lwIP and before the normal CH390 runtime init can alter the device startup state and timing relationship between the MCU and CH390. - -### Action Taken - -- The extra `ch390_hardware_reset()` in `App_Init()` was removed. -- The firmware now relies only on the standard driver-owned reset inside the CH390 runtime initialization path. - -### Conclusion - -- The temporary extra reset was not kept. -- The strongest software-side conclusion is that the manual-reset sensitivity was caused by redundant reset sequencing rather than by optimization level. - -## 2026-03-31 HardFault Root Cause And Fix - -### Symptom - -- After CH390 bring-up completed and boot diagnostics printed, the firmware entered: - -```text -TRAP: HardFault_Handler -``` - -- At the same time, `PC13` stopped blinking, which originally looked like a timer or LED problem. - -### Fault Evidence - -- Fault-status registers showed a real fault rather than a normal busy wait. -- The trap location was `Debug_TrapWithRttHint()` in `Core/Src/main.c`. -- The stacked fault frame pointed back into the normal runtime path rather than the trap itself. -- `TIM4` was configured and had already advanced `g_led_blink_ticks`, so the LED path was alive before the fault. - -### Root Cause - -- `MX_IWDG_Init()` had been temporarily commented out in `main()`. -- However, `App_Poll()` still executed: - -```c -HAL_IWDG_Refresh(&hiwdg); -``` - -- Because `hiwdg` was never initialized, this call operated on an invalid handle and led to the observed fault path. - -### Fix Applied - -- `Core/Src/main.c` was changed so watchdog refresh only runs when `hiwdg.Instance == IWDG`. -- This preserves normal behavior when IWDG is enabled, while avoiding invalid access when IWDG init is intentionally disabled for debugging. - -### Verification - -- Rebuilt successfully with `0 error`, `1 warning`. -- Reflashed target and reran startup. -- Boot RTT still showed CH390 diagnostics, but no longer showed `TRAP: HardFault_Handler`. -- A 5-second runtime window completed without a new trap. -- `g_led_blink_ticks` continued advancing after the fix, confirming that `TIM4` interrupts and the LED heartbeat path were alive again. - -### Conclusion - -- The HardFault was caused by refreshing an uninitialized IWDG handle, not by the CH390 SPI path itself. -- This issue is fixed. -- CH390 bring-up is still unresolved at the register-communication level, but the main task is again able to continue running normally. - -## 2026-03-31 Runtime Freeze Root Cause And Fix - -### Symptom - -- After re-soldering CH390D, the system could boot and print the normal CH390 startup diagnostics. -- However, after running for a while, the device would appear to freeze. -- In that state, the LED heartbeat behavior became unreliable and the system appeared to stop making useful progress. - -### Key Runtime Evidence - -- The new freeze was **not** another HardFault: no new `TRAP:` line appeared during the freeze window. -- `g_led_blink_ticks` continued advancing during observation windows, proving that `TIM4` interrupts were still alive and the MCU was not fully dead. -- A short halt during the bad behavior repeatedly landed in `HAL_SPI_TransmitReceive()`. -- Code inspection showed that CH390 runtime paths in `ethernetif.c` were executing blocking SPI transactions while global interrupts were disabled via `ethernetif_lock()`. - -### Root Cause - -- `low_level_output()`, `low_level_input()`, and `ethernetif_check_link()` in `Drivers/LwIP/src/netif/ethernetif.c` wrapped CH390 SPI register/memory accesses inside `ethernetif_lock()` / `ethernetif_unlock()`. -- Those helpers globally disable interrupts by manipulating `PRIMASK`. -- The CH390 access path uses blocking HAL SPI functions and timeout logic based on `HAL_GetTick()`. -- Running those blocking accesses with interrupts disabled can stall or livelock the runtime path, especially after startup when network polling begins. - -### Fix Applied - -- Reduced the interrupt-masked critical sections in `ethernetif.c` to only protect the shared IRQ-pending flag. -- Removed `ethernetif_lock()` coverage from the long CH390 SPI transaction paths in: - - `low_level_output()` - - `low_level_input()` - - `ethernetif_check_link()` -- In `ethernetif_poll()`, only the `g_ch390_irq_pending` flag is now cleared under the short critical section; the actual CH390 register access happens with interrupts enabled. - -### Verification - -- Rebuilt successfully with `0 error`, `0 warning`. -- Reflashed and reran the target. -- Boot RTT still completed normally through: - -```text -TCP2UART boot -ETH init: gpio -ETH init: spi -ETH init: reset -ETH init: default -ETH init: mac -ETH init: getmac -ETH init: irq -ETH init: done -CH390 VID=0x0000 PID=0x0000 REV=0x00 NSR=0x00 LINK=0 -CH390 NCR=0x00 RCR=0x00 IMR=0x00 INTCR=0x00 GPR=0x00 ISR=0x00 -``` - -- No new `TRAP:` message appeared during extended runtime observation. -- `g_led_blink_ticks` continued advancing over multiple samples, indicating that the heartbeat timer and interrupt delivery remained active. -- The system no longer reproduced the earlier “runs for a while then appears frozen” behavior in the observed validation window. - -### Conclusion - -- This freeze was caused by doing blocking CH390 SPI operations inside a global interrupt-disabled critical section. -- The runtime freeze is fixed. -- CH390 register communication is still invalid (`0x0000` ID values), but that is now a separate communication/bring-up problem rather than the cause of the observed runtime stall. - -## 2026-03-31 SPI Ownership Decoupling And CH390 Current Status - -### Why This Refactor Was Done - -- The project previously allowed multiple runtime layers to reach down into CH390/SPI behavior directly: - - `ethernetif.c` handled init, IRQ-driven poll service, RX/TX transactions, and link checks - - `main.c` directly read CH390 registers for boot diagnostics - - the CH390 low-level SPI transport sat underneath those callers with no single runtime owner boundary -- This made the system harder to reason about and contributed to runtime instability when CH390 accesses happened from different code paths with different assumptions. - -### Refactor Outcome - -- Added a single runtime owner module: `Drivers/CH390/ch390_runtime.c` + `Drivers/CH390/ch390_runtime.h`. -- After this change: - - `CH390_Interface.c` remains the **only** SPI transport implementation - - `CH390.c` remains the chip-level helper layer - - `ch390_runtime.c` is now the **only runtime owner** of CH390 transactions after boot - - `ethernetif.c` delegates runtime TX/RX/link/IRQ servicing to `ch390_runtime` - - `main.c` no longer performs direct CH390 register reads; boot diagnostics use `ch390_runtime_get_diag()` - - `EXTI0_IRQHandler()` only posts the IRQ-pending event into the runtime owner and does not touch CH390 directly - -### Behavior After Refactor - -- Build passed with `0 error`, `0 warning`. -- The system remained stable in the post-refactor runtime window: - - no new trap output - - heartbeat/timer activity continued - - previous runtime freeze did not reproduce in the observed window - -### CH390 Result After Refactor - -- The CH390 did **not** come up successfully. -- However, the failure signature became cleaner and more trustworthy: - -```text -CH390 VID=0xFFFF PID=0xFFFF REV=0xFF NSR=0xFF LINK=1 -CH390 NCR=0xFF RCR=0xFF IMR=0xFF INTCR=0xFF GPR=0xFF ISR=0xFF -``` - -- This is materially different from the earlier unstable mixture of: - - all-zero reads - - intermittent hangs - - transaction artifacts - - watchdog-related HardFaults - -### Trusted Interpretation Of Current Failure - -- With the SPI access model cleaned up and the system remaining stable, the current CH390 failure can now be treated as a **credible transport-level non-response** rather than a concurrency artifact. -- A uniform `0xFF` readback across identity and status/control registers strongly suggests one of these conditions: - - CH390 still does not actively drive MISO during the register-read phase - - CS reaches the MCU logic but is not effectively selecting the CH390 device on the board side - - the CH390 digital core is not entering a valid SPI-responding state after reset even though the MCU-side sequence now looks consistent - -### Practical Conclusion - -- The architectural decoupling requirement is complete. -- The runtime stability requirement is complete. -- CH390 connection is **still failed**, but the reason is now narrowed to a believable low-level bus/device-response problem rather than a software ownership/concurrency problem. - -## 2026-04-01 Final Software Boundary Check: Hardware SPI vs Bit-Bang - -### Goal - -- Decide whether another software-side SPI transaction rewrite is still justified. -- Use one last high-signal experiment to separate STM32 hardware-SPI issues from board-level CH390 non-response. - -### Experiment - -- Kept the normal hardware-SPI identity probe in `ch390_runtime_probe_identity()`. -- Added a temporary bit-bang register read helper in `Drivers/CH390/CH390_Interface.c` that: - - disables `SPI1` - - reconfigures `PA5/PA7` as GPIO outputs and `PA6` as GPIO input - - clocks out register reads in software with explicit `CS/SCK/MOSI/MISO` control - - restores the pins back to hardware-SPI mode before returning -- On hardware-SPI probe failure, startup now prints one additional RTT line with bit-bang reads of: - - `VIDL` - - `VIDH` - - `PIDL` - - `PIDH` - - `CHIPR` - -### Observed RTT Output - -```text -TCP2UART boot -ETH init: gpio -ETH init: spi -ETH init: reset -ETH init: probe -CH390 bitbang VIDL=0xFF VIDH=0xFF PIDL=0xFF PIDH=0xFF CHIPR=0xFF -ETH init: invalid chip id -CH390 VID=0xFFFF PID=0xFFFF REV=0xFF NSR=0xFF LINK=0 -CH390 NCR=0xFF RCR=0xFF IMR=0xFF INTCR=0xFF GPR=0xFF ISR=0xFF -``` - -### Interpretation - -- The MCU is alive, RTT is alive, and the firmware reaches the CH390 identity-probe stage normally. -- The normal hardware-SPI read path still returns all `0xFF`. -- The independent bit-bang read path also returns all `0xFF` for the same critical identity registers. -- This is the most important final discriminator collected so far: - - if hardware SPI alone were the remaining problem, bit-bang should have had a realistic chance to return valid IDs - - because both methods return the same all-`0xFF` result, the remaining fault is much more consistent with CH390-side non-response than with STM32 SPI peripheral behavior - -### External Reference Cross-Check - -- Public working CH390/DM9051 drivers commonly use `SPI mode 0`, `1-bit command + 7-bit address` framing, and contiguous packet-memory bursts. -- Those references support further cleanup of packet-memory transaction framing once basic register access works. -- They do **not** make packet-memory fragmentation a strong explanation for `VID/PID/basic regs = 0xFFFF/0xFF` from the very start. -- Therefore the remaining software-side suspects are now weaker than the board-level suspects. - -### Final Conclusion - -- The project has already removed several real software defects and sources of diagnostic noise: - - PHY access timeout hole - - watchdog-related HardFault - - interrupt-masked blocking SPI runtime path - - redundant early CH390 reset in `main()` - - warning-producing dead code / unused values -- After those fixes, both hardware-SPI and bit-bang reads still show CH390 register non-response. -- The most defensible current conclusion is: - - **software is no longer the primary blocker** - - the remaining fault is more likely in board wiring, chip power/reset state, effective CS selection, MISO drive, signal integrity, or the CH390D device itself - -### Recommended Next Step Outside Firmware - -- Probe real waveforms on `CS/SCK/MOSI/MISO/RST/INT` during the identity-read transaction. -- Specifically verify that: - - `CS` actually reaches the CH390 pin and stays low for the transaction - - `RST` is released high at the chip pin - - `MISO` is actively driven by the CH390 instead of floating high - - the CH390 supply and reset domain are valid during the read window diff --git a/工程调试指南.md b/工程调试指南.md index 5812fe6..3490e08 100644 --- a/工程调试指南.md +++ b/工程调试指南.md @@ -87,12 +87,11 @@ ### 3.3 配置口与业务口边界 1. `USART1` - - 配置口 - - 负责接收 `AT` 命令 - - 当前接收逻辑在: - - `Core/Src/main.c` 的 `App_PollUart1ConfigRx()` - - `Core/Src/stm32f1xx_it.c` 的 `HAL_UART_RxCpltCallback()` - - `App/config.c` 的 `config_uart_rx_byte()` / `config_process_at_cmd()` + - 配置口 + - 负责接收 `AT` 命令 + - 当前接收逻辑在: + - `Core/Src/stm32f1xx_it.c` 的 `HAL_UART_RxCpltCallback()` + - `App/config.c` 的 `config_uart_rx_byte()` / `config_poll()` / `config_process_at_cmd()` 2. `USART2 / USART3` - 数据口 - 负责普通透传或 MUX 承载 @@ -245,10 +244,11 @@ 根据已有联调记录,配置口最关键的 bench 规则是: -1. 当前现场验证时,配置命令必须保证以换行完成帧。 +1. 对外手册统一要求 AT 文本以 `\r\n` 结束,现场工具也应优先按这个格式发送。 2. 若主机侧发送方式不对,现象会很像“配置口完全无响应”。 3. 因此,配置口不响应时,第一优先级不是改 parser,而是先验证主机端发送格式与接线。 -4. `BAUD` 类命令若查询值已变化,但 `USART2/USART3` 现场波特率尚未变化,不应立即归因为命令无效,应先确认是否已经执行 `AT+SAVE` 与 `AT+RESET`。 +4. 当前实现可能容忍只以 `\n` 结束的输入,但这只是接收实现细节,不作为对外协议口径。 +5. `BAUD` 类命令若查询值已变化,但 `USART2/USART3` 现场波特率尚未变化,不应立即归因为命令无效,应先确认是否已经执行 `AT+SAVE` 与 `AT+RESET`。 ### 6.3 最小验证步骤 @@ -629,9 +629,9 @@ RTT输出: 1. `AT固件使用手册.md` 2. `项目技术实现.md` 3. `项目需求说明.md` -4. `uart-ch390-debug-handoff.md` -5. `CH390_最终结论报告.md` -6. `build_keil.log` +4. `代码结构与阅读指南.md` +5. `项目文档索引.md` +6. `CH390_最终结论报告.md` 7. `PCB/SCH_Schematic1_2026-03-26.pdf` 8. `tools/tcp_debug_server.py` 9. `tools/start_tcp_debug_server.ps1` diff --git a/项目技术实现.md b/项目技术实现.md index a6812ea..cbea61f 100644 --- a/项目技术实现.md +++ b/项目技术实现.md @@ -295,21 +295,23 @@ EN,LPORT,RIP,RPORT,UART 3. `flash_bytes = 57404` 4. `ram_bytes = 20440` -## 七、主循环实现方向 +## 七、主循环实现路径 主循环仍保持裸机轮询风格: ```c while (1) { + config_poll(); + uart_trans_poll(); ethernetif_poll(); ethernetif_check_link(); sys_check_timeouts(); - tcp_link_poll(); - uart_mux_poll(); - config_poll(); - - route_dispatch(); + App_StopLinksIfNeeded(); + App_StartLinksIfNeeded(); + App_RouteRawUartTraffic(); + App_RouteMuxUartTraffic(); + App_RouteTcpTraffic(); if (reset_requested) { NVIC_SystemReset(); @@ -317,11 +319,11 @@ while (1) } ``` -下一阶段实现要求: +当前实现要求: 1. 统一由 `LINK[idx]` 驱动实例状态 2. 统一由 `MUX` 决定数据口承载模式 -3. 统一由 `route_dispatch()` 按 `SRCID / DSTMASK` 分发 +3. RAW 路由与 MUX 路由分别由 `App_RouteRawUartTraffic()`、`App_RouteMuxUartTraffic()`、`App_RouteTcpTraffic()` 执行 ## 八、实现边界 diff --git a/项目文档索引.md b/项目文档索引.md new file mode 100644 index 0000000..1d47997 --- /dev/null +++ b/项目文档索引.md @@ -0,0 +1,45 @@ +# TCP2UART 项目文档索引 + +本文档用于说明当前仓库中应长期维护的项目文档,以及已合并或删除的过时资料归属。 + +## 当前有效文档 + +| 文档 | 用途 | 阅读时机 | +|------|------|----------| +| `项目需求说明.md` | 需求源头,定义硬件边界、软件边界、最终协议模型和验收口径 | 立项、需求确认、验收前 | +| `AT固件使用手册.md` | 对外 AT 协议和 MUX 帧使用说明 | 上位机开发、联调、测试脚本编写 | +| `项目技术实现.md` | 内部实现口径,说明配置模型、路由层、TCP 背压和网络链路策略 | 修改固件架构或核心逻辑前 | +| `代码结构与阅读指南.md` | 代码目录、主流程、模块职责和推荐阅读路径 | 新成员接手、代码审查、定位问题前 | +| `工程调试指南.md` | 实机 bring-up、串口、CH390、lwIP、TCP/UART 通路调试步骤 | 现场调试、故障复现、回归验证 | +| `CH390_最终结论报告.md` | CH390 阶段性硬件/软件排障结论归档 | 遇到 CH390 低层异常时回看历史结论 | + +## 非项目叙述文档 + +| 文件 | 说明 | +|------|------| +| `Reference/stm32f103r8.pdf` | STM32F103R8 参考资料 | +| `Reference/CH390DS1.PDF` | CH390D 数据手册 | +| `TCP2UART.ioc` | STM32CubeMX 外设、时钟、DMA、引脚配置源 | +| `MDK-ARM/TCP2UART.uvprojx` | Keil MDK 主工程文件 | +| `CMakeLists.txt`、`cmake/stm32cubemx/CMakeLists.txt` | CMake 工程入口与源码/包含路径清单 | + +## 已合并或删除的过时资料 + +以下文件不再作为长期文档维护: + +| 原文件 | 处理方式 | 原因 | +|--------|----------|------| +| `项目计划.md` | 删除 | 早期计划仍以 FreeRTOS、socket/netconn 为目标,已与当前 bare-metal + lwIP RAW 实现不一致 | +| `uart-ch390-debug-handoff.md` | 删除并将有效结论并入 `工程调试指南.md` | 阶段性调试交接记录,包含旧 AT 命令、旧换行口径和历史测试现场信息 | +| `Keil工程配置说明.txt` | 删除并将有效构建入口并入本索引和 `代码结构与阅读指南.md` | 手工配置清单包含旧 FreeRTOS/sys_arch 路径,容易误导当前工程维护 | +| `uv4_stdout.txt` | 删除 | 构建输出日志,不属于长期项目文档 | +| `MDK-ARM/build_capture.txt` | 删除 | 构建捕获日志,不属于长期项目文档 | +| `MDK-ARM/keil-build-viewer-record.txt` | 删除 | 构建查看器记录文件,不属于长期项目文档 | + +## 文档维护原则 + +1. 对外协议只在 `AT固件使用手册.md` 中完整展开;其他文档只引用核心约束,避免重复维护。 +2. 需求和实现统一使用 `MUX / NET / LINK` 三层模型。 +3. `LINK[idx]` 是内部配置数组模型,`S1/S2/C1/C2` 是 AT 命令中使用的对外角色名。 +4. 调试现场日志只在仍有长期诊断价值时整理进 `工程调试指南.md` 或 `CH390_最终结论报告.md`,不要直接保留临时 handoff/log 文件。 +5. 构建结果、IDE 输出、串口抓包原始记录应放入未纳入长期文档的 artifacts/logs 位置,避免污染项目根目录。