diff --git a/App/config.h b/App/config.h index 76345c8..65403b6 100644 --- a/App/config.h +++ b/App/config.h @@ -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 diff --git a/App/tcp_client.c b/App/tcp_client.c index 9817fe9..5bcdbb2 100644 --- a/App/tcp_client.c +++ b/App/tcp_client.c @@ -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; } diff --git a/App/tcp_client.h b/App/tcp_client.h index 234c30c..f8f5e38 100644 --- a/App/tcp_client.h +++ b/App/tcp_client.h @@ -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 */ diff --git a/App/tcp_server.c b/App/tcp_server.c index 8e3dad2..524b2ed 100644 --- a/App/tcp_server.c +++ b/App/tcp_server.c @@ -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; } diff --git a/CH390_最终结论报告.md b/CH390_最终结论报告.md index 138653a..e2aeb84 100644 --- a/CH390_最终结论报告.md +++ b/CH390_最终结论报告.md @@ -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. 后续应在硬件修复后的稳定板卡上继续推进应用层联调与文档收口。 diff --git a/Core/Src/main.c b/Core/Src/main.c index bcf1211..cda4d2e 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -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(); diff --git a/Core/Src/spi.c b/Core/Src/spi.c index b709dd2..c04adb0 100644 --- a/Core/Src/spi.c +++ b/Core/Src/spi.c @@ -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; diff --git a/Core/Src/stm32f1xx_it.c b/Core/Src/stm32f1xx_it.c index b77097a..a2bb9c8 100644 --- a/Core/Src/stm32f1xx_it.c +++ b/Core/Src/stm32f1xx_it.c @@ -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(); } } diff --git a/Drivers/CH390/CH390.c b/Drivers/CH390/CH390.c index 5100293..67c1d07 100644 --- a/Drivers/CH390/CH390.c +++ b/Drivers/CH390/CH390.c @@ -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); } diff --git a/Drivers/CH390/CH390_Interface.c b/Drivers/CH390/CH390_Interface.c index 4fe4cae..d55da0c 100644 --- a/Drivers/CH390/CH390_Interface.c +++ b/Drivers/CH390/CH390_Interface.c @@ -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 */ } diff --git a/Drivers/CH390/ch390_runtime.c b/Drivers/CH390/ch390_runtime.c index 14bd464..afbb699 100644 --- a/Drivers/CH390/ch390_runtime.c +++ b/Drivers/CH390/ch390_runtime.c @@ -9,6 +9,32 @@ #include "lwip/pbuf.h" #include "lwip/stats.h" +#include + +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,56 +210,82 @@ 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++; - g_ch390_irq_pending = 0u; - int_status = ch390_read_reg(CH390_ISR); - ch390_write_reg(CH390_ISR, int_status); + rx_budget = 1u; + rx_hint = 0u; - if ((int_status & ISR_LNKCHG) != 0u) { - HAL_Delay(65u); - ch390_runtime_check_link(netif); - } + if ((g_ch390_irq_pending != 0u) || (ch390_get_int_pin() == GPIO_PIN_RESET)) { + g_ch390_irq_pending = 0u; + int_status = ch390_get_int_status(); - if ((int_status & ISR_ROS) != 0u) { - 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_LNKCHG) != 0u) { + ch390_runtime_check_link(netif); } + + if ((int_status & ISR_ROS) != 0u) { + LINK_STATS_INC(link.err); + } + + 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; } diff --git a/Drivers/CH390/ch390_runtime.h b/Drivers/CH390/ch390_runtime.h index b2295fc..e2e0ffe 100644 --- a/Drivers/CH390/ch390_runtime.h +++ b/Drivers/CH390/ch390_runtime.h @@ -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); diff --git a/Drivers/LwIP/src/core/ipv4/etharp.c b/Drivers/LwIP/src/core/ipv4/etharp.c index 3092dc9..7039a59 100644 --- a/Drivers/LwIP/src/core/ipv4/etharp.c +++ b/Drivers/LwIP/src/core/ipv4/etharp.c @@ -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;); diff --git a/Drivers/LwIP/src/core/ipv4/icmp.c b/Drivers/LwIP/src/core/ipv4/icmp.c index 9a82a67..f253060 100644 --- a/Drivers/LwIP/src/core/ipv4/icmp.c +++ b/Drivers/LwIP/src/core/ipv4/icmp.c @@ -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); diff --git a/Drivers/LwIP/src/netif/ethernetif.c b/Drivers/LwIP/src/netif/ethernetif.c index 1096a96..a832638 100644 --- a/Drivers/LwIP/src/netif/ethernetif.c +++ b/Drivers/LwIP/src/netif/ethernetif.c @@ -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) diff --git a/STM32F103XX_FLASH.ld b/STM32F103XX_FLASH.ld index a072aa3..ef7ef94 100644 --- a/STM32F103XX_FLASH.ld +++ b/STM32F103XX_FLASH.ld @@ -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 = .); diff --git a/工程调试指南.md b/工程调试指南.md new file mode 100644 index 0000000..e2b7169 --- /dev/null +++ b/工程调试指南.md @@ -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` diff --git a/项目技术实现.md b/项目技术实现.md index 1d09c5c..cd5df95 100644 --- a/项目技术实现.md +++ b/项目技术实现.md @@ -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 调试前置检查项,而不是放到软件排查后期才介入