开启正文前先来一个关于怎么使用msdk做该文章相关内容(wifi)开发的小教程大佬可以跳过~~~
先在 https://www.iceasy.com/cloud/RISC-V?pid=1907770165638025244 下载:
解压后获得以下内容:
打开GD32EmbeddedBuilder软件,依次点击左上角的 file->>open projects from file system
接下来点击directory找到刚刚下载的文件中的路径GD32VW55x_RELEASE_V1.0.3a\GD32VW55x_RELEASE_V1.0.3a\MSDK\examples\wifi\softap_tcp_server\Eclipse_project
最后点击finish即可导入项目
相关的oled代码在我的另一个测评中有说明 https://www.iceasy.com/review/1944795609719500802
导入oled相关代码到前面操作导入的项目中编译即可适用下文的代码了
好的接下来才是正文
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
一、开发基础:核心 API 与硬件资源梳理
在开发前需明确开发指南中的关键 API 与硬件资源映射关系,这是功能实现的核心依据
这款开发板相比前代增加了pb2口连接的led灯和pb11口连接的按钮,在本文中也进行测试使用
1.1 核心 API 选型(源自开发指南)
功能模块 | 关键 API(开发指南章节) | 作用描述 |
---|---|---|
SoftAP 管理 | wifi_management_ap_start (4.4.8 节) | 启动 SoftAP,支持配置 SSID、密码、信道、加密模式 |
wifi_management_ap_stop (4.4.10 节) | 停止 SoftAP,断开所有客户端连接 | |
wifi_vif_is_softap (4.2.16 节) | 检测指定 VIF(虚拟接口)是否为 SoftAP 模式,判断 AP 启停状态 | |
TCP 服务器 | LwIP 套接字 API(3.1 节) | 含 socket /bind /listen /accept /recv /send ,实现 TCP 数据收发 |
RTOS 任务管理 | sys_task_create_dynamic (2.2.2 节) | 动态创建独立任务,避免单任务阻塞(如 TCP 与按键检测拆分) |
sys_task_delete (2.2.4 节) | 删除任务,释放资源 | |
系统延时 | sys_ms_sleep (2.4.3 节) | 任务级延时,释放 CPU 资源,符合 RTOS 调度逻辑 |
1.2 硬件资源定义
硬件接口 | 功能映射 | 关键配置(开发指南隐含 GPIO 逻辑) |
---|---|---|
PB2(输出) | 测试灯 | 推挽输出模式,这边是做了一个定时翻转电平功能 |
PB11(输入) | AP 启停切换按键 | 上拉输入模式,按下时电平为低 |
OLED(I2C/SPI) | 信息显示 | 显示 SSID、连接设备数、AP 状态(ok/false) |
二、实战开发:分模块实现与代码解析
2.1 硬件初始化:GPIO 与 OLED 配置
2.1.1 PB2 指示灯与 PB11 按键初始化
遵循开发指南 “Low level 层外设操作” 逻辑(1.1 节),先使能 GPIO 时钟,再配置引脚模式:
#include "gd32vw55x_gpio.h"
#include "gd32vw55x_rcu.h"
// PB2 指示灯初始化(推挽输出)
static void pb2_led_init(void) {
rcu_periph_clock_enable(RCU_GPIOB); // 使能 GPIOB 时钟
gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_2); // 推挽输出
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_2);
gpio_bit_reset(GPIOB, GPIO_PIN_2); // 初始灭灯
}
// PB11 按键初始化(上拉输入)
static void pb11_key_init(void) {
rcu_periph_clock_enable(RCU_GPIOB);
gpio_mode_set(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_11); // 上拉输入
}
// 按键扫描(可带消抖)
uint8_t
key_scan(
void
) {
static
uint8_t
key_up = 1;
// 按键松开标志
if
(key_up && (
gpio_input_bit_get
(GPIOB, GPIO_PIN_11) == 0)) {
// delay_1ms(1); // 消抖
if
(
gpio_input_bit_get
(GPIOB, GPIO_PIN_11) == 0) {
key_up = 0;
return
1;
// 按键按下
}
}
else
if
(
gpio_input_bit_get
(GPIOB, GPIO_PIN_11) == 1) {
key_up = 1;
// 按键松开
}
return
0;
}
2.1.2 OLED 初始化与显示函数
OLED 初始化需确保 I2C/SPI 引脚配置正确,显示逻辑需适配开发指南中 “任务内周期性刷新” 的要求使用rtos实现oled显示的任务调度:
static
void
oled_display_task(
void
*param) {
OLED_Init();
char
*state_str;
char
count_str[16];
uint8_t
connected_sta_num;
gpio_mode_set
(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_2);
gpio_output_options_set
(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_2);
while
(1) {
state_str = (g_softap_enabled== 0) ?
"false"
:
"ok"
;
connected_sta_num = get_ap_connected_sta_num();
sprintf
(count_str,
"%d"
, connected_sta_num);
OLED_Clear();
OLED_ShowString(10, 5,
"AP:"
, OLED_8X16);
OLED_ShowString(40, 5, SSID, OLED_8X16);
OLED_ShowString(10, 25,
"STA COUNT:"
, OLED_8X16);
OLED_ShowString(90, 25, count_str, OLED_8X16);
OLED_ShowString(10, 45,
"STATE:"
, OLED_8X16);
OLED_ShowString(65, 45, state_str, OLED_8X16);
OLED_Update();
gpio_bit_toggle
(GPIOB, GPIO_PIN_2);
vTaskDelay
(pdMS_TO_TICKS(1000));
// 依赖FreeRTOS,需确保包含task.h
}
}
2.2 Wi-Fi 功能实现:SoftAP 与连接设备检测
2.2.1 SoftAP 启动与停止
严格使用开发指南 4.4 节 API,配置 SSID 为 “GD32VW553_AP”,密码为 “12345678”,信道 11,用rtos实现ap的任务调度:
static
void
softap_tcp_server_task(
void
*param) {
int
ret = 0;
char
*ssid = SSID;
char
*password = PASSWORD;
uint8_t
channel = 11, is_hidden = 0;
wifi_ap_auth_mode_t
auth_mode =
AUTH_MODE_WPA2_WPA3
;
// -------------------------- 初始化 PB11 按键--------------------------
rcu_periph_clock_enable
(
RCU_GPIOB
);
// 使能 GPIOB 时钟
gpio_mode_set
(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_11);
// 上拉输入
// -------------------------- 初始启动 SoftAP --------------------------
if
(ssid == NULL) {
printf
(
"ssid can not be NULL!\r\n"
);
goto
exit;
}
if
(password && (
strlen
(password) == 0)) {
password = NULL;
auth_mode =
AUTH_MODE_OPEN
;
// 无密码时为开放模式
}
// 初始启动 SoftAP(文档 API:wifi_management_ap_start)
printf
(
"Start Wi-Fi softap.\r\n"
);
ret =
wifi_management_ap_start
(ssid, password, channel, auth_mode, is_hidden);
if
(ret != 0) {
printf
(
"Wi-Fi softap start failed.\r\n"
);
g_softap_enabled = 0;
// 启动失败,标记 AP 关闭
goto
exit;
}
else
{
printf
(
"SoftAP:%s successfully started!\r\n"
, ssid);
g_softap_enabled = 1;
// 启动成功,标记 AP 开启
}
// -------------------------- 按键检测与 AP 开关切换 --------------------------
while
(1) {
// 检测 PB11 按键按下(调用 key_scan 函数)
if
(key_scan() == 1) {
printf
(
"PB11 key pressed, toggle AP state...\r\n"
);
// 获取当前 AP 状态(基于文档 API:wifi_vif_is_softap)
//g_softap_enabled = get_softap_state();
if
(g_softap_enabled == 1) {
// 情况1:AP 已开启 → 关闭 AP
ret =
wifi_management_ap_stop
();
if
(ret == 0) {
printf
(
"SoftAP stopped successfully!\r\n"
);
g_softap_enabled = 0;
// 标记 AP 关闭
// 关闭 AP 后,停止 TCP 服务器
// (若需重新开启 AP 时重启 TCP,可在此处清理 TCP 资源)
}
else
{
printf
(
"SoftAP stop failed! Error code: %d\r\n"
, ret);
}
}
else
{
// 情况2:AP 已关闭 → 重新开启 AP(文档 API:wifi_management_ap_start)
ret =
wifi_management_ap_start
(ssid, password, channel, auth_mode, is_hidden);
if
(ret == 0) {
printf
(
"SoftAP restarted successfully!\r\n"
);
g_softap_enabled = 1;
// 标记 AP 开启
// 重新开启 AP 后,重启 TCP 服务器
//tcp_server_test();//这个是tcp服务器程序启动代码,这边和按钮开关ap功能冲突了所以注释掉,有大佬可以解决的话可以删掉最左侧的注释符
}
else
{
printf
(
"SoftAP restart failed! Error code: %d\r\n"
, ret);
}
}
}
// 延时 50ms,避免频繁检测占用 CPU(文档 RTOS 任务调度推荐)
vTaskDelay
(pdMS_TO_TICKS(50));
}
// -------------------------- 清理逻辑 --------------------------
exit:
printf
(
"the test has ended.\r\n"
);
// 退出前关闭 AP(文档推荐:资源需手动释放)
if
(g_softap_enabled == 1) {
wifi_management_ap_stop
();
}
sys_task_delete
(NULL);
}
2.2.2 连接设备数检测
基于开发指南 5.3 节 “启动 SoftAP” 示例,使用 macif_vif_ap_assoc_info_get 获取客户端 MAC 地址,统计设备数:
// 获取 SoftAP 已连接的客户端数量
static
uint8_t
get_ap_connected_sta_num(
void
) {
// 存储客户端MAC地址:CFG_STA_NUM个客户端,每个MAC占6字节
uint8_t
cli_mac[CFG_STA_NUM][6] = {0};
int
vif_idx = 0;
// SoftAP默认VIF序号为0(文档隐含)
int
cli_count = 0;
// 实际连接的客户端数量
// 调用函数:仅传2个参数(VIF序号 + MAC缓冲区指针)
cli_count =
macif_vif_ap_assoc_info_get
(
vif_idx,
// 参数1:SoftAP的VIF序号
(
uint16_t
*)cli_mac
// 参数2:MAC缓冲区(强制转换为uint16_t*匹配声明)
);
// (可选)打印客户端MAC和IP(修正指针类型)
for
(
int
i = 0; i < cli_count; i++) {
// 参数改为 uint8_t* 类型,匹配 dhcpd_find_ipaddr_by_macaddr 要求
uint32_t
client_ip =
dhcpd_find_ipaddr_by_macaddr
(cli_mac[i]);
printf
(
"Client[%d]: MAC=%02X:%02X:%02X:%02X:%02X:%02X, IP=%d.%d.%d.%d\r\n"
,
i,
cli_mac[i][0], cli_mac[i][1], cli_mac[i][2],
cli_mac[i][3], cli_mac[i][4], cli_mac[i][5],
(client_ip >> 0) & 0xFF, (client_ip >> 8) & 0xFF,
(client_ip >> 16) & 0xFF, (client_ip >> 24) & 0xFF);
}
// 确保返回值为uint8_t(客户端数量不会超过CFG_STA_NUM,通常≤8)
return
(cli_count > CFG_STA_NUM) ? CFG_STA_NUM : (
uint8_t
)cli_count;
}
2.3 TCP 服务器实现:独立任务设计
遵循开发指南 2.2 节 “任务功能拆分” 原则,将 TCP 服务器封装为独立任务,避免阻塞按键检测与 OLED 刷新:
static
void
tcp_server_test(
void
)
{
int
listen_fd = -1, ret, reuse, idx;
struct
sockaddr_in
server_addr;
int
cli_fd[
TCP_SERVER_LISTEN_NUM
];
struct
sockaddr_in
client_addr;
uint8_t
cli_count = 0;
char
recv_buf[128];
socklen_t
len;
struct
timeval
timeout;
fd_set
read_set;
int
max_fd_num = 0;
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if
(listen_fd < 0) {
printf
(
"Create tcp server socket fd error!\r\n"
);
goto
exit;
}
printf
(
"Create tcp server, fd: %d, port: %u.\r\n"
, listen_fd, TCP_SERVER_LISTEN_PORT);
reuse = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (
const
char
*)&reuse,
sizeof
(reuse));
sys_memset
(&server_addr, 0,
sizeof
(server_addr));
server_addr.
sin_family
= AF_INET;
server_addr.
sin_len
=
sizeof
(server_addr);
server_addr.
sin_port
= htons(TCP_SERVER_LISTEN_PORT);
server_addr.
sin_addr
.
s_addr
= htonl(INADDR_ANY);
ret = bind(listen_fd, (
struct
sockaddr
*)&server_addr,
sizeof
(server_addr));
if
(ret < 0) {
printf
(
"Bind tcp server socket fd error!\r\n"
);
goto
exit;
}
ret = listen(listen_fd,
TCP_SERVER_LISTEN_NUM
);
if
(ret != 0) {
printf
(
"Listen tcp server socket fd error!\r\n"
);
goto
exit;
}
for
(idx = 0; idx <
TCP_SERVER_LISTEN_NUM
; idx++)
cli_fd[idx] = -1;
timeout.
tv_sec
= 1;
timeout.
tv_usec
= 0;
len =
sizeof
(
struct
sockaddr
);
while
(1) {
FD_ZERO(&read_set);
for
(idx = 0; idx <
TCP_SERVER_LISTEN_NUM
; idx++) {
if
(cli_fd[idx] > 0) {
FD_SET(cli_fd[idx], &read_set);
if
(cli_fd[idx] > max_fd_num)
max_fd_num = cli_fd[idx];
}
}
if
(cli_count <
TCP_SERVER_LISTEN_NUM
) {
FD_SET(listen_fd, &read_set);
if
(listen_fd > max_fd_num)
max_fd_num = listen_fd;
}
select(max_fd_num + 1, &read_set, NULL, NULL, &timeout);
if
(FD_ISSET(listen_fd, &read_set)) {
for
(idx = 0; idx <
TCP_SERVER_LISTEN_NUM
; idx++) {
if
(cli_fd[idx] == -1) {
break
;
}
}
if
(idx ==
TCP_SERVER_LISTEN_NUM
) {
printf
(
"cli count error!\r\n"
);
goto
exit;
}
cli_fd[idx] = accept(listen_fd, (
struct
sockaddr
*)&client_addr, (
socklen_t
*)&len);
if
(cli_fd[idx] < 0) {
if
(errno != EAGAIN)
printf
(
"accept error. %d\r\n"
, errno);
if
(errno == EBADF) {
goto
exit;
}
for
(idx = 0; idx <
TCP_SERVER_LISTEN_NUM
; idx++) {
if
(cli_fd[idx] != -1 && FD_ISSET(cli_fd[idx], &read_set))
break
;
}
if
(idx ==
TCP_SERVER_LISTEN_NUM
) {
continue
;
}
}
else
{
printf
(
"Add tcp client, fd: %d.\r\n"
, cli_fd[idx]);
cli_count++;
FD_SET(cli_fd[idx], &read_set);
if
(cli_fd[idx] > max_fd_num)
max_fd_num = cli_fd[idx];
}
}
for
(idx = 0; idx <
TCP_SERVER_LISTEN_NUM
; idx++) {
sys_memset
(recv_buf, 0, 128);
if
(cli_fd[idx] == -1)
continue
;
if
(FD_ISSET(cli_fd[idx], &read_set)) {
ret = recv(cli_fd[idx], recv_buf, 128, 0);
if
(ret == 0) {
printf
(
"remote close, from client, fd: %d.\r\n"
, cli_fd[idx]);
goto
remove_client;
}
else
if
(ret > 0) {
printf
(
"recv:[%s], from client, fd: %d.\r\n"
, recv_buf, cli_fd[idx]);
}
else
{
if
(errno == EAGAIN) {
continue
;
}
else
if
(errno == EBADF) {
printf
(
"rev error: %d, from client, fd: %d.\r\n"
, errno, cli_fd[idx]);
goto
exit;
}
else
{
printf
(
"rev error: %d, from client, fd: %d.\r\n"
, errno, cli_fd[idx]);
goto
remove_client;
}
}
ret = send(cli_fd[idx], recv_buf,
strlen
(recv_buf), 0);
if
(ret <= 0) {
printf
(
"send error: %d, send to client, fd: %d.\r\n"
, errno, cli_fd[idx]);
goto
remove_client;
}
}
continue
;
remove_client:
printf
(
"Remove tcp client, fd: %d.\r\n"
, cli_fd[idx]);
shutdown(cli_fd[idx], SHUT_RD);
close(cli_fd[idx]);
cli_fd[idx] = -1;
cli_count--;
}
}
exit:
printf
(
"tcp server has closed.\r\n"
);
for
(idx = 0; idx <
TCP_SERVER_LISTEN_NUM
; idx++) {
if
(cli_fd[idx] != -1) {
shutdown(cli_fd[idx], SHUT_RD);
close(cli_fd[idx]);
}
}
if
(listen_fd > -1) {
shutdown(listen_fd, SHUT_RD);
close(listen_fd);
}
}
2.4 主任务:硬件交互与功能协同
主任务负责整合所有模块,实现 “按键控制 AP 启停 + 指示灯闪烁 + OLED 实时显示” 的完整逻辑:
int
main(
void
)
{
platform_init
();
if
(
wifi_init
()) {
printf
(
"wifi init failed.\r\n"
);
}
// 创建 TCP 服务器任务
sys_task_create_dynamic(
(
const
uint8_t
*)
"softap tcp server"
,
4096,
OS_TASK_PRIORITY(0),
softap_tcp_server_task,
NULL
);
// 创建 OLED 显示任务
sys_task_create_dynamic(
(
const
uint8_t
*)
"oled display"
,
2048,
// 栈空间
OS_TASK_PRIORITY(1),
// 优先级可与 TCP 任务相同或略低
oled_display_task,
NULL
);
// 启动操作系统
sys_os_start
();
}
三、功能验证:预期效果与 API 合规性
3.1 硬件交互效果
PB2 指示灯:
上电后 AP 启动,PB2 灯开始闪烁;按下 PB11 按键,AP 关闭;再次按下,AP开启
PB11 按键:
短按(<1s)切换 AP 状态,可以自定义消抖,无误触发;长按不影响逻辑,仅识别一次按下。
OLED 显示:
固定显示 SSID “test_ap”;
“AP STATE” 栏显示 “ok”(开启)或 “false”(关闭);
“STA COUNT” 栏实时显示连接设备数(0~8),设备连接 / 断开时立即更新。
3.2 Wi-Fi 与 TCP 功能效果
SoftAP 功能:
手机 / 电脑可搜索到 “test_ap”,输入密码 “12345678” 成功连接;
连接设备数超过 8 时,新设备无法接入(符合开发指南 SoftAP 最大容量)。
TCP 功能:
使用 TCP 调试工具连接 192.168.4.1:4065(AP 默认网关),发送数据后可收到回显;
AP 关闭时 TCP 服务器停止,调试工具提示 “连接断开”;AP 重启后需重新连接。
3.3 API 合规性验证
所有功能实现均基于开发指南定义的 API,无文档外私有接口:
Wi-Fi 操作:wifi_management_ap_start/wifi_management_ap_stop(4.4 节);
任务管理:sys_task_create_dynamic/sys_task_delete(2.2 节);
网络操作:LwIP 套接字 API(3.1 节);
系统延时:sys_ms_sleep(2.4.3 节)。
四、开发总结:关键经验与注意事项
任务拆分原则:
遵循开发指南 2.2 节,将 TCP 服务器、按键检测、OLED 刷新拆分为独立逻辑(或任务),避免单任务阻塞,这是解决 “按键无响应” 的核心(如前期调试中 TCP 阻塞按键检测的问题)。
资源清理规范:
停止 AP 时需同步删除 TCP 任务、关闭套接字(开发指南 3.1 节),避免端口占用导致后续启动失败。
通过以上开发流程,可基于 GD32VW553 芯片快速实现 “SoftAP + TCP + 硬件交互” 的 Wi-Fi 应用,所有代码均遵循《GD32VW553 Wi-Fi 开发指南.pdf》规范,具备良好的可移植性与稳定性。
因为稿子上传日期是2025/10/6 在这里祝大家中秋国庆节快乐!!!

