萤火工场 GD32VW553-IOT-V2使用TCP协议 + FreeRTOS控制IO

分享作者:wx17604307949653
评测品牌:萤火工场
评测型号:GD32VW553-IOT-V2
发布时间:2025-11-03 15:11:11
2
概要
萤火工场 GD32VW553-IOT-V2开发板,160MHzRISCV核心,拥有WIFI6、蓝牙5.2等丰富外设的同时,还提供了各类RTOS的移植。本文用于记录SDK快速开发,实现TCP服务器的流程。
开源口碑分享内容

从ICEasy获取开发套件

需要下载的内容有

  1. GD32embededbulider (基于eclipse的IDE)
  2. GD32AllInOneProgrammer (串口下载软件)
  3. GD32VW55x_RELEASE_V1.0.3a (SDK)

导入TCP模板

打开IDE,在左上角菜单中依次找到

File -> Open Projects From System

随后按照如下所示导入模板内容

本文作者存储的文件路径为"H:\GD32\GD32VW55x_RELEASE_V1.0.3a\MSDK\examples\wifi\softap_tcp_server\Eclipse_project"

导入成功后,左侧菜单出现项目内容,选择当前项目后编译,确保环境正确。

编译产物位于"H:\GD32\GD32VW55x_RELEASE_V1.0.3a\MSDK\examples\wifi\softap_tcp_server\Eclipse_project\softap_tcp_server\softap_tcp_server.bin"

添加LED翻转代码逻辑

在main.c开头添加IO初始化(PB2、推挽)



  /* enable GPIOB clock */
    rcu_periph_clock_enable(RCU_GPIOB);
    /* configure PB2 as output */
    gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_2);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, GPIO_PIN_2);
    /* set PB2 low (optional, similar to LED off) */
    gpio_bit_reset(GPIOB, GPIO_PIN_2);


定义二值信号量、创建RTOS线程函数

// 模板代码
// 定义信号量
os_sema_t TCP_Bin;
// 模板代码
static void tcp_recv_task(void *param)  // 获取信号量,当信号量被give,触发IO电平翻转
{
    while (1) {
        if (sys_sema_down(&TCP_Bin, -1) == OS_OK) {  // 无限等待信号量
            printf("Received Toggle signal, toggle LED\r\n");
            gpio_bit_toggle(GPIOB, GPIO_PIN_2);

        }
    }
}
//模板代码
int main(void)
{
    /* enable GPIOB clock */
    rcu_periph_clock_enable(RCU_GPIOB);
    /* configure PB2 as output */
    gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_2);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, GPIO_PIN_2);
    /* set PB2 low (optional, similar to LED off) */
    gpio_bit_reset(GPIOB, GPIO_PIN_2);
    int32_t ret = sys_sema_init(&TCP_Bin, 0);  // 创建二值信号量
    if (ret != OS_OK) {
        printf("Semaphore init failed.\r\n");
        return -1;
    }

    platform_init();
    if (wifi_init()) {
        printf("wifi init failed.\r\n");
        return -1;
    }

    sys_task_create_dynamic((const uint8_t *)"softap tcp server", 4096, OS_TASK_PRIORITY(0), softap_tcp_server_task, NULL);
    sys_task_create_dynamic((const uint8_t *)"tcp_recv", 1024, OS_TASK_PRIORITY(1), tcp_recv_task, NULL);

    sys_os_start();

    for (;;) ;
}

修改TCP业务逻辑

如下修改tcp_server_test函数使得TCP服务器接收到"Toggle"的时候give一个信号量

在这里freertos底层API被宏封装了,得用官方API进行任务和信号量创建,以及信号量的give和take也改变了,详情请看代码内容


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]);
                    // 信号处理逻辑
                    if (strcmp(recv_buf, "Toggle") == 0) {
                        sys_sema_up(&TCP_Bin);  // 给信号量
                    }
                } 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) {c
        shutdown(listen_fd, SHUT_RD);
        close(listen_fd);
    }
}

设置log输出串口

文件和宏如下图修改,将CONFIG_BOARD 改为 PLATFORM_BOARD_32VW55XF527

并且将log端口改为USART0 (即连接CH340的串口)

编译并烧录到开发板当中

  1. 开发板boot1设置为on
  2. 开发板通电
  3. 使用GD32AllInOneProgrammer 选择编译出的.bin文件进行烧录
  4. 开发板boot1拨回
  5. 按下K1进行reset

网络连接

烧录成功后,打开电脑wifi网卡,可见以“test_ap"为默认名称的ap信号。

SSID和PSWD由代码内容的宏定义

功能测试

将开发板typec口连接计算机,打开对应com口,波特率默认115200,查看输出日志

输出内容包含文件描述符、IP地址、设备连接等信息

随后编写如下程序,在电脑运行python程序,向网关(即开发板)发送数据包


import socket

# 服务器IP和端口
SERVER_IP = "192.168.237.1"  #改为你获取到开发板的IP
SERVER_PORT = 4065

# 创建TCP socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 连接服务器
try:
    client_socket.connect((SERVER_IP, SERVER_PORT))
    print("Connected to server")

    # 发送数据
    message = "Toggle"
    client_socket.send(message.encode())

    # 接收回显数据
    response = client_socket.recv(1024).decode()
    print(f"Received: {response}")

except Exception as e:
    print(f"Error: {e}")

finally:
    client_socket.close()

实验结果表明 每次运行python程序都会使得LED亮灭


自动补全插件分享

研究了好久IDE的自动补全,发现GD32的IDE和stm32的cubeIDE一样都是基于eclipse,试了一下当时给后者配补全插件的方法,下载对应.jar文件放入"H:\GD32\GD32EmbeddedBuilder_v1.5.4_Rel\GD32EmbeddedBuilder\plugins"后,在IDE上方菜单的window -> preferences 当中打开如下窗口使能自动补全即可

通过网盘分享的文件:org.eclipse.cdt.ui_8.1.200.202310201538.jar 链接: https://pan.baidu.com/s/1PP9if9vBKtNLpVz6i2Vfrg?pwd=1145 提取码: 1145

链接失效请移步 :  https://blog.csdn.net/wyn3514/article/details/139274725

碎碎念

楼主第一次发测评,如果有什么没讲明白和不妥的地方,请各位指出,感激不尽,本人小菜鸡一个,轻喷,谢谢喵

最后感谢ICEasy商城的大力支持,希望咱们的国产芯片越做越好!





全部评论
暂无评论
0/144