Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d5b2506269 | |||
| 6f4ba247a4 | |||
| ac04bfc923 | |||
| da0f8ef72c | |||
| 9fd748c512 | |||
| fd1fae8ad7 | |||
| 1ef1ba9490 | |||
| 81594c6520 | |||
| 1808f9916f | |||
| 14a532290d |
@@ -28,6 +28,9 @@ Release/
|
|||||||
MDK-ARM/DebugConfig/
|
MDK-ARM/DebugConfig/
|
||||||
MDK-ARM/TCP2UART/
|
MDK-ARM/TCP2UART/
|
||||||
|
|
||||||
|
# CMake build
|
||||||
|
build/
|
||||||
|
|
||||||
# OS
|
# OS
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
Desktop.ini
|
Desktop.ini
|
||||||
@@ -38,3 +41,6 @@ Desktop.ini
|
|||||||
|
|
||||||
# Local debug handoff logs
|
# Local debug handoff logs
|
||||||
.debug/
|
.debug/
|
||||||
|
|
||||||
|
# Local packet captures
|
||||||
|
WiresharkLog/
|
||||||
|
|||||||
+387
@@ -0,0 +1,387 @@
|
|||||||
|
# TCP2UART AT 固件使用手册
|
||||||
|
|
||||||
|
## 1. 文档范围
|
||||||
|
|
||||||
|
本文档定义 `TCP2UART` 项目的最终 AT 外部协议。
|
||||||
|
|
||||||
|
本文档只描述最终协议模型,不保留任何历史展开式实例字段,不包含测试记录,不讨论旧版兼容命令。
|
||||||
|
|
||||||
|
适用对象:
|
||||||
|
|
||||||
|
- 上位机开发人员
|
||||||
|
- 联调与测试人员
|
||||||
|
- 固件接口实现人员
|
||||||
|
|
||||||
|
## 2. 设备与接口
|
||||||
|
|
||||||
|
- 主控:`STM32F103R8T6`
|
||||||
|
- 以太网芯片:`CH390D`
|
||||||
|
- 配置口:`USART1`
|
||||||
|
- 数据口:`USART2`、`USART3`
|
||||||
|
|
||||||
|
职责划分:
|
||||||
|
|
||||||
|
- `USART1`:AT 配置口
|
||||||
|
- `USART2 / USART3`:业务数据口,可工作于普通透传或 MUX 透传模式
|
||||||
|
|
||||||
|
## 3. 最终协议模型
|
||||||
|
|
||||||
|
本项目最终控制协议由三部分组成:
|
||||||
|
|
||||||
|
1. `MUX`:全局数据承载模式开关
|
||||||
|
2. `NET`:全局静态网络配置记录
|
||||||
|
3. `LINK[idx]`:按索引组织的链路配置记录
|
||||||
|
|
||||||
|
约束如下:
|
||||||
|
|
||||||
|
- 设备只有一张网卡,因此本地网络参数只配置一次
|
||||||
|
- DHCP 不属于最终协议范围
|
||||||
|
- 所有 AT 文本命令必须以 `\r\n` 结尾
|
||||||
|
- 当 `DSTMASK=0x00` 时,MUX 数据口中的系统控制帧进入 AT 解析路径,其控制文本同样必须以 `\r\n` 结尾
|
||||||
|
|
||||||
|
## 4. MUX 帧格式
|
||||||
|
|
||||||
|
当 `MUX=1` 时,数据口使用如下帧格式:
|
||||||
|
|
||||||
|
```text
|
||||||
|
SYNC | LEN_H | LEN_L | SRCID | DSTMASK | PAYLOAD | TAIL
|
||||||
|
```
|
||||||
|
|
||||||
|
字段定义:
|
||||||
|
|
||||||
|
- `SYNC`:帧起始标记,建议固定为 `0x7E`
|
||||||
|
- `LEN_H / LEN_L`:`PAYLOAD` 长度,高字节在前
|
||||||
|
- `SRCID`:单字节源端点 ID
|
||||||
|
- `DSTMASK`:单字节目标端点位图
|
||||||
|
- `PAYLOAD`:负载数据
|
||||||
|
- `TAIL`:帧结束标记,建议固定为 `0x7F`
|
||||||
|
|
||||||
|
规则:
|
||||||
|
|
||||||
|
- `DSTMASK != 0x00`:业务数据帧
|
||||||
|
- `DSTMASK = 0x00`:系统控制帧
|
||||||
|
- 系统控制帧的 `PAYLOAD` 为 AT 文本,必须以 `\r\n` 结束
|
||||||
|
|
||||||
|
## 5. 统一端点编码
|
||||||
|
|
||||||
|
`UART` 与 `TCP` 逻辑实例统一进入同一套编码空间:
|
||||||
|
|
||||||
|
| 端点 | 编码 |
|
||||||
|
|------|------|
|
||||||
|
| `C1` | `0x01` |
|
||||||
|
| `C2` | `0x02` |
|
||||||
|
| `UART2` | `0x04` |
|
||||||
|
| `UART3` | `0x08` |
|
||||||
|
| `S1` | `0x10` |
|
||||||
|
| `S2` | `0x20` |
|
||||||
|
|
||||||
|
说明:
|
||||||
|
|
||||||
|
- `SRCID` 必须为单一端点值
|
||||||
|
- `DSTMASK` 可以是一个或多个端点编码按位或的结果
|
||||||
|
- `DSTMASK=0x00` 保留给系统控制帧
|
||||||
|
|
||||||
|
## 6. AT 命令总则
|
||||||
|
|
||||||
|
### 6.1 命令结尾
|
||||||
|
|
||||||
|
所有 AT 命令均必须以 `\r\n` 结尾。
|
||||||
|
|
||||||
|
例如:
|
||||||
|
|
||||||
|
```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
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 持久化规则
|
||||||
|
|
||||||
|
参数设置成功后只写入当前运行配置,不会自动写入 Flash。
|
||||||
|
|
||||||
|
若要掉电保持,必须执行:
|
||||||
|
|
||||||
|
1. `AT+SAVE\r\n`
|
||||||
|
2. `AT+RESET\r\n`
|
||||||
|
|
||||||
|
### 6.3 响应风格
|
||||||
|
|
||||||
|
- 成功:`OK`
|
||||||
|
- 需要保存后生效时,允许追加提示文本
|
||||||
|
- 失败:`ERROR: <reason>`
|
||||||
|
|
||||||
|
## 7. 默认配置
|
||||||
|
|
||||||
|
### 7.1 MUX 默认值
|
||||||
|
|
||||||
|
```text
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.3 LINK 默认值
|
||||||
|
|
||||||
|
```text
|
||||||
|
LINK0 = 1,8080,0.0.0.0,0,U0
|
||||||
|
LINK1 = 0,8081,0.0.0.0,0,U1
|
||||||
|
LINK2 = 1,9001,192.168.1.200,9000,U1
|
||||||
|
LINK3 = 0,9002,192.168.1.201,9001,U0
|
||||||
|
```
|
||||||
|
|
||||||
|
固定索引映射:
|
||||||
|
|
||||||
|
- `0 = S1`
|
||||||
|
- `1 = S2`
|
||||||
|
- `2 = C1`
|
||||||
|
- `3 = C2`
|
||||||
|
|
||||||
|
UART 记号约定:
|
||||||
|
|
||||||
|
- `U0 = USART2`
|
||||||
|
- `U1 = USART3`
|
||||||
|
|
||||||
|
## 8. AT 命令定义
|
||||||
|
|
||||||
|
### 8.1 测试设备在线
|
||||||
|
|
||||||
|
命令:
|
||||||
|
|
||||||
|
```text
|
||||||
|
AT\r\n
|
||||||
|
```
|
||||||
|
|
||||||
|
返回:
|
||||||
|
|
||||||
|
```text
|
||||||
|
OK
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.2 查询摘要
|
||||||
|
|
||||||
|
命令:
|
||||||
|
|
||||||
|
```text
|
||||||
|
AT+?\r\n
|
||||||
|
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
|
||||||
|
+LINK:0,EN=1,LPORT=8080,RIP=0.0.0.0,RPORT=0,UART=U0
|
||||||
|
+LINK:1,EN=0,LPORT=8081,RIP=0.0.0.0,RPORT=0,UART=U1
|
||||||
|
+LINK:2,EN=1,LPORT=9001,RIP=192.168.1.200,RPORT=9000,UART=U1
|
||||||
|
+LINK:3,EN=0,LPORT=9002,RIP=192.168.1.201,RPORT=9001,UART=U0
|
||||||
|
+MUX:0
|
||||||
|
+MAP:UART2=0x04,UART3=0x08,C1=0x01,C2=0x02,S1=0x10,S2=0x20
|
||||||
|
+BAUD:U0=115200,U1=115200
|
||||||
|
OK
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.3 MUX 类命令
|
||||||
|
|
||||||
|
#### 设置 MUX
|
||||||
|
|
||||||
|
```text
|
||||||
|
AT+MUX=1\r\n
|
||||||
|
```
|
||||||
|
|
||||||
|
参数:
|
||||||
|
|
||||||
|
- `0`:普通透传模式
|
||||||
|
- `1`:MUX 透传模式
|
||||||
|
|
||||||
|
查询:
|
||||||
|
|
||||||
|
```text
|
||||||
|
AT+MUX?\r\n
|
||||||
|
```
|
||||||
|
|
||||||
|
返回示例:
|
||||||
|
|
||||||
|
```text
|
||||||
|
+MUX:1
|
||||||
|
OK
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.4 NET 类命令
|
||||||
|
|
||||||
|
#### 设置 NET
|
||||||
|
|
||||||
|
```text
|
||||||
|
AT+NET=192.168.1.100,255.255.255.0,192.168.1.1,02:00:00:00:00:01\r\n
|
||||||
|
```
|
||||||
|
|
||||||
|
字段顺序:
|
||||||
|
|
||||||
|
```text
|
||||||
|
IP,MASK,GW,MAC
|
||||||
|
```
|
||||||
|
|
||||||
|
查询:
|
||||||
|
|
||||||
|
```text
|
||||||
|
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
|
||||||
|
OK
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.5 LINK 类命令
|
||||||
|
|
||||||
|
#### 设置单条 LINK 记录
|
||||||
|
|
||||||
|
```text
|
||||||
|
AT+LINK=0,1,8080,0.0.0.0,0,U0\r\n
|
||||||
|
AT+LINK=2,1,9001,192.168.1.200,9000,U1\r\n
|
||||||
|
```
|
||||||
|
|
||||||
|
字段顺序:
|
||||||
|
|
||||||
|
```text
|
||||||
|
IDX,EN,LPORT,RIP,RPORT,UART
|
||||||
|
```
|
||||||
|
|
||||||
|
字段说明:
|
||||||
|
|
||||||
|
- `IDX`:实例索引,固定为 `0..3`
|
||||||
|
- `EN`:`0/1`
|
||||||
|
- `LPORT`:本地端口
|
||||||
|
- `RIP`:对端 IP
|
||||||
|
- `RPORT`:对端端口
|
||||||
|
- `UART`:`U0/U1`
|
||||||
|
|
||||||
|
说明:
|
||||||
|
|
||||||
|
- `Server` 与 `Client` 共用同一条 `LINK` 记录模型
|
||||||
|
- `Server` 中 `RIP/RPORT` 可作为允许接入的对端约束或预设对端信息
|
||||||
|
- `Client` 中 `RIP/RPORT` 表示远端目标地址与端口
|
||||||
|
|
||||||
|
#### 查询单条 LINK
|
||||||
|
|
||||||
|
```text
|
||||||
|
AT+LINK=0\r\n
|
||||||
|
```
|
||||||
|
|
||||||
|
返回示例:
|
||||||
|
|
||||||
|
```text
|
||||||
|
+LINK:0,EN=1,LPORT=8080,RIP=0.0.0.0,RPORT=0,UART=U0
|
||||||
|
OK
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 查询全部 LINK
|
||||||
|
|
||||||
|
```text
|
||||||
|
AT+LINK?\r\n
|
||||||
|
```
|
||||||
|
|
||||||
|
返回示例:
|
||||||
|
|
||||||
|
```text
|
||||||
|
+LINK:0,EN=1,LPORT=8080,RIP=0.0.0.0,RPORT=0,UART=U0
|
||||||
|
+LINK:1,EN=0,LPORT=8081,RIP=0.0.0.0,RPORT=0,UART=U1
|
||||||
|
+LINK:2,EN=1,LPORT=9001,RIP=192.168.1.200,RPORT=9000,UART=U1
|
||||||
|
+LINK:3,EN=0,LPORT=9002,RIP=192.168.1.201,RPORT=9001,UART=U0
|
||||||
|
OK
|
||||||
|
```
|
||||||
|
|
||||||
|
## 9. 保存与复位命令
|
||||||
|
|
||||||
|
### 9.1 保存配置
|
||||||
|
|
||||||
|
```text
|
||||||
|
AT+SAVE\r\n
|
||||||
|
```
|
||||||
|
|
||||||
|
成功返回:
|
||||||
|
|
||||||
|
```text
|
||||||
|
OK: Configuration saved
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9.2 软件复位
|
||||||
|
|
||||||
|
```text
|
||||||
|
AT+RESET\r\n
|
||||||
|
```
|
||||||
|
|
||||||
|
返回:
|
||||||
|
|
||||||
|
```text
|
||||||
|
OK: Resetting...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9.3 恢复默认值
|
||||||
|
|
||||||
|
```text
|
||||||
|
AT+DEFAULT\r\n
|
||||||
|
```
|
||||||
|
|
||||||
|
返回:
|
||||||
|
|
||||||
|
```text
|
||||||
|
OK: Defaults restored
|
||||||
|
```
|
||||||
|
|
||||||
|
## 10. 常见错误返回
|
||||||
|
|
||||||
|
| 场景 | 返回 |
|
||||||
|
|------|------|
|
||||||
|
| 未知命令 | `ERROR: Unknown command` |
|
||||||
|
| 非法端口 | `ERROR: Invalid port` |
|
||||||
|
| 非法波特率 | `ERROR: Invalid baudrate` |
|
||||||
|
| 非法 IP 地址 | `ERROR: Invalid IP format` |
|
||||||
|
| 非法掩码 | `ERROR: Invalid mask format` |
|
||||||
|
| 非法网关 | `ERROR: Invalid gateway format` |
|
||||||
|
| 非法远端 IP | `ERROR: Invalid remote IP format` |
|
||||||
|
| 非法 MAC | `ERROR: Invalid MAC format` |
|
||||||
|
| 非法 `SRCID` / `DSTMASK` | `ERROR: Invalid route field` |
|
||||||
|
| Flash 保存失败 | `ERROR: Save failed` |
|
||||||
|
|
||||||
|
## 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+LINK=0,1,10001,0.0.0.0,0,U1\r\n
|
||||||
|
AT+LINK=1,1,10003,0.0.0.0,0,U1\r\n
|
||||||
|
AT+LINK=2,1,20001,192.168.1.201,10002,U0\r\n
|
||||||
|
AT+MUX=1\r\n
|
||||||
|
AT+SAVE\r\n
|
||||||
|
AT+RESET\r\n
|
||||||
|
```
|
||||||
|
|
||||||
|
## 12. 故障排查建议
|
||||||
|
|
||||||
|
### 12.1 发送 `AT` 没有返回
|
||||||
|
|
||||||
|
优先检查:
|
||||||
|
|
||||||
|
1. 是否连接到 `USART1`
|
||||||
|
2. 串口参数是否为 `115200 8N1`
|
||||||
|
3. 是否严格使用 `\r\n` 作为命令结尾
|
||||||
|
4. 接线是否正确
|
||||||
|
5. 设备是否正常上电运行
|
||||||
|
|
||||||
|
### 12.2 设置成功但重启后参数丢失
|
||||||
|
|
||||||
|
检查是否漏掉以下步骤:
|
||||||
|
|
||||||
|
1. `AT+SAVE\r\n`
|
||||||
|
2. `AT+RESET\r\n`
|
||||||
|
|
||||||
|
## 13. 相关文件
|
||||||
|
|
||||||
|
- AT 命令实现:[config.c](/D:/code/STM32Project/TCP2UART/App/config.c)
|
||||||
|
- 配置结构与默认值:[config.h](/D:/code/STM32Project/TCP2UART/App/config.h)
|
||||||
|
- 调试与测试记录:[uart-ch390-debug-handoff.md](/D:/code/STM32Project/TCP2UART/uart-ch390-debug-handoff.md)
|
||||||
+532
-198
@@ -1,32 +1,61 @@
|
|||||||
/**
|
/**
|
||||||
* @file config.c
|
* @file config.c
|
||||||
* @brief Bare-metal AT command configuration module implementation.
|
* @brief Bare-metal final AT configuration module implementation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "flash_param.h"
|
#include "flash_param.h"
|
||||||
#include "usart.h"
|
#include "../Core/Inc/usart.h"
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "SEGGER_RTT.h"
|
#define CONFIG_TX_BUFFER_SIZE 512u
|
||||||
|
#define CONFIG_CMD_MAX_LEN 160u
|
||||||
#define CONFIG_TX_BUFFER_SIZE 256u
|
|
||||||
#define CONFIG_CMD_MAX_LEN 128u
|
|
||||||
|
|
||||||
#define CONFIG_UART_HANDLE huart1
|
#define CONFIG_UART_HANDLE huart1
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t magic;
|
||||||
|
uint16_t version;
|
||||||
|
uint16_t reserved;
|
||||||
|
uint8_t mac[6];
|
||||||
|
uint8_t dhcp_enable;
|
||||||
|
uint8_t reserved2;
|
||||||
|
uint8_t ip[4];
|
||||||
|
uint8_t mask[4];
|
||||||
|
uint8_t gw[4];
|
||||||
|
uint16_t server_port;
|
||||||
|
uint16_t reserved3;
|
||||||
|
uint8_t remote_ip[4];
|
||||||
|
uint16_t remote_port;
|
||||||
|
uint16_t reconnect_interval;
|
||||||
|
uint32_t uart2_baudrate;
|
||||||
|
uint32_t uart3_baudrate;
|
||||||
|
uint8_t uart2_databits;
|
||||||
|
uint8_t uart2_stopbits;
|
||||||
|
uint8_t uart2_parity;
|
||||||
|
uint8_t uart3_databits;
|
||||||
|
uint8_t uart3_stopbits;
|
||||||
|
uint8_t uart3_parity;
|
||||||
|
uint16_t reserved4;
|
||||||
|
uint32_t crc;
|
||||||
|
} legacy_device_config_v2_t;
|
||||||
|
|
||||||
static device_config_t g_config;
|
static device_config_t g_config;
|
||||||
static volatile bool g_reset_requested;
|
static volatile bool g_reset_requested;
|
||||||
static char g_uart_cmd_buffer[CONFIG_CMD_MAX_LEN];
|
static uint8_t g_uart_cmd_buffer[CONFIG_CMD_MAX_LEN];
|
||||||
static uint16_t g_uart_cmd_len;
|
static uint16_t g_uart_cmd_len;
|
||||||
|
static bool g_uart_rx_seen_cr;
|
||||||
static char g_pending_cmd_buffer[CONFIG_CMD_MAX_LEN];
|
static char g_pending_cmd_buffer[CONFIG_CMD_MAX_LEN];
|
||||||
static volatile uint16_t g_pending_cmd_len;
|
static volatile uint16_t g_pending_cmd_len;
|
||||||
static volatile bool g_pending_cmd_ready;
|
static volatile bool g_pending_cmd_ready;
|
||||||
|
static char g_at_response_buffer[CONFIG_TX_BUFFER_SIZE];
|
||||||
|
static char g_cmd_parse_buffer[CONFIG_CMD_MAX_LEN];
|
||||||
|
static char g_cmd_work_buffer[CONFIG_CMD_MAX_LEN];
|
||||||
|
|
||||||
static uint32_t config_calc_crc(const device_config_t *cfg)
|
static uint32_t config_calc_crc(const device_config_t *cfg)
|
||||||
{
|
{
|
||||||
@@ -56,8 +85,12 @@ static bool equals_ignore_case(const char *a, const char *b)
|
|||||||
char c1 = *a++;
|
char c1 = *a++;
|
||||||
char c2 = *b++;
|
char c2 = *b++;
|
||||||
|
|
||||||
if (c1 >= 'a' && c1 <= 'z') c1 -= 32;
|
if (c1 >= 'a' && c1 <= 'z') {
|
||||||
if (c2 >= 'a' && c2 <= 'z') c2 -= 32;
|
c1 -= 32;
|
||||||
|
}
|
||||||
|
if (c2 >= 'a' && c2 <= 'z') {
|
||||||
|
c2 -= 32;
|
||||||
|
}
|
||||||
|
|
||||||
if (c1 != c2) {
|
if (c1 != c2) {
|
||||||
return false;
|
return false;
|
||||||
@@ -67,12 +100,31 @@ static bool equals_ignore_case(const char *a, const char *b)
|
|||||||
return (*a == '\0' && *b == '\0');
|
return (*a == '\0' && *b == '\0');
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_baudrate_value(const char *value, uint32_t *baudrate)
|
static int prefix_equals_ignore_case(const char *str, const char *prefix)
|
||||||
|
{
|
||||||
|
while (*prefix != '\0') {
|
||||||
|
char c1 = *str++;
|
||||||
|
char c2 = *prefix++;
|
||||||
|
|
||||||
|
if (c1 >= 'a' && c1 <= 'z') {
|
||||||
|
c1 -= 32;
|
||||||
|
}
|
||||||
|
if (c2 >= 'a' && c2 <= 'z') {
|
||||||
|
c2 -= 32;
|
||||||
|
}
|
||||||
|
if (c1 != c2) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_u32_value(const char *value, uint32_t min_value, uint32_t max_value, uint32_t *parsed_value)
|
||||||
{
|
{
|
||||||
char *endptr;
|
char *endptr;
|
||||||
unsigned long parsed;
|
unsigned long parsed;
|
||||||
|
|
||||||
if (value == NULL || baudrate == NULL) {
|
if (value == NULL || parsed_value == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,54 +132,262 @@ static int parse_baudrate_value(const char *value, uint32_t *baudrate)
|
|||||||
if (endptr == value || *skip_whitespace(endptr) != '\0') {
|
if (endptr == value || *skip_whitespace(endptr) != '\0') {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if (parsed < min_value || parsed > max_value) {
|
||||||
if (parsed < 1200ul || parsed > 921600ul) {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
*baudrate = (uint32_t)parsed;
|
*parsed_value = (uint32_t)parsed;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static at_result_t handle_query(char *response, uint16_t max_len)
|
static int parse_link_uart(const char *value, uint8_t *uart)
|
||||||
|
{
|
||||||
|
if (equals_ignore_case(value, "U0")) {
|
||||||
|
*uart = LINK_UART_U0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (equals_ignore_case(value, "U1")) {
|
||||||
|
*uart = LINK_UART_U1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *link_uart_to_str(uint8_t uart)
|
||||||
|
{
|
||||||
|
return (uart == LINK_UART_U1) ? "U1" : "U0";
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parse_command_with_value(const char *cmd, const char *name, const char **value)
|
||||||
|
{
|
||||||
|
size_t name_len;
|
||||||
|
|
||||||
|
if (cmd == NULL || name == NULL || value == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
name_len = strlen(name);
|
||||||
|
if (!prefix_equals_ignore_case(cmd, name)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cmd[name_len] != '=') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*value = skip_whitespace(cmd + name_len + 1u);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *config_next_token(char **cursor)
|
||||||
|
{
|
||||||
|
char *start;
|
||||||
|
char *end;
|
||||||
|
|
||||||
|
if (cursor == NULL || *cursor == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
start = *cursor;
|
||||||
|
while (*start == ' ' || *start == '\t') {
|
||||||
|
++start;
|
||||||
|
}
|
||||||
|
if (*start == '\0') {
|
||||||
|
*cursor = NULL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
end = start;
|
||||||
|
while (*end != '\0' && *end != ',') {
|
||||||
|
++end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*end == ',') {
|
||||||
|
*end = '\0';
|
||||||
|
*cursor = end + 1;
|
||||||
|
} else {
|
||||||
|
*cursor = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
trim_trailing(start);
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_link_defaults(void)
|
||||||
|
{
|
||||||
|
static const uint8_t zero_ip[4] = {0u, 0u, 0u, 0u};
|
||||||
|
static const uint8_t c1_ip[4] = {192u, 168u, 1u, 200u};
|
||||||
|
static const uint8_t c2_ip[4] = {192u, 168u, 1u, 201u};
|
||||||
|
|
||||||
|
memset(g_config.links, 0, sizeof(g_config.links));
|
||||||
|
|
||||||
|
g_config.links[CONFIG_LINK_S1].enabled = 1u;
|
||||||
|
g_config.links[CONFIG_LINK_S1].uart = LINK_UART_U0;
|
||||||
|
g_config.links[CONFIG_LINK_S1].local_port = 8080u;
|
||||||
|
memcpy(g_config.links[CONFIG_LINK_S1].remote_ip, zero_ip, sizeof(zero_ip));
|
||||||
|
|
||||||
|
g_config.links[CONFIG_LINK_S2].enabled = 0u;
|
||||||
|
g_config.links[CONFIG_LINK_S2].uart = LINK_UART_U1;
|
||||||
|
g_config.links[CONFIG_LINK_S2].local_port = 8081u;
|
||||||
|
memcpy(g_config.links[CONFIG_LINK_S2].remote_ip, zero_ip, sizeof(zero_ip));
|
||||||
|
|
||||||
|
g_config.links[CONFIG_LINK_C1].enabled = 1u;
|
||||||
|
g_config.links[CONFIG_LINK_C1].uart = LINK_UART_U1;
|
||||||
|
g_config.links[CONFIG_LINK_C1].local_port = 9001u;
|
||||||
|
memcpy(g_config.links[CONFIG_LINK_C1].remote_ip, c1_ip, sizeof(c1_ip));
|
||||||
|
g_config.links[CONFIG_LINK_C1].remote_port = 9000u;
|
||||||
|
|
||||||
|
g_config.links[CONFIG_LINK_C2].enabled = 0u;
|
||||||
|
g_config.links[CONFIG_LINK_C2].uart = LINK_UART_U0;
|
||||||
|
g_config.links[CONFIG_LINK_C2].local_port = 9002u;
|
||||||
|
memcpy(g_config.links[CONFIG_LINK_C2].remote_ip, c2_ip, sizeof(c2_ip));
|
||||||
|
g_config.links[CONFIG_LINK_C2].remote_port = 9001u;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void migrate_legacy_config(const legacy_device_config_v2_t *legacy)
|
||||||
|
{
|
||||||
|
config_set_defaults();
|
||||||
|
|
||||||
|
memcpy(g_config.net.mac, legacy->mac, sizeof(g_config.net.mac));
|
||||||
|
memcpy(g_config.net.ip, legacy->ip, sizeof(g_config.net.ip));
|
||||||
|
memcpy(g_config.net.mask, legacy->mask, sizeof(g_config.net.mask));
|
||||||
|
memcpy(g_config.net.gw, legacy->gw, sizeof(g_config.net.gw));
|
||||||
|
g_config.uart_baudrate[0] = legacy->uart2_baudrate;
|
||||||
|
g_config.uart_baudrate[1] = legacy->uart3_baudrate;
|
||||||
|
g_config.mux_mode = MUX_MODE_RAW;
|
||||||
|
|
||||||
|
g_config.links[CONFIG_LINK_S1].enabled = (legacy->server_port != 0u) ? 1u : 0u;
|
||||||
|
g_config.links[CONFIG_LINK_S1].uart = LINK_UART_U0;
|
||||||
|
g_config.links[CONFIG_LINK_S1].local_port = legacy->server_port;
|
||||||
|
memset(g_config.links[CONFIG_LINK_S1].remote_ip, 0, sizeof(g_config.links[CONFIG_LINK_S1].remote_ip));
|
||||||
|
g_config.links[CONFIG_LINK_S1].remote_port = 0u;
|
||||||
|
|
||||||
|
g_config.links[CONFIG_LINK_S2].enabled = 0u;
|
||||||
|
|
||||||
|
g_config.links[CONFIG_LINK_C1].enabled = (legacy->remote_port != 0u) ? 1u : 0u;
|
||||||
|
g_config.links[CONFIG_LINK_C1].uart = LINK_UART_U1;
|
||||||
|
g_config.links[CONFIG_LINK_C1].local_port = 8081u;
|
||||||
|
memcpy(g_config.links[CONFIG_LINK_C1].remote_ip, legacy->remote_ip, sizeof(g_config.links[CONFIG_LINK_C1].remote_ip));
|
||||||
|
g_config.links[CONFIG_LINK_C1].remote_port = legacy->remote_port;
|
||||||
|
|
||||||
|
g_config.links[CONFIG_LINK_C2].enabled = 0u;
|
||||||
|
g_config.crc = config_calc_crc(&g_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool try_load_legacy_config(void)
|
||||||
|
{
|
||||||
|
legacy_device_config_v2_t legacy;
|
||||||
|
uint32_t expected_crc;
|
||||||
|
|
||||||
|
if (flash_param_read(&legacy, sizeof(legacy)) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
expected_crc = flash_param_crc32(&legacy, offsetof(legacy_device_config_v2_t, crc));
|
||||||
|
if (legacy.magic != CONFIG_MAGIC || legacy.version != 0x0002u || legacy.crc != expected_crc) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
migrate_legacy_config(&legacy);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static at_result_t handle_summary_query(char *response, uint16_t max_len)
|
||||||
{
|
{
|
||||||
char ip_str[16];
|
char ip_str[16];
|
||||||
char mask_str[16];
|
char mask_str[16];
|
||||||
char gw_str[16];
|
char gw_str[16];
|
||||||
char rip_str[16];
|
|
||||||
char mac_str[18];
|
char mac_str[18];
|
||||||
|
char rip_str[CONFIG_LINK_COUNT][16];
|
||||||
|
|
||||||
config_ip_to_str(g_config.ip, ip_str);
|
config_ip_to_str(g_config.net.ip, ip_str);
|
||||||
config_ip_to_str(g_config.mask, mask_str);
|
config_ip_to_str(g_config.net.mask, mask_str);
|
||||||
config_ip_to_str(g_config.gw, gw_str);
|
config_ip_to_str(g_config.net.gw, gw_str);
|
||||||
config_ip_to_str(g_config.remote_ip, rip_str);
|
config_mac_to_str(g_config.net.mac, mac_str);
|
||||||
config_mac_to_str(g_config.mac, mac_str);
|
for (uint32_t i = 0; i < CONFIG_LINK_COUNT; ++i) {
|
||||||
|
config_ip_to_str(g_config.links[i].remote_ip, rip_str[i]);
|
||||||
|
}
|
||||||
|
|
||||||
snprintf(response, max_len,
|
snprintf(response, max_len,
|
||||||
"MAC: %s\r\n"
|
"+NET:IP=%s,MASK=%s,GW=%s,MAC=%s\r\n"
|
||||||
"DHCP: %u\r\n"
|
"+LINK:0,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
|
||||||
"IP: %s\r\n"
|
"+LINK:1,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
|
||||||
"MASK: %s\r\n"
|
"+LINK:2,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
|
||||||
"GW: %s\r\n"
|
"+LINK:3,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
|
||||||
"PORT: %u\r\n"
|
"+MUX:%u\r\n"
|
||||||
"RIP: %s\r\n"
|
"+MAP:UART2=0x04,UART3=0x08,C1=0x01,C2=0x02,S1=0x10,S2=0x20\r\n"
|
||||||
"RPORT: %u\r\n"
|
"+BAUD:U0=%lu,U1=%lu\r\n"
|
||||||
"BAUD1: %lu\r\n"
|
"OK\r\n",
|
||||||
"BAUD2: %lu\r\n",
|
ip_str, mask_str, gw_str, mac_str,
|
||||||
mac_str,
|
g_config.links[0].enabled, g_config.links[0].local_port, rip_str[0], g_config.links[0].remote_port, link_uart_to_str(g_config.links[0].uart),
|
||||||
g_config.dhcp_enable,
|
g_config.links[1].enabled, g_config.links[1].local_port, rip_str[1], g_config.links[1].remote_port, link_uart_to_str(g_config.links[1].uart),
|
||||||
ip_str,
|
g_config.links[2].enabled, g_config.links[2].local_port, rip_str[2], g_config.links[2].remote_port, link_uart_to_str(g_config.links[2].uart),
|
||||||
mask_str,
|
g_config.links[3].enabled, g_config.links[3].local_port, rip_str[3], g_config.links[3].remote_port, link_uart_to_str(g_config.links[3].uart),
|
||||||
gw_str,
|
g_config.mux_mode,
|
||||||
g_config.server_port,
|
g_config.uart_baudrate[0],
|
||||||
rip_str,
|
g_config.uart_baudrate[1]);
|
||||||
g_config.remote_port,
|
|
||||||
g_config.uart2_baudrate,
|
|
||||||
g_config.uart3_baudrate);
|
|
||||||
|
|
||||||
return AT_OK;
|
return AT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static at_result_t handle_net_query(char *response, uint16_t max_len)
|
||||||
|
{
|
||||||
|
char ip_str[16];
|
||||||
|
char mask_str[16];
|
||||||
|
char gw_str[16];
|
||||||
|
char mac_str[18];
|
||||||
|
|
||||||
|
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);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static at_result_t handle_link_query(uint32_t index, char *response, uint16_t max_len)
|
||||||
|
{
|
||||||
|
char rip_str[16];
|
||||||
|
|
||||||
|
if (index >= CONFIG_LINK_COUNT) {
|
||||||
|
snprintf(response, max_len, "ERROR: Invalid route field\r\n");
|
||||||
|
return AT_INVALID_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
config_ip_to_str(g_config.links[index].remote_ip, rip_str);
|
||||||
|
snprintf(response,
|
||||||
|
max_len,
|
||||||
|
"+LINK:%lu,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\nOK\r\n",
|
||||||
|
index,
|
||||||
|
g_config.links[index].enabled,
|
||||||
|
g_config.links[index].local_port,
|
||||||
|
rip_str,
|
||||||
|
g_config.links[index].remote_port,
|
||||||
|
link_uart_to_str(g_config.links[index].uart));
|
||||||
|
return AT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static at_result_t handle_all_link_query(char *response, uint16_t max_len)
|
||||||
|
{
|
||||||
|
char rip_str[CONFIG_LINK_COUNT][16];
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < CONFIG_LINK_COUNT; ++i) {
|
||||||
|
config_ip_to_str(g_config.links[i].remote_ip, rip_str[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(response,
|
||||||
|
max_len,
|
||||||
|
"+LINK:0,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
|
||||||
|
"+LINK:1,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
|
||||||
|
"+LINK:2,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
|
||||||
|
"+LINK:3,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
|
||||||
|
"OK\r\n",
|
||||||
|
g_config.links[0].enabled, g_config.links[0].local_port, rip_str[0], g_config.links[0].remote_port, link_uart_to_str(g_config.links[0].uart),
|
||||||
|
g_config.links[1].enabled, g_config.links[1].local_port, rip_str[1], g_config.links[1].remote_port, link_uart_to_str(g_config.links[1].uart),
|
||||||
|
g_config.links[2].enabled, g_config.links[2].local_port, rip_str[2], g_config.links[2].remote_port, link_uart_to_str(g_config.links[2].uart),
|
||||||
|
g_config.links[3].enabled, g_config.links[3].local_port, rip_str[3], g_config.links[3].remote_port, link_uart_to_str(g_config.links[3].uart));
|
||||||
|
return AT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
int config_init(void)
|
int config_init(void)
|
||||||
{
|
{
|
||||||
flash_param_init();
|
flash_param_init();
|
||||||
@@ -136,17 +396,21 @@ int config_init(void)
|
|||||||
|
|
||||||
int config_load(void)
|
int config_load(void)
|
||||||
{
|
{
|
||||||
if (flash_param_read(&g_config, sizeof(g_config)) != 0 ||
|
if (flash_param_read(&g_config, sizeof(g_config)) == 0 &&
|
||||||
g_config.magic != CONFIG_MAGIC ||
|
g_config.magic == CONFIG_MAGIC &&
|
||||||
g_config.version != CONFIG_VERSION ||
|
g_config.version == CONFIG_VERSION &&
|
||||||
g_config.crc != config_calc_crc(&g_config)) {
|
g_config.crc == config_calc_crc(&g_config)) {
|
||||||
config_set_defaults();
|
return 0;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (try_load_legacy_config()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config_set_defaults();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
int config_save(void)
|
int config_save(void)
|
||||||
{
|
{
|
||||||
g_config.magic = CONFIG_MAGIC;
|
g_config.magic = CONFIG_MAGIC;
|
||||||
@@ -157,33 +421,22 @@ int config_save(void)
|
|||||||
|
|
||||||
void config_set_defaults(void)
|
void config_set_defaults(void)
|
||||||
{
|
{
|
||||||
const uint8_t default_ip[] = DEFAULT_IP;
|
const uint8_t default_ip[] = DEFAULT_NET_IP;
|
||||||
const uint8_t default_mask[] = DEFAULT_MASK;
|
const uint8_t default_mask[] = DEFAULT_NET_MASK;
|
||||||
const uint8_t default_gw[] = DEFAULT_GW;
|
const uint8_t default_gw[] = DEFAULT_NET_GW;
|
||||||
const uint8_t default_mac[] = DEFAULT_MAC;
|
const uint8_t default_mac[] = DEFAULT_NET_MAC;
|
||||||
const uint8_t default_rip[] = DEFAULT_REMOTE_IP;
|
|
||||||
|
|
||||||
memset(&g_config, 0, sizeof(g_config));
|
memset(&g_config, 0, sizeof(g_config));
|
||||||
g_config.magic = CONFIG_MAGIC;
|
g_config.magic = CONFIG_MAGIC;
|
||||||
g_config.version = CONFIG_VERSION;
|
g_config.version = CONFIG_VERSION;
|
||||||
memcpy(g_config.mac, default_mac, sizeof(g_config.mac));
|
g_config.mux_mode = MUX_MODE_RAW;
|
||||||
memcpy(g_config.ip, default_ip, sizeof(g_config.ip));
|
memcpy(g_config.net.ip, default_ip, sizeof(g_config.net.ip));
|
||||||
memcpy(g_config.mask, default_mask, sizeof(g_config.mask));
|
memcpy(g_config.net.mask, default_mask, sizeof(g_config.net.mask));
|
||||||
memcpy(g_config.gw, default_gw, sizeof(g_config.gw));
|
memcpy(g_config.net.gw, default_gw, sizeof(g_config.net.gw));
|
||||||
memcpy(g_config.remote_ip, default_rip, sizeof(g_config.remote_ip));
|
memcpy(g_config.net.mac, default_mac, sizeof(g_config.net.mac));
|
||||||
|
set_link_defaults();
|
||||||
g_config.dhcp_enable = DEFAULT_DHCP_ENABLE;
|
g_config.uart_baudrate[0] = DEFAULT_UART_BAUDRATE;
|
||||||
g_config.server_port = DEFAULT_SERVER_PORT;
|
g_config.uart_baudrate[1] = DEFAULT_UART_BAUDRATE;
|
||||||
g_config.remote_port = DEFAULT_REMOTE_PORT;
|
|
||||||
g_config.reconnect_interval = DEFAULT_RECONNECT_MS;
|
|
||||||
g_config.uart2_baudrate = DEFAULT_UART_BAUDRATE;
|
|
||||||
g_config.uart3_baudrate = DEFAULT_UART_BAUDRATE;
|
|
||||||
g_config.uart2_databits = DEFAULT_UART_DATABITS;
|
|
||||||
g_config.uart2_stopbits = DEFAULT_UART_STOPBITS;
|
|
||||||
g_config.uart2_parity = DEFAULT_UART_PARITY;
|
|
||||||
g_config.uart3_databits = DEFAULT_UART_DATABITS;
|
|
||||||
g_config.uart3_stopbits = DEFAULT_UART_STOPBITS;
|
|
||||||
g_config.uart3_parity = DEFAULT_UART_PARITY;
|
|
||||||
g_config.crc = config_calc_crc(&g_config);
|
g_config.crc = config_calc_crc(&g_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,50 +452,37 @@ device_config_t *config_get_mutable(void)
|
|||||||
|
|
||||||
at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_len)
|
at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_len)
|
||||||
{
|
{
|
||||||
char cmd_copy[CONFIG_CMD_MAX_LEN];
|
const char *value;
|
||||||
char *cmd_name;
|
|
||||||
char *value;
|
|
||||||
const char *p;
|
const char *p;
|
||||||
|
|
||||||
if (cmd == NULL || response == NULL || max_len == 0u) {
|
if (cmd == NULL || response == NULL || max_len == 0u) {
|
||||||
return AT_ERROR;
|
return AT_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
strncpy(cmd_copy, cmd, CONFIG_CMD_MAX_LEN - 1u);
|
strncpy(g_cmd_work_buffer, cmd, sizeof(g_cmd_work_buffer) - 1u);
|
||||||
cmd_copy[CONFIG_CMD_MAX_LEN - 1u] = '\0';
|
g_cmd_work_buffer[sizeof(g_cmd_work_buffer) - 1u] = '\0';
|
||||||
trim_trailing(cmd_copy);
|
trim_trailing(g_cmd_work_buffer);
|
||||||
p = skip_whitespace(cmd_copy);
|
p = skip_whitespace(g_cmd_work_buffer);
|
||||||
|
|
||||||
if ((p[0] != 'A' && p[0] != 'a') || (p[1] != 'T' && p[1] != 't')) {
|
if ((p[0] != 'A' && p[0] != 'a') || (p[1] != 'T' && p[1] != 't')) {
|
||||||
snprintf(response, max_len, "ERROR: Unknown command\r\n");
|
snprintf(response, max_len, "ERROR: Unknown command\r\n");
|
||||||
return AT_UNKNOWN_CMD;
|
return AT_UNKNOWN_CMD;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p[2] == '\0') {
|
if (p[2] == '\0') {
|
||||||
snprintf(response, max_len, "OK\r\n");
|
snprintf(response, max_len, "OK\r\n");
|
||||||
return AT_OK;
|
return AT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p[2] != '+') {
|
if (p[2] != '+') {
|
||||||
snprintf(response, max_len, "ERROR: Unknown command\r\n");
|
snprintf(response, max_len, "ERROR: Unknown command\r\n");
|
||||||
return AT_UNKNOWN_CMD;
|
return AT_UNKNOWN_CMD;
|
||||||
}
|
}
|
||||||
|
|
||||||
p += 3;
|
p += 3;
|
||||||
value = strchr((char *)p, '=');
|
|
||||||
if (value != NULL) {
|
|
||||||
*value = '\0';
|
|
||||||
++value;
|
|
||||||
value = (char *)skip_whitespace(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd_name = (char *)p;
|
if ((equals_ignore_case(p, "?") || equals_ignore_case(p, "QUERY"))) {
|
||||||
trim_trailing(cmd_name);
|
return handle_summary_query(response, max_len);
|
||||||
|
|
||||||
if ((equals_ignore_case(cmd_name, "?") || equals_ignore_case(cmd_name, "QUERY")) && value == NULL) {
|
|
||||||
return handle_query(response, max_len);
|
|
||||||
}
|
}
|
||||||
if (equals_ignore_case(cmd_name, "SAVE") && value == NULL) {
|
if (equals_ignore_case(p, "SAVE")) {
|
||||||
if (config_save() != 0) {
|
if (config_save() != 0) {
|
||||||
snprintf(response, max_len, "ERROR: Save failed\r\n");
|
snprintf(response, max_len, "ERROR: Save failed\r\n");
|
||||||
return AT_SAVE_FAILED;
|
return AT_SAVE_FAILED;
|
||||||
@@ -250,103 +490,139 @@ at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_
|
|||||||
snprintf(response, max_len, "OK: Configuration saved\r\n");
|
snprintf(response, max_len, "OK: Configuration saved\r\n");
|
||||||
return AT_OK;
|
return AT_OK;
|
||||||
}
|
}
|
||||||
if (equals_ignore_case(cmd_name, "RESET") && value == NULL) {
|
if (equals_ignore_case(p, "RESET")) {
|
||||||
g_reset_requested = true;
|
g_reset_requested = true;
|
||||||
snprintf(response, max_len, "OK: Resetting...\r\n");
|
snprintf(response, max_len, "OK: Resetting...\r\n");
|
||||||
return AT_OK;
|
return AT_OK;
|
||||||
}
|
}
|
||||||
if (equals_ignore_case(cmd_name, "DEFAULT") && value == NULL) {
|
if (equals_ignore_case(p, "DEFAULT")) {
|
||||||
config_set_defaults();
|
config_set_defaults();
|
||||||
snprintf(response, max_len, "OK: Defaults restored\r\n");
|
snprintf(response, max_len, "OK: Defaults restored\r\n");
|
||||||
return AT_OK;
|
return AT_OK;
|
||||||
}
|
}
|
||||||
if (equals_ignore_case(cmd_name, "IP") && value != NULL) {
|
if (equals_ignore_case(p, "MUX?")) {
|
||||||
if (config_str_to_ip(value, g_config.ip) != 0) {
|
snprintf(response, max_len, "+MUX:%u\r\nOK\r\n", g_config.mux_mode);
|
||||||
snprintf(response, max_len, "ERROR: Invalid IP format\r\n");
|
return AT_OK;
|
||||||
return AT_INVALID_PARAM;
|
|
||||||
}
|
}
|
||||||
snprintf(response, max_len, "OK\r\n");
|
if (parse_command_with_value(p, "MUX", &value)) {
|
||||||
return AT_NEED_REBOOT;
|
uint32_t mux_value;
|
||||||
}
|
if (parse_u32_value(value, 0u, 1u, &mux_value) != 0) {
|
||||||
if (equals_ignore_case(cmd_name, "MASK") && value != NULL) {
|
|
||||||
if (config_str_to_ip(value, g_config.mask) != 0) {
|
|
||||||
snprintf(response, max_len, "ERROR: Invalid mask format\r\n");
|
|
||||||
return AT_INVALID_PARAM;
|
|
||||||
}
|
|
||||||
snprintf(response, max_len, "OK\r\n");
|
|
||||||
return AT_NEED_REBOOT;
|
|
||||||
}
|
|
||||||
if (equals_ignore_case(cmd_name, "GW") && value != NULL) {
|
|
||||||
if (config_str_to_ip(value, g_config.gw) != 0) {
|
|
||||||
snprintf(response, max_len, "ERROR: Invalid gateway format\r\n");
|
|
||||||
return AT_INVALID_PARAM;
|
|
||||||
}
|
|
||||||
snprintf(response, max_len, "OK\r\n");
|
|
||||||
return AT_NEED_REBOOT;
|
|
||||||
}
|
|
||||||
if (equals_ignore_case(cmd_name, "RIP") && value != NULL) {
|
|
||||||
if (config_str_to_ip(value, g_config.remote_ip) != 0) {
|
|
||||||
snprintf(response, max_len, "ERROR: Invalid remote IP format\r\n");
|
|
||||||
return AT_INVALID_PARAM;
|
|
||||||
}
|
|
||||||
snprintf(response, max_len, "OK\r\n");
|
|
||||||
return AT_NEED_REBOOT;
|
|
||||||
}
|
|
||||||
if (equals_ignore_case(cmd_name, "MAC") && value != NULL) {
|
|
||||||
if (config_str_to_mac(value, g_config.mac) != 0) {
|
|
||||||
snprintf(response, max_len, "ERROR: Invalid MAC format\r\n");
|
|
||||||
return AT_INVALID_PARAM;
|
|
||||||
}
|
|
||||||
snprintf(response, max_len, "OK\r\n");
|
|
||||||
return AT_NEED_REBOOT;
|
|
||||||
}
|
|
||||||
if (equals_ignore_case(cmd_name, "PORT") && value != NULL) {
|
|
||||||
int port = atoi(value);
|
|
||||||
if (port < 1 || port > 65535) {
|
|
||||||
snprintf(response, max_len, "ERROR: Invalid port\r\n");
|
|
||||||
return AT_INVALID_PARAM;
|
|
||||||
}
|
|
||||||
g_config.server_port = (uint16_t)port;
|
|
||||||
snprintf(response, max_len, "OK\r\n");
|
|
||||||
return AT_NEED_REBOOT;
|
|
||||||
}
|
|
||||||
if (equals_ignore_case(cmd_name, "RPORT") && value != NULL) {
|
|
||||||
int port = atoi(value);
|
|
||||||
if (port < 1 || port > 65535) {
|
|
||||||
snprintf(response, max_len, "ERROR: Invalid port\r\n");
|
|
||||||
return AT_INVALID_PARAM;
|
|
||||||
}
|
|
||||||
g_config.remote_port = (uint16_t)port;
|
|
||||||
snprintf(response, max_len, "OK\r\n");
|
|
||||||
return AT_NEED_REBOOT;
|
|
||||||
}
|
|
||||||
if (equals_ignore_case(cmd_name, "BAUD1") && value != NULL) {
|
|
||||||
if (parse_baudrate_value(value, &g_config.uart2_baudrate) != 0) {
|
|
||||||
snprintf(response, max_len, "ERROR: Invalid baudrate\r\n");
|
|
||||||
return AT_INVALID_PARAM;
|
|
||||||
}
|
|
||||||
snprintf(response, max_len, "OK\r\n");
|
|
||||||
return AT_NEED_REBOOT;
|
|
||||||
}
|
|
||||||
if (equals_ignore_case(cmd_name, "BAUD2") && value != NULL) {
|
|
||||||
if (parse_baudrate_value(value, &g_config.uart3_baudrate) != 0) {
|
|
||||||
snprintf(response, max_len, "ERROR: Invalid baudrate\r\n");
|
|
||||||
return AT_INVALID_PARAM;
|
|
||||||
}
|
|
||||||
snprintf(response, max_len, "OK\r\n");
|
|
||||||
return AT_NEED_REBOOT;
|
|
||||||
}
|
|
||||||
if (equals_ignore_case(cmd_name, "DHCP") && value != NULL) {
|
|
||||||
int dhcp = atoi(value);
|
|
||||||
if (dhcp != 0 && dhcp != 1) {
|
|
||||||
snprintf(response, max_len, "ERROR: Invalid value\r\n");
|
snprintf(response, max_len, "ERROR: Invalid value\r\n");
|
||||||
return AT_INVALID_PARAM;
|
return AT_INVALID_PARAM;
|
||||||
}
|
}
|
||||||
if (dhcp != 0) {
|
g_config.mux_mode = (uint8_t)mux_value;
|
||||||
snprintf(response, max_len, "ERROR: DHCP disabled in this build\r\n");
|
snprintf(response, max_len, "OK\r\n");
|
||||||
|
return AT_NEED_REBOOT;
|
||||||
|
}
|
||||||
|
if (equals_ignore_case(p, "NET?")) {
|
||||||
|
return handle_net_query(response, max_len);
|
||||||
|
}
|
||||||
|
if (parse_command_with_value(p, "NET", &value)) {
|
||||||
|
char value_copy[96];
|
||||||
|
char *token;
|
||||||
|
char *cursor;
|
||||||
|
uint8_t ip[4];
|
||||||
|
uint8_t mask[4];
|
||||||
|
uint8_t gw[4];
|
||||||
|
uint8_t mac[6];
|
||||||
|
|
||||||
|
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 || config_str_to_ip(token, ip) != 0) {
|
||||||
|
snprintf(response, max_len, "ERROR: Invalid IP format\r\n");
|
||||||
return AT_INVALID_PARAM;
|
return AT_INVALID_PARAM;
|
||||||
}
|
}
|
||||||
g_config.dhcp_enable = (uint8_t)dhcp;
|
token = config_next_token(&cursor);
|
||||||
|
if (token == NULL || config_str_to_ip(token, mask) != 0) {
|
||||||
|
snprintf(response, max_len, "ERROR: Invalid mask format\r\n");
|
||||||
|
return AT_INVALID_PARAM;
|
||||||
|
}
|
||||||
|
token = config_next_token(&cursor);
|
||||||
|
if (token == NULL || config_str_to_ip(token, gw) != 0) {
|
||||||
|
snprintf(response, max_len, "ERROR: Invalid gateway format\r\n");
|
||||||
|
return AT_INVALID_PARAM;
|
||||||
|
}
|
||||||
|
token = config_next_token(&cursor);
|
||||||
|
if (token == NULL || config_str_to_mac(token, mac) != 0) {
|
||||||
|
snprintf(response, max_len, "ERROR: Invalid MAC format\r\n");
|
||||||
|
return AT_INVALID_PARAM;
|
||||||
|
}
|
||||||
|
if (config_next_token(&cursor) != NULL) {
|
||||||
|
snprintf(response, max_len, "ERROR: Invalid value\r\n");
|
||||||
|
return AT_INVALID_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(g_config.net.ip, ip, sizeof(ip));
|
||||||
|
memcpy(g_config.net.mask, mask, sizeof(mask));
|
||||||
|
memcpy(g_config.net.gw, gw, sizeof(gw));
|
||||||
|
memcpy(g_config.net.mac, mac, sizeof(mac));
|
||||||
|
snprintf(response, max_len, "OK\r\n");
|
||||||
|
return AT_NEED_REBOOT;
|
||||||
|
}
|
||||||
|
if (equals_ignore_case(p, "LINK?")) {
|
||||||
|
return handle_all_link_query(response, max_len);
|
||||||
|
}
|
||||||
|
if (parse_command_with_value(p, "LINK", &value)) {
|
||||||
|
char value_copy[96];
|
||||||
|
char *cursor;
|
||||||
|
char *token;
|
||||||
|
uint32_t index;
|
||||||
|
uint32_t enabled;
|
||||||
|
uint32_t local_port;
|
||||||
|
uint32_t remote_port;
|
||||||
|
uint8_t rip[4];
|
||||||
|
uint8_t uart;
|
||||||
|
|
||||||
|
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_u32_value(token, 0u, CONFIG_LINK_COUNT - 1u, &index) != 0) {
|
||||||
|
snprintf(response, max_len, "ERROR: Invalid route field\r\n");
|
||||||
|
return AT_INVALID_PARAM;
|
||||||
|
}
|
||||||
|
token = config_next_token(&cursor);
|
||||||
|
if (token == NULL) {
|
||||||
|
return handle_link_query(index, response, max_len);
|
||||||
|
}
|
||||||
|
if (parse_u32_value(token, 0u, 1u, &enabled) != 0) {
|
||||||
|
snprintf(response, max_len, "ERROR: Invalid value\r\n");
|
||||||
|
return AT_INVALID_PARAM;
|
||||||
|
}
|
||||||
|
token = config_next_token(&cursor);
|
||||||
|
if (token == NULL || parse_u32_value(token, 1u, 65535u, &local_port) != 0) {
|
||||||
|
snprintf(response, max_len, "ERROR: Invalid port\r\n");
|
||||||
|
return AT_INVALID_PARAM;
|
||||||
|
}
|
||||||
|
token = config_next_token(&cursor);
|
||||||
|
if (token == NULL || config_str_to_ip(token, rip) != 0) {
|
||||||
|
snprintf(response, max_len, "ERROR: Invalid remote IP format\r\n");
|
||||||
|
return AT_INVALID_PARAM;
|
||||||
|
}
|
||||||
|
token = config_next_token(&cursor);
|
||||||
|
if (token == NULL || parse_u32_value(token, 0u, 65535u, &remote_port) != 0) {
|
||||||
|
snprintf(response, max_len, "ERROR: Invalid port\r\n");
|
||||||
|
return AT_INVALID_PARAM;
|
||||||
|
}
|
||||||
|
token = config_next_token(&cursor);
|
||||||
|
if (token == NULL || parse_link_uart(token, &uart) != 0) {
|
||||||
|
snprintf(response, max_len, "ERROR: Invalid route field\r\n");
|
||||||
|
return AT_INVALID_PARAM;
|
||||||
|
}
|
||||||
|
if (config_next_token(&cursor) != NULL) {
|
||||||
|
snprintf(response, max_len, "ERROR: Invalid value\r\n");
|
||||||
|
return AT_INVALID_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_config.links[index].enabled = (uint8_t)enabled;
|
||||||
|
g_config.links[index].local_port = (uint16_t)local_port;
|
||||||
|
memcpy(g_config.links[index].remote_ip, rip, sizeof(rip));
|
||||||
|
g_config.links[index].remote_port = (uint16_t)remote_port;
|
||||||
|
g_config.links[index].uart = uart;
|
||||||
snprintf(response, max_len, "OK\r\n");
|
snprintf(response, max_len, "OK\r\n");
|
||||||
return AT_NEED_REBOOT;
|
return AT_NEED_REBOOT;
|
||||||
}
|
}
|
||||||
@@ -362,7 +638,10 @@ void config_ip_to_str(const uint8_t *ip, char *str)
|
|||||||
|
|
||||||
int config_str_to_ip(const char *str, uint8_t *ip)
|
int config_str_to_ip(const char *str, uint8_t *ip)
|
||||||
{
|
{
|
||||||
int a, b, c, d;
|
int a;
|
||||||
|
int b;
|
||||||
|
int c;
|
||||||
|
int d;
|
||||||
|
|
||||||
if (sscanf(str, "%d.%d.%d.%d", &a, &b, &c, &d) != 4) {
|
if (sscanf(str, "%d.%d.%d.%d", &a, &b, &c, &d) != 4) {
|
||||||
return -1;
|
return -1;
|
||||||
@@ -398,7 +677,6 @@ int config_str_to_mac(const char *str, uint8_t *mac)
|
|||||||
}
|
}
|
||||||
mac[i] = (uint8_t)a[i];
|
mac[i] = (uint8_t)a[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,7 +684,6 @@ void config_poll(void)
|
|||||||
{
|
{
|
||||||
if (g_pending_cmd_ready) {
|
if (g_pending_cmd_ready) {
|
||||||
uint16_t len = g_pending_cmd_len;
|
uint16_t len = g_pending_cmd_len;
|
||||||
|
|
||||||
g_pending_cmd_ready = false;
|
g_pending_cmd_ready = false;
|
||||||
g_pending_cmd_len = 0u;
|
g_pending_cmd_len = 0u;
|
||||||
(void)config_try_process_frame((const uint8_t *)g_pending_cmd_buffer, len);
|
(void)config_try_process_frame((const uint8_t *)g_pending_cmd_buffer, len);
|
||||||
@@ -415,8 +692,13 @@ void config_poll(void)
|
|||||||
|
|
||||||
void config_uart_rx_byte(uint8_t byte)
|
void config_uart_rx_byte(uint8_t byte)
|
||||||
{
|
{
|
||||||
if (byte == '\r' || byte == '\n') {
|
if (byte == '\r') {
|
||||||
if (g_uart_cmd_len > 0u) {
|
g_uart_rx_seen_cr = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byte == '\n') {
|
||||||
|
if (g_uart_rx_seen_cr && g_uart_cmd_len > 0u) {
|
||||||
if (!g_pending_cmd_ready) {
|
if (!g_pending_cmd_ready) {
|
||||||
memcpy(g_pending_cmd_buffer, g_uart_cmd_buffer, g_uart_cmd_len);
|
memcpy(g_pending_cmd_buffer, g_uart_cmd_buffer, g_uart_cmd_len);
|
||||||
g_pending_cmd_buffer[g_uart_cmd_len] = '\0';
|
g_pending_cmd_buffer[g_uart_cmd_len] = '\0';
|
||||||
@@ -425,46 +707,70 @@ void config_uart_rx_byte(uint8_t byte)
|
|||||||
}
|
}
|
||||||
g_uart_cmd_len = 0u;
|
g_uart_cmd_len = 0u;
|
||||||
}
|
}
|
||||||
|
g_uart_rx_seen_cr = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (g_uart_rx_seen_cr) {
|
||||||
|
g_uart_cmd_len = 0u;
|
||||||
|
g_uart_rx_seen_cr = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (g_uart_cmd_len < (CONFIG_CMD_MAX_LEN - 1u)) {
|
if (g_uart_cmd_len < (CONFIG_CMD_MAX_LEN - 1u)) {
|
||||||
g_uart_cmd_buffer[g_uart_cmd_len++] = (char)byte;
|
g_uart_cmd_buffer[g_uart_cmd_len++] = byte;
|
||||||
g_uart_cmd_buffer[g_uart_cmd_len] = '\0';
|
g_uart_cmd_buffer[g_uart_cmd_len] = '\0';
|
||||||
} else {
|
} else {
|
||||||
g_uart_cmd_len = 0u;
|
g_uart_cmd_len = 0u;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool config_build_response_frame(const uint8_t *data,
|
||||||
|
uint16_t len,
|
||||||
|
char *response,
|
||||||
|
uint16_t max_len,
|
||||||
|
at_result_t *result)
|
||||||
|
{
|
||||||
|
if (data == NULL || response == NULL || len < 2u || max_len == 0u) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (len >= CONFIG_CMD_MAX_LEN) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(g_cmd_parse_buffer, data, len);
|
||||||
|
g_cmd_parse_buffer[len] = '\0';
|
||||||
|
if (((g_cmd_parse_buffer[0] != 'A') && (g_cmd_parse_buffer[0] != 'a')) ||
|
||||||
|
((g_cmd_parse_buffer[1] != 'T') && (g_cmd_parse_buffer[1] != 't'))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*result = config_process_at_cmd(g_cmd_parse_buffer, response, max_len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool config_try_process_frame(const uint8_t *data, uint16_t len)
|
bool config_try_process_frame(const uint8_t *data, uint16_t len)
|
||||||
{
|
{
|
||||||
char response[CONFIG_TX_BUFFER_SIZE];
|
at_result_t result = AT_ERROR;
|
||||||
char cmd_buffer[CONFIG_CMD_MAX_LEN];
|
|
||||||
at_result_t result;
|
|
||||||
HAL_StatusTypeDef tx_status;
|
|
||||||
|
|
||||||
if (data == NULL || len < 2u) {
|
if (!config_build_response_frame(data, len, g_at_response_buffer, sizeof(g_at_response_buffer), &result)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len >= CONFIG_CMD_MAX_LEN) {
|
if (HAL_UART_Transmit(&CONFIG_UART_HANDLE,
|
||||||
len = CONFIG_CMD_MAX_LEN - 1u;
|
(uint8_t *)g_at_response_buffer,
|
||||||
}
|
(uint16_t)strlen(g_at_response_buffer),
|
||||||
|
1000u) != HAL_OK) {
|
||||||
memcpy(cmd_buffer, data, len);
|
|
||||||
cmd_buffer[len] = '\0';
|
|
||||||
|
|
||||||
if (((cmd_buffer[0] != 'A') && (cmd_buffer[0] != 'a')) ||
|
|
||||||
((cmd_buffer[1] != 'T') && (cmd_buffer[1] != 't'))) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = config_process_at_cmd(cmd_buffer, response, sizeof(response));
|
|
||||||
tx_status = HAL_UART_Transmit(&CONFIG_UART_HANDLE, (uint8_t *)response, (uint16_t)strlen(response), 1000u);
|
|
||||||
|
|
||||||
if (result == AT_NEED_REBOOT) {
|
if (result == AT_NEED_REBOOT) {
|
||||||
static const char hint[] = "Note: Use AT+SAVE then AT+RESET to apply changes\r\n";
|
static const char hint[] = "Note: Use AT+SAVE then AT+RESET to apply changes\r\n";
|
||||||
tx_status = HAL_UART_Transmit(&CONFIG_UART_HANDLE, (uint8_t *)hint, sizeof(hint) - 1u, 1000u);
|
if (HAL_UART_Transmit(&CONFIG_UART_HANDLE,
|
||||||
|
(uint8_t *)hint,
|
||||||
|
sizeof(hint) - 1u,
|
||||||
|
1000u) != HAL_OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -479,3 +785,31 @@ void config_clear_reset_requested(void)
|
|||||||
{
|
{
|
||||||
g_reset_requested = false;
|
g_reset_requested = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t config_link_index_to_endpoint(uint8_t index)
|
||||||
|
{
|
||||||
|
switch (index) {
|
||||||
|
case CONFIG_LINK_S1:
|
||||||
|
return ENDPOINT_S1;
|
||||||
|
case CONFIG_LINK_S2:
|
||||||
|
return ENDPOINT_S2;
|
||||||
|
case CONFIG_LINK_C1:
|
||||||
|
return ENDPOINT_C1;
|
||||||
|
case CONFIG_LINK_C2:
|
||||||
|
return ENDPOINT_C2;
|
||||||
|
default:
|
||||||
|
return 0u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t config_uart_index_to_endpoint(uint8_t uart_index)
|
||||||
|
{
|
||||||
|
return (uart_index == LINK_UART_U1) ? ENDPOINT_UART3 : ENDPOINT_UART2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool config_endpoint_is_single(uint8_t endpoint)
|
||||||
|
{
|
||||||
|
return endpoint == ENDPOINT_C1 || endpoint == ENDPOINT_C2 ||
|
||||||
|
endpoint == ENDPOINT_UART2 || endpoint == ENDPOINT_UART3 ||
|
||||||
|
endpoint == ENDPOINT_S1 || endpoint == ENDPOINT_S2;
|
||||||
|
}
|
||||||
|
|||||||
+61
-157
@@ -1,96 +1,78 @@
|
|||||||
/**
|
/**
|
||||||
* @file config.h
|
* @file config.h
|
||||||
* @brief AT command configuration module for TCP2UART
|
* @brief Final AT configuration model for TCP2UART.
|
||||||
*
|
|
||||||
* Handles UART1 AT commands for network and serial port configuration.
|
|
||||||
*
|
|
||||||
* Supported AT commands:
|
|
||||||
* - AT+IP=192.168.1.100 Set device IP
|
|
||||||
* - AT+MASK=255.255.255.0 Set subnet mask
|
|
||||||
* - AT+GW=192.168.1.1 Set gateway
|
|
||||||
* - AT+PORT=8080 Set TCP Server listen port
|
|
||||||
* - AT+RIP=192.168.1.200 Set TCP Client remote IP
|
|
||||||
* - AT+RPORT=9000 Set TCP Client remote port
|
|
||||||
* - AT+BAUD1=115200 Set UART2 baudrate
|
|
||||||
* - AT+BAUD2=115200 Set UART3 baudrate
|
|
||||||
* - AT+MAC=00:11:22:33:44:55 Set MAC address
|
|
||||||
* - AT+DHCP=0/1 Enable/disable DHCP
|
|
||||||
* - AT+SAVE Save parameters to Flash
|
|
||||||
* - AT+RESET Reset device
|
|
||||||
* - AT+DEFAULT Restore factory defaults
|
|
||||||
* - AT+? Query current configuration
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __CONFIG_H__
|
#ifndef __CONFIG_H__
|
||||||
#define __CONFIG_H__
|
#define __CONFIG_H__
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Configuration magic number "TCPU" */
|
#define CONFIG_MAGIC 0x54435055u
|
||||||
#define CONFIG_MAGIC 0x54435055
|
#define CONFIG_VERSION 0x0003u
|
||||||
|
|
||||||
/* Configuration version for compatibility */
|
#define CONFIG_UART_COUNT 2u
|
||||||
#define CONFIG_VERSION 0x0001
|
#define CONFIG_LINK_COUNT 4u
|
||||||
|
|
||||||
|
#define CONFIG_LINK_S1 0u
|
||||||
|
#define CONFIG_LINK_S2 1u
|
||||||
|
#define CONFIG_LINK_C1 2u
|
||||||
|
#define CONFIG_LINK_C2 3u
|
||||||
|
|
||||||
|
#define ENDPOINT_C1 0x01u
|
||||||
|
#define ENDPOINT_C2 0x02u
|
||||||
|
#define ENDPOINT_UART2 0x04u
|
||||||
|
#define ENDPOINT_UART3 0x08u
|
||||||
|
#define ENDPOINT_S1 0x10u
|
||||||
|
#define ENDPOINT_S2 0x20u
|
||||||
|
|
||||||
|
#define LINK_UART_U0 0u
|
||||||
|
#define LINK_UART_U1 1u
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
MUX_MODE_RAW = 0,
|
||||||
|
MUX_MODE_FRAME = 1
|
||||||
|
} mux_mode_t;
|
||||||
|
|
||||||
/* Device configuration structure */
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t magic; /* Magic number for validation */
|
uint8_t ip[4];
|
||||||
uint16_t version; /* Configuration version */
|
uint8_t mask[4];
|
||||||
uint16_t reserved; /* Reserved for alignment */
|
uint8_t gw[4];
|
||||||
|
uint8_t mac[6];
|
||||||
|
uint8_t reserved[2];
|
||||||
|
} net_config_t;
|
||||||
|
|
||||||
/* Network settings */
|
typedef struct {
|
||||||
uint8_t mac[6]; /* MAC address */
|
uint8_t enabled;
|
||||||
uint8_t dhcp_enable; /* DHCP enable flag */
|
uint8_t uart;
|
||||||
uint8_t reserved2; /* Reserved for alignment */
|
uint16_t local_port;
|
||||||
uint8_t ip[4]; /* Device IP address */
|
uint8_t remote_ip[4];
|
||||||
uint8_t mask[4]; /* Subnet mask */
|
uint16_t remote_port;
|
||||||
uint8_t gw[4]; /* Gateway */
|
uint16_t reserved;
|
||||||
|
} link_config_t;
|
||||||
|
|
||||||
/* TCP Server settings */
|
typedef struct {
|
||||||
uint16_t server_port; /* Server listen port */
|
uint32_t magic;
|
||||||
uint16_t reserved3; /* Reserved for alignment */
|
uint16_t version;
|
||||||
|
uint8_t mux_mode;
|
||||||
/* TCP Client settings */
|
uint8_t reserved0;
|
||||||
uint8_t remote_ip[4]; /* Remote server IP */
|
net_config_t net;
|
||||||
uint16_t remote_port; /* Remote server port */
|
link_config_t links[CONFIG_LINK_COUNT];
|
||||||
uint16_t reconnect_interval;/* Reconnect interval (ms) */
|
uint32_t uart_baudrate[CONFIG_UART_COUNT];
|
||||||
|
|
||||||
/* UART settings */
|
|
||||||
uint32_t uart2_baudrate; /* UART2 (Server) baudrate */
|
|
||||||
uint32_t uart3_baudrate; /* UART3 (Client) baudrate */
|
|
||||||
uint8_t uart2_databits; /* UART2 data bits */
|
|
||||||
uint8_t uart2_stopbits; /* UART2 stop bits */
|
|
||||||
uint8_t uart2_parity; /* UART2 parity */
|
|
||||||
uint8_t uart3_databits; /* UART3 data bits */
|
|
||||||
uint8_t uart3_stopbits; /* UART3 stop bits */
|
|
||||||
uint8_t uart3_parity; /* UART3 parity */
|
|
||||||
uint16_t reserved4; /* Reserved for alignment */
|
|
||||||
|
|
||||||
/* CRC32 checksum (must be last) */
|
|
||||||
uint32_t crc;
|
uint32_t crc;
|
||||||
} device_config_t;
|
} device_config_t;
|
||||||
|
|
||||||
/* Default configuration values */
|
#define DEFAULT_NET_IP {192, 168, 1, 100}
|
||||||
#define DEFAULT_IP {192, 168, 1, 100}
|
#define DEFAULT_NET_MASK {255, 255, 255, 0}
|
||||||
#define DEFAULT_MASK {255, 255, 255, 0}
|
#define DEFAULT_NET_GW {192, 168, 1, 1}
|
||||||
#define DEFAULT_GW {192, 168, 1, 1}
|
#define DEFAULT_NET_MAC {0x02, 0x00, 0x00, 0x00, 0x00, 0x01}
|
||||||
#define DEFAULT_MAC {0x02, 0x00, 0x00, 0x00, 0x00, 0x01}
|
#define DEFAULT_UART_BAUDRATE 115200u
|
||||||
#define DEFAULT_SERVER_PORT 8080
|
|
||||||
#define DEFAULT_REMOTE_IP {192, 168, 1, 200}
|
|
||||||
#define DEFAULT_REMOTE_PORT 9000
|
|
||||||
#define DEFAULT_UART_BAUDRATE 115200
|
|
||||||
#define DEFAULT_UART_DATABITS 8
|
|
||||||
#define DEFAULT_UART_STOPBITS 1
|
|
||||||
#define DEFAULT_UART_PARITY 0
|
|
||||||
#define DEFAULT_DHCP_ENABLE 0
|
|
||||||
#define DEFAULT_RECONNECT_MS 3000
|
|
||||||
|
|
||||||
/* AT command result codes */
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
AT_OK = 0,
|
AT_OK = 0,
|
||||||
AT_ERROR,
|
AT_ERROR,
|
||||||
@@ -100,108 +82,30 @@ typedef enum {
|
|||||||
AT_NEED_REBOOT
|
AT_NEED_REBOOT
|
||||||
} at_result_t;
|
} at_result_t;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialize configuration module
|
|
||||||
* @return 0 on success, negative on error
|
|
||||||
*/
|
|
||||||
int config_init(void);
|
int config_init(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Load configuration from Flash
|
|
||||||
* @return 0 on success, negative on error (defaults loaded)
|
|
||||||
*/
|
|
||||||
int config_load(void);
|
int config_load(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Save configuration to Flash
|
|
||||||
* @return 0 on success, negative on error
|
|
||||||
*/
|
|
||||||
int config_save(void);
|
int config_save(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Reset configuration to factory defaults
|
|
||||||
*/
|
|
||||||
void config_set_defaults(void);
|
void config_set_defaults(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get current configuration
|
|
||||||
* @return Pointer to current configuration (read-only)
|
|
||||||
*/
|
|
||||||
const device_config_t *config_get(void);
|
const device_config_t *config_get(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get mutable configuration for modification
|
|
||||||
* @return Pointer to configuration structure
|
|
||||||
*/
|
|
||||||
device_config_t *config_get_mutable(void);
|
device_config_t *config_get_mutable(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Process AT command received from UART1
|
|
||||||
* @param cmd Command string (null-terminated)
|
|
||||||
* @param response Response buffer
|
|
||||||
* @param max_len Maximum response length
|
|
||||||
* @return AT command result code
|
|
||||||
*/
|
|
||||||
at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_len);
|
at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_len);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Poll configuration UART and process pending AT commands
|
|
||||||
*/
|
|
||||||
void config_poll(void);
|
void config_poll(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Feed one byte received from the config UART.
|
|
||||||
* @param byte Received byte.
|
|
||||||
*/
|
|
||||||
void config_uart_rx_byte(uint8_t byte);
|
void config_uart_rx_byte(uint8_t byte);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Try to process one AT command frame from an external UART source.
|
|
||||||
* @param data Input bytes.
|
|
||||||
* @param len Input length.
|
|
||||||
* @return true if the frame was recognized as an AT/config command.
|
|
||||||
*/
|
|
||||||
bool config_try_process_frame(const uint8_t *data, uint16_t len);
|
bool config_try_process_frame(const uint8_t *data, uint16_t len);
|
||||||
|
bool config_build_response_frame(const uint8_t *data,
|
||||||
/**
|
uint16_t len,
|
||||||
* @brief Check whether AT+RESET requested a system reset
|
char *response,
|
||||||
*/
|
uint16_t max_len,
|
||||||
|
at_result_t *result);
|
||||||
bool config_is_reset_requested(void);
|
bool config_is_reset_requested(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Clear the pending reset request flag
|
|
||||||
*/
|
|
||||||
void config_clear_reset_requested(void);
|
void config_clear_reset_requested(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Format IP address to string
|
|
||||||
* @param ip IP address bytes
|
|
||||||
* @param str Output string buffer (min 16 bytes)
|
|
||||||
*/
|
|
||||||
void config_ip_to_str(const uint8_t *ip, char *str);
|
void config_ip_to_str(const uint8_t *ip, char *str);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Parse IP address from string
|
|
||||||
* @param str IP address string (e.g. "192.168.1.100")
|
|
||||||
* @param ip Output IP address bytes
|
|
||||||
* @return 0 on success, negative on error
|
|
||||||
*/
|
|
||||||
int config_str_to_ip(const char *str, uint8_t *ip);
|
int config_str_to_ip(const char *str, uint8_t *ip);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Format MAC address to string
|
|
||||||
* @param mac MAC address bytes
|
|
||||||
* @param str Output string buffer (min 18 bytes)
|
|
||||||
*/
|
|
||||||
void config_mac_to_str(const uint8_t *mac, char *str);
|
void config_mac_to_str(const uint8_t *mac, char *str);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Parse MAC address from string
|
|
||||||
* @param str MAC address string (e.g. "00:11:22:33:44:55")
|
|
||||||
* @param mac Output MAC address bytes
|
|
||||||
* @return 0 on success, negative on error
|
|
||||||
*/
|
|
||||||
int config_str_to_mac(const char *str, uint8_t *mac);
|
int config_str_to_mac(const char *str, uint8_t *mac);
|
||||||
|
uint8_t config_link_index_to_endpoint(uint8_t index);
|
||||||
|
uint8_t config_uart_index_to_endpoint(uint8_t uart_index);
|
||||||
|
bool config_endpoint_is_single(uint8_t endpoint);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-44
@@ -19,41 +19,10 @@
|
|||||||
* Private Variables
|
* Private Variables
|
||||||
*---------------------------------------------------------------------------*/
|
*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
/* CRC32 lookup table */
|
|
||||||
static uint32_t g_crc_table[256];
|
|
||||||
static bool g_crc_table_initialized = false;
|
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------
|
/*---------------------------------------------------------------------------
|
||||||
* Private Functions
|
* Private Functions
|
||||||
*---------------------------------------------------------------------------*/
|
*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialize CRC32 lookup table
|
|
||||||
*/
|
|
||||||
static void crc32_init_table(void)
|
|
||||||
{
|
|
||||||
uint32_t i, j, crc;
|
|
||||||
|
|
||||||
for (i = 0; i < 256; i++)
|
|
||||||
{
|
|
||||||
crc = i;
|
|
||||||
for (j = 0; j < 8; j++)
|
|
||||||
{
|
|
||||||
if (crc & 1)
|
|
||||||
{
|
|
||||||
crc = (crc >> 1) ^ CRC32_POLYNOMIAL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
crc >>= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g_crc_table[i] = crc;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_crc_table_initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Unlock Flash for writing
|
* @brief Unlock Flash for writing
|
||||||
*/
|
*/
|
||||||
@@ -105,12 +74,6 @@ static HAL_StatusTypeDef flash_program_halfword(uint32_t addr, uint16_t data)
|
|||||||
*/
|
*/
|
||||||
int flash_param_init(void)
|
int flash_param_init(void)
|
||||||
{
|
{
|
||||||
/* Initialize CRC table */
|
|
||||||
if (!g_crc_table_initialized)
|
|
||||||
{
|
|
||||||
crc32_init_table();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,16 +206,22 @@ uint32_t flash_param_crc32(const void *data, uint32_t len)
|
|||||||
const uint8_t *p = (const uint8_t *)data;
|
const uint8_t *p = (const uint8_t *)data;
|
||||||
uint32_t crc = 0xFFFFFFFF;
|
uint32_t crc = 0xFFFFFFFF;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
uint32_t j;
|
||||||
/* Initialize table if needed */
|
|
||||||
if (!g_crc_table_initialized)
|
|
||||||
{
|
|
||||||
crc32_init_table();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < len; i++)
|
for (i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
crc = g_crc_table[(crc ^ p[i]) & 0xFF] ^ (crc >> 8);
|
crc ^= p[i];
|
||||||
|
for (j = 0; j < 8u; ++j)
|
||||||
|
{
|
||||||
|
if ((crc & 1u) != 0u)
|
||||||
|
{
|
||||||
|
crc = (crc >> 1) ^ CRC32_POLYNOMIAL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
crc >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return crc ^ 0xFFFFFFFF;
|
return crc ^ 0xFFFFFFFF;
|
||||||
|
|||||||
+131
-107
@@ -1,15 +1,14 @@
|
|||||||
/**
|
/**
|
||||||
* @file tcp_client.c
|
* @file tcp_client.c
|
||||||
* @brief lwIP RAW TCP client for the UART3 bridge.
|
* @brief Indexed lwIP RAW TCP client manager.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "tcp_client.h"
|
#include "tcp_client.h"
|
||||||
|
|
||||||
#include "main.h"
|
#include "../Core/Inc/main.h"
|
||||||
|
#include "../Drivers/LwIP/src/include/lwip/ip_addr.h"
|
||||||
#include "lwip/ip_addr.h"
|
#include "../Drivers/LwIP/src/include/lwip/pbuf.h"
|
||||||
#include "lwip/pbuf.h"
|
#include "../Drivers/LwIP/src/include/lwip/tcp.h"
|
||||||
#include "lwip/tcp.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@@ -19,11 +18,12 @@ typedef struct {
|
|||||||
uint16_t rx_head;
|
uint16_t rx_head;
|
||||||
uint16_t rx_tail;
|
uint16_t rx_tail;
|
||||||
uint32_t next_retry_ms;
|
uint32_t next_retry_ms;
|
||||||
tcp_client_config_t config;
|
uint8_t index;
|
||||||
|
tcp_client_instance_config_t config;
|
||||||
tcp_client_status_t status;
|
tcp_client_status_t status;
|
||||||
} tcp_client_ctx_t;
|
} tcp_client_ctx_t;
|
||||||
|
|
||||||
static tcp_client_ctx_t g_client;
|
static tcp_client_ctx_t g_clients[TCP_CLIENT_INSTANCE_COUNT];
|
||||||
|
|
||||||
static uint16_t ring_free(uint16_t head, uint16_t tail, uint16_t size)
|
static uint16_t ring_free(uint16_t head, uint16_t tail, uint16_t size)
|
||||||
{
|
{
|
||||||
@@ -35,13 +35,18 @@ static err_t tcp_client_on_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p,
|
|||||||
tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg;
|
tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg;
|
||||||
struct pbuf *q;
|
struct pbuf *q;
|
||||||
|
|
||||||
|
if (ctx == NULL) {
|
||||||
|
if (p != NULL) {
|
||||||
|
pbuf_free(p);
|
||||||
|
}
|
||||||
|
return ERR_ARG;
|
||||||
|
}
|
||||||
if (err != ERR_OK) {
|
if (err != ERR_OK) {
|
||||||
if (p != NULL) {
|
if (p != NULL) {
|
||||||
pbuf_free(p);
|
pbuf_free(p);
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p == NULL) {
|
if (p == NULL) {
|
||||||
tcp_arg(pcb, NULL);
|
tcp_arg(pcb, NULL);
|
||||||
tcp_recv(pcb, NULL);
|
tcp_recv(pcb, NULL);
|
||||||
@@ -78,24 +83,32 @@ static err_t tcp_client_on_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
|
|||||||
{
|
{
|
||||||
tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg;
|
tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg;
|
||||||
(void)pcb;
|
(void)pcb;
|
||||||
|
if (ctx != NULL) {
|
||||||
ctx->status.tx_bytes += len;
|
ctx->status.tx_bytes += len;
|
||||||
|
}
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tcp_client_on_err(void *arg, err_t err)
|
static void tcp_client_on_err(void *arg, err_t err)
|
||||||
{
|
{
|
||||||
tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg;
|
tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg;
|
||||||
(void)err;
|
if (ctx == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
ctx->pcb = NULL;
|
ctx->pcb = NULL;
|
||||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||||
ctx->status.errors++;
|
ctx->status.errors++;
|
||||||
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
|
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
|
||||||
|
(void)err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static err_t tcp_client_on_connected(void *arg, struct tcp_pcb *pcb, err_t err)
|
static err_t tcp_client_on_connected(void *arg, struct tcp_pcb *pcb, err_t err)
|
||||||
{
|
{
|
||||||
tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg;
|
tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg;
|
||||||
|
|
||||||
|
if (ctx == NULL) {
|
||||||
|
return ERR_ARG;
|
||||||
|
}
|
||||||
if (err != ERR_OK) {
|
if (err != ERR_OK) {
|
||||||
ctx->pcb = NULL;
|
ctx->pcb = NULL;
|
||||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||||
@@ -106,6 +119,7 @@ static err_t tcp_client_on_connected(void *arg, struct tcp_pcb *pcb, err_t err)
|
|||||||
|
|
||||||
ctx->pcb = pcb;
|
ctx->pcb = pcb;
|
||||||
ctx->status.state = TCP_CLIENT_STATE_CONNECTED;
|
ctx->status.state = TCP_CLIENT_STATE_CONNECTED;
|
||||||
|
tcp_nagle_disable(pcb);
|
||||||
tcp_arg(pcb, ctx);
|
tcp_arg(pcb, ctx);
|
||||||
tcp_recv(pcb, tcp_client_on_recv);
|
tcp_recv(pcb, tcp_client_on_recv);
|
||||||
tcp_sent(pcb, tcp_client_on_sent);
|
tcp_sent(pcb, tcp_client_on_sent);
|
||||||
@@ -113,171 +127,181 @@ static err_t tcp_client_on_connected(void *arg, struct tcp_pcb *pcb, err_t err)
|
|||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tcp_client_init(const tcp_client_config_t *config)
|
int tcp_client_init_all(void)
|
||||||
{
|
{
|
||||||
memset(&g_client, 0, sizeof(g_client));
|
memset(g_clients, 0, sizeof(g_clients));
|
||||||
g_client.config.server_ip[0] = 192u;
|
for (uint8_t i = 0; i < TCP_CLIENT_INSTANCE_COUNT; ++i) {
|
||||||
g_client.config.server_ip[1] = 168u;
|
g_clients[i].index = i;
|
||||||
g_client.config.server_ip[2] = 1u;
|
g_clients[i].status.state = TCP_CLIENT_STATE_IDLE;
|
||||||
g_client.config.server_ip[3] = 100u;
|
g_clients[i].config.reconnect_interval_ms = TCP_CLIENT_RECONNECT_DELAY_MS;
|
||||||
g_client.config.server_port = TCP_CLIENT_DEFAULT_PORT;
|
g_clients[i].config.auto_reconnect = true;
|
||||||
g_client.config.auto_reconnect = true;
|
|
||||||
g_client.config.reconnect_interval_ms = TCP_CLIENT_RECONNECT_DELAY_MS;
|
|
||||||
g_client.status.state = TCP_CLIENT_STATE_IDLE;
|
|
||||||
|
|
||||||
if (config != NULL) {
|
|
||||||
g_client.config = *config;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tcp_client_connect(void)
|
int tcp_client_config(uint8_t instance, const tcp_client_instance_config_t *config)
|
||||||
|
{
|
||||||
|
if (instance >= TCP_CLIENT_INSTANCE_COUNT || config == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
g_clients[instance].config = *config;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tcp_client_connect(uint8_t instance)
|
||||||
{
|
{
|
||||||
struct tcp_pcb *pcb;
|
struct tcp_pcb *pcb;
|
||||||
ip_addr_t remote_addr;
|
ip_addr_t remote_addr;
|
||||||
err_t err;
|
err_t err;
|
||||||
|
tcp_client_ctx_t *ctx;
|
||||||
|
|
||||||
if (g_client.pcb != NULL) {
|
if (instance >= TCP_CLIENT_INSTANCE_COUNT) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ctx = &g_clients[instance];
|
||||||
|
if (!ctx->config.enabled) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ctx->pcb != NULL) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pcb = tcp_new_ip_type(IPADDR_TYPE_V4);
|
pcb = tcp_new_ip_type(IPADDR_TYPE_V4);
|
||||||
if (pcb == NULL) {
|
if (pcb == NULL) {
|
||||||
g_client.status.errors++;
|
ctx->status.errors++;
|
||||||
|
ctx->status.state = TCP_CLIENT_STATE_ERROR;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if (ctx->config.local_port != 0u) {
|
||||||
|
err = tcp_bind(pcb, IP_ANY_TYPE, ctx->config.local_port);
|
||||||
|
if (err != ERR_OK) {
|
||||||
|
tcp_abort(pcb);
|
||||||
|
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||||
|
ctx->status.errors++;
|
||||||
|
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IP_ADDR4(&remote_addr,
|
IP_ADDR4(&remote_addr,
|
||||||
g_client.config.server_ip[0],
|
ctx->config.remote_ip[0],
|
||||||
g_client.config.server_ip[1],
|
ctx->config.remote_ip[1],
|
||||||
g_client.config.server_ip[2],
|
ctx->config.remote_ip[2],
|
||||||
g_client.config.server_ip[3]);
|
ctx->config.remote_ip[3]);
|
||||||
|
|
||||||
g_client.status.state = TCP_CLIENT_STATE_CONNECTING;
|
ctx->status.state = TCP_CLIENT_STATE_CONNECTING;
|
||||||
tcp_arg(pcb, &g_client);
|
tcp_arg(pcb, ctx);
|
||||||
err = tcp_connect(pcb, &remote_addr, g_client.config.server_port, tcp_client_on_connected);
|
tcp_err(pcb, tcp_client_on_err);
|
||||||
|
err = tcp_connect(pcb, &remote_addr, ctx->config.remote_port, tcp_client_on_connected);
|
||||||
if (err != ERR_OK) {
|
if (err != ERR_OK) {
|
||||||
|
tcp_err(pcb, NULL);
|
||||||
tcp_abort(pcb);
|
tcp_abort(pcb);
|
||||||
g_client.status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||||
g_client.status.errors++;
|
ctx->status.errors++;
|
||||||
g_client.next_retry_ms = HAL_GetTick() + g_client.config.reconnect_interval_ms;
|
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_client.pcb = pcb;
|
ctx->pcb = pcb;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tcp_client_disconnect(void)
|
int tcp_client_disconnect(uint8_t instance)
|
||||||
{
|
{
|
||||||
if (g_client.pcb != NULL) {
|
tcp_client_ctx_t *ctx;
|
||||||
tcp_arg(g_client.pcb, NULL);
|
|
||||||
tcp_recv(g_client.pcb, NULL);
|
|
||||||
tcp_sent(g_client.pcb, NULL);
|
|
||||||
tcp_err(g_client.pcb, NULL);
|
|
||||||
tcp_abort(g_client.pcb);
|
|
||||||
g_client.pcb = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_client.status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
if (instance >= TCP_CLIENT_INSTANCE_COUNT) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ctx = &g_clients[instance];
|
||||||
|
if (ctx->pcb != NULL) {
|
||||||
|
tcp_arg(ctx->pcb, NULL);
|
||||||
|
tcp_recv(ctx->pcb, NULL);
|
||||||
|
tcp_sent(ctx->pcb, NULL);
|
||||||
|
tcp_err(ctx->pcb, NULL);
|
||||||
|
tcp_abort(ctx->pcb);
|
||||||
|
ctx->pcb = NULL;
|
||||||
|
}
|
||||||
|
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||||
|
ctx->rx_head = 0u;
|
||||||
|
ctx->rx_tail = 0u;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tcp_client_send(const uint8_t *data, uint16_t len)
|
int tcp_client_send(uint8_t instance, const uint8_t *data, uint16_t len)
|
||||||
{
|
{
|
||||||
err_t err;
|
err_t err;
|
||||||
|
tcp_client_ctx_t *ctx;
|
||||||
|
|
||||||
if (g_client.pcb == NULL || data == NULL || len == 0u) {
|
if (instance >= TCP_CLIENT_INSTANCE_COUNT || data == NULL || len == 0u) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
ctx = &g_clients[instance];
|
||||||
if (tcp_sndbuf(g_client.pcb) < len) {
|
if (ctx->pcb == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (tcp_sndbuf(ctx->pcb) < len) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
err = tcp_write(ctx->pcb, data, len, TCP_WRITE_FLAG_COPY);
|
||||||
err = tcp_write(g_client.pcb, data, len, TCP_WRITE_FLAG_COPY);
|
|
||||||
if (err != ERR_OK) {
|
if (err != ERR_OK) {
|
||||||
g_client.status.errors++;
|
ctx->status.errors++;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
err = tcp_output(ctx->pcb);
|
||||||
err = tcp_output(g_client.pcb);
|
|
||||||
if (err != ERR_OK) {
|
if (err != ERR_OK) {
|
||||||
g_client.status.errors++;
|
ctx->status.errors++;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (int)len;
|
return (int)len;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tcp_client_recv(uint8_t *data, uint16_t max_len, uint32_t timeout_ms)
|
int tcp_client_recv(uint8_t instance, uint8_t *data, uint16_t max_len)
|
||||||
{
|
{
|
||||||
uint16_t copied = 0u;
|
uint16_t copied = 0u;
|
||||||
(void)timeout_ms;
|
tcp_client_ctx_t *ctx;
|
||||||
|
|
||||||
if (data == NULL || max_len == 0u) {
|
if (instance >= TCP_CLIENT_INSTANCE_COUNT || data == NULL || max_len == 0u) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
ctx = &g_clients[instance];
|
||||||
while (copied < max_len && g_client.rx_tail != g_client.rx_head) {
|
while (copied < max_len && ctx->rx_tail != ctx->rx_head) {
|
||||||
data[copied++] = g_client.rx_ring[g_client.rx_tail];
|
data[copied++] = ctx->rx_ring[ctx->rx_tail];
|
||||||
g_client.rx_tail = (uint16_t)((g_client.rx_tail + 1u) % TCP_CLIENT_RX_BUFFER_SIZE);
|
ctx->rx_tail = (uint16_t)((ctx->rx_tail + 1u) % TCP_CLIENT_RX_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (int)copied;
|
return (int)copied;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tcp_client_is_connected(void)
|
bool tcp_client_is_connected(uint8_t instance)
|
||||||
{
|
{
|
||||||
return (g_client.pcb != NULL) && (g_client.status.state == TCP_CLIENT_STATE_CONNECTED);
|
return (instance < TCP_CLIENT_INSTANCE_COUNT) &&
|
||||||
|
(g_clients[instance].pcb != NULL) &&
|
||||||
|
(g_clients[instance].status.state == TCP_CLIENT_STATE_CONNECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
int tcp_client_set_server(const uint8_t *ip, uint16_t port)
|
void tcp_client_get_status(uint8_t instance, tcp_client_status_t *status)
|
||||||
{
|
{
|
||||||
if (ip == NULL || port == 0u) {
|
if (instance < TCP_CLIENT_INSTANCE_COUNT && status != NULL) {
|
||||||
return -1;
|
*status = g_clients[instance].status;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(g_client.config.server_ip, ip, 4u);
|
|
||||||
g_client.config.server_port = port;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void tcp_client_get_status(tcp_client_status_t *status)
|
|
||||||
{
|
|
||||||
if (status != NULL) {
|
|
||||||
*status = g_client.status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void *tcp_client_get_rx_stream(void)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *tcp_client_get_tx_stream(void)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void tcp_client_task(void *argument)
|
|
||||||
{
|
|
||||||
(void)argument;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void tcp_client_poll(void)
|
void tcp_client_poll(void)
|
||||||
{
|
{
|
||||||
uint32_t now;
|
uint32_t now = HAL_GetTick();
|
||||||
|
|
||||||
if (!g_client.config.auto_reconnect || g_client.pcb != NULL) {
|
for (uint8_t i = 0; i < TCP_CLIENT_INSTANCE_COUNT; ++i) {
|
||||||
return;
|
tcp_client_ctx_t *ctx = &g_clients[i];
|
||||||
|
if (!ctx->config.enabled || !ctx->config.auto_reconnect || tcp_client_is_connected(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((ctx->pcb != NULL) && (ctx->status.state == TCP_CLIENT_STATE_CONNECTING)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (now >= ctx->next_retry_ms) {
|
||||||
|
ctx->status.reconnect_count++;
|
||||||
|
ctx->next_retry_ms = now + ctx->config.reconnect_interval_ms;
|
||||||
|
(void)tcp_client_connect(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
now = HAL_GetTick();
|
|
||||||
if (now >= g_client.next_retry_ms) {
|
|
||||||
g_client.status.reconnect_count++;
|
|
||||||
g_client.next_retry_ms = now + g_client.config.reconnect_interval_ms;
|
|
||||||
(void)tcp_client_connect();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-96
@@ -1,48 +1,39 @@
|
|||||||
/**
|
/**
|
||||||
* @file tcp_client.h
|
* @file tcp_client.h
|
||||||
* @brief TCP Client module for transparent transmission with UART3
|
* @brief Indexed lwIP RAW TCP client manager.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __TCP_CLIENT_H__
|
#ifndef __TCP_CLIENT_H__
|
||||||
#define __TCP_CLIENT_H__
|
#define __TCP_CLIENT_H__
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Default TCP Client settings */
|
#define TCP_CLIENT_INSTANCE_COUNT 2u
|
||||||
#define TCP_CLIENT_DEFAULT_PORT 8081
|
#define TCP_CLIENT_RX_BUFFER_SIZE 512u
|
||||||
#define TCP_CLIENT_DEFAULT_SERVER "192.168.1.100"
|
#define TCP_CLIENT_RECONNECT_DELAY_MS 3000u
|
||||||
|
|
||||||
/* Reconnect settings */
|
|
||||||
#define TCP_CLIENT_RECONNECT_DELAY_MS 3000
|
|
||||||
#define TCP_CLIENT_MAX_RECONNECT_TRIES 0 /* 0 = infinite */
|
|
||||||
|
|
||||||
/* Buffer sizes */
|
|
||||||
#define TCP_CLIENT_RX_BUFFER_SIZE 512
|
|
||||||
#define TCP_CLIENT_TX_BUFFER_SIZE 512
|
|
||||||
|
|
||||||
/* TCP Client state */
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TCP_CLIENT_STATE_IDLE,
|
TCP_CLIENT_STATE_IDLE = 0,
|
||||||
TCP_CLIENT_STATE_CONNECTING,
|
TCP_CLIENT_STATE_CONNECTING,
|
||||||
TCP_CLIENT_STATE_CONNECTED,
|
TCP_CLIENT_STATE_CONNECTED,
|
||||||
TCP_CLIENT_STATE_DISCONNECTED,
|
TCP_CLIENT_STATE_DISCONNECTED,
|
||||||
TCP_CLIENT_STATE_ERROR
|
TCP_CLIENT_STATE_ERROR
|
||||||
} tcp_client_state_t;
|
} tcp_client_state_t;
|
||||||
|
|
||||||
/* TCP Client configuration */
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t server_ip[4]; /* Server IP address */
|
uint8_t remote_ip[4];
|
||||||
uint16_t server_port; /* Server port */
|
uint16_t local_port;
|
||||||
bool auto_reconnect; /* Auto reconnect on disconnect */
|
uint16_t remote_port;
|
||||||
uint16_t reconnect_interval_ms; /* Reconnect interval */
|
uint16_t reconnect_interval_ms;
|
||||||
} tcp_client_config_t;
|
bool enabled;
|
||||||
|
bool auto_reconnect;
|
||||||
|
} tcp_client_instance_config_t;
|
||||||
|
|
||||||
/* TCP Client status */
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
tcp_client_state_t state;
|
tcp_client_state_t state;
|
||||||
uint32_t rx_bytes;
|
uint32_t rx_bytes;
|
||||||
@@ -51,80 +42,14 @@ typedef struct {
|
|||||||
uint32_t errors;
|
uint32_t errors;
|
||||||
} tcp_client_status_t;
|
} tcp_client_status_t;
|
||||||
|
|
||||||
/**
|
int tcp_client_init_all(void);
|
||||||
* @brief Initialize TCP Client module
|
int tcp_client_config(uint8_t instance, const tcp_client_instance_config_t *config);
|
||||||
* @param config Client configuration
|
int tcp_client_connect(uint8_t instance);
|
||||||
* @return 0 on success, negative on error
|
int tcp_client_disconnect(uint8_t instance);
|
||||||
*/
|
int tcp_client_send(uint8_t instance, const uint8_t *data, uint16_t len);
|
||||||
int tcp_client_init(const tcp_client_config_t *config);
|
int tcp_client_recv(uint8_t instance, uint8_t *data, uint16_t max_len);
|
||||||
|
bool tcp_client_is_connected(uint8_t instance);
|
||||||
/**
|
void tcp_client_get_status(uint8_t instance, tcp_client_status_t *status);
|
||||||
* @brief Connect to remote server
|
|
||||||
* @return 0 on success, negative on error
|
|
||||||
*/
|
|
||||||
int tcp_client_connect(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Disconnect from server
|
|
||||||
* @return 0 on success, negative on error
|
|
||||||
*/
|
|
||||||
int tcp_client_disconnect(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Send data to server
|
|
||||||
* @param data Data buffer
|
|
||||||
* @param len Data length
|
|
||||||
* @return Number of bytes sent, negative on error
|
|
||||||
*/
|
|
||||||
int tcp_client_send(const uint8_t *data, uint16_t len);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Receive data from server
|
|
||||||
* @param data Data buffer
|
|
||||||
* @param max_len Maximum length to receive
|
|
||||||
* @param timeout_ms Timeout in milliseconds (0 = non-blocking)
|
|
||||||
* @return Number of bytes received, 0 if no data, negative on error
|
|
||||||
*/
|
|
||||||
int tcp_client_recv(uint8_t *data, uint16_t max_len, uint32_t timeout_ms);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if connected to server
|
|
||||||
* @return true if connected
|
|
||||||
*/
|
|
||||||
bool tcp_client_is_connected(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Update server configuration (for AT command)
|
|
||||||
* @param ip Server IP address (4 bytes)
|
|
||||||
* @param port Server port
|
|
||||||
* @return 0 on success, negative on error
|
|
||||||
*/
|
|
||||||
int tcp_client_set_server(const uint8_t *ip, uint16_t port);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get TCP Client status
|
|
||||||
* @param status Pointer to status structure
|
|
||||||
*/
|
|
||||||
void tcp_client_get_status(tcp_client_status_t *status);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get TCP Client RX StreamBuffer handle for UART integration
|
|
||||||
* @return StreamBuffer handle for receiving data from TCP
|
|
||||||
*/
|
|
||||||
void *tcp_client_get_rx_stream(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get TCP Client TX StreamBuffer handle for UART integration
|
|
||||||
* @return StreamBuffer handle for sending data to TCP
|
|
||||||
*/
|
|
||||||
void *tcp_client_get_tx_stream(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief TCP Client task function (for FreeRTOS)
|
|
||||||
* @param argument Task argument (unused)
|
|
||||||
*/
|
|
||||||
void tcp_client_task(void *argument);
|
|
||||||
|
|
||||||
void tcp_client_poll(void);
|
void tcp_client_poll(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
+107
-87
@@ -1,12 +1,14 @@
|
|||||||
/**
|
/**
|
||||||
* @file tcp_server.c
|
* @file tcp_server.c
|
||||||
* @brief lwIP RAW TCP server for the UART2 bridge.
|
* @brief Indexed lwIP RAW TCP server manager.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "tcp_server.h"
|
#include "tcp_server.h"
|
||||||
|
|
||||||
#include "lwip/pbuf.h"
|
#include "../Drivers/LwIP/src/include/lwip/pbuf.h"
|
||||||
#include "lwip/tcp.h"
|
#include "../Drivers/LwIP/src/include/lwip/tcp.h"
|
||||||
|
|
||||||
|
#include "SEGGER_RTT.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@@ -16,11 +18,12 @@ typedef struct {
|
|||||||
uint8_t rx_ring[TCP_SERVER_RX_BUFFER_SIZE];
|
uint8_t rx_ring[TCP_SERVER_RX_BUFFER_SIZE];
|
||||||
uint16_t rx_head;
|
uint16_t rx_head;
|
||||||
uint16_t rx_tail;
|
uint16_t rx_tail;
|
||||||
tcp_server_config_t config;
|
uint8_t index;
|
||||||
|
tcp_server_instance_config_t config;
|
||||||
tcp_server_status_t status;
|
tcp_server_status_t status;
|
||||||
} tcp_server_ctx_t;
|
} tcp_server_ctx_t;
|
||||||
|
|
||||||
static tcp_server_ctx_t g_server;
|
static tcp_server_ctx_t g_servers[TCP_SERVER_INSTANCE_COUNT];
|
||||||
|
|
||||||
static uint16_t ring_free(uint16_t head, uint16_t tail, uint16_t size)
|
static uint16_t ring_free(uint16_t head, uint16_t tail, uint16_t size)
|
||||||
{
|
{
|
||||||
@@ -32,13 +35,18 @@ static err_t tcp_server_on_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p,
|
|||||||
tcp_server_ctx_t *ctx = (tcp_server_ctx_t *)arg;
|
tcp_server_ctx_t *ctx = (tcp_server_ctx_t *)arg;
|
||||||
struct pbuf *q;
|
struct pbuf *q;
|
||||||
|
|
||||||
|
if (ctx == NULL) {
|
||||||
|
if (p != NULL) {
|
||||||
|
pbuf_free(p);
|
||||||
|
}
|
||||||
|
return ERR_ARG;
|
||||||
|
}
|
||||||
if (err != ERR_OK) {
|
if (err != ERR_OK) {
|
||||||
if (p != NULL) {
|
if (p != NULL) {
|
||||||
pbuf_free(p);
|
pbuf_free(p);
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p == NULL) {
|
if (p == NULL) {
|
||||||
tcp_arg(pcb, NULL);
|
tcp_arg(pcb, NULL);
|
||||||
tcp_recv(pcb, NULL);
|
tcp_recv(pcb, NULL);
|
||||||
@@ -48,7 +56,7 @@ static err_t tcp_server_on_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p,
|
|||||||
tcp_abort(pcb);
|
tcp_abort(pcb);
|
||||||
}
|
}
|
||||||
ctx->client_pcb = NULL;
|
ctx->client_pcb = NULL;
|
||||||
ctx->status.state = TCP_SERVER_STATE_LISTENING;
|
ctx->status.state = ctx->config.enabled ? TCP_SERVER_STATE_LISTENING : TCP_SERVER_STATE_IDLE;
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,27 +82,31 @@ static err_t tcp_server_on_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
|
|||||||
{
|
{
|
||||||
tcp_server_ctx_t *ctx = (tcp_server_ctx_t *)arg;
|
tcp_server_ctx_t *ctx = (tcp_server_ctx_t *)arg;
|
||||||
(void)pcb;
|
(void)pcb;
|
||||||
|
if (ctx != NULL) {
|
||||||
ctx->status.tx_bytes += len;
|
ctx->status.tx_bytes += len;
|
||||||
|
}
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tcp_server_on_err(void *arg, err_t err)
|
static void tcp_server_on_err(void *arg, err_t err)
|
||||||
{
|
{
|
||||||
tcp_server_ctx_t *ctx = (tcp_server_ctx_t *)arg;
|
tcp_server_ctx_t *ctx = (tcp_server_ctx_t *)arg;
|
||||||
(void)err;
|
if (ctx == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
ctx->client_pcb = NULL;
|
ctx->client_pcb = NULL;
|
||||||
ctx->status.state = TCP_SERVER_STATE_LISTENING;
|
ctx->status.state = ctx->config.enabled ? TCP_SERVER_STATE_LISTENING : TCP_SERVER_STATE_IDLE;
|
||||||
ctx->status.errors++;
|
ctx->status.errors++;
|
||||||
|
SEGGER_RTT_printf(0, "TCP server[%u] connection error=%d\r\n", ctx->index, (int)err);
|
||||||
}
|
}
|
||||||
|
|
||||||
static err_t tcp_server_on_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
|
static err_t tcp_server_on_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
|
||||||
{
|
{
|
||||||
tcp_server_ctx_t *ctx = (tcp_server_ctx_t *)arg;
|
tcp_server_ctx_t *ctx = (tcp_server_ctx_t *)arg;
|
||||||
|
|
||||||
if (err != ERR_OK) {
|
if (ctx == NULL || err != ERR_OK) {
|
||||||
return err;
|
return (ctx == NULL) ? ERR_ARG : err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->client_pcb != NULL) {
|
if (ctx->client_pcb != NULL) {
|
||||||
tcp_abort(newpcb);
|
tcp_abort(newpcb);
|
||||||
return ERR_ABRT;
|
return ERR_ABRT;
|
||||||
@@ -103,7 +115,7 @@ static err_t tcp_server_on_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
|
|||||||
ctx->client_pcb = newpcb;
|
ctx->client_pcb = newpcb;
|
||||||
ctx->status.state = TCP_SERVER_STATE_CONNECTED;
|
ctx->status.state = TCP_SERVER_STATE_CONNECTED;
|
||||||
ctx->status.connections++;
|
ctx->status.connections++;
|
||||||
|
tcp_nagle_disable(newpcb);
|
||||||
tcp_arg(newpcb, ctx);
|
tcp_arg(newpcb, ctx);
|
||||||
tcp_recv(newpcb, tcp_server_on_recv);
|
tcp_recv(newpcb, tcp_server_on_recv);
|
||||||
tcp_sent(newpcb, tcp_server_on_sent);
|
tcp_sent(newpcb, tcp_server_on_sent);
|
||||||
@@ -111,143 +123,151 @@ static err_t tcp_server_on_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
|
|||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tcp_server_init(const tcp_server_config_t *config)
|
int tcp_server_init_all(void)
|
||||||
{
|
{
|
||||||
memset(&g_server, 0, sizeof(g_server));
|
memset(g_servers, 0, sizeof(g_servers));
|
||||||
g_server.config.port = TCP_SERVER_DEFAULT_PORT;
|
for (uint8_t i = 0; i < TCP_SERVER_INSTANCE_COUNT; ++i) {
|
||||||
g_server.config.auto_reconnect = true;
|
g_servers[i].index = i;
|
||||||
g_server.status.state = TCP_SERVER_STATE_IDLE;
|
g_servers[i].status.state = TCP_SERVER_STATE_IDLE;
|
||||||
|
|
||||||
if (config != NULL) {
|
|
||||||
g_server.config = *config;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tcp_server_start(void)
|
int tcp_server_config(uint8_t instance, const tcp_server_instance_config_t *config)
|
||||||
|
{
|
||||||
|
if (instance >= TCP_SERVER_INSTANCE_COUNT || config == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
g_servers[instance].config = *config;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tcp_server_start(uint8_t instance)
|
||||||
{
|
{
|
||||||
struct tcp_pcb *pcb;
|
struct tcp_pcb *pcb;
|
||||||
err_t err;
|
err_t err;
|
||||||
|
tcp_server_ctx_t *ctx;
|
||||||
|
|
||||||
if (g_server.listen_pcb != NULL) {
|
if (instance >= TCP_SERVER_INSTANCE_COUNT) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ctx = &g_servers[instance];
|
||||||
|
if (!ctx->config.enabled) {
|
||||||
|
ctx->status.state = TCP_SERVER_STATE_IDLE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ctx->listen_pcb != NULL) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pcb = tcp_new_ip_type(IPADDR_TYPE_V4);
|
pcb = tcp_new_ip_type(IPADDR_TYPE_V4);
|
||||||
if (pcb == NULL) {
|
if (pcb == NULL) {
|
||||||
g_server.status.errors++;
|
ctx->status.errors++;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = tcp_bind(pcb, IP_ANY_TYPE, g_server.config.port);
|
err = tcp_bind(pcb, IP_ANY_TYPE, ctx->config.port);
|
||||||
if (err != ERR_OK) {
|
if (err != ERR_OK) {
|
||||||
tcp_abort(pcb);
|
tcp_abort(pcb);
|
||||||
g_server.status.errors++;
|
ctx->status.errors++;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_server.listen_pcb = tcp_listen_with_backlog(pcb, 1);
|
ctx->listen_pcb = tcp_listen_with_backlog(pcb, 1);
|
||||||
if (g_server.listen_pcb == NULL) {
|
if (ctx->listen_pcb == NULL) {
|
||||||
g_server.status.errors++;
|
ctx->status.errors++;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
tcp_arg(g_server.listen_pcb, &g_server);
|
tcp_arg(ctx->listen_pcb, ctx);
|
||||||
tcp_accept(g_server.listen_pcb, tcp_server_on_accept);
|
tcp_accept(ctx->listen_pcb, tcp_server_on_accept);
|
||||||
g_server.status.state = TCP_SERVER_STATE_LISTENING;
|
ctx->status.state = TCP_SERVER_STATE_LISTENING;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tcp_server_stop(void)
|
int tcp_server_stop(uint8_t instance)
|
||||||
{
|
{
|
||||||
if (g_server.client_pcb != NULL) {
|
tcp_server_ctx_t *ctx;
|
||||||
tcp_arg(g_server.client_pcb, NULL);
|
|
||||||
tcp_recv(g_server.client_pcb, NULL);
|
if (instance >= TCP_SERVER_INSTANCE_COUNT) {
|
||||||
tcp_sent(g_server.client_pcb, NULL);
|
return -1;
|
||||||
tcp_err(g_server.client_pcb, NULL);
|
}
|
||||||
tcp_abort(g_server.client_pcb);
|
ctx = &g_servers[instance];
|
||||||
g_server.client_pcb = NULL;
|
|
||||||
|
if (ctx->client_pcb != NULL) {
|
||||||
|
tcp_arg(ctx->client_pcb, NULL);
|
||||||
|
tcp_recv(ctx->client_pcb, NULL);
|
||||||
|
tcp_sent(ctx->client_pcb, NULL);
|
||||||
|
tcp_err(ctx->client_pcb, NULL);
|
||||||
|
tcp_abort(ctx->client_pcb);
|
||||||
|
ctx->client_pcb = NULL;
|
||||||
|
}
|
||||||
|
if (ctx->listen_pcb != NULL) {
|
||||||
|
tcp_arg(ctx->listen_pcb, NULL);
|
||||||
|
tcp_accept(ctx->listen_pcb, NULL);
|
||||||
|
tcp_close(ctx->listen_pcb);
|
||||||
|
ctx->listen_pcb = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_server.listen_pcb != NULL) {
|
ctx->status.state = TCP_SERVER_STATE_IDLE;
|
||||||
tcp_arg(g_server.listen_pcb, NULL);
|
ctx->rx_head = 0u;
|
||||||
tcp_accept(g_server.listen_pcb, NULL);
|
ctx->rx_tail = 0u;
|
||||||
tcp_close(g_server.listen_pcb);
|
|
||||||
g_server.listen_pcb = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_server.status.state = TCP_SERVER_STATE_IDLE;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tcp_server_send(const uint8_t *data, uint16_t len)
|
int tcp_server_send(uint8_t instance, const uint8_t *data, uint16_t len)
|
||||||
{
|
{
|
||||||
err_t err;
|
err_t err;
|
||||||
|
tcp_server_ctx_t *ctx;
|
||||||
|
|
||||||
if (g_server.client_pcb == NULL || data == NULL || len == 0u) {
|
if (instance >= TCP_SERVER_INSTANCE_COUNT || data == NULL || len == 0u) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
ctx = &g_servers[instance];
|
||||||
if (tcp_sndbuf(g_server.client_pcb) < len) {
|
if (ctx->client_pcb == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (tcp_sndbuf(ctx->client_pcb) < len) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = tcp_write(g_server.client_pcb, data, len, TCP_WRITE_FLAG_COPY);
|
err = tcp_write(ctx->client_pcb, data, len, TCP_WRITE_FLAG_COPY);
|
||||||
if (err != ERR_OK) {
|
if (err != ERR_OK) {
|
||||||
g_server.status.errors++;
|
ctx->status.errors++;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
err = tcp_output(ctx->client_pcb);
|
||||||
err = tcp_output(g_server.client_pcb);
|
|
||||||
if (err != ERR_OK) {
|
if (err != ERR_OK) {
|
||||||
g_server.status.errors++;
|
ctx->status.errors++;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (int)len;
|
return (int)len;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tcp_server_recv(uint8_t *data, uint16_t max_len, uint32_t timeout_ms)
|
int tcp_server_recv(uint8_t instance, uint8_t *data, uint16_t max_len)
|
||||||
{
|
{
|
||||||
uint16_t copied = 0u;
|
uint16_t copied = 0u;
|
||||||
(void)timeout_ms;
|
tcp_server_ctx_t *ctx;
|
||||||
|
|
||||||
if (data == NULL || max_len == 0u) {
|
if (instance >= TCP_SERVER_INSTANCE_COUNT || data == NULL || max_len == 0u) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
ctx = &g_servers[instance];
|
||||||
while (copied < max_len && g_server.rx_tail != g_server.rx_head) {
|
while (copied < max_len && ctx->rx_tail != ctx->rx_head) {
|
||||||
data[copied++] = g_server.rx_ring[g_server.rx_tail];
|
data[copied++] = ctx->rx_ring[ctx->rx_tail];
|
||||||
g_server.rx_tail = (uint16_t)((g_server.rx_tail + 1u) % TCP_SERVER_RX_BUFFER_SIZE);
|
ctx->rx_tail = (uint16_t)((ctx->rx_tail + 1u) % TCP_SERVER_RX_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (int)copied;
|
return (int)copied;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tcp_server_is_connected(void)
|
bool tcp_server_is_connected(uint8_t instance)
|
||||||
{
|
{
|
||||||
return g_server.client_pcb != NULL;
|
return (instance < TCP_SERVER_INSTANCE_COUNT) && (g_servers[instance].client_pcb != NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tcp_server_get_status(tcp_server_status_t *status)
|
void tcp_server_get_status(uint8_t instance, tcp_server_status_t *status)
|
||||||
{
|
{
|
||||||
if (status != NULL) {
|
if (instance < TCP_SERVER_INSTANCE_COUNT && status != NULL) {
|
||||||
*status = g_server.status;
|
*status = g_servers[instance].status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void *tcp_server_get_rx_stream(void)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *tcp_server_get_tx_stream(void)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void tcp_server_task(void *argument)
|
|
||||||
{
|
|
||||||
(void)argument;
|
|
||||||
}
|
|
||||||
|
|||||||
+15
-82
@@ -1,43 +1,33 @@
|
|||||||
/**
|
/**
|
||||||
* @file tcp_server.h
|
* @file tcp_server.h
|
||||||
* @brief TCP Server module for transparent transmission with UART2
|
* @brief Indexed lwIP RAW TCP server manager.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __TCP_SERVER_H__
|
#ifndef __TCP_SERVER_H__
|
||||||
#define __TCP_SERVER_H__
|
#define __TCP_SERVER_H__
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Default TCP Server port */
|
#define TCP_SERVER_INSTANCE_COUNT 2u
|
||||||
#define TCP_SERVER_DEFAULT_PORT 8080
|
#define TCP_SERVER_RX_BUFFER_SIZE 512u
|
||||||
|
|
||||||
/* Maximum number of simultaneous connections */
|
|
||||||
#define TCP_SERVER_MAX_CONNECTIONS 1
|
|
||||||
|
|
||||||
/* Buffer sizes */
|
|
||||||
#define TCP_SERVER_RX_BUFFER_SIZE 512
|
|
||||||
#define TCP_SERVER_TX_BUFFER_SIZE 512
|
|
||||||
|
|
||||||
/* TCP Server state */
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TCP_SERVER_STATE_IDLE,
|
TCP_SERVER_STATE_IDLE = 0,
|
||||||
TCP_SERVER_STATE_LISTENING,
|
TCP_SERVER_STATE_LISTENING,
|
||||||
TCP_SERVER_STATE_CONNECTED,
|
TCP_SERVER_STATE_CONNECTED,
|
||||||
TCP_SERVER_STATE_ERROR
|
TCP_SERVER_STATE_ERROR
|
||||||
} tcp_server_state_t;
|
} tcp_server_state_t;
|
||||||
|
|
||||||
/* TCP Server configuration */
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
bool auto_reconnect;
|
bool enabled;
|
||||||
} tcp_server_config_t;
|
} tcp_server_instance_config_t;
|
||||||
|
|
||||||
/* TCP Server status */
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
tcp_server_state_t state;
|
tcp_server_state_t state;
|
||||||
uint32_t rx_bytes;
|
uint32_t rx_bytes;
|
||||||
@@ -46,71 +36,14 @@ typedef struct {
|
|||||||
uint32_t errors;
|
uint32_t errors;
|
||||||
} tcp_server_status_t;
|
} tcp_server_status_t;
|
||||||
|
|
||||||
/**
|
int tcp_server_init_all(void);
|
||||||
* @brief Initialize TCP Server module
|
int tcp_server_config(uint8_t instance, const tcp_server_instance_config_t *config);
|
||||||
* @param config Server configuration
|
int tcp_server_start(uint8_t instance);
|
||||||
* @return 0 on success, negative on error
|
int tcp_server_stop(uint8_t instance);
|
||||||
*/
|
int tcp_server_send(uint8_t instance, const uint8_t *data, uint16_t len);
|
||||||
int tcp_server_init(const tcp_server_config_t *config);
|
int tcp_server_recv(uint8_t instance, uint8_t *data, uint16_t max_len);
|
||||||
|
bool tcp_server_is_connected(uint8_t instance);
|
||||||
/**
|
void tcp_server_get_status(uint8_t instance, tcp_server_status_t *status);
|
||||||
* @brief Start TCP Server (begin listening)
|
|
||||||
* @return 0 on success, negative on error
|
|
||||||
*/
|
|
||||||
int tcp_server_start(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Stop TCP Server
|
|
||||||
* @return 0 on success, negative on error
|
|
||||||
*/
|
|
||||||
int tcp_server_stop(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Send data to connected client
|
|
||||||
* @param data Data buffer
|
|
||||||
* @param len Data length
|
|
||||||
* @return Number of bytes sent, negative on error
|
|
||||||
*/
|
|
||||||
int tcp_server_send(const uint8_t *data, uint16_t len);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Receive data from connected client
|
|
||||||
* @param data Data buffer
|
|
||||||
* @param max_len Maximum length to receive
|
|
||||||
* @param timeout_ms Timeout in milliseconds (0 = non-blocking)
|
|
||||||
* @return Number of bytes received, 0 if no data, negative on error
|
|
||||||
*/
|
|
||||||
int tcp_server_recv(uint8_t *data, uint16_t max_len, uint32_t timeout_ms);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if client is connected
|
|
||||||
* @return true if connected
|
|
||||||
*/
|
|
||||||
bool tcp_server_is_connected(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get TCP Server status
|
|
||||||
* @param status Pointer to status structure
|
|
||||||
*/
|
|
||||||
void tcp_server_get_status(tcp_server_status_t *status);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get TCP Server StreamBuffer handle for UART integration
|
|
||||||
* @return StreamBuffer handle for receiving data from TCP
|
|
||||||
*/
|
|
||||||
void *tcp_server_get_rx_stream(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get TCP Server TX StreamBuffer handle for UART integration
|
|
||||||
* @return StreamBuffer handle for sending data to TCP
|
|
||||||
*/
|
|
||||||
void *tcp_server_get_tx_stream(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief TCP Server task function (for FreeRTOS)
|
|
||||||
* @param argument Task argument (unused)
|
|
||||||
*/
|
|
||||||
void tcp_server_task(void *argument);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
+113
-82
@@ -1,14 +1,17 @@
|
|||||||
/**
|
/**
|
||||||
* @file uart_trans.c
|
* @file uart_trans.c
|
||||||
* @brief Bare-metal UART DMA/IDLE transport layer.
|
* @brief Bare-metal UART DMA/IDLE transport and MUX helpers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "uart_trans.h"
|
#include "uart_trans.h"
|
||||||
|
|
||||||
#include "usart.h"
|
#include "../Core/Inc/usart.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#define UART_MUX_SYNC 0x7Eu
|
||||||
|
#define UART_MUX_TAIL 0x7Fu
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
UART_HandleTypeDef *huart;
|
UART_HandleTypeDef *huart;
|
||||||
uint8_t rx_dma_buffer[UART_RX_DMA_BUFFER_SIZE];
|
uint8_t rx_dma_buffer[UART_RX_DMA_BUFFER_SIZE];
|
||||||
@@ -40,55 +43,23 @@ static uint16_t ring_free(uint16_t head, uint16_t tail, uint16_t size)
|
|||||||
return (uint16_t)(size - ring_used(head, tail, size) - 1u);
|
return (uint16_t)(size - ring_used(head, tail, size) - 1u);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void apply_default_config(uart_channel_ctx_t *ctx)
|
|
||||||
{
|
|
||||||
ctx->config.baudrate = UART_DEFAULT_BAUDRATE;
|
|
||||||
ctx->config.data_bits = UART_DEFAULT_DATA_BITS;
|
|
||||||
ctx->config.stop_bits = UART_DEFAULT_STOP_BITS;
|
|
||||||
ctx->config.parity = UART_DEFAULT_PARITY;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int apply_uart_config(uart_channel_t channel)
|
static int apply_uart_config(uart_channel_t channel)
|
||||||
{
|
{
|
||||||
uart_channel_ctx_t *ctx = &g_channels[channel];
|
uart_channel_ctx_t *ctx = &g_channels[channel];
|
||||||
UART_HandleTypeDef *huart = ctx->huart;
|
if (ctx->huart == NULL) {
|
||||||
uint32_t word_length;
|
|
||||||
uint32_t parity;
|
|
||||||
|
|
||||||
if (huart == NULL) {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->running) {
|
if (ctx->running) {
|
||||||
HAL_UART_DMAStop(huart);
|
HAL_UART_DMAStop(ctx->huart);
|
||||||
ctx->running = false;
|
ctx->running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
huart->Init.BaudRate = ctx->config.baudrate;
|
ctx->huart->Init.BaudRate = ctx->config.baudrate;
|
||||||
huart->Init.StopBits = (ctx->config.stop_bits == 2u) ? UART_STOPBITS_2 : UART_STOPBITS_1;
|
ctx->huart->Init.WordLength = UART_WORDLENGTH_8B;
|
||||||
|
ctx->huart->Init.StopBits = UART_STOPBITS_1;
|
||||||
switch (ctx->config.parity) {
|
ctx->huart->Init.Parity = UART_PARITY_NONE;
|
||||||
case 1:
|
return (HAL_UART_Init(ctx->huart) == HAL_OK) ? 0 : -1;
|
||||||
parity = UART_PARITY_ODD;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
parity = UART_PARITY_EVEN;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
parity = UART_PARITY_NONE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parity == UART_PARITY_NONE) {
|
|
||||||
word_length = (ctx->config.data_bits == 9u) ? UART_WORDLENGTH_9B : UART_WORDLENGTH_8B;
|
|
||||||
} else {
|
|
||||||
word_length = (ctx->config.data_bits >= 8u) ? UART_WORDLENGTH_9B : UART_WORDLENGTH_8B;
|
|
||||||
}
|
|
||||||
|
|
||||||
huart->Init.WordLength = word_length;
|
|
||||||
huart->Init.Parity = parity;
|
|
||||||
|
|
||||||
return (HAL_UART_Init(huart) == HAL_OK) ? 0 : -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void process_rx_snapshot(uart_channel_t channel, uint16_t dma_write_index)
|
static void process_rx_snapshot(uart_channel_t channel, uint16_t dma_write_index)
|
||||||
@@ -96,9 +67,7 @@ static void process_rx_snapshot(uart_channel_t channel, uint16_t dma_write_index
|
|||||||
uart_channel_ctx_t *ctx = &g_channels[channel];
|
uart_channel_ctx_t *ctx = &g_channels[channel];
|
||||||
|
|
||||||
while (ctx->rx_dma_read_index != dma_write_index) {
|
while (ctx->rx_dma_read_index != dma_write_index) {
|
||||||
uint16_t next_head;
|
uint16_t next_head = (uint16_t)((ctx->rx_head + 1u) % UART_RX_RING_BUFFER_SIZE);
|
||||||
|
|
||||||
next_head = (uint16_t)((ctx->rx_head + 1u) % UART_RX_RING_BUFFER_SIZE);
|
|
||||||
if (next_head == ctx->rx_tail) {
|
if (next_head == ctx->rx_tail) {
|
||||||
ctx->stats.errors++;
|
ctx->stats.errors++;
|
||||||
break;
|
break;
|
||||||
@@ -149,16 +118,12 @@ static void kick_tx(uart_channel_t channel)
|
|||||||
int uart_trans_init(void)
|
int uart_trans_init(void)
|
||||||
{
|
{
|
||||||
memset(g_channels, 0, sizeof(g_channels));
|
memset(g_channels, 0, sizeof(g_channels));
|
||||||
|
g_channels[UART_CHANNEL_U0].huart = &huart2;
|
||||||
g_channels[UART_CHANNEL_SERVER].huart = &huart2;
|
g_channels[UART_CHANNEL_U1].huart = &huart3;
|
||||||
g_channels[UART_CHANNEL_CLIENT].huart = &huart3;
|
g_channels[UART_CHANNEL_U0].config.baudrate = UART_DEFAULT_BAUDRATE;
|
||||||
|
g_channels[UART_CHANNEL_U1].config.baudrate = UART_DEFAULT_BAUDRATE;
|
||||||
apply_default_config(&g_channels[UART_CHANNEL_SERVER]);
|
g_channels[UART_CHANNEL_U0].initialized = true;
|
||||||
apply_default_config(&g_channels[UART_CHANNEL_CLIENT]);
|
g_channels[UART_CHANNEL_U1].initialized = true;
|
||||||
|
|
||||||
g_channels[UART_CHANNEL_SERVER].initialized = true;
|
|
||||||
g_channels[UART_CHANNEL_CLIENT].initialized = true;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,7 +132,6 @@ int uart_trans_config(uart_channel_t channel, const uart_config_t *config)
|
|||||||
if (channel >= UART_CHANNEL_MAX || config == NULL) {
|
if (channel >= UART_CHANNEL_MAX || config == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_channels[channel].config = *config;
|
g_channels[channel].config = *config;
|
||||||
return apply_uart_config(channel);
|
return apply_uart_config(channel);
|
||||||
}
|
}
|
||||||
@@ -208,29 +172,16 @@ int uart_trans_stop(uart_channel_t channel)
|
|||||||
if (channel >= UART_CHANNEL_MAX) {
|
if (channel >= UART_CHANNEL_MAX) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
HAL_UART_DMAStop(g_channels[channel].huart);
|
HAL_UART_DMAStop(g_channels[channel].huart);
|
||||||
g_channels[channel].running = false;
|
g_channels[channel].running = false;
|
||||||
g_channels[channel].tx_busy = false;
|
g_channels[channel].tx_busy = false;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void uart_trans_get_stats(uart_channel_t channel, uart_stats_t *stats)
|
void uart_trans_poll(void)
|
||||||
{
|
{
|
||||||
if (channel >= UART_CHANNEL_MAX || stats == NULL) {
|
kick_tx(UART_CHANNEL_U0);
|
||||||
return;
|
kick_tx(UART_CHANNEL_U1);
|
||||||
}
|
|
||||||
|
|
||||||
*stats = g_channels[channel].stats;
|
|
||||||
}
|
|
||||||
|
|
||||||
void uart_trans_reset_stats(uart_channel_t channel)
|
|
||||||
{
|
|
||||||
if (channel >= UART_CHANNEL_MAX) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&g_channels[channel].stats, 0, sizeof(g_channels[channel].stats));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t uart_trans_rx_available(uart_channel_t channel)
|
uint16_t uart_trans_rx_available(uart_channel_t channel)
|
||||||
@@ -238,7 +189,6 @@ uint16_t uart_trans_rx_available(uart_channel_t channel)
|
|||||||
if (channel >= UART_CHANNEL_MAX) {
|
if (channel >= UART_CHANNEL_MAX) {
|
||||||
return 0u;
|
return 0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ring_used(g_channels[channel].rx_head, g_channels[channel].rx_tail, UART_RX_RING_BUFFER_SIZE);
|
return ring_used(g_channels[channel].rx_head, g_channels[channel].rx_tail, UART_RX_RING_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,11 +206,9 @@ uint16_t uart_trans_read(uart_channel_t channel, uint8_t *data, uint16_t max_len
|
|||||||
data[copied++] = ctx->rx_ring[ctx->rx_tail];
|
data[copied++] = ctx->rx_ring[ctx->rx_tail];
|
||||||
ctx->rx_tail = (uint16_t)((ctx->rx_tail + 1u) % UART_RX_RING_BUFFER_SIZE);
|
ctx->rx_tail = (uint16_t)((ctx->rx_tail + 1u) % UART_RX_RING_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (copied > 0u) {
|
if (copied > 0u) {
|
||||||
ctx->stats.rx_packets++;
|
ctx->stats.rx_packets++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return copied;
|
return copied;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,10 +235,18 @@ uint16_t uart_trans_write(uart_channel_t channel, const uint8_t *data, uint16_t
|
|||||||
return written;
|
return written;
|
||||||
}
|
}
|
||||||
|
|
||||||
void uart_trans_poll(void)
|
void uart_trans_get_stats(uart_channel_t channel, uart_stats_t *stats)
|
||||||
{
|
{
|
||||||
kick_tx(UART_CHANNEL_SERVER);
|
if (channel < UART_CHANNEL_MAX && stats != NULL) {
|
||||||
kick_tx(UART_CHANNEL_CLIENT);
|
*stats = g_channels[channel].stats;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void uart_trans_reset_stats(uart_channel_t channel)
|
||||||
|
{
|
||||||
|
if (channel < UART_CHANNEL_MAX) {
|
||||||
|
memset(&g_channels[channel].stats, 0, sizeof(g_channels[channel].stats));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void uart_trans_idle_handler(uart_channel_t channel)
|
void uart_trans_idle_handler(uart_channel_t channel)
|
||||||
@@ -301,14 +257,12 @@ void uart_trans_idle_handler(uart_channel_t channel)
|
|||||||
if (channel >= UART_CHANNEL_MAX) {
|
if (channel >= UART_CHANNEL_MAX) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
huart = g_channels[channel].huart;
|
huart = g_channels[channel].huart;
|
||||||
g_channels[channel].stats.idle_events++;
|
g_channels[channel].stats.idle_events++;
|
||||||
dma_write_index = (uint16_t)(UART_RX_DMA_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(huart->hdmarx));
|
dma_write_index = (uint16_t)(UART_RX_DMA_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(huart->hdmarx));
|
||||||
if (dma_write_index >= UART_RX_DMA_BUFFER_SIZE) {
|
if (dma_write_index >= UART_RX_DMA_BUFFER_SIZE) {
|
||||||
dma_write_index = 0u;
|
dma_write_index = 0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
process_rx_snapshot(channel, dma_write_index);
|
process_rx_snapshot(channel, dma_write_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,7 +271,6 @@ void uart_trans_rx_half_cplt_handler(uart_channel_t channel)
|
|||||||
if (channel >= UART_CHANNEL_MAX) {
|
if (channel >= UART_CHANNEL_MAX) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_channels[channel].stats.rx_half_events++;
|
g_channels[channel].stats.rx_half_events++;
|
||||||
process_rx_snapshot(channel, UART_RX_DMA_BUFFER_SIZE / 2u);
|
process_rx_snapshot(channel, UART_RX_DMA_BUFFER_SIZE / 2u);
|
||||||
}
|
}
|
||||||
@@ -327,7 +280,6 @@ void uart_trans_rx_cplt_handler(uart_channel_t channel)
|
|||||||
if (channel >= UART_CHANNEL_MAX) {
|
if (channel >= UART_CHANNEL_MAX) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_channels[channel].stats.rx_full_events++;
|
g_channels[channel].stats.rx_full_events++;
|
||||||
process_rx_snapshot(channel, 0u);
|
process_rx_snapshot(channel, 0u);
|
||||||
}
|
}
|
||||||
@@ -337,9 +289,88 @@ void uart_trans_tx_cplt_handler(uart_channel_t channel)
|
|||||||
if (channel >= UART_CHANNEL_MAX) {
|
if (channel >= UART_CHANNEL_MAX) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_channels[channel].tx_busy = false;
|
g_channels[channel].tx_busy = false;
|
||||||
g_channels[channel].stats.tx_bytes += g_channels[channel].tx_dma_len;
|
g_channels[channel].stats.tx_bytes += g_channels[channel].tx_dma_len;
|
||||||
g_channels[channel].tx_dma_len = 0u;
|
g_channels[channel].tx_dma_len = 0u;
|
||||||
kick_tx(channel);
|
kick_tx(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool uart_mux_try_extract_frame(uart_channel_t channel, uart_mux_frame_t *frame)
|
||||||
|
{
|
||||||
|
uint8_t header[5];
|
||||||
|
uint16_t available;
|
||||||
|
uint16_t payload_len;
|
||||||
|
|
||||||
|
if (channel >= UART_CHANNEL_MAX || frame == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
available = uart_trans_rx_available(channel);
|
||||||
|
if (available < 6u) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uart_trans_read(channel, header, sizeof(header)) != sizeof(header)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (header[0] != UART_MUX_SYNC) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
payload_len = (uint16_t)(((uint16_t)header[1] << 8) | header[2]);
|
||||||
|
if (payload_len > sizeof(frame->payload)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (uart_trans_rx_available(channel) < (uint16_t)(payload_len + 1u)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame->src_id = header[3];
|
||||||
|
frame->dst_mask = header[4];
|
||||||
|
frame->payload_len = payload_len;
|
||||||
|
if (payload_len > 0u) {
|
||||||
|
if (uart_trans_read(channel, frame->payload, payload_len) != payload_len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
uint8_t tail = 0u;
|
||||||
|
if (uart_trans_read(channel, &tail, 1u) != 1u || tail != UART_MUX_TAIL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool uart_mux_encode_frame(uint8_t src_id,
|
||||||
|
uint8_t dst_mask,
|
||||||
|
const uint8_t *payload,
|
||||||
|
uint16_t payload_len,
|
||||||
|
uint8_t *out,
|
||||||
|
uint16_t *out_len,
|
||||||
|
uint16_t out_capacity)
|
||||||
|
{
|
||||||
|
uint16_t frame_len;
|
||||||
|
|
||||||
|
if (out == NULL || out_len == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
frame_len = (uint16_t)(payload_len + 6u);
|
||||||
|
if (frame_len > out_capacity) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
out[0] = UART_MUX_SYNC;
|
||||||
|
out[1] = (uint8_t)(payload_len >> 8);
|
||||||
|
out[2] = (uint8_t)(payload_len & 0xFFu);
|
||||||
|
out[3] = src_id;
|
||||||
|
out[4] = dst_mask;
|
||||||
|
if (payload_len > 0u && payload != NULL) {
|
||||||
|
memcpy(&out[5], payload, payload_len);
|
||||||
|
}
|
||||||
|
out[5 + payload_len] = UART_MUX_TAIL;
|
||||||
|
*out_len = frame_len;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
+22
-14
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* @file uart_trans.h
|
* @file uart_trans.h
|
||||||
* @brief Bare-metal UART DMA/IDLE transport layer.
|
* @brief Bare-metal UART DMA/IDLE transport and MUX framing helpers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __UART_TRANS_H__
|
#ifndef __UART_TRANS_H__
|
||||||
@@ -14,28 +14,28 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
UART_CHANNEL_SERVER = 0,
|
UART_CHANNEL_U0 = 0,
|
||||||
UART_CHANNEL_CLIENT = 1,
|
UART_CHANNEL_U1 = 1,
|
||||||
UART_CHANNEL_MAX
|
UART_CHANNEL_MAX
|
||||||
} uart_channel_t;
|
} uart_channel_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t src_id;
|
||||||
|
uint8_t dst_mask;
|
||||||
|
uint16_t payload_len;
|
||||||
|
uint8_t payload[256];
|
||||||
|
} uart_mux_frame_t;
|
||||||
|
|
||||||
#define UART_RX_DMA_BUFFER_SIZE 128u
|
#define UART_RX_DMA_BUFFER_SIZE 128u
|
||||||
#define UART_TX_DMA_BUFFER_SIZE 128u
|
#define UART_TX_DMA_BUFFER_SIZE 128u
|
||||||
#define UART_RX_RING_BUFFER_SIZE 512u
|
#define UART_RX_RING_BUFFER_SIZE 256u
|
||||||
#define UART_TX_RING_BUFFER_SIZE 512u
|
#define UART_TX_RING_BUFFER_SIZE 256u
|
||||||
|
#define UART_DEFAULT_BAUDRATE 115200u
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t baudrate;
|
uint32_t baudrate;
|
||||||
uint8_t data_bits;
|
|
||||||
uint8_t stop_bits;
|
|
||||||
uint8_t parity;
|
|
||||||
} uart_config_t;
|
} uart_config_t;
|
||||||
|
|
||||||
#define UART_DEFAULT_BAUDRATE 115200u
|
|
||||||
#define UART_DEFAULT_DATA_BITS 8u
|
|
||||||
#define UART_DEFAULT_STOP_BITS 1u
|
|
||||||
#define UART_DEFAULT_PARITY 0u
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t rx_bytes;
|
uint32_t rx_bytes;
|
||||||
uint32_t tx_bytes;
|
uint32_t tx_bytes;
|
||||||
@@ -61,9 +61,17 @@ void uart_trans_idle_handler(uart_channel_t channel);
|
|||||||
void uart_trans_rx_half_cplt_handler(uart_channel_t channel);
|
void uart_trans_rx_half_cplt_handler(uart_channel_t channel);
|
||||||
void uart_trans_rx_cplt_handler(uart_channel_t channel);
|
void uart_trans_rx_cplt_handler(uart_channel_t channel);
|
||||||
void uart_trans_tx_cplt_handler(uart_channel_t channel);
|
void uart_trans_tx_cplt_handler(uart_channel_t channel);
|
||||||
|
bool uart_mux_try_extract_frame(uart_channel_t channel, uart_mux_frame_t *frame);
|
||||||
|
bool uart_mux_encode_frame(uint8_t src_id,
|
||||||
|
uint8_t dst_mask,
|
||||||
|
const uint8_t *payload,
|
||||||
|
uint16_t payload_len,
|
||||||
|
uint8_t *out,
|
||||||
|
uint16_t *out_len,
|
||||||
|
uint16_t out_capacity);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif /* __UART_TRANS_H__ */
|
||||||
|
|||||||
+105
@@ -0,0 +1,105 @@
|
|||||||
|
# CH390 最终结论报告
|
||||||
|
|
||||||
|
## 结论
|
||||||
|
|
||||||
|
本轮循环调试的最终结论是:
|
||||||
|
|
||||||
|
1. 当前工程中的主要软件问题已经完成收敛和清理。
|
||||||
|
2. CH390D 驱动、lwIP `netif`、ARP 与 ICMP 基本链路已经在实机上打通。
|
||||||
|
3. 本轮最终根因已确认不是普通软件逻辑错误,而是 CH390D 相关供电滤波电容虚焊,导致供电不稳定。
|
||||||
|
|
||||||
|
## 已完成的软件侧工作
|
||||||
|
|
||||||
|
本轮已完成并验证的事项包括:
|
||||||
|
|
||||||
|
1. 修复 PHY 访问无超时导致的永久卡死风险。
|
||||||
|
2. 修复未初始化 IWDG 句柄刷新导致的 HardFault。
|
||||||
|
3. 清理 CH390 运行时中断屏蔽范围,消除阻塞式 SPI 访问造成的运行时假死。
|
||||||
|
4. 重构 CH390 运行时所有权,避免多层并发触达底层 SPI 路径。
|
||||||
|
5. 在 `main()` 中移除重复 CH390 复位,避免启动阶段额外复位噪声。
|
||||||
|
6. 清理已确认 warning 来源,避免无效变量继续污染构建结果。
|
||||||
|
7. 增加 CH390 identity gate,避免在无效寄存器读回前继续执行默认配置和 PHY 初始化。
|
||||||
|
8. 曾增加 bit-bang 诊断读用于快速隔离问题,该临时调试路径已在当前代码中移除。
|
||||||
|
|
||||||
|
## 实机关键证据
|
||||||
|
|
||||||
|
### 1. MCU 自身正常工作
|
||||||
|
|
||||||
|
已验证:
|
||||||
|
|
||||||
|
1. RTT 正常输出。
|
||||||
|
2. 主循环正常运行。
|
||||||
|
3. `TIM4` 心跳正常。
|
||||||
|
4. 运行期不再出现此前已修复的 HardFault 和“长时间假死”症状。
|
||||||
|
|
||||||
|
### 2. 最终硬件根因已定位
|
||||||
|
|
||||||
|
最终实板排查结果:
|
||||||
|
|
||||||
|
1. 板载一颗 CH390D 供电相关滤波电容存在虚焊。
|
||||||
|
2. 该问题导致 CH390D 供电不稳定,表现为寄存器读写、链路状态和报文收发在调试过程中不一致。
|
||||||
|
3. 修复硬件后,实机已观察到:
|
||||||
|
- `VID=0x1C00`、`PID=0x9151`、`REV=0x2B`
|
||||||
|
- PHY 寄存器稳定可读
|
||||||
|
- `lwIP netif` 能进入 `LINK_UP`
|
||||||
|
- 设备可接收 ARP request 并发出 ARP reply
|
||||||
|
- 设备可接收 ICMP Echo Request 并发出 Echo Reply
|
||||||
|
|
||||||
|
### 3. 历史 bit-bang 对照结果(已归档)
|
||||||
|
|
||||||
|
在早期调试中,曾绕过 STM32 硬件 SPI 外设、直接用 GPIO 软件时序读取 `VIDL/VIDH/PIDL/PIDH/CHIPR`,RTT 输出为:
|
||||||
|
|
||||||
|
```text
|
||||||
|
CH390 bitbang VIDL=0xFF VIDH=0xFF PIDL=0xFF PIDH=0xFF CHIPR=0xFF
|
||||||
|
```
|
||||||
|
|
||||||
|
该历史证据用于定位阶段,当前仅保留结论,不再保留对应代码路径。它说明:
|
||||||
|
|
||||||
|
1. 在硬件未修复前,单看软件现象会误导排查方向。
|
||||||
|
2. 电源完整性问题会放大为看似“SPI/IRQ/RX/TX 都可疑”的复合症状。
|
||||||
|
|
||||||
|
## 外部参考对结论的支撑
|
||||||
|
|
||||||
|
对公开 CH390 / DM9051 实现的对照结果表明:
|
||||||
|
|
||||||
|
1. CH390 SPI 访问时序、模式选择和 RX SRAM 连续事务仍然值得严格对照参考实现。
|
||||||
|
2. 但本项目最终问题并非“参考实现缺失”,而是硬件供电缺陷放大了调试噪声。
|
||||||
|
3. 外部参考对软件排查有帮助,但不能替代板级供电与焊接检查。
|
||||||
|
|
||||||
|
## 当前最可信判断
|
||||||
|
|
||||||
|
最终确认的板级问题为:
|
||||||
|
|
||||||
|
1. CH390D 供电滤波电容虚焊。
|
||||||
|
2. 该虚焊导致供电稳定性不足,从而引出不稳定的寄存器读写、链路与收发行为。
|
||||||
|
|
||||||
|
## 版本库状态
|
||||||
|
|
||||||
|
本轮已创建一个阶段性 checkpoint commit:
|
||||||
|
|
||||||
|
1. `1808f99` `fix: harden CH390 bring-up diagnostics`
|
||||||
|
|
||||||
|
该提交记录了:
|
||||||
|
|
||||||
|
1. warning 清理
|
||||||
|
2. 移除重复复位
|
||||||
|
3. CH390 早期 identity gate
|
||||||
|
4. 链路变化稳定等待
|
||||||
|
|
||||||
|
## 推荐的下一步
|
||||||
|
|
||||||
|
后续更高价值的工作不再是继续怀疑 CH390 是否“完全不通”,而是:
|
||||||
|
|
||||||
|
1. 在硬件问题修复后补充长时间稳定性测试。
|
||||||
|
2. 验证 TCP Server / TCP Client 业务流量与桥接逻辑在修复硬件后的行为。
|
||||||
|
3. 保持驱动层日志最小化,仅在重新排障时按需开启详细 RTT。
|
||||||
|
|
||||||
|
## 收尾说明
|
||||||
|
|
||||||
|
本轮循环的退出条件已经满足:软件主路径已验证,且硬件根因已定位。
|
||||||
|
|
||||||
|
因此当前最合理的结论是:
|
||||||
|
|
||||||
|
1. CH390D 驱动、lwIP `netif`、ARP 和 ICMP 基本链路已在实机打通。
|
||||||
|
2. 本轮真正拦路的不是普通软件逻辑,而是板级供电滤波电容虚焊。
|
||||||
|
3. 后续应在硬件修复后的稳定板卡上继续推进应用层联调与文档收口。
|
||||||
+311
-245
@@ -4,16 +4,6 @@
|
|||||||
* @file : main.c
|
* @file : main.c
|
||||||
* @brief : Main program body
|
* @brief : Main program body
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* @attention
|
|
||||||
*
|
|
||||||
* Copyright (c) 2026 STMicroelectronics.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* This software is licensed under terms that can be found in the LICENSE file
|
|
||||||
* in the root directory of this software component.
|
|
||||||
* If no LICENSE file comes with this software, it is provided AS-IS.
|
|
||||||
*
|
|
||||||
******************************************************************************
|
|
||||||
*/
|
*/
|
||||||
/* USER CODE END Header */
|
/* USER CODE END Header */
|
||||||
/* Includes ------------------------------------------------------------------*/
|
/* Includes ------------------------------------------------------------------*/
|
||||||
@@ -34,8 +24,8 @@
|
|||||||
#include "CH390_Interface.h"
|
#include "CH390_Interface.h"
|
||||||
#include "SEGGER_RTT.h"
|
#include "SEGGER_RTT.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "flash_param.h"
|
|
||||||
#include "ethernetif.h"
|
#include "ethernetif.h"
|
||||||
|
#include "ch390_runtime.h"
|
||||||
#include "lwip/init.h"
|
#include "lwip/init.h"
|
||||||
#include "lwip/timeouts.h"
|
#include "lwip/timeouts.h"
|
||||||
#include "tcp_client.h"
|
#include "tcp_client.h"
|
||||||
@@ -43,80 +33,69 @@
|
|||||||
#include "uart_trans.h"
|
#include "uart_trans.h"
|
||||||
/* USER CODE END Includes */
|
/* USER CODE END Includes */
|
||||||
|
|
||||||
/* Private typedef -----------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN PTD */
|
|
||||||
|
|
||||||
/* USER CODE END PTD */
|
|
||||||
|
|
||||||
/* Private define ------------------------------------------------------------*/
|
/* Private define ------------------------------------------------------------*/
|
||||||
/* USER CODE BEGIN PD */
|
/* USER CODE BEGIN PD */
|
||||||
/* CH390 硬件控制引脚 */
|
|
||||||
#define CH390_RST_PIN GPIO_PIN_1
|
|
||||||
#define CH390_RST_PORT GPIOB
|
|
||||||
#define CH390_CS_PIN GPIO_PIN_4
|
|
||||||
#define CH390_CS_PORT GPIOA
|
|
||||||
|
|
||||||
/* LED 指示灯 */
|
|
||||||
#define LED_PIN GPIO_PIN_13
|
#define LED_PIN GPIO_PIN_13
|
||||||
#define LED_PORT GPIOC
|
#define LED_PORT GPIOC
|
||||||
|
#define APP_ROUTE_BUFFER_SIZE 256u
|
||||||
|
#define STACK_GUARD_WORD 0xA5A5A5A5u
|
||||||
/* USER CODE END PD */
|
/* USER CODE END PD */
|
||||||
|
|
||||||
/* Private macro -------------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN PM */
|
|
||||||
|
|
||||||
/* USER CODE END PM */
|
|
||||||
|
|
||||||
/* Private variables ---------------------------------------------------------*/
|
/* Private variables ---------------------------------------------------------*/
|
||||||
|
|
||||||
/* USER CODE BEGIN PV */
|
/* USER CODE BEGIN PV */
|
||||||
static volatile uint16_t g_led_blink_ticks = 0;
|
static volatile uint16_t g_led_blink_ticks = 0;
|
||||||
static uint8_t g_clock_fallback_to_hsi = 0u;
|
static uint8_t g_clock_fallback_to_hsi = 0u;
|
||||||
volatile uint8_t g_uart1_rx_probe_byte = 0u;
|
volatile uint8_t g_uart1_rx_probe_byte = 0u;
|
||||||
|
static uint8_t g_stack_guard_reported = 0u;
|
||||||
|
static uint8_t g_mux_response_frame[272];
|
||||||
|
static uint8_t g_links_started = 0u;
|
||||||
/* USER CODE END PV */
|
/* USER CODE END PV */
|
||||||
|
|
||||||
/* Private function prototypes -----------------------------------------------*/
|
/* Private function prototypes -----------------------------------------------*/
|
||||||
void SystemClock_Config(void);
|
void SystemClock_Config(void);
|
||||||
/* USER CODE BEGIN PFP */
|
/* USER CODE BEGIN PFP */
|
||||||
static void CH390_HardwareReset(void);
|
|
||||||
static void LED_Init(void);
|
static void LED_Init(void);
|
||||||
static void LED_StartBlink(void);
|
static void LED_StartBlink(void);
|
||||||
static void BootDiag_ReportCh390(void);
|
static void BootDiag_ReportCh390(void);
|
||||||
static void App_PollUart1ConfigRx(void);
|
static void App_PollUart1ConfigRx(void);
|
||||||
static void App_Init(void);
|
static void App_Init(void);
|
||||||
static void App_Poll(void);
|
static void App_Poll(void);
|
||||||
|
static void App_ConfigureLinks(const device_config_t *cfg);
|
||||||
|
static void App_RouteRawUartTraffic(void);
|
||||||
|
static void App_RouteMuxUartTraffic(void);
|
||||||
|
static void App_RouteTcpTraffic(void);
|
||||||
|
static void StackGuard_Init(void);
|
||||||
|
static void StackGuard_Check(void);
|
||||||
|
static void App_SendToUart(uint8_t uart_index, uint8_t src_id, uint8_t dst_mask, const uint8_t *data, uint16_t len);
|
||||||
/* USER CODE END PFP */
|
/* USER CODE END PFP */
|
||||||
|
|
||||||
/* Private user code ---------------------------------------------------------*/
|
/* Private user code ---------------------------------------------------------*/
|
||||||
/* USER CODE BEGIN 0 */
|
/* USER CODE BEGIN 0 */
|
||||||
|
extern uint32_t Stack_Mem[];
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief CH390 硬件复位
|
|
||||||
* @note 复位时序: RST 低电平至少 10us,然后高电平等待 50ms 完成初始化
|
|
||||||
*/
|
|
||||||
static void CH390_HardwareReset(void)
|
|
||||||
{
|
|
||||||
/* 拉低 RST 引脚 */
|
|
||||||
HAL_GPIO_WritePin(CH390_RST_PORT, CH390_RST_PIN, GPIO_PIN_RESET);
|
|
||||||
HAL_Delay(1); /* 保持低电平 1ms (远超最小 10us 要求) */
|
|
||||||
|
|
||||||
/* 拉高 RST 引脚,等待芯片初始化完成 */
|
|
||||||
HAL_GPIO_WritePin(CH390_RST_PORT, CH390_RST_PIN, GPIO_PIN_SET);
|
|
||||||
HAL_Delay(50); /* 等待 50ms */
|
|
||||||
|
|
||||||
/* 确保 CS 为高电平(未选中状态) */
|
|
||||||
HAL_GPIO_WritePin(CH390_CS_PORT, CH390_CS_PIN, GPIO_PIN_SET);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief LED 初始化(点亮表示系统启动)
|
|
||||||
*/
|
|
||||||
static void LED_Init(void)
|
static void LED_Init(void)
|
||||||
{
|
{
|
||||||
/* LED 灭(PC13 高电平灭,低电平亮) */
|
|
||||||
HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_SET);
|
HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void StackGuard_Init(void)
|
||||||
|
{
|
||||||
|
Stack_Mem[0] = STACK_GUARD_WORD;
|
||||||
|
g_stack_guard_reported = 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void StackGuard_Check(void)
|
||||||
|
{
|
||||||
|
if (Stack_Mem[0] != STACK_GUARD_WORD) {
|
||||||
|
if (g_stack_guard_reported == 0u) {
|
||||||
|
g_stack_guard_reported = 1u;
|
||||||
|
SEGGER_RTT_WriteString(0, "ERROR: Main stack guard overwritten\r\n");
|
||||||
|
}
|
||||||
|
__disable_irq();
|
||||||
|
NVIC_SystemReset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void LED_StartBlink(void)
|
static void LED_StartBlink(void)
|
||||||
{
|
{
|
||||||
if (HAL_TIM_Base_Start_IT(&htim4) != HAL_OK) {
|
if (HAL_TIM_Base_Start_IT(&htim4) != HAL_OK) {
|
||||||
@@ -124,9 +103,6 @@ static void LED_StartBlink(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief LED 闪烁(用于指示系统运行状态)
|
|
||||||
*/
|
|
||||||
void LED_Toggle(void)
|
void LED_Toggle(void)
|
||||||
{
|
{
|
||||||
HAL_GPIO_TogglePin(LED_PORT, LED_PIN);
|
HAL_GPIO_TogglePin(LED_PORT, LED_PIN);
|
||||||
@@ -145,65 +121,26 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
|
|||||||
|
|
||||||
static void BootDiag_ReportCh390(void)
|
static void BootDiag_ReportCh390(void)
|
||||||
{
|
{
|
||||||
uint16_t vendor_id;
|
ch390_diag_t diag;
|
||||||
uint16_t product_id;
|
const device_config_t *cfg = config_get();
|
||||||
uint8_t revision;
|
uint8_t mac_hw[6];
|
||||||
uint8_t nsr;
|
|
||||||
uint8_t ncr;
|
|
||||||
uint8_t rcr;
|
|
||||||
uint8_t imr;
|
|
||||||
uint8_t intcr;
|
|
||||||
uint8_t gpr;
|
|
||||||
uint8_t isr;
|
|
||||||
uint8_t ncr_before;
|
|
||||||
uint8_t ncr_after;
|
|
||||||
uint8_t intcr_before;
|
|
||||||
uint8_t intcr_after;
|
|
||||||
int link_status;
|
|
||||||
|
|
||||||
vendor_id = ch390_get_vendor_id();
|
ch390_runtime_get_diag(&diag);
|
||||||
product_id = ch390_get_product_id();
|
ch390_get_mac(mac_hw);
|
||||||
revision = ch390_get_revision();
|
|
||||||
nsr = ch390_read_reg(CH390_NSR);
|
|
||||||
ncr = ch390_read_reg(CH390_NCR);
|
|
||||||
rcr = ch390_read_reg(CH390_RCR);
|
|
||||||
imr = ch390_read_reg(CH390_IMR);
|
|
||||||
intcr = ch390_read_reg(CH390_INTCR);
|
|
||||||
gpr = ch390_read_reg(CH390_GPR);
|
|
||||||
isr = ch390_read_reg(CH390_ISR);
|
|
||||||
link_status = ch390_get_link_status();
|
|
||||||
|
|
||||||
ncr_before = ncr;
|
|
||||||
ch390_write_reg(CH390_NCR, (uint8_t)(ncr_before ^ NCR_FDX));
|
|
||||||
ncr_after = ch390_read_reg(CH390_NCR);
|
|
||||||
ch390_write_reg(CH390_NCR, ncr_before);
|
|
||||||
|
|
||||||
intcr_before = intcr;
|
|
||||||
ch390_write_reg(CH390_INTCR, (uint8_t)(INCR_TYPE_OD | INCR_POL_L));
|
|
||||||
intcr_after = ch390_read_reg(CH390_INTCR);
|
|
||||||
ch390_write_reg(CH390_INTCR, intcr_before);
|
|
||||||
|
|
||||||
SEGGER_RTT_printf(0,
|
SEGGER_RTT_printf(0,
|
||||||
"CH390 VID=0x%04X PID=0x%04X REV=0x%02X NSR=0x%02X LINK=%d\r\n",
|
"CH390 VID=0x%04X PID=0x%04X REV=0x%02X LINK=%u MAC=%02X:%02X:%02X:%02X:%02X:%02X\r\n",
|
||||||
vendor_id,
|
diag.vendor_id,
|
||||||
product_id,
|
diag.product_id,
|
||||||
revision,
|
diag.revision,
|
||||||
nsr,
|
diag.link_up,
|
||||||
link_status);
|
mac_hw[0], mac_hw[1], mac_hw[2], mac_hw[3], mac_hw[4], mac_hw[5]);
|
||||||
SEGGER_RTT_printf(0,
|
SEGGER_RTT_printf(0,
|
||||||
"CH390 NCR=0x%02X RCR=0x%02X IMR=0x%02X INTCR=0x%02X GPR=0x%02X ISR=0x%02X\r\n",
|
"NET cfg IP=%u.%u.%u.%u MASK=%u.%u.%u.%u GW=%u.%u.%u.%u MUX=%u\r\n",
|
||||||
ncr,
|
cfg->net.ip[0], cfg->net.ip[1], cfg->net.ip[2], cfg->net.ip[3],
|
||||||
rcr,
|
cfg->net.mask[0], cfg->net.mask[1], cfg->net.mask[2], cfg->net.mask[3],
|
||||||
imr,
|
cfg->net.gw[0], cfg->net.gw[1], cfg->net.gw[2], cfg->net.gw[3],
|
||||||
intcr,
|
cfg->mux_mode);
|
||||||
gpr,
|
|
||||||
isr);
|
|
||||||
SEGGER_RTT_printf(0,
|
|
||||||
"CH390 WRCHK NCR:0x%02X->0x%02X INTCR:0x%02X->0x%02X\r\n",
|
|
||||||
ncr_before,
|
|
||||||
ncr_after,
|
|
||||||
intcr_before,
|
|
||||||
intcr_after);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void App_PollUart1ConfigRx(void)
|
static void App_PollUart1ConfigRx(void)
|
||||||
@@ -214,6 +151,73 @@ static void App_PollUart1ConfigRx(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void App_ConfigureLinks(const device_config_t *cfg)
|
||||||
|
{
|
||||||
|
tcp_server_instance_config_t server_cfg;
|
||||||
|
tcp_client_instance_config_t client_cfg;
|
||||||
|
|
||||||
|
(void)tcp_server_init_all();
|
||||||
|
(void)tcp_client_init_all();
|
||||||
|
|
||||||
|
server_cfg.enabled = (cfg->links[CONFIG_LINK_S1].enabled != 0u);
|
||||||
|
server_cfg.port = cfg->links[CONFIG_LINK_S1].local_port;
|
||||||
|
(void)tcp_server_config(0u, &server_cfg);
|
||||||
|
|
||||||
|
server_cfg.enabled = (cfg->links[CONFIG_LINK_S2].enabled != 0u);
|
||||||
|
server_cfg.port = cfg->links[CONFIG_LINK_S2].local_port;
|
||||||
|
(void)tcp_server_config(1u, &server_cfg);
|
||||||
|
|
||||||
|
memcpy(client_cfg.remote_ip, cfg->links[CONFIG_LINK_C1].remote_ip, sizeof(client_cfg.remote_ip));
|
||||||
|
client_cfg.local_port = cfg->links[CONFIG_LINK_C1].local_port;
|
||||||
|
client_cfg.remote_port = cfg->links[CONFIG_LINK_C1].remote_port;
|
||||||
|
client_cfg.reconnect_interval_ms = TCP_CLIENT_RECONNECT_DELAY_MS;
|
||||||
|
client_cfg.enabled = (cfg->links[CONFIG_LINK_C1].enabled != 0u);
|
||||||
|
client_cfg.auto_reconnect = true;
|
||||||
|
(void)tcp_client_config(0u, &client_cfg);
|
||||||
|
|
||||||
|
memcpy(client_cfg.remote_ip, cfg->links[CONFIG_LINK_C2].remote_ip, sizeof(client_cfg.remote_ip));
|
||||||
|
client_cfg.local_port = cfg->links[CONFIG_LINK_C2].local_port;
|
||||||
|
client_cfg.remote_port = cfg->links[CONFIG_LINK_C2].remote_port;
|
||||||
|
client_cfg.enabled = (cfg->links[CONFIG_LINK_C2].enabled != 0u);
|
||||||
|
(void)tcp_client_config(1u, &client_cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void App_StartLinksIfNeeded(void)
|
||||||
|
{
|
||||||
|
if ((g_links_started != 0u) || !netif_is_link_up(&ch390_netif)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < TCP_SERVER_INSTANCE_COUNT; ++i) {
|
||||||
|
(void)tcp_server_start(i);
|
||||||
|
}
|
||||||
|
for (uint8_t i = 0; i < TCP_CLIENT_INSTANCE_COUNT; ++i) {
|
||||||
|
(void)tcp_client_connect(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_links_started = 1u;
|
||||||
|
SEGGER_RTT_WriteString(0, "NET links started after link-up\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void App_StopLinksIfNeeded(void)
|
||||||
|
{
|
||||||
|
if (netif_is_link_up(&ch390_netif)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_links_started != 0u) {
|
||||||
|
for (uint8_t i = 0; i < TCP_CLIENT_INSTANCE_COUNT; ++i) {
|
||||||
|
(void)tcp_client_disconnect(i);
|
||||||
|
}
|
||||||
|
for (uint8_t i = 0; i < TCP_SERVER_INSTANCE_COUNT; ++i) {
|
||||||
|
(void)tcp_server_stop(i);
|
||||||
|
}
|
||||||
|
SEGGER_RTT_WriteString(0, "NET links stopped after link-down\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
g_links_started = 0u;
|
||||||
|
}
|
||||||
|
|
||||||
static void App_Init(void)
|
static void App_Init(void)
|
||||||
{
|
{
|
||||||
const device_config_t *cfg;
|
const device_config_t *cfg;
|
||||||
@@ -221,91 +225,227 @@ static void App_Init(void)
|
|||||||
ip4_addr_t netmask;
|
ip4_addr_t netmask;
|
||||||
ip4_addr_t gateway;
|
ip4_addr_t gateway;
|
||||||
uart_config_t uart_cfg;
|
uart_config_t uart_cfg;
|
||||||
tcp_server_config_t server_cfg;
|
|
||||||
tcp_client_config_t client_cfg;
|
|
||||||
|
|
||||||
config_init();
|
(void)config_init();
|
||||||
cfg = config_get();
|
cfg = config_get();
|
||||||
|
|
||||||
uart_trans_init();
|
(void)uart_trans_init();
|
||||||
|
uart_cfg.baudrate = cfg->uart_baudrate[0];
|
||||||
uart_cfg.baudrate = cfg->uart2_baudrate;
|
(void)uart_trans_config(UART_CHANNEL_U0, &uart_cfg);
|
||||||
uart_cfg.data_bits = cfg->uart2_databits;
|
uart_cfg.baudrate = cfg->uart_baudrate[1];
|
||||||
uart_cfg.stop_bits = cfg->uart2_stopbits;
|
(void)uart_trans_config(UART_CHANNEL_U1, &uart_cfg);
|
||||||
uart_cfg.parity = cfg->uart2_parity;
|
(void)uart_trans_start(UART_CHANNEL_U0);
|
||||||
uart_trans_config(UART_CHANNEL_SERVER, &uart_cfg);
|
(void)uart_trans_start(UART_CHANNEL_U1);
|
||||||
|
|
||||||
uart_cfg.baudrate = cfg->uart3_baudrate;
|
|
||||||
uart_cfg.data_bits = cfg->uart3_databits;
|
|
||||||
uart_cfg.stop_bits = cfg->uart3_stopbits;
|
|
||||||
uart_cfg.parity = cfg->uart3_parity;
|
|
||||||
uart_trans_config(UART_CHANNEL_CLIENT, &uart_cfg);
|
|
||||||
|
|
||||||
uart_trans_start(UART_CHANNEL_SERVER);
|
|
||||||
uart_trans_start(UART_CHANNEL_CLIENT);
|
|
||||||
|
|
||||||
SEGGER_RTT_Init();
|
SEGGER_RTT_Init();
|
||||||
|
StackGuard_Init();
|
||||||
SEGGER_RTT_WriteString(0, "\r\nTCP2UART boot\r\n");
|
SEGGER_RTT_WriteString(0, "\r\nTCP2UART boot\r\n");
|
||||||
if (g_clock_fallback_to_hsi != 0u) {
|
if (g_clock_fallback_to_hsi != 0u) {
|
||||||
SEGGER_RTT_WriteString(0, "WARN: HSE start failed, fallback to HSI PLL\r\n");
|
SEGGER_RTT_WriteString(0, "WARN: HSE start failed, fallback to HSI PLL\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
lwip_init();
|
lwip_init();
|
||||||
IP4_ADDR(&ipaddr, cfg->ip[0], cfg->ip[1], cfg->ip[2], cfg->ip[3]);
|
IP4_ADDR(&ipaddr, cfg->net.ip[0], cfg->net.ip[1], cfg->net.ip[2], cfg->net.ip[3]);
|
||||||
IP4_ADDR(&netmask, cfg->mask[0], cfg->mask[1], cfg->mask[2], cfg->mask[3]);
|
IP4_ADDR(&netmask, cfg->net.mask[0], cfg->net.mask[1], cfg->net.mask[2], cfg->net.mask[3]);
|
||||||
IP4_ADDR(&gateway, cfg->gw[0], cfg->gw[1], cfg->gw[2], cfg->gw[3]);
|
IP4_ADDR(&gateway, cfg->net.gw[0], cfg->net.gw[1], cfg->net.gw[2], cfg->net.gw[3]);
|
||||||
lwip_netif_init(&ipaddr, &netmask, &gateway);
|
lwip_netif_init(&ipaddr, &netmask, &gateway);
|
||||||
|
App_ConfigureLinks(cfg);
|
||||||
BootDiag_ReportCh390();
|
BootDiag_ReportCh390();
|
||||||
|
|
||||||
server_cfg.port = cfg->server_port;
|
|
||||||
server_cfg.auto_reconnect = true;
|
|
||||||
tcp_server_init(&server_cfg);
|
|
||||||
tcp_server_start();
|
|
||||||
|
|
||||||
memcpy(client_cfg.server_ip, cfg->remote_ip, sizeof(client_cfg.server_ip));
|
|
||||||
client_cfg.server_port = cfg->remote_port;
|
|
||||||
client_cfg.auto_reconnect = true;
|
|
||||||
client_cfg.reconnect_interval_ms = cfg->reconnect_interval;
|
|
||||||
tcp_client_init(&client_cfg);
|
|
||||||
tcp_client_connect();
|
|
||||||
|
|
||||||
/* Arm UART1 RX interrupt path so config commands can enter via USART1. */
|
|
||||||
if (HAL_UART_Receive_IT(&huart1, (uint8_t *)&g_uart1_rx_probe_byte, 1u) != HAL_OK) {
|
if (HAL_UART_Receive_IT(&huart1, (uint8_t *)&g_uart1_rx_probe_byte, 1u) != HAL_OK) {
|
||||||
Error_Handler();
|
Error_Handler();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void App_SendToUart(uint8_t uart_index, uint8_t src_id, uint8_t dst_mask, const uint8_t *data, uint16_t len)
|
||||||
|
{
|
||||||
|
const device_config_t *cfg = config_get();
|
||||||
|
uart_channel_t channel = (uart_index == LINK_UART_U1) ? UART_CHANNEL_U1 : UART_CHANNEL_U0;
|
||||||
|
|
||||||
|
if (cfg->mux_mode == MUX_MODE_FRAME) {
|
||||||
|
uint8_t frame[APP_ROUTE_BUFFER_SIZE + 6u];
|
||||||
|
uint16_t frame_len = 0u;
|
||||||
|
if (uart_mux_encode_frame(src_id, dst_mask, data, len, frame, &frame_len, sizeof(frame))) {
|
||||||
|
(void)uart_trans_write(channel, frame, frame_len);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(void)uart_trans_write(channel, data, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void App_RouteTcpTraffic(void)
|
||||||
|
{
|
||||||
|
const device_config_t *cfg = config_get();
|
||||||
|
uint8_t buffer[APP_ROUTE_BUFFER_SIZE];
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < TCP_SERVER_INSTANCE_COUNT; ++i) {
|
||||||
|
int rc = tcp_server_recv(i, buffer, sizeof(buffer));
|
||||||
|
if (rc > 0) {
|
||||||
|
uint8_t link_index = (i == 0u) ? CONFIG_LINK_S1 : CONFIG_LINK_S2;
|
||||||
|
App_SendToUart(cfg->links[link_index].uart,
|
||||||
|
config_link_index_to_endpoint(link_index),
|
||||||
|
config_uart_index_to_endpoint(cfg->links[link_index].uart),
|
||||||
|
buffer,
|
||||||
|
(uint16_t)rc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < TCP_CLIENT_INSTANCE_COUNT; ++i) {
|
||||||
|
int rc = tcp_client_recv(i, buffer, sizeof(buffer));
|
||||||
|
if (rc > 0) {
|
||||||
|
uint8_t link_index = (i == 0u) ? CONFIG_LINK_C1 : CONFIG_LINK_C2;
|
||||||
|
App_SendToUart(cfg->links[link_index].uart,
|
||||||
|
config_link_index_to_endpoint(link_index),
|
||||||
|
config_uart_index_to_endpoint(cfg->links[link_index].uart),
|
||||||
|
buffer,
|
||||||
|
(uint16_t)rc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void App_RouteRawUartTraffic(void)
|
||||||
|
{
|
||||||
|
const device_config_t *cfg = config_get();
|
||||||
|
uint8_t buffer[APP_ROUTE_BUFFER_SIZE];
|
||||||
|
uint16_t len;
|
||||||
|
|
||||||
|
len = uart_trans_read(UART_CHANNEL_U0, buffer, sizeof(buffer));
|
||||||
|
if (len > 0u) {
|
||||||
|
for (uint8_t i = 0; i < CONFIG_LINK_COUNT; ++i) {
|
||||||
|
if (cfg->links[i].enabled == 0u || cfg->links[i].uart != LINK_UART_U0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (i == CONFIG_LINK_S1) {
|
||||||
|
(void)tcp_server_send(0u, buffer, len);
|
||||||
|
} else if (i == CONFIG_LINK_S2) {
|
||||||
|
(void)tcp_server_send(1u, buffer, len);
|
||||||
|
} else if (i == CONFIG_LINK_C1) {
|
||||||
|
(void)tcp_client_send(0u, buffer, len);
|
||||||
|
} else if (i == CONFIG_LINK_C2) {
|
||||||
|
(void)tcp_client_send(1u, buffer, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
len = uart_trans_read(UART_CHANNEL_U1, buffer, sizeof(buffer));
|
||||||
|
if (len > 0u) {
|
||||||
|
for (uint8_t i = 0; i < CONFIG_LINK_COUNT; ++i) {
|
||||||
|
if (cfg->links[i].enabled == 0u || cfg->links[i].uart != LINK_UART_U1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (i == CONFIG_LINK_S1) {
|
||||||
|
(void)tcp_server_send(0u, buffer, len);
|
||||||
|
} else if (i == CONFIG_LINK_S2) {
|
||||||
|
(void)tcp_server_send(1u, buffer, len);
|
||||||
|
} else if (i == CONFIG_LINK_C1) {
|
||||||
|
(void)tcp_client_send(0u, buffer, len);
|
||||||
|
} else if (i == CONFIG_LINK_C2) {
|
||||||
|
(void)tcp_client_send(1u, buffer, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void App_RouteMuxUartTraffic(void)
|
||||||
|
{
|
||||||
|
uart_mux_frame_t frame;
|
||||||
|
const device_config_t *cfg = config_get();
|
||||||
|
|
||||||
|
while (uart_mux_try_extract_frame(UART_CHANNEL_U0, &frame)) {
|
||||||
|
if (frame.dst_mask == 0u) {
|
||||||
|
at_result_t result;
|
||||||
|
char *response_text = (char *)&g_mux_response_frame[5];
|
||||||
|
if (config_build_response_frame(frame.payload, frame.payload_len, response_text, (uint16_t)(sizeof(g_mux_response_frame) - 6u), &result)) {
|
||||||
|
uint16_t response_len = (uint16_t)strlen(response_text);
|
||||||
|
uint16_t frame_len = 0u;
|
||||||
|
if (uart_mux_encode_frame(config_uart_index_to_endpoint(LINK_UART_U0), 0u, (const uint8_t *)response_text, response_len, g_mux_response_frame, &frame_len, sizeof(g_mux_response_frame))) {
|
||||||
|
(void)uart_trans_write(UART_CHANNEL_U0, g_mux_response_frame, frame_len);
|
||||||
|
}
|
||||||
|
if (result == AT_NEED_REBOOT) {
|
||||||
|
static const char hint[] = "Note: Use AT+SAVE then AT+RESET to apply changes\r\n";
|
||||||
|
response_len = (uint16_t)strlen(hint);
|
||||||
|
if (uart_mux_encode_frame(config_uart_index_to_endpoint(LINK_UART_U0), 0u, (const uint8_t *)hint, response_len, g_mux_response_frame, &frame_len, sizeof(g_mux_response_frame))) {
|
||||||
|
(void)uart_trans_write(UART_CHANNEL_U0, g_mux_response_frame, frame_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((frame.dst_mask & ENDPOINT_S1) != 0u) {
|
||||||
|
(void)tcp_server_send(0u, frame.payload, frame.payload_len);
|
||||||
|
}
|
||||||
|
if ((frame.dst_mask & ENDPOINT_S2) != 0u) {
|
||||||
|
(void)tcp_server_send(1u, frame.payload, frame.payload_len);
|
||||||
|
}
|
||||||
|
if ((frame.dst_mask & ENDPOINT_C1) != 0u) {
|
||||||
|
(void)tcp_client_send(0u, frame.payload, frame.payload_len);
|
||||||
|
}
|
||||||
|
if ((frame.dst_mask & ENDPOINT_C2) != 0u) {
|
||||||
|
(void)tcp_client_send(1u, frame.payload, frame.payload_len);
|
||||||
|
}
|
||||||
|
if ((frame.dst_mask & ENDPOINT_UART3) != 0u && cfg->links[CONFIG_LINK_S2].uart == LINK_UART_U1) {
|
||||||
|
App_SendToUart(LINK_UART_U1, frame.src_id, ENDPOINT_UART3, frame.payload, frame.payload_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (uart_mux_try_extract_frame(UART_CHANNEL_U1, &frame)) {
|
||||||
|
if (frame.dst_mask == 0u) {
|
||||||
|
at_result_t result;
|
||||||
|
char *response_text = (char *)&g_mux_response_frame[5];
|
||||||
|
if (config_build_response_frame(frame.payload, frame.payload_len, response_text, (uint16_t)(sizeof(g_mux_response_frame) - 6u), &result)) {
|
||||||
|
uint16_t response_len = (uint16_t)strlen(response_text);
|
||||||
|
uint16_t frame_len = 0u;
|
||||||
|
if (uart_mux_encode_frame(config_uart_index_to_endpoint(LINK_UART_U1), 0u, (const uint8_t *)response_text, response_len, g_mux_response_frame, &frame_len, sizeof(g_mux_response_frame))) {
|
||||||
|
(void)uart_trans_write(UART_CHANNEL_U1, g_mux_response_frame, frame_len);
|
||||||
|
}
|
||||||
|
if (result == AT_NEED_REBOOT) {
|
||||||
|
static const char hint[] = "Note: Use AT+SAVE then AT+RESET to apply changes\r\n";
|
||||||
|
response_len = (uint16_t)strlen(hint);
|
||||||
|
if (uart_mux_encode_frame(config_uart_index_to_endpoint(LINK_UART_U1), 0u, (const uint8_t *)hint, response_len, g_mux_response_frame, &frame_len, sizeof(g_mux_response_frame))) {
|
||||||
|
(void)uart_trans_write(UART_CHANNEL_U1, g_mux_response_frame, frame_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((frame.dst_mask & ENDPOINT_S1) != 0u) {
|
||||||
|
(void)tcp_server_send(0u, frame.payload, frame.payload_len);
|
||||||
|
}
|
||||||
|
if ((frame.dst_mask & ENDPOINT_S2) != 0u) {
|
||||||
|
(void)tcp_server_send(1u, frame.payload, frame.payload_len);
|
||||||
|
}
|
||||||
|
if ((frame.dst_mask & ENDPOINT_C1) != 0u) {
|
||||||
|
(void)tcp_client_send(0u, frame.payload, frame.payload_len);
|
||||||
|
}
|
||||||
|
if ((frame.dst_mask & ENDPOINT_C2) != 0u) {
|
||||||
|
(void)tcp_client_send(1u, frame.payload, frame.payload_len);
|
||||||
|
}
|
||||||
|
if ((frame.dst_mask & ENDPOINT_UART2) != 0u) {
|
||||||
|
App_SendToUart(LINK_UART_U0, frame.src_id, ENDPOINT_UART2, frame.payload, frame.payload_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void App_Poll(void)
|
static void App_Poll(void)
|
||||||
{
|
{
|
||||||
uint8_t buffer[128];
|
|
||||||
int len;
|
|
||||||
|
|
||||||
ethernetif_poll();
|
ethernetif_poll();
|
||||||
ethernetif_check_link();
|
ethernetif_check_link();
|
||||||
sys_check_timeouts();
|
sys_check_timeouts();
|
||||||
|
App_StopLinksIfNeeded();
|
||||||
|
App_StartLinksIfNeeded();
|
||||||
tcp_client_poll();
|
tcp_client_poll();
|
||||||
uart_trans_poll();
|
uart_trans_poll();
|
||||||
App_PollUart1ConfigRx();
|
App_PollUart1ConfigRx();
|
||||||
|
StackGuard_Check();
|
||||||
config_poll();
|
config_poll();
|
||||||
|
App_RouteTcpTraffic();
|
||||||
|
|
||||||
len = tcp_server_recv(buffer, sizeof(buffer), 0u);
|
if (config_get()->mux_mode == MUX_MODE_FRAME) {
|
||||||
if (len > 0) {
|
App_RouteMuxUartTraffic();
|
||||||
uart_trans_write(UART_CHANNEL_SERVER, buffer, (uint16_t)len);
|
} else {
|
||||||
}
|
App_RouteRawUartTraffic();
|
||||||
|
|
||||||
len = tcp_client_recv(buffer, sizeof(buffer), 0u);
|
|
||||||
if (len > 0) {
|
|
||||||
uart_trans_write(UART_CHANNEL_CLIENT, buffer, (uint16_t)len);
|
|
||||||
}
|
|
||||||
|
|
||||||
len = (int)uart_trans_read(UART_CHANNEL_SERVER, buffer, sizeof(buffer));
|
|
||||||
if (len > 0) {
|
|
||||||
tcp_server_send(buffer, (uint16_t)len);
|
|
||||||
}
|
|
||||||
|
|
||||||
len = (int)uart_trans_read(UART_CHANNEL_CLIENT, buffer, sizeof(buffer));
|
|
||||||
if (len > 0) {
|
|
||||||
tcp_client_send(buffer, (uint16_t)len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config_is_reset_requested()) {
|
if (config_is_reset_requested()) {
|
||||||
@@ -313,83 +453,40 @@ static void App_Poll(void)
|
|||||||
NVIC_SystemReset();
|
NVIC_SystemReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hiwdg.Instance == IWDG) {
|
||||||
HAL_IWDG_Refresh(&hiwdg);
|
HAL_IWDG_Refresh(&hiwdg);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/* USER CODE END 0 */
|
/* USER CODE END 0 */
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The application entry point.
|
|
||||||
* @retval int
|
|
||||||
*/
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
/* USER CODE BEGIN 1 */
|
|
||||||
|
|
||||||
/* USER CODE END 1 */
|
|
||||||
|
|
||||||
/* MCU Configuration--------------------------------------------------------*/
|
|
||||||
|
|
||||||
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
|
|
||||||
HAL_Init();
|
HAL_Init();
|
||||||
|
|
||||||
/* USER CODE BEGIN Init */
|
|
||||||
|
|
||||||
/* USER CODE END Init */
|
|
||||||
|
|
||||||
/* Configure the system clock */
|
|
||||||
SystemClock_Config();
|
SystemClock_Config();
|
||||||
|
|
||||||
/* USER CODE BEGIN SysInit */
|
|
||||||
|
|
||||||
/* USER CODE END SysInit */
|
|
||||||
|
|
||||||
/* Initialize all configured peripherals */
|
|
||||||
MX_GPIO_Init();
|
MX_GPIO_Init();
|
||||||
MX_DMA_Init();
|
MX_DMA_Init();
|
||||||
MX_IWDG_Init();
|
|
||||||
MX_USART1_UART_Init();
|
MX_USART1_UART_Init();
|
||||||
MX_USART2_UART_Init();
|
MX_USART2_UART_Init();
|
||||||
MX_USART3_UART_Init();
|
MX_USART3_UART_Init();
|
||||||
MX_SPI1_Init();
|
MX_SPI1_Init();
|
||||||
MX_TIM4_Init();
|
MX_TIM4_Init();
|
||||||
/* USER CODE BEGIN 2 */
|
|
||||||
|
|
||||||
/* LED 初始化 */
|
|
||||||
LED_Init();
|
LED_Init();
|
||||||
LED_StartBlink();
|
LED_StartBlink();
|
||||||
|
|
||||||
App_Init();
|
App_Init();
|
||||||
|
|
||||||
/* USER CODE END 2 */
|
|
||||||
|
|
||||||
/* Infinite loop */
|
|
||||||
/* USER CODE BEGIN WHILE */
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
/* USER CODE END WHILE */
|
|
||||||
|
|
||||||
/* USER CODE BEGIN 3 */
|
|
||||||
App_Poll();
|
App_Poll();
|
||||||
}
|
}
|
||||||
/* USER CODE END 3 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief System Clock Configuration
|
|
||||||
* @retval None
|
|
||||||
*/
|
|
||||||
void SystemClock_Config(void)
|
void SystemClock_Config(void)
|
||||||
{
|
{
|
||||||
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
|
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
|
||||||
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
|
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
|
||||||
|
|
||||||
g_clock_fallback_to_hsi = 0u;
|
g_clock_fallback_to_hsi = 0u;
|
||||||
|
|
||||||
/** Initializes the RCC Oscillators according to the specified parameters
|
|
||||||
* in the RCC_OscInitTypeDef structure.
|
|
||||||
*/
|
|
||||||
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE;
|
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE;
|
||||||
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
|
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
|
||||||
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
|
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
|
||||||
@@ -399,11 +496,6 @@ void SystemClock_Config(void)
|
|||||||
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
|
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
|
||||||
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
|
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
|
||||||
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
|
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
|
||||||
/*
|
|
||||||
* Some bring-up boards fail to start the external crystal cleanly.
|
|
||||||
* Fall back to HSI-based PLL so the firmware can still boot and expose
|
|
||||||
* RTT / debugger evidence instead of trapping during clock init.
|
|
||||||
*/
|
|
||||||
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI | RCC_OSCILLATORTYPE_HSI;
|
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI | RCC_OSCILLATORTYPE_HSI;
|
||||||
RCC_OscInitStruct.HSEState = RCC_HSE_OFF;
|
RCC_OscInitStruct.HSEState = RCC_HSE_OFF;
|
||||||
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
|
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
|
||||||
@@ -417,8 +509,6 @@ void SystemClock_Config(void)
|
|||||||
g_clock_fallback_to_hsi = 1u;
|
g_clock_fallback_to_hsi = 1u;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Initializes the CPU, AHB and APB buses clocks
|
|
||||||
*/
|
|
||||||
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|
||||||
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
|
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
|
||||||
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
|
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
|
||||||
@@ -432,11 +522,6 @@ void SystemClock_Config(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* USER CODE BEGIN 4 */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 重定向 printf 到 UART1(调试输出)
|
|
||||||
*/
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
int _write(int file, char *ptr, int len)
|
int _write(int file, char *ptr, int len)
|
||||||
{
|
{
|
||||||
@@ -464,35 +549,16 @@ void Debug_TrapWithRttHint(const char *tag)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* USER CODE END 4 */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function is executed in case of error occurrence.
|
|
||||||
* @retval None
|
|
||||||
*/
|
|
||||||
void Error_Handler(void)
|
void Error_Handler(void)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN Error_Handler_Debug */
|
|
||||||
/* User can add his own implementation to report the HAL error return state */
|
|
||||||
Debug_TrapWithRttHint("Error_Handler");
|
Debug_TrapWithRttHint("Error_Handler");
|
||||||
/* USER CODE END Error_Handler_Debug */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_FULL_ASSERT
|
#ifdef USE_FULL_ASSERT
|
||||||
/**
|
|
||||||
* @brief Reports the name of the source file and the source line number
|
|
||||||
* where the assert_param error has occurred.
|
|
||||||
* @param file: pointer to the source file name
|
|
||||||
* @param line: assert_param error line source number
|
|
||||||
* @retval None
|
|
||||||
*/
|
|
||||||
void assert_failed(uint8_t *file, uint32_t line)
|
void assert_failed(uint8_t *file, uint32_t line)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN 6 */
|
|
||||||
(void)file;
|
(void)file;
|
||||||
(void)line;
|
(void)line;
|
||||||
Debug_TrapWithRttHint("assert_failed");
|
Debug_TrapWithRttHint("assert_failed");
|
||||||
/* User can add his own implementation to report the file name and line number,
|
|
||||||
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
|
|
||||||
/* USER CODE END 6 */
|
|
||||||
}
|
}
|
||||||
#endif /* USE_FULL_ASSERT */
|
#endif
|
||||||
|
|||||||
+3
-3
@@ -41,10 +41,10 @@ void MX_SPI1_Init(void)
|
|||||||
hspi1.Init.Mode = SPI_MODE_MASTER;
|
hspi1.Init.Mode = SPI_MODE_MASTER;
|
||||||
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
|
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
|
||||||
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
|
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
|
||||||
hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH; /* CH390 requires CPOL=High (Mode 3) */
|
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; /* Match CH390 runtime baseline: CPOL=Low */
|
||||||
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE; /* CH390 requires CPHA=2Edge (Mode 3) */
|
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; /* Match CH390 runtime baseline: CPHA=1Edge (Mode 0) */
|
||||||
hspi1.Init.NSS = SPI_NSS_SOFT; /* Software CS control for CH390 */
|
hspi1.Init.NSS = SPI_NSS_SOFT; /* Software CS control for CH390 */
|
||||||
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; /* 72MHz/8 = 9MHz (CH390 max 10MHz) */
|
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64; /* 72MHz/64 = 1.125MHz for conservative CH390 bring-up */
|
||||||
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
|
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
|
||||||
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
|
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
|
||||||
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
|
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
|
||||||
|
|||||||
+10
-235
@@ -4,61 +4,15 @@
|
|||||||
* @file stm32f1xx_it.c
|
* @file stm32f1xx_it.c
|
||||||
* @brief Interrupt Service Routines.
|
* @brief Interrupt Service Routines.
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* @attention
|
|
||||||
*
|
|
||||||
* Copyright (c) 2026 STMicroelectronics.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* This software is licensed under terms that can be found in the LICENSE file
|
|
||||||
* in the root directory of this software component.
|
|
||||||
* If no LICENSE file comes with this software, it is provided AS-IS.
|
|
||||||
*
|
|
||||||
******************************************************************************
|
|
||||||
*/
|
*/
|
||||||
/* USER CODE END Header */
|
/* USER CODE END Header */
|
||||||
|
|
||||||
/* Includes ------------------------------------------------------------------*/
|
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "stm32f1xx_it.h"
|
#include "stm32f1xx_it.h"
|
||||||
/* Private includes ----------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN Includes */
|
|
||||||
#include "ethernetif.h"
|
#include "ethernetif.h"
|
||||||
#include "SEGGER_RTT.h"
|
|
||||||
#include "uart_trans.h"
|
#include "uart_trans.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
/* USER CODE END Includes */
|
|
||||||
|
|
||||||
/* Private typedef -----------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN TD */
|
|
||||||
|
|
||||||
/* USER CODE END TD */
|
|
||||||
|
|
||||||
/* Private define ------------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN PD */
|
|
||||||
|
|
||||||
/* USER CODE END PD */
|
|
||||||
|
|
||||||
/* Private macro -------------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN PM */
|
|
||||||
|
|
||||||
/* USER CODE END PM */
|
|
||||||
|
|
||||||
/* Private variables ---------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN PV */
|
|
||||||
|
|
||||||
/* USER CODE END PV */
|
|
||||||
|
|
||||||
/* Private function prototypes -----------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN PFP */
|
|
||||||
|
|
||||||
/* USER CODE END PFP */
|
|
||||||
|
|
||||||
/* Private user code ---------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN 0 */
|
|
||||||
|
|
||||||
/* USER CODE END 0 */
|
|
||||||
|
|
||||||
/* External variables --------------------------------------------------------*/
|
|
||||||
extern SPI_HandleTypeDef hspi1;
|
extern SPI_HandleTypeDef hspi1;
|
||||||
extern TIM_HandleTypeDef htim4;
|
extern TIM_HandleTypeDef htim4;
|
||||||
extern DMA_HandleTypeDef hdma_usart1_rx;
|
extern DMA_HandleTypeDef hdma_usart1_rx;
|
||||||
@@ -70,332 +24,153 @@ extern DMA_HandleTypeDef hdma_usart3_tx;
|
|||||||
extern UART_HandleTypeDef huart1;
|
extern UART_HandleTypeDef huart1;
|
||||||
extern UART_HandleTypeDef huart2;
|
extern UART_HandleTypeDef huart2;
|
||||||
extern UART_HandleTypeDef huart3;
|
extern UART_HandleTypeDef huart3;
|
||||||
/* USER CODE BEGIN EV */
|
extern volatile uint8_t g_uart1_rx_probe_byte;
|
||||||
|
|
||||||
/* USER CODE END EV */
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
/* Cortex-M3 Processor Interruption and Exception Handlers */
|
|
||||||
/******************************************************************************/
|
|
||||||
/**
|
|
||||||
* @brief This function handles Non maskable interrupt.
|
|
||||||
*/
|
|
||||||
void NMI_Handler(void)
|
void NMI_Handler(void)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN NonMaskableInt_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END NonMaskableInt_IRQn 0 */
|
|
||||||
/* USER CODE BEGIN NonMaskableInt_IRQn 1 */
|
|
||||||
Debug_TrapWithRttHint("NMI_Handler");
|
Debug_TrapWithRttHint("NMI_Handler");
|
||||||
/* USER CODE END NonMaskableInt_IRQn 1 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles Hard fault interrupt.
|
|
||||||
*/
|
|
||||||
void HardFault_Handler(void)
|
void HardFault_Handler(void)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN HardFault_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END HardFault_IRQn 0 */
|
|
||||||
Debug_TrapWithRttHint("HardFault_Handler");
|
Debug_TrapWithRttHint("HardFault_Handler");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles Memory management fault.
|
|
||||||
*/
|
|
||||||
void MemManage_Handler(void)
|
void MemManage_Handler(void)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN MemoryManagement_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END MemoryManagement_IRQn 0 */
|
|
||||||
Debug_TrapWithRttHint("MemManage_Handler");
|
Debug_TrapWithRttHint("MemManage_Handler");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles Prefetch fault, memory access fault.
|
|
||||||
*/
|
|
||||||
void BusFault_Handler(void)
|
void BusFault_Handler(void)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN BusFault_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END BusFault_IRQn 0 */
|
|
||||||
Debug_TrapWithRttHint("BusFault_Handler");
|
Debug_TrapWithRttHint("BusFault_Handler");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles Undefined instruction or illegal state.
|
|
||||||
*/
|
|
||||||
void UsageFault_Handler(void)
|
void UsageFault_Handler(void)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN UsageFault_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END UsageFault_IRQn 0 */
|
|
||||||
Debug_TrapWithRttHint("UsageFault_Handler");
|
Debug_TrapWithRttHint("UsageFault_Handler");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles Debug monitor.
|
|
||||||
*/
|
|
||||||
void DebugMon_Handler(void)
|
void DebugMon_Handler(void)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN DebugMonitor_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END DebugMonitor_IRQn 0 */
|
|
||||||
/* USER CODE BEGIN DebugMonitor_IRQn 1 */
|
|
||||||
|
|
||||||
/* USER CODE END DebugMonitor_IRQn 1 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles System tick timer.
|
|
||||||
*/
|
|
||||||
void SysTick_Handler(void)
|
void SysTick_Handler(void)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN SysTick_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END SysTick_IRQn 0 */
|
|
||||||
HAL_IncTick();
|
HAL_IncTick();
|
||||||
/* USER CODE BEGIN SysTick_IRQn 1 */
|
|
||||||
|
|
||||||
/* USER CODE END SysTick_IRQn 1 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
/* STM32F1xx Peripheral Interrupt Handlers */
|
|
||||||
/* Add here the Interrupt Handlers for the used peripherals. */
|
|
||||||
/* For the available peripheral interrupt handler names, */
|
|
||||||
/* please refer to the startup file (startup_stm32f1xx.s). */
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles DMA1 channel2 global interrupt.
|
|
||||||
*/
|
|
||||||
void DMA1_Channel2_IRQHandler(void)
|
void DMA1_Channel2_IRQHandler(void)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN DMA1_Channel2_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END DMA1_Channel2_IRQn 0 */
|
|
||||||
HAL_DMA_IRQHandler(&hdma_usart3_tx);
|
HAL_DMA_IRQHandler(&hdma_usart3_tx);
|
||||||
/* USER CODE BEGIN DMA1_Channel2_IRQn 1 */
|
|
||||||
|
|
||||||
/* USER CODE END DMA1_Channel2_IRQn 1 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles DMA1 channel3 global interrupt.
|
|
||||||
*/
|
|
||||||
void DMA1_Channel3_IRQHandler(void)
|
void DMA1_Channel3_IRQHandler(void)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN DMA1_Channel3_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END DMA1_Channel3_IRQn 0 */
|
|
||||||
HAL_DMA_IRQHandler(&hdma_usart3_rx);
|
HAL_DMA_IRQHandler(&hdma_usart3_rx);
|
||||||
/* USER CODE BEGIN DMA1_Channel3_IRQn 1 */
|
|
||||||
|
|
||||||
/* USER CODE END DMA1_Channel3_IRQn 1 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles DMA1 channel4 global interrupt.
|
|
||||||
*/
|
|
||||||
void DMA1_Channel4_IRQHandler(void)
|
void DMA1_Channel4_IRQHandler(void)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN DMA1_Channel4_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END DMA1_Channel4_IRQn 0 */
|
|
||||||
HAL_DMA_IRQHandler(&hdma_usart1_tx);
|
HAL_DMA_IRQHandler(&hdma_usart1_tx);
|
||||||
/* USER CODE BEGIN DMA1_Channel4_IRQn 1 */
|
|
||||||
|
|
||||||
/* USER CODE END DMA1_Channel4_IRQn 1 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles DMA1 channel5 global interrupt.
|
|
||||||
*/
|
|
||||||
void DMA1_Channel5_IRQHandler(void)
|
void DMA1_Channel5_IRQHandler(void)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN DMA1_Channel5_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END DMA1_Channel5_IRQn 0 */
|
|
||||||
HAL_DMA_IRQHandler(&hdma_usart1_rx);
|
HAL_DMA_IRQHandler(&hdma_usart1_rx);
|
||||||
/* USER CODE BEGIN DMA1_Channel5_IRQn 1 */
|
|
||||||
|
|
||||||
/* USER CODE END DMA1_Channel5_IRQn 1 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles DMA1 channel6 global interrupt.
|
|
||||||
*/
|
|
||||||
void DMA1_Channel6_IRQHandler(void)
|
void DMA1_Channel6_IRQHandler(void)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN DMA1_Channel6_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END DMA1_Channel6_IRQn 0 */
|
|
||||||
HAL_DMA_IRQHandler(&hdma_usart2_rx);
|
HAL_DMA_IRQHandler(&hdma_usart2_rx);
|
||||||
/* USER CODE BEGIN DMA1_Channel6_IRQn 1 */
|
|
||||||
|
|
||||||
/* USER CODE END DMA1_Channel6_IRQn 1 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles DMA1 channel7 global interrupt.
|
|
||||||
*/
|
|
||||||
void DMA1_Channel7_IRQHandler(void)
|
void DMA1_Channel7_IRQHandler(void)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN DMA1_Channel7_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END DMA1_Channel7_IRQn 0 */
|
|
||||||
HAL_DMA_IRQHandler(&hdma_usart2_tx);
|
HAL_DMA_IRQHandler(&hdma_usart2_tx);
|
||||||
/* USER CODE BEGIN DMA1_Channel7_IRQn 1 */
|
|
||||||
|
|
||||||
/* USER CODE END DMA1_Channel7_IRQn 1 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles TIM4 global interrupt.
|
|
||||||
*/
|
|
||||||
void TIM4_IRQHandler(void)
|
void TIM4_IRQHandler(void)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN TIM4_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END TIM4_IRQn 0 */
|
|
||||||
HAL_TIM_IRQHandler(&htim4);
|
HAL_TIM_IRQHandler(&htim4);
|
||||||
/* USER CODE BEGIN TIM4_IRQn 1 */
|
|
||||||
|
|
||||||
/* USER CODE END TIM4_IRQn 1 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles SPI1 global interrupt.
|
|
||||||
*/
|
|
||||||
void SPI1_IRQHandler(void)
|
void SPI1_IRQHandler(void)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN SPI1_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END SPI1_IRQn 0 */
|
|
||||||
HAL_SPI_IRQHandler(&hspi1);
|
HAL_SPI_IRQHandler(&hspi1);
|
||||||
/* USER CODE BEGIN SPI1_IRQn 1 */
|
|
||||||
|
|
||||||
/* USER CODE END SPI1_IRQn 1 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles USART1 global interrupt.
|
|
||||||
*/
|
|
||||||
void USART1_IRQHandler(void)
|
void USART1_IRQHandler(void)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN USART1_IRQn 0 */
|
|
||||||
/* USER CODE END USART1_IRQn 0 */
|
|
||||||
HAL_UART_IRQHandler(&huart1);
|
HAL_UART_IRQHandler(&huart1);
|
||||||
/* USER CODE BEGIN USART1_IRQn 1 */
|
|
||||||
|
|
||||||
/* USER CODE END USART1_IRQn 1 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles USART2 global interrupt.
|
|
||||||
*/
|
|
||||||
void USART2_IRQHandler(void)
|
void USART2_IRQHandler(void)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN USART2_IRQn 0 */
|
|
||||||
/* Handle IDLE interrupt for Server transparent transmission */
|
|
||||||
if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE))
|
if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE))
|
||||||
{
|
{
|
||||||
__HAL_UART_CLEAR_IDLEFLAG(&huart2);
|
__HAL_UART_CLEAR_IDLEFLAG(&huart2);
|
||||||
uart_trans_idle_handler(UART_CHANNEL_SERVER);
|
uart_trans_idle_handler(UART_CHANNEL_U0);
|
||||||
}
|
}
|
||||||
/* USER CODE END USART2_IRQn 0 */
|
|
||||||
HAL_UART_IRQHandler(&huart2);
|
HAL_UART_IRQHandler(&huart2);
|
||||||
/* USER CODE BEGIN USART2_IRQn 1 */
|
|
||||||
|
|
||||||
/* USER CODE END USART2_IRQn 1 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles USART3 global interrupt.
|
|
||||||
*/
|
|
||||||
void USART3_IRQHandler(void)
|
void USART3_IRQHandler(void)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN USART3_IRQn 0 */
|
|
||||||
/* Handle IDLE interrupt for Client transparent transmission */
|
|
||||||
if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE))
|
if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE))
|
||||||
{
|
{
|
||||||
__HAL_UART_CLEAR_IDLEFLAG(&huart3);
|
__HAL_UART_CLEAR_IDLEFLAG(&huart3);
|
||||||
uart_trans_idle_handler(UART_CHANNEL_CLIENT);
|
uart_trans_idle_handler(UART_CHANNEL_U1);
|
||||||
}
|
}
|
||||||
/* USER CODE END USART3_IRQn 0 */
|
|
||||||
HAL_UART_IRQHandler(&huart3);
|
HAL_UART_IRQHandler(&huart3);
|
||||||
/* USER CODE BEGIN USART3_IRQn 1 */
|
|
||||||
|
|
||||||
/* USER CODE END USART3_IRQn 1 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* USER CODE BEGIN 1 */
|
|
||||||
extern volatile uint8_t g_uart1_rx_probe_byte;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles EXTI0 interrupt (CH390D INT pin).
|
|
||||||
*/
|
|
||||||
void EXTI0_IRQHandler(void)
|
void EXTI0_IRQHandler(void)
|
||||||
{
|
{
|
||||||
/* Clear interrupt flag */
|
|
||||||
if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0))
|
if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0))
|
||||||
{
|
{
|
||||||
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
|
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
|
||||||
|
|
||||||
/* Defer CH390 processing to main loop */
|
|
||||||
ethernetif_set_irq_pending();
|
ethernetif_set_irq_pending();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief HAL UART TX Complete callback
|
|
||||||
*/
|
|
||||||
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
|
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
|
||||||
{
|
{
|
||||||
if (huart == &huart2)
|
if (huart == &huart2)
|
||||||
{
|
{
|
||||||
uart_trans_tx_cplt_handler(UART_CHANNEL_SERVER);
|
uart_trans_tx_cplt_handler(UART_CHANNEL_U0);
|
||||||
}
|
}
|
||||||
else if (huart == &huart3)
|
else if (huart == &huart3)
|
||||||
{
|
{
|
||||||
uart_trans_tx_cplt_handler(UART_CHANNEL_CLIENT);
|
uart_trans_tx_cplt_handler(UART_CHANNEL_U1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief HAL UART RX Complete callback
|
|
||||||
*/
|
|
||||||
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
|
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
|
||||||
{
|
{
|
||||||
if (huart == &huart1)
|
if (huart == &huart1)
|
||||||
{
|
{
|
||||||
config_uart_rx_byte(g_uart1_rx_probe_byte);
|
config_uart_rx_byte(g_uart1_rx_probe_byte);
|
||||||
HAL_UART_Receive_IT(&huart1, (uint8_t *)&g_uart1_rx_probe_byte, 1u);
|
(void)HAL_UART_Receive_IT(&huart1, (uint8_t *)&g_uart1_rx_probe_byte, 1u);
|
||||||
}
|
}
|
||||||
else if (huart == &huart2)
|
else if (huart == &huart2)
|
||||||
{
|
{
|
||||||
uart_trans_rx_cplt_handler(UART_CHANNEL_SERVER);
|
uart_trans_rx_cplt_handler(UART_CHANNEL_U0);
|
||||||
}
|
}
|
||||||
else if (huart == &huart3)
|
else if (huart == &huart3)
|
||||||
{
|
{
|
||||||
uart_trans_rx_cplt_handler(UART_CHANNEL_CLIENT);
|
uart_trans_rx_cplt_handler(UART_CHANNEL_U1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief HAL UART RX Half Complete callback
|
|
||||||
*/
|
|
||||||
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart)
|
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart)
|
||||||
{
|
{
|
||||||
if (huart == &huart2)
|
if (huart == &huart2)
|
||||||
{
|
{
|
||||||
uart_trans_rx_half_cplt_handler(UART_CHANNEL_SERVER);
|
uart_trans_rx_half_cplt_handler(UART_CHANNEL_U0);
|
||||||
}
|
}
|
||||||
else if (huart == &huart3)
|
else if (huart == &huart3)
|
||||||
{
|
{
|
||||||
uart_trans_rx_half_cplt_handler(UART_CHANNEL_CLIENT);
|
uart_trans_rx_half_cplt_handler(UART_CHANNEL_U1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* USER CODE END 1 */
|
|
||||||
|
|||||||
+22
-5
@@ -12,6 +12,8 @@
|
|||||||
#include "CH390.h"
|
#include "CH390.h"
|
||||||
#include "CH390_Interface.h"
|
#include "CH390_Interface.h"
|
||||||
|
|
||||||
|
#define CH390_PHY_BUSY_TIMEOUT_LOOPS 2000u
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name ch390_receive_packet
|
* @name ch390_receive_packet
|
||||||
* @brief Receive packet
|
* @brief Receive packet
|
||||||
@@ -119,10 +121,17 @@ void ch390_drop_packet(uint16_t len)
|
|||||||
*/
|
*/
|
||||||
uint16_t ch390_read_phy(uint8_t reg)
|
uint16_t ch390_read_phy(uint8_t reg)
|
||||||
{
|
{
|
||||||
|
uint32_t timeout = CH390_PHY_BUSY_TIMEOUT_LOOPS;
|
||||||
|
|
||||||
ch390_write_reg(CH390_EPAR, CH390_PHY | reg);
|
ch390_write_reg(CH390_EPAR, CH390_PHY | reg);
|
||||||
// Chose PHY, send read command
|
// Chose PHY, send read command
|
||||||
ch390_write_reg(CH390_EPCR, EPCR_ERPRR | EPCR_EPOS);
|
ch390_write_reg(CH390_EPCR, EPCR_ERPRR | EPCR_EPOS);
|
||||||
while(ch390_read_reg(CH390_EPCR) & 0x01);
|
while ((ch390_read_reg(CH390_EPCR) & EPCR_ERRE) != 0u) {
|
||||||
|
if (timeout-- == 0u) {
|
||||||
|
ch390_write_reg(CH390_EPCR, 0x00);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
// Clear read command
|
// Clear read command
|
||||||
ch390_write_reg(CH390_EPCR, 0x00);
|
ch390_write_reg(CH390_EPCR, 0x00);
|
||||||
return (ch390_read_reg(CH390_EPDRH) << 8) |
|
return (ch390_read_reg(CH390_EPDRH) << 8) |
|
||||||
@@ -137,12 +146,19 @@ uint16_t ch390_read_phy(uint8_t reg)
|
|||||||
*/
|
*/
|
||||||
void ch390_write_phy(uint8_t reg, uint16_t value)
|
void ch390_write_phy(uint8_t reg, uint16_t value)
|
||||||
{
|
{
|
||||||
|
uint32_t timeout = CH390_PHY_BUSY_TIMEOUT_LOOPS;
|
||||||
|
|
||||||
ch390_write_reg(CH390_EPAR, CH390_PHY | reg);
|
ch390_write_reg(CH390_EPAR, CH390_PHY | reg);
|
||||||
ch390_write_reg(CH390_EPDRL, (value & 0xff)); // Low byte
|
ch390_write_reg(CH390_EPDRL, (value & 0xff)); // Low byte
|
||||||
ch390_write_reg(CH390_EPDRH, ((value >> 8) & 0xff)); // High byte
|
ch390_write_reg(CH390_EPDRH, ((value >> 8) & 0xff)); // High byte
|
||||||
// Chose PHY, send write command
|
// Chose PHY, send write command
|
||||||
ch390_write_reg(CH390_EPCR, 0x0A);
|
ch390_write_reg(CH390_EPCR, 0x0A);
|
||||||
while(ch390_read_reg(CH390_EPCR) & 0x01);
|
while ((ch390_read_reg(CH390_EPCR) & EPCR_ERRE) != 0u) {
|
||||||
|
if (timeout-- == 0u) {
|
||||||
|
ch390_write_reg(CH390_EPCR, 0x00);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
// Clear write command
|
// Clear write command
|
||||||
ch390_write_reg(CH390_EPCR, 0x00);
|
ch390_write_reg(CH390_EPCR, 0x00);
|
||||||
}
|
}
|
||||||
@@ -194,6 +210,7 @@ void ch390_default_config()
|
|||||||
uint8_t multicase_addr[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
uint8_t multicase_addr[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||||
|
|
||||||
ch390_set_phy_mode(CH390_AUTO);
|
ch390_set_phy_mode(CH390_AUTO);
|
||||||
|
ch390_write_reg(CH390_INTCR, (uint8_t)(INCR_TYPE_OD | INCR_POL_L));
|
||||||
// Clear status
|
// Clear status
|
||||||
ch390_write_reg(CH390_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);
|
ch390_write_reg(CH390_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);
|
||||||
ch390_write_reg(CH390_ISR, 0xFF); // Clear interrupt status
|
ch390_write_reg(CH390_ISR, 0xFF); // Clear interrupt status
|
||||||
@@ -203,9 +220,9 @@ void ch390_default_config()
|
|||||||
// ch390_set_mac_address(mac_addr);
|
// ch390_set_mac_address(mac_addr);
|
||||||
ch390_set_multicast(multicase_addr);
|
ch390_set_multicast(multicase_addr);
|
||||||
|
|
||||||
// Enable all interrupt and PAR
|
// Enable only the interrupts needed by the NO_SYS polling path.
|
||||||
ch390_write_reg(CH390_IMR, IMR_ALL);
|
ch390_write_reg(CH390_IMR, (uint8_t)(IMR_PRI | IMR_LNKCHGI | IMR_ROOI | IMR_ROI));
|
||||||
// Enable RX
|
// Enable RX with the reference receive filter.
|
||||||
ch390_write_reg(CH390_RCR, RCR_DIS_CRC | RCR_RXEN);
|
ch390_write_reg(CH390_RCR, RCR_DIS_CRC | RCR_RXEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "CH390.h"
|
#include "CH390.h"
|
||||||
#include "CH390_Interface.h"
|
#include "CH390_Interface.h"
|
||||||
|
#include "SEGGER_RTT.h"
|
||||||
|
|
||||||
/* FreeRTOS includes */
|
/* FreeRTOS includes */
|
||||||
#ifdef USE_FREERTOS
|
#ifdef USE_FREERTOS
|
||||||
@@ -47,6 +48,13 @@
|
|||||||
#define CH390_INT_PORT GPIOB
|
#define CH390_INT_PORT GPIOB
|
||||||
#define CH390_INT_PIN GPIO_PIN_0
|
#define CH390_INT_PIN GPIO_PIN_0
|
||||||
|
|
||||||
|
#define CH390_SCK_PORT GPIOA
|
||||||
|
#define CH390_SCK_PIN GPIO_PIN_5
|
||||||
|
#define CH390_MISO_PORT GPIOA
|
||||||
|
#define CH390_MISO_PIN GPIO_PIN_6
|
||||||
|
#define CH390_MOSI_PORT GPIOA
|
||||||
|
#define CH390_MOSI_PIN GPIO_PIN_7
|
||||||
|
|
||||||
/* External SPI handle from spi.c */
|
/* External SPI handle from spi.c */
|
||||||
extern SPI_HandleTypeDef hspi1;
|
extern SPI_HandleTypeDef hspi1;
|
||||||
|
|
||||||
@@ -65,6 +73,7 @@ static inline void ch390_cs(uint8_t state)
|
|||||||
{
|
{
|
||||||
HAL_GPIO_WritePin(CH390_CS_PORT, CH390_CS_PIN,
|
HAL_GPIO_WritePin(CH390_CS_PORT, CH390_CS_PIN,
|
||||||
state ? GPIO_PIN_SET : GPIO_PIN_RESET);
|
state ? GPIO_PIN_SET : GPIO_PIN_RESET);
|
||||||
|
ch390_delay_us(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -89,10 +98,24 @@ static inline void ch390_rst(uint8_t state)
|
|||||||
static uint8_t ch390_spi_exchange_byte(uint8_t byte)
|
static uint8_t ch390_spi_exchange_byte(uint8_t byte)
|
||||||
{
|
{
|
||||||
uint8_t rx_data = 0;
|
uint8_t rx_data = 0;
|
||||||
HAL_SPI_TransmitReceive(&hspi1, &byte, &rx_data, 1, SPI_TIMEOUT);
|
if (HAL_SPI_TransmitReceive(&hspi1, &byte, &rx_data, 1, SPI_TIMEOUT) != HAL_OK)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
return rx_data;
|
return rx_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ch390_spi_apply_mode(uint32_t polarity, uint32_t phase)
|
||||||
|
{
|
||||||
|
hspi1.Init.CLKPolarity = polarity;
|
||||||
|
hspi1.Init.CLKPhase = phase;
|
||||||
|
hspi1.Init.NSS = SPI_NSS_SOFT;
|
||||||
|
if (HAL_SPI_Init(&hspi1) != HAL_OK)
|
||||||
|
{
|
||||||
|
Error_Handler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read a dummy byte (send 0x00)
|
* @brief Read a dummy byte (send 0x00)
|
||||||
* @return Received byte
|
* @return Received byte
|
||||||
@@ -157,20 +180,9 @@ void ch390_interrupt_init(void)
|
|||||||
void ch390_spi_init(void)
|
void ch390_spi_init(void)
|
||||||
{
|
{
|
||||||
/* SPI1 is initialized by MX_SPI1_Init() in main.c */
|
/* SPI1 is initialized by MX_SPI1_Init() in main.c */
|
||||||
/* We need to ensure correct SPI mode for CH390: */
|
/* Reference CH390 SPI path uses mode 3. */
|
||||||
/* - CPOL = High (idle clock is high) */
|
ch390_spi_apply_mode(SPI_POLARITY_HIGH, SPI_PHASE_2EDGE);
|
||||||
/* - CPHA = 2Edge (data captured on second edge) */
|
SEGGER_RTT_WriteString(0, "CH390 SPI mode=3 (CPOL=1 CPHA=1)\r\n");
|
||||||
|
|
||||||
/* Reconfigure SPI for CH390 if needed */
|
|
||||||
hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
|
|
||||||
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
|
|
||||||
hspi1.Init.NSS = SPI_NSS_SOFT; /* We control CS manually */
|
|
||||||
|
|
||||||
if (HAL_SPI_Init(&hspi1) != HAL_OK)
|
|
||||||
{
|
|
||||||
/* Handle error */
|
|
||||||
Error_Handler();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -221,10 +233,11 @@ void ch390_delay_us(uint32_t time)
|
|||||||
*/
|
*/
|
||||||
void ch390_hardware_reset(void)
|
void ch390_hardware_reset(void)
|
||||||
{
|
{
|
||||||
|
ch390_delay_us(10000); /* Short delay before reset */
|
||||||
ch390_rst(0); /* Assert reset (low) */
|
ch390_rst(0); /* Assert reset (low) */
|
||||||
ch390_delay_us(100); /* Hold reset for 100us (min 10us required) */
|
ch390_delay_us(3000); /* Hold reset for 3ms to satisfy datasheet minimum */
|
||||||
ch390_rst(1); /* Release reset (high) */
|
ch390_rst(1); /* Release reset (high) */
|
||||||
ch390_delay_us(10000); /* Wait 10ms for CH390 to initialize */
|
ch390_delay_us(50000); /* Wait 50ms for CH390 to initialize reliably */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------
|
/*----------------------------------------------------------------------------
|
||||||
@@ -256,8 +269,8 @@ uint8_t ch390_read_reg(uint8_t reg)
|
|||||||
void ch390_write_reg(uint8_t reg, uint8_t value)
|
void ch390_write_reg(uint8_t reg, uint8_t value)
|
||||||
{
|
{
|
||||||
ch390_cs(0); /* CS low - select */
|
ch390_cs(0); /* CS low - select */
|
||||||
ch390_spi_exchange_byte(reg | OPC_REG_W); /* Send write command */
|
(void)ch390_spi_exchange_byte(reg | OPC_REG_W);
|
||||||
ch390_spi_exchange_byte(value); /* Write register value */
|
(void)ch390_spi_exchange_byte(value);
|
||||||
ch390_cs(1); /* CS high - deselect */
|
ch390_cs(1); /* CS high - deselect */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,354 @@
|
|||||||
|
#include "ch390_runtime.h"
|
||||||
|
|
||||||
|
#include "CH390.h"
|
||||||
|
#include "CH390_Interface.h"
|
||||||
|
#include "SEGGER_RTT.h"
|
||||||
|
#include "ethernetif.h"
|
||||||
|
#include "stm32f1xx_hal.h"
|
||||||
|
#include "lwip/etharp.h"
|
||||||
|
#include "lwip/pbuf.h"
|
||||||
|
#include "lwip/stats.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static void ch390_runtime_dispatch_frame(struct netif *netif, struct pbuf *p)
|
||||||
|
{
|
||||||
|
if ((p != NULL) && (netif->input(p, netif) != ERR_OK)) {
|
||||||
|
pbuf_free(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t ch390_runtime_drain_rx(struct netif *netif, uint8_t max_frames)
|
||||||
|
{
|
||||||
|
struct pbuf *p;
|
||||||
|
uint8_t drained = 0u;
|
||||||
|
|
||||||
|
while (drained < max_frames) {
|
||||||
|
p = ch390_runtime_input_frame(netif);
|
||||||
|
if (p == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ch390_runtime_dispatch_frame(netif, p);
|
||||||
|
drained++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return drained;
|
||||||
|
}
|
||||||
|
|
||||||
|
static volatile uint8_t g_ch390_irq_pending;
|
||||||
|
static uint8_t g_ch390_ready;
|
||||||
|
static ch390_diag_t g_diag;
|
||||||
|
|
||||||
|
static uint8_t ch390_runtime_probe_identity(void)
|
||||||
|
{
|
||||||
|
g_diag.vendor_id = ch390_get_vendor_id();
|
||||||
|
g_diag.product_id = ch390_get_product_id();
|
||||||
|
g_diag.revision = ch390_get_revision();
|
||||||
|
g_diag.phy_bmcr = ch390_read_phy(CH390_PHY_BMCR);
|
||||||
|
g_diag.phy_bmsr = ch390_read_phy(CH390_PHY_BMSR);
|
||||||
|
g_diag.phy_id1 = ch390_read_phy(CH390_PHY_PHYID1);
|
||||||
|
g_diag.phy_id2 = ch390_read_phy(CH390_PHY_PHYID2);
|
||||||
|
g_diag.phy_anar = ch390_read_phy(CH390_PHY_ANAR);
|
||||||
|
g_diag.phy_anlpar = ch390_read_phy(CH390_PHY_ANLPAR);
|
||||||
|
g_diag.phy_aner = ch390_read_phy(CH390_PHY_ANER);
|
||||||
|
g_diag.nsr = ch390_read_reg(CH390_NSR);
|
||||||
|
g_diag.ncr = ch390_read_reg(CH390_NCR);
|
||||||
|
g_diag.rcr = ch390_read_reg(CH390_RCR);
|
||||||
|
g_diag.imr = ch390_read_reg(CH390_IMR);
|
||||||
|
g_diag.intcr = ch390_read_reg(CH390_INTCR);
|
||||||
|
g_diag.gpr = ch390_read_reg(CH390_GPR);
|
||||||
|
g_diag.isr = ch390_read_reg(CH390_ISR);
|
||||||
|
g_diag.phy_speed_10m = 0u;
|
||||||
|
g_diag.phy_full_duplex = 0u;
|
||||||
|
g_diag.link_up = (uint8_t)0u;
|
||||||
|
g_diag.id_valid = (uint8_t)((g_diag.vendor_id != 0x0000u) &&
|
||||||
|
(g_diag.vendor_id != 0xFFFFu) &&
|
||||||
|
(g_diag.product_id != 0x0000u) &&
|
||||||
|
(g_diag.product_id != 0xFFFFu));
|
||||||
|
return g_diag.id_valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ch390_runtime_refresh_diag(void)
|
||||||
|
{
|
||||||
|
uint8_t id_valid = ch390_runtime_probe_identity();
|
||||||
|
g_diag.int_pin = (uint8_t)ch390_get_int_pin();
|
||||||
|
|
||||||
|
if (id_valid != 0u) {
|
||||||
|
g_diag.phy_speed_10m = (uint8_t)ch390_get_phy_speed();
|
||||||
|
g_diag.phy_full_duplex = (uint8_t)ch390_get_duplex_mode();
|
||||||
|
g_diag.link_up = (uint8_t)ch390_get_link_status();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pbuf *ch390_runtime_input_frame(struct netif *netif)
|
||||||
|
{
|
||||||
|
struct ethernetif *ethernetif = (struct ethernetif *)netif->state;
|
||||||
|
struct pbuf *p = NULL;
|
||||||
|
struct pbuf *q;
|
||||||
|
uint16_t len;
|
||||||
|
uint16_t frame_len;
|
||||||
|
uint8_t rcr;
|
||||||
|
uint8_t rx_ready;
|
||||||
|
uint8_t rx_header[4];
|
||||||
|
ch390_read_reg(CH390_MRCMDX);
|
||||||
|
rx_ready = ch390_read_reg(CH390_MRCMDX);
|
||||||
|
|
||||||
|
if (rx_ready & CH390_PKT_ERR) {
|
||||||
|
rcr = ch390_read_reg(CH390_RCR);
|
||||||
|
ch390_write_reg(CH390_RCR, (uint8_t)(rcr & (uint8_t)(~RCR_RXEN)));
|
||||||
|
ch390_write_reg(CH390_MPTRCR, 0x01u);
|
||||||
|
ch390_write_reg(CH390_MRRH, 0x0Cu);
|
||||||
|
ch390_delay_us(1000u);
|
||||||
|
ch390_write_reg(CH390_RCR, rcr);
|
||||||
|
ethernetif->rx_len = 0u;
|
||||||
|
LINK_STATS_INC(link.drop);
|
||||||
|
g_diag.rx_packets_drop++;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((rx_ready & CH390_PKT_RDY) == 0u) {
|
||||||
|
ethernetif->rx_len = 0u;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_diag.rx_ready_hits++;
|
||||||
|
|
||||||
|
ch390_read_mem(rx_header, 4);
|
||||||
|
ethernetif->rx_status = rx_header[1];
|
||||||
|
frame_len = (uint16_t)((uint16_t)rx_header[2] | ((uint16_t)rx_header[3] << 8));
|
||||||
|
|
||||||
|
if ((ethernetif->rx_status & 0x3Fu) != 0u || frame_len == 0u || frame_len > CH390_PKT_MAX) {
|
||||||
|
ethernetif->rx_len = 0u;
|
||||||
|
ch390_drop_packet(frame_len);
|
||||||
|
LINK_STATS_INC(link.drop);
|
||||||
|
g_diag.rx_packets_drop++;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ethernetif->rx_len = frame_len;
|
||||||
|
len = ethernetif->rx_len;
|
||||||
|
#if ETH_PAD_SIZE
|
||||||
|
len += ETH_PAD_SIZE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
|
||||||
|
if (p != NULL) {
|
||||||
|
#if ETH_PAD_SIZE
|
||||||
|
pbuf_remove_header(p, ETH_PAD_SIZE);
|
||||||
|
#endif
|
||||||
|
for (q = p; q != NULL; q = q->next) {
|
||||||
|
ch390_read_mem((uint8_t *)q->payload, q->len);
|
||||||
|
}
|
||||||
|
#if ETH_PAD_SIZE
|
||||||
|
pbuf_add_header(p, ETH_PAD_SIZE);
|
||||||
|
#endif
|
||||||
|
LINK_STATS_INC(link.recv);
|
||||||
|
g_diag.rx_packets_ok++;
|
||||||
|
|
||||||
|
g_diag.last_frame_len = frame_len;
|
||||||
|
g_diag.last_payload_len = p->tot_len;
|
||||||
|
} else {
|
||||||
|
ch390_drop_packet(ethernetif->rx_len);
|
||||||
|
LINK_STATS_INC(link.memerr);
|
||||||
|
LINK_STATS_INC(link.drop);
|
||||||
|
g_diag.rx_packets_drop++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ch390_runtime_init(struct netif *netif, const uint8_t *mac)
|
||||||
|
{
|
||||||
|
struct ethernetif *ethernetif = (struct ethernetif *)netif->state;
|
||||||
|
|
||||||
|
SEGGER_RTT_WriteString(0, "ETH init: gpio\r\n");
|
||||||
|
ch390_gpio_init();
|
||||||
|
SEGGER_RTT_WriteString(0, "ETH init: spi\r\n");
|
||||||
|
ch390_spi_init();
|
||||||
|
SEGGER_RTT_WriteString(0, "ETH init: reset\r\n");
|
||||||
|
ch390_hardware_reset();
|
||||||
|
|
||||||
|
SEGGER_RTT_WriteString(0, "ETH init: probe\r\n");
|
||||||
|
g_ch390_ready = ch390_runtime_probe_identity();
|
||||||
|
if (g_ch390_ready == 0u) {
|
||||||
|
netif->hwaddr_len = ETHARP_HWADDR_LEN;
|
||||||
|
netif->mtu = 1500;
|
||||||
|
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET;
|
||||||
|
|
||||||
|
ethernetif->rx_len = 0u;
|
||||||
|
ethernetif->rx_status = 0u;
|
||||||
|
|
||||||
|
netif_set_link_down(netif);
|
||||||
|
SEGGER_RTT_WriteString(0, "ETH init: invalid chip id\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SEGGER_RTT_WriteString(0, "ETH init: default\r\n");
|
||||||
|
ch390_default_config();
|
||||||
|
SEGGER_RTT_WriteString(0, "ETH init: mac\r\n");
|
||||||
|
ch390_set_mac_address((uint8_t *)mac);
|
||||||
|
|
||||||
|
netif->hwaddr_len = ETHARP_HWADDR_LEN;
|
||||||
|
SEGGER_RTT_WriteString(0, "ETH init: getmac\r\n");
|
||||||
|
ch390_get_mac(netif->hwaddr);
|
||||||
|
netif->mtu = 1500;
|
||||||
|
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET;
|
||||||
|
|
||||||
|
ethernetif->rx_len = 0u;
|
||||||
|
ethernetif->rx_status = 0u;
|
||||||
|
|
||||||
|
ch390_runtime_refresh_diag();
|
||||||
|
g_ch390_ready = g_diag.id_valid;
|
||||||
|
|
||||||
|
SEGGER_RTT_WriteString(0, "ETH init: irq\r\n");
|
||||||
|
ch390_interrupt_init();
|
||||||
|
SEGGER_RTT_WriteString(0, "ETH init: done\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ch390_runtime_set_irq_pending(void)
|
||||||
|
{
|
||||||
|
g_ch390_irq_pending = 1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ch390_runtime_is_irq_pending(void)
|
||||||
|
{
|
||||||
|
return g_ch390_irq_pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ch390_runtime_poll(struct netif *netif)
|
||||||
|
{
|
||||||
|
uint8_t int_status;
|
||||||
|
uint8_t rx_ready;
|
||||||
|
uint8_t rx_budget;
|
||||||
|
uint8_t rx_hint;
|
||||||
|
|
||||||
|
if (!g_ch390_ready) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_diag.rx_poll_calls++;
|
||||||
|
|
||||||
|
rx_budget = 1u;
|
||||||
|
rx_hint = 0u;
|
||||||
|
|
||||||
|
if ((g_ch390_irq_pending != 0u) || (ch390_get_int_pin() == GPIO_PIN_RESET)) {
|
||||||
|
g_ch390_irq_pending = 0u;
|
||||||
|
int_status = ch390_get_int_status();
|
||||||
|
|
||||||
|
if ((int_status & ISR_LNKCHG) != 0u) {
|
||||||
|
ch390_runtime_check_link(netif);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((int_status & ISR_ROS) != 0u) {
|
||||||
|
LINK_STATS_INC(link.err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((int_status & (ISR_PR | ISR_ROS | ISR_ROO)) != 0u) {
|
||||||
|
rx_hint = 1u;
|
||||||
|
rx_budget = 8u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ch390_read_reg(CH390_MRCMDX);
|
||||||
|
rx_ready = ch390_read_reg(CH390_MRCMDX);
|
||||||
|
if ((rx_ready & CH390_PKT_RDY) != 0u) {
|
||||||
|
rx_hint = 1u;
|
||||||
|
if (rx_budget < 4u) {
|
||||||
|
rx_budget = 4u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rx_hint != 0u) {
|
||||||
|
(void)ch390_runtime_drain_rx(netif, rx_budget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ch390_runtime_check_link(struct netif *netif)
|
||||||
|
{
|
||||||
|
uint8_t link_up;
|
||||||
|
static uint8_t s_last_reported = 0xFFu;
|
||||||
|
|
||||||
|
if (!g_ch390_ready) {
|
||||||
|
netif_set_link_down(netif);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ch390_runtime_refresh_diag();
|
||||||
|
link_up = (uint8_t)ch390_get_link_status();
|
||||||
|
|
||||||
|
if (link_up != s_last_reported) {
|
||||||
|
SEGGER_RTT_printf(0,
|
||||||
|
"ETH link %s nsr=0x%02X bmsr=0x%04X anlpar=0x%04X\r\n",
|
||||||
|
link_up ? "up" : "down",
|
||||||
|
g_diag.nsr,
|
||||||
|
g_diag.phy_bmsr,
|
||||||
|
g_diag.phy_anlpar);
|
||||||
|
s_last_reported = link_up;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (link_up) {
|
||||||
|
if (!netif_is_link_up(netif)) {
|
||||||
|
netif_set_link_up(netif);
|
||||||
|
}
|
||||||
|
} else if (netif_is_link_up(netif)) {
|
||||||
|
netif_set_link_down(netif);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err_t ch390_runtime_output(struct netif *netif, struct pbuf *p)
|
||||||
|
{
|
||||||
|
struct pbuf *q;
|
||||||
|
uint32_t start_tick;
|
||||||
|
|
||||||
|
LWIP_UNUSED_ARG(netif);
|
||||||
|
|
||||||
|
if (!g_ch390_ready) {
|
||||||
|
LINK_STATS_INC(link.drop);
|
||||||
|
return ERR_IF;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if ETH_PAD_SIZE
|
||||||
|
pbuf_remove_header(p, ETH_PAD_SIZE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
start_tick = HAL_GetTick();
|
||||||
|
while (ch390_read_reg(CH390_TCR) & TCR_TXREQ) {
|
||||||
|
if ((HAL_GetTick() - start_tick) > 10u) {
|
||||||
|
#if ETH_PAD_SIZE
|
||||||
|
pbuf_add_header(p, ETH_PAD_SIZE);
|
||||||
|
#endif
|
||||||
|
LINK_STATS_INC(link.drop);
|
||||||
|
g_diag.tx_packets_timeout++;
|
||||||
|
return ERR_TIMEOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (q = p; q != NULL; q = q->next) {
|
||||||
|
ch390_write_mem((uint8_t *)q->payload, q->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
ch390_write_reg(CH390_TXPLL, p->tot_len & 0xFFu);
|
||||||
|
ch390_write_reg(CH390_TXPLH, (p->tot_len >> 8) & 0xFFu);
|
||||||
|
ch390_send_request();
|
||||||
|
|
||||||
|
#if ETH_PAD_SIZE
|
||||||
|
pbuf_add_header(p, ETH_PAD_SIZE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LINK_STATS_INC(link.xmit);
|
||||||
|
g_diag.tx_packets_ok++;
|
||||||
|
return ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ch390_runtime_get_diag(ch390_diag_t *diag)
|
||||||
|
{
|
||||||
|
if (diag != NULL) {
|
||||||
|
ch390_runtime_refresh_diag();
|
||||||
|
*diag = g_diag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ch390_runtime_is_ready(void)
|
||||||
|
{
|
||||||
|
return g_ch390_ready != 0u;
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
#ifndef __CH390_RUNTIME_H__
|
||||||
|
#define __CH390_RUNTIME_H__
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "lwip/err.h"
|
||||||
|
|
||||||
|
struct netif;
|
||||||
|
struct pbuf;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t vendor_id;
|
||||||
|
uint16_t product_id;
|
||||||
|
uint8_t revision;
|
||||||
|
uint16_t phy_bmcr;
|
||||||
|
uint16_t phy_bmsr;
|
||||||
|
uint16_t phy_id1;
|
||||||
|
uint16_t phy_id2;
|
||||||
|
uint16_t phy_anar;
|
||||||
|
uint16_t phy_anlpar;
|
||||||
|
uint16_t phy_aner;
|
||||||
|
uint8_t nsr;
|
||||||
|
uint8_t ncr;
|
||||||
|
uint8_t rcr;
|
||||||
|
uint8_t imr;
|
||||||
|
uint8_t intcr;
|
||||||
|
uint8_t gpr;
|
||||||
|
uint8_t isr;
|
||||||
|
uint8_t int_pin;
|
||||||
|
uint8_t phy_speed_10m;
|
||||||
|
uint8_t phy_full_duplex;
|
||||||
|
uint8_t link_up;
|
||||||
|
uint8_t id_valid;
|
||||||
|
uint32_t rx_poll_calls;
|
||||||
|
uint32_t rx_ready_hits;
|
||||||
|
uint32_t rx_packets_ok;
|
||||||
|
uint32_t rx_packets_drop;
|
||||||
|
uint32_t tx_packets_ok;
|
||||||
|
uint32_t tx_packets_timeout;
|
||||||
|
uint32_t rx_arp_frames;
|
||||||
|
uint32_t rx_ip_frames;
|
||||||
|
uint32_t rx_other_frames;
|
||||||
|
uint32_t rx_unicast_self_frames;
|
||||||
|
uint32_t rx_broadcast_frames;
|
||||||
|
uint32_t rx_multicast_frames;
|
||||||
|
uint16_t last_frame_len;
|
||||||
|
uint16_t last_payload_len;
|
||||||
|
uint16_t last_eth_type;
|
||||||
|
} ch390_diag_t;
|
||||||
|
|
||||||
|
void ch390_runtime_init(struct netif *netif, const uint8_t *mac);
|
||||||
|
struct pbuf *ch390_runtime_input_frame(struct netif *netif);
|
||||||
|
void ch390_runtime_set_irq_pending(void);
|
||||||
|
uint8_t ch390_runtime_is_irq_pending(void);
|
||||||
|
void ch390_runtime_poll(struct netif *netif);
|
||||||
|
void ch390_runtime_check_link(struct netif *netif);
|
||||||
|
err_t ch390_runtime_output(struct netif *netif, struct pbuf *p);
|
||||||
|
void ch390_runtime_get_diag(ch390_diag_t *diag);
|
||||||
|
bool ch390_runtime_is_ready(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -645,7 +645,6 @@ etharp_input(struct pbuf *p, struct netif *netif)
|
|||||||
/* these are aligned properly, whereas the ARP header fields might not be */
|
/* these are aligned properly, whereas the ARP header fields might not be */
|
||||||
ip4_addr_t sipaddr, dipaddr;
|
ip4_addr_t sipaddr, dipaddr;
|
||||||
u8_t for_us, from_us;
|
u8_t for_us, from_us;
|
||||||
|
|
||||||
LWIP_ASSERT_CORE_LOCKED();
|
LWIP_ASSERT_CORE_LOCKED();
|
||||||
|
|
||||||
LWIP_ERROR("netif != NULL", (netif != NULL), return;);
|
LWIP_ERROR("netif != NULL", (netif != NULL), return;);
|
||||||
|
|||||||
@@ -87,7 +87,6 @@ icmp_input(struct pbuf *p, struct netif *inp)
|
|||||||
const struct ip_hdr *iphdr_in;
|
const struct ip_hdr *iphdr_in;
|
||||||
u16_t hlen;
|
u16_t hlen;
|
||||||
const ip4_addr_t *src;
|
const ip4_addr_t *src;
|
||||||
|
|
||||||
ICMP_STATS_INC(icmp.recv);
|
ICMP_STATS_INC(icmp.recv);
|
||||||
MIB2_STATS_INC(mib2.icmpinmsgs);
|
MIB2_STATS_INC(mib2.icmpinmsgs);
|
||||||
|
|
||||||
|
|||||||
@@ -18,175 +18,51 @@
|
|||||||
|
|
||||||
#include "CH390.h"
|
#include "CH390.h"
|
||||||
#include "CH390_Interface.h"
|
#include "CH390_Interface.h"
|
||||||
|
#include "ch390_runtime.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "stm32f1xx_hal.h"
|
#include "stm32f1xx_hal.h"
|
||||||
|
#include "SEGGER_RTT.h"
|
||||||
|
|
||||||
#define IFNAME0 'e'
|
#define IFNAME0 'e'
|
||||||
#define IFNAME1 'n'
|
#define IFNAME1 'n'
|
||||||
|
|
||||||
struct netif ch390_netif;
|
struct netif ch390_netif;
|
||||||
static volatile uint8_t g_ch390_irq_pending;
|
|
||||||
static void ethernetif_unlock(uint32_t primask);
|
|
||||||
|
|
||||||
static uint32_t ethernetif_lock(void)
|
|
||||||
{
|
|
||||||
uint32_t primask = __get_PRIMASK();
|
|
||||||
__disable_irq();
|
|
||||||
return primask;
|
|
||||||
}
|
|
||||||
|
|
||||||
sys_prot_t sys_arch_protect(void)
|
sys_prot_t sys_arch_protect(void)
|
||||||
{
|
{
|
||||||
return (sys_prot_t)ethernetif_lock();
|
__disable_irq();
|
||||||
|
return 0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sys_arch_unprotect(sys_prot_t pval)
|
void sys_arch_unprotect(sys_prot_t pval)
|
||||||
{
|
{
|
||||||
ethernetif_unlock((uint32_t)pval);
|
LWIP_UNUSED_ARG(pval);
|
||||||
}
|
|
||||||
|
|
||||||
static void ethernetif_unlock(uint32_t primask)
|
|
||||||
{
|
|
||||||
if ((primask & 1u) == 0u) {
|
|
||||||
__enable_irq();
|
__enable_irq();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void ethernetif_set_irq_pending(void)
|
void ethernetif_set_irq_pending(void)
|
||||||
{
|
{
|
||||||
g_ch390_irq_pending = 1u;
|
ch390_runtime_set_irq_pending();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t ethernetif_is_irq_pending(void)
|
uint8_t ethernetif_is_irq_pending(void)
|
||||||
{
|
{
|
||||||
return g_ch390_irq_pending;
|
return ch390_runtime_is_irq_pending();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void low_level_init(struct netif *netif)
|
static void low_level_init(struct netif *netif)
|
||||||
{
|
{
|
||||||
struct ethernetif *ethernetif = netif->state;
|
ch390_runtime_init(netif, config_get()->net.mac);
|
||||||
|
|
||||||
ch390_gpio_init();
|
|
||||||
ch390_spi_init();
|
|
||||||
ch390_hardware_reset();
|
|
||||||
ch390_default_config();
|
|
||||||
ch390_set_mac_address((uint8_t *)config_get()->mac);
|
|
||||||
|
|
||||||
netif->hwaddr_len = ETHARP_HWADDR_LEN;
|
|
||||||
ch390_get_mac(netif->hwaddr);
|
|
||||||
netif->mtu = 1500;
|
|
||||||
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
|
|
||||||
|
|
||||||
ethernetif->rx_len = 0u;
|
|
||||||
ethernetif->rx_status = 0u;
|
|
||||||
|
|
||||||
ch390_interrupt_init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static err_t low_level_output(struct netif *netif, struct pbuf *p)
|
static err_t low_level_output(struct netif *netif, struct pbuf *p)
|
||||||
{
|
{
|
||||||
struct pbuf *q;
|
return ch390_runtime_output(netif, p);
|
||||||
uint32_t primask;
|
|
||||||
uint32_t start_tick;
|
|
||||||
|
|
||||||
LWIP_UNUSED_ARG(netif);
|
|
||||||
primask = ethernetif_lock();
|
|
||||||
|
|
||||||
#if ETH_PAD_SIZE
|
|
||||||
pbuf_remove_header(p, ETH_PAD_SIZE);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
start_tick = HAL_GetTick();
|
|
||||||
while (ch390_read_reg(CH390_TCR) & TCR_TXREQ) {
|
|
||||||
if ((HAL_GetTick() - start_tick) > 10u) {
|
|
||||||
ethernetif_unlock(primask);
|
|
||||||
#if ETH_PAD_SIZE
|
|
||||||
pbuf_add_header(p, ETH_PAD_SIZE);
|
|
||||||
#endif
|
|
||||||
LINK_STATS_INC(link.drop);
|
|
||||||
return ERR_TIMEOUT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (q = p; q != NULL; q = q->next) {
|
|
||||||
ch390_write_mem(q->payload, q->len);
|
|
||||||
}
|
|
||||||
|
|
||||||
ch390_write_reg(CH390_TXPLL, p->tot_len & 0xFFu);
|
|
||||||
ch390_write_reg(CH390_TXPLH, (p->tot_len >> 8) & 0xFFu);
|
|
||||||
ch390_send_request();
|
|
||||||
ethernetif_unlock(primask);
|
|
||||||
|
|
||||||
#if ETH_PAD_SIZE
|
|
||||||
pbuf_add_header(p, ETH_PAD_SIZE);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
LINK_STATS_INC(link.xmit);
|
|
||||||
return ERR_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct pbuf *low_level_input(struct netif *netif)
|
static struct pbuf *low_level_input(struct netif *netif)
|
||||||
{
|
{
|
||||||
struct ethernetif *ethernetif = netif->state;
|
return ch390_runtime_input_frame(netif);
|
||||||
struct pbuf *p = NULL;
|
|
||||||
struct pbuf *q;
|
|
||||||
uint16_t len;
|
|
||||||
uint8_t rx_ready;
|
|
||||||
uint8_t rx_header[4];
|
|
||||||
uint32_t primask;
|
|
||||||
|
|
||||||
primask = ethernetif_lock();
|
|
||||||
ch390_read_reg(CH390_MRCMDX);
|
|
||||||
rx_ready = ch390_read_reg(CH390_MRCMDX);
|
|
||||||
|
|
||||||
if (rx_ready & CH390_PKT_ERR) {
|
|
||||||
ch390_write_reg(CH390_RCR, 0u);
|
|
||||||
ch390_write_reg(CH390_MPTRCR, 0x01u);
|
|
||||||
ch390_write_reg(CH390_MRRH, 0x0Cu);
|
|
||||||
ch390_delay_us(1000u);
|
|
||||||
ch390_write_reg(CH390_RCR, RCR_RXEN | RCR_DIS_CRC);
|
|
||||||
ethernetif->rx_len = 0u;
|
|
||||||
LINK_STATS_INC(link.drop);
|
|
||||||
ethernetif_unlock(primask);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((rx_ready & CH390_PKT_RDY) == 0u) {
|
|
||||||
ethernetif->rx_len = 0u;
|
|
||||||
ethernetif_unlock(primask);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ch390_read_mem(rx_header, 4);
|
|
||||||
ethernetif->rx_status = rx_header[1];
|
|
||||||
ethernetif->rx_len = (uint16_t)(((uint16_t)rx_header[2] | ((uint16_t)rx_header[3] << 8)) - 4u);
|
|
||||||
len = ethernetif->rx_len;
|
|
||||||
|
|
||||||
#if ETH_PAD_SIZE
|
|
||||||
len += ETH_PAD_SIZE;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
|
|
||||||
if (p != NULL) {
|
|
||||||
#if ETH_PAD_SIZE
|
|
||||||
pbuf_remove_header(p, ETH_PAD_SIZE);
|
|
||||||
#endif
|
|
||||||
for (q = p; q != NULL; q = q->next) {
|
|
||||||
ch390_read_mem(q->payload, q->len);
|
|
||||||
}
|
|
||||||
#if ETH_PAD_SIZE
|
|
||||||
pbuf_add_header(p, ETH_PAD_SIZE);
|
|
||||||
#endif
|
|
||||||
ch390_drop_packet(4u);
|
|
||||||
LINK_STATS_INC(link.recv);
|
|
||||||
} else {
|
|
||||||
ch390_drop_packet((uint16_t)(ethernetif->rx_len + 4u));
|
|
||||||
LINK_STATS_INC(link.memerr);
|
|
||||||
LINK_STATS_INC(link.drop);
|
|
||||||
}
|
|
||||||
|
|
||||||
ethernetif_unlock(primask);
|
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ethernetif_input(struct netif *netif)
|
void ethernetif_input(struct netif *netif)
|
||||||
@@ -237,55 +113,12 @@ void lwip_netif_init(const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const
|
|||||||
|
|
||||||
void ethernetif_check_link(void)
|
void ethernetif_check_link(void)
|
||||||
{
|
{
|
||||||
uint8_t link_up;
|
ch390_runtime_check_link(&ch390_netif);
|
||||||
uint32_t primask = ethernetif_lock();
|
|
||||||
|
|
||||||
link_up = (uint8_t)ch390_get_link_status();
|
|
||||||
ethernetif_unlock(primask);
|
|
||||||
|
|
||||||
if (link_up) {
|
|
||||||
if (!netif_is_link_up(&ch390_netif)) {
|
|
||||||
netif_set_link_up(&ch390_netif);
|
|
||||||
}
|
|
||||||
} else if (netif_is_link_up(&ch390_netif)) {
|
|
||||||
netif_set_link_down(&ch390_netif);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ethernetif_poll(void)
|
void ethernetif_poll(void)
|
||||||
{
|
{
|
||||||
uint8_t int_status;
|
ch390_runtime_poll(&ch390_netif);
|
||||||
uint32_t primask;
|
|
||||||
|
|
||||||
if (g_ch390_irq_pending == 0u) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
primask = ethernetif_lock();
|
|
||||||
int_status = ch390_read_reg(CH390_ISR);
|
|
||||||
ch390_write_reg(CH390_ISR, int_status);
|
|
||||||
g_ch390_irq_pending = 0u;
|
|
||||||
ethernetif_unlock(primask);
|
|
||||||
|
|
||||||
if ((int_status & ISR_LNKCHG) != 0u) {
|
|
||||||
ethernetif_check_link();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((int_status & ISR_ROS) != 0u) {
|
|
||||||
LINK_STATS_INC(link.err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((int_status & ISR_PR) != 0u) {
|
|
||||||
while (1) {
|
|
||||||
struct pbuf *p = low_level_input(&ch390_netif);
|
|
||||||
if (p == NULL) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (ch390_netif.input(p, &ch390_netif) != ERR_OK) {
|
|
||||||
pbuf_free(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32_t sys_now(void)
|
u32_t sys_now(void)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "lwip/err.h"
|
#include "lwip/err.h"
|
||||||
#include "lwip/netif.h"
|
#include "lwip/netif.h"
|
||||||
|
#include "ch390_runtime.h"
|
||||||
|
|
||||||
struct ethernetif {
|
struct ethernetif {
|
||||||
uint16_t rx_len;
|
uint16_t rx_len;
|
||||||
|
|||||||
+435
-78
@@ -103,7 +103,7 @@
|
|||||||
<bEvRecOn>1</bEvRecOn>
|
<bEvRecOn>1</bEvRecOn>
|
||||||
<bSchkAxf>0</bSchkAxf>
|
<bSchkAxf>0</bSchkAxf>
|
||||||
<bTchkAxf>0</bTchkAxf>
|
<bTchkAxf>0</bTchkAxf>
|
||||||
<nTsel>6</nTsel>
|
<nTsel>3</nTsel>
|
||||||
<sDll></sDll>
|
<sDll></sDll>
|
||||||
<sDllPa></sDllPa>
|
<sDllPa></sDllPa>
|
||||||
<sDlgDll></sDlgDll>
|
<sDlgDll></sDlgDll>
|
||||||
@@ -114,9 +114,34 @@
|
|||||||
<tDlgDll></tDlgDll>
|
<tDlgDll></tDlgDll>
|
||||||
<tDlgPa></tDlgPa>
|
<tDlgPa></tDlgPa>
|
||||||
<tIfile></tIfile>
|
<tIfile></tIfile>
|
||||||
<pMon>STLink\ST-LINKIII-KEIL_SWO.dll</pMon>
|
<pMon>BIN\CMSIS_AGDI.dll</pMon>
|
||||||
</DebugOpt>
|
</DebugOpt>
|
||||||
<TargetDriverDllRegistry>
|
<TargetDriverDllRegistry>
|
||||||
|
<SetRegEntry>
|
||||||
|
<Number>0</Number>
|
||||||
|
<Key>ARMRTXEVENTFLAGS</Key>
|
||||||
|
<Name>-L70 -Z18 -C0 -M0 -T1</Name>
|
||||||
|
</SetRegEntry>
|
||||||
|
<SetRegEntry>
|
||||||
|
<Number>0</Number>
|
||||||
|
<Key>DLGTARM</Key>
|
||||||
|
<Name>(1010=-1,-1,-1,-1,0)(1007=-1,-1,-1,-1,0)(1008=-1,-1,-1,-1,0)(1009=-1,-1,-1,-1,0)</Name>
|
||||||
|
</SetRegEntry>
|
||||||
|
<SetRegEntry>
|
||||||
|
<Number>0</Number>
|
||||||
|
<Key>ARMDBGFLAGS</Key>
|
||||||
|
<Name></Name>
|
||||||
|
</SetRegEntry>
|
||||||
|
<SetRegEntry>
|
||||||
|
<Number>0</Number>
|
||||||
|
<Key>DLGUARM</Key>
|
||||||
|
<Name></Name>
|
||||||
|
</SetRegEntry>
|
||||||
|
<SetRegEntry>
|
||||||
|
<Number>0</Number>
|
||||||
|
<Key>CMSIS_AGDI</Key>
|
||||||
|
<Name>-X"Any" -UAny -O206 -S8 -C0 -P00000000 -N00("ARM CoreSight SW-DP") -D00(1BA01477) -L00(0) -TO65554 -TC10000000 -TT10000000 -TP20 -TDS8007 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -FO7 -FD20000000 -FC1000 -FN1 -FF0STM32F10x_128.FLM -FS08000000 -FL020000 -FP0($$Device:STM32F103R8$Flash\STM32F10x_128.FLM)</Name>
|
||||||
|
</SetRegEntry>
|
||||||
<SetRegEntry>
|
<SetRegEntry>
|
||||||
<Number>0</Number>
|
<Number>0</Number>
|
||||||
<Key>UL2CM3</Key>
|
<Key>UL2CM3</Key>
|
||||||
@@ -194,8 +219,8 @@
|
|||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
<bDave2>0</bDave2>
|
<bDave2>0</bDave2>
|
||||||
<PathWithFileName>startup_stm32f103xe.s</PathWithFileName>
|
<PathWithFileName>startup_stm32f103xb.s</PathWithFileName>
|
||||||
<FilenameWithoutPath>startup_stm32f103xe.s</FilenameWithoutPath>
|
<FilenameWithoutPath>startup_stm32f103xb.s</FilenameWithoutPath>
|
||||||
<RteFlg>0</RteFlg>
|
<RteFlg>0</RteFlg>
|
||||||
<bShared>0</bShared>
|
<bShared>0</bShared>
|
||||||
</File>
|
</File>
|
||||||
@@ -238,8 +263,8 @@
|
|||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
<bDave2>0</bDave2>
|
<bDave2>0</bDave2>
|
||||||
<PathWithFileName>../Core/Src/freertos.c</PathWithFileName>
|
<PathWithFileName>../Core/Src/dma.c</PathWithFileName>
|
||||||
<FilenameWithoutPath>freertos.c</FilenameWithoutPath>
|
<FilenameWithoutPath>dma.c</FilenameWithoutPath>
|
||||||
<RteFlg>0</RteFlg>
|
<RteFlg>0</RteFlg>
|
||||||
<bShared>0</bShared>
|
<bShared>0</bShared>
|
||||||
</File>
|
</File>
|
||||||
@@ -250,8 +275,8 @@
|
|||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
<bDave2>0</bDave2>
|
<bDave2>0</bDave2>
|
||||||
<PathWithFileName>../Core/Src/dma.c</PathWithFileName>
|
<PathWithFileName>../Core/Src/iwdg.c</PathWithFileName>
|
||||||
<FilenameWithoutPath>dma.c</FilenameWithoutPath>
|
<FilenameWithoutPath>iwdg.c</FilenameWithoutPath>
|
||||||
<RteFlg>0</RteFlg>
|
<RteFlg>0</RteFlg>
|
||||||
<bShared>0</bShared>
|
<bShared>0</bShared>
|
||||||
</File>
|
</File>
|
||||||
@@ -262,8 +287,8 @@
|
|||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
<bDave2>0</bDave2>
|
<bDave2>0</bDave2>
|
||||||
<PathWithFileName>../Core/Src/iwdg.c</PathWithFileName>
|
<PathWithFileName>../Core/Src/tim.c</PathWithFileName>
|
||||||
<FilenameWithoutPath>iwdg.c</FilenameWithoutPath>
|
<FilenameWithoutPath>tim.c</FilenameWithoutPath>
|
||||||
<RteFlg>0</RteFlg>
|
<RteFlg>0</RteFlg>
|
||||||
<bShared>0</bShared>
|
<bShared>0</bShared>
|
||||||
</File>
|
</File>
|
||||||
@@ -342,6 +367,18 @@
|
|||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
<bDave2>0</bDave2>
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_iwdg.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>stm32f1xx_hal_iwdg.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>3</GroupNumber>
|
||||||
|
<FileNumber>13</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal.c</PathWithFileName>
|
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal.c</PathWithFileName>
|
||||||
<FilenameWithoutPath>stm32f1xx_hal.c</FilenameWithoutPath>
|
<FilenameWithoutPath>stm32f1xx_hal.c</FilenameWithoutPath>
|
||||||
<RteFlg>0</RteFlg>
|
<RteFlg>0</RteFlg>
|
||||||
@@ -349,7 +386,7 @@
|
|||||||
</File>
|
</File>
|
||||||
<File>
|
<File>
|
||||||
<GroupNumber>3</GroupNumber>
|
<GroupNumber>3</GroupNumber>
|
||||||
<FileNumber>13</FileNumber>
|
<FileNumber>14</FileNumber>
|
||||||
<FileType>1</FileType>
|
<FileType>1</FileType>
|
||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
@@ -361,7 +398,7 @@
|
|||||||
</File>
|
</File>
|
||||||
<File>
|
<File>
|
||||||
<GroupNumber>3</GroupNumber>
|
<GroupNumber>3</GroupNumber>
|
||||||
<FileNumber>14</FileNumber>
|
<FileNumber>15</FileNumber>
|
||||||
<FileType>1</FileType>
|
<FileType>1</FileType>
|
||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
@@ -373,7 +410,7 @@
|
|||||||
</File>
|
</File>
|
||||||
<File>
|
<File>
|
||||||
<GroupNumber>3</GroupNumber>
|
<GroupNumber>3</GroupNumber>
|
||||||
<FileNumber>15</FileNumber>
|
<FileNumber>16</FileNumber>
|
||||||
<FileType>1</FileType>
|
<FileType>1</FileType>
|
||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
@@ -385,7 +422,7 @@
|
|||||||
</File>
|
</File>
|
||||||
<File>
|
<File>
|
||||||
<GroupNumber>3</GroupNumber>
|
<GroupNumber>3</GroupNumber>
|
||||||
<FileNumber>16</FileNumber>
|
<FileNumber>17</FileNumber>
|
||||||
<FileType>1</FileType>
|
<FileType>1</FileType>
|
||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
@@ -397,7 +434,7 @@
|
|||||||
</File>
|
</File>
|
||||||
<File>
|
<File>
|
||||||
<GroupNumber>3</GroupNumber>
|
<GroupNumber>3</GroupNumber>
|
||||||
<FileNumber>17</FileNumber>
|
<FileNumber>18</FileNumber>
|
||||||
<FileType>1</FileType>
|
<FileType>1</FileType>
|
||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
@@ -409,7 +446,7 @@
|
|||||||
</File>
|
</File>
|
||||||
<File>
|
<File>
|
||||||
<GroupNumber>3</GroupNumber>
|
<GroupNumber>3</GroupNumber>
|
||||||
<FileNumber>18</FileNumber>
|
<FileNumber>19</FileNumber>
|
||||||
<FileType>1</FileType>
|
<FileType>1</FileType>
|
||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
@@ -421,7 +458,7 @@
|
|||||||
</File>
|
</File>
|
||||||
<File>
|
<File>
|
||||||
<GroupNumber>3</GroupNumber>
|
<GroupNumber>3</GroupNumber>
|
||||||
<FileNumber>19</FileNumber>
|
<FileNumber>20</FileNumber>
|
||||||
<FileType>1</FileType>
|
<FileType>1</FileType>
|
||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
@@ -433,7 +470,7 @@
|
|||||||
</File>
|
</File>
|
||||||
<File>
|
<File>
|
||||||
<GroupNumber>3</GroupNumber>
|
<GroupNumber>3</GroupNumber>
|
||||||
<FileNumber>20</FileNumber>
|
<FileNumber>21</FileNumber>
|
||||||
<FileType>1</FileType>
|
<FileType>1</FileType>
|
||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
@@ -445,7 +482,7 @@
|
|||||||
</File>
|
</File>
|
||||||
<File>
|
<File>
|
||||||
<GroupNumber>3</GroupNumber>
|
<GroupNumber>3</GroupNumber>
|
||||||
<FileNumber>21</FileNumber>
|
<FileNumber>22</FileNumber>
|
||||||
<FileType>1</FileType>
|
<FileType>1</FileType>
|
||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
@@ -455,18 +492,6 @@
|
|||||||
<RteFlg>0</RteFlg>
|
<RteFlg>0</RteFlg>
|
||||||
<bShared>0</bShared>
|
<bShared>0</bShared>
|
||||||
</File>
|
</File>
|
||||||
<File>
|
|
||||||
<GroupNumber>3</GroupNumber>
|
|
||||||
<FileNumber>22</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_iwdg.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>stm32f1xx_hal_iwdg.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
<File>
|
||||||
<GroupNumber>3</GroupNumber>
|
<GroupNumber>3</GroupNumber>
|
||||||
<FileNumber>23</FileNumber>
|
<FileNumber>23</FileNumber>
|
||||||
@@ -486,6 +511,30 @@
|
|||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
<bDave2>0</bDave2>
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_tim.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>stm32f1xx_hal_tim.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>3</GroupNumber>
|
||||||
|
<FileNumber>25</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_tim_ex.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>stm32f1xx_hal_tim_ex.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>3</GroupNumber>
|
||||||
|
<FileNumber>26</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_uart.c</PathWithFileName>
|
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_uart.c</PathWithFileName>
|
||||||
<FilenameWithoutPath>stm32f1xx_hal_uart.c</FilenameWithoutPath>
|
<FilenameWithoutPath>stm32f1xx_hal_uart.c</FilenameWithoutPath>
|
||||||
<RteFlg>0</RteFlg>
|
<RteFlg>0</RteFlg>
|
||||||
@@ -501,7 +550,7 @@
|
|||||||
<RteFlg>0</RteFlg>
|
<RteFlg>0</RteFlg>
|
||||||
<File>
|
<File>
|
||||||
<GroupNumber>4</GroupNumber>
|
<GroupNumber>4</GroupNumber>
|
||||||
<FileNumber>25</FileNumber>
|
<FileNumber>27</FileNumber>
|
||||||
<FileType>1</FileType>
|
<FileType>1</FileType>
|
||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
@@ -514,35 +563,11 @@
|
|||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Group>
|
<Group>
|
||||||
<GroupName>Middlewares/FreeRTOS</GroupName>
|
<GroupName>Drivers/CH390</GroupName>
|
||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
<cbSel>0</cbSel>
|
<cbSel>0</cbSel>
|
||||||
<RteFlg>0</RteFlg>
|
<RteFlg>0</RteFlg>
|
||||||
<File>
|
|
||||||
<GroupNumber>5</GroupNumber>
|
|
||||||
<FileNumber>26</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Middlewares/Third_Party/FreeRTOS/Source/croutine.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>croutine.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>5</GroupNumber>
|
|
||||||
<FileNumber>27</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Middlewares/Third_Party/FreeRTOS/Source/event_groups.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>event_groups.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
<File>
|
||||||
<GroupNumber>5</GroupNumber>
|
<GroupNumber>5</GroupNumber>
|
||||||
<FileNumber>28</FileNumber>
|
<FileNumber>28</FileNumber>
|
||||||
@@ -550,8 +575,8 @@
|
|||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
<bDave2>0</bDave2>
|
<bDave2>0</bDave2>
|
||||||
<PathWithFileName>../Middlewares/Third_Party/FreeRTOS/Source/list.c</PathWithFileName>
|
<PathWithFileName>..\Drivers\CH390\CH390.c</PathWithFileName>
|
||||||
<FilenameWithoutPath>list.c</FilenameWithoutPath>
|
<FilenameWithoutPath>CH390.c</FilenameWithoutPath>
|
||||||
<RteFlg>0</RteFlg>
|
<RteFlg>0</RteFlg>
|
||||||
<bShared>0</bShared>
|
<bShared>0</bShared>
|
||||||
</File>
|
</File>
|
||||||
@@ -562,8 +587,8 @@
|
|||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
<bDave2>0</bDave2>
|
<bDave2>0</bDave2>
|
||||||
<PathWithFileName>../Middlewares/Third_Party/FreeRTOS/Source/queue.c</PathWithFileName>
|
<PathWithFileName>..\Drivers\CH390\CH390_Interface.c</PathWithFileName>
|
||||||
<FilenameWithoutPath>queue.c</FilenameWithoutPath>
|
<FilenameWithoutPath>CH390_Interface.c</FilenameWithoutPath>
|
||||||
<RteFlg>0</RteFlg>
|
<RteFlg>0</RteFlg>
|
||||||
<bShared>0</bShared>
|
<bShared>0</bShared>
|
||||||
</File>
|
</File>
|
||||||
@@ -574,68 +599,400 @@
|
|||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
<bDave2>0</bDave2>
|
<bDave2>0</bDave2>
|
||||||
<PathWithFileName>../Middlewares/Third_Party/FreeRTOS/Source/stream_buffer.c</PathWithFileName>
|
<PathWithFileName>..\Drivers\CH390\ch390_runtime.c</PathWithFileName>
|
||||||
<FilenameWithoutPath>stream_buffer.c</FilenameWithoutPath>
|
<FilenameWithoutPath>ch390_runtime.c</FilenameWithoutPath>
|
||||||
<RteFlg>0</RteFlg>
|
<RteFlg>0</RteFlg>
|
||||||
<bShared>0</bShared>
|
<bShared>0</bShared>
|
||||||
</File>
|
</File>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Group>
|
||||||
|
<GroupName>Drivers/LwIP/core</GroupName>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<cbSel>0</cbSel>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
<File>
|
<File>
|
||||||
<GroupNumber>5</GroupNumber>
|
<GroupNumber>6</GroupNumber>
|
||||||
<FileNumber>31</FileNumber>
|
<FileNumber>31</FileNumber>
|
||||||
<FileType>1</FileType>
|
<FileType>1</FileType>
|
||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
<bDave2>0</bDave2>
|
<bDave2>0</bDave2>
|
||||||
<PathWithFileName>../Middlewares/Third_Party/FreeRTOS/Source/tasks.c</PathWithFileName>
|
<PathWithFileName>..\Drivers\LwIP\src\core\def.c</PathWithFileName>
|
||||||
<FilenameWithoutPath>tasks.c</FilenameWithoutPath>
|
<FilenameWithoutPath>def.c</FilenameWithoutPath>
|
||||||
<RteFlg>0</RteFlg>
|
<RteFlg>0</RteFlg>
|
||||||
<bShared>0</bShared>
|
<bShared>0</bShared>
|
||||||
</File>
|
</File>
|
||||||
<File>
|
<File>
|
||||||
<GroupNumber>5</GroupNumber>
|
<GroupNumber>6</GroupNumber>
|
||||||
<FileNumber>32</FileNumber>
|
<FileNumber>32</FileNumber>
|
||||||
<FileType>1</FileType>
|
<FileType>1</FileType>
|
||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
<bDave2>0</bDave2>
|
<bDave2>0</bDave2>
|
||||||
<PathWithFileName>../Middlewares/Third_Party/FreeRTOS/Source/timers.c</PathWithFileName>
|
<PathWithFileName>..\Drivers\LwIP\src\core\inet_chksum.c</PathWithFileName>
|
||||||
<FilenameWithoutPath>timers.c</FilenameWithoutPath>
|
<FilenameWithoutPath>inet_chksum.c</FilenameWithoutPath>
|
||||||
<RteFlg>0</RteFlg>
|
<RteFlg>0</RteFlg>
|
||||||
<bShared>0</bShared>
|
<bShared>0</bShared>
|
||||||
</File>
|
</File>
|
||||||
<File>
|
<File>
|
||||||
<GroupNumber>5</GroupNumber>
|
<GroupNumber>6</GroupNumber>
|
||||||
<FileNumber>33</FileNumber>
|
<FileNumber>33</FileNumber>
|
||||||
<FileType>1</FileType>
|
<FileType>1</FileType>
|
||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
<bDave2>0</bDave2>
|
<bDave2>0</bDave2>
|
||||||
<PathWithFileName>../Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os2.c</PathWithFileName>
|
<PathWithFileName>..\Drivers\LwIP\src\core\init.c</PathWithFileName>
|
||||||
<FilenameWithoutPath>cmsis_os2.c</FilenameWithoutPath>
|
<FilenameWithoutPath>init.c</FilenameWithoutPath>
|
||||||
<RteFlg>0</RteFlg>
|
<RteFlg>0</RteFlg>
|
||||||
<bShared>0</bShared>
|
<bShared>0</bShared>
|
||||||
</File>
|
</File>
|
||||||
<File>
|
<File>
|
||||||
<GroupNumber>5</GroupNumber>
|
<GroupNumber>6</GroupNumber>
|
||||||
<FileNumber>34</FileNumber>
|
<FileNumber>34</FileNumber>
|
||||||
<FileType>1</FileType>
|
<FileType>1</FileType>
|
||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
<bDave2>0</bDave2>
|
<bDave2>0</bDave2>
|
||||||
<PathWithFileName>../Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c</PathWithFileName>
|
<PathWithFileName>..\Drivers\LwIP\src\core\ip.c</PathWithFileName>
|
||||||
<FilenameWithoutPath>heap_4.c</FilenameWithoutPath>
|
<FilenameWithoutPath>ip.c</FilenameWithoutPath>
|
||||||
<RteFlg>0</RteFlg>
|
<RteFlg>0</RteFlg>
|
||||||
<bShared>0</bShared>
|
<bShared>0</bShared>
|
||||||
</File>
|
</File>
|
||||||
<File>
|
<File>
|
||||||
<GroupNumber>5</GroupNumber>
|
<GroupNumber>6</GroupNumber>
|
||||||
<FileNumber>35</FileNumber>
|
<FileNumber>35</FileNumber>
|
||||||
<FileType>1</FileType>
|
<FileType>1</FileType>
|
||||||
<tvExp>0</tvExp>
|
<tvExp>0</tvExp>
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
<bDave2>0</bDave2>
|
<bDave2>0</bDave2>
|
||||||
<PathWithFileName>../Middlewares/Third_Party/FreeRTOS/Source/portable/RVDS/ARM_CM3/port.c</PathWithFileName>
|
<PathWithFileName>..\Drivers\LwIP\src\core\mem.c</PathWithFileName>
|
||||||
<FilenameWithoutPath>port.c</FilenameWithoutPath>
|
<FilenameWithoutPath>mem.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>6</GroupNumber>
|
||||||
|
<FileNumber>36</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\Drivers\LwIP\src\core\memp.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>memp.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>6</GroupNumber>
|
||||||
|
<FileNumber>37</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\Drivers\LwIP\src\core\netif.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>netif.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>6</GroupNumber>
|
||||||
|
<FileNumber>38</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\Drivers\LwIP\src\core\pbuf.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>pbuf.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>6</GroupNumber>
|
||||||
|
<FileNumber>39</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\Drivers\LwIP\src\core\raw.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>raw.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>6</GroupNumber>
|
||||||
|
<FileNumber>40</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\Drivers\LwIP\src\core\stats.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>stats.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>6</GroupNumber>
|
||||||
|
<FileNumber>41</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\Drivers\LwIP\src\core\sys.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>sys.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>6</GroupNumber>
|
||||||
|
<FileNumber>42</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\Drivers\LwIP\src\core\tcp.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>tcp.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>6</GroupNumber>
|
||||||
|
<FileNumber>43</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\Drivers\LwIP\src\core\tcp_in.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>tcp_in.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>6</GroupNumber>
|
||||||
|
<FileNumber>44</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\Drivers\LwIP\src\core\tcp_out.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>tcp_out.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>6</GroupNumber>
|
||||||
|
<FileNumber>45</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\Drivers\LwIP\src\core\timeouts.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>timeouts.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>6</GroupNumber>
|
||||||
|
<FileNumber>46</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\Drivers\LwIP\src\core\udp.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>udp.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>6</GroupNumber>
|
||||||
|
<FileNumber>47</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\Drivers\LwIP\src\core\ipv4\etharp.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>etharp.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>6</GroupNumber>
|
||||||
|
<FileNumber>48</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\Drivers\LwIP\src\core\ipv4\icmp.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>icmp.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>6</GroupNumber>
|
||||||
|
<FileNumber>49</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\Drivers\LwIP\src\core\ipv4\ip4.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>ip4.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>6</GroupNumber>
|
||||||
|
<FileNumber>50</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\Drivers\LwIP\src\core\ipv4\ip4_addr.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>ip4_addr.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>6</GroupNumber>
|
||||||
|
<FileNumber>51</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\Drivers\LwIP\src\core\ipv4\ip4_frag.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>ip4_frag.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Group>
|
||||||
|
<GroupName>Drivers/LwIP/netif</GroupName>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<cbSel>0</cbSel>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>7</GroupNumber>
|
||||||
|
<FileNumber>52</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\Drivers\LwIP\src\netif\ethernet.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>ethernet.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>7</GroupNumber>
|
||||||
|
<FileNumber>53</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\Drivers\LwIP\src\netif\ethernetif.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>ethernetif.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Group>
|
||||||
|
<GroupName>APP</GroupName>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<cbSel>0</cbSel>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>8</GroupNumber>
|
||||||
|
<FileNumber>54</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\App\config.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>config.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>8</GroupNumber>
|
||||||
|
<FileNumber>55</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\App\flash_param.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>flash_param.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>8</GroupNumber>
|
||||||
|
<FileNumber>56</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\App\tcp_client.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>tcp_client.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>8</GroupNumber>
|
||||||
|
<FileNumber>57</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\App\tcp_server.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>tcp_server.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>8</GroupNumber>
|
||||||
|
<FileNumber>58</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\App\uart_trans.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>uart_trans.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Group>
|
||||||
|
<GroupName>Middlewares/SEGGER_RTT</GroupName>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<cbSel>0</cbSel>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>9</GroupNumber>
|
||||||
|
<FileNumber>59</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\Middlewares\Third_Party\SEGGER_RTT\SEGGER_RTT.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>SEGGER_RTT.c</FilenameWithoutPath>
|
||||||
|
<RteFlg>0</RteFlg>
|
||||||
|
<bShared>0</bShared>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<GroupNumber>9</GroupNumber>
|
||||||
|
<FileNumber>60</FileNumber>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<tvExp>0</tvExp>
|
||||||
|
<tvExpOptDlg>0</tvExpOptDlg>
|
||||||
|
<bDave2>0</bDave2>
|
||||||
|
<PathWithFileName>..\Middlewares\Third_Party\SEGGER_RTT\SEGGER_RTT_printf.c</PathWithFileName>
|
||||||
|
<FilenameWithoutPath>SEGGER_RTT_printf.c</FilenameWithoutPath>
|
||||||
<RteFlg>0</RteFlg>
|
<RteFlg>0</RteFlg>
|
||||||
<bShared>0</bShared>
|
<bShared>0</bShared>
|
||||||
</File>
|
</File>
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
<CreateHexFile>1</CreateHexFile>
|
<CreateHexFile>1</CreateHexFile>
|
||||||
<DebugInformation>1</DebugInformation>
|
<DebugInformation>1</DebugInformation>
|
||||||
<BrowseInformation>1</BrowseInformation>
|
<BrowseInformation>1</BrowseInformation>
|
||||||
<ListingPath>TCP2UART\</ListingPath>
|
<ListingPath></ListingPath>
|
||||||
<HexFormatSelection>1</HexFormatSelection>
|
<HexFormatSelection>1</HexFormatSelection>
|
||||||
<Merge32K>0</Merge32K>
|
<Merge32K>0</Merge32K>
|
||||||
<CreateBatchFile>0</CreateBatchFile>
|
<CreateBatchFile>0</CreateBatchFile>
|
||||||
@@ -80,9 +80,9 @@
|
|||||||
<nStopB2X>0</nStopB2X>
|
<nStopB2X>0</nStopB2X>
|
||||||
</BeforeMake>
|
</BeforeMake>
|
||||||
<AfterMake>
|
<AfterMake>
|
||||||
<RunUserProg1>1</RunUserProg1>
|
<RunUserProg1>0</RunUserProg1>
|
||||||
<RunUserProg2>0</RunUserProg2>
|
<RunUserProg2>0</RunUserProg2>
|
||||||
<UserProg1Name>keil-build-viewer.exe -NOPATH</UserProg1Name>
|
<UserProg1Name></UserProg1Name>
|
||||||
<UserProg2Name></UserProg2Name>
|
<UserProg2Name></UserProg2Name>
|
||||||
<UserProg1Dos16Mode>0</UserProg1Dos16Mode>
|
<UserProg1Dos16Mode>0</UserProg1Dos16Mode>
|
||||||
<UserProg2Dos16Mode>0</UserProg2Dos16Mode>
|
<UserProg2Dos16Mode>0</UserProg2Dos16Mode>
|
||||||
@@ -337,7 +337,7 @@
|
|||||||
<v6WtE>0</v6WtE>
|
<v6WtE>0</v6WtE>
|
||||||
<v6Rtti>0</v6Rtti>
|
<v6Rtti>0</v6Rtti>
|
||||||
<VariousControls>
|
<VariousControls>
|
||||||
<MiscControls></MiscControls>
|
<MiscControls>--diag_suppress=111,128</MiscControls>
|
||||||
<Define>USE_HAL_DRIVER,STM32F103xB</Define>
|
<Define>USE_HAL_DRIVER,STM32F103xB</Define>
|
||||||
<Undefine></Undefine>
|
<Undefine></Undefine>
|
||||||
<IncludePath>../Core/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy;../Drivers/CMSIS/Device/ST/STM32F1xx/Include;../Drivers/CMSIS/Include;../Drivers/CH390;../Drivers/LwIP/src/include;../Drivers/LwIP/src/include/lwip;../Drivers/LwIP/src/include/netif;../Drivers/LwIP/src/include/arch;../Drivers/LwIP/src/netif;../App;../Middlewares/Third_Party/SEGGER_RTT</IncludePath>
|
<IncludePath>../Core/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy;../Drivers/CMSIS/Device/ST/STM32F1xx/Include;../Drivers/CMSIS/Include;../Drivers/CH390;../Drivers/LwIP/src/include;../Drivers/LwIP/src/include/lwip;../Drivers/LwIP/src/include/netif;../Drivers/LwIP/src/include/arch;../Drivers/LwIP/src/netif;../App;../Middlewares/Third_Party/SEGGER_RTT</IncludePath>
|
||||||
@@ -549,6 +549,11 @@
|
|||||||
<FileType>1</FileType>
|
<FileType>1</FileType>
|
||||||
<FilePath>..\Drivers\CH390\CH390_Interface.c</FilePath>
|
<FilePath>..\Drivers\CH390\CH390_Interface.c</FilePath>
|
||||||
</File>
|
</File>
|
||||||
|
<File>
|
||||||
|
<FileName>ch390_runtime.c</FileName>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<FilePath>..\Drivers\CH390\ch390_runtime.c</FilePath>
|
||||||
|
</File>
|
||||||
</Files>
|
</Files>
|
||||||
</Group>
|
</Group>
|
||||||
<Group>
|
<Group>
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
Stack_Size EQU 0x400
|
Stack_Size EQU 0x400
|
||||||
|
|
||||||
AREA STACK, NOINIT, READWRITE, ALIGN=3
|
AREA STACK, NOINIT, READWRITE, ALIGN=3
|
||||||
|
EXPORT Stack_Mem
|
||||||
Stack_Mem SPACE Stack_Size
|
Stack_Mem SPACE Stack_Size
|
||||||
__initial_sp
|
__initial_sp
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef BUFFER_SIZE_UP
|
#ifndef BUFFER_SIZE_UP
|
||||||
#define BUFFER_SIZE_UP 1024
|
#define BUFFER_SIZE_UP 256
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef BUFFER_SIZE_DOWN
|
#ifndef BUFFER_SIZE_DOWN
|
||||||
|
|||||||
@@ -102,14 +102,14 @@ SECTIONS
|
|||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
} >FLASH
|
} >FLASH
|
||||||
|
|
||||||
.ARM.extab (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
|
.ARM.extab :
|
||||||
{
|
{
|
||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
} >FLASH
|
} >FLASH
|
||||||
|
|
||||||
.ARM (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
|
.ARM :
|
||||||
{
|
{
|
||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
__exidx_start = .;
|
__exidx_start = .;
|
||||||
@@ -118,7 +118,7 @@ SECTIONS
|
|||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
} >FLASH
|
} >FLASH
|
||||||
|
|
||||||
.preinit_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
|
.preinit_array :
|
||||||
{
|
{
|
||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||||
@@ -127,7 +127,7 @@ SECTIONS
|
|||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
} >FLASH
|
} >FLASH
|
||||||
|
|
||||||
.init_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
|
.init_array :
|
||||||
{
|
{
|
||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
PROVIDE_HIDDEN (__init_array_start = .);
|
PROVIDE_HIDDEN (__init_array_start = .);
|
||||||
@@ -137,7 +137,7 @@ SECTIONS
|
|||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
} >FLASH
|
} >FLASH
|
||||||
|
|
||||||
.fini_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
|
.fini_array :
|
||||||
{
|
{
|
||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||||
|
|||||||
@@ -11,10 +11,16 @@ set(MX_Defines_Syms
|
|||||||
# STM32CubeMX generated include paths
|
# STM32CubeMX generated include paths
|
||||||
set(MX_Include_Dirs
|
set(MX_Include_Dirs
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Inc
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Inc
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../App
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/CH390
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/include
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/include/arch
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/netif
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F1xx_HAL_Driver/Inc
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F1xx_HAL_Driver/Inc
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/CMSIS/Device/ST/STM32F1xx/Include
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/CMSIS/Device/ST/STM32F1xx/Include
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/CMSIS/Include
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/CMSIS/Include
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/Third_Party/SEGGER_RTT
|
||||||
)
|
)
|
||||||
|
|
||||||
# STM32CubeMX generated application sources
|
# STM32CubeMX generated application sources
|
||||||
@@ -30,6 +36,37 @@ set(MX_Application_Src
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/stm32f1xx_hal_msp.c
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/stm32f1xx_hal_msp.c
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/sysmem.c
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/sysmem.c
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/syscalls.c
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Core/Src/syscalls.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../App/config.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../App/flash_param.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../App/tcp_client.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../App/tcp_server.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../App/uart_trans.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/CH390/CH390.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/CH390/CH390_Interface.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/CH390/ch390_runtime.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/core/def.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/core/inet_chksum.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/core/init.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/core/ip.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/core/mem.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/core/memp.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/core/netif.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/core/pbuf.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/core/raw.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/core/stats.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/core/sys.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/core/tcp.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/core/tcp_in.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/core/tcp_out.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/core/timeouts.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/core/ipv4/etharp.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/core/ipv4/icmp.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/core/ipv4/ip4.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/core/ipv4/ip4_addr.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/netif/ethernet.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Drivers/LwIP/src/netif/ethernetif.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/Third_Party/SEGGER_RTT/SEGGER_RTT.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../Middlewares/Third_Party/SEGGER_RTT/SEGGER_RTT_printf.c
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../startup_stm32f103xb.s
|
${CMAKE_CURRENT_SOURCE_DIR}/../../startup_stm32f103xb.s
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
param(
|
||||||
|
[string]$BindHost = "0.0.0.0",
|
||||||
|
[int]$Port = 8081,
|
||||||
|
[switch]$Echo,
|
||||||
|
[switch]$NoStdin
|
||||||
|
)
|
||||||
|
|
||||||
|
$listeners = Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction SilentlyContinue
|
||||||
|
if ($listeners) {
|
||||||
|
$pids = $listeners | Select-Object -ExpandProperty OwningProcess -Unique
|
||||||
|
Write-Host "Stopping existing listeners on TCP ${Port}: $($pids -join ', ')"
|
||||||
|
foreach ($procId in $pids) {
|
||||||
|
try {
|
||||||
|
Stop-Process -Id $procId -Force -ErrorAction Stop
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Warning "Failed to stop process $procId : $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Start-Sleep -Milliseconds 300
|
||||||
|
}
|
||||||
|
|
||||||
|
$args = @("tools/tcp_debug_server.py", "--host", $BindHost, "--port", "$Port")
|
||||||
|
if ($Echo) {
|
||||||
|
$args += "--echo"
|
||||||
|
}
|
||||||
|
if ($NoStdin) {
|
||||||
|
$args += "--no-stdin"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Starting TCP debug server on ${BindHost}:${Port}"
|
||||||
|
python @args
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Simple TCP debug server for validating raw payload forwarding.
|
||||||
|
|
||||||
|
Usage examples:
|
||||||
|
python tools/tcp_debug_server.py --host 0.0.0.0 --port 8081
|
||||||
|
python tools/tcp_debug_server.py --port 8081 --echo
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Listen as a plain TCP server
|
||||||
|
- Print connect/disconnect events
|
||||||
|
- Print received payload as text and hex
|
||||||
|
- Optional echo mode
|
||||||
|
- Optional stdin -> socket sender for manual testing
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import select
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import threading
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
def ts() -> str:
|
||||||
|
return datetime.now().strftime("%H:%M:%S.%f")[:-3]
|
||||||
|
|
||||||
|
|
||||||
|
def hex_bytes(data: bytes) -> str:
|
||||||
|
return " ".join(f"{b:02X}" for b in data)
|
||||||
|
|
||||||
|
|
||||||
|
def text_view(data: bytes) -> str:
|
||||||
|
return "".join(chr(b) if 32 <= b < 127 else "." for b in data)
|
||||||
|
|
||||||
|
|
||||||
|
def sender_loop(conn: socket.socket, stop_event: threading.Event) -> None:
|
||||||
|
print(f"[{ts()}] stdin sender ready. Type text and press Enter to send.")
|
||||||
|
while not stop_event.is_set():
|
||||||
|
line = sys.stdin.readline()
|
||||||
|
if line == "":
|
||||||
|
stop_event.set()
|
||||||
|
break
|
||||||
|
payload = line.encode("utf-8", errors="replace")
|
||||||
|
try:
|
||||||
|
conn.sendall(payload)
|
||||||
|
print(
|
||||||
|
f"[{ts()}] TX {len(payload)} bytes | text={payload!r} | hex={hex_bytes(payload)}"
|
||||||
|
)
|
||||||
|
except OSError as exc:
|
||||||
|
print(f"[{ts()}] TX failed: {exc}")
|
||||||
|
stop_event.set()
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def serve(host: str, port: int, echo: bool, no_stdin: bool) -> int:
|
||||||
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
|
||||||
|
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
server.bind((host, port))
|
||||||
|
server.listen(1)
|
||||||
|
print(f"[{ts()}] Listening on {host}:{port}")
|
||||||
|
|
||||||
|
conn, addr = server.accept()
|
||||||
|
with conn:
|
||||||
|
print(f"[{ts()}] Client connected from {addr[0]}:{addr[1]}")
|
||||||
|
stop_event = threading.Event()
|
||||||
|
sender_thread = None
|
||||||
|
|
||||||
|
if not no_stdin:
|
||||||
|
sender_thread = threading.Thread(
|
||||||
|
target=sender_loop, args=(conn, stop_event), daemon=True
|
||||||
|
)
|
||||||
|
sender_thread.start()
|
||||||
|
|
||||||
|
conn.setblocking(False)
|
||||||
|
|
||||||
|
while not stop_event.is_set():
|
||||||
|
ready, _, _ = select.select([conn], [], [], 0.2)
|
||||||
|
if not ready:
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = conn.recv(4096)
|
||||||
|
except BlockingIOError:
|
||||||
|
continue
|
||||||
|
except OSError as exc:
|
||||||
|
print(f"[{ts()}] RX failed: {exc}")
|
||||||
|
break
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
print(f"[{ts()}] Client disconnected")
|
||||||
|
break
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"[{ts()}] RX {len(data)} bytes | text={text_view(data)} | hex={hex_bytes(data)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if echo:
|
||||||
|
try:
|
||||||
|
conn.sendall(data)
|
||||||
|
print(f"[{ts()}] ECHO {len(data)} bytes")
|
||||||
|
except OSError as exc:
|
||||||
|
print(f"[{ts()}] Echo failed: {exc}")
|
||||||
|
break
|
||||||
|
|
||||||
|
stop_event.set()
|
||||||
|
if sender_thread is not None:
|
||||||
|
sender_thread.join(timeout=0.5)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args() -> argparse.Namespace:
|
||||||
|
parser = argparse.ArgumentParser(description="Minimal raw TCP debug server")
|
||||||
|
parser.add_argument(
|
||||||
|
"--host", default="0.0.0.0", help="Listen host, default: 0.0.0.0"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--port", type=int, default=8081, help="Listen port, default: 8081"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--echo", action="store_true", help="Echo received payload back to client"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-stdin",
|
||||||
|
action="store_true",
|
||||||
|
help="Disable stdin sender thread (receive-only mode)",
|
||||||
|
)
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
args = parse_args()
|
||||||
|
try:
|
||||||
|
return serve(args.host, args.port, args.echo, args.no_stdin)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print(f"\n[{ts()}] Stopped by user")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -0,0 +1,586 @@
|
|||||||
|
# UART CH390 Debug Handoff
|
||||||
|
|
||||||
|
## 2026-03-31 Config UART Test Session
|
||||||
|
|
||||||
|
### Goal
|
||||||
|
|
||||||
|
- Exhaustively test the `USART1` config command interface.
|
||||||
|
- Verify that Flash-backed parameters survive `AT+SAVE` plus reset.
|
||||||
|
- Record concrete bench procedure, failures, fixes, and evidence paths.
|
||||||
|
|
||||||
|
### Bench Baseline
|
||||||
|
|
||||||
|
- Workspace: `D:\code\STM32Project\TCP2UART`
|
||||||
|
- Target MCU: `STM32F103R8T6`
|
||||||
|
- Config UART: `USART1` on `PA9/PA10`
|
||||||
|
- Host visible COM ports during this session: `COM1`, `COM9`
|
||||||
|
- Debug probe visible during this session: `STLink V2`
|
||||||
|
- Flash parameter page: `0x0800FC00`
|
||||||
|
- Firmware image used for test bring-up: `MDK-ARM\TCP2UART\TCP2UART.axf`
|
||||||
|
|
||||||
|
### Source-of-Truth Command Surface
|
||||||
|
|
||||||
|
From `App/config.c`, the tested command surface is:
|
||||||
|
|
||||||
|
- `AT`
|
||||||
|
- `AT+?`
|
||||||
|
- `AT+QUERY`
|
||||||
|
- `AT+SAVE`
|
||||||
|
- `AT+RESET`
|
||||||
|
- `AT+DEFAULT`
|
||||||
|
- `AT+IP=...`
|
||||||
|
- `AT+MASK=...`
|
||||||
|
- `AT+GW=...`
|
||||||
|
- `AT+RIP=...`
|
||||||
|
- `AT+MAC=...`
|
||||||
|
- `AT+PORT=...`
|
||||||
|
- `AT+RPORT=...`
|
||||||
|
- `AT+BAUD1=...`
|
||||||
|
- `AT+BAUD2=...`
|
||||||
|
- `AT+DHCP=0/1`
|
||||||
|
|
||||||
|
### Test Procedure
|
||||||
|
|
||||||
|
1. Flash the current `axf` image with `probe-rs download --chip STM32F103R8`.
|
||||||
|
2. Connect to the config UART at `115200 8N1`.
|
||||||
|
3. Run `python tools/uart_config_test.py --port COM9 --scenario inventory`.
|
||||||
|
4. Run `python tools/uart_config_test.py --port COM9 --scenario persistence`.
|
||||||
|
5. Read back Flash words at `0x0800FC00` and compare them before and after reset.
|
||||||
|
6. Save all transcripts into `artifacts/uart-config/`.
|
||||||
|
|
||||||
|
### Important Expectations
|
||||||
|
|
||||||
|
- Setter commands update RAM immediately and return `OK` plus the reboot hint.
|
||||||
|
- Persistence is not proven until the sequence `set -> query -> save -> reset -> query` passes.
|
||||||
|
- `AT+DEFAULT` only resets RAM state and still requires `AT+SAVE` to persist.
|
||||||
|
- `AT+DHCP=1` must fail in this build by design.
|
||||||
|
|
||||||
|
### Session Findings
|
||||||
|
|
||||||
|
- `probe-rs download` succeeded against `STM32F103R8`.
|
||||||
|
- `pyserial` had to be installed on the host before scripted UART testing.
|
||||||
|
- The requested handoff filename did not exist in the repo, so this file was created as the dedicated config-UART handoff log.
|
||||||
|
- Raw command transcripts and Flash comparisons should be attached from `artifacts/uart-config/` for any future regression analysis.
|
||||||
|
|
||||||
|
### 2026-03-31 Live Test Evidence
|
||||||
|
|
||||||
|
- Host-side strict inventory run on `COM9` failed with `non_empty_responses = 0`.
|
||||||
|
- Artifact: `artifacts/uart-config/inventory-20260331-185333.json`
|
||||||
|
- Direct host probe on `COM9` with `AT\r\n` returned `b''`.
|
||||||
|
- Direct host probe on `COM1` with `AT\r\n` also returned `b''`.
|
||||||
|
- Strict persistence run was intentionally not accepted as valid after the script was corrected, because all command responses were empty.
|
||||||
|
- Flash page `0x0800FC00` remained all `0xFFFFFFFF`, proving `AT+SAVE` never actually executed on target.
|
||||||
|
|
||||||
|
### Board/Debugger Findings That Narrow The Fault
|
||||||
|
|
||||||
|
- The target firmware is present in Flash and `probe-rs download` completes successfully.
|
||||||
|
- After a clean reset and short run window, the MCU executes from Flash and initializes clocks and `USART1` registers.
|
||||||
|
- `USART1` register block was observed in an initialized state after boot, not all-zero.
|
||||||
|
- Firmware was patched to explicitly start `HAL_UART_Receive_IT(&huart1, &g_uart1_rx_probe_byte, 1)` during `App_Init()`.
|
||||||
|
- Even after that fix, repeated `AT` frames from the host produced no visible UART response.
|
||||||
|
- Debug readout of software-side config RX state showed:
|
||||||
|
- `g_pending_cmd_ready = 0`
|
||||||
|
- `g_pending_cmd_len = 0`
|
||||||
|
- `g_uart_cmd_len = 0`
|
||||||
|
- `g_uart_cmd_buffer` remained zero-filled
|
||||||
|
- `g_pending_cmd_buffer` remained zero-filled
|
||||||
|
- This means the config parser never received any bytes from the live UART path during the test window.
|
||||||
|
|
||||||
|
### Current Best Conclusion
|
||||||
|
|
||||||
|
- The immediate blocker is no longer the parser itself.
|
||||||
|
- Current evidence points to a board-level `USART1_RX` path problem or wrong host wiring/port assumption, because the firmware is alive, `USART1` is initialized, but no command bytes enter `config_uart_rx_byte()`.
|
||||||
|
- Until the physical config-UART path is proven, it is not meaningful to claim that every config command or Flash persistence path has passed on real hardware.
|
||||||
|
|
||||||
|
### Corrected Final Conclusion
|
||||||
|
|
||||||
|
- The earlier "no response" conclusion was wrong because the host was sending `\r\n` terminated commands.
|
||||||
|
- This firmware expects the config command to be terminated by `\n` to complete the frame in the real bench setup.
|
||||||
|
- After switching the host sender to `AT...\n`, `COM9` immediately returned `OK\r\n` for `AT` and the complete config command set became testable.
|
||||||
|
- Therefore the key bench rule is: every config command must end with `\n`.
|
||||||
|
|
||||||
|
### Final Verified Results
|
||||||
|
|
||||||
|
- `AT` returns `OK`.
|
||||||
|
- `AT+?` and `AT+QUERY` return the full current configuration snapshot.
|
||||||
|
- Setter commands `AT+IP`, `AT+MASK`, `AT+GW`, `AT+RIP`, `AT+MAC`, `AT+PORT`, `AT+RPORT`, `AT+BAUD1`, `AT+BAUD2`, `AT+DHCP=0` all return `OK` plus the reboot hint.
|
||||||
|
- `AT+SAVE` returns `OK: Configuration saved`.
|
||||||
|
- `AT+RESET` returns `OK: Resetting...` and the board comes back responding to `AT`.
|
||||||
|
- `AT+DEFAULT` returns `OK: Defaults restored`.
|
||||||
|
- Negative cases were verified:
|
||||||
|
- `AT+UNKNOWN` -> `ERROR: Unknown command`
|
||||||
|
- `AT+PORT=0` / `AT+PORT=65536` -> `ERROR: Invalid port`
|
||||||
|
- `AT+BAUD1=1199` / `AT+BAUD1=921601` -> `ERROR: Invalid baudrate`
|
||||||
|
- `AT+DHCP=1` -> `ERROR: DHCP disabled in this build`
|
||||||
|
- `AT+IP=999.1.1.1` -> `ERROR: Invalid IP format`
|
||||||
|
- `AT+MAC=GG:11:22:33:44:55` -> `ERROR: Invalid MAC format`
|
||||||
|
- Non-AT input `BT` produced no response, which matches the parser gate.
|
||||||
|
|
||||||
|
### Final Flash Persistence Evidence
|
||||||
|
|
||||||
|
- Tested persisted values:
|
||||||
|
- `IP=192.168.1.123`
|
||||||
|
- `MASK=255.255.255.0`
|
||||||
|
- `GW=192.168.1.1`
|
||||||
|
- `RIP=192.168.1.201`
|
||||||
|
- `MAC=02:12:34:56:78:9A`
|
||||||
|
- `PORT=10001`
|
||||||
|
- `RPORT=10002`
|
||||||
|
- `BAUD1=57600`
|
||||||
|
- `BAUD2=38400`
|
||||||
|
- Sequence used:
|
||||||
|
1. set values with `\n`-terminated AT commands
|
||||||
|
2. query with `AT+?`
|
||||||
|
3. `AT+SAVE`
|
||||||
|
4. `AT+RESET`
|
||||||
|
5. query again with `AT+?`
|
||||||
|
6. read raw Flash words at `0x0800FC00`
|
||||||
|
- Query values before and after reset matched exactly.
|
||||||
|
- Raw Flash read before and after reset also matched exactly.
|
||||||
|
- Factory default restoration was also proven with `AT+DEFAULT -> AT+SAVE -> AT+RESET -> AT+?`.
|
||||||
|
|
||||||
|
### Evidence Files
|
||||||
|
|
||||||
|
- Inventory transcript: `artifacts/uart-config/inventory-20260331-185752.json`
|
||||||
|
- Inventory raw text: `artifacts/uart-config/inventory-20260331-185752.txt`
|
||||||
|
- Persistence transcript: `artifacts/uart-config/persistence-20260331-190039.json`
|
||||||
|
- Persistence raw text: `artifacts/uart-config/persistence-20260331-190039.txt`
|
||||||
|
|
||||||
|
### Firmware Adjustment Made During This Session
|
||||||
|
|
||||||
|
- Added explicit `HAL_UART_Receive_IT(&huart1, &g_uart1_rx_probe_byte, 1u)` arming in `App_Init()` so the `USART1` interrupt receive path is definitely started after boot.
|
||||||
|
- This is a safe, minimal bring-up fix and should remain in place.
|
||||||
|
|
||||||
|
### Practical Lessons
|
||||||
|
|
||||||
|
- Do not treat a script exit code alone as proof of UART success; require at least one non-empty response in the captured transcript.
|
||||||
|
- Do not treat `probe-rs read 0x0800FC00` returning all `0xFFFFFFFF` as a flash-driver failure until you first prove that `AT+SAVE` was actually accepted by the parser.
|
||||||
|
- In this project, the fastest truth test is:
|
||||||
|
1. prove target is executing
|
||||||
|
2. prove `USART1` is initialized
|
||||||
|
3. prove bytes reach `config_uart_rx_byte`
|
||||||
|
4. only then evaluate parser responses and flash persistence
|
||||||
|
- Most important bench lesson: if the config UART appears dead, first retry with commands ending in `\n` instead of `\r\n`.
|
||||||
|
|
||||||
|
### Open Items
|
||||||
|
|
||||||
|
- Confirm whether `COM9` is the real `USART1` config port by live command-response evidence.
|
||||||
|
- If command-response is unstable, inspect whether host wiring/USB-UART level shifting is the cause before changing parser logic.
|
||||||
|
- If persistence fails after a clean `AT+SAVE`, inspect `App/flash_param.c` and raw Flash contents at `0x0800FC00` before changing higher-level config logic.
|
||||||
|
|
||||||
|
## 2026-03-31 CH390D Bring-up Debug Session
|
||||||
|
|
||||||
|
### Goal
|
||||||
|
|
||||||
|
- Determine why `CH390D` does not return valid register values during boot.
|
||||||
|
- Find a software-side root cause if one exists and attempt a minimal fix.
|
||||||
|
|
||||||
|
### Baseline Symptom
|
||||||
|
|
||||||
|
- MCU boots normally and RTT works.
|
||||||
|
- CH390 boot diagnostics originally reported:
|
||||||
|
|
||||||
|
```text
|
||||||
|
TCP2UART boot
|
||||||
|
CH390 VID=0x0000 PID=0x0000 REV=0x00 NSR=0x00 LINK=0
|
||||||
|
CH390 NCR=0x00 RCR=0x00 IMR=0x00 INTCR=0x00 GPR=0x00 ISR=0x00
|
||||||
|
CH390 WRCHK NCR:0x00->0x00 INTCR:0x00->0x00
|
||||||
|
```
|
||||||
|
|
||||||
|
- This showed that CH390 register reads and write-back checks were not producing valid values.
|
||||||
|
|
||||||
|
### Board-Side Evidence Already Collected
|
||||||
|
|
||||||
|
- `RST` line was observed released high.
|
||||||
|
- `CS` line idle state was high.
|
||||||
|
- `INT` line was observed low and mapped to EXTI.
|
||||||
|
- `SPI1` was enabled and configured for `Mode 3` in the active firmware.
|
||||||
|
- These observations did not by themselves restore valid CH390 responses.
|
||||||
|
|
||||||
|
### What Was Tried
|
||||||
|
|
||||||
|
1. Added richer RTT startup diagnostics in `BootDiag_ReportCh390()`.
|
||||||
|
2. Lowered `SPI1` speed from `/8` to `/64`.
|
||||||
|
3. Added stage markers around `low_level_init()` to localize the hang.
|
||||||
|
4. Step-debugged and breakpoint-debugged `ch390_default_config()` and `ch390_write_phy()`.
|
||||||
|
5. Added timeout protection to `ch390_read_phy()` / `ch390_write_phy()` so `EPCR` polling cannot hang forever.
|
||||||
|
6. Temporarily skipped `ch390_set_phy_mode(CH390_AUTO)` to isolate non-PHY register access.
|
||||||
|
7. Compared current driver against `Reference/EVT/EXAM/PUB/CH390.c` and `Reference/EVT/EXAM/PUB/CH390_Interface.c`.
|
||||||
|
8. Tried multiple SPI register transaction shapes:
|
||||||
|
- original two-byte exchange style
|
||||||
|
- split `Transmit` then read phase
|
||||||
|
- explicit dummy-byte read phase
|
||||||
|
- single-frame two-byte full-duplex read
|
||||||
|
9. Scanned all four SPI modes (`mode0`..`mode3`) during startup.
|
||||||
|
10. Added small `CS` setup/hold delays.
|
||||||
|
11. Increased hardware reset release wait to `50ms`.
|
||||||
|
12. Restored EVT-style init order and PHY setup path to see whether EVT sequence alone fixes the problem.
|
||||||
|
|
||||||
|
### Key Intermediate Findings
|
||||||
|
|
||||||
|
- Lowering SPI speed changed behavior, but did not recover valid CH390 IDs.
|
||||||
|
- Stage markers showed that low-speed SPI could stall during `ETH init: default`.
|
||||||
|
- Step/RTT evidence localized the original stall to PHY access during `ch390_default_config()`.
|
||||||
|
- The PHY access loop in `ch390_read_phy()` / `ch390_write_phy()` had no timeout and could hang indefinitely. This is a real software bug and should stay fixed.
|
||||||
|
- After adding PHY timeouts and temporarily skipping PHY setup, the init path completed, but all CH390 reads became `0xFF` rather than valid IDs.
|
||||||
|
- SPI mode scan result under that condition was:
|
||||||
|
|
||||||
|
```text
|
||||||
|
CH390 SPI mode0 [FF FF FF FF FF]
|
||||||
|
CH390 SPI mode1 [FF FF FF FF FF]
|
||||||
|
CH390 SPI mode2 [FF FF FF FF FF]
|
||||||
|
CH390 SPI mode3 [FF FF FF FF FF]
|
||||||
|
```
|
||||||
|
|
||||||
|
- This ruled out a simple `CPOL/CPHA` mismatch.
|
||||||
|
- External code comparison did not reveal an `opcode` or register-address mismatch. Public CH390 implementations use the same `OPC_REG_R=0x00`, `OPC_REG_W=0x80`, and the same register map.
|
||||||
|
- One experimental split transaction path produced repeatable but obviously bogus values like `0x03`, `0xAC`, `0xAE`, which strongly suggests transaction artifacts rather than real CH390 data.
|
||||||
|
- A debug read of `SPI1->SR` showed `OVR=1` during one of the experimental transaction variants, indicating the SPI transaction layer was not trustworthy in that configuration.
|
||||||
|
|
||||||
|
### EVT Comparison Outcome
|
||||||
|
|
||||||
|
- `Reference/EVT` is useful as a baseline, but it is not a drop-in fix for this project.
|
||||||
|
- The broad init order in the live project already matches EVT closely through the lwIP glue path.
|
||||||
|
- The most important EVT-specific difference is that EVT performs `ch390_set_phy_mode(CH390_AUTO)` at the start of `ch390_default_config()`.
|
||||||
|
- Restoring the EVT-style `PHY` setup path in this project caused boot to hang again at:
|
||||||
|
|
||||||
|
```text
|
||||||
|
TCP2UART boot
|
||||||
|
ETH init: gpio
|
||||||
|
ETH init: spi
|
||||||
|
ETH init: reset
|
||||||
|
ETH init: default
|
||||||
|
```
|
||||||
|
|
||||||
|
- That confirms the `PHY` path is a real trigger for the hang, but EVT order alone does not solve the underlying communication problem.
|
||||||
|
|
||||||
|
### Current Best Technical Conclusion
|
||||||
|
|
||||||
|
- A real software defect was found and fixed: `EPCR` polling in PHY access had no timeout.
|
||||||
|
- That fix prevents the firmware from hanging forever, but it does **not** restore valid CH390 register communication.
|
||||||
|
- The core unresolved problem remains: the SPI register-access path still does not yield believable CH390 register data.
|
||||||
|
- At this point, the following common explanations have already been tested and are **not** sufficient by themselves:
|
||||||
|
- SPI mode selection
|
||||||
|
- adding dummy bytes
|
||||||
|
- `CS` setup/hold delays
|
||||||
|
- changing reset wait from `10ms` to `50ms`
|
||||||
|
- reverting to EVT transaction style
|
||||||
|
- restoring EVT initialization order
|
||||||
|
- public `opcode` / register-map mismatch
|
||||||
|
|
||||||
|
### Recommended Next Debug Step
|
||||||
|
|
||||||
|
- The next high-value experiment is a temporary GPIO bit-bang read of `VIDL/VIDH/CHIPR` with a fully controlled continuous command+clock sequence.
|
||||||
|
- If bit-bang returns valid IDs while HAL-SPI paths do not, the remaining fault is in the SPI transaction implementation rather than CH390 higher-level init order.
|
||||||
|
- If bit-bang still returns invalid data, the investigation must move back to board-level bus behavior even if static continuity checks look correct.
|
||||||
|
|
||||||
|
### Additional 2026-03-31 Finding: HAL SPI Re-init And Bit-Bang Side Effects
|
||||||
|
|
||||||
|
- A valid concern was raised about calling `HAL_SPI_Init()` after temporarily changing SPI pins to GPIO mode.
|
||||||
|
- Code review of `stm32f1xx_hal_spi.c` showed that `HAL_SPI_MspInit()` only runs when `hspi->State == HAL_SPI_STATE_RESET`.
|
||||||
|
- Therefore, simply calling `HAL_SPI_Init()` after bit-bang mode does **not** automatically restore `PA5/PA7` to SPI alternate-function output mode.
|
||||||
|
- This was a real software-side risk in the temporary bit-bang probe and was corrected by explicitly restoring:
|
||||||
|
- `PA5` -> `AF_PP`
|
||||||
|
- `PA7` -> `AF_PP`
|
||||||
|
- `PA6` -> input
|
||||||
|
before calling `HAL_SPI_Init()` again.
|
||||||
|
- After that correction, the observed behavior changed again: boot output stopped at `ETH init: reset`, and a short halt showed execution inside `HAL_SPI_TransmitReceive()` called from the CH390 SPI exchange path.
|
||||||
|
- This means the earlier bit-bang experiments could have polluted later SPI results, but after the GPIO restore fix, the active blocker is again a live SPI transaction stall rather than a missing-GPIO-restore artifact.
|
||||||
|
|
||||||
|
### Additional 2026-03-31 Finding: Reset Exists In Runtime Path
|
||||||
|
|
||||||
|
- The project does **not** lack a CH390 reset process.
|
||||||
|
- The actual runtime order is:
|
||||||
|
1. `App_Init()`
|
||||||
|
2. `lwip_netif_init()`
|
||||||
|
3. `ethernetif_init()`
|
||||||
|
4. `low_level_init()`
|
||||||
|
5. `ch390_gpio_init()`
|
||||||
|
6. `ch390_spi_init()`
|
||||||
|
7. `ch390_hardware_reset()`
|
||||||
|
8. `ch390_default_config()`
|
||||||
|
- The reset process is therefore present and executed, but it lives in the lwIP/netif bring-up path instead of being written inline in `main.c` as in the EVT sample.
|
||||||
|
- The current unresolved problem is not "missing reset"; it is that SPI transactions after reset still do not produce valid CH390 register responses.
|
||||||
|
|
||||||
|
## 2026-03-31 Manual Reset Sensitivity Analysis
|
||||||
|
|
||||||
|
### Observed Symptom
|
||||||
|
|
||||||
|
- An extra `ch390_hardware_reset()` was temporarily inserted into `App_Init()` before `lwip_init()`.
|
||||||
|
- With that extra reset in place, a manual board reset could lead to the firmware appearing stuck and the LED heartbeat not behaving normally.
|
||||||
|
- The same image could still look more normal when observed after a `probe-rs` flash-and-run cycle.
|
||||||
|
|
||||||
|
### Code-Level Finding
|
||||||
|
|
||||||
|
- The inserted extra reset sat here in `Core/Src/main.c`:
|
||||||
|
|
||||||
|
```c
|
||||||
|
SEGGER_RTT_Init();
|
||||||
|
SEGGER_RTT_WriteString(0, "\r\nTCP2UART boot\r\n");
|
||||||
|
...
|
||||||
|
ch390_hardware_reset();
|
||||||
|
lwip_init();
|
||||||
|
lwip_netif_init(...);
|
||||||
|
```
|
||||||
|
|
||||||
|
- But the normal bring-up path already performs a reset later inside `ch390_runtime_init()` / `low_level_init()` before `ch390_default_config()`.
|
||||||
|
- That means the temporary line created a redundant early reset in a different initialization phase than the normal driver-owned reset.
|
||||||
|
|
||||||
|
### Interpretation
|
||||||
|
|
||||||
|
- This pattern is much more consistent with a reset-sequencing / startup-state issue than with compiler optimization level.
|
||||||
|
- The Keil target uses one fixed optimization configuration, so a plain manual reset does not change code generation.
|
||||||
|
- In contrast, an extra CH390 reset inserted before lwIP and before the normal CH390 runtime init can alter the device startup state and timing relationship between the MCU and CH390.
|
||||||
|
|
||||||
|
### Action Taken
|
||||||
|
|
||||||
|
- The extra `ch390_hardware_reset()` in `App_Init()` was removed.
|
||||||
|
- The firmware now relies only on the standard driver-owned reset inside the CH390 runtime initialization path.
|
||||||
|
|
||||||
|
### Conclusion
|
||||||
|
|
||||||
|
- The temporary extra reset was not kept.
|
||||||
|
- The strongest software-side conclusion is that the manual-reset sensitivity was caused by redundant reset sequencing rather than by optimization level.
|
||||||
|
|
||||||
|
## 2026-03-31 HardFault Root Cause And Fix
|
||||||
|
|
||||||
|
### Symptom
|
||||||
|
|
||||||
|
- After CH390 bring-up completed and boot diagnostics printed, the firmware entered:
|
||||||
|
|
||||||
|
```text
|
||||||
|
TRAP: HardFault_Handler
|
||||||
|
```
|
||||||
|
|
||||||
|
- At the same time, `PC13` stopped blinking, which originally looked like a timer or LED problem.
|
||||||
|
|
||||||
|
### Fault Evidence
|
||||||
|
|
||||||
|
- Fault-status registers showed a real fault rather than a normal busy wait.
|
||||||
|
- The trap location was `Debug_TrapWithRttHint()` in `Core/Src/main.c`.
|
||||||
|
- The stacked fault frame pointed back into the normal runtime path rather than the trap itself.
|
||||||
|
- `TIM4` was configured and had already advanced `g_led_blink_ticks`, so the LED path was alive before the fault.
|
||||||
|
|
||||||
|
### Root Cause
|
||||||
|
|
||||||
|
- `MX_IWDG_Init()` had been temporarily commented out in `main()`.
|
||||||
|
- However, `App_Poll()` still executed:
|
||||||
|
|
||||||
|
```c
|
||||||
|
HAL_IWDG_Refresh(&hiwdg);
|
||||||
|
```
|
||||||
|
|
||||||
|
- Because `hiwdg` was never initialized, this call operated on an invalid handle and led to the observed fault path.
|
||||||
|
|
||||||
|
### Fix Applied
|
||||||
|
|
||||||
|
- `Core/Src/main.c` was changed so watchdog refresh only runs when `hiwdg.Instance == IWDG`.
|
||||||
|
- This preserves normal behavior when IWDG is enabled, while avoiding invalid access when IWDG init is intentionally disabled for debugging.
|
||||||
|
|
||||||
|
### Verification
|
||||||
|
|
||||||
|
- Rebuilt successfully with `0 error`, `1 warning`.
|
||||||
|
- Reflashed target and reran startup.
|
||||||
|
- Boot RTT still showed CH390 diagnostics, but no longer showed `TRAP: HardFault_Handler`.
|
||||||
|
- A 5-second runtime window completed without a new trap.
|
||||||
|
- `g_led_blink_ticks` continued advancing after the fix, confirming that `TIM4` interrupts and the LED heartbeat path were alive again.
|
||||||
|
|
||||||
|
### Conclusion
|
||||||
|
|
||||||
|
- The HardFault was caused by refreshing an uninitialized IWDG handle, not by the CH390 SPI path itself.
|
||||||
|
- This issue is fixed.
|
||||||
|
- CH390 bring-up is still unresolved at the register-communication level, but the main task is again able to continue running normally.
|
||||||
|
|
||||||
|
## 2026-03-31 Runtime Freeze Root Cause And Fix
|
||||||
|
|
||||||
|
### Symptom
|
||||||
|
|
||||||
|
- After re-soldering CH390D, the system could boot and print the normal CH390 startup diagnostics.
|
||||||
|
- However, after running for a while, the device would appear to freeze.
|
||||||
|
- In that state, the LED heartbeat behavior became unreliable and the system appeared to stop making useful progress.
|
||||||
|
|
||||||
|
### Key Runtime Evidence
|
||||||
|
|
||||||
|
- The new freeze was **not** another HardFault: no new `TRAP:` line appeared during the freeze window.
|
||||||
|
- `g_led_blink_ticks` continued advancing during observation windows, proving that `TIM4` interrupts were still alive and the MCU was not fully dead.
|
||||||
|
- A short halt during the bad behavior repeatedly landed in `HAL_SPI_TransmitReceive()`.
|
||||||
|
- Code inspection showed that CH390 runtime paths in `ethernetif.c` were executing blocking SPI transactions while global interrupts were disabled via `ethernetif_lock()`.
|
||||||
|
|
||||||
|
### Root Cause
|
||||||
|
|
||||||
|
- `low_level_output()`, `low_level_input()`, and `ethernetif_check_link()` in `Drivers/LwIP/src/netif/ethernetif.c` wrapped CH390 SPI register/memory accesses inside `ethernetif_lock()` / `ethernetif_unlock()`.
|
||||||
|
- Those helpers globally disable interrupts by manipulating `PRIMASK`.
|
||||||
|
- The CH390 access path uses blocking HAL SPI functions and timeout logic based on `HAL_GetTick()`.
|
||||||
|
- Running those blocking accesses with interrupts disabled can stall or livelock the runtime path, especially after startup when network polling begins.
|
||||||
|
|
||||||
|
### Fix Applied
|
||||||
|
|
||||||
|
- Reduced the interrupt-masked critical sections in `ethernetif.c` to only protect the shared IRQ-pending flag.
|
||||||
|
- Removed `ethernetif_lock()` coverage from the long CH390 SPI transaction paths in:
|
||||||
|
- `low_level_output()`
|
||||||
|
- `low_level_input()`
|
||||||
|
- `ethernetif_check_link()`
|
||||||
|
- In `ethernetif_poll()`, only the `g_ch390_irq_pending` flag is now cleared under the short critical section; the actual CH390 register access happens with interrupts enabled.
|
||||||
|
|
||||||
|
### Verification
|
||||||
|
|
||||||
|
- Rebuilt successfully with `0 error`, `0 warning`.
|
||||||
|
- Reflashed and reran the target.
|
||||||
|
- Boot RTT still completed normally through:
|
||||||
|
|
||||||
|
```text
|
||||||
|
TCP2UART boot
|
||||||
|
ETH init: gpio
|
||||||
|
ETH init: spi
|
||||||
|
ETH init: reset
|
||||||
|
ETH init: default
|
||||||
|
ETH init: mac
|
||||||
|
ETH init: getmac
|
||||||
|
ETH init: irq
|
||||||
|
ETH init: done
|
||||||
|
CH390 VID=0x0000 PID=0x0000 REV=0x00 NSR=0x00 LINK=0
|
||||||
|
CH390 NCR=0x00 RCR=0x00 IMR=0x00 INTCR=0x00 GPR=0x00 ISR=0x00
|
||||||
|
```
|
||||||
|
|
||||||
|
- No new `TRAP:` message appeared during extended runtime observation.
|
||||||
|
- `g_led_blink_ticks` continued advancing over multiple samples, indicating that the heartbeat timer and interrupt delivery remained active.
|
||||||
|
- The system no longer reproduced the earlier “runs for a while then appears frozen” behavior in the observed validation window.
|
||||||
|
|
||||||
|
### Conclusion
|
||||||
|
|
||||||
|
- This freeze was caused by doing blocking CH390 SPI operations inside a global interrupt-disabled critical section.
|
||||||
|
- The runtime freeze is fixed.
|
||||||
|
- CH390 register communication is still invalid (`0x0000` ID values), but that is now a separate communication/bring-up problem rather than the cause of the observed runtime stall.
|
||||||
|
|
||||||
|
## 2026-03-31 SPI Ownership Decoupling And CH390 Current Status
|
||||||
|
|
||||||
|
### Why This Refactor Was Done
|
||||||
|
|
||||||
|
- The project previously allowed multiple runtime layers to reach down into CH390/SPI behavior directly:
|
||||||
|
- `ethernetif.c` handled init, IRQ-driven poll service, RX/TX transactions, and link checks
|
||||||
|
- `main.c` directly read CH390 registers for boot diagnostics
|
||||||
|
- the CH390 low-level SPI transport sat underneath those callers with no single runtime owner boundary
|
||||||
|
- This made the system harder to reason about and contributed to runtime instability when CH390 accesses happened from different code paths with different assumptions.
|
||||||
|
|
||||||
|
### Refactor Outcome
|
||||||
|
|
||||||
|
- Added a single runtime owner module: `Drivers/CH390/ch390_runtime.c` + `Drivers/CH390/ch390_runtime.h`.
|
||||||
|
- After this change:
|
||||||
|
- `CH390_Interface.c` remains the **only** SPI transport implementation
|
||||||
|
- `CH390.c` remains the chip-level helper layer
|
||||||
|
- `ch390_runtime.c` is now the **only runtime owner** of CH390 transactions after boot
|
||||||
|
- `ethernetif.c` delegates runtime TX/RX/link/IRQ servicing to `ch390_runtime`
|
||||||
|
- `main.c` no longer performs direct CH390 register reads; boot diagnostics use `ch390_runtime_get_diag()`
|
||||||
|
- `EXTI0_IRQHandler()` only posts the IRQ-pending event into the runtime owner and does not touch CH390 directly
|
||||||
|
|
||||||
|
### Behavior After Refactor
|
||||||
|
|
||||||
|
- Build passed with `0 error`, `0 warning`.
|
||||||
|
- The system remained stable in the post-refactor runtime window:
|
||||||
|
- no new trap output
|
||||||
|
- heartbeat/timer activity continued
|
||||||
|
- previous runtime freeze did not reproduce in the observed window
|
||||||
|
|
||||||
|
### CH390 Result After Refactor
|
||||||
|
|
||||||
|
- The CH390 did **not** come up successfully.
|
||||||
|
- However, the failure signature became cleaner and more trustworthy:
|
||||||
|
|
||||||
|
```text
|
||||||
|
CH390 VID=0xFFFF PID=0xFFFF REV=0xFF NSR=0xFF LINK=1
|
||||||
|
CH390 NCR=0xFF RCR=0xFF IMR=0xFF INTCR=0xFF GPR=0xFF ISR=0xFF
|
||||||
|
```
|
||||||
|
|
||||||
|
- This is materially different from the earlier unstable mixture of:
|
||||||
|
- all-zero reads
|
||||||
|
- intermittent hangs
|
||||||
|
- transaction artifacts
|
||||||
|
- watchdog-related HardFaults
|
||||||
|
|
||||||
|
### Trusted Interpretation Of Current Failure
|
||||||
|
|
||||||
|
- With the SPI access model cleaned up and the system remaining stable, the current CH390 failure can now be treated as a **credible transport-level non-response** rather than a concurrency artifact.
|
||||||
|
- A uniform `0xFF` readback across identity and status/control registers strongly suggests one of these conditions:
|
||||||
|
- CH390 still does not actively drive MISO during the register-read phase
|
||||||
|
- CS reaches the MCU logic but is not effectively selecting the CH390 device on the board side
|
||||||
|
- the CH390 digital core is not entering a valid SPI-responding state after reset even though the MCU-side sequence now looks consistent
|
||||||
|
|
||||||
|
### Practical Conclusion
|
||||||
|
|
||||||
|
- The architectural decoupling requirement is complete.
|
||||||
|
- The runtime stability requirement is complete.
|
||||||
|
- CH390 connection is **still failed**, but the reason is now narrowed to a believable low-level bus/device-response problem rather than a software ownership/concurrency problem.
|
||||||
|
|
||||||
|
## 2026-04-01 Final Software Boundary Check: Hardware SPI vs Bit-Bang
|
||||||
|
|
||||||
|
### Goal
|
||||||
|
|
||||||
|
- Decide whether another software-side SPI transaction rewrite is still justified.
|
||||||
|
- Use one last high-signal experiment to separate STM32 hardware-SPI issues from board-level CH390 non-response.
|
||||||
|
|
||||||
|
### Experiment
|
||||||
|
|
||||||
|
- Kept the normal hardware-SPI identity probe in `ch390_runtime_probe_identity()`.
|
||||||
|
- Added a temporary bit-bang register read helper in `Drivers/CH390/CH390_Interface.c` that:
|
||||||
|
- disables `SPI1`
|
||||||
|
- reconfigures `PA5/PA7` as GPIO outputs and `PA6` as GPIO input
|
||||||
|
- clocks out register reads in software with explicit `CS/SCK/MOSI/MISO` control
|
||||||
|
- restores the pins back to hardware-SPI mode before returning
|
||||||
|
- On hardware-SPI probe failure, startup now prints one additional RTT line with bit-bang reads of:
|
||||||
|
- `VIDL`
|
||||||
|
- `VIDH`
|
||||||
|
- `PIDL`
|
||||||
|
- `PIDH`
|
||||||
|
- `CHIPR`
|
||||||
|
|
||||||
|
### Observed RTT Output
|
||||||
|
|
||||||
|
```text
|
||||||
|
TCP2UART boot
|
||||||
|
ETH init: gpio
|
||||||
|
ETH init: spi
|
||||||
|
ETH init: reset
|
||||||
|
ETH init: probe
|
||||||
|
CH390 bitbang VIDL=0xFF VIDH=0xFF PIDL=0xFF PIDH=0xFF CHIPR=0xFF
|
||||||
|
ETH init: invalid chip id
|
||||||
|
CH390 VID=0xFFFF PID=0xFFFF REV=0xFF NSR=0xFF LINK=0
|
||||||
|
CH390 NCR=0xFF RCR=0xFF IMR=0xFF INTCR=0xFF GPR=0xFF ISR=0xFF
|
||||||
|
```
|
||||||
|
|
||||||
|
### Interpretation
|
||||||
|
|
||||||
|
- The MCU is alive, RTT is alive, and the firmware reaches the CH390 identity-probe stage normally.
|
||||||
|
- The normal hardware-SPI read path still returns all `0xFF`.
|
||||||
|
- The independent bit-bang read path also returns all `0xFF` for the same critical identity registers.
|
||||||
|
- This is the most important final discriminator collected so far:
|
||||||
|
- if hardware SPI alone were the remaining problem, bit-bang should have had a realistic chance to return valid IDs
|
||||||
|
- because both methods return the same all-`0xFF` result, the remaining fault is much more consistent with CH390-side non-response than with STM32 SPI peripheral behavior
|
||||||
|
|
||||||
|
### External Reference Cross-Check
|
||||||
|
|
||||||
|
- Public working CH390/DM9051 drivers commonly use `SPI mode 0`, `1-bit command + 7-bit address` framing, and contiguous packet-memory bursts.
|
||||||
|
- Those references support further cleanup of packet-memory transaction framing once basic register access works.
|
||||||
|
- They do **not** make packet-memory fragmentation a strong explanation for `VID/PID/basic regs = 0xFFFF/0xFF` from the very start.
|
||||||
|
- Therefore the remaining software-side suspects are now weaker than the board-level suspects.
|
||||||
|
|
||||||
|
### Final Conclusion
|
||||||
|
|
||||||
|
- The project has already removed several real software defects and sources of diagnostic noise:
|
||||||
|
- PHY access timeout hole
|
||||||
|
- watchdog-related HardFault
|
||||||
|
- interrupt-masked blocking SPI runtime path
|
||||||
|
- redundant early CH390 reset in `main()`
|
||||||
|
- warning-producing dead code / unused values
|
||||||
|
- After those fixes, both hardware-SPI and bit-bang reads still show CH390 register non-response.
|
||||||
|
- The most defensible current conclusion is:
|
||||||
|
- **software is no longer the primary blocker**
|
||||||
|
- the remaining fault is more likely in board wiring, chip power/reset state, effective CS selection, MISO drive, signal integrity, or the CH390D device itself
|
||||||
|
|
||||||
|
### Recommended Next Step Outside Firmware
|
||||||
|
|
||||||
|
- Probe real waveforms on `CS/SCK/MOSI/MISO/RST/INT` during the identity-read transaction.
|
||||||
|
- Specifically verify that:
|
||||||
|
- `CS` actually reaches the CH390 pin and stays low for the transaction
|
||||||
|
- `RST` is released high at the chip pin
|
||||||
|
- `MISO` is actively driven by the CH390 instead of floating high
|
||||||
|
- the CH390 supply and reset domain are valid during the read window
|
||||||
@@ -0,0 +1,496 @@
|
|||||||
|
# TCP2UART 调试指导
|
||||||
|
|
||||||
|
## 1. 适用范围
|
||||||
|
|
||||||
|
本指导面向当前 `TCP2UART` 工程,覆盖以下四类调试场景:
|
||||||
|
|
||||||
|
1. `STM32F103R8T6 + CH390D` 的基础 bring-up
|
||||||
|
2. `SEGGER RTT`、异常陷阱与主循环运行状态确认
|
||||||
|
3. `USART1` 配置口、`USART2/USART3` 数据口与 `MUX / NET / LINK[idx]` 协议联调
|
||||||
|
4. `TCP Server / TCP Client / UART` 三层数据通路联调与问题隔离
|
||||||
|
|
||||||
|
本指导默认基线如下:
|
||||||
|
|
||||||
|
1. 当前工程采用裸机主循环架构,未使用 FreeRTOS 参与主业务调度
|
||||||
|
2. `CH390` 运行时访问统一由 `ch390_runtime` 持有
|
||||||
|
3. 调试输出统一使用 `SEGGER RTT`
|
||||||
|
4. 当前应用层协议模型已经收敛到 `MUX / NET / LINK[idx]`
|
||||||
|
5. 当前代码应以 `MDK-ARM` 工程构建结果为准,而不是 `CMake + MSVC` 结果
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 当前工程边界与真实状态
|
||||||
|
|
||||||
|
在进入现场调试前,先统一以下工程边界,避免沿用过时结论:
|
||||||
|
|
||||||
|
1. 当前项目的主要软件路径已经切换为:
|
||||||
|
- `NET`:网络基础参数
|
||||||
|
- `LINK[idx]`:链路配置记录
|
||||||
|
- `MUX`:数据口承载模式
|
||||||
|
2. 对外 AT 配置面应只围绕以下命令展开:
|
||||||
|
- `AT`
|
||||||
|
- `AT+?`
|
||||||
|
- `AT+QUERY`
|
||||||
|
- `AT+MUX`
|
||||||
|
- `AT+NET`
|
||||||
|
- `AT+LINK`
|
||||||
|
- `AT+SAVE`
|
||||||
|
- `AT+RESET`
|
||||||
|
- `AT+DEFAULT`
|
||||||
|
3. 当前 `CH390D` 的历史“全 `0xFF` / 全 `0x0000`”结论不应再直接沿用。
|
||||||
|
4. 已有结论表明:
|
||||||
|
- MCU 启动、RTT、主循环、TIM4 心跳路径可工作
|
||||||
|
- `CH390D` 基础寄存器读写与 `lwIP netif` 基本链路已经打通过一次
|
||||||
|
- 真实硬件侧曾定位到 `CH390D` 供电滤波电容虚焊问题
|
||||||
|
5. 因此,当前调试重点不再是“CH390 是否完全不通”,而是:
|
||||||
|
- 启动阶段是否稳定
|
||||||
|
- `MUX / NET / LINK[idx]` 协议是否与代码一致
|
||||||
|
- UART / TCP / CH390 三层通路是否协同稳定
|
||||||
|
- 参数保存、复位和恢复流程是否可靠
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 代码入口与调试责任边界
|
||||||
|
|
||||||
|
### 3.1 启动与主循环入口
|
||||||
|
|
||||||
|
以下代码路径是 bring-up 的第一现场:
|
||||||
|
|
||||||
|
1. `Core/Src/main.c`
|
||||||
|
- `main()`:总启动入口
|
||||||
|
- `SystemClock_Config()`:时钟初始化
|
||||||
|
- `App_Init()`:应用层初始化
|
||||||
|
- `App_Poll()`:主循环核心路径
|
||||||
|
- `BootDiag_ReportCh390()`:启动阶段 CH390 诊断输出
|
||||||
|
2. `Core/Src/stm32f1xx_it.c`
|
||||||
|
- 故障与中断入口
|
||||||
|
- `USART1/2/3`、`EXTI0`、DMA 回调等联调关键入口
|
||||||
|
|
||||||
|
### 3.2 CH390 责任边界
|
||||||
|
|
||||||
|
当前 CH390 调试必须遵守以下责任边界:
|
||||||
|
|
||||||
|
1. `Drivers/CH390/CH390_Interface.c`
|
||||||
|
- 只负责 GPIO / SPI / 寄存器与内存事务
|
||||||
|
2. `Drivers/CH390/CH390.c`
|
||||||
|
- 只负责芯片级 helper,例如默认配置、PHY、MAC 读写
|
||||||
|
3. `Drivers/CH390/ch390_runtime.c`
|
||||||
|
- 唯一的运行时拥有者
|
||||||
|
- 负责初始化、链路检查、IRQ 消费、RX/TX 服务与诊断快照
|
||||||
|
4. `Drivers/LwIP/src/netif/ethernetif.c`
|
||||||
|
- 只承担 netif glue 与轮询桥接,不应重新下沉复杂 CH390 运行时事务
|
||||||
|
5. `Core/Src/main.c`
|
||||||
|
- 启动后只通过 runtime 对外暴露的诊断与轮询接口工作
|
||||||
|
|
||||||
|
调试时不要把原始 CH390 寄存器访问重新散回 `main.c`、中断或多个业务层。
|
||||||
|
|
||||||
|
### 3.3 配置口与业务口边界
|
||||||
|
|
||||||
|
1. `USART1`
|
||||||
|
- 配置口
|
||||||
|
- 负责接收 `AT` 命令
|
||||||
|
- 当前接收逻辑在:
|
||||||
|
- `Core/Src/main.c` 的 `App_PollUart1ConfigRx()`
|
||||||
|
- `Core/Src/stm32f1xx_it.c` 的 `HAL_UART_RxCpltCallback()`
|
||||||
|
- `App/config.c` 的 `config_uart_rx_byte()` / `config_process_at_cmd()`
|
||||||
|
2. `USART2 / USART3`
|
||||||
|
- 数据口
|
||||||
|
- 负责普通透传或 MUX 承载
|
||||||
|
- 当前入口在 `App/uart_trans.c`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 当前硬件与调试工具基线
|
||||||
|
|
||||||
|
### 4.1 核心硬件对象
|
||||||
|
|
||||||
|
1. MCU:`STM32F103R8T6`
|
||||||
|
2. 以太网芯片:`CH390D`
|
||||||
|
3. 配置串口:`USART1`
|
||||||
|
4. 数据串口:`USART2 / USART3`
|
||||||
|
5. 调试输出:`SEGGER RTT`
|
||||||
|
|
||||||
|
### 4.2 构建与下载基线
|
||||||
|
|
||||||
|
当前建议优先使用以下工程与产物:
|
||||||
|
|
||||||
|
1. `MDK-ARM/TCP2UART.uvprojx`
|
||||||
|
2. `MDK-ARM/TCP2UART/TCP2UART.axf`
|
||||||
|
3. `MDK-ARM/TCP2UART/TCP2UART.hex`
|
||||||
|
4. `MDK-ARM/TCP2UART/TCP2UART.map`
|
||||||
|
5. `MDK-ARM/TCP2UART/TCP2UART.build_log.htm`
|
||||||
|
6. `build_keil.log`
|
||||||
|
|
||||||
|
说明:
|
||||||
|
|
||||||
|
1. 当前 `CMake configure` 可以完成,但 `CMake + MSVC` 不适合作为 STM32/CMSIS 的最终构建验收依据。
|
||||||
|
2. 若需要验证“当前代码是否真实可编译”,优先看 `MDK-ARM` 构建产物与日志。
|
||||||
|
|
||||||
|
### 4.3 常用调试工具
|
||||||
|
|
||||||
|
1. `Keil MDK-ARM`
|
||||||
|
2. `ST-Link / J-Link`
|
||||||
|
3. `SEGGER RTT Viewer`
|
||||||
|
4. `PowerShell`
|
||||||
|
5. `tools/start_tcp_debug_server.ps1`
|
||||||
|
6. `tools/tcp_debug_server.py`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 启动阶段调试顺序
|
||||||
|
|
||||||
|
建议按 P0 ~ P5 顺序推进,不要跳层。
|
||||||
|
|
||||||
|
### 5.1 P0:确认最小基础条件
|
||||||
|
|
||||||
|
每次现场调试前,先确认:
|
||||||
|
|
||||||
|
1. `MDK-ARM` 可构建并产出新的 `axf/hex/map`
|
||||||
|
2. 板卡可正常下载与复位
|
||||||
|
3. RTT 可连接并看到启动输出
|
||||||
|
4. LED 心跳可工作
|
||||||
|
5. `App_Poll()` 已经进入稳定轮询
|
||||||
|
|
||||||
|
这一层若失败,不要进入网络或协议调试。
|
||||||
|
|
||||||
|
### 5.2 P1:确认启动日志与 trap 状态
|
||||||
|
|
||||||
|
上电或复位后,优先看 RTT 输出中是否出现:
|
||||||
|
|
||||||
|
1. `TCP2UART boot`
|
||||||
|
2. 若 HSE 启动失败,则会出现:
|
||||||
|
- `WARN: HSE start failed, fallback to HSI PLL`
|
||||||
|
3. `BootDiag_ReportCh390()` 输出的 CH390 诊断与网络配置快照
|
||||||
|
|
||||||
|
若发生异常,优先观察是否打印:
|
||||||
|
|
||||||
|
1. `TRAP: Error_Handler`
|
||||||
|
2. `TRAP: HardFault_Handler`
|
||||||
|
3. `TRAP: MemManage_Handler`
|
||||||
|
4. `TRAP: BusFault_Handler`
|
||||||
|
5. `TRAP: UsageFault_Handler`
|
||||||
|
|
||||||
|
当前 trap 统一收敛到:
|
||||||
|
|
||||||
|
1. `Core/Src/main.c` 的 `Debug_TrapWithRttHint()`
|
||||||
|
2. 它会打印 RTT、执行 `__BKPT(0)` 并停住
|
||||||
|
|
||||||
|
因此,若 RTT 中出现 `TRAP:`,应立即接调试器看断点现场,而不是继续盲猜高层逻辑。
|
||||||
|
|
||||||
|
### 5.3 P2:确认 CH390 初始化链路
|
||||||
|
|
||||||
|
启动阶段应重点关注 `Drivers/CH390/ch390_runtime.c` 中初始化阶段日志,理想情况下应能依次看到:
|
||||||
|
|
||||||
|
1. `ETH init: gpio`
|
||||||
|
2. `ETH init: spi`
|
||||||
|
3. `ETH init: reset`
|
||||||
|
4. `ETH init: probe`
|
||||||
|
5. `ETH init: default`
|
||||||
|
6. `ETH init: mac`
|
||||||
|
7. `ETH init: getmac`
|
||||||
|
8. `ETH init: irq`
|
||||||
|
9. `ETH init: done`
|
||||||
|
|
||||||
|
此阶段重点判定:
|
||||||
|
|
||||||
|
1. `VID / PID / REV` 是否可信
|
||||||
|
2. PHY 寄存器是否稳定可读
|
||||||
|
3. MAC 写入与回读是否一致
|
||||||
|
4. `link_up` 是否与真实网线状态一致
|
||||||
|
|
||||||
|
若这一层失败,优先做硬件侧量测,而不是先改业务层:
|
||||||
|
|
||||||
|
1. `RSTB`
|
||||||
|
2. `CS`
|
||||||
|
3. `SCK`
|
||||||
|
4. `MOSI`
|
||||||
|
5. `MISO`
|
||||||
|
6. `INT`
|
||||||
|
7. `VDDK`
|
||||||
|
8. `AVDD33 / VDDIO / AVDD33`
|
||||||
|
9. `XI / XO`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. USART1 配置口调试
|
||||||
|
|
||||||
|
### 6.1 当前命令面
|
||||||
|
|
||||||
|
根据当前代码与手册,配置口应围绕以下命令验证:
|
||||||
|
|
||||||
|
1. `AT`
|
||||||
|
2. `AT+?`
|
||||||
|
3. `AT+QUERY`
|
||||||
|
4. `AT+MUX=...`
|
||||||
|
5. `AT+MUX?`
|
||||||
|
6. `AT+NET=...`
|
||||||
|
7. `AT+NET?`
|
||||||
|
8. `AT+LINK=...`
|
||||||
|
9. `AT+LINK?`
|
||||||
|
10. `AT+SAVE`
|
||||||
|
11. `AT+RESET`
|
||||||
|
12. `AT+DEFAULT`
|
||||||
|
|
||||||
|
### 6.2 现场关键规则
|
||||||
|
|
||||||
|
根据已有联调记录,配置口最关键的 bench 规则是:
|
||||||
|
|
||||||
|
1. 当前现场验证时,配置命令必须保证以换行完成帧。
|
||||||
|
2. 若主机侧发送方式不对,现象会很像“配置口完全无响应”。
|
||||||
|
3. 因此,配置口不响应时,第一优先级不是改 parser,而是先验证主机端发送格式与接线。
|
||||||
|
|
||||||
|
### 6.3 最小验证步骤
|
||||||
|
|
||||||
|
建议按以下顺序验证:
|
||||||
|
|
||||||
|
1. 连接 `USART1`
|
||||||
|
2. 先发 `AT`
|
||||||
|
3. 再发 `AT+QUERY`
|
||||||
|
4. 再发 `AT+NET?`
|
||||||
|
5. 再发 `AT+LINK?`
|
||||||
|
6. 修改一个最小参数,例如:
|
||||||
|
- `AT+MUX=1`
|
||||||
|
7. 执行:
|
||||||
|
- `AT+SAVE`
|
||||||
|
- `AT+RESET`
|
||||||
|
8. 复位后再次查询,确认配置是否保留
|
||||||
|
|
||||||
|
### 6.4 持久化失败时怎么查
|
||||||
|
|
||||||
|
优先检查以下路径:
|
||||||
|
|
||||||
|
1. `App/config.c`
|
||||||
|
- `config_save()`
|
||||||
|
- `config_load()`
|
||||||
|
- `config_set_defaults()`
|
||||||
|
2. `App/flash_param.c`
|
||||||
|
- Flash 解锁
|
||||||
|
- 页擦除
|
||||||
|
- 半字编程
|
||||||
|
- 写后校验
|
||||||
|
3. 参数页地址:
|
||||||
|
- `0x0800FC00`
|
||||||
|
|
||||||
|
不要在还没证明 `AT+SAVE` 已真正被接受之前,就直接把 `Flash 全 FFFFFFFF` 归因到 Flash 驱动错误。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. MUX / NET / LINK[idx] 联调指导
|
||||||
|
|
||||||
|
### 7.1 协议总则
|
||||||
|
|
||||||
|
当前协议必须按以下模型理解:
|
||||||
|
|
||||||
|
1. `MUX`:全局数据承载模式开关
|
||||||
|
2. `NET`:IP / Mask / GW / MAC
|
||||||
|
3. `LINK[idx]`:链路配置项
|
||||||
|
|
||||||
|
固定链路索引映射为:
|
||||||
|
|
||||||
|
1. `LINK[0] = S1`
|
||||||
|
2. `LINK[1] = S2`
|
||||||
|
3. `LINK[2] = C1`
|
||||||
|
4. `LINK[3] = C2`
|
||||||
|
|
||||||
|
固定端点编码为:
|
||||||
|
|
||||||
|
1. `C1 = 0x01`
|
||||||
|
2. `C2 = 0x02`
|
||||||
|
3. `UART2 = 0x04`
|
||||||
|
4. `UART3 = 0x08`
|
||||||
|
5. `S1 = 0x10`
|
||||||
|
6. `S2 = 0x20`
|
||||||
|
|
||||||
|
### 7.2 MUX 数据口规则
|
||||||
|
|
||||||
|
当 `MUX=1` 时,数据口应使用 MUX 帧。
|
||||||
|
|
||||||
|
重点规则:
|
||||||
|
|
||||||
|
1. `DSTMASK=0x00` 表示系统控制帧
|
||||||
|
2. 控制帧中的 AT 文本必须严格按手册要求结束
|
||||||
|
3. 普通数据帧走业务转发路径,不应进入配置解析器
|
||||||
|
|
||||||
|
### 7.3 调试时重点检查什么
|
||||||
|
|
||||||
|
若怀疑 `MUX` 模式不工作,优先检查:
|
||||||
|
|
||||||
|
1. `App/uart_trans.c`
|
||||||
|
- `uart_mux_try_extract_frame()`
|
||||||
|
- `uart_mux_encode_frame()`
|
||||||
|
2. `Core/Src/main.c`
|
||||||
|
- `App_RouteMuxUartTraffic()`
|
||||||
|
- `App_RouteRawUartTraffic()`
|
||||||
|
- `App_RouteTcpTraffic()`
|
||||||
|
3. `App/config.c`
|
||||||
|
- `config_build_response_frame()`
|
||||||
|
- `config_process_at_cmd()`
|
||||||
|
|
||||||
|
推荐最小 MUX 联调顺序:
|
||||||
|
|
||||||
|
1. 先在 `MUX=0` 下跑通原始透传
|
||||||
|
2. 再切换 `MUX=1`
|
||||||
|
3. 先发一个控制帧,确认 `DSTMASK=0x00` 路径可通
|
||||||
|
4. 再发一个单目标数据帧,例如只打到 `S1`
|
||||||
|
5. 最后验证多目标位图转发
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. TCP / UART / CH390 联调顺序
|
||||||
|
|
||||||
|
### 8.1 先做链路,再做业务
|
||||||
|
|
||||||
|
在 `CH390` 初始化、链路和 IRQ 未被证明稳定前,不要先调高层 TCP/UART 业务。
|
||||||
|
|
||||||
|
### 8.2 推荐顺序
|
||||||
|
|
||||||
|
建议按以下顺序推进:
|
||||||
|
|
||||||
|
1. RTT 启动与 trap 状态正常
|
||||||
|
2. CH390 启动日志完整
|
||||||
|
3. 链路检测可信
|
||||||
|
4. `TCP server` / `TCP client` 建链可信
|
||||||
|
5. UART 原始透传可信
|
||||||
|
6. 再切入 `MUX` 模式联调
|
||||||
|
|
||||||
|
### 8.3 最小 TCP 调试工具
|
||||||
|
|
||||||
|
当需要验证板子是否真的把 payload 发到主机时,优先使用仓库内置最小工具:
|
||||||
|
|
||||||
|
1. `tools/tcp_debug_server.py`
|
||||||
|
- 打印连接、收包、文本视图和十六进制视图
|
||||||
|
2. `tools/start_tcp_debug_server.ps1`
|
||||||
|
- 会先清理冲突监听,再启动 Python 服务端
|
||||||
|
|
||||||
|
推荐命令:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell -ExecutionPolicy Bypass -File ".\tools\start_tcp_debug_server.ps1" -Port 8081 -NoStdin
|
||||||
|
```
|
||||||
|
|
||||||
|
如需回显:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell -ExecutionPolicy Bypass -File ".\tools\start_tcp_debug_server.ps1" -Port 8081 -Echo
|
||||||
|
```
|
||||||
|
|
||||||
|
直接运行 Python 服务端也可以:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python .\tools\tcp_debug_server.py --host 0.0.0.0 --port 8081 --no-stdin
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.4 推荐验证方法
|
||||||
|
|
||||||
|
1. 先关闭 VOFA、`ncat` 和其它可能占用 `8081` 的进程
|
||||||
|
2. 启动 `start_tcp_debug_server.ps1`
|
||||||
|
3. 让板子连接主机 `TCP client` 目标端口
|
||||||
|
4. 再从主机连接板子的 `TCP server` 端口发送固定测试文本
|
||||||
|
5. 同时观察:
|
||||||
|
- Python 工具是否收到连接与 payload
|
||||||
|
- 板子 RTT 是否出现连接或错误信息
|
||||||
|
|
||||||
|
若板子 RTT 显示已连接,但主机工具无数据,优先检查本机端口占用而不是先改板端逻辑。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. 异常、卡死与假死排查
|
||||||
|
|
||||||
|
### 9.1 看到 `TRAP:` 时怎么做
|
||||||
|
|
||||||
|
1. 先记录 RTT 中的 trap 标签
|
||||||
|
2. 立刻用调试器查看当前 PC / LR / 调用栈
|
||||||
|
3. 结合 `Core/Src/stm32f1xx_it.c` 中对应 handler 定位异常类型
|
||||||
|
|
||||||
|
### 9.2 没有 `TRAP:` 但系统不工作时怎么做
|
||||||
|
|
||||||
|
若没有 `TRAP:`,但系统表现异常,应优先区分以下情况:
|
||||||
|
|
||||||
|
1. 主循环仍在跑,只是业务路径没反应
|
||||||
|
2. 中断未到或链路未更新
|
||||||
|
3. 发生了阻塞式等待或超时问题
|
||||||
|
4. 上层工具接错端口或被错误进程抢占
|
||||||
|
|
||||||
|
### 9.3 历史上已经确认过的典型软件问题
|
||||||
|
|
||||||
|
以下问题在历史排查中已经出现过,应优先复核,不要重复踩坑:
|
||||||
|
|
||||||
|
1. PHY 访问无超时,导致永久卡死
|
||||||
|
2. 刷新未初始化的 IWDG 句柄导致 HardFault
|
||||||
|
3. 在长耗时 SPI 路径中错误扩大临界区,导致看似“系统假死”
|
||||||
|
4. 在多个层次同时触达 CH390 / SPI,导致运行时边界混乱
|
||||||
|
5. 配置口命令结束方式不对,导致误判为 parser 无响应
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. 常见误区
|
||||||
|
|
||||||
|
调试当前工程时,应避免以下误区:
|
||||||
|
|
||||||
|
1. 不要继续沿用“CH390 恒为全 `0xFF`”这一过时结论
|
||||||
|
2. 不要在 `main.c`、IRQ、netif 多处重新插入原始 CH390 访问
|
||||||
|
3. 不要在没有芯片脚侧证据前,只凭 MCU 侧 GPIO 判断总线正常
|
||||||
|
4. 不要在基础寄存器读写尚不可信时,直接调高层 `TCP/UART/MUX` 业务逻辑
|
||||||
|
5. 不要把一次性 bring-up 实验代码长期留在正式路径中
|
||||||
|
6. 不要让多个本机进程同时监听板子要连接的 TCP 端口
|
||||||
|
7. 不要在尚未证明命令已真正进入 parser 之前,直接归因到 Flash、协议或网络层
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. 推荐的现场记录模板
|
||||||
|
|
||||||
|
建议每次现场调试至少记录以下信息:
|
||||||
|
|
||||||
|
1. 日期时间
|
||||||
|
2. 板卡编号
|
||||||
|
3. 固件产物路径
|
||||||
|
4. 下载方式
|
||||||
|
5. RTT 关键日志
|
||||||
|
6. 串口发送内容
|
||||||
|
7. TCP 调试工具输出
|
||||||
|
8. 关键波形或电压量测点
|
||||||
|
9. 结论
|
||||||
|
10. 下一步动作
|
||||||
|
|
||||||
|
建议记录格式:
|
||||||
|
|
||||||
|
```text
|
||||||
|
时间:
|
||||||
|
板卡:
|
||||||
|
固件:
|
||||||
|
下载方式:
|
||||||
|
操作步骤:
|
||||||
|
RTT输出:
|
||||||
|
串口/TCP现象:
|
||||||
|
硬件量测:
|
||||||
|
结论:
|
||||||
|
下一步:
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. 当前推荐的结论表达方式
|
||||||
|
|
||||||
|
若需要向项目成员同步当前状态,建议采用以下口径:
|
||||||
|
|
||||||
|
1. 当前工程软件架构已稳定在 `bare-metal + lwIP RAW + ch390_runtime 单一拥有者`
|
||||||
|
2. 当前调试重点已经从“CH390 是否完全无响应”转移到协议、链路和系统级联调
|
||||||
|
3. 当前对外协议和配置模型应以 `MUX / NET / LINK[idx]` 为准
|
||||||
|
4. `USART1` 配置口、`USART2/3` 数据口与 TCP 路由必须按最新代码路径调试,不应再参照历史 `IP/MASK/GW/PORT/RIP/RPORT` 公开接口模型
|
||||||
|
5. 硬件验证仍必须以 CH390 芯片脚侧波形和供电域量测为准
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. 建议配套阅读
|
||||||
|
|
||||||
|
建议与本指导配套阅读:
|
||||||
|
|
||||||
|
1. `AT固件使用手册.md`
|
||||||
|
2. `项目技术实现.md`
|
||||||
|
3. `项目需求说明.md`
|
||||||
|
4. `uart-ch390-debug-handoff.md`
|
||||||
|
5. `CH390_最终结论报告.md`
|
||||||
|
6. `build_keil.log`
|
||||||
|
7. `PCB/SCH_Schematic1_2026-03-26.pdf`
|
||||||
|
8. `tools/tcp_debug_server.py`
|
||||||
|
9. `tools/start_tcp_debug_server.ps1`
|
||||||
@@ -1,214 +1,192 @@
|
|||||||
# TCP2UART 项目技术实现
|
# TCP2UART 项目技术实现
|
||||||
|
|
||||||
## 一、当前实现结论
|
## 一、文档目的
|
||||||
|
|
||||||
当前工程已经从原先的 `FreeRTOS + lwIP socket/netconn` 方向,重构为适配 `STM32F103R8T6` 的裸机实现。
|
本文档描述 `TCP2UART` 项目的最终内部实现口径。
|
||||||
|
|
||||||
当前基线特征如下:
|
本文档只围绕最终协议模型展开:
|
||||||
|
|
||||||
1. MCU 目标固定为 `STM32F103R8T6 / STM32F103xB`
|
- `MUX`:串口承载层
|
||||||
2. 软件架构改为 `bare-metal main loop + DMA/IDLE + EXTI`
|
- `NET`:全局网络配置层
|
||||||
3. 网络栈采用 `lwIP RAW API + NO_SYS=1`
|
- `LINK[idx]`:实例配置与连接管理层
|
||||||
4. 调试输出采用 `SEGGER RTT`
|
|
||||||
5. 构建目标已通过 `MDK-ARM` 编译,适配 `64KB Flash / 20KB SRAM`
|
|
||||||
|
|
||||||
## 二、硬件与资源约束
|
不再保留历史 `S1... / C1...` 外部字段模型。
|
||||||
|
|
||||||
### 2.1 MCU
|
## 二、当前工程基础
|
||||||
|
|
||||||
- 型号:`STM32F103R8T6`
|
当前工程基础约束如下:
|
||||||
- Flash:`64 KB`
|
|
||||||
- SRAM:`20 KB`
|
|
||||||
- 主频:`72 MHz`
|
|
||||||
|
|
||||||
### 2.2 主要外设
|
1. MCU:`STM32F103R8T6`
|
||||||
|
2. 网络芯片:`CH390D`
|
||||||
|
3. 软件架构:`bare-metal main loop`
|
||||||
|
4. 协议栈:`lwIP RAW API + NO_SYS=1`
|
||||||
|
5. 调试输出:`SEGGER RTT`
|
||||||
|
6. 不使用 `FreeRTOS`
|
||||||
|
7. 不实现 DHCP
|
||||||
|
|
||||||
- `SPI1`:连接 `CH390D`
|
## 三、总体架构
|
||||||
- `USART1`:配置串口
|
|
||||||
- `USART2`:Server 透传串口
|
|
||||||
- `USART3`:Client 透传串口
|
|
||||||
- `DMA1`:UART 收发 DMA
|
|
||||||
- `EXTI0`:CH390 中断输入
|
|
||||||
- `IWDG`:独立看门狗
|
|
||||||
|
|
||||||
### 2.3 当前引脚分配
|
|
||||||
|
|
||||||
| 引脚 | 功能 | 用途 |
|
|
||||||
|------|------|------|
|
|
||||||
| PA2 | USART2_TX | Server 透传串口 |
|
|
||||||
| PA3 | USART2_RX | Server 透传串口 |
|
|
||||||
| PA4 | GPIO_Output | CH390D 片选 |
|
|
||||||
| PA5 | SPI1_SCK | CH390D SPI 时钟 |
|
|
||||||
| PA6 | SPI1_MISO | CH390D SPI 数据输入 |
|
|
||||||
| PA7 | SPI1_MOSI | CH390D SPI 数据输出 |
|
|
||||||
| PA9 | USART1_TX | 配置串口 |
|
|
||||||
| PA10 | USART1_RX | 配置串口 |
|
|
||||||
| PB0 | EXTI0 | CH390D INT |
|
|
||||||
| PB1 | GPIO_Output | CH390D RESET |
|
|
||||||
| PB10 | USART3_TX | Client 透传串口 |
|
|
||||||
| PB11 | USART3_RX | Client 透传串口 |
|
|
||||||
| PC13 | GPIO_Output | 状态 LED |
|
|
||||||
| PD0/PD1 | HSE | 8MHz 外部晶振 |
|
|
||||||
|
|
||||||
## 三、架构选择原因
|
|
||||||
|
|
||||||
`STM32F103R8T6` 的资源上限不足以稳定承载原方案中的以下组合:
|
|
||||||
|
|
||||||
1. `FreeRTOS`
|
|
||||||
2. `CMSIS-RTOS V2`
|
|
||||||
3. 多任务栈
|
|
||||||
4. `lwIP socket/netconn/tcpip` OS 路线
|
|
||||||
5. `StreamBuffer / Semaphore / Mutex`
|
|
||||||
|
|
||||||
因此当前实现采用如下组合:
|
|
||||||
|
|
||||||
1. 去掉 `FreeRTOS`
|
|
||||||
2. 去掉 `CMSIS-RTOS V2`
|
|
||||||
3. 去掉 `lwIP socket/netconn`
|
|
||||||
4. 改为 `lwIP RAW API + NO_SYS=1`
|
|
||||||
5. 串口与网口统一由主循环推进
|
|
||||||
|
|
||||||
## 四、当前软件架构
|
|
||||||
|
|
||||||
### 4.1 分层
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
+--------------------------------------------------+
|
+--------------------------------------------------+
|
||||||
| Application Logic |
|
| AT / Control Plane |
|
||||||
| config / tcp_server / tcp_client / uart bridge |
|
| USART1 AT parser + MUX control frame parser |
|
||||||
+--------------------------------------------------+
|
+--------------------------------------------------+
|
||||||
| Main Poll Loop |
|
| Configuration Model |
|
||||||
| ethernetif_poll / sys_check_timeouts / watchdog |
|
| MUX / NET / LINK[idx] |
|
||||||
+--------------------------------------------------+
|
+--------------------------------------------------+
|
||||||
| Peripheral/Event Layer |
|
| Routing & Session Layer |
|
||||||
| UART DMA+IDLE / DMA IRQ / EXTI / SysTick |
|
| TCP instance scheduling + UART dispatch |
|
||||||
+--------------------------------------------------+
|
+--------------------------------------------------+
|
||||||
| Drivers |
|
| Transport Poll Loop |
|
||||||
| CH390 / lwIP netif / HAL |
|
| ethernetif_poll / sys_check_timeouts / uart poll |
|
||||||
|
+--------------------------------------------------+
|
||||||
|
| Driver Layer |
|
||||||
|
| CH390 / lwIP netif / UART DMA+IDLE / HAL |
|
||||||
+--------------------------------------------------+
|
+--------------------------------------------------+
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4.2 执行模型
|
## 四、最终协议实现模型
|
||||||
|
|
||||||
当前执行模型为:
|
### 4.1 MUX 帧承载层
|
||||||
|
|
||||||
1. `SysTick` 提供全局毫秒时基
|
数据口启用 MUX 后,统一处理如下帧:
|
||||||
2. `EXTI0` 只置位 CH390 待处理标志
|
|
||||||
3. `DMA IRQ` 和 `UART IRQ` 只完成回调分发与 IDLE 采样
|
|
||||||
4. 主循环统一执行网络轮询、超时推进、串口桥接和看门狗喂狗
|
|
||||||
|
|
||||||
## 五、当前模块实现状态
|
```text
|
||||||
|
SYNC | LEN_H | LEN_L | SRCID | DSTMASK | PAYLOAD | TAIL
|
||||||
|
```
|
||||||
|
|
||||||
### 5.1 配置模块
|
实现职责:
|
||||||
|
|
||||||
文件:`App/config.c/.h`
|
1. 识别帧边界
|
||||||
|
2. 解析长度字段
|
||||||
|
3. 提取 `SRCID`
|
||||||
|
4. 解析 `DSTMASK`
|
||||||
|
5. 按控制帧或数据帧分流
|
||||||
|
|
||||||
已实现:
|
### 4.2 控制帧与数据帧分离
|
||||||
|
|
||||||
1. 从 Flash 加载配置
|
控制规则固定如下:
|
||||||
2. UART1 命令口接收
|
|
||||||
3. 常用网络与串口参数解析
|
|
||||||
4. 参数保存与软复位请求
|
|
||||||
|
|
||||||
当前约束:
|
- `DSTMASK = 0x00`:系统控制帧
|
||||||
|
- `DSTMASK != 0x00`:业务数据帧
|
||||||
|
|
||||||
1. 构建已关闭 `DHCP`,因此 `AT+DHCP=1` 会明确返回错误
|
系统控制帧处理要求:
|
||||||
2. 配置损坏时会回退默认值,但默认值不会自动写回 Flash,仍需手动 `AT+SAVE`
|
|
||||||
|
|
||||||
### 5.2 UART 透传模块
|
1. `PAYLOAD` 解释为 AT 文本
|
||||||
|
2. AT 文本必须以 `\r\n` 结束
|
||||||
|
3. 控制帧进入 AT 命令处理链路
|
||||||
|
|
||||||
文件:`App/uart_trans.c/.h`
|
业务数据帧处理要求:
|
||||||
|
|
||||||
已实现:
|
1. `SRCID` 表示单一源端点
|
||||||
|
2. `DSTMASK` 表示目标端点集合
|
||||||
|
3. 路由层根据 `DSTMASK` 做多目标分发
|
||||||
|
|
||||||
1. `USART2/USART3` 使用 `DMA + IDLE`
|
### 4.3 统一端点编码
|
||||||
2. RX 使用 DMA 缓冲转环形缓冲
|
|
||||||
3. TX 使用 DMA 发送
|
|
||||||
4. TX/RX 完成由 `HAL_UART_*Callback` 驱动
|
|
||||||
|
|
||||||
### 5.3 TCP Server 模块
|
内部与外部文档统一使用以下端点编码:
|
||||||
|
|
||||||
文件:`App/tcp_server.c/.h`
|
| 端点 | 编码 |
|
||||||
|
|------|------|
|
||||||
|
| `C1` | `0x01` |
|
||||||
|
| `C2` | `0x02` |
|
||||||
|
| `UART2` | `0x04` |
|
||||||
|
| `UART3` | `0x08` |
|
||||||
|
| `S1` | `0x10` |
|
||||||
|
| `S2` | `0x20` |
|
||||||
|
|
||||||
已实现:
|
实现要求:
|
||||||
|
|
||||||
1. `lwIP RAW API` 监听指定端口
|
- `SRCID` 为单值
|
||||||
2. 单连接接入
|
- `DSTMASK` 为位图
|
||||||
3. 网络数据写入本地环形缓冲
|
- `DSTMASK=0x00` 仅保留为控制帧
|
||||||
4. 主循环中与 UART2 做双向桥接
|
|
||||||
|
|
||||||
### 5.4 TCP Client 模块
|
## 五、配置层设计
|
||||||
|
|
||||||
文件:`App/tcp_client.c/.h`
|
### 5.1 MUX 记录
|
||||||
|
|
||||||
已实现:
|
`MUX` 为全局记录,仅控制设备数据口是否进入 MUX 承载模式。
|
||||||
|
|
||||||
1. `lwIP RAW API` 主动连接远端地址
|
取值:
|
||||||
2. 断链后按周期重连
|
|
||||||
3. 网络数据写入本地环形缓冲
|
|
||||||
4. 主循环中与 UART3 做双向桥接
|
|
||||||
|
|
||||||
### 5.5 CH390 与 netif 模块
|
- `0`:普通透传
|
||||||
|
- `1`:MUX 透传
|
||||||
|
|
||||||
文件:`Drivers/CH390/*`、`Drivers/LwIP/src/netif/*`
|
### 5.2 NET 记录
|
||||||
|
|
||||||
已实现:
|
`NET` 为全局静态网络记录:
|
||||||
|
|
||||||
1. `SPI1 + GPIO CS + EXTI0` 驱动 CH390
|
```text
|
||||||
2. `ethernetif.c` 采用 `NO_SYS=1` 路线
|
IP,MASK,GW,MAC
|
||||||
3. CH390 中断在主循环中轮询处理
|
```
|
||||||
4. 配置中的 MAC 地址会在初始化时写入 CH390
|
|
||||||
|
|
||||||
### 5.6 RTT 调试输出
|
说明:
|
||||||
|
|
||||||
文件:`Middlewares/Third_Party/SEGGER_RTT/*`
|
- 设备只有一张网卡,因此不为每个实例单独配置本地 IP
|
||||||
|
- 当前实现目标中不包含 DHCP
|
||||||
|
|
||||||
已实现:
|
### 5.3 LINK 记录
|
||||||
|
|
||||||
1. 工程内置最小 `SEGGER RTT` 源文件
|
`LINK[idx]` 为统一实例记录:
|
||||||
2. `main.c` 中 `printf/fputc` 已重定向到 RTT
|
|
||||||
|
|
||||||
### 5.7 TIM4 心跳闪烁
|
```text
|
||||||
|
EN,LPORT,RIP,RPORT,UART
|
||||||
|
```
|
||||||
|
|
||||||
文件:`Core/Src/tim.c`、`Core/Src/main.c`、`Core/Src/stm32f1xx_it.c`
|
固定索引映射:
|
||||||
|
|
||||||
已实现:
|
- `0 = S1`
|
||||||
|
- `1 = S2`
|
||||||
|
- `2 = C1`
|
||||||
|
- `3 = C2`
|
||||||
|
|
||||||
1. 使用 `TIM4` 作为 LED 心跳定时器
|
字段职责:
|
||||||
2. `TIM4` 时钟来自 `APB1 Timer Clock = 72 MHz`
|
|
||||||
3. 通过 `Prescaler = 7199` 和 `Period = 9` 生成 `1 ms` 更新中断
|
|
||||||
4. 在 `HAL_TIM_PeriodElapsedCallback()` 中进行 1ms 计数
|
|
||||||
5. 累计 `1000` 次后翻转一次 `PC13`,形成 `1 s` 闪烁节拍
|
|
||||||
|
|
||||||
当前实现说明:
|
- `EN`:实例启用状态
|
||||||
|
- `LPORT`:本地端口
|
||||||
|
- `RIP / RPORT`:对端地址与端口
|
||||||
|
- `UART`:对应业务数据口
|
||||||
|
|
||||||
1. `PC13` 为低电平点亮、高电平熄灭
|
说明:
|
||||||
2. 当前逻辑为每 `1 s` 翻转一次 LED 状态,即完整亮灭周期为 `2 s`
|
|
||||||
3. 若后续需要“每 1 秒完整闪烁一次”,可改为 `500 ms` 翻转一次
|
|
||||||
|
|
||||||
## 六、lwIP 配置策略
|
- `Server` 与 `Client` 共享同一记录结构
|
||||||
|
- `Server` 的 `RIP / RPORT` 可作为对端约束或预设
|
||||||
|
- `Client` 的 `RIP / RPORT` 表示远端目标
|
||||||
|
|
||||||
当前 `lwIP` 配置以适配 `R8T6` 资源为原则,核心策略如下:
|
## 六、模块职责调整
|
||||||
|
|
||||||
1. `NO_SYS = 1`
|
### 6.1 配置模块 `config.c/.h`
|
||||||
2. `LWIP_SOCKET = 0`
|
|
||||||
3. `LWIP_NETCONN = 0`
|
|
||||||
4. `LWIP_NETIF_API = 0`
|
|
||||||
5. `LWIP_DHCP = 0`
|
|
||||||
6. `LWIP_UDP = 0`
|
|
||||||
7. `LWIP_DNS = 0`
|
|
||||||
8. `LWIP_IGMP = 0`
|
|
||||||
9. 关闭 `lwIP` 平台诊断 `printf`
|
|
||||||
|
|
||||||
同时从 `MDK` 工程中移除了:
|
最终职责:
|
||||||
|
|
||||||
1. `FreeRTOS` 相关源码
|
1. 解析 `AT+MUX`
|
||||||
2. `lwIP api/socket/netconn/tcpip` 路线源码
|
2. 解析 `AT+NET`
|
||||||
3. `altcp / autoip / acd / dhcp / dns / igmp` 等当前不需要模块
|
3. 解析 `AT+LINK`
|
||||||
|
4. 加载与保存配置
|
||||||
|
5. 处理 `SAVE / RESET / DEFAULT`
|
||||||
|
|
||||||
## 七、主循环实际骨架
|
不再以历史展开式字段作为外部接口模型。
|
||||||
|
|
||||||
当前主循环逻辑可概括为:
|
### 6.2 UART 透传模块 `uart_trans.c/.h`
|
||||||
|
|
||||||
|
最终职责:
|
||||||
|
|
||||||
|
1. 保持 `USART2 / USART3` 的 `DMA + IDLE` 接收发送基线
|
||||||
|
2. 在 `MUX=0` 时执行普通透传
|
||||||
|
3. 在 `MUX=1` 时执行 MUX 帧收发
|
||||||
|
4. 将控制帧与业务数据帧分流
|
||||||
|
|
||||||
|
### 6.3 TCP Server / Client 模块
|
||||||
|
|
||||||
|
最终职责:
|
||||||
|
|
||||||
|
1. 不再从外部协议角度区分不同字段模型
|
||||||
|
2. 统一受 `LINK[idx]` 配置驱动
|
||||||
|
3. 由调度层决定实例与 UART 的数据交换路径
|
||||||
|
|
||||||
|
## 七、主循环实现方向
|
||||||
|
|
||||||
|
主循环仍保持裸机轮询风格:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
while (1)
|
while (1)
|
||||||
@@ -216,69 +194,37 @@ while (1)
|
|||||||
ethernetif_poll();
|
ethernetif_poll();
|
||||||
ethernetif_check_link();
|
ethernetif_check_link();
|
||||||
sys_check_timeouts();
|
sys_check_timeouts();
|
||||||
tcp_client_poll();
|
tcp_link_poll();
|
||||||
uart_trans_poll();
|
uart_mux_poll();
|
||||||
config_poll();
|
config_poll();
|
||||||
|
|
||||||
tcp_server <-> UART2;
|
route_dispatch();
|
||||||
tcp_client <-> UART3;
|
|
||||||
|
|
||||||
if (reset_requested) {
|
if (reset_requested) {
|
||||||
NVIC_SystemReset();
|
NVIC_SystemReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
HAL_IWDG_Refresh(&hiwdg);
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 八、当前构建状态
|
下一阶段实现要求:
|
||||||
|
|
||||||
### 8.1 MDK 构建命令
|
1. 统一由 `LINK[idx]` 驱动实例状态
|
||||||
|
2. 统一由 `MUX` 决定数据口承载模式
|
||||||
|
3. 统一由 `route_dispatch()` 按 `SRCID / DSTMASK` 分发
|
||||||
|
|
||||||
```bat
|
## 八、实现边界
|
||||||
"C:\Keil_v5\UV4\UV4.exe" -b "D:\code\STM32Project\TCP2UART\MDK-ARM\TCP2UART.uvprojx" -j0
|
|
||||||
```
|
|
||||||
|
|
||||||
### 8.2 当前结果
|
1. 保持单网卡静态网络模型
|
||||||
|
2. 不实现 DHCP
|
||||||
|
3. 不实现旧 `S1... / C1...` 外部协议字段
|
||||||
|
4. 不在文档中保留兼容层描述
|
||||||
|
5. 所有 AT 文本控制统一要求 `\r\n` 结束
|
||||||
|
|
||||||
当前构建结果:
|
## 九、文档一致性要求
|
||||||
|
|
||||||
1. `0 Error(s)`
|
后续实现、联调、测试与代码注释必须遵守以下统一口径:
|
||||||
2. `0 Warning(s)`
|
|
||||||
3. `Code=39988`
|
|
||||||
4. `RO-data=1272`
|
|
||||||
5. `RW-data=172`
|
|
||||||
6. `ZI-data=19188`
|
|
||||||
|
|
||||||
说明当前版本已经满足:
|
1. 对外协议只使用 `MUX / NET / LINK`
|
||||||
|
2. 控制帧只使用 `DSTMASK=0x00`
|
||||||
1. `STM32F103R8T6 64KB Flash` 约束
|
3. MUX 帧格式固定为 `SYNC | LEN_H | LEN_L | SRCID | DSTMASK | PAYLOAD | TAIL`
|
||||||
2. `20KB SRAM` 约束
|
4. AT 手册、需求说明、技术实现三份文档不得再出现历史展开式字段
|
||||||
3. `MDK-ARM` 可直接编译
|
|
||||||
|
|
||||||
## 九、当前已知限制与待验证项
|
|
||||||
|
|
||||||
### 9.1 功能限制
|
|
||||||
|
|
||||||
1. 当前使用静态 IP,不支持 DHCP
|
|
||||||
2. 目前未提供上板网络与串口吞吐测试结论
|
|
||||||
3. `config` 模块仍保留较重的字符串解析逻辑,但当前体积已可接受
|
|
||||||
|
|
||||||
### 9.2 上板验证重点
|
|
||||||
|
|
||||||
1. 验证 CH390 INT 极性与 EXTI 触发行为
|
|
||||||
2. 验证 `SPI1` 与 CH390 的稳定性
|
|
||||||
3. 验证 `TIM4` 1ms 中断稳定性与 `PC13` 1秒翻转节拍
|
|
||||||
4. 验证 `UART2/UART3 DMA + IDLE` 在长连续流量下的行为
|
|
||||||
5. 验证 TCP Server 与 TCP Client 双链路同时工作时的稳定性
|
|
||||||
6. 验证配置保存、复位、MAC 生效路径
|
|
||||||
|
|
||||||
## 十、后续建议
|
|
||||||
|
|
||||||
下一阶段建议按以下顺序推进:
|
|
||||||
|
|
||||||
1. 上板联调 CH390 链路与 RTT 输出
|
|
||||||
2. 验证 UART2/3 透传功能
|
|
||||||
3. 补充双向透传稳定性与丢包测试
|
|
||||||
4. 视需要继续优化 `config.c` 的体积与命令集
|
|
||||||
5. 若后续必须支持 DHCP,再单独评估资源预算
|
|
||||||
|
|||||||
@@ -1,95 +1,161 @@
|
|||||||
# TCP2UART 项目需求说明
|
# TCP2UART 项目需求说明
|
||||||
|
|
||||||
## 一、项目概述
|
## 一、项目目标
|
||||||
|
|
||||||
基于 `STM32F103R8T6` 和 `CH390D` 实现双链路 TCP 串口透传设备。设备提供一条 TCP Server 链路和一条 TCP Client 链路,分别与两路 UART 做双向透明传输,并通过 UART1 进行参数配置。
|
本项目基于 `STM32F103R8T6` 与 `CH390D` 实现一台多实例 TCP 与双串口数据透传设备。
|
||||||
|
|
||||||
当前项目实现路线已经固定为:
|
最终对外协议模型固定为:
|
||||||
|
|
||||||
1. `STM32CubeMX + HAL`
|
1. `MUX`:控制串口侧是否采用 MUX 承载
|
||||||
2. `bare-metal`
|
2. `NET`:全局静态网络配置
|
||||||
3. `lwIP RAW API + NO_SYS=1`
|
3. `LINK[idx]`:按实例索引组织的链路配置
|
||||||
4. `SEGGER RTT` 调试输出
|
|
||||||
|
|
||||||
不再采用 `FreeRTOS` 作为正式交付架构。
|
系统必须支持:
|
||||||
|
|
||||||
## 二、硬件平台
|
- `2` 路 TCP Server 实例
|
||||||
|
- `2` 路 TCP Client 实例
|
||||||
|
- `UART1` 作为 AT 配置口
|
||||||
|
- `UART2 / UART3` 作为业务数据口
|
||||||
|
|
||||||
| 项目 | 说明 |
|
## 二、硬件与软件边界
|
||||||
|
|
||||||
|
### 2.1 硬件边界
|
||||||
|
|
||||||
|
- 主控:`STM32F103R8T6`
|
||||||
|
- 以太网芯片:`CH390D`
|
||||||
|
- 网卡数量:`1`
|
||||||
|
- 配置口:`UART1`
|
||||||
|
- 数据口:`UART2`、`UART3`
|
||||||
|
|
||||||
|
### 2.2 软件边界
|
||||||
|
|
||||||
|
- 执行模型:`bare-metal`
|
||||||
|
- 网络协议栈:`lwIP RAW API + NO_SYS=1`
|
||||||
|
- 调试输出:`SEGGER RTT`
|
||||||
|
- 不采用 `FreeRTOS`
|
||||||
|
- 不采用 `socket/netconn`
|
||||||
|
- 不包含 DHCP 协议支持
|
||||||
|
|
||||||
|
## 三、最终协议需求
|
||||||
|
|
||||||
|
### 3.1 MUX 帧格式
|
||||||
|
|
||||||
|
所有 MUX 数据承载必须使用如下格式:
|
||||||
|
|
||||||
|
```text
|
||||||
|
SYNC | LEN_H | LEN_L | SRCID | DSTMASK | PAYLOAD | TAIL
|
||||||
|
```
|
||||||
|
|
||||||
|
要求:
|
||||||
|
|
||||||
|
- `DSTMASK != 0x00`:业务数据帧
|
||||||
|
- `DSTMASK = 0x00`:系统控制帧
|
||||||
|
- 系统控制帧承载 AT 文本命令
|
||||||
|
- AT 文本命令必须以 `\r\n` 结尾
|
||||||
|
|
||||||
|
### 3.2 统一端点编码
|
||||||
|
|
||||||
|
系统必须使用统一端点编码,同时覆盖 UART 与 TCP 逻辑实例:
|
||||||
|
|
||||||
|
| 端点 | 编码 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 主控芯片 | `STM32F103R8T6` |
|
| `C1` | `0x01` |
|
||||||
| 以太网芯片 | `CH390D` |
|
| `C2` | `0x02` |
|
||||||
| PCB 设计工具 | 立创 EDA |
|
| `UART2` | `0x04` |
|
||||||
| 串口通道 | `UART1 + UART2 + UART3` |
|
| `UART3` | `0x08` |
|
||||||
|
| `S1` | `0x10` |
|
||||||
|
| `S2` | `0x20` |
|
||||||
|
|
||||||
|
要求:
|
||||||
|
|
||||||
|
- `SRCID` 为单值
|
||||||
|
- `DSTMASK` 为位图
|
||||||
|
- `DSTMASK=0x00` 仅保留给系统控制帧
|
||||||
|
|
||||||
|
## 四、AT 接口需求
|
||||||
|
|
||||||
|
### 4.1 命令分类
|
||||||
|
|
||||||
|
AT 协议必须收敛为以下三类命令:
|
||||||
|
|
||||||
|
1. `AT+MUX`
|
||||||
|
2. `AT+NET`
|
||||||
|
3. `AT+LINK`
|
||||||
|
|
||||||
|
不再保留历史展开式实例字段命令。
|
||||||
|
|
||||||
|
### 4.2 MUX 命令需求
|
||||||
|
|
||||||
|
- `AT+MUX=0/1`:设置全局 MUX 模式
|
||||||
|
- `AT+MUX?`:查询当前 MUX 模式
|
||||||
|
|
||||||
|
### 4.3 NET 命令需求
|
||||||
|
|
||||||
|
`NET` 必须统一表达以下静态网络参数:
|
||||||
|
|
||||||
|
```text
|
||||||
|
IP,MASK,GW,MAC
|
||||||
|
```
|
||||||
|
|
||||||
说明:
|
说明:
|
||||||
|
|
||||||
1. `UART1` 用于配置口
|
- 设备只有一张网卡,因此本地 IP 不按实例拆分
|
||||||
2. `UART2` 对应 TCP Server 透传链路
|
- DHCP 不属于协议需求范围
|
||||||
3. `UART3` 对应 TCP Client 透传链路
|
|
||||||
|
|
||||||
## 三、软件平台
|
### 4.4 LINK 命令需求
|
||||||
|
|
||||||
| 项目 | 说明 |
|
`LINK[idx]` 必须统一表达如下字段:
|
||||||
|------|------|
|
|
||||||
| 开发环境 | `STM32CubeMX + HAL + MDK-ARM` |
|
|
||||||
| 执行模型 | 裸机主循环 + 中断驱动 |
|
|
||||||
| 协议栈 | `lwIP RAW API` |
|
|
||||||
| 调试输出 | `SEGGER RTT` |
|
|
||||||
|
|
||||||
## 四、核心功能需求
|
```text
|
||||||
|
EN,LPORT,RIP,RPORT,UART
|
||||||
|
```
|
||||||
|
|
||||||
### 4.1 双链路 TCP 通信
|
要求:
|
||||||
|
|
||||||
- `Server` 链路:设备作为 TCP Server,监听指定端口
|
- `idx` 固定映射四个实例:`0=S1`、`1=S2`、`2=C1`、`3=C2`
|
||||||
- `Client` 链路:设备作为 TCP Client,主动连接远程服务器
|
- `Server` 与 `Client` 共用同一条 `LINK` 配置模型
|
||||||
- 两条链路共享同一个设备 IP 地址
|
- `LPORT` 必须可配置
|
||||||
|
- `RIP / RPORT` 必须可配置
|
||||||
|
- `UART` 必须可配置
|
||||||
|
|
||||||
### 4.2 串口透传
|
## 五、功能需求
|
||||||
|
|
||||||
- `Server` 链路数据 <=> `UART2` 双向透传
|
### 5.1 TCP 功能
|
||||||
- `Client` 链路数据 <=> `UART3` 双向透传
|
|
||||||
- 仅透传 TCP Payload,不解析业务层协议
|
|
||||||
|
|
||||||
### 4.3 参数配置
|
- 支持 `2` 路 Server
|
||||||
|
- 支持 `2` 路 Client
|
||||||
|
- 每个实例通过 `LINK[idx]` 配置其本地端口、对端地址、对端端口和串口路由
|
||||||
|
|
||||||
- 通过 `UART1` 配置网络与串口参数
|
### 5.2 串口透传功能
|
||||||
- 配置参数掉电保存
|
|
||||||
- 支持设备复位后按保存配置生效
|
|
||||||
|
|
||||||
### 4.4 调试与维护
|
- `UART2 / UART3` 支持普通透传模式与 MUX 透传模式
|
||||||
|
- 当需要多实例共享数据口时,必须启用 MUX 模式
|
||||||
|
- 业务数据流向由 `SRCID / DSTMASK` 决定
|
||||||
|
|
||||||
- 调试输出统一走 `SEGGER RTT`
|
### 5.3 系统控制功能
|
||||||
- 工程需可在 `MDK-ARM` 下直接构建
|
|
||||||
|
|
||||||
## 五、当前实现边界
|
- 系统控制帧由 `DSTMASK=0x00` 表示
|
||||||
|
- 系统控制帧进入 AT 解析路径
|
||||||
|
- 控制文本必须以 `\r\n` 结束
|
||||||
|
|
||||||
基于 `STM32F103R8T6` 的 `64KB Flash / 20KB SRAM` 约束,当前交付版本约束如下:
|
### 5.4 参数保存功能
|
||||||
|
|
||||||
1. 使用静态 IP
|
- 参数修改后支持 `SAVE`
|
||||||
2. 当前构建不支持 DHCP
|
- 支持 `RESET` 后按保存配置启动
|
||||||
3. 不使用 `lwIP socket/netconn`
|
- 支持恢复默认配置
|
||||||
4. 不使用 `FreeRTOS`
|
|
||||||
|
|
||||||
这不是降级,而是基于资源约束后的正式实现路线。
|
## 六、非功能需求
|
||||||
|
|
||||||
## 六、数据可靠性要求
|
1. 满足 `STM32F103R8T6` 的 `64KB Flash / 20KB SRAM` 约束
|
||||||
|
2. 工程可在 `MDK-ARM` 下构建
|
||||||
|
3. 调试输出统一使用 `SEGGER RTT`
|
||||||
|
4. 不引入 DHCP、DNS、UDP 等当前非目标协议
|
||||||
|
|
||||||
- 目标是保证 TCP 数据与串口数据双向透传稳定工作
|
## 七、验收口径
|
||||||
- 需要后续补充上板联调后的丢包率测试方案与结果
|
|
||||||
- 需要验证双链路同时工作时的稳定性
|
|
||||||
|
|
||||||
## 七、交付物
|
验收时以下几点必须同时成立:
|
||||||
|
|
||||||
1. 原理图及 PCB 设计文件
|
1. 文档只使用 `MUX / NET / LINK` 作为最终协议模型
|
||||||
2. STM32 固件源码
|
2. 文档不再出现历史 `S1... / C1...` 外部字段
|
||||||
3. `CubeMX` 工程与 `MDK-ARM` 工程
|
3. 串口控制文本统一规定为 `\r\n` 结束
|
||||||
4. 使用说明文档
|
4. MUX 帧格式与端点编码在需求、手册、技术实现三份文档中表述一致
|
||||||
5. 后续补充的透传与丢包测试结果
|
|
||||||
|
|
||||||
## 八、约束条件
|
|
||||||
|
|
||||||
1. 通信协议为标准 TCP/IP
|
|
||||||
2. 串口透传为纯数据透传,不解析上层协议
|
|
||||||
3. 当前正式目标器件为 `STM32F103R8T6`
|
|
||||||
4. 所有正式实现应服从 `64KB Flash / 20KB SRAM` 约束
|
|
||||||
|
|||||||
Reference in New Issue
Block a user