fix: restore CH390 bridge flow and sync driver docs
This commit is contained in:
+5
-5
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
}
|
||||
|
||||
|
||||
+109
-33
@@ -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,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 = .);
|
||||
|
||||
@@ -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`
|
||||
@@ -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 调试前置检查项,而不是放到软件排查后期才介入
|
||||
|
||||
Reference in New Issue
Block a user