Compare commits
22 Commits
3f91481d00
...
V2.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 58361589d8 | |||
| 0681b8bbe4 | |||
| 3cb49fc4f8 | |||
| db714471b8 | |||
| 8c204aad77 | |||
| a6040e7d68 | |||
| 60d2af0a27 | |||
| ac0c464910 | |||
| c9ece65182 | |||
| c519f90149 | |||
| d6a1565503 | |||
| ab3b6bfc9a | |||
| c1a0822227 | |||
| 22bc6a7fef | |||
| fe03bee588 | |||
| 00be10f134 | |||
| ccd69a523e | |||
| eeccfb84a0 | |||
| e3450fd0ad | |||
| afd90d357c | |||
| c8f27e21f1 | |||
| 77da670f5c |
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"keil": {
|
||||
"project": "MDK-ARM/TCP2UART.uvprojx",
|
||||
"target": "TCP2UART",
|
||||
"log_dir": ".embeddedskills/build"
|
||||
}
|
||||
}
|
||||
@@ -28,11 +28,14 @@ MDK-ARM/DebugConfig/
|
||||
MDK-ARM/TCP2UART/
|
||||
build_keil.log
|
||||
MDK-ARM/build.log
|
||||
MDK-ARM/build_capture.txt
|
||||
MDK-ARM/build_output.txt
|
||||
MDK-ARM/keil-build-viewer.log
|
||||
MDK-ARM/keil-build-viewer-record.txt
|
||||
MDK-ARM/keil-build-viewer.exe
|
||||
MDK-ARM/EventRecorderStub.scvd
|
||||
.embeddedskills/build/
|
||||
.embeddedskills/state.json
|
||||
build_current.log
|
||||
uv4_stdout.txt
|
||||
|
||||
|
||||
+19
-7
@@ -19,6 +19,13 @@
|
||||
- 配置口:`USART1`
|
||||
- 数据口:`USART2`、`USART3`
|
||||
|
||||
### 2.1 固件版本线
|
||||
|
||||
- FreeRTOS + lwIP 版本线从 `V2.0.0` 开始。
|
||||
- 裸机版本线从 `V1.0.0` 开始。
|
||||
- 当前 FreeRTOS 固件基线 release:`TCP2UART RTOS V2.0.0`。
|
||||
- 固件下载:`https://git.furtherverse.com/gaoro-xiao/TCP2UART/releases/tag/V2.0.0`
|
||||
|
||||
职责划分:
|
||||
|
||||
- `USART1`:AT 配置口
|
||||
@@ -92,7 +99,7 @@ SYNC | LEN_H | LEN_L | SRCID | DSTMASK | PAYLOAD | TAIL
|
||||
```text
|
||||
AT\r\n
|
||||
AT+MUX?\r\n
|
||||
AT+NET=192.168.1.100,255.255.255.0,192.168.1.1,02:00:00:00:00:01\r\n
|
||||
AT+NET=192.168.31.100,255.255.255.0,192.168.31.1,00:00:00:00:00:00\r\n
|
||||
```
|
||||
|
||||
### 6.2 持久化规则
|
||||
@@ -121,9 +128,11 @@ MUX = 0
|
||||
### 7.2 NET 默认值
|
||||
|
||||
```text
|
||||
NET = 192.168.1.100,255.255.255.0,192.168.1.1,02:00:00:00:00:01
|
||||
NET = 192.168.31.100,255.255.255.0,192.168.31.1,00:00:00:00:00:00
|
||||
```
|
||||
|
||||
默认 MAC 为全 0,表示 Flash 中不固化板卡 MAC;运行时使用 `CH390D` 内部 MAC。`AT+?` 与 `AT+NET?` 回显的是当前生效 MAC。
|
||||
|
||||
### 7.3 LINK 默认值
|
||||
|
||||
```text
|
||||
@@ -171,7 +180,7 @@ AT+QUERY\r\n
|
||||
推荐返回格式:
|
||||
|
||||
```text
|
||||
+NET:IP=192.168.1.100,MASK=255.255.255.0,GW=192.168.1.1,MAC=02:00:00:00:00:01
|
||||
+NET:IP=192.168.31.100,MASK=255.255.255.0,GW=192.168.31.1,MAC=<当前生效MAC>
|
||||
+LINK:S1,EN=1,LPORT=8080,RIP=0.0.0.0,RPORT=0,UART=U0
|
||||
+LINK:S2,EN=0,LPORT=8081,RIP=0.0.0.0,RPORT=0,UART=U1
|
||||
+LINK:C1,EN=1,LPORT=9001,RIP=192.168.1.200,RPORT=9000,UART=U1
|
||||
@@ -213,7 +222,7 @@ OK
|
||||
#### 设置 NET
|
||||
|
||||
```text
|
||||
AT+NET=192.168.1.100,255.255.255.0,192.168.1.1,02:00:00:00:00:01\r\n
|
||||
AT+NET=192.168.31.100,255.255.255.0,192.168.31.1,00:00:00:00:00:00\r\n
|
||||
```
|
||||
|
||||
字段顺序:
|
||||
@@ -231,13 +240,13 @@ AT+NET?\r\n
|
||||
返回示例:
|
||||
|
||||
```text
|
||||
+NET:IP=192.168.1.100,MASK=255.255.255.0,GW=192.168.1.1,MAC=02:00:00:00:00:01
|
||||
+NET:IP=192.168.31.100,MASK=255.255.255.0,GW=192.168.31.1,MAC=<当前生效MAC>
|
||||
OK
|
||||
```
|
||||
|
||||
**MAC 设置说明:**
|
||||
|
||||
当MAC设置为全0时,固件将使用硬件MAC地址,此时通过AT+?查询到的MAC地址即为当前生效的硬件MAC地址。
|
||||
当 MAC 设置为全 0 时,固件将使用 `CH390D` 内部 MAC 地址。此时 Flash 内仍保存全 0,不会把内部 MAC 写回 Flash;`AT+?` 和 `AT+NET?` 查询到的 MAC 地址为当前运行时生效的硬件 MAC 地址。
|
||||
|
||||
### 8.5 LINK 类命令
|
||||
|
||||
@@ -268,6 +277,9 @@ ROLE,EN,LPORT,RIP,RPORT,UART
|
||||
- `Server` 与 `Client` 共用同一条 `LINK` 记录模型
|
||||
- `Server` 中 `RIP/RPORT` 可作为允许接入的对端约束或预设对端信息
|
||||
- `Client` 中 `RIP/RPORT` 表示远端目标地址与端口
|
||||
- `Client` 侧当前保留固定 `LPORT` 语义,用于满足部分上位机或现场网络策略对固定源端口的依赖
|
||||
- 为避免固定 `LPORT` 下频繁重连被 lwIP `TIME_WAIT` 长时间占用阻塞,当前固件对 `Client` 主动断开后的释放路径采用 abortive close(RST)而非优雅 `FIN/ACK` 关闭
|
||||
- 因此 `Client` 重连场景下,对端可能观察到 `RST` 或“连接被重置”,这属于当前产品约束下的有意设计取舍,不影响 `AT+LINK` 对 `LPORT` 的配置语义
|
||||
|
||||
#### 查询单条 LINK
|
||||
|
||||
@@ -354,7 +366,7 @@ OK: Defaults restored
|
||||
## 11. 推荐配置流程
|
||||
|
||||
```text
|
||||
AT+NET=192.168.1.123,255.255.255.0,192.168.1.1,02:00:00:00:00:01\r\n
|
||||
AT+NET=192.168.31.123,255.255.255.0,192.168.31.1,00:00:00:00:00:00\r\n
|
||||
AT+LINK=S1,1,10001,0.0.0.0,0,U1\r\n
|
||||
AT+LINK=S2,1,10003,0.0.0.0,0,U1\r\n
|
||||
AT+LINK=C1,1,20001,192.168.1.201,10002,U0\r\n
|
||||
|
||||
@@ -28,6 +28,14 @@ extern volatile int32_t g_netif_set_up_err;
|
||||
extern volatile int32_t g_netif_init_ok;
|
||||
|
||||
void app_start_network_tasks(void);
|
||||
void app_request_network_task_stop(void);
|
||||
void app_clear_network_task_stop(void);
|
||||
BaseType_t app_network_task_stop_requested(void);
|
||||
BaseType_t app_network_tasks_are_stopped(void);
|
||||
void app_on_network_task_exit(TaskHandle_t task_handle);
|
||||
void app_request_network_restart(void);
|
||||
void app_clear_network_restart_request(void);
|
||||
BaseType_t app_network_restart_requested(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
+124
-14
@@ -13,6 +13,7 @@
|
||||
#include "route_msg.h"
|
||||
#include "app_runtime.h"
|
||||
#include "debug_log.h"
|
||||
#include "ethernetif.h"
|
||||
#include "uart_trans.h"
|
||||
|
||||
#define CONFIG_RX_BUFFER_SIZE 160u
|
||||
@@ -22,7 +23,11 @@
|
||||
static device_config_t g_config;
|
||||
static volatile bool g_reset_requested;
|
||||
static volatile bool g_uart1_tx_busy;
|
||||
static volatile uint32_t g_config_rx_route_fail_count;
|
||||
static volatile route_send_result_t g_config_rx_route_fail_reason;
|
||||
static uint8_t g_uart1_rx_buffer[CONFIG_RX_BUFFER_SIZE];
|
||||
static char g_config_cmd_buffer[CONFIG_CMD_MAX_LEN];
|
||||
static char g_config_response_buffer[CONFIG_TX_BUFFER_SIZE];
|
||||
|
||||
static uint32_t config_calc_crc(const device_config_t *cfg)
|
||||
{
|
||||
@@ -111,6 +116,19 @@ static int parse_link_uart(const char *value, uint8_t *uart)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int parse_uart_name(const char *value, uint8_t *uart_index)
|
||||
{
|
||||
if (equals_ignore_case(value, "U0") || equals_ignore_case(value, "UART2")) {
|
||||
*uart_index = LINK_UART_U0;
|
||||
return 0;
|
||||
}
|
||||
if (equals_ignore_case(value, "U1") || equals_ignore_case(value, "UART3")) {
|
||||
*uart_index = LINK_UART_U1;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const char *link_uart_to_str(uint8_t uart)
|
||||
{
|
||||
return (uart == LINK_UART_U1) ? "U1" : "U0";
|
||||
@@ -132,6 +150,15 @@ static const char *link_index_to_name(uint32_t index)
|
||||
}
|
||||
}
|
||||
|
||||
static void config_get_display_mac(uint8_t *mac)
|
||||
{
|
||||
if (ethernetif_get_effective_mac(mac) != 0u) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(mac, g_config.net.mac, sizeof(g_config.net.mac));
|
||||
}
|
||||
|
||||
static int parse_link_name(const char *value, uint32_t *index)
|
||||
{
|
||||
if (equals_ignore_case(value, "S1")) {
|
||||
@@ -227,13 +254,15 @@ static at_result_t handle_summary_query(char *response, uint16_t max_len)
|
||||
char mask_str[16];
|
||||
char gw_str[16];
|
||||
char mac_str[18];
|
||||
uint8_t display_mac[6];
|
||||
char rip_str[CONFIG_LINK_COUNT][16];
|
||||
uint32_t i;
|
||||
|
||||
config_ip_to_str(g_config.net.ip, ip_str);
|
||||
config_ip_to_str(g_config.net.mask, mask_str);
|
||||
config_ip_to_str(g_config.net.gw, gw_str);
|
||||
config_mac_to_str(g_config.net.mac, mac_str);
|
||||
config_get_display_mac(display_mac);
|
||||
config_mac_to_str(display_mac, mac_str);
|
||||
for (i = 0; i < CONFIG_LINK_COUNT; ++i) {
|
||||
config_ip_to_str(g_config.links[i].remote_ip, rip_str[i]);
|
||||
}
|
||||
@@ -313,6 +342,14 @@ const device_config_t *config_get(void)
|
||||
return &g_config;
|
||||
}
|
||||
|
||||
uint32_t config_get_uart_baudrate(uint8_t uart_index)
|
||||
{
|
||||
if (uart_index >= CONFIG_UART_COUNT) {
|
||||
return DEFAULT_UART_BAUDRATE;
|
||||
}
|
||||
return g_config.uart_baudrate[uart_index];
|
||||
}
|
||||
|
||||
device_config_t *config_get_mutable(void)
|
||||
{
|
||||
return &g_config;
|
||||
@@ -368,6 +405,39 @@ at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_
|
||||
snprintf(response, max_len, "+MUX:%u\r\nOK\r\n", g_config.mux_mode);
|
||||
return AT_OK;
|
||||
}
|
||||
if (equals_ignore_case(p, "BAUD?")) {
|
||||
snprintf(response, max_len,
|
||||
"+BAUD:U0=%lu,U1=%lu\r\nOK\r\n",
|
||||
(unsigned long)g_config.uart_baudrate[0],
|
||||
(unsigned long)g_config.uart_baudrate[1]);
|
||||
return AT_OK;
|
||||
}
|
||||
if (parse_command_with_value(p, "BAUD", &value)) {
|
||||
char value_copy[48];
|
||||
char *cursor;
|
||||
char *token;
|
||||
uint8_t uart_index;
|
||||
uint32_t baudrate;
|
||||
|
||||
strncpy(value_copy, value, sizeof(value_copy) - 1u);
|
||||
value_copy[sizeof(value_copy) - 1u] = '\0';
|
||||
cursor = value_copy;
|
||||
token = config_next_token(&cursor);
|
||||
if (token == NULL || parse_uart_name(token, &uart_index) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid UART\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
token = config_next_token(&cursor);
|
||||
if (token == NULL || parse_u32_value(token, 1200u, 921600u, &baudrate) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid baudrate\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
g_config.uart_baudrate[uart_index] = baudrate;
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
if (parse_command_with_value(p, "MUX", &value)) {
|
||||
uint32_t mux_value;
|
||||
if (parse_u32_value(value, 0u, 1u, &mux_value) != 0) {
|
||||
@@ -383,11 +453,13 @@ at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_
|
||||
char mask_str[16];
|
||||
char gw_str[16];
|
||||
char mac_str[18];
|
||||
uint8_t display_mac[6];
|
||||
|
||||
config_ip_to_str(g_config.net.ip, ip_str);
|
||||
config_ip_to_str(g_config.net.mask, mask_str);
|
||||
config_ip_to_str(g_config.net.gw, gw_str);
|
||||
config_mac_to_str(g_config.net.mac, mac_str);
|
||||
config_get_display_mac(display_mac);
|
||||
config_mac_to_str(display_mac, mac_str);
|
||||
snprintf(response, max_len, "+NET:IP=%s,MASK=%s,GW=%s,MAC=%s\r\nOK\r\n", ip_str, mask_str, gw_str, mac_str);
|
||||
return AT_OK;
|
||||
}
|
||||
@@ -568,19 +640,24 @@ void config_uart_idle_handler(void)
|
||||
uint16_t len = CONFIG_RX_BUFFER_SIZE - dma_counter;
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
HAL_StatusTypeDef hal_status;
|
||||
route_send_result_t route_result;
|
||||
|
||||
if (g_uart1_tx_busy) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (len > 0u && xConfigQueue != NULL) {
|
||||
(void)route_send_from_isr(xConfigQueue,
|
||||
route_result = route_send_from_isr(xConfigQueue,
|
||||
0u,
|
||||
0u,
|
||||
ROUTE_CONN_UART1,
|
||||
g_uart1_rx_buffer,
|
||||
len,
|
||||
&xHigherPriorityTaskWoken);
|
||||
if (route_result != ROUTE_SEND_OK) {
|
||||
g_config_rx_route_fail_reason = route_result;
|
||||
g_config_rx_route_fail_count += 1u;
|
||||
}
|
||||
}
|
||||
|
||||
hal_status = HAL_UART_DMAStop(&huart1);
|
||||
@@ -623,18 +700,47 @@ static void config_respond_to_uart(route_msg_t *msg, const char *response)
|
||||
uart_channel_t channel = (msg->src_id == ENDPOINT_UART3) ? UART_CHANNEL_U1 : UART_CHANNEL_U0;
|
||||
uint8_t frame[ROUTE_MSG_MAX_PAYLOAD + 6u];
|
||||
uint16_t frame_len = 0u;
|
||||
uart_trans_send_result_t uart_result;
|
||||
if (uart_mux_encode_frame(msg->src_id, 0u, (const uint8_t *)response, (uint16_t)strlen(response), frame, &frame_len, sizeof(frame))) {
|
||||
(void)uart_trans_send_buffer(channel, frame, frame_len);
|
||||
uart_result = uart_trans_send_buffer(channel, frame, frame_len);
|
||||
if (uart_result != UART_TRANS_SEND_OK) {
|
||||
debug_log_printf("[CFG] resp-tx-fail ch=%u rc=%s len=%u\r\n",
|
||||
(unsigned int)channel,
|
||||
uart_trans_send_result_to_str(uart_result),
|
||||
(unsigned int)frame_len);
|
||||
}
|
||||
} else {
|
||||
debug_log_printf("[CFG] resp-enc-fail src=0x%02X len=%u\r\n",
|
||||
(unsigned int)msg->src_id,
|
||||
(unsigned int)strlen(response));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void config_report_route_failures(uint32_t *reported_route_fail_count)
|
||||
{
|
||||
uint32_t fail_count;
|
||||
route_send_result_t fail_reason;
|
||||
|
||||
if (reported_route_fail_count == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
fail_count = g_config_rx_route_fail_count;
|
||||
fail_reason = g_config_rx_route_fail_reason;
|
||||
if (fail_count != *reported_route_fail_count) {
|
||||
*reported_route_fail_count = fail_count;
|
||||
debug_log_printf("[CFG] rx-route-fail rc=%s cnt=%lu\r\n",
|
||||
route_send_result_to_str(fail_reason),
|
||||
(unsigned long)fail_count);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigTask(void *argument)
|
||||
{
|
||||
route_msg_t *msg;
|
||||
char cmd[CONFIG_CMD_MAX_LEN];
|
||||
char response[CONFIG_TX_BUFFER_SIZE];
|
||||
at_result_t result;
|
||||
uint32_t reported_route_fail_count = 0u;
|
||||
|
||||
(void)argument;
|
||||
debug_log_write("[CFG] task-entry\r\n");
|
||||
@@ -642,18 +748,22 @@ void ConfigTask(void *argument)
|
||||
debug_log_write("[CFG] task-ready\r\n");
|
||||
|
||||
for (;;) {
|
||||
if (xQueueReceive(xConfigQueue, &msg, portMAX_DELAY) != pdPASS) {
|
||||
config_report_route_failures(&reported_route_fail_count);
|
||||
|
||||
if (xQueueReceive(xConfigQueue, &msg, pdMS_TO_TICKS(50)) != pdPASS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (msg->len >= sizeof(cmd)) {
|
||||
msg->len = sizeof(cmd) - 1u;
|
||||
}
|
||||
memcpy(cmd, msg->data, msg->len);
|
||||
cmd[msg->len] = '\0';
|
||||
config_report_route_failures(&reported_route_fail_count);
|
||||
|
||||
result = config_process_at_cmd(cmd, response, sizeof(response));
|
||||
config_respond_to_uart(msg, response);
|
||||
if (msg->len >= sizeof(g_config_cmd_buffer)) {
|
||||
msg->len = sizeof(g_config_cmd_buffer) - 1u;
|
||||
}
|
||||
memcpy(g_config_cmd_buffer, msg->data, msg->len);
|
||||
g_config_cmd_buffer[msg->len] = '\0';
|
||||
|
||||
result = config_process_at_cmd(g_config_cmd_buffer, g_config_response_buffer, sizeof(g_config_response_buffer));
|
||||
config_respond_to_uart(msg, g_config_response_buffer);
|
||||
if (result == AT_NEED_REBOOT) {
|
||||
config_respond_to_uart(msg, "Note: Use AT+SAVE then AT+RESET to apply changes\r\n");
|
||||
}
|
||||
|
||||
+2
-1
@@ -66,7 +66,7 @@ typedef struct {
|
||||
#define DEFAULT_NET_IP {192, 168, 31, 100}
|
||||
#define DEFAULT_NET_MASK {255, 255, 255, 0}
|
||||
#define DEFAULT_NET_GW {192, 168, 31, 1}
|
||||
#define DEFAULT_NET_MAC {0x02, 0x00, 0x00, 0x00, 0x00, 0x01}
|
||||
#define DEFAULT_NET_MAC {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
||||
#define DEFAULT_UART_BAUDRATE 115200u
|
||||
|
||||
#define DIAG_CH390_RAW_POLL 0
|
||||
@@ -86,6 +86,7 @@ int config_save(void);
|
||||
void config_set_defaults(void);
|
||||
const device_config_t *config_get(void);
|
||||
device_config_t *config_get_mutable(void);
|
||||
uint32_t config_get_uart_baudrate(uint8_t uart_index);
|
||||
at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_len);
|
||||
void ConfigTask(void *argument);
|
||||
void config_uart_idle_handler(void);
|
||||
|
||||
+4
-5
@@ -16,13 +16,12 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Flash configuration for STM32F103R8 (64KB Flash) */
|
||||
/* Flash configuration for the current STM32F103RDT6 target (384KB Flash). */
|
||||
#define FLASH_PARAM_PAGE_SIZE 1024 /* 1KB per page for STM32F103 */
|
||||
#define FLASH_PARAM_START_ADDR 0x0800FC00 /* Last 1KB of 64KB Flash */
|
||||
#define FLASH_PARAM_END_ADDR 0x08010000 /* End of Flash */
|
||||
#define FLASH_PARAM_START_ADDR 0x0805FC00 /* Last 1KB page of 384KB Flash */
|
||||
#define FLASH_PARAM_END_ADDR 0x08060000 /* End of 384KB Flash */
|
||||
|
||||
/* For STM32F103RC (256KB), use: 0x0803FC00 */
|
||||
/* For STM32F103RB (128KB), use: 0x0801FC00 */
|
||||
/* Historical reference: STM32F103RCT6 would use 0x0803FC00 as its last page. */
|
||||
|
||||
/**
|
||||
* @brief Initialize Flash parameter storage
|
||||
|
||||
+68
-15
@@ -12,6 +12,22 @@ typedef struct {
|
||||
|
||||
static route_slot_t g_route_slots[ROUTE_MSG_POOL_SIZE];
|
||||
|
||||
const char *route_send_result_to_str(route_send_result_t result)
|
||||
{
|
||||
switch (result) {
|
||||
case ROUTE_SEND_OK:
|
||||
return "ok";
|
||||
case ROUTE_SEND_INVALID_INPUT:
|
||||
return "invalid";
|
||||
case ROUTE_SEND_POOL_EXHAUSTED:
|
||||
return "pool";
|
||||
case ROUTE_SEND_QUEUE_FULL:
|
||||
return "queue";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void route_msg_init(void)
|
||||
{
|
||||
memset(g_route_slots, 0, sizeof(g_route_slots));
|
||||
@@ -108,7 +124,7 @@ void route_msg_free_from_isr(route_msg_t *msg)
|
||||
taskEXIT_CRITICAL_FROM_ISR(saved_interrupt_status);
|
||||
}
|
||||
|
||||
static bool route_prepare(route_msg_t *msg,
|
||||
static route_send_result_t route_prepare(route_msg_t *msg,
|
||||
uint8_t src_id,
|
||||
uint8_t dst_mask,
|
||||
uint8_t conn_type,
|
||||
@@ -116,7 +132,7 @@ static bool route_prepare(route_msg_t *msg,
|
||||
uint16_t len)
|
||||
{
|
||||
if (msg == NULL || data == NULL || len == 0u || len > ROUTE_MSG_MAX_PAYLOAD) {
|
||||
return false;
|
||||
return ROUTE_SEND_INVALID_INPUT;
|
||||
}
|
||||
|
||||
msg->src_id = src_id;
|
||||
@@ -124,10 +140,21 @@ static bool route_prepare(route_msg_t *msg,
|
||||
msg->conn_type = conn_type;
|
||||
msg->len = len;
|
||||
memcpy(msg->data, data, len);
|
||||
return true;
|
||||
return ROUTE_SEND_OK;
|
||||
}
|
||||
|
||||
bool route_send(QueueHandle_t queue,
|
||||
static route_send_result_t route_validate_args(QueueHandle_t queue,
|
||||
const uint8_t *data,
|
||||
uint16_t len)
|
||||
{
|
||||
if (queue == NULL || data == NULL || len == 0u || len > ROUTE_MSG_MAX_PAYLOAD) {
|
||||
return ROUTE_SEND_INVALID_INPUT;
|
||||
}
|
||||
|
||||
return ROUTE_SEND_OK;
|
||||
}
|
||||
|
||||
route_send_result_t route_send(QueueHandle_t queue,
|
||||
uint8_t src_id,
|
||||
uint8_t dst_mask,
|
||||
uint8_t conn_type,
|
||||
@@ -135,22 +162,35 @@ bool route_send(QueueHandle_t queue,
|
||||
uint16_t len,
|
||||
TickType_t wait_ticks)
|
||||
{
|
||||
route_msg_t *msg = route_msg_alloc(wait_ticks);
|
||||
route_send_result_t result;
|
||||
route_msg_t *msg;
|
||||
|
||||
if (!route_prepare(msg, src_id, dst_mask, conn_type, data, len)) {
|
||||
result = route_validate_args(queue, data, len);
|
||||
if (result != ROUTE_SEND_OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
msg = route_msg_alloc(wait_ticks);
|
||||
|
||||
if (msg == NULL) {
|
||||
return ROUTE_SEND_POOL_EXHAUSTED;
|
||||
}
|
||||
|
||||
result = route_prepare(msg, src_id, dst_mask, conn_type, data, len);
|
||||
if (result != ROUTE_SEND_OK) {
|
||||
route_msg_free(msg);
|
||||
return false;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (xQueueSend(queue, &msg, wait_ticks) != pdPASS) {
|
||||
route_msg_free(msg);
|
||||
return false;
|
||||
return ROUTE_SEND_QUEUE_FULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
return ROUTE_SEND_OK;
|
||||
}
|
||||
|
||||
bool route_send_from_isr(QueueHandle_t queue,
|
||||
route_send_result_t route_send_from_isr(QueueHandle_t queue,
|
||||
uint8_t src_id,
|
||||
uint8_t dst_mask,
|
||||
uint8_t conn_type,
|
||||
@@ -158,17 +198,30 @@ bool route_send_from_isr(QueueHandle_t queue,
|
||||
uint16_t len,
|
||||
BaseType_t *xHigherPriorityTaskWoken)
|
||||
{
|
||||
route_msg_t *msg = route_msg_alloc_from_isr(xHigherPriorityTaskWoken);
|
||||
route_send_result_t result;
|
||||
route_msg_t *msg;
|
||||
|
||||
if (!route_prepare(msg, src_id, dst_mask, conn_type, data, len)) {
|
||||
result = route_validate_args(queue, data, len);
|
||||
if (result != ROUTE_SEND_OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
msg = route_msg_alloc_from_isr(xHigherPriorityTaskWoken);
|
||||
|
||||
if (msg == NULL) {
|
||||
return ROUTE_SEND_POOL_EXHAUSTED;
|
||||
}
|
||||
|
||||
result = route_prepare(msg, src_id, dst_mask, conn_type, data, len);
|
||||
if (result != ROUTE_SEND_OK) {
|
||||
route_msg_free_from_isr(msg);
|
||||
return false;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (xQueueSendFromISR(queue, &msg, xHigherPriorityTaskWoken) != pdPASS) {
|
||||
route_msg_free_from_isr(msg);
|
||||
return false;
|
||||
return ROUTE_SEND_QUEUE_FULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
return ROUTE_SEND_OK;
|
||||
}
|
||||
|
||||
+10
-2
@@ -29,6 +29,13 @@ typedef enum {
|
||||
ROUTE_CONN_C2
|
||||
} route_conn_type_t;
|
||||
|
||||
typedef enum {
|
||||
ROUTE_SEND_OK = 0,
|
||||
ROUTE_SEND_INVALID_INPUT,
|
||||
ROUTE_SEND_POOL_EXHAUSTED,
|
||||
ROUTE_SEND_QUEUE_FULL
|
||||
} route_send_result_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t src_id;
|
||||
uint8_t dst_mask;
|
||||
@@ -42,14 +49,15 @@ route_msg_t *route_msg_alloc(TickType_t wait_ticks);
|
||||
route_msg_t *route_msg_alloc_from_isr(BaseType_t *xHigherPriorityTaskWoken);
|
||||
void route_msg_free(route_msg_t *msg);
|
||||
void route_msg_free_from_isr(route_msg_t *msg);
|
||||
bool route_send(QueueHandle_t queue,
|
||||
const char *route_send_result_to_str(route_send_result_t result);
|
||||
route_send_result_t route_send(QueueHandle_t queue,
|
||||
uint8_t src_id,
|
||||
uint8_t dst_mask,
|
||||
uint8_t conn_type,
|
||||
const uint8_t *data,
|
||||
uint16_t len,
|
||||
TickType_t wait_ticks);
|
||||
bool route_send_from_isr(QueueHandle_t queue,
|
||||
route_send_result_t route_send_from_isr(QueueHandle_t queue,
|
||||
uint8_t src_id,
|
||||
uint8_t dst_mask,
|
||||
uint8_t conn_type,
|
||||
|
||||
@@ -16,6 +16,64 @@
|
||||
#include "app_runtime.h"
|
||||
#include "debug_log.h"
|
||||
|
||||
#define CH390_RESTART_HOLD_DOWN_MS 500u
|
||||
#define NETWORK_TASK_DELETE_SETTLE_MS 50u
|
||||
#define CH390_EXPECTED_VENDOR_ID 0x1C00u
|
||||
#define CH390_EXPECTED_PRODUCT_ID 0x9151u
|
||||
|
||||
static void net_poll_wait_for_network_tasks_stop(void)
|
||||
{
|
||||
while (app_network_tasks_are_stopped() == pdFALSE) {
|
||||
vTaskDelay(pdMS_TO_TICKS(20));
|
||||
}
|
||||
}
|
||||
|
||||
static BaseType_t net_poll_restart_network_stack(const device_config_t *cfg)
|
||||
{
|
||||
#if !DIAG_CH390_RAW_POLL
|
||||
ip4_addr_t ipaddr;
|
||||
ip4_addr_t netmask;
|
||||
ip4_addr_t gateway;
|
||||
uint16_t vendor_id;
|
||||
uint16_t product_id;
|
||||
uint8_t revision;
|
||||
|
||||
IP4_ADDR(&ipaddr, cfg->net.ip[0], cfg->net.ip[1], cfg->net.ip[2], cfg->net.ip[3]);
|
||||
IP4_ADDR(&netmask, cfg->net.mask[0], cfg->net.mask[1], cfg->net.mask[2], cfg->net.mask[3]);
|
||||
IP4_ADDR(&gateway, cfg->net.gw[0], cfg->net.gw[1], cfg->net.gw[2], cfg->net.gw[3]);
|
||||
#endif
|
||||
|
||||
ethernetif_force_link_down();
|
||||
g_netif_ready = pdFALSE;
|
||||
app_request_network_task_stop();
|
||||
net_poll_wait_for_network_tasks_stop();
|
||||
vTaskDelay(pdMS_TO_TICKS(NETWORK_TASK_DELETE_SETTLE_MS));
|
||||
vTaskDelay(pdMS_TO_TICKS(CH390_RESTART_HOLD_DOWN_MS));
|
||||
|
||||
#if DIAG_CH390_RAW_POLL
|
||||
ethernetif_diag_ch390_init();
|
||||
#else
|
||||
ethernetif_force_full_recovery(&ipaddr, &netmask, &gateway, cfg->net.mac);
|
||||
vendor_id = ethernetif_ch390_get_vendor_id();
|
||||
product_id = ethernetif_ch390_get_product_id();
|
||||
revision = ethernetif_ch390_get_revision();
|
||||
if ((vendor_id != CH390_EXPECTED_VENDOR_ID) || (product_id != CH390_EXPECTED_PRODUCT_ID)) {
|
||||
debug_log_printf("[NET] restart-recovery id-warn vid=0x%04X pid=0x%04X rev=0x%02X free=%lu min=%lu\r\n",
|
||||
(unsigned int)vendor_id,
|
||||
(unsigned int)product_id,
|
||||
(unsigned int)revision,
|
||||
(unsigned long)xPortGetFreeHeapSize(),
|
||||
(unsigned long)xPortGetMinimumEverFreeHeapSize());
|
||||
}
|
||||
#endif
|
||||
|
||||
app_clear_network_task_stop();
|
||||
g_netif_ready = pdTRUE;
|
||||
app_start_network_tasks();
|
||||
app_clear_network_restart_request();
|
||||
return pdTRUE;
|
||||
}
|
||||
|
||||
void NetPollTask(void *argument)
|
||||
{
|
||||
const device_config_t *cfg;
|
||||
@@ -98,6 +156,11 @@ void NetPollTask(void *argument)
|
||||
debug_log_write("[NET] loop-enter\r\n");
|
||||
loop_logged = pdTRUE;
|
||||
}
|
||||
|
||||
if (app_network_restart_requested() != pdFALSE) {
|
||||
(void)net_poll_restart_network_stack(cfg);
|
||||
}
|
||||
|
||||
(void)xSemaphoreTake(xNetSemaphore, pdMS_TO_TICKS(2));
|
||||
|
||||
#if DIAG_CH390_RAW_POLL
|
||||
@@ -120,8 +183,10 @@ void NetPollTask(void *argument)
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (g_netif_ready != pdFALSE) {
|
||||
ethernetif_poll();
|
||||
ethernetif_check_link();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
+140
-17
@@ -5,12 +5,66 @@
|
||||
#include "queue.h"
|
||||
#include "lwip/api.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/tcpip.h"
|
||||
|
||||
#include "app_runtime.h"
|
||||
#include "config.h"
|
||||
#include "debug_log.h"
|
||||
#include "ethernetif.h"
|
||||
#include "route_msg.h"
|
||||
|
||||
#define TCP_CLIENT_CONNECT_TIMEOUT_MS 500
|
||||
#define TCP_CLIENT_RECONNECT_INTERVAL_MS 3000u
|
||||
#define TCP_CLIENT_STOP_POLL_MS 50u
|
||||
|
||||
static BaseType_t tcp_client_stop_requested(void)
|
||||
{
|
||||
return (app_network_task_stop_requested() != pdFALSE) ? pdTRUE : pdFALSE;
|
||||
}
|
||||
|
||||
static BaseType_t tcp_client_delay_with_stop(uint32_t delay_ms)
|
||||
{
|
||||
uint32_t remaining_ms = delay_ms;
|
||||
|
||||
while (remaining_ms > 0u) {
|
||||
uint32_t slice_ms = (remaining_ms > TCP_CLIENT_STOP_POLL_MS) ? TCP_CLIENT_STOP_POLL_MS : remaining_ms;
|
||||
|
||||
if (tcp_client_stop_requested() != pdFALSE) {
|
||||
return pdFALSE;
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(slice_ms));
|
||||
remaining_ms -= slice_ms;
|
||||
}
|
||||
|
||||
return (tcp_client_stop_requested() == pdFALSE) ? pdTRUE : pdFALSE;
|
||||
}
|
||||
|
||||
static void tcp_client_abort_and_delete(struct netconn *conn, uint8_t link_index)
|
||||
{
|
||||
struct tcp_pcb *pcb;
|
||||
|
||||
if (conn == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
pcb = conn->pcb.tcp;
|
||||
if (pcb != NULL) {
|
||||
LOCK_TCPIP_CORE();
|
||||
pcb = conn->pcb.tcp;
|
||||
if (pcb != NULL) {
|
||||
tcp_abort(pcb);
|
||||
conn->pcb.tcp = NULL;
|
||||
conn->state = NETCONN_NONE;
|
||||
debug_log_printf("[CLI] idx=%u abort-close\r\n", (unsigned int)link_index);
|
||||
}
|
||||
UNLOCK_TCPIP_CORE();
|
||||
}
|
||||
|
||||
netconn_delete(conn);
|
||||
}
|
||||
|
||||
static err_t tcp_client_worker(struct netconn *conn, uint8_t link_index)
|
||||
{
|
||||
struct netbuf *buf;
|
||||
@@ -19,30 +73,51 @@ static err_t tcp_client_worker(struct netconn *conn, uint8_t link_index)
|
||||
uint8_t src_endpoint = config_link_index_to_endpoint(link_index);
|
||||
err_t err;
|
||||
route_msg_t *tx_msg;
|
||||
route_send_result_t route_result;
|
||||
|
||||
netconn_set_recvtimeout(conn, 10);
|
||||
|
||||
for (;;) {
|
||||
if (tcp_client_stop_requested() != pdFALSE) {
|
||||
return ERR_CLSD;
|
||||
}
|
||||
|
||||
err = netconn_recv(conn, &buf);
|
||||
if (err == ERR_OK) {
|
||||
do {
|
||||
void *data;
|
||||
uint16_t len;
|
||||
netbuf_data(buf, &data, &len);
|
||||
(void)route_send(xTcpRxQueue,
|
||||
route_result = route_send(xTcpRxQueue,
|
||||
src_endpoint,
|
||||
uart_endpoint,
|
||||
(link_index == CONFIG_LINK_C1) ? ROUTE_CONN_C1 : ROUTE_CONN_C2,
|
||||
(const uint8_t *)data,
|
||||
len,
|
||||
pdMS_TO_TICKS(10));
|
||||
if (route_result != ROUTE_SEND_OK) {
|
||||
debug_log_printf("[CLI] idx=%u rx-route-fail rc=%s len=%u\r\n",
|
||||
(unsigned int)link_index,
|
||||
route_send_result_to_str(route_result),
|
||||
(unsigned int)len);
|
||||
netbuf_delete(buf);
|
||||
return ERR_CLSD;
|
||||
}
|
||||
} while (netbuf_next(buf) >= 0);
|
||||
netbuf_delete(buf);
|
||||
} else if (err != ERR_TIMEOUT) {
|
||||
} else if (err == ERR_TIMEOUT) {
|
||||
if (tcp_client_stop_requested() != pdFALSE) {
|
||||
return ERR_CLSD;
|
||||
}
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
|
||||
while (xQueueReceive(xLinkTxQueues[link_index], &tx_msg, 0) == pdPASS) {
|
||||
if (tcp_client_stop_requested() != pdFALSE) {
|
||||
route_msg_free(tx_msg);
|
||||
return ERR_CLSD;
|
||||
}
|
||||
err = netconn_write(conn, tx_msg->data, tx_msg->len, NETCONN_COPY);
|
||||
route_msg_free(tx_msg);
|
||||
if (err != ERR_OK) {
|
||||
@@ -62,45 +137,62 @@ static void tcp_client_task(uint8_t link_index)
|
||||
err_t err;
|
||||
uint8_t first_connect_deferred;
|
||||
|
||||
netconn_thread_init();
|
||||
first_connect_deferred = (link_index == CONFIG_LINK_C1) ? 1u : 0u;
|
||||
|
||||
for (;;) {
|
||||
while (g_netif_ready == pdFALSE) {
|
||||
if (tcp_client_stop_requested() != pdFALSE) {
|
||||
break;
|
||||
}
|
||||
|
||||
while ((g_netif_ready == pdFALSE) || (ethernetif_link_is_up() == 0u)) {
|
||||
if (tcp_client_stop_requested() != pdFALSE) {
|
||||
goto exit_task;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
cfg = config_get();
|
||||
debug_log_printf("[CLI] idx=%u hwm=%lu en=%u lport=%u rport=%u\r\n",
|
||||
(unsigned int)link_index,
|
||||
(unsigned long)uxTaskGetStackHighWaterMark(NULL),
|
||||
(unsigned int)cfg->links[link_index].enabled,
|
||||
(unsigned int)cfg->links[link_index].local_port,
|
||||
(unsigned int)cfg->links[link_index].remote_port);
|
||||
if (cfg->links[link_index].enabled == 0u) {
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
if (tcp_client_stop_requested() != pdFALSE) {
|
||||
break;
|
||||
}
|
||||
if (tcp_client_delay_with_stop(500u) == pdFALSE) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
delay_ms = (cfg->reconnect_interval_ms == 0u) ? 3000u : cfg->reconnect_interval_ms;
|
||||
delay_ms = TCP_CLIENT_RECONNECT_INTERVAL_MS;
|
||||
|
||||
if (first_connect_deferred != 0u) {
|
||||
first_connect_deferred = 0u;
|
||||
debug_log_write("[CLI] C1 first-connect defer\r\n");
|
||||
vTaskDelay(pdMS_TO_TICKS(delay_ms));
|
||||
if (tcp_client_delay_with_stop(delay_ms) == pdFALSE) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
conn = netconn_new(NETCONN_TCP);
|
||||
if (conn == NULL) {
|
||||
vTaskDelay(pdMS_TO_TICKS(delay_ms));
|
||||
if (tcp_client_delay_with_stop(delay_ms) == pdFALSE) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cfg->links[link_index].local_port != 0u) {
|
||||
err = netconn_bind(conn, IP_ADDR_ANY, cfg->links[link_index].local_port);
|
||||
if (err != ERR_OK) {
|
||||
debug_log_printf("[CLI] idx=%u bind-fail err=%d lport=%u\r\n",
|
||||
(unsigned int)link_index,
|
||||
(int)err,
|
||||
(unsigned int)cfg->links[link_index].local_port);
|
||||
netconn_delete(conn);
|
||||
vTaskDelay(pdMS_TO_TICKS(delay_ms));
|
||||
if (tcp_client_delay_with_stop(delay_ms) == pdFALSE) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -111,15 +203,46 @@ static void tcp_client_task(uint8_t link_index)
|
||||
cfg->links[link_index].remote_ip[2],
|
||||
cfg->links[link_index].remote_ip[3]);
|
||||
|
||||
netconn_set_recvtimeout(conn, TCP_CLIENT_CONNECT_TIMEOUT_MS);
|
||||
err = netconn_connect(conn, &remote_ip, cfg->links[link_index].remote_port);
|
||||
if (err == ERR_OK) {
|
||||
debug_log_printf("[CLI] idx=%u connect-ok\r\n", (unsigned int)link_index);
|
||||
(void)tcp_client_worker(conn, link_index);
|
||||
} else {
|
||||
if (err == ERR_TIMEOUT) {
|
||||
debug_log_printf("[CLI] idx=%u connect-timeout ms=%u rip=%u.%u.%u.%u rport=%u\r\n",
|
||||
(unsigned int)link_index,
|
||||
(unsigned int)TCP_CLIENT_CONNECT_TIMEOUT_MS,
|
||||
(unsigned int)cfg->links[link_index].remote_ip[0],
|
||||
(unsigned int)cfg->links[link_index].remote_ip[1],
|
||||
(unsigned int)cfg->links[link_index].remote_ip[2],
|
||||
(unsigned int)cfg->links[link_index].remote_ip[3],
|
||||
(unsigned int)cfg->links[link_index].remote_port);
|
||||
} else {
|
||||
debug_log_printf("[CLI] idx=%u connect-fail err=%d rip=%u.%u.%u.%u rport=%u\r\n",
|
||||
(unsigned int)link_index,
|
||||
(int)err,
|
||||
(unsigned int)cfg->links[link_index].remote_ip[0],
|
||||
(unsigned int)cfg->links[link_index].remote_ip[1],
|
||||
(unsigned int)cfg->links[link_index].remote_ip[2],
|
||||
(unsigned int)cfg->links[link_index].remote_ip[3],
|
||||
(unsigned int)cfg->links[link_index].remote_port);
|
||||
}
|
||||
}
|
||||
|
||||
netconn_close(conn);
|
||||
netconn_delete(conn);
|
||||
vTaskDelay(pdMS_TO_TICKS(delay_ms));
|
||||
tcp_client_abort_and_delete(conn, link_index);
|
||||
if (tcp_client_stop_requested() != pdFALSE) {
|
||||
break;
|
||||
}
|
||||
if (tcp_client_delay_with_stop(delay_ms) == pdFALSE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
exit_task:
|
||||
netconn_thread_cleanup();
|
||||
app_on_network_task_exit(xTaskGetCurrentTaskHandle());
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void TcpCliTask_C1(void *argument)
|
||||
|
||||
+82
-13
@@ -11,7 +11,33 @@
|
||||
#include "debug_log.h"
|
||||
#include "route_msg.h"
|
||||
|
||||
static void tcp_server_worker(struct netconn *conn, uint8_t link_index)
|
||||
#define TCP_SERVER_ACCEPT_TIMEOUT_MS 100
|
||||
#define TCP_SERVER_STOP_POLL_MS 50u
|
||||
|
||||
static BaseType_t tcp_server_stop_requested(void)
|
||||
{
|
||||
return (app_network_task_stop_requested() != pdFALSE) ? pdTRUE : pdFALSE;
|
||||
}
|
||||
|
||||
static BaseType_t tcp_server_delay_with_stop(uint32_t delay_ms)
|
||||
{
|
||||
uint32_t remaining_ms = delay_ms;
|
||||
|
||||
while (remaining_ms > 0u) {
|
||||
uint32_t slice_ms = (remaining_ms > TCP_SERVER_STOP_POLL_MS) ? TCP_SERVER_STOP_POLL_MS : remaining_ms;
|
||||
|
||||
if (tcp_server_stop_requested() != pdFALSE) {
|
||||
return pdFALSE;
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(slice_ms));
|
||||
remaining_ms -= slice_ms;
|
||||
}
|
||||
|
||||
return (tcp_server_stop_requested() == pdFALSE) ? pdTRUE : pdFALSE;
|
||||
}
|
||||
|
||||
static err_t tcp_server_worker(struct netconn *conn, uint8_t link_index)
|
||||
{
|
||||
struct netbuf *buf;
|
||||
const device_config_t *cfg = config_get();
|
||||
@@ -19,37 +45,60 @@ static void tcp_server_worker(struct netconn *conn, uint8_t link_index)
|
||||
uint8_t src_endpoint = config_link_index_to_endpoint(link_index);
|
||||
err_t err;
|
||||
route_msg_t *tx_msg;
|
||||
route_send_result_t route_result;
|
||||
|
||||
netconn_set_recvtimeout(conn, 10);
|
||||
|
||||
for (;;) {
|
||||
if (tcp_server_stop_requested() != pdFALSE) {
|
||||
return ERR_CLSD;
|
||||
}
|
||||
|
||||
err = netconn_recv(conn, &buf);
|
||||
if (err == ERR_OK) {
|
||||
do {
|
||||
void *data;
|
||||
uint16_t len;
|
||||
netbuf_data(buf, &data, &len);
|
||||
(void)route_send(xTcpRxQueue,
|
||||
route_result = route_send(xTcpRxQueue,
|
||||
src_endpoint,
|
||||
uart_endpoint,
|
||||
(link_index == CONFIG_LINK_S1) ? ROUTE_CONN_S1 : ROUTE_CONN_S2,
|
||||
(const uint8_t *)data,
|
||||
len,
|
||||
pdMS_TO_TICKS(10));
|
||||
if (route_result != ROUTE_SEND_OK) {
|
||||
debug_log_printf("[SRV] idx=%u rx-route-fail rc=%s len=%u\r\n",
|
||||
(unsigned int)link_index,
|
||||
route_send_result_to_str(route_result),
|
||||
(unsigned int)len);
|
||||
netbuf_delete(buf);
|
||||
return ERR_CLSD;
|
||||
}
|
||||
} while (netbuf_next(buf) >= 0);
|
||||
netbuf_delete(buf);
|
||||
} else if (err != ERR_TIMEOUT) {
|
||||
} else if (err == ERR_TIMEOUT) {
|
||||
if (tcp_server_stop_requested() != pdFALSE) {
|
||||
return ERR_CLSD;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
while (xQueueReceive(xLinkTxQueues[link_index], &tx_msg, 0) == pdPASS) {
|
||||
if (tcp_server_stop_requested() != pdFALSE) {
|
||||
route_msg_free(tx_msg);
|
||||
return ERR_CLSD;
|
||||
}
|
||||
err = netconn_write(conn, tx_msg->data, tx_msg->len, NETCONN_COPY);
|
||||
route_msg_free(tx_msg);
|
||||
if (err != ERR_OK) {
|
||||
return;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void tcp_server_task(uint8_t link_index)
|
||||
@@ -58,37 +107,52 @@ static void tcp_server_task(uint8_t link_index)
|
||||
struct netconn *listener;
|
||||
struct netconn *newconn;
|
||||
|
||||
netconn_thread_init();
|
||||
|
||||
for (;;) {
|
||||
if (tcp_server_stop_requested() != pdFALSE) {
|
||||
break;
|
||||
}
|
||||
|
||||
while (g_netif_ready == pdFALSE) {
|
||||
if (tcp_server_stop_requested() != pdFALSE) {
|
||||
goto exit_task;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
cfg = config_get();
|
||||
debug_log_printf("[SRV] idx=%u hwm=%lu en=%u port=%u\r\n",
|
||||
(unsigned int)link_index,
|
||||
(unsigned long)uxTaskGetStackHighWaterMark(NULL),
|
||||
(unsigned int)cfg->links[link_index].enabled,
|
||||
(unsigned int)cfg->links[link_index].local_port);
|
||||
if (cfg->links[link_index].enabled == 0u) {
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
if (tcp_server_stop_requested() != pdFALSE) {
|
||||
break;
|
||||
}
|
||||
if (tcp_server_delay_with_stop(500u) == pdFALSE) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
listener = netconn_new(NETCONN_TCP);
|
||||
if (listener == NULL) {
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
if (tcp_server_delay_with_stop(500u) == pdFALSE) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
netconn_set_recvtimeout(listener, TCP_SERVER_ACCEPT_TIMEOUT_MS);
|
||||
|
||||
if (netconn_bind(listener, IP_ADDR_ANY, cfg->links[link_index].local_port) != ERR_OK ||
|
||||
netconn_listen(listener) != ERR_OK) {
|
||||
netconn_delete(listener);
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
if (tcp_server_delay_with_stop(500u) == pdFALSE) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (cfg->links[link_index].enabled == 0u) {
|
||||
if (tcp_server_stop_requested() != pdFALSE || cfg->links[link_index].enabled == 0u) {
|
||||
break;
|
||||
}
|
||||
if (netconn_accept(listener, &newconn) == ERR_OK) {
|
||||
@@ -101,6 +165,11 @@ static void tcp_server_task(uint8_t link_index)
|
||||
netconn_close(listener);
|
||||
netconn_delete(listener);
|
||||
}
|
||||
|
||||
exit_task:
|
||||
netconn_thread_cleanup();
|
||||
app_on_network_task_exit(xTaskGetCurrentTaskHandle());
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void TcpSrvTask_S1(void *argument)
|
||||
|
||||
+236
-46
@@ -32,10 +32,27 @@ typedef struct {
|
||||
volatile uint16_t tx_tail;
|
||||
volatile uint16_t tx_dma_len;
|
||||
volatile uint8_t tx_busy;
|
||||
volatile uint8_t tx_kick_fail_logged;
|
||||
} uart_channel_ctx_t;
|
||||
|
||||
static uart_channel_ctx_t g_channels[UART_CHANNEL_MAX];
|
||||
|
||||
const char *uart_trans_send_result_to_str(uart_trans_send_result_t result)
|
||||
{
|
||||
switch (result) {
|
||||
case UART_TRANS_SEND_OK:
|
||||
return "ok";
|
||||
case UART_TRANS_SEND_INVALID_INPUT:
|
||||
return "invalid";
|
||||
case UART_TRANS_SEND_RING_FULL:
|
||||
return "full";
|
||||
case UART_TRANS_SEND_KICK_FAILED:
|
||||
return "kick";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t ring_used(uint16_t head, uint16_t tail, uint16_t size)
|
||||
{
|
||||
return (head >= tail) ? (head - tail) : (size - tail + head);
|
||||
@@ -66,20 +83,21 @@ static void process_rx_snapshot(uart_channel_t channel)
|
||||
}
|
||||
}
|
||||
|
||||
static void kick_tx(uart_channel_t channel)
|
||||
static uart_trans_send_result_t kick_tx(uart_channel_t channel)
|
||||
{
|
||||
uart_channel_ctx_t *ctx = &g_channels[channel];
|
||||
uint16_t available;
|
||||
uint16_t chunk;
|
||||
uint16_t tail;
|
||||
uint16_t i;
|
||||
|
||||
if (ctx->tx_busy != 0u) {
|
||||
return;
|
||||
return UART_TRANS_SEND_OK;
|
||||
}
|
||||
|
||||
available = ring_used(ctx->tx_head, ctx->tx_tail, UART_TX_RING_BUFFER_SIZE);
|
||||
if (available == 0u) {
|
||||
return;
|
||||
return UART_TRANS_SEND_OK;
|
||||
}
|
||||
|
||||
chunk = available;
|
||||
@@ -87,16 +105,28 @@ static void kick_tx(uart_channel_t channel)
|
||||
chunk = UART_TX_DMA_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
tail = ctx->tx_tail;
|
||||
for (i = 0; i < chunk; ++i) {
|
||||
ctx->tx_dma_buffer[i] = ctx->tx_ring[ctx->tx_tail];
|
||||
ctx->tx_tail = (uint16_t)((ctx->tx_tail + 1u) % UART_TX_RING_BUFFER_SIZE);
|
||||
ctx->tx_dma_buffer[i] = ctx->tx_ring[tail];
|
||||
tail = (uint16_t)((tail + 1u) % UART_TX_RING_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
if (HAL_UART_Transmit_DMA(ctx->huart, ctx->tx_dma_buffer, chunk) != HAL_OK) {
|
||||
ctx->tx_dma_len = 0u;
|
||||
if (ctx->tx_kick_fail_logged == 0u) {
|
||||
debug_log_printf("[UART] kick-fail ch=%u len=%u\r\n",
|
||||
(unsigned int)channel,
|
||||
(unsigned int)chunk);
|
||||
ctx->tx_kick_fail_logged = 1u;
|
||||
}
|
||||
return UART_TRANS_SEND_KICK_FAILED;
|
||||
}
|
||||
|
||||
ctx->tx_tail = tail;
|
||||
ctx->tx_dma_len = chunk;
|
||||
ctx->tx_busy = 1u;
|
||||
if (HAL_UART_Transmit_DMA(ctx->huart, ctx->tx_dma_buffer, chunk) != HAL_OK) {
|
||||
ctx->tx_busy = 0u;
|
||||
}
|
||||
ctx->tx_kick_fail_logged = 0u;
|
||||
return UART_TRANS_SEND_OK;
|
||||
}
|
||||
|
||||
static uint16_t uart_ring_available(uart_channel_t channel)
|
||||
@@ -143,6 +173,7 @@ static void uart_route_raw_channel(uart_channel_t channel)
|
||||
uint16_t len;
|
||||
uint8_t uart_endpoint = (channel == UART_CHANNEL_U1) ? ENDPOINT_UART3 : ENDPOINT_UART2;
|
||||
uint32_t i;
|
||||
route_send_result_t route_result;
|
||||
|
||||
len = uart_ring_read(channel, buffer, sizeof(buffer));
|
||||
if (len == 0u) {
|
||||
@@ -154,76 +185,213 @@ static void uart_route_raw_channel(uart_channel_t channel)
|
||||
continue;
|
||||
}
|
||||
|
||||
(void)route_send(xLinkTxQueues[i],
|
||||
route_result = route_send(xLinkTxQueues[i],
|
||||
uart_endpoint,
|
||||
config_link_index_to_endpoint((uint8_t)i),
|
||||
(channel == UART_CHANNEL_U1) ? ROUTE_CONN_UART3 : ROUTE_CONN_UART2,
|
||||
buffer,
|
||||
len,
|
||||
pdMS_TO_TICKS(10));
|
||||
if (route_result != ROUTE_SEND_OK) {
|
||||
debug_log_printf("[UART] raw-route-fail idx=%u rc=%s len=%u\r\n",
|
||||
(unsigned int)i,
|
||||
route_send_result_to_str(route_result),
|
||||
(unsigned int)len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void uart_send_tcp_msg_to_uarts(route_msg_t *msg)
|
||||
static uart_trans_send_result_t uart_send_tcp_msg_chunk(route_msg_t *msg,
|
||||
uint16_t offset,
|
||||
uint16_t *accepted_len)
|
||||
{
|
||||
uint8_t frame[ROUTE_MSG_MAX_PAYLOAD + 6u];
|
||||
uint16_t frame_len = 0u;
|
||||
uint16_t remaining;
|
||||
uint16_t chunk_len;
|
||||
uint8_t uart_mask;
|
||||
uart_trans_send_result_t uart_result;
|
||||
|
||||
if ((msg->dst_mask & ENDPOINT_UART2) != 0u) {
|
||||
if (config_get()->mux_mode == MUX_MODE_FRAME) {
|
||||
if (uart_mux_encode_frame(msg->src_id, ENDPOINT_UART2, msg->data, msg->len, frame, &frame_len, sizeof(frame))) {
|
||||
(void)uart_trans_send_buffer(UART_CHANNEL_U0, frame, frame_len);
|
||||
}
|
||||
} else {
|
||||
(void)uart_trans_send_buffer(UART_CHANNEL_U0, msg->data, msg->len);
|
||||
}
|
||||
if (accepted_len == NULL || msg == NULL || offset >= msg->len) {
|
||||
return UART_TRANS_SEND_INVALID_INPUT;
|
||||
}
|
||||
|
||||
if ((msg->dst_mask & ENDPOINT_UART3) != 0u) {
|
||||
if (config_get()->mux_mode == MUX_MODE_FRAME) {
|
||||
if (uart_mux_encode_frame(msg->src_id, ENDPOINT_UART3, msg->data, msg->len, frame, &frame_len, sizeof(frame))) {
|
||||
(void)uart_trans_send_buffer(UART_CHANNEL_U1, frame, frame_len);
|
||||
*accepted_len = 0u;
|
||||
|
||||
uart_mask = (uint8_t)(msg->dst_mask & (ENDPOINT_UART2 | ENDPOINT_UART3));
|
||||
if ((msg->dst_mask != uart_mask) ||
|
||||
(uart_mask != ENDPOINT_UART2 && uart_mask != ENDPOINT_UART3)) {
|
||||
return UART_TRANS_SEND_INVALID_INPUT;
|
||||
}
|
||||
} else {
|
||||
(void)uart_trans_send_buffer(UART_CHANNEL_U1, msg->data, msg->len);
|
||||
|
||||
remaining = (uint16_t)(msg->len - offset);
|
||||
|
||||
if (uart_mask == ENDPOINT_UART2) {
|
||||
if (config_get()->mux_mode == MUX_MODE_FRAME) {
|
||||
chunk_len = remaining;
|
||||
if (chunk_len > (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u - 6u)) {
|
||||
chunk_len = (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u - 6u);
|
||||
}
|
||||
if (!uart_mux_encode_frame(msg->src_id, ENDPOINT_UART2, &msg->data[offset], chunk_len, frame, &frame_len, sizeof(frame))) {
|
||||
return UART_TRANS_SEND_INVALID_INPUT;
|
||||
}
|
||||
uart_result = uart_trans_send_buffer(UART_CHANNEL_U0, frame, frame_len);
|
||||
if (uart_result != UART_TRANS_SEND_OK) {
|
||||
return uart_result;
|
||||
}
|
||||
*accepted_len = chunk_len;
|
||||
return UART_TRANS_SEND_OK;
|
||||
}
|
||||
|
||||
chunk_len = remaining;
|
||||
if (chunk_len > (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u)) {
|
||||
chunk_len = (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u);
|
||||
}
|
||||
uart_result = uart_trans_send_buffer(UART_CHANNEL_U0, &msg->data[offset], chunk_len);
|
||||
if (uart_result != UART_TRANS_SEND_OK) {
|
||||
return uart_result;
|
||||
}
|
||||
*accepted_len = chunk_len;
|
||||
return UART_TRANS_SEND_OK;
|
||||
}
|
||||
|
||||
if (config_get()->mux_mode == MUX_MODE_FRAME) {
|
||||
chunk_len = remaining;
|
||||
if (chunk_len > (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u - 6u)) {
|
||||
chunk_len = (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u - 6u);
|
||||
}
|
||||
if (!uart_mux_encode_frame(msg->src_id, ENDPOINT_UART3, &msg->data[offset], chunk_len, frame, &frame_len, sizeof(frame))) {
|
||||
return UART_TRANS_SEND_INVALID_INPUT;
|
||||
}
|
||||
uart_result = uart_trans_send_buffer(UART_CHANNEL_U1, frame, frame_len);
|
||||
if (uart_result != UART_TRANS_SEND_OK) {
|
||||
return uart_result;
|
||||
}
|
||||
*accepted_len = chunk_len;
|
||||
return UART_TRANS_SEND_OK;
|
||||
}
|
||||
|
||||
chunk_len = remaining;
|
||||
if (chunk_len > (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u)) {
|
||||
chunk_len = (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u);
|
||||
}
|
||||
uart_result = uart_trans_send_buffer(UART_CHANNEL_U1, &msg->data[offset], chunk_len);
|
||||
if (uart_result != UART_TRANS_SEND_OK) {
|
||||
return uart_result;
|
||||
}
|
||||
*accepted_len = chunk_len;
|
||||
return UART_TRANS_SEND_OK;
|
||||
}
|
||||
|
||||
static void uart_try_advance_pending_tcp_msg(route_msg_t **pending_tcp_msg,
|
||||
uint16_t *pending_tcp_offset,
|
||||
uart_trans_send_result_t *pending_tcp_result)
|
||||
{
|
||||
route_msg_t *msg;
|
||||
uart_trans_send_result_t uart_result;
|
||||
uint16_t accepted_len;
|
||||
|
||||
if (pending_tcp_msg == NULL || pending_tcp_offset == NULL || pending_tcp_result == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
msg = *pending_tcp_msg;
|
||||
if (msg == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
accepted_len = 0u;
|
||||
uart_result = uart_send_tcp_msg_chunk(msg, *pending_tcp_offset, &accepted_len);
|
||||
if (uart_result != UART_TRANS_SEND_OK) {
|
||||
if (uart_result != *pending_tcp_result) {
|
||||
debug_log_printf("[UART] tcp-pend src=0x%02X dst=0x%02X off=%u rc=%s\r\n",
|
||||
(unsigned int)msg->src_id,
|
||||
(unsigned int)msg->dst_mask,
|
||||
(unsigned int)(*pending_tcp_offset),
|
||||
uart_trans_send_result_to_str(uart_result));
|
||||
*pending_tcp_result = uart_result;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
*pending_tcp_offset = (uint16_t)(*pending_tcp_offset + accepted_len);
|
||||
*pending_tcp_result = UART_TRANS_SEND_OK;
|
||||
if (*pending_tcp_offset >= msg->len) {
|
||||
route_msg_free(msg);
|
||||
*pending_tcp_msg = NULL;
|
||||
*pending_tcp_offset = 0u;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void uart_route_mux_frame(uart_channel_t source_channel, const uart_mux_frame_t *frame)
|
||||
{
|
||||
const device_config_t *cfg = config_get();
|
||||
uint32_t i;
|
||||
uint8_t endpoint;
|
||||
uint8_t source_conn = (source_channel == UART_CHANNEL_U1) ? ROUTE_CONN_UART3 : ROUTE_CONN_UART2;
|
||||
uint8_t out_frame[ROUTE_MSG_MAX_PAYLOAD + 6u];
|
||||
uint16_t out_frame_len = 0u;
|
||||
route_send_result_t route_result;
|
||||
uart_trans_send_result_t uart_result;
|
||||
|
||||
if (frame->dst_mask == 0u) {
|
||||
(void)route_send(xConfigQueue,
|
||||
route_result = route_send(xConfigQueue,
|
||||
frame->src_id,
|
||||
0u,
|
||||
source_conn,
|
||||
frame->payload,
|
||||
frame->payload_len,
|
||||
pdMS_TO_TICKS(10));
|
||||
if (route_result != ROUTE_SEND_OK) {
|
||||
debug_log_printf("[UART] mux-cfg-fail rc=%s len=%u\r\n",
|
||||
route_send_result_to_str(route_result),
|
||||
(unsigned int)frame->payload_len);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < CONFIG_LINK_COUNT; ++i) {
|
||||
uint8_t endpoint = config_link_index_to_endpoint((uint8_t)i);
|
||||
if (cfg->links[i].enabled == 0u) {
|
||||
continue;
|
||||
}
|
||||
endpoint = config_link_index_to_endpoint((uint8_t)i);
|
||||
if ((frame->dst_mask & endpoint) != 0u) {
|
||||
(void)route_send(xLinkTxQueues[i], frame->src_id, endpoint, source_conn, frame->payload, frame->payload_len, pdMS_TO_TICKS(10));
|
||||
route_result = route_send(xLinkTxQueues[i], frame->src_id, endpoint, source_conn, frame->payload, frame->payload_len, pdMS_TO_TICKS(10));
|
||||
if (route_result != ROUTE_SEND_OK) {
|
||||
debug_log_printf("[UART] mux-route-fail idx=%u rc=%s len=%u\r\n",
|
||||
(unsigned int)i,
|
||||
route_send_result_to_str(route_result),
|
||||
(unsigned int)frame->payload_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((frame->dst_mask & ENDPOINT_UART2) != 0u && source_channel != UART_CHANNEL_U0) {
|
||||
if (uart_mux_encode_frame(frame->src_id, ENDPOINT_UART2, frame->payload, frame->payload_len, out_frame, &out_frame_len, sizeof(out_frame))) {
|
||||
(void)uart_trans_send_buffer(UART_CHANNEL_U0, out_frame, out_frame_len);
|
||||
uart_result = uart_trans_send_buffer(UART_CHANNEL_U0, out_frame, out_frame_len);
|
||||
if (uart_result != UART_TRANS_SEND_OK) {
|
||||
debug_log_printf("[UART] mux-u0-tx-fail rc=%s len=%u\r\n",
|
||||
uart_trans_send_result_to_str(uart_result),
|
||||
(unsigned int)out_frame_len);
|
||||
}
|
||||
} else {
|
||||
debug_log_printf("[UART] mux-u0-enc-fail len=%u\r\n", (unsigned int)frame->payload_len);
|
||||
}
|
||||
}
|
||||
|
||||
if ((frame->dst_mask & ENDPOINT_UART3) != 0u && source_channel != UART_CHANNEL_U1) {
|
||||
if (uart_mux_encode_frame(frame->src_id, ENDPOINT_UART3, frame->payload, frame->payload_len, out_frame, &out_frame_len, sizeof(out_frame))) {
|
||||
(void)uart_trans_send_buffer(UART_CHANNEL_U1, out_frame, out_frame_len);
|
||||
uart_result = uart_trans_send_buffer(UART_CHANNEL_U1, out_frame, out_frame_len);
|
||||
if (uart_result != UART_TRANS_SEND_OK) {
|
||||
debug_log_printf("[UART] mux-u1-tx-fail rc=%s len=%u\r\n",
|
||||
uart_trans_send_result_to_str(uart_result),
|
||||
(unsigned int)out_frame_len);
|
||||
}
|
||||
} else {
|
||||
debug_log_printf("[UART] mux-u1-enc-fail len=%u\r\n", (unsigned int)frame->payload_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -237,13 +405,6 @@ int uart_trans_init(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uart_trans_config(uint8_t uart_index, uint32_t baudrate)
|
||||
{
|
||||
UART_HandleTypeDef *huart = (uart_index == LINK_UART_U1) ? &huart3 : &huart2;
|
||||
huart->Init.BaudRate = baudrate;
|
||||
return (HAL_UART_Init(huart) == HAL_OK) ? 0 : -1;
|
||||
}
|
||||
|
||||
int uart_trans_start_all(void)
|
||||
{
|
||||
uint32_t i;
|
||||
@@ -269,22 +430,44 @@ int uart_trans_start_all(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool uart_trans_send_buffer(uart_channel_t channel, const uint8_t *data, uint16_t len)
|
||||
uart_trans_send_result_t uart_trans_send_buffer(uart_channel_t channel, const uint8_t *data, uint16_t len)
|
||||
{
|
||||
uart_channel_ctx_t *ctx = &g_channels[channel];
|
||||
uart_channel_ctx_t *ctx;
|
||||
uart_trans_send_result_t uart_result;
|
||||
uint16_t original_head;
|
||||
uint16_t written = 0u;
|
||||
|
||||
if (data == NULL || len == 0u) {
|
||||
return false;
|
||||
if (channel >= UART_CHANNEL_MAX || data == NULL || len == 0u || len >= UART_TX_RING_BUFFER_SIZE) {
|
||||
return UART_TRANS_SEND_INVALID_INPUT;
|
||||
}
|
||||
|
||||
while (written < len && ring_free(ctx->tx_head, ctx->tx_tail, UART_TX_RING_BUFFER_SIZE) > 0u) {
|
||||
ctx = &g_channels[channel];
|
||||
if (ctx->huart == NULL) {
|
||||
return UART_TRANS_SEND_INVALID_INPUT;
|
||||
}
|
||||
|
||||
taskENTER_CRITICAL();
|
||||
original_head = ctx->tx_head;
|
||||
if (ring_free(ctx->tx_head, ctx->tx_tail, UART_TX_RING_BUFFER_SIZE) < len) {
|
||||
taskEXIT_CRITICAL();
|
||||
return UART_TRANS_SEND_RING_FULL;
|
||||
}
|
||||
|
||||
while (written < len) {
|
||||
ctx->tx_ring[ctx->tx_head] = data[written++];
|
||||
ctx->tx_head = (uint16_t)((ctx->tx_head + 1u) % UART_TX_RING_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
kick_tx(channel);
|
||||
return (written == len);
|
||||
uart_result = kick_tx(channel);
|
||||
if (uart_result != UART_TRANS_SEND_OK) {
|
||||
ctx->tx_head = original_head;
|
||||
taskEXIT_CRITICAL();
|
||||
return uart_result;
|
||||
}
|
||||
|
||||
taskEXIT_CRITICAL();
|
||||
|
||||
return UART_TRANS_SEND_OK;
|
||||
}
|
||||
|
||||
void uart_trans_notify_rx_from_isr(uart_channel_t channel, BaseType_t *xHigherPriorityTaskWoken)
|
||||
@@ -408,8 +591,11 @@ void UartRxTask(void *argument)
|
||||
uint32_t notify_value;
|
||||
BaseType_t notified;
|
||||
route_msg_t *msg;
|
||||
route_msg_t *pending_tcp_msg = NULL;
|
||||
uint16_t pending_tcp_offset = 0u;
|
||||
uart_mux_frame_t frame;
|
||||
const device_config_t *cfg;
|
||||
uart_trans_send_result_t pending_tcp_result = UART_TRANS_SEND_OK;
|
||||
|
||||
(void)argument;
|
||||
if (uart_trans_start_all() != 0) {
|
||||
@@ -435,9 +621,13 @@ void UartRxTask(void *argument)
|
||||
g_channels[UART_CHANNEL_U1].tx_busy = 0u;
|
||||
}
|
||||
|
||||
while (xQueueReceive(xTcpRxQueue, &msg, 0) == pdPASS) {
|
||||
uart_send_tcp_msg_to_uarts(msg);
|
||||
route_msg_free(msg);
|
||||
uart_try_advance_pending_tcp_msg(&pending_tcp_msg, &pending_tcp_offset, &pending_tcp_result);
|
||||
|
||||
while (pending_tcp_msg == NULL && xQueueReceive(xTcpRxQueue, &msg, 0) == pdPASS) {
|
||||
pending_tcp_msg = msg;
|
||||
pending_tcp_offset = 0u;
|
||||
pending_tcp_result = UART_TRANS_SEND_OK;
|
||||
uart_try_advance_pending_tcp_msg(&pending_tcp_msg, &pending_tcp_offset, &pending_tcp_result);
|
||||
}
|
||||
|
||||
cfg = config_get();
|
||||
|
||||
+9
-2
@@ -16,6 +16,13 @@ typedef enum {
|
||||
UART_CHANNEL_MAX
|
||||
} uart_channel_t;
|
||||
|
||||
typedef enum {
|
||||
UART_TRANS_SEND_OK = 0,
|
||||
UART_TRANS_SEND_INVALID_INPUT,
|
||||
UART_TRANS_SEND_RING_FULL,
|
||||
UART_TRANS_SEND_KICK_FAILED
|
||||
} uart_trans_send_result_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t src_id;
|
||||
uint8_t dst_mask;
|
||||
@@ -29,9 +36,9 @@ typedef struct {
|
||||
#define UART_TX_RING_BUFFER_SIZE 256u
|
||||
|
||||
int uart_trans_init(void);
|
||||
int uart_trans_config(uint8_t uart_index, uint32_t baudrate);
|
||||
int uart_trans_start_all(void);
|
||||
bool uart_trans_send_buffer(uart_channel_t channel, const uint8_t *data, uint16_t len);
|
||||
const char *uart_trans_send_result_to_str(uart_trans_send_result_t result);
|
||||
uart_trans_send_result_t uart_trans_send_buffer(uart_channel_t channel, const uint8_t *data, uint16_t len);
|
||||
void uart_trans_notify_rx_from_isr(uart_channel_t channel, BaseType_t *xHigherPriorityTaskWoken);
|
||||
void uart_trans_tx_cplt_handler(uart_channel_t channel);
|
||||
void UartRxTask(void *argument);
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
# CH390 / lwIP 固定次数 ping 失败问题修复复盘
|
||||
|
||||
## 1. 问题现象
|
||||
|
||||
在 TCP2UART 固件运行后,设备初期可以正常 ARP 和 ping,但连续 ping 一段时间后不再响应。
|
||||
|
||||
典型现象:
|
||||
|
||||
- 设备 IP:`192.168.31.100`
|
||||
- 设备 MAC:`02:00:00:00:00:01`
|
||||
- 对端/网关 IP:`192.168.31.1`
|
||||
- 对端/网关 MAC:`00:e0:4c:28:1e:60`
|
||||
- 失败后设备仍持续发送 TCP SYN/RST 或 client timeout 相关流量,说明 TX、任务调度和应用层并未整体死机。
|
||||
- 失败后对端继续向设备 MAC 发送 ICMP/ARP,但设备不再回复。
|
||||
|
||||
关键抓包:
|
||||
|
||||
- `WiresharkLog/04290150.pcapng`
|
||||
- `seq=1884..1891` 共 8 次 ping reply 正常。
|
||||
- 第 9 次 `seq=1892` 开始无 reply。
|
||||
- `WiresharkLog/04290206.pcapng`
|
||||
- 曾把 `PBUF_POOL_SIZE` / `MEMP_NUM_TCPIP_MSG_INPKT` 从 8 临时扩大到 16。
|
||||
- 成功 ping 从 8 次变为 `seq=1900..1915` 共 16 次。
|
||||
- 第 17 次 `seq=1916` 开始无 reply。
|
||||
|
||||
这个“成功次数随池大小移动”的现象证明:问题不是 CH390 随机丢包,也不是 PHY/TX 死掉,而是每次成功处理 ping 后都有某个 pbuf 引用没有释放,最终耗尽 `PBUF_POOL`。
|
||||
|
||||
## 2. 排查过程中的重要结论
|
||||
|
||||
### 2.1 CH390 RX 读包路径曾存在风险,但不是最终根因
|
||||
|
||||
早期排查时发现 CH390 RX 路径与参考驱动存在若干不一致,已修正:
|
||||
|
||||
- `ch390_receive_packet()` 按参考序列读取 RX ready:先读 `MRCMDX` dummy,再读 `MRCMDX1`。
|
||||
- 校验 RX header 的 `Head` 字节必须为 `CH390_PKT_RDY`。
|
||||
- CH390 RX SRAM 中的 `rx_len` 包含 Ethernet FCS,交给 lwIP 前需要减去 4 字节。
|
||||
- `ch390_rx_reset()` 显式写 `MPTRCR_RST_RX` 复位 RX memory pointer。
|
||||
|
||||
这些修正确保 CH390 RX FIFO 读包更接近参考实现,但无法解释“固定 8 次/16 次后失败”。
|
||||
|
||||
### 2.2 扩大 lwIP 池只能延迟问题
|
||||
|
||||
曾临时将如下配置从 8 提到 16:
|
||||
|
||||
```c
|
||||
#define PBUF_POOL_SIZE 16
|
||||
#define MEMP_NUM_PBUF 16
|
||||
#define MEMP_NUM_TCPIP_MSG_INPKT 16
|
||||
```
|
||||
|
||||
结果成功 ping 次数也从 8 变成 16。这说明扩大池子只是延迟耗尽,不能作为根修复。
|
||||
|
||||
最终已恢复为 8:
|
||||
|
||||
```c
|
||||
#define PBUF_POOL_SIZE 8
|
||||
#define MEMP_NUM_PBUF 8
|
||||
#define MEMP_NUM_TCPIP_MSG_INPKT 8
|
||||
```
|
||||
|
||||
### 2.3 `tcpip_input()` 异步队列不是最终根因
|
||||
|
||||
项目启用了 lwIP core locking。为避免每个 RX 包占用 `MEMP_TCPIP_MSG_INPKT`,配置已改为同步输入:
|
||||
|
||||
```c
|
||||
#define LWIP_TCPIP_CORE_LOCKING 1
|
||||
#define LWIP_TCPIP_CORE_LOCKING_INPUT 1
|
||||
```
|
||||
|
||||
这样 `tcpip_input()` 会在 core lock 下同步调用 `ethernet_input()`,不再通过 `TCPIP_MSG_INPKT` 邮箱异步排队。
|
||||
|
||||
但用户后续验证仍然固定 8 次后停止,且每次成功 ping 都已经有 reply,因此说明 RX 包确实已经进入 ICMP 处理路径,问题更可能是 reply 输出路径增加了 pbuf 引用但未释放。
|
||||
|
||||
## 3. 最终根因
|
||||
|
||||
最终根因位于:
|
||||
|
||||
```text
|
||||
Drivers/LwIP/src/netif/ethernet.c
|
||||
```
|
||||
|
||||
原 `ethernet_output()` 实现:
|
||||
|
||||
```c
|
||||
q = pbuf_alloc(PBUF_RAW_TX, SIZEOF_ETH_HDR, PBUF_RAM);
|
||||
if (q == NULL) {
|
||||
LINK_STATS_INC(link.memerr);
|
||||
LINK_STATS_INC(link.drop);
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
pbuf_chain(q, p);
|
||||
|
||||
ethhdr = (struct eth_hdr *)q->payload;
|
||||
SMEMCPY(ðhdr->dest, dst, sizeof(struct eth_addr));
|
||||
SMEMCPY(ðhdr->src, src, sizeof(struct eth_addr));
|
||||
ethhdr->type = lwip_htons(eth_type);
|
||||
|
||||
return netif->linkoutput(netif, q);
|
||||
```
|
||||
|
||||
问题在 `pbuf_chain(q, p)`。
|
||||
|
||||
lwIP 的 `pbuf_chain()` 会执行:
|
||||
|
||||
```c
|
||||
pbuf_ref(t);
|
||||
```
|
||||
|
||||
也就是给被挂接的原始 pbuf `p` 引用计数加 1。
|
||||
|
||||
ICMP echo reply 路径会复用 RX pbuf:
|
||||
|
||||
```text
|
||||
ethernetif_poll()
|
||||
-> tcpip_input()
|
||||
-> ethernet_input()
|
||||
-> ip4_input()
|
||||
-> icmp_input()
|
||||
-> ip4_output_if()
|
||||
-> etharp_output()
|
||||
-> ethernet_output()
|
||||
```
|
||||
|
||||
`icmp_input()` 末尾本身会 `pbuf_free(p)`,这部分是正确的。但在原实现中,`ethernet_output()` 通过 `pbuf_chain(q, p)` 给 `p` 额外加了一次引用,却没有在 `linkoutput()` 返回后释放临时 header pbuf `q`。
|
||||
|
||||
因此每次 ping 的引用计数变化是:
|
||||
|
||||
```text
|
||||
RX pbuf 初始 ref = 1
|
||||
pbuf_chain(q, p) 后 ref = 2
|
||||
icmp_input() 末尾 pbuf_free(p) 后 ref = 1
|
||||
=> p 永远没有回到 0,PBUF_POOL 泄漏 1 个
|
||||
```
|
||||
|
||||
所以:
|
||||
|
||||
- `PBUF_POOL_SIZE=8` 时,8 次 ping reply 后耗尽。
|
||||
- 临时扩大到 16 时,16 次 ping reply 后耗尽。
|
||||
|
||||
## 4. 修复方案
|
||||
|
||||
修复 `ethernet_output()`,在同步 `linkoutput()` 完成后释放临时 header pbuf 链:
|
||||
|
||||
```c
|
||||
err_t ethernet_output(struct netif *netif,
|
||||
struct pbuf *p,
|
||||
const struct eth_addr *src,
|
||||
const struct eth_addr *dst,
|
||||
u16_t eth_type)
|
||||
{
|
||||
struct pbuf *q;
|
||||
struct eth_hdr *ethhdr;
|
||||
err_t err;
|
||||
|
||||
LWIP_ASSERT("netif != NULL", netif != NULL);
|
||||
LWIP_ASSERT("p != NULL", p != NULL);
|
||||
LWIP_ASSERT("src != NULL", src != NULL);
|
||||
LWIP_ASSERT("dst != NULL", dst != NULL);
|
||||
|
||||
q = pbuf_alloc(PBUF_RAW_TX, SIZEOF_ETH_HDR, PBUF_RAM);
|
||||
if (q == NULL) {
|
||||
LINK_STATS_INC(link.memerr);
|
||||
LINK_STATS_INC(link.drop);
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
pbuf_chain(q, p);
|
||||
|
||||
ethhdr = (struct eth_hdr *)q->payload;
|
||||
SMEMCPY(ðhdr->dest, dst, sizeof(struct eth_addr));
|
||||
SMEMCPY(ðhdr->src, src, sizeof(struct eth_addr));
|
||||
ethhdr->type = lwip_htons(eth_type);
|
||||
|
||||
err = netif->linkoutput(netif, q);
|
||||
pbuf_free(q);
|
||||
return err;
|
||||
}
|
||||
```
|
||||
|
||||
为什么这里可以释放 `q`:
|
||||
|
||||
- 本项目 `low_level_output()` 是同步发送。
|
||||
- 它会立即遍历 pbuf 链,把数据复制到 `s_tx_buffer`。
|
||||
- 随后调用 `ch390_runtime_send_packet()` 把连续 buffer 发给 CH390。
|
||||
- `low_level_output()` 返回后不再持有 pbuf 指针。
|
||||
|
||||
因此 `ethernet_output()` 在 `linkoutput()` 返回后释放 `q` 是正确的。
|
||||
|
||||
`pbuf_free(q)` 会同时:
|
||||
|
||||
- 释放临时 Ethernet header pbuf `q`;
|
||||
- 解除 `pbuf_chain()` 对原始 RX pbuf `p` 增加的引用;
|
||||
- 之后 `icmp_input()` 末尾的 `pbuf_free(p)` 可以真正把 RX pbuf 归还 `PBUF_POOL`。
|
||||
|
||||
## 5. 不要做的错误修复
|
||||
|
||||
### 5.1 不要在 `netif->input()` 成功后手动释放 pbuf
|
||||
|
||||
驱动层当前逻辑是正确的:
|
||||
|
||||
```c
|
||||
input_err = ch390_netif.input(p, &ch390_netif);
|
||||
if (input_err != ERR_OK) {
|
||||
pbuf_free(p);
|
||||
}
|
||||
```
|
||||
|
||||
`netif->input()` 返回 `ERR_OK` 时,pbuf ownership 已经交给 lwIP。此时驱动不能再 `pbuf_free(p)`,否则会造成 double-free 或 use-after-free。
|
||||
|
||||
### 5.2 不要只扩大 `PBUF_POOL_SIZE`
|
||||
|
||||
扩大池子只会让失败次数从 8 变 16、32……不会修复泄漏。
|
||||
|
||||
### 5.3 不要继续优先怀疑 CH390 PHY/TX
|
||||
|
||||
抓包中失败后设备仍持续发送 TCP SYN/RST,说明 TX 和任务仍活着。固定次数失败更符合 pbuf 引用泄漏。
|
||||
|
||||
## 6. 验证结果
|
||||
|
||||
修复后 Keil 构建通过:
|
||||
|
||||
```text
|
||||
"TCP2UART\TCP2UART.axf" - 0 Error(s), 0 Warning(s).
|
||||
Program Size: Code=93376 RO-data=2768 RW-data=456 ZI-data=56032
|
||||
```
|
||||
|
||||
用户烧录验证后确认问题已修复。
|
||||
|
||||
## 7. 后续排查建议
|
||||
|
||||
如后续再次出现固定次数网络停止,优先检查:
|
||||
|
||||
1. 是否存在 `pbuf_chain()` / `pbuf_ref()` 后没有配对 `pbuf_free()` 的路径。
|
||||
2. 是否有 ARP pending queue 长时间持有 pbuf。
|
||||
3. 是否有 TCP `recvmbox` / 应用桥接队列背压长期持有 pbuf。
|
||||
4. 是否有人在 `netif->input()` 成功后错误释放 pbuf,导致内存破坏。
|
||||
|
||||
推荐排查点:
|
||||
|
||||
- `Drivers/LwIP/src/netif/ethernet.c`
|
||||
- `Drivers/LwIP/src/core/ipv4/icmp.c`
|
||||
- `Drivers/LwIP/src/core/ipv4/etharp.c`
|
||||
- `Drivers/LwIP/src/core/pbuf.c`
|
||||
- `Drivers/LwIP/src/netif/ethernetif.c`
|
||||
@@ -44,6 +44,7 @@
|
||||
|
||||
/* USER CODE BEGIN Includes */
|
||||
/* Section where include file can be added */
|
||||
#include "debug_log.h"
|
||||
/* USER CODE END Includes */
|
||||
|
||||
/* Ensure definitions are only used by the compiler, and not by the assembler. */
|
||||
@@ -65,7 +66,7 @@
|
||||
#define configTICK_RATE_HZ ((TickType_t)1000)
|
||||
#define configMAX_PRIORITIES ( 7 )
|
||||
#define configMINIMAL_STACK_SIZE ((uint16_t)128)
|
||||
#define configTOTAL_HEAP_SIZE ((size_t)15360)
|
||||
#define configTOTAL_HEAP_SIZE ((size_t)21760)
|
||||
#define configMAX_TASK_NAME_LEN ( 16 )
|
||||
#define configUSE_TRACE_FACILITY 1
|
||||
#define configUSE_16_BIT_TICKS 0
|
||||
@@ -86,6 +87,8 @@
|
||||
#define configTIMER_TASK_PRIORITY ( 2 )
|
||||
#define configTIMER_QUEUE_LENGTH 10
|
||||
#define configTIMER_TASK_STACK_DEPTH 256
|
||||
#define configCHECK_FOR_STACK_OVERFLOW 2
|
||||
#define configUSE_MALLOC_FAILED_HOOK 1
|
||||
|
||||
/* Set the following definitions to 1 to include the API function, or zero
|
||||
to exclude the API function. */
|
||||
@@ -137,7 +140,7 @@ See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
|
||||
/* Normal assert() semantics without relying on the provision of an assert.h
|
||||
header file. */
|
||||
/* USER CODE BEGIN 1 */
|
||||
#define configASSERT( x ) if ((x) == 0) {taskDISABLE_INTERRUPTS(); for( ;; );}
|
||||
#define configASSERT( x ) do { if ((x) == 0) { debug_log_fault_context("config-assert", __FILE__, __LINE__); taskDISABLE_INTERRUPTS(); for( ;; ) { } } } while (0)
|
||||
/* USER CODE END 1 */
|
||||
|
||||
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
|
||||
@@ -164,13 +167,13 @@ standard names. */
|
||||
|
||||
/* Application task stack sizes (in words) */
|
||||
#define TASK_STACK_TCPIP 512
|
||||
#define TASK_STACK_NET_POLL 384
|
||||
#define TASK_STACK_TCP_SERVER 384
|
||||
#define TASK_STACK_TCP_CLIENT 384
|
||||
#define TASK_STACK_UART_RX 256
|
||||
#define TASK_STACK_NET_POLL 512
|
||||
#define TASK_STACK_TCP_SERVER 512
|
||||
#define TASK_STACK_TCP_CLIENT 512
|
||||
#define TASK_STACK_UART_RX 384
|
||||
#define TASK_STACK_ROUTE 512
|
||||
#define TASK_STACK_CONFIG 256
|
||||
#define TASK_STACK_DEFAULT 128
|
||||
#define TASK_STACK_CONFIG 384
|
||||
#define TASK_STACK_DEFAULT 192
|
||||
|
||||
/* Route message pool for zero-copy inter-task communication */
|
||||
#define ROUTE_MSG_POOL_SIZE 8
|
||||
|
||||
@@ -17,6 +17,7 @@ void debug_log_printf(const char *fmt, ...);
|
||||
void debug_log_boot(const char *tag);
|
||||
void debug_log_fault(const char *tag);
|
||||
void debug_log_runtime_snapshot(void);
|
||||
void debug_log_fault_context(const char *tag, const char *file, int line);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ extern IWDG_HandleTypeDef hiwdg;
|
||||
|
||||
/* USER CODE END Private defines */
|
||||
|
||||
void MX_IWDG_Init(void);
|
||||
HAL_StatusTypeDef MX_IWDG_Init(void);
|
||||
|
||||
/* USER CODE BEGIN Prototypes */
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ void MX_USART2_UART_Init(void);
|
||||
void MX_USART3_UART_Init(void);
|
||||
|
||||
/* USER CODE BEGIN Prototypes */
|
||||
void USART_SetConfiguredBaudrates(uint32_t usart2_baudrate, uint32_t usart3_baudrate);
|
||||
|
||||
/* USER CODE END Prototypes */
|
||||
|
||||
|
||||
+14
-1
@@ -45,7 +45,7 @@ void debug_log_write(const char *msg)
|
||||
|
||||
void debug_log_printf(const char *fmt, ...)
|
||||
{
|
||||
char buffer[128];
|
||||
char buffer[256];
|
||||
va_list args;
|
||||
int len;
|
||||
|
||||
@@ -75,6 +75,19 @@ void debug_log_fault(const char *tag)
|
||||
debug_log_printf("[FAULT] %s\r\n", (tag != NULL) ? tag : "(null)");
|
||||
}
|
||||
|
||||
void debug_log_fault_context(const char *tag, const char *file, int line)
|
||||
{
|
||||
debug_log_printf("[FAULT] %s file=%s line=%d free=%lu min=%lu seq=%lu drop=%lu last_drop=%lu\r\n",
|
||||
(tag != NULL) ? tag : "(null)",
|
||||
(file != NULL) ? file : "(null)",
|
||||
line,
|
||||
(unsigned long)xPortGetFreeHeapSize(),
|
||||
(unsigned long)xPortGetMinimumEverFreeHeapSize(),
|
||||
(unsigned long)g_rtt_log_seq,
|
||||
(unsigned long)g_rtt_log_drop_count,
|
||||
(unsigned long)g_rtt_log_last_drop_seq);
|
||||
}
|
||||
|
||||
void debug_log_runtime_snapshot(void)
|
||||
{
|
||||
UBaseType_t default_hwm;
|
||||
|
||||
+101
-21
@@ -36,48 +36,77 @@ static TaskHandle_t xTcpSrvTaskS2Handle = NULL;
|
||||
static TaskHandle_t xTcpCliTaskC1Handle = NULL;
|
||||
static TaskHandle_t xTcpCliTaskC2Handle = NULL;
|
||||
static TaskHandle_t xDefaultTaskHandle = NULL;
|
||||
static BaseType_t xNetworkTasksStarted = pdFALSE;
|
||||
static volatile BaseType_t xNetworkTaskStopRequested = pdFALSE;
|
||||
static volatile BaseType_t xNetworkRestartRequested = pdFALSE;
|
||||
|
||||
void app_start_network_tasks(void)
|
||||
{
|
||||
#if !DIAG_TASK_ISOLATION
|
||||
BaseType_t rc;
|
||||
const device_config_t *cfg;
|
||||
|
||||
if (xTcpSrvTaskS1Handle != NULL) {
|
||||
if (xNetworkTasksStarted != pdFALSE) {
|
||||
debug_log_write("[NET] start-network-tasks already\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (xNetworkTaskStopRequested != pdFALSE) {
|
||||
debug_log_write("[NET] start-network-tasks stop-pending\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
cfg = config_get();
|
||||
|
||||
debug_log_printf("[NET] start-network-tasks enter free=%lu min=%lu\r\n",
|
||||
(unsigned long)xPortGetFreeHeapSize(),
|
||||
(unsigned long)xPortGetMinimumEverFreeHeapSize());
|
||||
|
||||
if (cfg->links[CONFIG_LINK_S1].enabled != 0u) {
|
||||
rc = xTaskCreate(TcpSrvTask_S1, "TcpSrvS1", TASK_STACK_TCP_SERVER, NULL, TASK_PRIORITY_TCP_SERVER, &xTcpSrvTaskS1Handle);
|
||||
debug_log_printf("[NET] create TcpSrvS1 rc=%ld free=%lu min=%lu\r\n",
|
||||
(long)rc,
|
||||
(unsigned long)xPortGetFreeHeapSize(),
|
||||
(unsigned long)xPortGetMinimumEverFreeHeapSize());
|
||||
configASSERT(rc == pdPASS);
|
||||
} else {
|
||||
debug_log_write("[NET] skip TcpSrvS1 en=0\r\n");
|
||||
}
|
||||
|
||||
if (cfg->links[CONFIG_LINK_S2].enabled != 0u) {
|
||||
rc = xTaskCreate(TcpSrvTask_S2, "TcpSrvS2", TASK_STACK_TCP_SERVER, NULL, TASK_PRIORITY_TCP_SERVER, &xTcpSrvTaskS2Handle);
|
||||
debug_log_printf("[NET] create TcpSrvS2 rc=%ld free=%lu min=%lu\r\n",
|
||||
(long)rc,
|
||||
(unsigned long)xPortGetFreeHeapSize(),
|
||||
(unsigned long)xPortGetMinimumEverFreeHeapSize());
|
||||
configASSERT(rc == pdPASS);
|
||||
} else {
|
||||
debug_log_write("[NET] skip TcpSrvS2 en=0\r\n");
|
||||
}
|
||||
|
||||
if (cfg->links[CONFIG_LINK_C1].enabled != 0u) {
|
||||
rc = xTaskCreate(TcpCliTask_C1, "TcpCliC1", TASK_STACK_TCP_CLIENT, NULL, TASK_PRIORITY_TCP_CLIENT, &xTcpCliTaskC1Handle);
|
||||
debug_log_printf("[NET] create TcpCliC1 rc=%ld free=%lu min=%lu\r\n",
|
||||
(long)rc,
|
||||
(unsigned long)xPortGetFreeHeapSize(),
|
||||
(unsigned long)xPortGetMinimumEverFreeHeapSize());
|
||||
configASSERT(rc == pdPASS);
|
||||
} else {
|
||||
debug_log_write("[NET] skip TcpCliC1 en=0\r\n");
|
||||
}
|
||||
|
||||
if (cfg->links[CONFIG_LINK_C2].enabled != 0u) {
|
||||
rc = xTaskCreate(TcpCliTask_C2, "TcpCliC2", TASK_STACK_TCP_CLIENT, NULL, TASK_PRIORITY_TCP_CLIENT, &xTcpCliTaskC2Handle);
|
||||
debug_log_printf("[NET] create TcpCliC2 rc=%ld free=%lu min=%lu\r\n",
|
||||
(long)rc,
|
||||
(unsigned long)xPortGetFreeHeapSize(),
|
||||
(unsigned long)xPortGetMinimumEverFreeHeapSize());
|
||||
configASSERT(rc == pdPASS);
|
||||
} else {
|
||||
debug_log_write("[NET] skip TcpCliC2 en=0\r\n");
|
||||
}
|
||||
|
||||
xNetworkTasksStarted = pdTRUE;
|
||||
|
||||
debug_log_printf("[NET] start-network-tasks exit free=%lu min=%lu\r\n",
|
||||
(unsigned long)xPortGetFreeHeapSize(),
|
||||
@@ -85,35 +114,86 @@ void app_start_network_tasks(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
void app_request_network_task_stop(void)
|
||||
{
|
||||
xNetworkTaskStopRequested = pdTRUE;
|
||||
}
|
||||
|
||||
void app_clear_network_task_stop(void)
|
||||
{
|
||||
xNetworkTaskStopRequested = pdFALSE;
|
||||
}
|
||||
|
||||
BaseType_t app_network_task_stop_requested(void)
|
||||
{
|
||||
return xNetworkTaskStopRequested;
|
||||
}
|
||||
|
||||
BaseType_t app_network_tasks_are_stopped(void)
|
||||
{
|
||||
return (xTcpSrvTaskS1Handle == NULL &&
|
||||
xTcpSrvTaskS2Handle == NULL &&
|
||||
xTcpCliTaskC1Handle == NULL &&
|
||||
xTcpCliTaskC2Handle == NULL) ? pdTRUE : pdFALSE;
|
||||
}
|
||||
|
||||
void app_on_network_task_exit(TaskHandle_t task_handle)
|
||||
{
|
||||
taskENTER_CRITICAL();
|
||||
|
||||
if (task_handle == xTcpSrvTaskS1Handle) {
|
||||
xTcpSrvTaskS1Handle = NULL;
|
||||
} else if (task_handle == xTcpSrvTaskS2Handle) {
|
||||
xTcpSrvTaskS2Handle = NULL;
|
||||
} else if (task_handle == xTcpCliTaskC1Handle) {
|
||||
xTcpCliTaskC1Handle = NULL;
|
||||
} else if (task_handle == xTcpCliTaskC2Handle) {
|
||||
xTcpCliTaskC2Handle = NULL;
|
||||
}
|
||||
|
||||
if (xTcpSrvTaskS1Handle == NULL &&
|
||||
xTcpSrvTaskS2Handle == NULL &&
|
||||
xTcpCliTaskC1Handle == NULL &&
|
||||
xTcpCliTaskC2Handle == NULL) {
|
||||
xNetworkTasksStarted = pdFALSE;
|
||||
}
|
||||
|
||||
taskEXIT_CRITICAL();
|
||||
}
|
||||
|
||||
void app_request_network_restart(void)
|
||||
{
|
||||
xNetworkRestartRequested = pdTRUE;
|
||||
}
|
||||
|
||||
void app_clear_network_restart_request(void)
|
||||
{
|
||||
xNetworkRestartRequested = pdFALSE;
|
||||
}
|
||||
|
||||
BaseType_t app_network_restart_requested(void)
|
||||
{
|
||||
return xNetworkRestartRequested;
|
||||
}
|
||||
|
||||
static void StartDefaultTask(void *argument)
|
||||
{
|
||||
TickType_t last_snapshot = xTaskGetTickCount();
|
||||
BaseType_t alive_logged = pdFALSE;
|
||||
BaseType_t iwdg_ready = pdFALSE;
|
||||
|
||||
(void)argument;
|
||||
debug_log_boot("default-task");
|
||||
|
||||
if (MX_IWDG_Init() == HAL_OK) {
|
||||
debug_log_write("[BOOT] iwdg-started\r\n");
|
||||
iwdg_ready = pdTRUE;
|
||||
} else {
|
||||
debug_log_write("[BOOT] iwdg-init-fail\r\n");
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
|
||||
if (iwdg_ready == pdTRUE) {
|
||||
HAL_IWDG_Refresh(&hiwdg);
|
||||
if (alive_logged == pdFALSE) {
|
||||
debug_log_write("[RTOS] alive\r\n");
|
||||
alive_logged = pdTRUE;
|
||||
}
|
||||
if ((xTaskGetTickCount() - last_snapshot) >= pdMS_TO_TICKS(5000)) {
|
||||
debug_log_printf("[RTOS] seq=%lu drops=%lu last=%lu free=%lu min=%lu self_hwm=%lu\r\n",
|
||||
(unsigned long)g_rtt_log_seq,
|
||||
(unsigned long)g_rtt_log_drop_count,
|
||||
(unsigned long)g_rtt_log_last_drop_seq,
|
||||
(unsigned long)xPortGetFreeHeapSize(),
|
||||
(unsigned long)xPortGetMinimumEverFreeHeapSize(),
|
||||
(unsigned long)uxTaskGetStackHighWaterMark(NULL));
|
||||
if (xNetPollTaskHandle != NULL) {
|
||||
debug_log_printf("[RTOS] net_phase=%lu net_hwm=%lu\r\n",
|
||||
(unsigned long)g_netif_phase,
|
||||
(unsigned long)uxTaskGetStackHighWaterMark(xNetPollTaskHandle));
|
||||
}
|
||||
last_snapshot = xTaskGetTickCount();
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
}
|
||||
|
||||
+8
-2
@@ -76,9 +76,15 @@ void MX_GPIO_Init(void)
|
||||
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
|
||||
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
|
||||
|
||||
/* EXTI interrupt init*/
|
||||
/* EXTI interrupt init
|
||||
* Keep CH390 INT masked during early boot. PB0 may already be asserted at
|
||||
* power-on, while the FreeRTOS semaphore is not created until
|
||||
* MX_FREERTOS_Init(). The network driver enables EXTI0 after CH390 and the
|
||||
* RTOS objects are ready.
|
||||
*/
|
||||
HAL_NVIC_DisableIRQ(EXTI0_IRQn);
|
||||
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
|
||||
HAL_NVIC_SetPriority(EXTI0_IRQn, 6, 0);
|
||||
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
|
||||
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -27,7 +27,7 @@
|
||||
IWDG_HandleTypeDef hiwdg;
|
||||
|
||||
/* IWDG init function */
|
||||
void MX_IWDG_Init(void)
|
||||
HAL_StatusTypeDef MX_IWDG_Init(void)
|
||||
{
|
||||
|
||||
/* USER CODE BEGIN IWDG_Init 0 */
|
||||
@@ -42,12 +42,12 @@ void MX_IWDG_Init(void)
|
||||
hiwdg.Init.Reload = 4095;
|
||||
if (HAL_IWDG_Init(&hiwdg) != HAL_OK)
|
||||
{
|
||||
Error_Handler();
|
||||
return HAL_ERROR;
|
||||
}
|
||||
/* USER CODE BEGIN IWDG_Init 2 */
|
||||
|
||||
/* USER CODE END IWDG_Init 2 */
|
||||
|
||||
return HAL_OK;
|
||||
}
|
||||
|
||||
/* USER CODE BEGIN 1 */
|
||||
|
||||
+32
-5
@@ -68,6 +68,7 @@ void MX_FREERTOS_Init(void);
|
||||
/* USER CODE BEGIN PFP */
|
||||
static void CH390_HardwareReset(void);
|
||||
static void LED_Init(void);
|
||||
static void ApplyConfiguredUartBaudrates(void);
|
||||
void Debug_TrapWithRttHint(const char *tag);
|
||||
/* USER CODE END PFP */
|
||||
|
||||
@@ -109,6 +110,12 @@ void LED_Toggle(void)
|
||||
HAL_GPIO_TogglePin(LED_PORT, LED_PIN);
|
||||
}
|
||||
|
||||
static void ApplyConfiguredUartBaudrates(void)
|
||||
{
|
||||
USART_SetConfiguredBaudrates(config_get_uart_baudrate(LINK_UART_U0),
|
||||
config_get_uart_baudrate(LINK_UART_U1));
|
||||
}
|
||||
|
||||
/* USER CODE END 0 */
|
||||
|
||||
/**
|
||||
@@ -142,8 +149,9 @@ int main(void)
|
||||
/* Initialize all configured peripherals */
|
||||
MX_GPIO_Init();
|
||||
MX_DMA_Init();
|
||||
MX_IWDG_Init();
|
||||
MX_USART1_UART_Init();
|
||||
config_init();
|
||||
ApplyConfiguredUartBaudrates();
|
||||
MX_USART2_UART_Init();
|
||||
MX_USART3_UART_Init();
|
||||
MX_SPI1_Init();
|
||||
@@ -156,8 +164,6 @@ int main(void)
|
||||
/* CH390 硬件复位 */
|
||||
CH390_HardwareReset();
|
||||
|
||||
/* Initialize configuration from Flash (fallback to defaults on invalid data) */
|
||||
config_init();
|
||||
debug_log_boot("config-ready");
|
||||
|
||||
/* USER CODE END 2 */
|
||||
@@ -231,7 +237,28 @@ void SystemClock_Config(void)
|
||||
*/
|
||||
void Debug_TrapWithRttHint(const char *tag)
|
||||
{
|
||||
debug_log_fault(tag);
|
||||
debug_log_fault_context(tag, __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
void vApplicationMallocFailedHook(void)
|
||||
{
|
||||
debug_log_fault_context("malloc-failed", __FILE__, __LINE__);
|
||||
__disable_irq();
|
||||
for (;;)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
|
||||
{
|
||||
debug_log_printf("[FAULT] stack-overflow task=%s\r\n",
|
||||
(pcTaskName != NULL) ? pcTaskName : "(null)");
|
||||
debug_log_fault_context("stack-overflow", __FILE__, __LINE__);
|
||||
(void)xTask;
|
||||
__disable_irq();
|
||||
for (;;)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/* USER CODE END 4 */
|
||||
@@ -244,7 +271,7 @@ void Error_Handler(void)
|
||||
{
|
||||
/* USER CODE BEGIN Error_Handler_Debug */
|
||||
/* User can add his own implementation to report the HAL error return state */
|
||||
debug_log_fault("error-handler");
|
||||
debug_log_fault_context("error-handler", __FILE__, __LINE__);
|
||||
__disable_irq();
|
||||
while (1)
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "stm32f1xx_it.h"
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "semphr.h"
|
||||
#include "task.h"
|
||||
|
||||
#include "app_runtime.h"
|
||||
@@ -101,9 +102,12 @@ void EXTI0_IRQHandler(void)
|
||||
|
||||
if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0) != RESET) {
|
||||
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
|
||||
if ((xNetSemaphore != NULL) &&
|
||||
(xTaskGetSchedulerState() == taskSCHEDULER_RUNNING)) {
|
||||
xSemaphoreGiveFromISR(xNetSemaphore, &xHigherPriorityTaskWoken);
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SPI1_IRQHandler(void)
|
||||
|
||||
+11
-2
@@ -22,6 +22,9 @@
|
||||
|
||||
/* USER CODE BEGIN 0 */
|
||||
|
||||
static uint32_t g_usart2_baudrate = 115200u;
|
||||
static uint32_t g_usart3_baudrate = 115200u;
|
||||
|
||||
/* USER CODE END 0 */
|
||||
|
||||
UART_HandleTypeDef huart1;
|
||||
@@ -76,7 +79,7 @@ void MX_USART2_UART_Init(void)
|
||||
|
||||
/* USER CODE END USART2_Init 1 */
|
||||
huart2.Instance = USART2;
|
||||
huart2.Init.BaudRate = 115200;
|
||||
huart2.Init.BaudRate = g_usart2_baudrate;
|
||||
huart2.Init.WordLength = UART_WORDLENGTH_8B;
|
||||
huart2.Init.StopBits = UART_STOPBITS_1;
|
||||
huart2.Init.Parity = UART_PARITY_NONE;
|
||||
@@ -105,7 +108,7 @@ void MX_USART3_UART_Init(void)
|
||||
|
||||
/* USER CODE END USART3_Init 1 */
|
||||
huart3.Instance = USART3;
|
||||
huart3.Init.BaudRate = 115200;
|
||||
huart3.Init.BaudRate = g_usart3_baudrate;
|
||||
huart3.Init.WordLength = UART_WORDLENGTH_8B;
|
||||
huart3.Init.StopBits = UART_STOPBITS_1;
|
||||
huart3.Init.Parity = UART_PARITY_NONE;
|
||||
@@ -396,4 +399,10 @@ void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
|
||||
|
||||
/* USER CODE BEGIN 1 */
|
||||
|
||||
void USART_SetConfiguredBaudrates(uint32_t usart2_baudrate, uint32_t usart3_baudrate)
|
||||
{
|
||||
g_usart2_baudrate = usart2_baudrate;
|
||||
g_usart3_baudrate = usart3_baudrate;
|
||||
}
|
||||
|
||||
/* USER CODE END 1 */
|
||||
|
||||
+100
-6
@@ -12,6 +12,25 @@
|
||||
#include "CH390.h"
|
||||
#include "CH390_Interface.h"
|
||||
|
||||
#define CH390_EPCR_POLL_LIMIT 100000u
|
||||
|
||||
static int ch390_wait_epcr_ready(void)
|
||||
{
|
||||
uint32_t poll_count = CH390_EPCR_POLL_LIMIT;
|
||||
|
||||
while ((ch390_read_reg(CH390_EPCR) & 0x01u) != 0u)
|
||||
{
|
||||
if (poll_count == 0u)
|
||||
{
|
||||
ch390_write_reg(CH390_EPCR, 0x00u);
|
||||
return -1;
|
||||
}
|
||||
--poll_count;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ch390_probe_rx_header(uint8_t *head)
|
||||
{
|
||||
if (head == 0)
|
||||
@@ -22,6 +41,46 @@ void ch390_probe_rx_header(uint8_t *head)
|
||||
ch390_read_mem(head, 4);
|
||||
}
|
||||
|
||||
int ch390_peek_packet(uint8_t *rx_status, uint16_t *rx_len)
|
||||
{
|
||||
uint8_t nsr;
|
||||
uint8_t header[4];
|
||||
uint16_t mrr;
|
||||
|
||||
if (rx_status != 0)
|
||||
{
|
||||
*rx_status = 0u;
|
||||
}
|
||||
|
||||
if (rx_len != 0)
|
||||
{
|
||||
*rx_len = 0u;
|
||||
}
|
||||
|
||||
nsr = ch390_read_reg(CH390_NSR);
|
||||
if ((nsr & NSR_RXRDY) == 0u)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
mrr = (uint16_t)ch390_read_mrrl() | ((uint16_t)ch390_read_mrrh() << 8);
|
||||
ch390_read_mem(header, 4);
|
||||
ch390_write_reg(CH390_MRRL, (uint8_t)(mrr & 0xffu));
|
||||
ch390_write_reg(CH390_MRRH, (uint8_t)((mrr >> 8) & 0xffu));
|
||||
|
||||
if (rx_status != 0)
|
||||
{
|
||||
*rx_status = header[1];
|
||||
}
|
||||
|
||||
if (rx_len != 0)
|
||||
{
|
||||
*rx_len = (uint16_t)header[2] | ((uint16_t)header[3] << 8);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name ch390_receive_packet
|
||||
* @brief Receive packet
|
||||
@@ -33,6 +92,7 @@ void ch390_probe_rx_header(uint8_t *head)
|
||||
uint32_t ch390_receive_packet(uint8_t *buff, uint8_t *rx_status)
|
||||
{
|
||||
uint8_t nsr;
|
||||
uint8_t ready;
|
||||
uint16_t rx_len = 0;
|
||||
uint8_t ReceiveData[4];
|
||||
|
||||
@@ -48,6 +108,18 @@ uint32_t ch390_receive_packet(uint8_t *buff, uint8_t *rx_status)
|
||||
return 0;
|
||||
}
|
||||
|
||||
(void)ch390_read_mrcmdx();
|
||||
ready = ch390_read_mrcmdx1();
|
||||
if (ready == 0u)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (ready != CH390_PKT_RDY)
|
||||
{
|
||||
ch390_rx_reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ch390_read_mem(ReceiveData, 4);
|
||||
|
||||
if (rx_status != 0)
|
||||
@@ -56,15 +128,17 @@ uint32_t ch390_receive_packet(uint8_t *buff, uint8_t *rx_status)
|
||||
}
|
||||
rx_len = (uint16_t)ReceiveData[2] | ((uint16_t)ReceiveData[3] << 8);
|
||||
|
||||
if (((ReceiveData[1] & 0x3Fu) != 0u) ||
|
||||
(rx_len < 14u) ||
|
||||
if ((ReceiveData[0] != CH390_PKT_RDY) ||
|
||||
((ReceiveData[1] & 0x3Fu) != 0u) ||
|
||||
(rx_len < (uint16_t)(14u + CH390_PKT_CRC_LEN)) ||
|
||||
(rx_len > CH390_PKT_MAX))
|
||||
{
|
||||
ch390_rx_reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ch390_read_mem(buff, rx_len);
|
||||
return rx_len;
|
||||
return (uint32_t)(rx_len - CH390_PKT_CRC_LEN);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,6 +199,17 @@ void ch390_drop_packet(uint16_t len)
|
||||
ch390_write_reg(CH390_MRRH, (mdr >> 8) & 0xff);
|
||||
}
|
||||
|
||||
void ch390_rx_reset(void)
|
||||
{
|
||||
uint8_t rcr = ch390_read_reg(CH390_RCR);
|
||||
|
||||
ch390_write_reg(CH390_RCR, (uint8_t)(rcr & (uint8_t)(~RCR_RXEN)));
|
||||
ch390_write_reg(CH390_MPTRCR, MPTRCR_RST_RX);
|
||||
ch390_write_reg(CH390_NSR, NSR_RXOV);
|
||||
ch390_write_reg(CH390_ISR, (uint8_t)(ISR_ROS | ISR_ROO | ISR_PR));
|
||||
ch390_write_reg(CH390_RCR, (uint8_t)(rcr | RCR_RXEN));
|
||||
}
|
||||
|
||||
/**
|
||||
* @name ch390_read_phy
|
||||
* @brief Read PHY register
|
||||
@@ -135,7 +220,10 @@ uint16_t ch390_read_phy(uint8_t reg)
|
||||
ch390_write_reg(CH390_EPAR, CH390_PHY | reg);
|
||||
// Chose PHY, send read command
|
||||
ch390_write_reg(CH390_EPCR, EPCR_ERPRR | EPCR_EPOS);
|
||||
while(ch390_read_reg(CH390_EPCR) & 0x01);
|
||||
if (ch390_wait_epcr_ready() != 0)
|
||||
{
|
||||
return 0xFFFFu;
|
||||
}
|
||||
// Clear read command
|
||||
ch390_write_reg(CH390_EPCR, 0x00);
|
||||
return (ch390_read_reg(CH390_EPDRH) << 8) |
|
||||
@@ -155,7 +243,10 @@ void ch390_write_phy(uint8_t reg, uint16_t value)
|
||||
ch390_write_reg(CH390_EPDRH, ((value >> 8) & 0xff)); // High byte
|
||||
// Chose PHY, send write command
|
||||
ch390_write_reg(CH390_EPCR, 0x0A);
|
||||
while(ch390_read_reg(CH390_EPCR) & 0x01);
|
||||
if (ch390_wait_epcr_ready() != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Clear write command
|
||||
ch390_write_reg(CH390_EPCR, 0x00);
|
||||
}
|
||||
@@ -173,7 +264,10 @@ void ch390_write_eeprom(uint8_t reg, uint16_t value)
|
||||
ch390_write_reg(CH390_EPDRH, ((value >> 8) & 0xff)); // High byte
|
||||
// Chose EEPROM, send write command
|
||||
ch390_write_reg(CH390_EPCR, EPCR_ERPRW);
|
||||
while(ch390_read_reg(CH390_EPCR) & 0x01);
|
||||
if (ch390_wait_epcr_ready() != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Clear write command
|
||||
ch390_write_reg(CH390_EPCR, 0x00);
|
||||
}
|
||||
|
||||
@@ -150,6 +150,8 @@ enum ch390_phy_mode
|
||||
#define CH390_BCASTCR 0x53
|
||||
#define CH390_INTCKCR 0x54
|
||||
#define CH390_MPTRCR 0x55
|
||||
#define MPTRCR_RST_TX (1<<1)
|
||||
#define MPTRCR_RST_RX (1<<0)
|
||||
#define CH390_MLEDCR 0x57
|
||||
#define CH390_MRCMDX 0x70
|
||||
#define CH390_MRCMDX1 0x71
|
||||
@@ -302,6 +304,8 @@ enum ch390_phy_mode
|
||||
#define CH390_RLENCR 0x52
|
||||
#define CH390_BCASTCR 0x53
|
||||
#define CH390_MPTRCR 0x55
|
||||
#define MPTRCR_RST_TX (1<<1)
|
||||
#define MPTRCR_RST_RX (1<<0)
|
||||
#define CH390_MRCMDX 0xF0
|
||||
#define CH390_MRCMDX1 0xF1
|
||||
#define CH390_MRCMD 0xF2
|
||||
@@ -356,6 +360,7 @@ enum ch390_phy_mode
|
||||
#define CH390_PKT_NONE 0x00 /* No packet received */
|
||||
#define CH390_PKT_RDY 0x01 /* Packet ready to receive */
|
||||
#define CH390_PKT_ERR 0xFE /* Un-stable states */
|
||||
#define CH390_PKT_CRC_LEN 4u /* Ethernet FCS stored in RX SRAM */
|
||||
#define CH390_PKT_MAX 1536 /* Received packet max size */
|
||||
#define CH390_PKT_MIN 64
|
||||
|
||||
@@ -657,6 +662,21 @@ int ch390_runtime_link_up_from_status(const struct ch390_runtime_status *status)
|
||||
*/
|
||||
void ch390_probe_rx_header(uint8_t *head);
|
||||
|
||||
/**
|
||||
* @name ch390_peek_packet
|
||||
* @brief Peek current RX header without consuming the packet.
|
||||
* @param rx_status - Output abnormal status while receiving packet
|
||||
* @param rx_len - Output packet length from RX header
|
||||
* @return 0: no packet pending 1: header sampled
|
||||
*/
|
||||
int ch390_peek_packet(uint8_t *rx_status, uint16_t *rx_len);
|
||||
|
||||
/**
|
||||
* @name ch390_rx_reset
|
||||
* @brief Repair RX datapath after overflow/corruption without full chip reset.
|
||||
*/
|
||||
void ch390_rx_reset(void);
|
||||
|
||||
/**
|
||||
* @name ch390_runtime_receive_packet
|
||||
* @brief Runtime RX entry point for packet receive
|
||||
|
||||
@@ -188,6 +188,8 @@ void ch390_interrupt_init(void)
|
||||
/* EXTI0 is configured in CubeMX for PB0 */
|
||||
/* NVIC priority should be >= configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY */
|
||||
/* for FreeRTOS compatibility */
|
||||
HAL_NVIC_DisableIRQ(EXTI0_IRQn);
|
||||
__HAL_GPIO_EXTI_CLEAR_IT(CH390_INT_PIN);
|
||||
HAL_NVIC_SetPriority(EXTI0_IRQn, 6, 0);
|
||||
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
|
||||
}
|
||||
@@ -283,25 +285,16 @@ uint8_t ch390_read_reg(uint8_t reg)
|
||||
|
||||
static uint8_t ch390_read_rx_reg(uint8_t reg)
|
||||
{
|
||||
uint8_t tx_buf[3];
|
||||
uint8_t rx_buf[3];
|
||||
|
||||
tx_buf[0] = OPC_MEM_DMY_R;
|
||||
tx_buf[1] = reg;
|
||||
tx_buf[2] = 0x00u;
|
||||
uint8_t value;
|
||||
|
||||
CH390_SPI_ATOMIC_ENTER();
|
||||
ch390_cs(0);
|
||||
if (HAL_SPI_TransmitReceive(&hspi1, tx_buf, rx_buf, 3, SPI_TIMEOUT) != HAL_OK)
|
||||
{
|
||||
ch390_cs(1);
|
||||
CH390_SPI_ATOMIC_EXIT();
|
||||
return 0u;
|
||||
}
|
||||
ch390_spi_exchange_byte(reg | OPC_REG_R);
|
||||
value = ch390_spi_dummy_read();
|
||||
ch390_cs(1);
|
||||
CH390_SPI_ATOMIC_EXIT();
|
||||
|
||||
return rx_buf[2];
|
||||
return value;
|
||||
}
|
||||
|
||||
uint8_t ch390_read_mrcmdx(void)
|
||||
|
||||
@@ -30,28 +30,28 @@ uint8_t ch390_read_reg(uint8_t reg);
|
||||
|
||||
/**
|
||||
* @name ch390_read_mrcmdx
|
||||
* @brief Read MRCMDX via memory-dummy-read opcode
|
||||
* @brief Read MRCMDX receive-ready latch
|
||||
* @return Register value
|
||||
*/
|
||||
uint8_t ch390_read_mrcmdx(void);
|
||||
|
||||
/**
|
||||
* @name ch390_read_mrcmdx1
|
||||
* @brief Read MRCMDX1 via memory-dummy-read opcode
|
||||
* @brief Read MRCMDX1 receive-ready latch
|
||||
* @return Register value
|
||||
*/
|
||||
uint8_t ch390_read_mrcmdx1(void);
|
||||
|
||||
/**
|
||||
* @name ch390_read_mrrl
|
||||
* @brief Read MRRL via memory-dummy-read opcode
|
||||
* @brief Read MRRL receive memory pointer register
|
||||
* @return Register value
|
||||
*/
|
||||
uint8_t ch390_read_mrrl(void);
|
||||
|
||||
/**
|
||||
* @name ch390_read_mrrh
|
||||
* @brief Read MRRH via memory-dummy-read opcode
|
||||
* @brief Read MRRH receive memory pointer register
|
||||
* @return Register value
|
||||
*/
|
||||
uint8_t ch390_read_mrrh(void);
|
||||
|
||||
@@ -1395,7 +1395,13 @@ lwip_netconn_do_connect(void *m)
|
||||
#if LWIP_TCPIP_CORE_LOCKING
|
||||
LWIP_ASSERT("state!", msg->conn->state == NETCONN_CONNECT);
|
||||
UNLOCK_TCPIP_CORE();
|
||||
sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
|
||||
if (sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), msg->conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
|
||||
if (msg->conn->state == NETCONN_CONNECT) {
|
||||
msg->conn->current_msg = NULL;
|
||||
msg->conn->state = NETCONN_NONE;
|
||||
msg->err = ERR_TIMEOUT;
|
||||
}
|
||||
}
|
||||
LOCK_TCPIP_CORE();
|
||||
LWIP_ASSERT("state!", msg->conn->state != NETCONN_CONNECT);
|
||||
#endif /* LWIP_TCPIP_CORE_LOCKING */
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
*
|
||||
* Key design decisions:
|
||||
* - netconn API for thread-safe multi-connection TCP
|
||||
* - tcpip_thread handles all lwIP core operations
|
||||
* - LWIP_TCPIP_CORE_LOCKING=1 allows direct send from application tasks
|
||||
* - tcpip_thread handles netconn API and timers
|
||||
* - core locking lets the poll task process RX packets synchronously
|
||||
* - Conservative memory footprint: target ~16KB for lwIP
|
||||
*/
|
||||
|
||||
@@ -27,9 +27,9 @@
|
||||
#define LWIP_NETCONN 1
|
||||
#define LWIP_NETIF_API 1
|
||||
|
||||
/* Core locking: allows netconn_write/recv from any task without going through mbox */
|
||||
/* Core locking: process netif RX synchronously instead of consuming TCPIP_MSG_INPKT slots. */
|
||||
#define LWIP_TCPIP_CORE_LOCKING 1
|
||||
#define LWIP_TCPIP_CORE_LOCKING_INPUT 0
|
||||
#define LWIP_TCPIP_CORE_LOCKING_INPUT 1
|
||||
|
||||
/* Critical section protection */
|
||||
#define SYS_LIGHTWEIGHT_PROT 1
|
||||
@@ -54,7 +54,7 @@
|
||||
#define MEM_SIZE (7 * 1024)
|
||||
|
||||
/* Number of pbufs in pool.
|
||||
* 10 pools for 4 concurrent connections with some headroom. */
|
||||
* RX is processed synchronously under the core lock, so a small pool is sufficient. */
|
||||
#define PBUF_POOL_SIZE 8
|
||||
|
||||
/* Size of each pbuf in pool (must hold one Ethernet frame) */
|
||||
|
||||
@@ -23,7 +23,24 @@ struct ethernetif {
|
||||
err_t ethernetif_init(struct netif *netif);
|
||||
void ethernetif_input(struct netif *netif);
|
||||
void lwip_netif_init(const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw);
|
||||
void ethernetif_diag_ch390_init(void);
|
||||
void ethernetif_diag_poll_status(void);
|
||||
void ethernetif_check_link(void);
|
||||
void ethernetif_apply_runtime_netif_config(const ip4_addr_t *ipaddr,
|
||||
const ip4_addr_t *netmask,
|
||||
const ip4_addr_t *gw,
|
||||
const uint8_t *mac);
|
||||
void ethernetif_force_full_recovery(const ip4_addr_t *ipaddr,
|
||||
const ip4_addr_t *netmask,
|
||||
const ip4_addr_t *gw,
|
||||
const uint8_t *mac);
|
||||
void ethernetif_force_link_down(void);
|
||||
uint16_t ethernetif_ch390_get_vendor_id(void);
|
||||
uint16_t ethernetif_ch390_get_product_id(void);
|
||||
uint8_t ethernetif_ch390_get_revision(void);
|
||||
uint8_t ethernetif_ch390_health_ok(void);
|
||||
uint8_t ethernetif_get_effective_mac(uint8_t *mac);
|
||||
uint8_t ethernetif_link_is_up(void);
|
||||
void ethernetif_poll(void);
|
||||
|
||||
void print_netif(struct netif *netif);
|
||||
|
||||
@@ -36,6 +36,7 @@ err_t ethernet_output(struct netif *netif,
|
||||
{
|
||||
struct pbuf *q;
|
||||
struct eth_hdr *ethhdr;
|
||||
err_t err;
|
||||
|
||||
LWIP_ASSERT("netif != NULL", netif != NULL);
|
||||
LWIP_ASSERT("p != NULL", p != NULL);
|
||||
@@ -56,7 +57,9 @@ err_t ethernet_output(struct netif *netif,
|
||||
SMEMCPY(ðhdr->src, src, sizeof(struct eth_addr));
|
||||
ethhdr->type = lwip_htons(eth_type);
|
||||
|
||||
return netif->linkoutput(netif, q);
|
||||
err = netif->linkoutput(netif, q);
|
||||
pbuf_free(q);
|
||||
return err;
|
||||
}
|
||||
|
||||
err_t ethernet_input(struct pbuf *p, struct netif *netif)
|
||||
|
||||
@@ -41,12 +41,59 @@ static SemaphoreHandle_t spi_mutex = NULL;
|
||||
static uint8_t s_rx_buffer[CH390_PKT_MAX];
|
||||
static uint8_t s_tx_buffer[CH390_PKT_MAX];
|
||||
static uint8_t s_garp_sent = 0u;
|
||||
static uint8_t s_effective_mac[ETHARP_HWADDR_LEN];
|
||||
static uint8_t s_effective_mac_valid = 0u;
|
||||
|
||||
#define CH390_TX_TIMEOUT_RESTART_THRESHOLD 6u
|
||||
#define CH390_EXPECTED_VENDOR_ID 0x1C00u
|
||||
#define CH390_EXPECTED_PRODUCT_ID 0x9151u
|
||||
#define ETH_RX_LOG_INITIAL_COUNT 3u
|
||||
#define ETH_RX_LOG_EVERY_COUNT 64u
|
||||
|
||||
#define ETH_RECOVERY_NONE 0u
|
||||
#define ETH_RECOVERY_RX 1u
|
||||
#define ETH_RECOVERY_FULL 2u
|
||||
|
||||
struct ethernetif_recovery_state
|
||||
{
|
||||
uint8_t pending;
|
||||
uint8_t in_progress;
|
||||
uint8_t tx_timeout_streak;
|
||||
uint32_t tx_timeout_count;
|
||||
uint32_t rx_overflow_count;
|
||||
uint32_t rx_bad_count;
|
||||
uint32_t rx_recovery_count;
|
||||
uint32_t full_recovery_count;
|
||||
};
|
||||
|
||||
static struct ethernetif_recovery_state s_recovery = {0};
|
||||
|
||||
/* Forward declarations */
|
||||
static err_t low_level_init(struct netif *netif);
|
||||
static err_t low_level_output(struct netif *netif, struct pbuf *p);
|
||||
static struct pbuf *low_level_input(struct netif *netif);
|
||||
static void ethernetif_update_link(uint8_t link_status);
|
||||
static uint32_t ethernetif_record_counter(uint32_t *counter);
|
||||
static uint32_t ethernetif_record_event_and_request(uint32_t *counter, uint8_t recovery_type);
|
||||
static uint8_t ethernetif_claim_recovery(void);
|
||||
static void ethernetif_finish_recovery(void);
|
||||
static uint8_t ethernetif_run_pending_recovery(void);
|
||||
static uint8_t ethernetif_should_log_counter(uint32_t count);
|
||||
static void ethernetif_clear_tx_timeout_streak(void);
|
||||
static uint32_t ethernetif_note_tx_timeout_streak(void);
|
||||
static uint8_t ethernetif_mac_is_all_zero(const uint8_t *mac);
|
||||
static void ethernetif_store_effective_mac(const uint8_t *mac);
|
||||
static void ethernetif_apply_configured_or_internal_mac_locked(const uint8_t *configured_mac);
|
||||
static void ethernetif_prepare_runtime_netif_locked(struct netif *netif);
|
||||
static void ethernetif_apply_runtime_netif_config_locked(const ip4_addr_t *ipaddr,
|
||||
const ip4_addr_t *netmask,
|
||||
const ip4_addr_t *gw,
|
||||
const uint8_t *mac);
|
||||
static void ethernetif_perform_full_recovery(const ip4_addr_t *ipaddr,
|
||||
const ip4_addr_t *netmask,
|
||||
const ip4_addr_t *gw,
|
||||
const uint8_t *mac,
|
||||
uint8_t *link_status);
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* Low Level Hardware Functions
|
||||
@@ -58,7 +105,6 @@ static void ethernetif_update_link(uint8_t link_status);
|
||||
*/
|
||||
static err_t low_level_init(struct netif *netif)
|
||||
{
|
||||
struct ethernetif *ethernetif = netif->state;
|
||||
const device_config_t *cfg = config_get();
|
||||
|
||||
/* Create SPI mutex */
|
||||
@@ -88,24 +134,9 @@ static err_t low_level_init(struct netif *netif)
|
||||
ch390_read_reg(CH390_BCASTCR),
|
||||
ch390_read_reg(CH390_MAR + 7));
|
||||
|
||||
/* Apply configured MAC address to CH390 before reading it back into lwIP */
|
||||
ch390_set_mac_address((uint8_t *)cfg->net.mac);
|
||||
ethernetif_apply_configured_or_internal_mac_locked(cfg->net.mac);
|
||||
|
||||
/* Set MAC hardware address length */
|
||||
netif->hwaddr_len = ETHARP_HWADDR_LEN;
|
||||
|
||||
/* Get MAC address from CH390 */
|
||||
ch390_get_mac(netif->hwaddr);
|
||||
|
||||
/* Maximum transfer unit */
|
||||
netif->mtu = 1500;
|
||||
|
||||
/* Device capabilities */
|
||||
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET;
|
||||
|
||||
/* Initialize state */
|
||||
ethernetif->rx_len = 0;
|
||||
ethernetif->rx_status = 0;
|
||||
ethernetif_prepare_runtime_netif_locked(netif);
|
||||
|
||||
/* Enable CH390 interrupt */
|
||||
ch390_interrupt_init();
|
||||
@@ -113,6 +144,408 @@ static err_t low_level_init(struct netif *netif)
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static uint32_t ethernetif_record_counter(uint32_t *counter)
|
||||
{
|
||||
uint32_t value;
|
||||
|
||||
taskENTER_CRITICAL();
|
||||
++(*counter);
|
||||
value = *counter;
|
||||
taskEXIT_CRITICAL();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static uint32_t ethernetif_record_event_and_request(uint32_t *counter, uint8_t recovery_type)
|
||||
{
|
||||
uint32_t value;
|
||||
|
||||
taskENTER_CRITICAL();
|
||||
++(*counter);
|
||||
if (recovery_type == ETH_RECOVERY_FULL)
|
||||
{
|
||||
app_request_network_restart();
|
||||
}
|
||||
else if (recovery_type > s_recovery.pending)
|
||||
{
|
||||
s_recovery.pending = recovery_type;
|
||||
}
|
||||
value = *counter;
|
||||
taskEXIT_CRITICAL();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void ethernetif_clear_tx_timeout_streak(void)
|
||||
{
|
||||
taskENTER_CRITICAL();
|
||||
s_recovery.tx_timeout_streak = 0u;
|
||||
taskEXIT_CRITICAL();
|
||||
}
|
||||
|
||||
static uint32_t ethernetif_note_tx_timeout_streak(void)
|
||||
{
|
||||
uint32_t streak;
|
||||
|
||||
taskENTER_CRITICAL();
|
||||
++s_recovery.tx_timeout_count;
|
||||
if (s_recovery.tx_timeout_streak < 0xFFu)
|
||||
{
|
||||
++s_recovery.tx_timeout_streak;
|
||||
}
|
||||
streak = s_recovery.tx_timeout_streak;
|
||||
taskEXIT_CRITICAL();
|
||||
|
||||
return streak;
|
||||
}
|
||||
|
||||
static uint8_t ethernetif_claim_recovery(void)
|
||||
{
|
||||
uint8_t recovery_type = ETH_RECOVERY_NONE;
|
||||
|
||||
taskENTER_CRITICAL();
|
||||
if ((s_recovery.in_progress == 0u) && (s_recovery.pending != ETH_RECOVERY_NONE))
|
||||
{
|
||||
recovery_type = s_recovery.pending;
|
||||
s_recovery.pending = ETH_RECOVERY_NONE;
|
||||
s_recovery.in_progress = 1u;
|
||||
}
|
||||
taskEXIT_CRITICAL();
|
||||
|
||||
return recovery_type;
|
||||
}
|
||||
|
||||
static void ethernetif_finish_recovery(void)
|
||||
{
|
||||
taskENTER_CRITICAL();
|
||||
s_recovery.in_progress = 0u;
|
||||
taskEXIT_CRITICAL();
|
||||
}
|
||||
|
||||
static uint8_t ethernetif_should_log_counter(uint32_t count)
|
||||
{
|
||||
if (count <= ETH_RX_LOG_INITIAL_COUNT)
|
||||
{
|
||||
return 1u;
|
||||
}
|
||||
|
||||
return ((count % ETH_RX_LOG_EVERY_COUNT) == 0u) ? 1u : 0u;
|
||||
}
|
||||
|
||||
static uint8_t ethernetif_run_pending_recovery(void)
|
||||
{
|
||||
ip4_addr_t ipaddr;
|
||||
ip4_addr_t netmask;
|
||||
ip4_addr_t gateway;
|
||||
uint8_t recovery_type;
|
||||
uint8_t link_status = 0u;
|
||||
uint32_t count;
|
||||
|
||||
recovery_type = ethernetif_claim_recovery();
|
||||
if (recovery_type == ETH_RECOVERY_NONE)
|
||||
{
|
||||
return ETH_RECOVERY_NONE;
|
||||
}
|
||||
|
||||
if (recovery_type == ETH_RECOVERY_FULL)
|
||||
{
|
||||
const device_config_t *cfg = config_get();
|
||||
|
||||
IP4_ADDR(&ipaddr, cfg->net.ip[0], cfg->net.ip[1], cfg->net.ip[2], cfg->net.ip[3]);
|
||||
IP4_ADDR(&netmask, cfg->net.mask[0], cfg->net.mask[1], cfg->net.mask[2], cfg->net.mask[3]);
|
||||
IP4_ADDR(&gateway, cfg->net.gw[0], cfg->net.gw[1], cfg->net.gw[2], cfg->net.gw[3]);
|
||||
ethernetif_clear_tx_timeout_streak();
|
||||
ethernetif_perform_full_recovery(&ipaddr, &netmask, &gateway, cfg->net.mac, &link_status);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (spi_mutex != NULL)
|
||||
{
|
||||
xSemaphoreTake(spi_mutex, portMAX_DELAY);
|
||||
}
|
||||
ch390_rx_reset();
|
||||
if (spi_mutex != NULL)
|
||||
{
|
||||
xSemaphoreGive(spi_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
if (recovery_type == ETH_RECOVERY_FULL)
|
||||
{
|
||||
count = ethernetif_record_counter(&s_recovery.full_recovery_count);
|
||||
debug_log_printf("[ETH] rec full n=%lu link=%u\r\n",
|
||||
(unsigned long)count,
|
||||
(unsigned int)link_status);
|
||||
}
|
||||
else
|
||||
{
|
||||
count = ethernetif_record_counter(&s_recovery.rx_recovery_count);
|
||||
if (ethernetif_should_log_counter(count) != 0u)
|
||||
{
|
||||
debug_log_printf("[ETH] rec rx n=%lu\r\n", (unsigned long)count);
|
||||
}
|
||||
}
|
||||
|
||||
ethernetif_finish_recovery();
|
||||
return recovery_type;
|
||||
}
|
||||
|
||||
static void ethernetif_perform_full_recovery(const ip4_addr_t *ipaddr,
|
||||
const ip4_addr_t *netmask,
|
||||
const ip4_addr_t *gw,
|
||||
const uint8_t *mac,
|
||||
uint8_t *link_status)
|
||||
{
|
||||
uint8_t local_link_status = 0u;
|
||||
|
||||
if (spi_mutex != NULL)
|
||||
{
|
||||
xSemaphoreTake(spi_mutex, portMAX_DELAY);
|
||||
}
|
||||
|
||||
ch390_spi_init();
|
||||
ch390_hardware_reset();
|
||||
ch390_default_config();
|
||||
ethernetif_apply_configured_or_internal_mac_locked(mac);
|
||||
ch390_set_phy_mode(CH390_AUTO);
|
||||
ch390_rx_enable(1);
|
||||
ethernetif_prepare_runtime_netif_locked(&ch390_netif);
|
||||
ethernetif_apply_runtime_netif_config_locked(ipaddr,
|
||||
netmask,
|
||||
gw,
|
||||
(s_effective_mac_valid != 0u) ? s_effective_mac : NULL);
|
||||
local_link_status = (uint8_t)ch390_get_link_status();
|
||||
|
||||
if (spi_mutex != NULL)
|
||||
{
|
||||
xSemaphoreGive(spi_mutex);
|
||||
}
|
||||
|
||||
ch390_interrupt_init();
|
||||
|
||||
if (link_status != NULL)
|
||||
{
|
||||
*link_status = local_link_status;
|
||||
}
|
||||
|
||||
ethernetif_update_link(local_link_status);
|
||||
}
|
||||
|
||||
static uint8_t ethernetif_mac_is_all_zero(const uint8_t *mac)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
if (mac == NULL)
|
||||
{
|
||||
return 1u;
|
||||
}
|
||||
|
||||
for (i = 0u; i < ETHARP_HWADDR_LEN; ++i)
|
||||
{
|
||||
if (mac[i] != 0u)
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
}
|
||||
|
||||
return 1u;
|
||||
}
|
||||
|
||||
static void ethernetif_store_effective_mac(const uint8_t *mac)
|
||||
{
|
||||
if (mac == NULL)
|
||||
{
|
||||
s_effective_mac_valid = 0u;
|
||||
return;
|
||||
}
|
||||
|
||||
MEMCPY(s_effective_mac, mac, ETHARP_HWADDR_LEN);
|
||||
s_effective_mac_valid = 1u;
|
||||
}
|
||||
|
||||
static void ethernetif_apply_configured_or_internal_mac_locked(const uint8_t *configured_mac)
|
||||
{
|
||||
uint8_t runtime_mac[ETHARP_HWADDR_LEN];
|
||||
|
||||
if (ethernetif_mac_is_all_zero(configured_mac) == 0u)
|
||||
{
|
||||
ch390_set_mac_address((uint8_t *)configured_mac);
|
||||
ethernetif_store_effective_mac(configured_mac);
|
||||
return;
|
||||
}
|
||||
|
||||
ch390_get_mac(runtime_mac);
|
||||
ethernetif_store_effective_mac(runtime_mac);
|
||||
debug_log_printf("[ETH] use internal mac=%02X:%02X:%02X:%02X:%02X:%02X\r\n",
|
||||
(unsigned int)runtime_mac[0],
|
||||
(unsigned int)runtime_mac[1],
|
||||
(unsigned int)runtime_mac[2],
|
||||
(unsigned int)runtime_mac[3],
|
||||
(unsigned int)runtime_mac[4],
|
||||
(unsigned int)runtime_mac[5]);
|
||||
}
|
||||
|
||||
static void ethernetif_prepare_runtime_netif_locked(struct netif *netif)
|
||||
{
|
||||
struct ethernetif *ethernetif;
|
||||
|
||||
if (netif == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
netif->hwaddr_len = ETHARP_HWADDR_LEN;
|
||||
ch390_get_mac(netif->hwaddr);
|
||||
ethernetif_store_effective_mac(netif->hwaddr);
|
||||
netif->mtu = 1500;
|
||||
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET;
|
||||
|
||||
ethernetif = (struct ethernetif *)netif->state;
|
||||
if (ethernetif != NULL)
|
||||
{
|
||||
ethernetif->rx_len = 0u;
|
||||
ethernetif->rx_status = 0u;
|
||||
}
|
||||
}
|
||||
|
||||
static void ethernetif_apply_runtime_netif_config_locked(const ip4_addr_t *ipaddr,
|
||||
const ip4_addr_t *netmask,
|
||||
const ip4_addr_t *gw,
|
||||
const uint8_t *mac)
|
||||
{
|
||||
err_t err;
|
||||
|
||||
if (mac != NULL)
|
||||
{
|
||||
MEMCPY(ch390_netif.hwaddr, mac, ETHARP_HWADDR_LEN);
|
||||
s_garp_sent = 0u;
|
||||
}
|
||||
|
||||
if (ipaddr != NULL && netmask != NULL && gw != NULL)
|
||||
{
|
||||
err = netifapi_netif_set_addr(&ch390_netif, ipaddr, netmask, gw);
|
||||
if (err != ERR_OK)
|
||||
{
|
||||
debug_log_printf("[ETH] set-addr fail err=%d\r\n", (int)err);
|
||||
}
|
||||
}
|
||||
|
||||
err = netifapi_netif_set_default(&ch390_netif);
|
||||
if (err != ERR_OK)
|
||||
{
|
||||
debug_log_printf("[ETH] resync-default fail err=%d\r\n", (int)err);
|
||||
}
|
||||
|
||||
err = netifapi_netif_set_up(&ch390_netif);
|
||||
if (err != ERR_OK)
|
||||
{
|
||||
debug_log_printf("[ETH] resync-up fail err=%d\r\n", (int)err);
|
||||
}
|
||||
}
|
||||
|
||||
void ethernetif_apply_runtime_netif_config(const ip4_addr_t *ipaddr,
|
||||
const ip4_addr_t *netmask,
|
||||
const ip4_addr_t *gw,
|
||||
const uint8_t *mac)
|
||||
{
|
||||
ethernetif_apply_runtime_netif_config_locked(ipaddr, netmask, gw, mac);
|
||||
}
|
||||
|
||||
void ethernetif_force_full_recovery(const ip4_addr_t *ipaddr,
|
||||
const ip4_addr_t *netmask,
|
||||
const ip4_addr_t *gw,
|
||||
const uint8_t *mac)
|
||||
{
|
||||
uint8_t link_status = 0u;
|
||||
uint32_t count;
|
||||
|
||||
ethernetif_clear_tx_timeout_streak();
|
||||
ethernetif_perform_full_recovery(ipaddr, netmask, gw, mac, &link_status);
|
||||
count = ethernetif_record_counter(&s_recovery.full_recovery_count);
|
||||
debug_log_printf("[ETH] rec full n=%lu link=%u\r\n",
|
||||
(unsigned long)count,
|
||||
(unsigned int)link_status);
|
||||
}
|
||||
|
||||
void ethernetif_force_link_down(void)
|
||||
{
|
||||
ethernetif_update_link(0u);
|
||||
}
|
||||
|
||||
uint8_t ethernetif_ch390_health_ok(void)
|
||||
{
|
||||
return ((ethernetif_ch390_get_vendor_id() == CH390_EXPECTED_VENDOR_ID) &&
|
||||
(ethernetif_ch390_get_product_id() == CH390_EXPECTED_PRODUCT_ID)) ? 1u : 0u;
|
||||
}
|
||||
|
||||
uint8_t ethernetif_get_effective_mac(uint8_t *mac)
|
||||
{
|
||||
if ((mac == NULL) || (s_effective_mac_valid == 0u))
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
MEMCPY(mac, s_effective_mac, ETHARP_HWADDR_LEN);
|
||||
return 1u;
|
||||
}
|
||||
|
||||
uint16_t ethernetif_ch390_get_vendor_id(void)
|
||||
{
|
||||
uint16_t vendor_id;
|
||||
|
||||
if (spi_mutex != NULL)
|
||||
{
|
||||
xSemaphoreTake(spi_mutex, portMAX_DELAY);
|
||||
}
|
||||
|
||||
vendor_id = ch390_get_vendor_id();
|
||||
|
||||
if (spi_mutex != NULL)
|
||||
{
|
||||
xSemaphoreGive(spi_mutex);
|
||||
}
|
||||
|
||||
return vendor_id;
|
||||
}
|
||||
|
||||
uint16_t ethernetif_ch390_get_product_id(void)
|
||||
{
|
||||
uint16_t product_id;
|
||||
|
||||
if (spi_mutex != NULL)
|
||||
{
|
||||
xSemaphoreTake(spi_mutex, portMAX_DELAY);
|
||||
}
|
||||
|
||||
product_id = ch390_get_product_id();
|
||||
|
||||
if (spi_mutex != NULL)
|
||||
{
|
||||
xSemaphoreGive(spi_mutex);
|
||||
}
|
||||
|
||||
return product_id;
|
||||
}
|
||||
|
||||
uint8_t ethernetif_ch390_get_revision(void)
|
||||
{
|
||||
uint8_t revision;
|
||||
|
||||
if (spi_mutex != NULL)
|
||||
{
|
||||
xSemaphoreTake(spi_mutex, portMAX_DELAY);
|
||||
}
|
||||
|
||||
revision = ch390_get_revision();
|
||||
|
||||
if (spi_mutex != NULL)
|
||||
{
|
||||
xSemaphoreGive(spi_mutex);
|
||||
}
|
||||
|
||||
return revision;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transmit a packet via CH390
|
||||
* @param netif Network interface structure
|
||||
@@ -125,6 +558,7 @@ static err_t low_level_output(struct netif *netif, struct pbuf *p)
|
||||
uint16_t offset;
|
||||
uint16_t tx_len;
|
||||
int tx_rc;
|
||||
uint32_t tx_timeout_streak;
|
||||
(void)netif;
|
||||
|
||||
/* Take SPI mutex */
|
||||
@@ -179,10 +613,18 @@ static err_t low_level_output(struct netif *netif, struct pbuf *p)
|
||||
{
|
||||
LINK_STATS_INC(link.drop);
|
||||
LINK_STATS_INC(link.err);
|
||||
debug_log_write("[ETH] tx timeout\r\n");
|
||||
tx_timeout_streak = ethernetif_note_tx_timeout_streak();
|
||||
debug_log_printf("[ETH] tx timeout n=%lu streak=%lu\r\n",
|
||||
(unsigned long)s_recovery.tx_timeout_count,
|
||||
(unsigned long)tx_timeout_streak);
|
||||
if (tx_timeout_streak >= CH390_TX_TIMEOUT_RESTART_THRESHOLD)
|
||||
{
|
||||
app_request_network_restart();
|
||||
}
|
||||
return ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
ethernetif_clear_tx_timeout_streak();
|
||||
LINK_STATS_INC(link.xmit);
|
||||
|
||||
return ERR_OK;
|
||||
@@ -266,7 +708,6 @@ static struct pbuf *low_level_input(struct netif *netif)
|
||||
else
|
||||
{
|
||||
/* No memory - drop packet */
|
||||
ch390_drop_packet(ethernetif->rx_len);
|
||||
LINK_STATS_INC(link.memerr);
|
||||
LINK_STATS_INC(link.drop);
|
||||
}
|
||||
@@ -428,7 +869,7 @@ void ethernetif_diag_ch390_init(void)
|
||||
ch390_spi_init();
|
||||
ch390_hardware_reset();
|
||||
ch390_default_config();
|
||||
ch390_set_mac_address((uint8_t *)cfg->net.mac);
|
||||
ethernetif_apply_configured_or_internal_mac_locked(cfg->net.mac);
|
||||
ch390_interrupt_init();
|
||||
|
||||
g_netif_init_ok = 1;
|
||||
@@ -457,6 +898,12 @@ void ethernetif_diag_poll_status(void)
|
||||
*/
|
||||
static void ethernetif_update_link(uint8_t link_status)
|
||||
{
|
||||
uint8_t old_link_status;
|
||||
|
||||
LOCK_TCPIP_CORE();
|
||||
|
||||
old_link_status = netif_is_link_up(&ch390_netif) ? 1u : 0u;
|
||||
|
||||
if (link_status)
|
||||
{
|
||||
if (!netif_is_link_up(&ch390_netif))
|
||||
@@ -464,9 +911,7 @@ static void ethernetif_update_link(uint8_t link_status)
|
||||
netif_set_link_up(&ch390_netif);
|
||||
if ((s_garp_sent == 0u) && netif_is_up(&ch390_netif))
|
||||
{
|
||||
LOCK_TCPIP_CORE();
|
||||
etharp_gratuitous(&ch390_netif);
|
||||
UNLOCK_TCPIP_CORE();
|
||||
s_garp_sent = 1u;
|
||||
}
|
||||
}
|
||||
@@ -479,6 +924,24 @@ static void ethernetif_update_link(uint8_t link_status)
|
||||
s_garp_sent = 0u;
|
||||
}
|
||||
}
|
||||
|
||||
UNLOCK_TCPIP_CORE();
|
||||
|
||||
if (old_link_status != ((link_status != 0u) ? 1u : 0u))
|
||||
{
|
||||
debug_log_printf("[ETH] link %s ip=%u.%u.%u.%u mac=%02X:%02X:%02X:%02X:%02X:%02X\r\n",
|
||||
(link_status != 0u) ? "up" : "down",
|
||||
(unsigned int)ip4_addr1_16(netif_ip4_addr(&ch390_netif)),
|
||||
(unsigned int)ip4_addr2_16(netif_ip4_addr(&ch390_netif)),
|
||||
(unsigned int)ip4_addr3_16(netif_ip4_addr(&ch390_netif)),
|
||||
(unsigned int)ip4_addr4_16(netif_ip4_addr(&ch390_netif)),
|
||||
(unsigned int)ch390_netif.hwaddr[0],
|
||||
(unsigned int)ch390_netif.hwaddr[1],
|
||||
(unsigned int)ch390_netif.hwaddr[2],
|
||||
(unsigned int)ch390_netif.hwaddr[3],
|
||||
(unsigned int)ch390_netif.hwaddr[4],
|
||||
(unsigned int)ch390_netif.hwaddr[5]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -489,6 +952,8 @@ void ethernetif_poll(void)
|
||||
struct ch390_runtime_status runtime_status;
|
||||
struct pbuf *p;
|
||||
err_t input_err;
|
||||
uint32_t rx_overflow_count;
|
||||
uint8_t recovery_type;
|
||||
uint8_t rx_budget = 4u;
|
||||
|
||||
/* Take SPI mutex */
|
||||
@@ -506,17 +971,23 @@ void ethernetif_poll(void)
|
||||
xSemaphoreGive(spi_mutex);
|
||||
}
|
||||
|
||||
/* Handle link change */
|
||||
if ((runtime_status.int_status & ISR_LNKCHG) != 0u)
|
||||
/* Handle RX overflow */
|
||||
if ((runtime_status.int_status & (ISR_ROS | ISR_ROO)) != 0u)
|
||||
{
|
||||
ethernetif_update_link((uint8_t)ch390_runtime_link_up_from_status(&runtime_status));
|
||||
LINK_STATS_INC(link.err);
|
||||
rx_overflow_count = ethernetif_record_event_and_request(&s_recovery.rx_overflow_count, ETH_RECOVERY_RX);
|
||||
if (ethernetif_should_log_counter(rx_overflow_count) != 0u)
|
||||
{
|
||||
debug_log_printf("[ETH] rx ovf n=%lu\r\n", (unsigned long)rx_overflow_count);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle RX overflow */
|
||||
if ((runtime_status.int_status & ISR_ROS) != 0u)
|
||||
recovery_type = ethernetif_run_pending_recovery();
|
||||
|
||||
/* Handle link change unless a full recovery already resynchronized link state. */
|
||||
if ((recovery_type != ETH_RECOVERY_FULL) && ((runtime_status.int_status & ISR_LNKCHG) != 0u))
|
||||
{
|
||||
/* RX overflow - packets might be corrupted */
|
||||
LINK_STATS_INC(link.err);
|
||||
ethernetif_update_link((uint8_t)ch390_runtime_link_up_from_status(&runtime_status));
|
||||
}
|
||||
|
||||
/* Always attempt to drain RX FIFO so packet handling does not depend only on ISR_PR timing. */
|
||||
@@ -537,6 +1008,8 @@ void ethernetif_poll(void)
|
||||
--rx_budget;
|
||||
}
|
||||
|
||||
(void)ethernetif_run_pending_recovery();
|
||||
|
||||
if (rx_budget == 0u)
|
||||
{
|
||||
taskYIELD();
|
||||
@@ -561,3 +1034,8 @@ void ethernetif_check_link(void)
|
||||
|
||||
ethernetif_update_link(link_status);
|
||||
}
|
||||
|
||||
uint8_t ethernetif_link_is_up(void)
|
||||
{
|
||||
return netif_is_link_up(&ch390_netif) ? 1u : 0u;
|
||||
}
|
||||
|
||||
@@ -62,6 +62,26 @@ void ethernetif_diag_poll_status(void);
|
||||
* @note Call this from the LwIP task periodically or on interrupt
|
||||
*/
|
||||
void ethernetif_check_link(void);
|
||||
void ethernetif_apply_runtime_netif_config(const ip4_addr_t *ipaddr,
|
||||
const ip4_addr_t *netmask,
|
||||
const ip4_addr_t *gw,
|
||||
const uint8_t *mac);
|
||||
void ethernetif_force_full_recovery(const ip4_addr_t *ipaddr,
|
||||
const ip4_addr_t *netmask,
|
||||
const ip4_addr_t *gw,
|
||||
const uint8_t *mac);
|
||||
void ethernetif_force_link_down(void);
|
||||
uint16_t ethernetif_ch390_get_vendor_id(void);
|
||||
uint16_t ethernetif_ch390_get_product_id(void);
|
||||
uint8_t ethernetif_ch390_get_revision(void);
|
||||
uint8_t ethernetif_ch390_health_ok(void);
|
||||
uint8_t ethernetif_get_effective_mac(uint8_t *mac);
|
||||
|
||||
/**
|
||||
* @brief Query whether physical Ethernet link is currently up
|
||||
* @return 1 if link is up, 0 otherwise
|
||||
*/
|
||||
uint8_t ethernetif_link_is_up(void);
|
||||
|
||||
/**
|
||||
* @brief Process all pending RX packets
|
||||
|
||||
@@ -92,7 +92,7 @@ Revision: $Rev: 24316 $
|
||||
#endif
|
||||
|
||||
#ifndef BUFFER_SIZE_UP
|
||||
#define BUFFER_SIZE_UP (512) // Size of the buffer for terminal output of target, up to host (Default: 1k)
|
||||
#define BUFFER_SIZE_UP (2048) // Size of the buffer for terminal output of target, up to host (Default: 1k)
|
||||
#endif
|
||||
|
||||
#ifndef BUFFER_SIZE_DOWN
|
||||
@@ -100,7 +100,7 @@ Revision: $Rev: 24316 $
|
||||
#endif
|
||||
|
||||
#ifndef SEGGER_RTT_PRINTF_BUFFER_SIZE
|
||||
#define SEGGER_RTT_PRINTF_BUFFER_SIZE (64u) // Size of buffer for RTT printf to bulk-send chars via RTT (Default: 64)
|
||||
#define SEGGER_RTT_PRINTF_BUFFER_SIZE (128u) // Size of buffer for RTT printf to bulk-send chars via RTT (Default: 64)
|
||||
#endif
|
||||
|
||||
#ifndef SEGGER_RTT_MODE_DEFAULT
|
||||
|
||||
+3
-3
@@ -113,8 +113,8 @@ USE_HAL_DRIVER,STM32F103xE
|
||||
========================================
|
||||
|
||||
确认 ROM 和 RAM 配置正确:
|
||||
- IROM1: 0x08000000, Size: 0x40000 (256KB)
|
||||
- IRAM1: 0x20000000, Size: 0xC000 (48KB)
|
||||
- IROM1: 0x08000000, Size: 0x60000 (384KB)
|
||||
- IRAM1: 0x20000000, Size: 0x10000 (64KB)
|
||||
|
||||
========================================
|
||||
六、编译验证
|
||||
@@ -138,7 +138,7 @@ Debug 选项卡:
|
||||
|
||||
Utilities 选项卡:
|
||||
- 选择正确的 Flash 算法
|
||||
- STM32F10x High-density Flash (256KB)
|
||||
- STM32F10x High-density Flash (384KB / 0x60000)
|
||||
|
||||
========================================
|
||||
快速添加方法(可选)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+11
-11
@@ -14,16 +14,16 @@
|
||||
<uAC6>0</uAC6>
|
||||
<TargetOption>
|
||||
<TargetCommonOption>
|
||||
<Device>STM32F103RC</Device>
|
||||
<Device>STM32F103RD</Device>
|
||||
<Vendor>STMicroelectronics</Vendor>
|
||||
<PackID>Keil.STM32F1xx_DFP.2.4.1</PackID>
|
||||
<PackURL>https://www.keil.com/pack/</PackURL>
|
||||
<Cpu>IRAM(0x20000000,0x0000C000) IROM(0x08000000,0x00040000) CPUTYPE("Cortex-M3") CLOCK(12000000) ELITTLE</Cpu>
|
||||
<Cpu>IRAM(0x20000000,0x00010000) IROM(0x08000000,0x00060000) CPUTYPE("Cortex-M3") CLOCK(12000000) ELITTLE</Cpu>
|
||||
<FlashUtilSpec></FlashUtilSpec>
|
||||
<StartupFile></StartupFile>
|
||||
<FlashDriverDll>UL2CM3(-S0 -C0 -P0 -FD20000000 -FC1000 -FN1 -FF0STM32F10x_HD -FS08000000 -FL040000 -FP0($$Device:STM32F103RC$Flash\STM32F10x_HD.FLM))</FlashDriverDll>
|
||||
<FlashDriverDll>UL2CM3(-S0 -C0 -P0 -FD20000000 -FC1000 -FN1 -FF0STM32F10x_512 -FS08000000 -FL080000 -FP0($$Device:STM32F103RD$Flash\STM32F10x_512.FLM))</FlashDriverDll>
|
||||
<DeviceId>0</DeviceId>
|
||||
<RegisterFile>$$Device:STM32F103RC$Device\Include\stm32f10x.h</RegisterFile>
|
||||
<RegisterFile>$$Device:STM32F103RD$Device\Include\stm32f10x.h</RegisterFile>
|
||||
<MemoryEnv></MemoryEnv>
|
||||
<Cmp></Cmp>
|
||||
<Asm></Asm>
|
||||
@@ -33,7 +33,7 @@
|
||||
<SLE66CMisc></SLE66CMisc>
|
||||
<SLE66AMisc></SLE66AMisc>
|
||||
<SLE66LinkerMisc></SLE66LinkerMisc>
|
||||
<SFDFile>$$Device:STM32F103RC$SVD\STM32F103xx.svd</SFDFile>
|
||||
<SFDFile>$$Device:STM32F103RD$SVD\STM32F103xx.svd</SFDFile>
|
||||
<bCustSvd>0</bCustSvd>
|
||||
<UseEnv>0</UseEnv>
|
||||
<BinPath></BinPath>
|
||||
@@ -110,7 +110,7 @@
|
||||
</CommonProperty>
|
||||
<DllOption>
|
||||
<SimDllName>SARMCM3.DLL</SimDllName>
|
||||
<SimDllArguments>-REMAP</SimDllArguments>
|
||||
<SimDllArguments> -REMAP</SimDllArguments>
|
||||
<SimDlgDll>DCM.DLL</SimDlgDll>
|
||||
<SimDlgDllArguments>-pCM3</SimDlgDllArguments>
|
||||
<TargetDllName>SARMCM3.DLL</TargetDllName>
|
||||
@@ -138,7 +138,7 @@
|
||||
</Flash1>
|
||||
<bUseTDR>1</bUseTDR>
|
||||
<Flash2>BIN\UL2CM3.DLL</Flash2>
|
||||
<Flash3>"" ()</Flash3>
|
||||
<Flash3></Flash3>
|
||||
<Flash4></Flash4>
|
||||
<pFcarmOut></pFcarmOut>
|
||||
<pFcarmGrp></pFcarmGrp>
|
||||
@@ -247,12 +247,12 @@
|
||||
<IRAM>
|
||||
<Type>0</Type>
|
||||
<StartAddress>0x20000000</StartAddress>
|
||||
<Size>0xc000</Size>
|
||||
<Size>0x10000</Size>
|
||||
</IRAM>
|
||||
<IROM>
|
||||
<Type>1</Type>
|
||||
<StartAddress>0x8000000</StartAddress>
|
||||
<Size>0x40000</Size>
|
||||
<Size>0x60000</Size>
|
||||
</IROM>
|
||||
<XRAM>
|
||||
<Type>0</Type>
|
||||
@@ -277,7 +277,7 @@
|
||||
<OCR_RVCT4>
|
||||
<Type>1</Type>
|
||||
<StartAddress>0x8000000</StartAddress>
|
||||
<Size>0x40000</Size>
|
||||
<Size>0x60000</Size>
|
||||
</OCR_RVCT4>
|
||||
<OCR_RVCT5>
|
||||
<Type>1</Type>
|
||||
@@ -302,7 +302,7 @@
|
||||
<OCR_RVCT9>
|
||||
<Type>0</Type>
|
||||
<StartAddress>0x20000000</StartAddress>
|
||||
<Size>0xc000</Size>
|
||||
<Size>0x10000</Size>
|
||||
</OCR_RVCT9>
|
||||
<OCR_RVCT10>
|
||||
<Type>0</Type>
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
*** Using Compiler 'V5.06 update 7 (build 960)', folder: 'C:\Keil_v5\ARM\ARMCC\Bin'
|
||||
Build target 'TCP2UART'
|
||||
compiling tcp_client.c...
|
||||
linking...
|
||||
Program Size: Code=84560 RO-data=2496 RW-data=432 ZI-data=47056
|
||||
FromELF: creating hex file...
|
||||
"TCP2UART\TCP2UART.axf" - 0 Error(s), 0 Warning(s).
|
||||
Build Time Elapsed: 00:00:01
|
||||
@@ -39,7 +39,7 @@ __initial_sp
|
||||
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
|
||||
; </h>
|
||||
|
||||
Heap_Size EQU 0x200
|
||||
Heap_Size EQU 0x400
|
||||
|
||||
AREA HEAP, NOINIT, READWRITE, ALIGN=3
|
||||
__heap_base
|
||||
|
||||
+8
-8
@@ -70,11 +70,11 @@ FREERTOS.configCHECK_FOR_STACK_OVERFLOW=2
|
||||
FREERTOS.configMAX_PRIORITIES=7
|
||||
FREERTOS.configMINIMAL_STACK_SIZE=128
|
||||
FREERTOS.configSUPPORT_DYNAMIC_ALLOCATION=1
|
||||
FREERTOS.configSUPPORT_STATIC_ALLOCATION=0
|
||||
FREERTOS.configSUPPORT_STATIC_ALLOCATION=1
|
||||
FREERTOS.configTICK_RATE_HZ=1000
|
||||
FREERTOS.configTOTAL_HEAP_SIZE=10240
|
||||
FREERTOS.configTOTAL_HEAP_SIZE=15360
|
||||
FREERTOS.configUSE_COUNTING_SEMAPHORES=1
|
||||
FREERTOS.configUSE_IDLE_HOOK=0
|
||||
FREERTOS.configUSE_IDLE_HOOK=1
|
||||
FREERTOS.configUSE_MALLOC_FAILED_HOOK=1
|
||||
FREERTOS.configUSE_MUTEXES=1
|
||||
FREERTOS.configUSE_PREEMPTION=1
|
||||
@@ -83,7 +83,7 @@ FREERTOS.configUSE_TICK_HOOK=0
|
||||
File.Version=6
|
||||
GPIO.groupedBy=Group By Peripherals
|
||||
KeepUserPlacement=false
|
||||
Mcu.CPN=STM32F103RCT6
|
||||
Mcu.CPN=STM32F103RDT6
|
||||
Mcu.Family=STM32F1
|
||||
Mcu.IP0=DMA
|
||||
Mcu.IP1=FREERTOS
|
||||
@@ -123,7 +123,7 @@ Mcu.Pin20=VP_TIM4_VS_ClockSourceINT
|
||||
Mcu.PinsNb=21
|
||||
Mcu.ThirdPartyNb=0
|
||||
Mcu.UserConstants=
|
||||
Mcu.UserName=STM32F103RCTx
|
||||
Mcu.UserName=STM32F103RDTx
|
||||
MxCube.Version=6.16.1
|
||||
MxDb.Version=DB.6.0.161
|
||||
NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
|
||||
@@ -182,7 +182,7 @@ PC13-TAMPER-RTC.Signal=GPIO_Output
|
||||
PCC.Checker=false
|
||||
PCC.Line=STM32F103
|
||||
PCC.MCU=STM32F103R(C-D-E)Tx
|
||||
PCC.PartNumber=STM32F103RCTx
|
||||
PCC.PartNumber=STM32F103RDTx
|
||||
PCC.Series=STM32F1
|
||||
PCC.Temperature=25
|
||||
PCC.Vdd=3.3
|
||||
@@ -200,12 +200,12 @@ ProjectManager.CoupleFile=true
|
||||
ProjectManager.CustomerFirmwarePackage=
|
||||
ProjectManager.DefaultFWLocation=true
|
||||
ProjectManager.DeletePrevious=true
|
||||
ProjectManager.DeviceId=STM32F103RCTx
|
||||
ProjectManager.DeviceId=STM32F103RDTx
|
||||
ProjectManager.FirmwarePackage=STM32Cube FW_F1 V1.8.7
|
||||
ProjectManager.FreePins=false
|
||||
ProjectManager.FreePinsContext=
|
||||
ProjectManager.HalAssertFull=false
|
||||
ProjectManager.HeapSize=0x200
|
||||
ProjectManager.HeapSize=0x400
|
||||
ProjectManager.KeepUserCode=true
|
||||
ProjectManager.LastFirmware=true
|
||||
ProjectManager.LibraryCopy=0
|
||||
|
||||
@@ -26,15 +26,19 @@
|
||||
|
||||
1. 当前 Keil 工程目标仍是 `STM32F103RC`
|
||||
2. 当前代码可以真实构建通过
|
||||
3. 当前构建真值应查看:
|
||||
3. 当前 FreeRTOS + lwIP 固件版本线从 `V2.0.0` 开始,裸机固件版本线从 `V1.0.0` 开始
|
||||
4. 当前 RTOS 基线 release:`https://git.furtherverse.com/gaoro-xiao/TCP2UART/releases/tag/V2.0.0`
|
||||
5. Release 附件:
|
||||
- `TCP2UART-RTOS-V2.0.0.hex`
|
||||
- `TCP2UART-RTOS-V2.0.0.elf`
|
||||
6. 当前构建真值应查看:
|
||||
- `MDK-ARM/build_capture.txt`
|
||||
- `MDK-ARM/TCP2UART/TCP2UART.build_log.htm`
|
||||
- `MDK-ARM/TCP2UART/TCP2UART.map`
|
||||
4. 最近一次 Keil 构建示例:
|
||||
- `Code=84560`
|
||||
- `RW-data=432`
|
||||
- `ZI-data=47056`
|
||||
- `0 Error(s), 0 Warning(s)`
|
||||
7. `V2.0.0` release 构建验证:
|
||||
- `"TCP2UART\TCP2UART.axf" - 0 Error(s), 0 Warning(s).`
|
||||
- `TCP2UART-RTOS-V2.0.0.hex` 用于烧录
|
||||
- `TCP2UART-RTOS-V2.0.0.elf` 用于符号/调试
|
||||
|
||||
### 3.2 当前调试结论摘要
|
||||
|
||||
|
||||
@@ -94,6 +94,11 @@
|
||||
2. 启动文件:`startup_stm32f103xe.s`
|
||||
3. 目标器件:`STM32F103RC`
|
||||
4. 预处理器宏:`USE_HAL_DRIVER, STM32F103xE`
|
||||
5. FreeRTOS + lwIP 固件版本线从 `V2.0.0` 开始;裸机固件版本线从 `V1.0.0` 开始。
|
||||
6. 当前 RTOS release:`https://git.furtherverse.com/gaoro-xiao/TCP2UART/releases/tag/V2.0.0`
|
||||
7. Release 附件命名:
|
||||
- `TCP2UART-RTOS-V2.0.0.hex`
|
||||
- `TCP2UART-RTOS-V2.0.0.elf`
|
||||
|
||||
### 4.3 常用调试工具
|
||||
|
||||
@@ -448,3 +453,22 @@ case ETHTYPE_ARP:
|
||||
3. 不要忽略 `DIAG_TASK_ISOLATION=1 正常、=0 异常` 这个前提
|
||||
4. 不要一次性修改 `C1/S1/CH390/lwIP` 多个方向,避免再次失去因果关系
|
||||
5. 每次只做一个能明显改变故障边界的最小改动,并用 RTT + Keil build 结果交叉验证
|
||||
|
||||
### 11.8 固定 Client 端口重连策略(TIME_WAIT 取舍)
|
||||
|
||||
当前工程的 `Client` 链路保留固定 `LPORT` 配置语义,默认 `C1/C2` 均使用明确的本地源端口。
|
||||
|
||||
在 `lwIP + netconn` 路径下,如果仍沿用优雅 `netconn_close()`,则相同 `LPORT` 的快速重连会受到 TCP `TIME_WAIT` 影响,表现为一段时间内重复 `bind/connect` 失败。
|
||||
|
||||
结合本项目的约束,当前版本固化如下取舍:
|
||||
|
||||
1. 不取消 `Client` 固定 `LPORT` 语义
|
||||
2. 不依赖扩大 PCB 池作为主修复手段
|
||||
3. 不通过降低 `TCP_MSL` 改写全局 TCP 保守语义
|
||||
4. 对 `Client` 主动断开后的释放路径采用 abortive close(RST),以立即释放 PCB 与本地端口
|
||||
|
||||
使用该策略时应明确接受以下副作用:
|
||||
|
||||
1. 对端可能看到 `RST` 或“连接被重置”
|
||||
2. 连接尾部未完成发送的数据不会再走优雅关闭路径
|
||||
3. 该策略仅用于固定 `Client` 端口快速重连场景,不应直接推广到所有 TCP 关闭路径
|
||||
|
||||
@@ -365,6 +365,14 @@ EN,LPORT,RIP,RPORT,UART
|
||||
5. 内部 ring buffer 可取消(netconn 内部已有 pbuf 缓冲)
|
||||
6. 每个 TCP 任务内直接处理路由,通过 Queue 指针传递数据
|
||||
|
||||
补充约束(当前实现口径):
|
||||
|
||||
1. `Client` 链路保留固定 `LPORT` 配置语义,以满足产品侧对固定源端口的依赖
|
||||
2. 在 `lwIP + netconn` 模型下,若 `Client` 继续使用优雅 `netconn_close()`,相同本地端口的快速重连会受 `TIME_WAIT` 影响
|
||||
3. 因此当前工程对 `Client` 会话结束后的释放路径采用 abortive close(`tcp_abort` / RST)以立即释放 PCB 与本地端口
|
||||
4. 该策略只针对 `Client` 固定端口重连路径,不扩展到 `Server` listener 或一般被动关闭场景
|
||||
5. 该策略的已知代价是:对端可能看到 `RST`,且尾部未完成发送的数据不会再走优雅 `FIN/ACK` 收尾
|
||||
|
||||
### 7.4 FreeRTOS 初始化 `freertos.c`
|
||||
|
||||
CubeMX 生成的 FreeRTOS 初始化文件,职责:
|
||||
@@ -520,8 +528,8 @@ FreeRTOS 可管理的中断优先级必须 >= `configMAX_SYSCALL_INTERRUPT_PRIOR
|
||||
| Flash | 256 KB | 384 KB |
|
||||
| SRAM | 48 KB | 64 KB |
|
||||
| 引脚 | LQFP64 | LQFP64(完全兼容) |
|
||||
| 启动文件 | `startup_stm32f103xe.s` | `startup_stm32f103xd.s` |
|
||||
| 宏定义 | `STM32F103xE` | `STM32F103xD` |
|
||||
| 启动文件 | `startup_stm32f103xe.s` | `startup_stm32f103xe.s` |
|
||||
| 宏定义 | `STM32F103xE` | `STM32F103xE` |
|
||||
| Flash 算法 | `STM32F10x_HD` | `STM32F10x_HD`(相同) |
|
||||
| SRAM 大小 | `0xC000` | `0x10000` |
|
||||
| Flash 大小 | `0x40000` | `0x60000` |
|
||||
|
||||
Reference in New Issue
Block a user