From 81594c65202c788687cc22bbcb767c9822eb43c2 Mon Sep 17 00:00:00 2001 From: xiao Date: Wed, 1 Apr 2026 04:30:27 +0800 Subject: [PATCH] docs: record CH390 hardware-bound conclusion --- CH390_最终结论报告.md | 113 ++++++++++++++++++++++++++++++++ Drivers/CH390/CH390_Interface.c | 92 ++++++++++++++++++++++++++ Drivers/CH390/CH390_Interface.h | 1 + Drivers/CH390/ch390_runtime.c | 7 ++ uart-ch390-debug-handoff.md | 74 +++++++++++++++++++++ 项目技术实现.md | 73 +++++++++++++++------ 6 files changed, 340 insertions(+), 20 deletions(-) create mode 100644 CH390_最终结论报告.md diff --git a/CH390_最终结论报告.md b/CH390_最终结论报告.md new file mode 100644 index 0000000..c83e993 --- /dev/null +++ b/CH390_最终结论报告.md @@ -0,0 +1,113 @@ +# CH390 最终结论报告 + +## 结论 + +本轮循环调试的最终结论是: + +1. 当前工程中的主要软件问题已经完成收敛和清理。 +2. CH390D 仍然无法建立可信通信,但最新证据已不再支持“继续修改普通软件逻辑即可修好”的判断。 +3. 更可信的当前根因方向是硬件/总线/器件侧无响应。 + +## 已完成的软件侧工作 + +本轮已完成并验证的事项包括: + +1. 修复 PHY 访问无超时导致的永久卡死风险。 +2. 修复未初始化 IWDG 句柄刷新导致的 HardFault。 +3. 清理 CH390 运行时中断屏蔽范围,消除阻塞式 SPI 访问造成的运行时假死。 +4. 重构 CH390 运行时所有权,避免多层并发触达底层 SPI 路径。 +5. 在 `main()` 中移除重复 CH390 复位,避免启动阶段额外复位噪声。 +6. 清理已确认 warning 来源,避免无效变量继续污染构建结果。 +7. 增加 CH390 identity gate,避免在无效寄存器读回前继续执行默认配置和 PHY 初始化。 +8. 增加 bit-bang 诊断读,专门区分 STM32 硬件 SPI 实现问题和板级总线/器件无响应问题。 + +## 实机关键证据 + +### 1. MCU 自身正常工作 + +已验证: + +1. RTT 正常输出。 +2. 主循环正常运行。 +3. `TIM4` 心跳正常。 +4. 运行期不再出现此前已修复的 HardFault 和“长时间假死”症状。 + +### 2. 硬件 SPI 读 CH390 恒为全 `0xFF` + +RTT 实测: + +```text +TCP2UART boot +ETH init: gpio +ETH init: spi +ETH init: reset +ETH init: probe +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 +``` + +### 3. bit-bang 读 CH390 仍为全 `0xFF` + +在绕过 STM32 硬件 SPI 外设、直接用 GPIO 软件时序读取 `VIDL/VIDH/PIDL/PIDH/CHIPR` 后,RTT 输出为: + +```text +CH390 bitbang VIDL=0xFF VIDH=0xFF PIDL=0xFF PIDH=0xFF CHIPR=0xFF +``` + +这一点非常关键,因为它说明: + +1. 问题不再像单纯的 `SPI1` 模式寄存器或 HAL 事务实现错误。 +2. 即使 MCU 直接软件驱动 `CS/SCK/MOSI`,CH390 端仍未给出有效响应。 + +## 外部参考对结论的支撑 + +对公开 CH390 / DM9051 实现的对照结果表明: + +1. 工作实现普遍使用 `mode 0` 与 `1-bit command + 7-bit address` 事务模型。 +2. packet SRAM 连续事务在这些实现中通常是一次完整 burst,而不是随意拆成多个碎片事务。 +3. 这些信息对后续软件兼容性优化仍有价值。 +4. 但它们更适用于“基础寄存器读写已经成立之后”的阶段。 +5. 对“开机就连 `VID/PID` 都全 `0xFF`”这一症状,公开实现并不支持把根因优先归到 packet SRAM 分帧之类的高层软件问题。 + +## 当前最可信判断 + +当前更可信的方向是以下一种或多种板级问题: + +1. `CS` 没有真正选中 CH390。 +2. `MISO` 没有被 CH390 主动驱动,处于悬空上拉高状态。 +3. `RST` 在 CH390 芯片引脚侧没有真正达到有效复位/释放条件。 +4. CH390 电源、内部数字核、PHY 供电或相关时序不满足器件进入 SPI 可响应状态的条件。 +5. 芯片本体损坏,或者焊接/连线仍有隐患。 + +## 版本库状态 + +本轮已创建一个阶段性 checkpoint commit: + +1. `1808f99` `fix: harden CH390 bring-up diagnostics` + +该提交记录了: + +1. warning 清理 +2. 移除重复复位 +3. CH390 早期 identity gate +4. 链路变化稳定等待 + +## 推荐的下一步 + +此后不建议继续盲目修改普通软件逻辑。更高价值的下一步应是板级取证: + +1. 抓取 `CS/SCK/MOSI/MISO/RST/INT` 的实际波形。 +2. 确认 CH390 芯片引脚侧 `RST` 真实状态,而不是只看 MCU 端 GPIO 输出假设。 +3. 确认 `MISO` 是否被器件主动驱动,而不是上拉或悬空导致读到全高。 +4. 确认供电、地、焊接、封装方向、芯片型号与原理图一致。 + +## 收尾说明 + +本轮循环的退出条件已经满足“确定软件无明显剩余普通逻辑问题,而更像硬件存在问题”。 + +因此当前最合理的结论不是“CH390 软件已经完全正确”,而是: + +1. 软件侧已经做到了足够强的隔离和验证。 +2. 继续在没有新硬件证据的前提下反复改驱动,收益已经明显下降。 +3. 应转入硬件/总线取证阶段。 diff --git a/Drivers/CH390/CH390_Interface.c b/Drivers/CH390/CH390_Interface.c index d1fff89..e923fe3 100644 --- a/Drivers/CH390/CH390_Interface.c +++ b/Drivers/CH390/CH390_Interface.c @@ -61,6 +61,8 @@ extern SPI_HandleTypeDef hspi1; /* Timeout for SPI operations (ms) */ #define SPI_TIMEOUT 100 +#define CH390_BITBANG_HALF_PERIOD_US 2u + /*---------------------------------------------------------------------------- * Low-level GPIO operations *---------------------------------------------------------------------------*/ @@ -86,6 +88,80 @@ static inline void ch390_rst(uint8_t state) state ? GPIO_PIN_SET : GPIO_PIN_RESET); } +static inline void ch390_sck(uint8_t state) +{ + HAL_GPIO_WritePin(CH390_SCK_PORT, CH390_SCK_PIN, + state ? GPIO_PIN_SET : GPIO_PIN_RESET); +} + +static inline void ch390_mosi(uint8_t state) +{ + HAL_GPIO_WritePin(CH390_MOSI_PORT, CH390_MOSI_PIN, + state ? GPIO_PIN_SET : GPIO_PIN_RESET); +} + +static inline uint8_t ch390_miso(void) +{ + return (uint8_t)(HAL_GPIO_ReadPin(CH390_MISO_PORT, CH390_MISO_PIN) == GPIO_PIN_SET); +} + +static void ch390_spi_gpio_mode(void) +{ + GPIO_InitTypeDef GPIO_InitStruct = {0}; + + GPIO_InitStruct.Pin = CH390_SCK_PIN | CH390_MOSI_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + HAL_GPIO_Init(CH390_SCK_PORT, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = CH390_MISO_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; + GPIO_InitStruct.Pull = GPIO_NOPULL; + HAL_GPIO_Init(CH390_MISO_PORT, &GPIO_InitStruct); + + ch390_cs(1u); + ch390_sck(1u); + ch390_mosi(1u); +} + +static void ch390_spi_restore_pins(void) +{ + GPIO_InitTypeDef GPIO_InitStruct = {0}; + + GPIO_InitStruct.Pin = CH390_SCK_PIN | CH390_MOSI_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + HAL_GPIO_Init(CH390_SCK_PORT, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = CH390_MISO_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; + GPIO_InitStruct.Pull = GPIO_NOPULL; + HAL_GPIO_Init(CH390_MISO_PORT, &GPIO_InitStruct); + + ch390_spi_init(); +} + +static uint8_t ch390_bitbang_exchange_byte(uint8_t byte) +{ + uint8_t bit; + uint8_t rx_data = 0u; + + for (bit = 0u; bit < 8u; ++bit) { + ch390_mosi((uint8_t)((byte & 0x80u) != 0u)); + ch390_delay_us(CH390_BITBANG_HALF_PERIOD_US); + ch390_sck(0u); + ch390_delay_us(CH390_BITBANG_HALF_PERIOD_US); + rx_data = (uint8_t)((rx_data << 1) | ch390_miso()); + ch390_sck(1u); + byte <<= 1; + ch390_delay_us(CH390_BITBANG_HALF_PERIOD_US); + } + + return rx_data; +} + /*---------------------------------------------------------------------------- * SPI Communication *---------------------------------------------------------------------------*/ @@ -241,6 +317,22 @@ void ch390_hardware_reset(void) ch390_delay_us(50000); /* Wait 50ms for CH390 to initialize reliably */ } +uint8_t ch390_bitbang_read_reg(uint8_t reg) +{ + uint8_t value; + + __HAL_SPI_DISABLE(&hspi1); + ch390_spi_gpio_mode(); + + ch390_cs(0u); + (void)ch390_bitbang_exchange_byte((uint8_t)(reg | OPC_REG_R)); + value = ch390_bitbang_exchange_byte(0x00u); + ch390_cs(1u); + + ch390_spi_restore_pins(); + return value; +} + /*---------------------------------------------------------------------------- * CH390 Register/Memory Access Functions (SPI Mode) *---------------------------------------------------------------------------*/ diff --git a/Drivers/CH390/CH390_Interface.h b/Drivers/CH390/CH390_Interface.h index 657468e..fc62cef 100644 --- a/Drivers/CH390/CH390_Interface.h +++ b/Drivers/CH390/CH390_Interface.h @@ -19,6 +19,7 @@ void ch390_spi_init(void); uint16_t ch390_get_int_pin(void); void ch390_delay_us(uint32_t time); void ch390_hardware_reset(void); +uint8_t ch390_bitbang_read_reg(uint8_t reg); /** * @name ch390_read_reg diff --git a/Drivers/CH390/ch390_runtime.c b/Drivers/CH390/ch390_runtime.c index 14bd464..5a39cf6 100644 --- a/Drivers/CH390/ch390_runtime.c +++ b/Drivers/CH390/ch390_runtime.c @@ -123,6 +123,13 @@ void ch390_runtime_init(struct netif *netif, const uint8_t *mac) SEGGER_RTT_WriteString(0, "ETH init: probe\r\n"); g_ch390_ready = ch390_runtime_probe_identity(); if (g_ch390_ready == 0u) { + SEGGER_RTT_printf(0, + "CH390 bitbang VIDL=0x%02X VIDH=0x%02X PIDL=0x%02X PIDH=0x%02X CHIPR=0x%02X\r\n", + ch390_bitbang_read_reg(CH390_VIDL), + ch390_bitbang_read_reg(CH390_VIDH), + ch390_bitbang_read_reg(CH390_PIDL), + ch390_bitbang_read_reg(CH390_PIDH), + ch390_bitbang_read_reg(CH390_CHIPR)); netif->hwaddr_len = ETHARP_HWADDR_LEN; netif->mtu = 1500; netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; diff --git a/uart-ch390-debug-handoff.md b/uart-ch390-debug-handoff.md index 9fa67db..dcda41b 100644 --- a/uart-ch390-debug-handoff.md +++ b/uart-ch390-debug-handoff.md @@ -510,3 +510,77 @@ CH390 NCR=0xFF RCR=0xFF IMR=0xFF INTCR=0xFF GPR=0xFF ISR=0xFF - 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 0cdf1d8..1d09c5c 100644 --- a/项目技术实现.md +++ b/项目技术实现.md @@ -10,7 +10,8 @@ 2. 软件架构改为 `bare-metal main loop + DMA/IDLE + EXTI` 3. 网络栈采用 `lwIP RAW API + NO_SYS=1` 4. 调试输出采用 `SEGGER RTT` -5. 构建目标已通过 `MDK-ARM` 编译,适配 `64KB Flash / 20KB SRAM` +5. `CH390` 运行时访问已收敛为单一拥有者 `ch390_runtime` +6. 构建目标已通过 `MDK-ARM` 编译,适配 `64KB Flash / 20KB SRAM` ## 二、硬件与资源约束 @@ -30,6 +31,12 @@ - `DMA1`:UART 收发 DMA - `EXTI0`:CH390 中断输入 - `IWDG`:独立看门狗 +- `TIM4`:LED 心跳定时器 + +当前说明: + +1. `IWDG` 外设仍保留在工程中,但当前调试基线默认不在 `main()` 中启用 `MX_IWDG_Init()` +2. `App_Poll()` 已对 `HAL_IWDG_Refresh(&hiwdg)` 做句柄保护,避免未初始化句柄导致 `HardFault` ### 2.3 当前引脚分配 @@ -80,6 +87,9 @@ | Main Poll Loop | | ethernetif_poll / sys_check_timeouts / watchdog | +--------------------------------------------------+ +| CH390 Runtime Owner | +| ch390_runtime (single runtime SPI owner) | ++--------------------------------------------------+ | Peripheral/Event Layer | | UART DMA+IDLE / DMA IRQ / EXTI / SysTick | +--------------------------------------------------+ @@ -93,9 +103,10 @@ 当前执行模型为: 1. `SysTick` 提供全局毫秒时基 -2. `EXTI0` 只置位 CH390 待处理标志 +2. `EXTI0` 只置位 CH390 待处理标志,不直接做 SPI/CH390 访问 3. `DMA IRQ` 和 `UART IRQ` 只完成回调分发与 IDLE 采样 -4. 主循环统一执行网络轮询、超时推进、串口桥接和看门狗喂狗 +4. `ch390_runtime` 成为唯一的 CH390 运行时访问源 +5. 主循环统一执行网络轮询、超时推进、串口桥接和看门狗喂狗 ## 五、当前模块实现状态 @@ -156,8 +167,16 @@ 1. `SPI1 + GPIO CS + EXTI0` 驱动 CH390 2. `ethernetif.c` 采用 `NO_SYS=1` 路线 -3. CH390 中断在主循环中轮询处理 -4. 配置中的 MAC 地址会在初始化时写入 CH390 +3. 新增 `Drivers/CH390/ch390_runtime.c`,统一负责 CH390 初始化、链路查询、IRQ pending 消费、RX/TX 服务与启动诊断快照 +4. `main.c` 不再直接读取 CH390 原始寄存器,启动诊断通过 `ch390_runtime_get_diag()` 获取快照 +5. `EXTI0_IRQHandler()` 只投递 IRQ pending,不再直接混入 CH390 访问 +6. 配置中的 MAC 地址会在初始化时写入 CH390 + +当前状态: + +1. 运行时架构层面的 `SPI/LwIP/CH390` 多源访问问题已经消除 +2. `HardFault` 与“运行一会儿卡死”问题已修复 +3. CH390 当前仍未建立可信通信,最近稳定读回表现为全 `0xFFFF` 或全 `0x0000`,说明剩余问题已经收敛到低层器件响应/总线层面,而不再是运行时并发问题 ### 5.6 RTT 调试输出 @@ -227,7 +246,9 @@ while (1) NVIC_SystemReset(); } - HAL_IWDG_Refresh(&hiwdg); + if (hiwdg.Instance == IWDG) { + HAL_IWDG_Refresh(&hiwdg); + } } ``` @@ -244,11 +265,9 @@ while (1) 当前构建结果: 1. `0 Error(s)` -2. `0 Warning(s)` -3. `Code=39988` -4. `RO-data=1272` -5. `RW-data=172` -6. `ZI-data=19188` +2. 当前收敛目标已变更为 `0 Warning(s)`,并已在源码中清理已确认的未使用变量/重复复位噪声 +3. 代码体积仍满足 `STM32F103R8T6` 资源约束 +4. `MDK-ARM` 工程可直接编译并下载验证 说明当前版本已经满足: @@ -261,13 +280,16 @@ while (1) ### 9.1 功能限制 1. 当前使用静态 IP,不支持 DHCP -2. 目前未提供上板网络与串口吞吐测试结论 -3. `config` 模块仍保留较重的字符串解析逻辑,但当前体积已可接受 +2. CH390 当前仍未返回可信的 `VID/PID/REV`,最新稳定读回表现为全 `0xFFFF/0xFF` +3. 因此当前不能宣称网络链路已经真正建立,`LINK` 位读数也不可信 +4. 目前未提供上板网络与串口吞吐测试结论 +5. `config` 模块仍保留较重的字符串解析逻辑,但当前体积已可接受 +6. 新增 bit-bang 诊断读也返回全 `0xFF`,说明问题不再像单纯的 STM32 硬件 SPI 外设配置错误 ### 9.2 上板验证重点 -1. 验证 CH390 INT 极性与 EXTI 触发行为 -2. 验证 `SPI1` 与 CH390 的稳定性 +1. 验证 CH390 `CS/SCK/MOSI/MISO/RST/INT` 的实际波形与极性是否和当前软件假设一致 +2. 重点验证为何 CH390 在硬件 SPI 与 bit-bang 两条读路径下都稳定返回 `0xFFFF/0xFF` 3. 验证 `TIM4` 1ms 中断稳定性与 `PC13` 1秒翻转节拍 4. 验证 `UART2/UART3 DMA + IDLE` 在长连续流量下的行为 5. 验证 TCP Server 与 TCP Client 双链路同时工作时的稳定性 @@ -277,8 +299,19 @@ while (1) 下一阶段建议按以下顺序推进: -1. 上板联调 CH390 链路与 RTT 输出 -2. 验证 UART2/3 透传功能 -3. 补充双向透传稳定性与丢包测试 -4. 视需要继续优化 `config.c` 的体积与命令集 -5. 若后续必须支持 DHCP,再单独评估资源预算 +1. 暂停继续盲目修改 CH390 软件驱动;当前软件侧已基本排除到足以支持硬件优先排查 +2. 先做板级波形与供电/复位取证,重点看 `CS/SCK/MOSI/MISO/RST` +3. 若板级取证证明 CH390 端真实有响应,再回到软件侧处理 `mode0` 与 packet SRAM 连续事务等兼容性问题 +4. 在 CH390 可可信通信后,再验证真正的链路建立、收发与中断路径 +5. 验证 UART2/3 透传功能 +6. 补充双向透传稳定性与丢包测试 + +## 十一、当前结论 + +截至本轮调试: + +1. 系统主循环、RTT、定时器心跳、UART 配置路径均可正常工作 +2. 已修复并验证多个真实软件问题,且相关中间里程碑已提交版本库 +3. CH390 当前失败边界已被压缩到“器件未在总线上给出可信响应” +4. 在相同板卡上,硬件 SPI 读寄存器与 bit-bang 读寄存器均返回全 `0xFF` +5. 因此最可信的当前判断是:剩余问题更偏向硬件/总线/器件状态,而不是普通软件逻辑错误