增加文件系统
This commit is contained in:
@@ -4,15 +4,16 @@
|
||||
|
||||
1. [概述](#1-概述)
|
||||
2. [架构说明](#2-架构说明)
|
||||
3. [快速开始](#3-快速开始)
|
||||
4. [TCP 编程](#4-tcp-编程)
|
||||
5. [UDP 编程](#5-udp-编程)
|
||||
6. [I/O 多路复用](#6-io-多路复用)
|
||||
7. [事件回调机制](#7-事件回调机制)
|
||||
8. [字节序处理](#8-字节序处理)
|
||||
9. [API 参考](#9-api-参考)
|
||||
10. [完整示例](#10-完整示例)
|
||||
11. [常见问题](#11-常见问题)
|
||||
3. [线程安全设计](#3-线程安全设计)
|
||||
4. [快速开始](#4-快速开始)
|
||||
5. [TCP 编程](#5-tcp-编程)
|
||||
6. [UDP 编程](#6-udp-编程)
|
||||
7. [I/O 多路复用](#7-io-多路复用)
|
||||
8. [事件回调机制](#8-事件回调机制)
|
||||
9. [字节序处理](#9-字节序处理)
|
||||
10. [API 参考](#10-api-参考)
|
||||
11. [完整示例](#11-完整示例)
|
||||
12. [常见问题](#12-常见问题)
|
||||
|
||||
---
|
||||
|
||||
@@ -46,32 +47,42 @@
|
||||
|
||||
## 2. 架构说明
|
||||
|
||||
### 2.1 分层架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ 应用层 (App) │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ net_socket.h (BSD Socket API) │ ← 用户接口
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ net_socket.c + net_select.c (状态机+select) │ ← 核心逻辑
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ ch395f.c/h (底层驱动) │ ← 硬件驱动
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ CH395F 硬件 │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ 应用层任务 (多个 FreeRTOS Task) │
|
||||
│ net_send() / net_recv() / net_close() (线程安全封装) │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ 消息队列 (osMessageQueue) │
|
||||
│ 由 CubeMX 生成,在 MX_FREERTOS_Init() 中创建 │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ netTask (网络任务线程) │
|
||||
│ net_process_messages() + net_poll() — 串行化执行 │
|
||||
│ net_send_sock() / net_recv_sock() / net_close_sock() │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ net_socket.c (BSD Socket API 核心) │
|
||||
│ Socket 状态机 + CH395F 命令封装 │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ ch395f.c/h (底层 SPI 驱动) │
|
||||
│ 所有 SPI 事务必须由 netTask 发起 │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ CH395F 硬件 (SPI2) │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.1 文件说明
|
||||
### 2.2 文件说明
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `net_config.h` | 配置宏定义(最大 Socket 数、超时时间等) |
|
||||
| `net_types.h` | 类型定义(地址结构、Socket 控制块等) |
|
||||
| `net_socket.h` | BSD Socket API 头文件 |
|
||||
| `net_socket.c` | API 核心实现 |
|
||||
| `net_socket.h` | BSD Socket API 头文件,含 `net_msg_t` 消息类型定义 |
|
||||
| `net_socket.c` | API 核心实现,含消息队列处理、线程安全封装、内部 `_sock()` 版本 |
|
||||
| `net_select.h` | select/poll API 头文件 |
|
||||
| `net_select.c` | select/poll 实现 |
|
||||
|
||||
### 2.1a 底层驱动改进(v2.1)
|
||||
### 2.3 底层驱动改进(v2.1)
|
||||
|
||||
ch395f.c/h 已完成以下优化:
|
||||
|
||||
@@ -84,7 +95,7 @@ ch395f.c/h 已完成以下优化:
|
||||
| 新增函数 | `ch395f_set_arp()` / `ch395f_set_ttl()` / `ch395f_get_unreach_info()` / `ch395f_poll_cmd_status()` |
|
||||
| KeepAlive | 参数单位为毫秒(非秒),必须为 500 的倍数 |
|
||||
|
||||
### 2.2 Socket 状态机
|
||||
### 2.4 Socket 状态机
|
||||
|
||||
```
|
||||
net_socket()
|
||||
@@ -120,16 +131,86 @@ ch395f.c/h 已完成以下优化:
|
||||
|
||||
---
|
||||
|
||||
## 3. 快速开始
|
||||
## 3. 线程安全设计
|
||||
|
||||
### 3.1 包含头文件
|
||||
### 3.1 设计背景
|
||||
|
||||
NET 层(`net_socket.c` + `ch395f.c`)不是线程安全的:
|
||||
- Socket 控制块(`s_net_socks[]`)无互斥保护
|
||||
- CH395F SPI 事务不重入(STM32 HAL SPI 无内部锁)
|
||||
- 状态机在多个任务中并发访问会崩溃
|
||||
|
||||
引入消息队列将所有的 CH395F 操作串行化到 `netTask` 中执行。
|
||||
|
||||
### 3.2 消息队列模式
|
||||
|
||||
```
|
||||
App Task A App Task B netTask
|
||||
| | |
|
||||
net_send() net_recv() net_process_messages()
|
||||
| | + net_poll()
|
||||
▼ ▼ │
|
||||
┌─────────┐ ┌─────────┐ │
|
||||
│send msg │ │recv msg │ │
|
||||
└────┬────┘ └────┬────┘ │
|
||||
│ │ │
|
||||
└─────────┬─────────┘ │
|
||||
▼ ▼
|
||||
┌──────────────────┐ ┌──────────────┐
|
||||
│ 消息队列 │──────────│ 取出消息 │
|
||||
│ osMessageQueue │ dequeue │ 执行操作 │
|
||||
└──────────────────┘ │ 写结果 │
|
||||
▲ │ 通知调用方 │
|
||||
│ └──────┬───────┘
|
||||
│ ulTaskNotifyTake() │
|
||||
│ (阻塞等待结果) │ xTaskNotifyGive()
|
||||
│ │
|
||||
┌─────┴──────┐ │
|
||||
│ 返回结果给 │◄──────────────────────┘
|
||||
│ 调用方 │
|
||||
└────────────┘
|
||||
```
|
||||
|
||||
### 3.3 关键规则
|
||||
|
||||
| 规则 | 说明 |
|
||||
|------|------|
|
||||
| **`netTask` 外调用** | 使用 `net_send()` / `net_recv()` / `net_close()` 公共 API |
|
||||
| **`netTask` 内调用** | 使用 `net_send_sock()` / `net_recv_sock()` / `net_close_sock()` 内部 API |
|
||||
| **创建/绑定/监听** | 在 `main()` 初始化阶段完成(`osKernelStart()` 之前),无需队列 |
|
||||
| **`net_poll()` 和 `net_process_messages()`** | 必须在 `netTask` 主循环中调用 |
|
||||
|
||||
### 3.4 内部 API 说明
|
||||
|
||||
内部 API(带 `_sock` 后缀)直接操作 Socket 控制块和 CH395F,不经过消息队列。用于 `netTask` 中的网络业务逻辑,避免向自身发送消息造成死锁:
|
||||
|
||||
```c
|
||||
/* 在 netTask 中直接操作 Socket,绕过消息队列 */
|
||||
int net_send_sock(net_sock_t *p_sock, const void *buf, int len);
|
||||
int net_recv_sock(net_sock_t *p_sock, void *buf, int len, int flags);
|
||||
int net_close_sock(net_sock_t *p_sock);
|
||||
```
|
||||
|
||||
### 3.5 注意事项
|
||||
|
||||
- **消息队列由 CubeMX 创建**:在 `MX_FREERTOS_Init()`(`osKernelInitialize()` 之后)中调用 `osMessageQueueNew(8, sizeof(net_msg_t), NULL)` 创建
|
||||
- **队列句柄全局可见**:通过 `extern osMessageQueueId_t netMsgQueueHandle` 引用
|
||||
- **队列满时**:`net_send()` / `net_recv()` 会阻塞等待,直到 `netTask` 处理完消息腾出空间
|
||||
- **`netTask` 不可向自身发消息**:`netTask` 中直接调 `net_send()` 会死锁(等待自己处理消息),必须在 `netTask` 中使用 `net_send_sock()` 等内部版本
|
||||
- **`net_socket()` / `net_bind()` / `net_listen()` 无需线程安全**:这些仅在初始化阶段调用,消息队列仅保护数据收发和连接关闭
|
||||
|
||||
---
|
||||
|
||||
## 4. 快速开始
|
||||
|
||||
### 4.1 包含头文件
|
||||
|
||||
```c
|
||||
#include "net_socket.h"
|
||||
#include "net_select.h"
|
||||
```
|
||||
|
||||
### 3.2 初始化网络
|
||||
### 4.2 初始化网络
|
||||
|
||||
```c
|
||||
int main(void)
|
||||
@@ -154,29 +235,55 @@ int main(void)
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 主循环要求
|
||||
### 4.3 主循环要求
|
||||
|
||||
**重要**:`net_poll()` 必须在主循环中定期调用,它负责:
|
||||
- 轮询 CH395F INT# 引脚电平(GPIO 轮询,无需 EXTI 中断)
|
||||
- 通过 `GET_GLOB_INT_STATUS_ALL`(2字节版)读取所有 8 个 Socket 的中断状态
|
||||
- 更新所有 Socket 的连接状态(CONNECT / DISCONNECT / RECV / TIMEOUT)
|
||||
- 触发事件回调
|
||||
- 处理数据接收
|
||||
**重要**:本项目使用 FreeRTOS 多任务架构,`net_poll()` 和 `net_process_messages()` 均在 `netTask`(FreeRTOS 线程)中定期调用,而非主循环。
|
||||
|
||||
- **主循环**:系统初始化完成后进入空循环,等待 `osKernelStart()` 启动调度器
|
||||
- **netTask**:在任务函数中持续运行,每 10ms 调用一次 `net_poll()` + `net_process_messages()`,负责:
|
||||
- 轮询 CH395F INT# 引脚电平(GPIO 轮询,无需 EXTI 中断)
|
||||
- 通过 `GET_GLOB_INT_STATUS_ALL`(2字节版)读取所有 8 个 Socket 的中断状态
|
||||
- 更新所有 Socket 的连接状态(CONNECT / DISCONNECT / RECV / TIMEOUT)
|
||||
- 触发事件回调
|
||||
- 处理数据接收
|
||||
|
||||
```c
|
||||
while (1)
|
||||
int main(void)
|
||||
{
|
||||
net_poll(); /* 建议至少每 10ms 调用一次 */
|
||||
/* 外设初始化... */
|
||||
|
||||
/* 其他应用代码... */
|
||||
/* BSD Socket API 初始化 */
|
||||
net_init("192.168.1.100", "255.255.255.0", "192.168.1.1");
|
||||
|
||||
/* 创建 Socket、绑定、监听... */
|
||||
|
||||
osKernelInitialize();
|
||||
MX_FREERTOS_Init(); /* 创建 netTask 等 FreeRTOS 对象 */
|
||||
osKernelStart(); /* 启动调度器 */
|
||||
|
||||
while (1) { } /* 空循环,控制流已交给调度器 */
|
||||
}
|
||||
|
||||
/* netTask 中(freertos.c): */
|
||||
void StartNetTask(void *argument)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
net_poll(); /* CH395F 中断轮询 + Socket 状态更新 */
|
||||
net_process_messages(); /* 处理其他任务发送的网络操作请求 */
|
||||
|
||||
/* 应用层网络业务逻辑... */
|
||||
|
||||
osDelay(10); /* 10ms 周期 */
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. TCP 编程
|
||||
## 5. TCP 编程
|
||||
|
||||
### 4.1 TCP Server(单连接)
|
||||
### 5.1 TCP Server(单连接)
|
||||
|
||||
单连接模式下,1 个 Socket 既做监听又做数据通信:
|
||||
|
||||
@@ -258,7 +365,7 @@ void tcp_server_single(void)
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 TCP Server(多连接)
|
||||
### 5.2 TCP Server(多连接)
|
||||
|
||||
多连接模式使用 1 个监听 Socket + N 个数据 Socket,支持并发连接:
|
||||
|
||||
@@ -348,7 +455,7 @@ void tcp_server_multi(void)
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 TCP Client
|
||||
### 5.3 TCP Client
|
||||
|
||||
```c
|
||||
#include "net_socket.h"
|
||||
@@ -413,9 +520,9 @@ void tcp_client(void)
|
||||
|
||||
---
|
||||
|
||||
## 5. UDP 编程
|
||||
## 6. UDP 编程
|
||||
|
||||
### 5.1 UDP Client
|
||||
### 6.1 UDP Client
|
||||
|
||||
```c
|
||||
#include "net_socket.h"
|
||||
@@ -472,7 +579,7 @@ void udp_client(void)
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 UDP Server
|
||||
### 6.2 UDP Server
|
||||
|
||||
```c
|
||||
#include "net_socket.h"
|
||||
@@ -531,9 +638,9 @@ void udp_server(void)
|
||||
|
||||
---
|
||||
|
||||
## 6. I/O 多路复用
|
||||
## 7. I/O 多路复用
|
||||
|
||||
### 6.1 select 使用
|
||||
### 7.1 select 使用
|
||||
|
||||
`net_select()` 可以同时监控多个 Socket 的读写事件:
|
||||
|
||||
@@ -578,7 +685,7 @@ void select_example(void)
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 poll 使用
|
||||
### 7.2 poll 使用
|
||||
|
||||
`net_poll_events()` 使用 `pollfd` 结构体数组:
|
||||
|
||||
@@ -616,7 +723,7 @@ void poll_example(void)
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 事件掩码说明
|
||||
### 7.3 事件掩码说明
|
||||
|
||||
| 事件 | 说明 |
|
||||
|------|------|
|
||||
@@ -628,11 +735,11 @@ void poll_example(void)
|
||||
|
||||
---
|
||||
|
||||
## 7. 事件回调机制
|
||||
## 8. 事件回调机制
|
||||
|
||||
可以通过注册回调函数,异步获取 Socket 事件通知:
|
||||
|
||||
### 7.1 定义回调函数
|
||||
### 8.1 定义回调函数
|
||||
|
||||
```c
|
||||
#include "net_socket.h"
|
||||
@@ -669,7 +776,7 @@ void my_event_callback(int sockfd, net_event_t event, void *arg)
|
||||
}
|
||||
```
|
||||
|
||||
### 7.2 注册回调
|
||||
### 8.2 注册回调
|
||||
|
||||
```c
|
||||
void event_callback_example(void)
|
||||
@@ -691,11 +798,11 @@ void event_callback_example(void)
|
||||
|
||||
---
|
||||
|
||||
## 8. 字节序处理
|
||||
## 9. 字节序处理
|
||||
|
||||
网络协议使用**大端序**(网络字节序),而 STM32 使用**小端序**(主机字节序)。发送和接收数据时需要进行字节序转换。
|
||||
|
||||
### 8.1 端口号转换
|
||||
### 9.1 端口号转换
|
||||
|
||||
```c
|
||||
uint16_t port = 8080;
|
||||
@@ -707,7 +814,7 @@ uint16_t net_port = net_htons(port);
|
||||
uint16_t host_port = net_ntohs(net_port);
|
||||
```
|
||||
|
||||
### 8.2 IP 地址转换
|
||||
### 9.2 IP 地址转换
|
||||
|
||||
```c
|
||||
/* 字符串转网络序 IP */
|
||||
@@ -718,7 +825,7 @@ char ip_str[16];
|
||||
net_inet_ntoa(ip, ip_str); /* ip_str = "192.168.1.100" */
|
||||
```
|
||||
|
||||
### 8.3 填充地址结构体
|
||||
### 9.3 填充地址结构体
|
||||
|
||||
```c
|
||||
struct net_sockaddr_in addr;
|
||||
@@ -733,7 +840,7 @@ addr.sin_addr.s_addr = net_inet_addr("0.0.0.0");
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
```
|
||||
|
||||
### 8.4 读取地址信息
|
||||
### 9.4 读取地址信息
|
||||
|
||||
```c
|
||||
struct net_sockaddr_in client_addr;
|
||||
@@ -757,9 +864,9 @@ if (client_sock >= 0)
|
||||
|
||||
---
|
||||
|
||||
## 9. API 参考
|
||||
## 10. API 参考
|
||||
|
||||
### 9.1 核心 API
|
||||
### 10.1 核心 API
|
||||
|
||||
#### net_init - 网络初始化
|
||||
|
||||
@@ -898,7 +1005,41 @@ int net_recvfrom(int sockfd, void *buf, int len, int flags,
|
||||
int net_close(int sockfd);
|
||||
```
|
||||
|
||||
### 9.2 辅助 API
|
||||
### 10.2 消息队列 API
|
||||
|
||||
#### net_process_messages - 处理消息队列
|
||||
|
||||
```c
|
||||
void net_process_messages(void);
|
||||
```
|
||||
|
||||
处理其他任务通过 `net_send()` / `net_recv()` / `net_close()` 发送到消息队列的请求。必须在 `netTask` 主循环中调用。
|
||||
|
||||
#### net_send_sock - 发送数据(内部版本)
|
||||
|
||||
```c
|
||||
int net_send_sock(net_sock_t *p_sock, const void *buf, int len);
|
||||
```
|
||||
|
||||
直接操作 Socket 控制块进行发送,不经过消息队列。**只能在 `netTask` 中调用**,用于 `netTask` 内的网络业务逻辑。
|
||||
|
||||
#### net_recv_sock - 接收数据(内部版本)
|
||||
|
||||
```c
|
||||
int net_recv_sock(net_sock_t *p_sock, void *buf, int len, int flags);
|
||||
```
|
||||
|
||||
直接操作 Socket 控制块进行接收,不经过消息队列。**只能在 `netTask` 中调用**。
|
||||
|
||||
#### net_close_sock - 关闭 Socket(内部版本)
|
||||
|
||||
```c
|
||||
int net_close_sock(net_sock_t *p_sock);
|
||||
```
|
||||
|
||||
直接操作 Socket 控制块关闭连接,不经过消息队列。**只能在 `netTask` 中调用**。
|
||||
|
||||
### 10.3 辅助 API
|
||||
|
||||
#### net_set_event_cb - 注册事件回调
|
||||
|
||||
@@ -918,7 +1059,7 @@ net_sock_t *net_get_sock(int sockfd);
|
||||
int net_get_errno(void);
|
||||
```
|
||||
|
||||
### 9.3 字节序转换
|
||||
### 10.4 字节序转换
|
||||
|
||||
```c
|
||||
uint16_t net_htons(uint16_t hostshort); /* 主机序 -> 网络序 */
|
||||
@@ -929,7 +1070,7 @@ uint32_t net_inet_addr(const char *cp); /* 字符串 -> 网络序 IP */
|
||||
char *net_inet_ntoa(uint32_t addr, char *buf); /* 网络序 IP -> 字符串 */
|
||||
```
|
||||
|
||||
### 9.4 select/poll
|
||||
### 10.5 select/poll
|
||||
|
||||
```c
|
||||
int net_select(int nfds, net_fd_set *readfds, net_fd_set *writefds,
|
||||
@@ -938,7 +1079,7 @@ int net_select(int nfds, net_fd_set *readfds, net_fd_set *writefds,
|
||||
int net_poll_events(net_pollfd *fds, int nfds, int timeout);
|
||||
```
|
||||
|
||||
### 9.5 fd_set 操作宏
|
||||
### 10.6 fd_set 操作宏
|
||||
|
||||
```c
|
||||
NET_FD_ZERO(fdset) /* 清空集合 */
|
||||
@@ -949,9 +1090,9 @@ NET_FD_ISSET(fd, fdset) /* 检查 fd 是否在集合中 */
|
||||
|
||||
---
|
||||
|
||||
## 10. 完整示例
|
||||
## 11. 完整示例
|
||||
|
||||
### 10.1 TCP Echo Server(多连接)
|
||||
### 11.1 TCP Echo Server(多连接)
|
||||
|
||||
```c
|
||||
#include "net_socket.h"
|
||||
@@ -1047,7 +1188,7 @@ void tcp_echo_server(void)
|
||||
}
|
||||
```
|
||||
|
||||
### 10.2 TCP Client 自动重连
|
||||
### 11.2 TCP Client 自动重连
|
||||
|
||||
```c
|
||||
#include "net_socket.h"
|
||||
@@ -1135,22 +1276,22 @@ void tcp_client_reconnect(void)
|
||||
|
||||
---
|
||||
|
||||
## 11. 常见问题
|
||||
## 12. 常见问题
|
||||
|
||||
### 11.1 net_poll() 必须调用吗?
|
||||
### 12.1 net_poll() 必须调用吗?
|
||||
|
||||
**是的**。`net_poll()` 负责轮询 CH395F 的中断状态并更新所有 Socket 的状态。如果不调用,连接状态不会更新,数据也无法接收。
|
||||
|
||||
建议至少每 10ms 调用一次。
|
||||
|
||||
### 11.2 Socket 数量有限制吗?
|
||||
### 12.2 Socket 数量有限制吗?
|
||||
|
||||
是的,CH395F 最多支持 8 个 Socket(索引 0~7)。其中:
|
||||
- TCP Server 多连接模式:1 个监听 Socket + 最多 7 个数据 Socket
|
||||
- TCP Client:最多 8 个
|
||||
- UDP:最多 8 个
|
||||
|
||||
### 11.3 TCP Server 多连接模式有什么要求?
|
||||
### 12.3 TCP Server 多连接模式有什么要求?
|
||||
|
||||
**模式工作原理:**
|
||||
- CH395F 多连接模式下,Socket 0 专职监听,Socket 1~7 由芯片自动分配
|
||||
@@ -1160,13 +1301,13 @@ void tcp_client_reconnect(void)
|
||||
**硬件要求:**
|
||||
- CH395F 固件版本 >= 0x44(支持 Socket 4~7)
|
||||
|
||||
### 11.4 发送数据有什么限制?
|
||||
### 12.4 发送数据有什么限制?
|
||||
|
||||
- 每次发送的数据长度不能超过 `NET_SEND_BUF_SIZE`(默认 4096 字节)
|
||||
- TCP 需要等待发送缓冲区空闲(`send_ready` 标志)
|
||||
- UDP 单次发送最大 1460 字节
|
||||
|
||||
### 11.5 如何处理连接断开?
|
||||
### 12.5 如何处理连接断开?
|
||||
|
||||
**方式一:阻塞接收**
|
||||
```c
|
||||
@@ -1197,7 +1338,7 @@ void callback(int sockfd, net_event_t event, void *arg)
|
||||
}
|
||||
```
|
||||
|
||||
### 11.6 select 和 poll 的区别?
|
||||
### 12.6 select 和 poll 的区别?
|
||||
|
||||
| 特性 | select | poll |
|
||||
|------|--------|------|
|
||||
@@ -1207,7 +1348,7 @@ void callback(int sockfd, net_event_t event, void *arg)
|
||||
|
||||
两者功能相同,推荐使用 `net_select()`,更接近 Linux 编程习惯。
|
||||
|
||||
### 11.7 错误码说明
|
||||
### 12.7 错误码说明
|
||||
|
||||
| 错误码 | 宏定义 | 说明 |
|
||||
|--------|--------|------|
|
||||
@@ -1224,7 +1365,7 @@ void callback(int sockfd, net_event_t event, void *arg)
|
||||
| -10 | NET_ERR_NOSPACE | 发送缓冲区无空间 |
|
||||
| -11 | NET_ERR_BUSY | 操作未完成(BUSY) |
|
||||
|
||||
### 11.8 CH395F 命令执行状态码
|
||||
### 12.8 CH395F 命令执行状态码
|
||||
|
||||
CH395F 底层驱动返回的状态码(`ch395f_get_cmd_status()` 或 `ch395f_poll_cmd_status()`):
|
||||
|
||||
@@ -1249,7 +1390,7 @@ CH395F 底层驱动返回的状态码(`ch395f_get_cmd_status()` 或 `ch395f_po
|
||||
| 0x5F | CH395F_CMD_RET_ABORT | 命令中止 |
|
||||
| 0xFA | CH395F_ERR_UNKNOW | 未知错误 |
|
||||
|
||||
### 11.9 故障排查
|
||||
### 12.9 故障排查
|
||||
|
||||
**问题:TCP Client 连接超时,无 CONNECT 中断**
|
||||
- 检查多连接模式下数据 Socket(1~7)是否设置了 `SET_PROTO_TCP` + `SET_SOUR_PORT`(与监听端口相同)
|
||||
@@ -1266,3 +1407,21 @@ CH395F 底层驱动返回的状态码(`ch395f_get_cmd_status()` 或 `ch395f_po
|
||||
**问题:CH395F 初始化超时**
|
||||
- 芯片 INIT 需要约 200ms,轮询间隔应为 20ms(过短的查询可能干扰内部处理)
|
||||
- 超时值建议设为 4 秒以上
|
||||
|
||||
### 12.10 线程安全问题
|
||||
|
||||
**问题:`net_send()` / `net_recv()` / `net_close()` 可以在任意任务中调用吗?**
|
||||
|
||||
可以。这些公共 API 通过消息队列将请求委托给 `netTask` 串行执行,自身阻塞等待结果,是线程安全的。
|
||||
|
||||
**问题:`netTask` 内可以直接调用 `net_send()` / `net_recv()` 吗?**
|
||||
|
||||
不可以。`netTask` 中必须使用 `net_send_sock()` / `net_recv_sock()` 等内部版本,否则会死锁(任务向自身发送消息,永远无人处理)。内部版本直接操作 Socket 控制块,不经过消息队列。
|
||||
|
||||
**问题:`net_socket()` / `net_bind()` / `net_listen()` 线程安全吗?**
|
||||
|
||||
这些函数仅在系统初始化阶段(`osKernelStart()` 之前)调用,不存在并发访问。消息队列仅保护数据收发和连接关闭操作。
|
||||
|
||||
**问题:消息队列由谁创建?在哪里创建?**
|
||||
|
||||
由 CubeMX 在 `MX_FREERTOS_Init()` 中创建(`osMessageQueueNew(8, sizeof(net_msg_t), NULL)`),在 `osKernelInitialize()` 之后执行,确保 FreeRTOS 内核已就绪。
|
||||
|
||||
Reference in New Issue
Block a user