本文转载自iCEasy商城口碑评测作者:kkun
欢迎大家戳链接GD32VW553 v2芯片 Wi-Fi 功能开发实践:SoftAP 及oled屏幕与按钮交互实现,与原贴作者互动交流哦~
视频链接 前言 基于《GD32VW553 Wi-Fi 开发指南.pdf》(以下简称 “开发指南”)的 API 规范,本文以 “SoftAP 模式 + TCP 服务器 + 硬件交互” 为核心,结合 PB2 指示灯、PB11 按键、OLED 显示的实战开发,完整呈现 GD32VW553 芯片 Wi-Fi 功能的开发流程,所有代码均遵循开发指南定义的接口,确保兼容性与规范性。 开源口碑分享内容 开启正文前先来一个关于怎么使用msdk做该文章相关内容(wifi)开发的小教程大佬可以跳过~~~file:///C:/Users/ADMINI~1/AppData/Local/Temp/ksohtml12772/wps1.jpg 解压后获得以下内容: file:///C:/Users/ADMINI~1/AppData/Local/Temp/ksohtml12772/wps2.jpg 打开GD32EmbeddedBuilder软件,依次点击左上角的 file->>open projects from file system file:///C:/Users/ADMINI~1/AppData/Local/Temp/ksohtml12772/wps3.jpg 接下来点击directory找到刚刚下载的文件中的路径GD32VW55x_RELEASE_V1.0.3a\GD32VW55x_RELEASE_V1.0.3a\MSDK\examples\wifi\softap_tcp_server\Eclipse_project 最后点击finish即可导入项目 file:///C:/Users/ADMINI~1/AppData/Local/Temp/ksohtml12772/wps4.jpg 导入oled相关代码到前面操作导入的项目中编译即可适用下文的代码了 一、开发基础:核心 API 与硬件资源梳理在开发前需明确开发指南中的关键 API 与硬件资源映射关系,这是功能实现的核心依据 这款开发板相比前代增加了pb2口连接的led灯和pb11口连接的按钮,在本文中也进行测试使用 file:///C:/Users/ADMINI~1/AppData/Local/Temp/ksohtml12772/wps5.jpgfile:///C:/Users/ADMINI~1/AppData/Local/Temp/ksohtml12772/wps6.jpg 1.1 核心 API 选型(源自开发指南) | | | | wifi_management_ap_start(4.4.8 节) | 启动 SoftAP,支持配置 SSID、密码、信道、加密模式 | | wifi_management_ap_stop(4.4.10 节) | | | wifi_vif_is_softap(4.2.16 节) | 检测指定 VIF(虚拟接口)是否为 SoftAP 模式,判断 AP 启停状态 | | | 含 socket/bind/listen/accept/recv/send,实现 TCP 数据收发 | | sys_task_create_dynamic(2.2.2 节) | 动态创建独立任务,避免单任务阻塞(如 TCP 与按键检测拆分) | | | | | | 任务级延时,释放 CPU 资源,符合 RTOS 调度逻辑 | 1.2 硬件资源定义 | | | | | | | | | | | 显示 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》规范,具备良好的可移植性与稳定性。
|