docs: record CH390 hardware-bound conclusion

This commit is contained in:
2026-04-01 04:30:27 +08:00
parent 1808f9916f
commit 81594c6520
6 changed files with 340 additions and 20 deletions
+113
View File
@@ -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. 应转入硬件/总线取证阶段。
+92
View File
@@ -61,6 +61,8 @@ extern SPI_HandleTypeDef hspi1;
/* Timeout for SPI operations (ms) */ /* Timeout for SPI operations (ms) */
#define SPI_TIMEOUT 100 #define SPI_TIMEOUT 100
#define CH390_BITBANG_HALF_PERIOD_US 2u
/*---------------------------------------------------------------------------- /*----------------------------------------------------------------------------
* Low-level GPIO operations * Low-level GPIO operations
*---------------------------------------------------------------------------*/ *---------------------------------------------------------------------------*/
@@ -86,6 +88,80 @@ static inline void ch390_rst(uint8_t state)
state ? GPIO_PIN_SET : GPIO_PIN_RESET); 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 * SPI Communication
*---------------------------------------------------------------------------*/ *---------------------------------------------------------------------------*/
@@ -241,6 +317,22 @@ void ch390_hardware_reset(void)
ch390_delay_us(50000); /* Wait 50ms for CH390 to initialize reliably */ 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) * CH390 Register/Memory Access Functions (SPI Mode)
*---------------------------------------------------------------------------*/ *---------------------------------------------------------------------------*/
+1
View File
@@ -19,6 +19,7 @@ void ch390_spi_init(void);
uint16_t ch390_get_int_pin(void); uint16_t ch390_get_int_pin(void);
void ch390_delay_us(uint32_t time); void ch390_delay_us(uint32_t time);
void ch390_hardware_reset(void); void ch390_hardware_reset(void);
uint8_t ch390_bitbang_read_reg(uint8_t reg);
/** /**
* @name ch390_read_reg * @name ch390_read_reg
+7
View File
@@ -123,6 +123,13 @@ void ch390_runtime_init(struct netif *netif, const uint8_t *mac)
SEGGER_RTT_WriteString(0, "ETH init: probe\r\n"); SEGGER_RTT_WriteString(0, "ETH init: probe\r\n");
g_ch390_ready = ch390_runtime_probe_identity(); g_ch390_ready = ch390_runtime_probe_identity();
if (g_ch390_ready == 0u) { 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->hwaddr_len = ETHARP_HWADDR_LEN;
netif->mtu = 1500; netif->mtu = 1500;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
+74
View File
@@ -510,3 +510,77 @@ CH390 NCR=0xFF RCR=0xFF IMR=0xFF INTCR=0xFF GPR=0xFF ISR=0xFF
- The architectural decoupling requirement is complete. - The architectural decoupling requirement is complete.
- The runtime stability 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. - 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
+53 -20
View File
@@ -10,7 +10,8 @@
2. 软件架构改为 `bare-metal main loop + DMA/IDLE + EXTI` 2. 软件架构改为 `bare-metal main loop + DMA/IDLE + EXTI`
3. 网络栈采用 `lwIP RAW API + NO_SYS=1` 3. 网络栈采用 `lwIP RAW API + NO_SYS=1`
4. 调试输出采用 `SEGGER RTT` 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 - `DMA1`UART 收发 DMA
- `EXTI0`CH390 中断输入 - `EXTI0`CH390 中断输入
- `IWDG`:独立看门狗 - `IWDG`:独立看门狗
- `TIM4`LED 心跳定时器
当前说明:
1. `IWDG` 外设仍保留在工程中,但当前调试基线默认不在 `main()` 中启用 `MX_IWDG_Init()`
2. `App_Poll()` 已对 `HAL_IWDG_Refresh(&hiwdg)` 做句柄保护,避免未初始化句柄导致 `HardFault`
### 2.3 当前引脚分配 ### 2.3 当前引脚分配
@@ -80,6 +87,9 @@
| Main Poll Loop | | Main Poll Loop |
| ethernetif_poll / sys_check_timeouts / watchdog | | ethernetif_poll / sys_check_timeouts / watchdog |
+--------------------------------------------------+ +--------------------------------------------------+
| CH390 Runtime Owner |
| ch390_runtime (single runtime SPI owner) |
+--------------------------------------------------+
| Peripheral/Event Layer | | Peripheral/Event Layer |
| UART DMA+IDLE / DMA IRQ / EXTI / SysTick | | UART DMA+IDLE / DMA IRQ / EXTI / SysTick |
+--------------------------------------------------+ +--------------------------------------------------+
@@ -93,9 +103,10 @@
当前执行模型为: 当前执行模型为:
1. `SysTick` 提供全局毫秒时基 1. `SysTick` 提供全局毫秒时基
2. `EXTI0` 只置位 CH390 待处理标志 2. `EXTI0` 只置位 CH390 待处理标志,不直接做 SPI/CH390 访问
3. `DMA IRQ``UART IRQ` 只完成回调分发与 IDLE 采样 3. `DMA IRQ``UART IRQ` 只完成回调分发与 IDLE 采样
4. 主循环统一执行网络轮询、超时推进、串口桥接和看门狗喂狗 4. `ch390_runtime` 成为唯一的 CH390 运行时访问源
5. 主循环统一执行网络轮询、超时推进、串口桥接和看门狗喂狗
## 五、当前模块实现状态 ## 五、当前模块实现状态
@@ -156,8 +167,16 @@
1. `SPI1 + GPIO CS + EXTI0` 驱动 CH390 1. `SPI1 + GPIO CS + EXTI0` 驱动 CH390
2. `ethernetif.c` 采用 `NO_SYS=1` 路线 2. `ethernetif.c` 采用 `NO_SYS=1` 路线
3. CH390 中断在主循环中轮询处理 3. 新增 `Drivers/CH390/ch390_runtime.c`,统一负责 CH390 初始化、链路查询、IRQ pending 消费、RX/TX 服务与启动诊断快照
4. 配置中的 MAC 地址会在初始化时写入 CH390 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 调试输出 ### 5.6 RTT 调试输出
@@ -227,7 +246,9 @@ while (1)
NVIC_SystemReset(); NVIC_SystemReset();
} }
HAL_IWDG_Refresh(&hiwdg); if (hiwdg.Instance == IWDG) {
HAL_IWDG_Refresh(&hiwdg);
}
} }
``` ```
@@ -244,11 +265,9 @@ while (1)
当前构建结果: 当前构建结果:
1. `0 Error(s)` 1. `0 Error(s)`
2. `0 Warning(s)` 2. 当前收敛目标已变更为 `0 Warning(s)`,并已在源码中清理已确认的未使用变量/重复复位噪声
3. `Code=39988` 3. 代码体积仍满足 `STM32F103R8T6` 资源约束
4. `RO-data=1272` 4. `MDK-ARM` 工程可直接编译并下载验证
5. `RW-data=172`
6. `ZI-data=19188`
说明当前版本已经满足: 说明当前版本已经满足:
@@ -261,13 +280,16 @@ while (1)
### 9.1 功能限制 ### 9.1 功能限制
1. 当前使用静态 IP,不支持 DHCP 1. 当前使用静态 IP,不支持 DHCP
2. 目前未提供上板网络与串口吞吐测试结论 2. CH390 当前仍未返回可信的 `VID/PID/REV`,最新稳定读回表现为全 `0xFFFF/0xFF`
3. `config` 模块仍保留较重的字符串解析逻辑,但当前体积已可接受 3. 因此当前不能宣称网络链路已经真正建立,`LINK` 位读数也不可信
4. 目前未提供上板网络与串口吞吐测试结论
5. `config` 模块仍保留较重的字符串解析逻辑,但当前体积已可接受
6. 新增 bit-bang 诊断读也返回全 `0xFF`,说明问题不再像单纯的 STM32 硬件 SPI 外设配置错误
### 9.2 上板验证重点 ### 9.2 上板验证重点
1. 验证 CH390 INT 极性与 EXTI 触发行为 1. 验证 CH390 `CS/SCK/MOSI/MISO/RST/INT` 的实际波形与极性是否和当前软件假设一致
2. 验证 `SPI1` 与 CH390 的稳定性 2. 重点验证为何 CH390 在硬件 SPI 与 bit-bang 两条读路径下都稳定返回 `0xFFFF/0xFF`
3. 验证 `TIM4` 1ms 中断稳定性与 `PC13` 1秒翻转节拍 3. 验证 `TIM4` 1ms 中断稳定性与 `PC13` 1秒翻转节拍
4. 验证 `UART2/UART3 DMA + IDLE` 在长连续流量下的行为 4. 验证 `UART2/UART3 DMA + IDLE` 在长连续流量下的行为
5. 验证 TCP Server 与 TCP Client 双链路同时工作时的稳定性 5. 验证 TCP Server 与 TCP Client 双链路同时工作时的稳定性
@@ -277,8 +299,19 @@ while (1)
下一阶段建议按以下顺序推进: 下一阶段建议按以下顺序推进:
1. 上板联调 CH390 链路与 RTT 输出 1. 暂停继续盲目修改 CH390 软件驱动;当前软件侧已基本排除到足以支持硬件优先排查
2. 验证 UART2/3 透传功能 2. 先做板级波形与供电/复位取证,重点看 `CS/SCK/MOSI/MISO/RST`
3. 补充双向透传稳定性与丢包测试 3. 若板级取证证明 CH390 端真实有响应,再回到软件侧处理 `mode0` 与 packet SRAM 连续事务等兼容性问题
4. 视需要继续优化 `config.c` 的体积与命令集 4. 在 CH390 可可信通信后,再验证真正的链路建立、收发与中断路径
5. 若后续必须支持 DHCP,再单独评估资源预算 5. 验证 UART2/3 透传功能
6. 补充双向透传稳定性与丢包测试
## 十一、当前结论
截至本轮调试:
1. 系统主循环、RTT、定时器心跳、UART 配置路径均可正常工作
2. 已修复并验证多个真实软件问题,且相关中间里程碑已提交版本库
3. CH390 当前失败边界已被压缩到“器件未在总线上给出可信响应”
4. 在相同板卡上,硬件 SPI 读寄存器与 bit-bang 读寄存器均返回全 `0xFF`
5. 因此最可信的当前判断是:剩余问题更偏向硬件/总线/器件状态,而不是普通软件逻辑错误