fix: restore CH390 bridge flow and sync driver docs

This commit is contained in:
2026-04-03 05:18:02 +08:00
parent 1ef1ba9490
commit fd1fae8ad7
18 changed files with 501 additions and 178 deletions
+5 -5
View File
@@ -35,7 +35,7 @@ extern "C" {
#define CONFIG_MAGIC 0x54435055
/* Configuration version for compatibility */
#define CONFIG_VERSION 0x0001
#define CONFIG_VERSION 0x0002
/* Device configuration structure */
typedef struct {
@@ -76,13 +76,13 @@ typedef struct {
} device_config_t;
/* Default configuration values */
#define DEFAULT_IP {192, 168, 1, 100}
#define DEFAULT_IP {192, 168, 31, 100}
#define DEFAULT_MASK {255, 255, 255, 0}
#define DEFAULT_GW {192, 168, 1, 1}
#define DEFAULT_GW {192, 168, 31, 1}
#define DEFAULT_MAC {0x02, 0x00, 0x00, 0x00, 0x00, 0x01}
#define DEFAULT_SERVER_PORT 8080
#define DEFAULT_REMOTE_IP {192, 168, 1, 200}
#define DEFAULT_REMOTE_PORT 9000
#define DEFAULT_REMOTE_IP {192, 168, 31, 1}
#define DEFAULT_REMOTE_PORT 8081
#define DEFAULT_UART_BAUDRATE 115200
#define DEFAULT_UART_DATABITS 8
#define DEFAULT_UART_STOPBITS 1
+20 -2
View File
@@ -106,6 +106,7 @@ static err_t tcp_client_on_connected(void *arg, struct tcp_pcb *pcb, err_t err)
ctx->pcb = pcb;
ctx->status.state = TCP_CLIENT_STATE_CONNECTED;
tcp_nagle_disable(pcb);
tcp_arg(pcb, ctx);
tcp_recv(pcb, tcp_client_on_recv);
tcp_sent(pcb, tcp_client_on_sent);
@@ -118,8 +119,9 @@ int tcp_client_init(const tcp_client_config_t *config)
memset(&g_client, 0, sizeof(g_client));
g_client.config.server_ip[0] = 192u;
g_client.config.server_ip[1] = 168u;
g_client.config.server_ip[2] = 1u;
g_client.config.server_ip[3] = 100u;
g_client.config.server_ip[2] = 31u;
g_client.config.server_ip[3] = 1u;
g_client.config.local_port = TCP_CLIENT_DEFAULT_PORT;
g_client.config.server_port = TCP_CLIENT_DEFAULT_PORT;
g_client.config.auto_reconnect = true;
g_client.config.reconnect_interval_ms = TCP_CLIENT_RECONNECT_DELAY_MS;
@@ -148,6 +150,17 @@ int tcp_client_connect(void)
return -1;
}
if (g_client.config.local_port != 0u) {
err = tcp_bind(pcb, IP_ANY_TYPE, g_client.config.local_port);
if (err != ERR_OK) {
tcp_abort(pcb);
g_client.status.state = TCP_CLIENT_STATE_DISCONNECTED;
g_client.status.errors++;
g_client.next_retry_ms = HAL_GetTick() + g_client.config.reconnect_interval_ms;
return -1;
}
}
IP_ADDR4(&remote_addr,
g_client.config.server_ip[0],
g_client.config.server_ip[1],
@@ -192,6 +205,11 @@ int tcp_client_send(const uint8_t *data, uint16_t len)
return -1;
}
if ((g_client.pcb->flags & TF_RXCLOSED) != 0u) {
g_client.status.errors++;
return -1;
}
if (tcp_sndbuf(g_client.pcb) < len) {
return 0;
}
+1
View File
@@ -37,6 +37,7 @@ typedef enum {
/* TCP Client configuration */
typedef struct {
uint8_t server_ip[4]; /* Server IP address */
uint16_t local_port; /* Local source port */
uint16_t server_port; /* Server port */
bool auto_reconnect; /* Auto reconnect on disconnect */
uint16_t reconnect_interval_ms; /* Reconnect interval */
+7
View File
@@ -104,6 +104,8 @@ static err_t tcp_server_on_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
ctx->status.state = TCP_SERVER_STATE_CONNECTED;
ctx->status.connections++;
tcp_nagle_disable(newpcb);
tcp_arg(newpcb, ctx);
tcp_recv(newpcb, tcp_server_on_recv);
tcp_sent(newpcb, tcp_server_on_sent);
@@ -189,6 +191,11 @@ int tcp_server_send(const uint8_t *data, uint16_t len)
return -1;
}
if ((g_server.client_pcb->flags & TF_RXCLOSED) != 0u) {
g_server.status.errors++;
return -1;
}
if (tcp_sndbuf(g_server.client_pcb) < len) {
return 0;
}
+29 -37
View File
@@ -5,8 +5,8 @@
本轮循环调试的最终结论是:
1. 当前工程中的主要软件问题已经完成收敛和清理。
2. CH390D 仍然无法建立可信通信,但最新证据已不再支持“继续修改普通软件逻辑即可修好”的判断
3. 更可信的当前根因方向是硬件/总线/器件侧无响应
2. CH390D 驱动、lwIP `netif`、ARP 与 ICMP 基本链路已经在实机上打通
3. 本轮最终根因已确认不是普通软件逻辑错误,而是 CH390D 相关供电滤波电容虚焊,导致供电不稳定
## 已完成的软件侧工作
@@ -32,20 +32,18 @@
3. `TIM4` 心跳正常。
4. 运行期不再出现此前已修复的 HardFault 和“长时间假死”症状。
### 2. 硬件 SPI 读 CH390 恒为全 `0xFF`
### 2. 最终硬件根因已定位
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
```
1. 板载一颗 CH390D 供电相关滤波电容存在虚焊。
2. 该问题导致 CH390D 供电不稳定,表现为寄存器读写、链路状态和报文收发在调试过程中不一致。
3. 修复硬件后,实机已观察到:
- `VID=0x1C00``PID=0x9151``REV=0x2B`
- PHY 寄存器稳定可读
- `lwIP netif` 能进入 `LINK_UP`
- 设备可接收 ARP request 并发出 ARP reply
- 设备可接收 ICMP Echo Request 并发出 Echo Reply
### 3. 历史 bit-bang 对照结果(已归档)
@@ -57,28 +55,23 @@ CH390 bitbang VIDL=0xFF VIDH=0xFF PIDL=0xFF PIDH=0xFF CHIPR=0xFF
该历史证据用于定位阶段,当前仅保留结论,不再保留对应代码路径。它说明:
1. 问题不再像单纯的 `SPI1` 模式寄存器或 HAL 事务实现错误
2. 即使 MCU 直接软件驱动 `CS/SCK/MOSI`CH390 端仍未给出有效响应
1. 在硬件未修复前,单看软件现象会误导排查方向
2. 电源完整性问题会放大为看似“SPI/IRQ/RX/TX 都可疑”的复合症状
## 外部参考对结论的支撑
对公开 CH390 / DM9051 实现的对照结果表明:
1. 工作实现普遍使用 `mode 0``1-bit command + 7-bit address` 事务模型
2. packet SRAM 连续事务在这些实现中通常是一次完整 burst,而不是随意拆成多个碎片事务
3. 这些信息对后续软件兼容性优化仍有价值
4. 但它们更适用于“基础寄存器读写已经成立之后”的阶段。
5. 对“开机就连 `VID/PID` 都全 `0xFF`”这一症状,公开实现并不支持把根因优先归到 packet SRAM 分帧之类的高层软件问题。
1. CH390 SPI 访问时序、模式选择和 RX SRAM 连续事务仍然值得严格对照参考实现
2. 但本项目最终问题并非“参考实现缺失”,而是硬件供电缺陷放大了调试噪声
3. 外部参考对软件排查有帮助,但不能替代板级供电与焊接检查
## 当前最可信判断
当前更可信的方向是以下一种或多种板级问题:
最终确认的板级问题
1. `CS` 没有真正选中 CH390
2. `MISO` 没有被 CH390 主动驱动,处于悬空上拉高状态
3. `RST` 在 CH390 芯片引脚侧没有真正达到有效复位/释放条件。
4. CH390 电源、内部数字核、PHY 供电或相关时序不满足器件进入 SPI 可响应状态的条件。
5. 芯片本体损坏,或者焊接/连线仍有隐患。
1. CH390D 供电滤波电容虚焊
2. 该虚焊导致供电稳定性不足,从而引出不稳定的寄存器读写、链路与收发行为
## 版本库状态
@@ -95,19 +88,18 @@ CH390 bitbang VIDL=0xFF VIDH=0xFF PIDL=0xFF PIDH=0xFF CHIPR=0xFF
## 推荐的下一步
此后不建议继续盲目修改普通软件逻辑。更高价值的下一步应是板级取证
后续更高价值的工作不再是继续怀疑 CH390 是否“完全不通”,而是
1. 抓取 `CS/SCK/MOSI/MISO/RST/INT` 的实际波形
2. 确认 CH390 芯片引脚侧 `RST` 真实状态,而不是只看 MCU 端 GPIO 输出假设
3. 确认 `MISO` 是否被器件主动驱动,而不是上拉或悬空导致读到全高
4. 确认供电、地、焊接、封装方向、芯片型号与原理图一致。
1. 在硬件问题修复后补充长时间稳定性测试
2. 验证 TCP Server / TCP Client 业务流量与桥接逻辑在修复硬件后的行为
3. 保持驱动层日志最小化,仅在重新排障时按需开启详细 RTT
## 收尾说明
本轮循环的退出条件已经满足“确定软件无明显剩余普通逻辑问题,而更像硬件存在问题”
本轮循环的退出条件已经满足:软件主路径已验证,且硬件根因已定位
因此当前最合理的结论不是“CH390 软件已经完全正确”,而是:
因此当前最合理的结论是:
1. 软件侧已经做到了足够强的隔离和验证
2. 继续在没有新硬件证据的前提下反复改驱动,收益已经明显下降
3. 应转入硬件/总线取证阶段
1. CH390D 驱动、lwIP `netif`、ARP 和 ICMP 基本链路已在实机打通
2. 本轮真正拦路的不是普通软件逻辑,而是板级供电滤波电容虚焊
3. 后续应在硬件修复后的稳定板卡上继续推进应用层联调与文档收口
+54 -37
View File
@@ -68,6 +68,22 @@ static volatile uint16_t g_led_blink_ticks = 0;
static uint8_t g_clock_fallback_to_hsi = 0u;
volatile uint8_t g_uart1_rx_probe_byte = 0u;
static void App_ForwardTcpPair(void)
{
uint8_t buffer[256];
int len;
len = tcp_server_recv(buffer, sizeof(buffer), 0u);
if (len > 0) {
(void)tcp_client_send(buffer, (uint16_t)len);
}
len = tcp_client_recv(buffer, sizeof(buffer), 0u);
if (len > 0) {
(void)tcp_server_send(buffer, (uint16_t)len);
}
}
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
@@ -122,24 +138,27 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
static void BootDiag_ReportCh390(void)
{
ch390_diag_t diag;
const device_config_t *cfg = config_get();
uint8_t mac_hw[6];
ch390_runtime_get_diag(&diag);
ch390_get_mac(mac_hw);
SEGGER_RTT_printf(0,
"CH390 VID=0x%04X PID=0x%04X REV=0x%02X NSR=0x%02X LINK=%d\r\n",
"CH390 VID=0x%04X PID=0x%04X REV=0x%02X LINK=%u MAC=%02X:%02X:%02X:%02X:%02X:%02X\r\n",
diag.vendor_id,
diag.product_id,
diag.revision,
diag.nsr,
diag.link_up);
diag.link_up,
mac_hw[0], mac_hw[1], mac_hw[2], mac_hw[3], mac_hw[4], mac_hw[5]);
SEGGER_RTT_printf(0,
"CH390 NCR=0x%02X RCR=0x%02X IMR=0x%02X INTCR=0x%02X GPR=0x%02X ISR=0x%02X\r\n",
diag.ncr,
diag.rcr,
diag.imr,
diag.intcr,
diag.gpr,
diag.isr);
"NET cfg IP=%u.%u.%u.%u MASK=%u.%u.%u.%u GW=%u.%u.%u.%u SrvPort=%u Cli=%u.%u.%u.%u:%u\r\n",
cfg->ip[0], cfg->ip[1], cfg->ip[2], cfg->ip[3],
cfg->mask[0], cfg->mask[1], cfg->mask[2], cfg->mask[3],
cfg->gw[0], cfg->gw[1], cfg->gw[2], cfg->gw[3],
cfg->server_port,
cfg->remote_ip[0], cfg->remote_ip[1], cfg->remote_ip[2], cfg->remote_ip[3],
cfg->remote_port);
}
static void App_PollUart1ConfigRx(void)
@@ -152,6 +171,7 @@ static void App_PollUart1ConfigRx(void)
static void App_Init(void)
{
device_config_t *cfg_mut;
const device_config_t *cfg;
ip4_addr_t ipaddr;
ip4_addr_t netmask;
@@ -161,6 +181,22 @@ static void App_Init(void)
tcp_client_config_t client_cfg;
config_init();
cfg_mut = config_get_mutable();
cfg_mut->dhcp_enable = 0u;
cfg_mut->ip[0] = 192u;
cfg_mut->ip[1] = 168u;
cfg_mut->ip[2] = 31u;
cfg_mut->ip[3] = 100u;
cfg_mut->mask[0] = 255u;
cfg_mut->mask[1] = 255u;
cfg_mut->mask[2] = 255u;
cfg_mut->mask[3] = 0u;
cfg_mut->gw[0] = 192u;
cfg_mut->gw[1] = 168u;
cfg_mut->gw[2] = 31u;
cfg_mut->gw[3] = 1u;
cfg_mut->server_port = 8080u;
cfg_mut->remote_port = 8081u;
cfg = config_get();
uart_trans_init();
@@ -195,15 +231,18 @@ static void App_Init(void)
server_cfg.port = cfg->server_port;
server_cfg.auto_reconnect = true;
tcp_server_init(&server_cfg);
tcp_server_start();
(void)tcp_server_init(&server_cfg);
(void)tcp_server_start();
memcpy(client_cfg.server_ip, cfg->remote_ip, sizeof(client_cfg.server_ip));
client_cfg.local_port = 8081u;
client_cfg.server_port = cfg->remote_port;
client_cfg.auto_reconnect = true;
client_cfg.reconnect_interval_ms = cfg->reconnect_interval;
tcp_client_init(&client_cfg);
tcp_client_connect();
(void)tcp_client_init(&client_cfg);
(void)tcp_client_connect();
SEGGER_RTT_WriteString(0, "TCP bridge enabled\r\n");
/* Arm UART1 RX interrupt path so config commands can enter via USART1. */
if (HAL_UART_Receive_IT(&huart1, (uint8_t *)&g_uart1_rx_probe_byte, 1u) != HAL_OK) {
@@ -213,37 +252,15 @@ static void App_Init(void)
static void App_Poll(void)
{
uint8_t buffer[128];
int len;
ethernetif_poll();
ethernetif_check_link();
sys_check_timeouts();
tcp_client_poll();
App_ForwardTcpPair();
uart_trans_poll();
App_PollUart1ConfigRx();
config_poll();
len = tcp_server_recv(buffer, sizeof(buffer), 0u);
if (len > 0) {
uart_trans_write(UART_CHANNEL_SERVER, buffer, (uint16_t)len);
}
len = tcp_client_recv(buffer, sizeof(buffer), 0u);
if (len > 0) {
uart_trans_write(UART_CHANNEL_CLIENT, buffer, (uint16_t)len);
}
len = (int)uart_trans_read(UART_CHANNEL_SERVER, buffer, sizeof(buffer));
if (len > 0) {
tcp_server_send(buffer, (uint16_t)len);
}
len = (int)uart_trans_read(UART_CHANNEL_CLIENT, buffer, sizeof(buffer));
if (len > 0) {
tcp_client_send(buffer, (uint16_t)len);
}
if (config_is_reset_requested()) {
config_clear_reset_requested();
NVIC_SystemReset();
+3 -3
View File
@@ -41,10 +41,10 @@ void MX_SPI1_Init(void)
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH; /* CH390 requires CPOL=High (Mode 3) */
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE; /* CH390 requires CPHA=2Edge (Mode 3) */
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; /* Match CH390 runtime baseline: CPOL=Low */
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; /* Match CH390 runtime baseline: CPHA=1Edge (Mode 0) */
hspi1.Init.NSS = SPI_NSS_SOFT; /* Software CS control for CH390 */
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64; /* 72MHz/64 = 1.125MHz for low-speed CH390 bring-up */
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64; /* 72MHz/64 = 1.125MHz for conservative CH390 bring-up */
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
+1 -1
View File
@@ -346,7 +346,7 @@ void EXTI0_IRQHandler(void)
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
/* Defer CH390 processing to main loop */
ch390_runtime_set_irq_pending();
ethernetif_set_irq_pending();
}
}
+3 -3
View File
@@ -220,9 +220,9 @@ void ch390_default_config()
// ch390_set_mac_address(mac_addr);
ch390_set_multicast(multicase_addr);
// Enable all interrupt and PAR
ch390_write_reg(CH390_IMR, IMR_ALL);
// Enable RX
// Enable only the interrupts needed by the NO_SYS polling path.
ch390_write_reg(CH390_IMR, (uint8_t)(IMR_PRI | IMR_LNKCHGI | IMR_ROOI | IMR_ROI));
// Enable RX with the reference receive filter.
ch390_write_reg(CH390_RCR, RCR_DIS_CRC | RCR_RXEN);
}
+6 -29
View File
@@ -86,23 +86,6 @@ 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);
}
/*----------------------------------------------------------------------------
* SPI Communication
*---------------------------------------------------------------------------*/
@@ -197,11 +180,9 @@ void ch390_interrupt_init(void)
void ch390_spi_init(void)
{
/* SPI1 is initialized by MX_SPI1_Init() in main.c */
/* We need to ensure correct SPI mode for CH390: */
/* - CPOL = High (idle clock is high) */
/* - CPHA = 2Edge (data captured on second edge) */
ch390_spi_apply_mode(SPI_POLARITY_LOW, SPI_PHASE_1EDGE); /* Start with Mode 0 */
/* Reference CH390 SPI path uses mode 3. */
ch390_spi_apply_mode(SPI_POLARITY_HIGH, SPI_PHASE_2EDGE);
SEGGER_RTT_WriteString(0, "CH390 SPI mode=3 (CPOL=1 CPHA=1)\r\n");
}
/**
@@ -252,7 +233,7 @@ void ch390_delay_us(uint32_t time)
*/
void ch390_hardware_reset(void)
{
ch390_delay_us(3000); /* Short delay before reset */
ch390_delay_us(10000); /* Short delay before reset */
ch390_rst(0); /* Assert reset (low) */
ch390_delay_us(3000); /* Hold reset for 3ms to satisfy datasheet minimum */
ch390_rst(1); /* Release reset (high) */
@@ -287,13 +268,9 @@ uint8_t ch390_read_reg(uint8_t reg)
*/
void ch390_write_reg(uint8_t reg, uint8_t value)
{
uint8_t frame[2];
frame[0] = reg | OPC_REG_W;
frame[1] = value;
ch390_cs(0); /* CS low - select */
ch390_spi_exchange_byte(frame[0]); /* Send write command */
ch390_spi_exchange_byte(frame[1]); /* Send write data */
(void)ch390_spi_exchange_byte(reg | OPC_REG_W);
(void)ch390_spi_exchange_byte(value);
ch390_cs(1); /* CS high - deselect */
}
+100 -24
View File
@@ -9,6 +9,32 @@
#include "lwip/pbuf.h"
#include "lwip/stats.h"
#include <string.h>
static void ch390_runtime_dispatch_frame(struct netif *netif, struct pbuf *p)
{
if ((p != NULL) && (netif->input(p, netif) != ERR_OK)) {
pbuf_free(p);
}
}
static uint8_t ch390_runtime_drain_rx(struct netif *netif, uint8_t max_frames)
{
struct pbuf *p;
uint8_t drained = 0u;
while (drained < max_frames) {
p = ch390_runtime_input_frame(netif);
if (p == NULL) {
break;
}
ch390_runtime_dispatch_frame(netif, p);
drained++;
}
return drained;
}
static volatile uint8_t g_ch390_irq_pending;
static uint8_t g_ch390_ready;
static ch390_diag_t g_diag;
@@ -18,6 +44,13 @@ static uint8_t ch390_runtime_probe_identity(void)
g_diag.vendor_id = ch390_get_vendor_id();
g_diag.product_id = ch390_get_product_id();
g_diag.revision = ch390_get_revision();
g_diag.phy_bmcr = ch390_read_phy(CH390_PHY_BMCR);
g_diag.phy_bmsr = ch390_read_phy(CH390_PHY_BMSR);
g_diag.phy_id1 = ch390_read_phy(CH390_PHY_PHYID1);
g_diag.phy_id2 = ch390_read_phy(CH390_PHY_PHYID2);
g_diag.phy_anar = ch390_read_phy(CH390_PHY_ANAR);
g_diag.phy_anlpar = ch390_read_phy(CH390_PHY_ANLPAR);
g_diag.phy_aner = ch390_read_phy(CH390_PHY_ANER);
g_diag.nsr = ch390_read_reg(CH390_NSR);
g_diag.ncr = ch390_read_reg(CH390_NCR);
g_diag.rcr = ch390_read_reg(CH390_RCR);
@@ -25,6 +58,8 @@ static uint8_t ch390_runtime_probe_identity(void)
g_diag.intcr = ch390_read_reg(CH390_INTCR);
g_diag.gpr = ch390_read_reg(CH390_GPR);
g_diag.isr = ch390_read_reg(CH390_ISR);
g_diag.phy_speed_10m = 0u;
g_diag.phy_full_duplex = 0u;
g_diag.link_up = (uint8_t)0u;
g_diag.id_valid = (uint8_t)((g_diag.vendor_id != 0x0000u) &&
(g_diag.vendor_id != 0xFFFFu) &&
@@ -36,22 +71,25 @@ static uint8_t ch390_runtime_probe_identity(void)
static void ch390_runtime_refresh_diag(void)
{
uint8_t id_valid = ch390_runtime_probe_identity();
g_diag.int_pin = (uint8_t)ch390_get_int_pin();
if (id_valid != 0u) {
g_diag.phy_speed_10m = (uint8_t)ch390_get_phy_speed();
g_diag.phy_full_duplex = (uint8_t)ch390_get_duplex_mode();
g_diag.link_up = (uint8_t)ch390_get_link_status();
}
}
static struct pbuf *ch390_runtime_input(struct netif *netif)
struct pbuf *ch390_runtime_input_frame(struct netif *netif)
{
struct ethernetif *ethernetif = (struct ethernetif *)netif->state;
struct pbuf *p = NULL;
struct pbuf *q;
uint16_t len;
uint16_t frame_len;
uint8_t rcr;
uint8_t rx_ready;
uint8_t rx_header[4];
ch390_read_reg(CH390_MRCMDX);
rx_ready = ch390_read_reg(CH390_MRCMDX);
@@ -64,6 +102,7 @@ static struct pbuf *ch390_runtime_input(struct netif *netif)
ch390_write_reg(CH390_RCR, rcr);
ethernetif->rx_len = 0u;
LINK_STATS_INC(link.drop);
g_diag.rx_packets_drop++;
return NULL;
}
@@ -72,16 +111,21 @@ static struct pbuf *ch390_runtime_input(struct netif *netif)
return NULL;
}
g_diag.rx_ready_hits++;
ch390_read_mem(rx_header, 4);
ethernetif->rx_status = rx_header[1];
ethernetif->rx_len = (uint16_t)(((uint16_t)rx_header[2] | ((uint16_t)rx_header[3] << 8)) - 4u);
frame_len = (uint16_t)((uint16_t)rx_header[2] | ((uint16_t)rx_header[3] << 8));
if ((ethernetif->rx_status & 0x3Fu) != 0u || ethernetif->rx_len == 0u || ethernetif->rx_len > 1520u) {
ch390_drop_packet((uint16_t)(ethernetif->rx_len + 4u));
if ((ethernetif->rx_status & 0x3Fu) != 0u || frame_len == 0u || frame_len > CH390_PKT_MAX) {
ethernetif->rx_len = 0u;
ch390_drop_packet(frame_len);
LINK_STATS_INC(link.drop);
g_diag.rx_packets_drop++;
return NULL;
}
ethernetif->rx_len = frame_len;
len = ethernetif->rx_len;
#if ETH_PAD_SIZE
len += ETH_PAD_SIZE;
@@ -98,12 +142,16 @@ static struct pbuf *ch390_runtime_input(struct netif *netif)
#if ETH_PAD_SIZE
pbuf_add_header(p, ETH_PAD_SIZE);
#endif
ch390_drop_packet(4u);
LINK_STATS_INC(link.recv);
g_diag.rx_packets_ok++;
g_diag.last_frame_len = frame_len;
g_diag.last_payload_len = p->tot_len;
} else {
ch390_drop_packet((uint16_t)(ethernetif->rx_len + 4u));
ch390_drop_packet(ethernetif->rx_len);
LINK_STATS_INC(link.memerr);
LINK_STATS_INC(link.drop);
g_diag.rx_packets_drop++;
}
return p;
@@ -125,7 +173,7 @@ void ch390_runtime_init(struct netif *netif, const uint8_t *mac)
if (g_ch390_ready == 0u) {
netif->hwaddr_len = ETHARP_HWADDR_LEN;
netif->mtu = 1500;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET;
ethernetif->rx_len = 0u;
ethernetif->rx_status = 0u;
@@ -144,7 +192,7 @@ void ch390_runtime_init(struct netif *netif, const uint8_t *mac)
SEGGER_RTT_WriteString(0, "ETH init: getmac\r\n");
ch390_get_mac(netif->hwaddr);
netif->mtu = 1500;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET;
ethernetif->rx_len = 0u;
ethernetif->rx_status = 0u;
@@ -162,24 +210,32 @@ void ch390_runtime_set_irq_pending(void)
g_ch390_irq_pending = 1u;
}
uint8_t ch390_runtime_is_irq_pending(void)
{
return g_ch390_irq_pending;
}
void ch390_runtime_poll(struct netif *netif)
{
uint8_t int_status;
uint8_t rx_ready;
uint8_t rx_budget;
uint8_t rx_hint;
if (!g_ch390_ready) {
return;
}
if (g_ch390_irq_pending == 0u) {
return;
}
g_diag.rx_poll_calls++;
rx_budget = 1u;
rx_hint = 0u;
if ((g_ch390_irq_pending != 0u) || (ch390_get_int_pin() == GPIO_PIN_RESET)) {
g_ch390_irq_pending = 0u;
int_status = ch390_read_reg(CH390_ISR);
ch390_write_reg(CH390_ISR, int_status);
int_status = ch390_get_int_status();
if ((int_status & ISR_LNKCHG) != 0u) {
HAL_Delay(65u);
ch390_runtime_check_link(netif);
}
@@ -187,31 +243,49 @@ void ch390_runtime_poll(struct netif *netif)
LINK_STATS_INC(link.err);
}
if ((int_status & ISR_PR) != 0u) {
uint8_t loops = 0u;
while (loops++ < 8u) {
struct pbuf *p = ch390_runtime_input(netif);
if (p == NULL) {
break;
}
if (netif->input(p, netif) != ERR_OK) {
pbuf_free(p);
if ((int_status & (ISR_PR | ISR_ROS | ISR_ROO)) != 0u) {
rx_hint = 1u;
rx_budget = 8u;
}
}
ch390_read_reg(CH390_MRCMDX);
rx_ready = ch390_read_reg(CH390_MRCMDX);
if ((rx_ready & CH390_PKT_RDY) != 0u) {
rx_hint = 1u;
if (rx_budget < 4u) {
rx_budget = 4u;
}
}
if (rx_hint != 0u) {
(void)ch390_runtime_drain_rx(netif, rx_budget);
}
}
void ch390_runtime_check_link(struct netif *netif)
{
uint8_t link_up;
static uint8_t s_last_reported = 0xFFu;
if (!g_ch390_ready) {
netif_set_link_down(netif);
return;
}
ch390_runtime_refresh_diag();
link_up = (uint8_t)ch390_get_link_status();
if (link_up != s_last_reported) {
SEGGER_RTT_printf(0,
"ETH link %s nsr=0x%02X bmsr=0x%04X anlpar=0x%04X\r\n",
link_up ? "up" : "down",
g_diag.nsr,
g_diag.phy_bmsr,
g_diag.phy_anlpar);
s_last_reported = link_up;
}
if (link_up) {
if (!netif_is_link_up(netif)) {
netif_set_link_up(netif);
@@ -244,6 +318,7 @@ err_t ch390_runtime_output(struct netif *netif, struct pbuf *p)
pbuf_add_header(p, ETH_PAD_SIZE);
#endif
LINK_STATS_INC(link.drop);
g_diag.tx_packets_timeout++;
return ERR_TIMEOUT;
}
}
@@ -261,6 +336,7 @@ err_t ch390_runtime_output(struct netif *netif, struct pbuf *p)
#endif
LINK_STATS_INC(link.xmit);
g_diag.tx_packets_ok++;
return ERR_OK;
}
+27
View File
@@ -13,6 +13,13 @@ typedef struct {
uint16_t vendor_id;
uint16_t product_id;
uint8_t revision;
uint16_t phy_bmcr;
uint16_t phy_bmsr;
uint16_t phy_id1;
uint16_t phy_id2;
uint16_t phy_anar;
uint16_t phy_anlpar;
uint16_t phy_aner;
uint8_t nsr;
uint8_t ncr;
uint8_t rcr;
@@ -20,12 +27,32 @@ typedef struct {
uint8_t intcr;
uint8_t gpr;
uint8_t isr;
uint8_t int_pin;
uint8_t phy_speed_10m;
uint8_t phy_full_duplex;
uint8_t link_up;
uint8_t id_valid;
uint32_t rx_poll_calls;
uint32_t rx_ready_hits;
uint32_t rx_packets_ok;
uint32_t rx_packets_drop;
uint32_t tx_packets_ok;
uint32_t tx_packets_timeout;
uint32_t rx_arp_frames;
uint32_t rx_ip_frames;
uint32_t rx_other_frames;
uint32_t rx_unicast_self_frames;
uint32_t rx_broadcast_frames;
uint32_t rx_multicast_frames;
uint16_t last_frame_len;
uint16_t last_payload_len;
uint16_t last_eth_type;
} ch390_diag_t;
void ch390_runtime_init(struct netif *netif, const uint8_t *mac);
struct pbuf *ch390_runtime_input_frame(struct netif *netif);
void ch390_runtime_set_irq_pending(void);
uint8_t ch390_runtime_is_irq_pending(void);
void ch390_runtime_poll(struct netif *netif);
void ch390_runtime_check_link(struct netif *netif);
err_t ch390_runtime_output(struct netif *netif, struct pbuf *p);
-1
View File
@@ -645,7 +645,6 @@ etharp_input(struct pbuf *p, struct netif *netif)
/* these are aligned properly, whereas the ARP header fields might not be */
ip4_addr_t sipaddr, dipaddr;
u8_t for_us, from_us;
LWIP_ASSERT_CORE_LOCKED();
LWIP_ERROR("netif != NULL", (netif != NULL), return;);
-1
View File
@@ -87,7 +87,6 @@ icmp_input(struct pbuf *p, struct netif *inp)
const struct ip_hdr *iphdr_in;
u16_t hlen;
const ip4_addr_t *src;
ICMP_STATS_INC(icmp.recv);
MIB2_STATS_INC(mib2.icmpinmsgs);
+2 -3
View File
@@ -47,7 +47,7 @@ void ethernetif_set_irq_pending(void)
uint8_t ethernetif_is_irq_pending(void)
{
return 0u;
return ch390_runtime_is_irq_pending();
}
static void low_level_init(struct netif *netif)
@@ -62,8 +62,7 @@ static err_t low_level_output(struct netif *netif, struct pbuf *p)
static struct pbuf *low_level_input(struct netif *netif)
{
LWIP_UNUSED_ARG(netif);
return NULL;
return ch390_runtime_input_frame(netif);
}
void ethernetif_input(struct netif *netif)
+5 -5
View File
@@ -102,14 +102,14 @@ SECTIONS
. = ALIGN(4);
} >FLASH
.ARM.extab (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
.ARM.extab :
{
. = ALIGN(4);
*(.ARM.extab* .gnu.linkonce.armextab.*)
. = ALIGN(4);
} >FLASH
.ARM (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
.ARM :
{
. = ALIGN(4);
__exidx_start = .;
@@ -118,7 +118,7 @@ SECTIONS
. = ALIGN(4);
} >FLASH
.preinit_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
.preinit_array :
{
. = ALIGN(4);
PROVIDE_HIDDEN (__preinit_array_start = .);
@@ -127,7 +127,7 @@ SECTIONS
. = ALIGN(4);
} >FLASH
.init_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
.init_array :
{
. = ALIGN(4);
PROVIDE_HIDDEN (__init_array_start = .);
@@ -137,7 +137,7 @@ SECTIONS
. = ALIGN(4);
} >FLASH
.fini_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
.fini_array :
{
. = ALIGN(4);
PROVIDE_HIDDEN (__fini_array_start = .);
+212
View File
@@ -0,0 +1,212 @@
# TCP2UART 工程调试指南
## 1. 适用范围
本指南面向当前 `TCP2UART` 工程,目标是指导以下调试场景:
1. `STM32F103R8T6 + CH390D` 的基础 bring-up
2. CH390D 的寄存器读写、链路检测、中断与收发调试
3. `lwIP RAW API + NO_SYS=1` 路线下的网络链路问题定位
4. UART 透传与 TCP 双链路联调
本指南默认基线:
1. 当前工程已切换为裸机主循环架构
2. CH390 运行时访问统一由 `ch390_runtime` 持有
3. 调试输出通过 `SEGGER RTT`
4. 当前代码已经通过 `MDK-ARM` 构建,且构建结果为 `0 error / 0 warning`
## 2. 当前工程调试边界
截至当前版本,工程状态应按以下层级理解:
1. MCU 启动、系统时钟、RTT、TIM4 心跳与主循环均可正常工作
2. CH390D 已恢复基础寄存器读写,不再处于“全 `0xFF` / 全 `0x0000`”阶段
3. 实机已读到可信芯片与 PHY 标识:`VID=0x1C00``PID=0x9151``REV=0x2B``PHYID1=0x7371``PHYID2=0x9011`
4. 实机已观察到主机 Realtek USB GbE 网卡为 `Connected 100 Mbps`,且设备侧 `lwIP netif` 标志包含 `LINK_UP`
5. 当前调试重点不再是“SPI 是否完全不通”,而是:
- 默认配置是否完整生效
- MAC 写入与回读是否一致
- PHY 协商、`INT/LINK/RX/TX` 是否进入正常工作态
- 网络链路与 UART 透传是否稳定协同
因此,后续调试应避免继续沿用“CH390 完全无响应”的旧结论,而应转入功能链路验证。
## 3. 代码入口与责任边界
### 3.1 启动路径
当前 CH390 相关启动链路为:
1. `main()`
2. `App_Init()`
3. `lwip_netif_init()`
4. `ethernetif_init()` / `low_level_init()`
5. `ch390_runtime_init()`
6. `ch390_gpio_init()`
7. `ch390_spi_init()`
8. `ch390_hardware_reset()`
9. `ch390_runtime_probe_identity()`
10. `ch390_default_config()`
11. `ch390_set_mac_address()` / `ch390_get_mac()`
12. `ch390_interrupt_init()`
### 3.2 运行时责任边界
当前代码约束如下:
1. `Drivers/CH390/CH390_Interface.c`
- 只负责底层 GPIO、SPI 与寄存器/内存访问
- 当前寄存器读写路径使用 `Mode 3 + bytewise exchange` 事务模型
2. `Drivers/CH390/CH390.c`
- 只负责芯片级 helper,例如 `default_config`、PHY 访问、MAC 操作
3. `Drivers/CH390/ch390_runtime.c`
- 唯一的 CH390 运行时拥有者
- 负责初始化、链路查询、IRQ 消费、RX/TX 服务
4. `Drivers/LwIP/src/netif/ethernetif.c`
- 不再直接承担复杂 CH390 运行时事务,只做 netif glue
5. `Core/Src/main.c`
- 启动后只通过 `BootDiag_ReportCh390()` 读取 runtime 提供的启动快照
调试时应优先维护这个边界,不要重新把原始寄存器访问散落回 `main.c` 或 EXTI 中断中。
## 4. 当前硬件连接事实
根据 `PCB/SCH_Schematic1_2026-03-26.pdf`CH390D 关键硬件事实如下:
1. MCU 与 CH390D 的连接关系:
- `PA4 -> CH_CS -> U4.SCS`
- `PA5 -> CH_CLK -> R10(22Ω) -> U4.SCK`
- `PA6 <- CH_SDO <- U4.SDO/MISO`
- `PA7 -> CH_SDI -> U4.SDI/MOSI`
- `PB0 <- CH_INT <- U4.INT`
- `PB1 -> CH_RST -> R8(470Ω) -> U4.RSTB`
2. CH390D 使用 `25MHz` 晶振 `X2`
3. CH390D 存在独立相关电源域:
- `VDDK`
- `AVDD33/VDDIO`
- `AVDD33`
4. CH390D 周边去耦电容包括 `C18/C20/C21/C22`
5. 原理图上未看到独立的 `MISO` 外部上拉电阻
6. 当前代码中的 `ch390_hardware_reset()` 采用:
- 复位前等待 `10ms`
- `RSTB` 拉低 `3ms`
- 释放后等待 `50ms`
调试时必须优先在 **CH390 芯片脚侧** 量测,而不是只在 MCU GPIO 侧判断。
## 5. 推荐调试顺序
### 5.1 P0:先确认基础条件
每次进入功能调试前,先确认以下最小基线:
1. `MDK` 可成功构建,且当前日志为 `0 error / 0 warning`
2. RTT 可输出启动日志
3. `BootDiag_ReportCh390()` 能打印出 `VID/PID/REV/NSR/NCR/RCR/IMR/INTCR/GPR/ISR`
4. `App_Poll()` 正常运行,LED 心跳正常
### 5.2 P1:验证 CH390 初始化状态
优先检查以下问题:
1. `ETH init: probe/default/mac/getmac/irq/done` 是否完整打印
2. `VID/PID/REV` 与 PHY ID 是否稳定、可信
3. MAC 写入与 `ch390_get_mac()` 回读是否一致
4. `RCR/IMR/INTCR/GPR` 是否进入预期工作态
5. `link_up``lwIP netif``LINK_UP` 标志是否与物理网线状态一致
如这一层失败,优先回到芯片侧量测:
1. `RSTB`
2. `CS`
3. `SCK`
4. `MOSI`
5. `MISO`
6. `VDDK / AVDD33/VDDIO / AVDD33`
### 5.3 P2:验证 IRQ 与链路检测
在基础寄存器读写可信后,重点验证:
1. `PB0/EXTI0` 是否能接收到 CH390 的 `INT`
2. `ISR_LNKCHG` 发生时,`ch390_runtime_poll()` 是否正确更新链路状态,不再依赖阻塞式 `HAL_Delay(65)`
3. `ethernetif_check_link()``ch390_runtime_check_link()` 的结果是否一致
4. 必须区分“启动快照中的 `g_diag.link_up`”与“主循环中实时更新后的 `netif->flags & NETIF_FLAG_LINK_UP`
### 5.4 P3:验证 RX/TX 数据通路
`link_up` 可信后,再继续验证数据通路:
1. 先确保主机网卡与设备处于同一子网;当前联调基线曾使用主机 `192.168.31.1` 与设备 `192.168.31.x`
2. `ch390_runtime_input()` 是否能稳定读取 `RX SRAM`
3. `rx_status / rx_len` 是否合理
4. `ch390_runtime_output()` 是否能正确等待 `TCR_TXREQ` 清零并发送帧
5. 网络端与 UART2/UART3 透传是否联通
### 5.5 P4:验证系统级联调
最终再验证:
1. TCP Server 与 UART2 双向透传
2. TCP Client 与 UART3 双向透传
3. 配置保存、复位与 MAC 生效路径
4. 长时间运行稳定性
## 6. 推荐的现场观测点
### 6.1 软件观测点
优先关注以下函数与状态:
1. `BootDiag_ReportCh390()`
2. `ch390_runtime_probe_identity()`
3. `ch390_runtime_init()`
4. `ch390_runtime_poll()`
5. `ch390_runtime_check_link()`
6. `ch390_runtime_input()`
7. `ch390_runtime_output()`
### 6.2 硬件观测点
优先关注 CH390 芯片脚侧:
1. `RSTB`
2. `SCS`
3. `SCK`
4. `SDI/MOSI`
5. `SDO/MISO`
6. `INT`
7. `VDDK`
8. `AVDD33/VDDIO`
9. `AVDD33`
10. `XI/XO`
## 7. 常见误区
调试当前工程时,应避免以下误区:
1. 不要再直接沿用“CH390 恒为全 `0xFF`”这一过时结论
2. 不要在 `main.c`、IRQ、netif 多处重新插入原始 CH390 访问
3. 不要在没有芯片脚侧证据前,只凭 MCU 侧 GPIO 就认定 `RST/CS/SCK/MISO` 正常
4. 不要在基础寄存器读写尚不可信时,直接调高层 `TCP/UART` 业务逻辑
5. 不要把一次性的 bring-up 实验代码长期留在正式启动路径中
## 8. 当前推荐结论表达方式
当需要向项目成员同步当前状态时,推荐使用如下表述:
1. 当前工程软件架构已稳定在 `bare-metal + lwIP RAW + ch390_runtime 单一拥有者`
2. 当前 CH390D 已恢复基础寄存器读写,调试重点已转入初始化完整性、链路、中断与收发功能
3. 硬件验证仍必须以 CH390 芯片脚侧波形与电源域量测为准
4. 上层 `TCP/UART` 联调应建立在 CH390 初始化、链路与供电稳定三者都可信之后
5. 本项目已确认过一次真实硬件根因:CH390D 供电滤波电容虚焊会直接表现为网络收发异常
## 9. 建议配套文档
建议与本指南配套阅读:
1. `项目技术实现.md`
2. `CH390D_硬件检查指南.md`
3. `uart-ch390-debug-handoff.md`
4. `PCB/SCH_Schematic1_2026-03-26.pdf`
+17 -18
View File
@@ -176,7 +176,8 @@
1. 运行时架构层面的 `SPI/LwIP/CH390` 多源访问问题已经消除
2. `HardFault` 与“运行一会儿卡死”问题已修复
3. CH390 当前仍未建立可信通信,最近稳定读回表现为全 `0xFFFF` 或全 `0x0000`,说明剩余问题已经收敛到低层器件响应/总线层面,而不再是运行时并发问题
3. CH390D 当前已恢复基础寄存器读写,调试重点已从“SPI 是否完全不通”转入初始化完整性、链路、中断与收发功能验证
4. 最终实板定位表明,一颗 CH390D 供电滤波电容虚焊会直接导致供电不稳,并放大为网络收发异常
### 5.6 RTT 调试输出
@@ -265,7 +266,7 @@ while (1)
当前构建结果:
1. `0 Error(s)`
2. 当前收敛目标已变更为 `0 Warning(s)`,并已在源码中清理已确认的未使用变量/重复复位噪声
2. `0 Warning(s)`
3. 代码体积仍满足 `STM32F103R8T6` 资源约束
4. `MDK-ARM` 工程可直接编译并下载验证
@@ -280,31 +281,28 @@ while (1)
### 9.1 功能限制
1. 当前使用静态 IP,不支持 DHCP
2. CH390 当前仍未返回可信的 `VID/PID/REV`,最新稳定读回表现为全 `0xFFFF/0xFF`
3. 因此当前不能宣称网络链路已经真正建立,`LINK` 位读数也不可信
4. 目前未提供上板网络与串口吞吐测试结论
5. `config` 模块仍保留较重的字符串解析逻辑,但当前体积已可接受
6. 新增 bit-bang 诊断读也返回全 `0xFF`,说明问题不再像单纯的 STM32 硬件 SPI 外设配置错误
2. 当前已验证 CH390 基础寄存器读写、PHY 管理寄存器访问与 lwIP `netif` 链路置位路径;RX/TX 实际业务流量仍需继续做板上验证
3. 目前未提供完整的上板网络吞吐、丢包率与长时间稳定性报告
4. `config` 模块仍保留较重的字符串解析逻辑,但当前体积已可接受
### 9.2 上板验证重点
1. 验证 CH390 `CS/SCK/MOSI/MISO/RST/INT` 的实际波形与极性是否和当前软件假设一致
2. 重点验证为何 CH390 在硬件 SPI 与 bit-bang 两条读路径下都稳定返回 `0xFFFF/0xFF`
1. 验证 CH390 `RST/CS/SCK/MOSI/MISO/INT` 在芯片脚侧的实际波形与当前软件假设一致
2. 验证 `VID/PID/REV`、MAC 写回、`RCR/IMR/INTCR`、PHY 寄存器与 `LINK`/`netif` 链路状态在多次上电下稳定一致
3. 验证 `TIM4` 1ms 中断稳定性与 `PC13` 1秒翻转节拍
4. 验证 `UART2/UART3 DMA + IDLE` 在长连续流量下的行为
5. 验证 TCP Server 与 TCP Client 双链路同时工作时的稳定性
6. 验证配置保存、复位、MAC 生效路径
7. 验证 CH390 收发路径、链路变化中断与 RX/TX 数据通路
## 十、后续建议
下一阶段建议按以下顺序推进:
1. 暂停继续盲目修改 CH390 软件驱动;当前软件侧已基本排除到足以支持硬件优先排查
2. 先做板级波形与供电/复位取证,重点看 `CS/SCK/MOSI/MISO/RST`
3.板级取证证明 CH390 端真实有响应,再回到软件侧处理 `mode0` 与 packet SRAM 连续事务等兼容性问题
4. 在 CH390 可可信通信后,再验证真正的链路建立、收发与中断路径
5. 验证 UART2/3 透传功能
6. 补充双向透传稳定性与丢包测试
1. 在当前可读写基线下,优先验证 `default_config`、MAC、PHY 协商/链路、IRQ、RX/TX 是否完整生效
2. 同步用芯片脚侧波形与电源域量测验证 `RST/CS/SCK/MOSI/MISO/INT``VDDK/AVDD33/VDDIO/AVDD33`
3.链路与寄存器状态可信,再推进 TCP Server / TCP Client 与 UART2 / UART3 的双向联调
4. 完成后补充吞吐、丢包、长稳与异常恢复测试
## 十一、当前结论
@@ -312,6 +310,7 @@ while (1)
1. 系统主循环、RTT、定时器心跳、UART 配置路径均可正常工作
2. 已修复并验证多个真实软件问题,且相关中间里程碑已提交版本库
3. CH390 当前失败边界已被压缩到“器件未在总线上给出可信响应”
4. 在相同板卡上,硬件 SPI 读寄存器与 bit-bang 读寄存器均返回全 `0xFF`
5. 因此最可信的当前判断是:剩余问题更偏向硬件/总线/器件状态,而不是普通软件逻辑错误
3. CH390D 当前已恢复基础寄存器读写,且已在实机上验证 `VID=0x1C00``PID=0x9151``REV=0x2B`、PHY ID 可读
4. 运行时 `lwIP netif` 已观察到 `LINK_UP` 置位,说明当前阶段已不再是“完全无响应”或“链路始终未起”
5. 当前硬件修复后,ARP/Ping 基础链路已验证通过,后续重点转为业务流量与长稳验证
6. 板级供电完整性应作为 CH390D 调试前置检查项,而不是放到软件排查后期才介入