GD32VW553 v2芯片 Wi-Fi 功能开发实践:SoftAP 及oled屏幕与按钮交互实现

[复制链接]
2208|0
本文转载自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代码在我的另一个测评中有说明 https://www.iceasy.com/review/1944795609719500802
导入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 选型(源自开发指南)
功能模块
关键 API(开发指南章节)
作用描述
SoftAP 管理
wifi_management_ap_start4.4.8 节)
启动 SoftAP,支持配置 SSID、密码、信道、加密模式
wifi_management_ap_stop4.4.10 节)
停止 SoftAP,断开所有客户端连接
wifi_vif_is_softap4.2.16 节)
检测指定 VIF(虚拟接口)是否为 SoftAP 模式,判断 AP 启停状态
TCP 服务器
LwIP 套接字 API(3.1 节)
socket/bind/listen/accept/recv/send,实现 TCP 数据收发
RTOS 任务管理
sys_task_create_dynamic2.2.2 节)
动态创建独立任务,避免单任务阻塞(如 TCP 与按键检测拆分)
sys_task_delete2.2.4 节)
删除任务,释放资源
系统延时
sys_ms_sleep2.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 时钟,再配置引脚模式:
  1. #include "gd32vw55x_gpio.h"

  2. #include "gd32vw55x_rcu.h"



  3. // PB2 指示灯初始化(推挽输出)

  4. static void pb2_led_init(void) {

  5.    rcu_periph_clock_enable(RCU_GPIOB);  // 使能 GPIOB 时钟

  6.    gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_2);  // 推挽输出

  7.    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_2);

  8.    gpio_bit_reset(GPIOB, GPIO_PIN_2);  // 初始灭灯

  9. }



  10. // PB11 按键初始化(上拉输入)

  11. static void pb11_key_init(void) {

  12.    rcu_periph_clock_enable(RCU_GPIOB);

  13.    gpio_mode_set(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_11);  // 上拉输入

  14. }



  15. // 按键扫描(可带消抖)

  16. uint8_t key_scan(void) {

  17. static uint8_t key_up = 1; // 按键松开标志

  18. if(key_up && (gpio_input_bit_get(GPIOB, GPIO_PIN_11) == 0)) {

  19. // delay_1ms(1); // 消抖

  20. if(gpio_input_bit_get(GPIOB, GPIO_PIN_11) == 0) {

  21. key_up = 0;

  22. return 1; // 按键按下

  23. }

  24. } else if(gpio_input_bit_get(GPIOB, GPIO_PIN_11) == 1) {

  25. key_up = 1; // 按键松开

  26. }

  27. return 0;

  28. }
2.1.2 OLED 初始化与显示函数
OLED 初始化需确保 I2C/SPI 引脚配置正确,显示逻辑需适配开发指南中 “任务内周期性刷新” 的要求使用rtos实现oled显示的任务调度:
  1. static void oled_display_task(void *param) {

  2. OLED_Init();

  3. char *state_str;

  4. char count_str[16];

  5. uint8_t connected_sta_num;

  6. gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_2);

  7. gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_2);

  8. while (1) {

  9. state_str = (g_softap_enabled== 0) ? "false" : "ok";

  10. connected_sta_num = get_ap_connected_sta_num();

  11. sprintf(count_str, "%d", connected_sta_num);



  12. OLED_Clear();

  13. OLED_ShowString(10, 5, "AP:", OLED_8X16);

  14. OLED_ShowString(40, 5, SSID, OLED_8X16);

  15. OLED_ShowString(10, 25, "STA COUNT:", OLED_8X16);

  16. OLED_ShowString(90, 25, count_str, OLED_8X16);

  17. OLED_ShowString(10, 45, "STATE:", OLED_8X16);

  18. OLED_ShowString(65, 45, state_str, OLED_8X16);

  19. OLED_Update();

  20. gpio_bit_toggle(GPIOB, GPIO_PIN_2);

  21. vTaskDelay(pdMS_TO_TICKS(1000)); // 依赖FreeRTOS,需确保包含task.h

  22. }

  23. }
2.2 Wi-Fi 功能实现:SoftAP 与连接设备检测2.2.1 SoftAP 启动与停止
严格使用开发指南 4.4 节 API,配置 SSID 为 “GD32VW553_AP”,密码为 “12345678”,信道 11,用rtos实现ap的任务调度:
  1. static void softap_tcp_server_task(void *param) {

  2. int ret = 0;

  3. char *ssid = SSID;

  4. char *password = PASSWORD;

  5. uint8_t channel = 11, is_hidden = 0;

  6. wifi_ap_auth_mode_t auth_mode = AUTH_MODE_WPA2_WPA3;



  7. // -------------------------- 初始化 PB11 按键--------------------------

  8. rcu_periph_clock_enable(RCU_GPIOB); // 使能 GPIOB 时钟

  9. gpio_mode_set(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_11); // 上拉输入



  10. // -------------------------- 初始启动 SoftAP --------------------------

  11. if (ssid == NULL) {

  12. printf("ssid can not be NULL!\r\n");

  13. goto exit;

  14. }

  15. if (password && (strlen(password) == 0)) {

  16. password = NULL;

  17. auth_mode = AUTH_MODE_OPEN; // 无密码时为开放模式

  18. }



  19. // 初始启动 SoftAP(文档 API:wifi_management_ap_start)

  20. printf("Start Wi-Fi softap.\r\n");

  21. ret = wifi_management_ap_start(ssid, password, channel, auth_mode, is_hidden);

  22. if (ret != 0) {

  23. printf("Wi-Fi softap start failed.\r\n");

  24. g_softap_enabled = 0; // 启动失败,标记 AP 关闭

  25. goto exit;

  26. } else {

  27. printf("SoftAP:%s successfully started!\r\n", ssid);

  28. g_softap_enabled = 1; // 启动成功,标记 AP 开启

  29. }



  30. // -------------------------- 按键检测与 AP 开关切换 --------------------------

  31. while (1) {

  32. // 检测 PB11 按键按下(调用 key_scan 函数)

  33. if (key_scan() == 1) {

  34. printf("PB11 key pressed, toggle AP state...\r\n");



  35. // 获取当前 AP 状态(基于文档 API:wifi_vif_is_softap)

  36. //g_softap_enabled = get_softap_state();

  37. if (g_softap_enabled == 1) {

  38. // 情况1:AP 已开启 → 关闭 AP

  39. ret = wifi_management_ap_stop();

  40. if (ret == 0) {

  41. printf("SoftAP stopped successfully!\r\n");

  42. g_softap_enabled = 0; // 标记 AP 关闭

  43. // 关闭 AP 后,停止 TCP 服务器

  44. // (若需重新开启 AP 时重启 TCP,可在此处清理 TCP 资源)

  45. } else {

  46. printf("SoftAP stop failed! Error code: %d\r\n", ret);

  47. }

  48. } else {

  49. // 情况2:AP 已关闭 → 重新开启 AP(文档 API:wifi_management_ap_start)

  50. ret = wifi_management_ap_start(ssid, password, channel, auth_mode, is_hidden);

  51. if (ret == 0) {

  52. printf("SoftAP restarted successfully!\r\n");

  53. g_softap_enabled = 1; // 标记 AP 开启

  54. // 重新开启 AP 后,重启 TCP 服务器

  55. //tcp_server_test();//这个是tcp服务器程序启动代码,这边和按钮开关ap功能冲突了所以注释掉,有大佬可以解决的话可以删掉最左侧的注释符

  56. } else {

  57. printf("SoftAP restart failed! Error code: %d\r\n", ret);

  58. }

  59. }

  60. }



  61. // 延时 50ms,避免频繁检测占用 CPU(文档 RTOS 任务调度推荐)

  62. vTaskDelay(pdMS_TO_TICKS(50));

  63. }



  64. // -------------------------- 清理逻辑 --------------------------

  65. exit:

  66. printf("the test has ended.\r\n");

  67. // 退出前关闭 AP(文档推荐:资源需手动释放)

  68. if (g_softap_enabled == 1) {

  69. wifi_management_ap_stop();

  70. }

  71. sys_task_delete(NULL);

  72. }

  73. 2.2.2 连接设备数检测
  74. 基于开发指南 5.3 节 “启动 SoftAP” 示例,使用 macif_vif_ap_assoc_info_get 获取客户端 MAC 地址,统计设备数:

  75. // 获取 SoftAP 已连接的客户端数量

  76. static uint8_t get_ap_connected_sta_num(void) {

  77. // 存储客户端MAC地址:CFG_STA_NUM个客户端,每个MAC占6字节

  78. uint8_t cli_mac[CFG_STA_NUM][6] = {0};

  79. int vif_idx = 0; // SoftAP默认VIF序号为0(文档隐含)

  80. int cli_count = 0; // 实际连接的客户端数量



  81. // 调用函数:仅传2个参数(VIF序号 + MAC缓冲区指针)

  82. cli_count = macif_vif_ap_assoc_info_get(

  83. vif_idx, // 参数1:SoftAP的VIF序号

  84. (uint16_t *)cli_mac // 参数2:MAC缓冲区(强制转换为uint16_t*匹配声明)

  85. );



  86. // (可选)打印客户端MAC和IP(修正指针类型)

  87. for (int i = 0; i < cli_count; i++) {

  88. // 参数改为 uint8_t* 类型,匹配 dhcpd_find_ipaddr_by_macaddr 要求

  89. uint32_t client_ip = dhcpd_find_ipaddr_by_macaddr(cli_mac[i]);

  90. printf("Client[%d]: MAC=%02X:%02X:%02X:%02X:%02X:%02X, IP=%d.%d.%d.%d\r\n",

  91. i,

  92. cli_mac[i][0], cli_mac[i][1], cli_mac[i][2],

  93. cli_mac[i][3], cli_mac[i][4], cli_mac[i][5],

  94. (client_ip >> 0) & 0xFF, (client_ip >> 8) & 0xFF,

  95. (client_ip >> 16) & 0xFF, (client_ip >> 24) & 0xFF);

  96. }



  97. // 确保返回值为uint8_t(客户端数量不会超过CFG_STA_NUM,通常≤8)

  98. return (cli_count > CFG_STA_NUM) ? CFG_STA_NUM : (uint8_t)cli_count;

  99. }
2.3 TCP 服务器实现:独立任务设计
遵循开发指南 2.2 节 “任务功能拆分” 原则,将 TCP 服务器封装为独立任务,避免阻塞按键检测与 OLED 刷新:
  1. static void tcp_server_test(void)

  2. {

  3. int listen_fd = -1, ret, reuse, idx;

  4. struct sockaddr_in server_addr;

  5. int cli_fd[TCP_SERVER_LISTEN_NUM];

  6. struct sockaddr_in client_addr;

  7. uint8_t cli_count = 0;

  8. char recv_buf[128];

  9. socklen_t len;

  10. struct timeval timeout;

  11. fd_set read_set;

  12. int max_fd_num = 0;



  13. listen_fd = socket(AF_INET, SOCK_STREAM, 0);

  14. if (listen_fd < 0) {

  15. printf("Create tcp server socket fd error!\r\n");

  16. goto exit;

  17. }

  18. printf("Create tcp server, fd: %d, port: %u.\r\n", listen_fd, TCP_SERVER_LISTEN_PORT);



  19. reuse = 1;

  20. setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse, sizeof(reuse));

  21. sys_memset(&server_addr, 0, sizeof(server_addr));

  22. server_addr.sin_family = AF_INET;

  23. server_addr.sin_len = sizeof(server_addr);

  24. server_addr.sin_port = htons(TCP_SERVER_LISTEN_PORT);

  25. server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

  26. ret = bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));

  27. if(ret < 0) {

  28. printf("Bind tcp server socket fd error!\r\n");

  29. goto exit;

  30. }



  31. ret = listen(listen_fd, TCP_SERVER_LISTEN_NUM);

  32. if(ret != 0) {

  33. printf("Listen tcp server socket fd error!\r\n");

  34. goto exit;

  35. }



  36. for (idx = 0; idx < TCP_SERVER_LISTEN_NUM; idx++)

  37. cli_fd[idx] = -1;



  38. timeout.tv_sec = 1;

  39. timeout.tv_usec = 0;

  40. len = sizeof(struct sockaddr);

  41. while (1) {

  42. FD_ZERO(&read_set);

  43. for (idx = 0; idx < TCP_SERVER_LISTEN_NUM; idx++) {

  44. if (cli_fd[idx] > 0) {

  45. FD_SET(cli_fd[idx], &read_set);

  46. if (cli_fd[idx] > max_fd_num)

  47. max_fd_num = cli_fd[idx];

  48. }

  49. }

  50. if (cli_count < TCP_SERVER_LISTEN_NUM) {

  51. FD_SET(listen_fd, &read_set);

  52. if (listen_fd > max_fd_num)

  53. max_fd_num = listen_fd;

  54. }



  55. select(max_fd_num + 1, &read_set, NULL, NULL, &timeout);



  56. if (FD_ISSET(listen_fd, &read_set)) {

  57. for (idx = 0; idx < TCP_SERVER_LISTEN_NUM; idx++) {

  58. if (cli_fd[idx] == -1) {

  59. break;

  60. }

  61. }

  62. if (idx == TCP_SERVER_LISTEN_NUM) {

  63. printf("cli count error!\r\n");

  64. goto exit;

  65. }

  66. cli_fd[idx] = accept(listen_fd, (struct sockaddr *)&client_addr, (socklen_t *)&len);

  67. if (cli_fd[idx] < 0) {

  68. if (errno != EAGAIN)

  69. printf("accept error. %d\r\n", errno);

  70. if (errno == EBADF) {

  71. goto exit;

  72. }

  73. for (idx = 0; idx < TCP_SERVER_LISTEN_NUM; idx++) {

  74. if (cli_fd[idx] != -1 && FD_ISSET(cli_fd[idx], &read_set))

  75. break;

  76. }

  77. if (idx == TCP_SERVER_LISTEN_NUM) {

  78. continue;

  79. }

  80. } else {

  81. printf("Add tcp client, fd: %d.\r\n", cli_fd[idx]);

  82. cli_count++;

  83. FD_SET(cli_fd[idx], &read_set);

  84. if (cli_fd[idx] > max_fd_num)

  85. max_fd_num = cli_fd[idx];

  86. }

  87. }



  88. for (idx = 0; idx < TCP_SERVER_LISTEN_NUM; idx++) {

  89. sys_memset(recv_buf, 0, 128);

  90. if (cli_fd[idx] == -1)

  91. continue;

  92. if (FD_ISSET(cli_fd[idx], &read_set)) {

  93. ret = recv(cli_fd[idx], recv_buf, 128, 0);

  94. if (ret == 0) {

  95. printf("remote close, from client, fd: %d.\r\n", cli_fd[idx]);

  96. goto remove_client;

  97. } else if (ret > 0) {

  98. printf("recv:[%s], from client, fd: %d.\r\n", recv_buf, cli_fd[idx]);

  99. } else {

  100. if (errno == EAGAIN) {

  101. continue;

  102. } else if (errno == EBADF) {

  103. printf("rev error: %d, from client, fd: %d.\r\n", errno, cli_fd[idx]);

  104. goto exit;

  105. } else {

  106. printf("rev error: %d, from client, fd: %d.\r\n", errno, cli_fd[idx]);

  107. goto remove_client;

  108. }

  109. }



  110. ret = send(cli_fd[idx], recv_buf, strlen(recv_buf), 0);

  111. if (ret <= 0) {

  112. printf("send error: %d, send to client, fd: %d.\r\n", errno, cli_fd[idx]);

  113. goto remove_client;

  114. }

  115. }

  116. continue;

  117. remove_client:

  118. printf("Remove tcp client, fd: %d.\r\n", cli_fd[idx]);

  119. shutdown(cli_fd[idx], SHUT_RD);

  120. close(cli_fd[idx]);

  121. cli_fd[idx] = -1;

  122. cli_count--;

  123. }

  124. }



  125. exit:

  126. printf("tcp server has closed.\r\n");

  127. for (idx = 0; idx < TCP_SERVER_LISTEN_NUM; idx++) {

  128. if (cli_fd[idx] != -1) {

  129. shutdown(cli_fd[idx], SHUT_RD);

  130. close(cli_fd[idx]);

  131. }

  132. }

  133. if (listen_fd > -1) {

  134. shutdown(listen_fd, SHUT_RD);

  135. close(listen_fd);

  136. }

  137. }
2.4 主任务:硬件交互与功能协同
主任务负责整合所有模块,实现 “按键控制 AP 启停 + 指示灯闪烁 + OLED 实时显示” 的完整逻辑:
  1. int main(void)

  2. {

  3. platform_init();

  4. if (wifi_init()) {

  5. printf("wifi init failed.\r\n");

  6. }

  7. // 创建 TCP 服务器任务

  8. sys_task_create_dynamic(

  9. (const uint8_t *)"softap tcp server",

  10. 4096,

  11. OS_TASK_PRIORITY(0),

  12. softap_tcp_server_task,

  13. NULL

  14. );

  15. // 创建 OLED 显示任务

  16. sys_task_create_dynamic(

  17. (const uint8_t *)"oled display",

  18. 2048, // 栈空间

  19. OS_TASK_PRIORITY(1), // 优先级可与 TCP 任务相同或略低

  20. oled_display_task,

  21. NULL

  22. );

  23. // 启动操作系统

  24. sys_os_start();

  25. }
三、功能验证:预期效果与 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》规范,具备良好的可移植性与稳定性。

您需要登录后才可以回帖 登录 | 注册

本版积分规则

16

主题

19

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部