萤火工场 GD32VW553-IOT-V2 开发板测评
分享作者:wx17590409409927
评测品牌:萤火工场
评测型号:GD32VW553-IOT-V2
发布时间:2025-10-13 11:22:10
1
前言
本测试程序基于 GD32VW55x_RELEASE_V1.0.3a SDK,通过开发板的 340 串口与电脑建立通信,将开发板模拟为电脑的 Wi-Fi 网卡:实现上位机(电脑)联网功能
开源口碑分享内容
#include "gd32vw55x.h"
#include "systick.h"
#include "gd32vw55x_usart.h"          // 340串口驱动
#include "wifi_management.h"// Wi-Fi连接管理API
#include "wifi_netif.h"     // 网络接口API
#include "lwip/tcp.h"       // TCP协议栈
#include "lwip/udp.h"       // UDP协议栈
#include "wrapper_freertos.h"          // 任务管理API

#define UART_BAUDRATE 115200
#define UART_TX_BUF_SIZE 1024  // 串口发送缓冲区大小
#define UART_RX_BUF_SIZE 1024  // 串口接收缓冲区大小

#define FRAME_HEADER1 0xAA
#define FRAME_HEADER2 0xBB
#define FRAME_TAIL1 0xCC
#define FRAME_TAIL2 0xDD
#define DATA_TYPE_TCP 0x01
#define DATA_TYPE_UDP 0x02

#define TCP_LOCAL_PORT 8080  // 开发板TCP本地端口(与电脑端一致)

// 串口收发缓冲区(全局变量,中断与任务间共享)
uint8_t uart_tx_buf[UART_TX_BUF_SIZE] = {0};
uint8_t uart_rx_buf[UART_RX_BUF_SIZE] = {0};
uint16_t uart_rx_len = 0;       // 串口接收数据长度
os_mutex_t uart_mutex;          // 串口缓冲区互斥锁(避免多任务竞争)

/**
 * @brief 340串口初始化(USART0)
 */
void uart340_init(void) {
    // 1. 使能GPIO与USART时钟
    rcu_periph_clock_enable(RCU_GPIOA);  // 假设TX=PA9,RX=PA10
    rcu_periph_clock_enable(RCU_USART0);

    // 2. 配置GPIO为复用功能(TX推挽输出,RX浮空输入)
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_10);
    gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_9);  // USART0_TX对应AF7
    gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_10); // USART0_RX对应AF7

    // 3. 配置USART参数(波特率115200,8位数据位,1位停止位,无校验)
    usart_deinit(USART0);
    usart_baudrate_set(USART0, UART_BAUDRATE);
    usart_word_length_set(USART0, USART_WL_8BIT);
    usart_stop_bit_set(USART0, USART_STB_1BIT);
    usart_parity_config(USART0, USART_PM_NONE);
    usart_hardware_flow_control_config(USART0, USART_HFCTRL_NONE);

    // 4. 使能USART接收中断(优先级:抢占2,响应2)
    nvic_irq_enable(USART0_IRQn, 2, 2);
    usart_interrupt_enable(USART0, USART_INT_RBNE);  // 接收非空中断
    usart_enable(USART0);

    // 5. 初始化串口互斥锁(保护缓冲区共享)
    sys_mutex_init(&uart_mutex);
}

/**
 * @brief USART0中断服务函数(接收数据存入缓冲区)
 */
void USART0_IRQHandler(void) {
    if (usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE) != RESET) {
        sys_mutex_lock(&uart_mutex);  // 加锁保护缓冲区
        if (uart_rx_len < UART_RX_BUF_SIZE) {
            uart_rx_buf[uart_rx_len++] = usart_data_receive(USART0);
        }
        sys_mutex_unlock(&uart_mutex);  // 解锁
        usart_interrupt_flag_clear(USART0, USART_INT_FLAG_RBNE);
    }
}

/**
 * @brief 串口发送数据(阻塞式,适用于短数据如状态反馈)
 * @param data:待发送数据指针
 * @param len:数据长度
 */
void uart340_send(uint8_t *data, uint16_t len) {
    for (uint16_t i = 0; i < len; i++) {
        while (usart_flag_get(USART0, USART_FLAG_TBE) == RESET);
        usart_data_transmit(USART0, data[i]);
    }
}

#define WIFI_SSID "Your_WiFi_SSID"    // 目标AP的SSID
#define WIFI_PASSWORD "Your_WiFi_PWD" // 目标AP的密码(8-63位)
#define WIFI_CHANNEL 1                // 目标AP信道(0xFF为自动扫描)

// Wi-Fi状态(全局变量,供其他任务查询)
typedef enum {
    WIFI_STA_DISCONNECTED = 0,
    WIFI_STA_CONNECTING,
    WIFI_STA_CONNECTED,
    WIFI_STA_IP_OBTAINED
} wifi_sta_state_t;
wifi_sta_state_t g_wifi_state = WIFI_STA_DISCONNECTED;
struct ip_addr g_wifi_ip;  // Wi-Fi获取的IP地址

/**
 * @brief Wi-Fi连接结果回调函数(事件驱动)
 */
void wifi_connect_callback(void *eloop_data, void *user_ctx) {
    if (wifi_vif_is_sta_connected(WIFI_VIF_INDEX_DEFAULT)) {
        g_wifi_state = WIFI_STA_CONNECTED;
        // 获取并打印IP地址
        wifi_get_vif_ip(WIFI_VIF_INDEX_DEFAULT, (struct wifi_ip_addr_cfg *)&g_wifi_ip);
        char ip_str[20] = {0};
        ip4addr_ntoa_r(&g_wifi_ip, ip_str, sizeof(ip_str));
        char msg[64] = {0};
        sprintf(msg, "Wi-Fi connected, IP: %s\r\n", ip_str);
        uart340_send((uint8_t *)msg, strlen(msg));

        // 启动DHCP(若使用动态IP)
        void *net_if = vif_idx_to_net_if(WIFI_VIF_INDEX_DEFAULT);
        net_dhcp_start(net_if);
        if (net_dhcp_address_obtained(net_if)) {
            g_wifi_state = WIFI_STA_IP_OBTAINED;
            uart340_send((uint8_t *)"DHCP IP obtained\r\n", 20);
        }
    } else {
        g_wifi_state = WIFI_STA_DISCONNECTED;
        uart340_send((uint8_t *)"Wi-Fi connect failed\r\n", 22);
    }
}

/**
 * @brief Wi-Fi管理任务(优先级:中)
 * @param ctx:任务上下文(未使用)
 */
void wifi_manager_task(void *ctx) {
    // 1. 初始化Wi-Fi与Event Loop
    wifi_management_init();
    wifi_eloop_init();

    // 2. 注册Wi-Fi连接事件回调
    eloop_event_register(WIFI_MGMT_EVENT_CONNECT_SUCCESS, wifi_connect_callback, NULL, NULL);
    eloop_event_register(WIFI_MGMT_EVENT_CONNECT_FAIL, wifi_connect_callback, NULL, NULL);

    // 3. 扫描并连接目标AP(阻塞模式,确保连接成功后再进行后续操作)
    uart340_send((uint8_t *)"Scanning Wi-Fi...\r\n", 19);
    wifi_management_scan(true, (uint8_t *)WIFI_SSID);  // 扫描指定SSID

    uart340_send((uint8_t *)"Connecting to AP...\r\n", 21);
    wifi_management_connect((uint8_t *)WIFI_SSID, (uint8_t *)WIFI_PASSWORD, true);

    // 4. 循环监控Wi-Fi状态(异常断开时自动重连)
    while (1) {
        if (g_wifi_state == WIFI_STA_DISCONNECTED) {
            uart340_send((uint8_t *)"Reconnecting Wi-Fi...\r\n", 23);
            wifi_management_connect((uint8_t *)WIFI_SSID, (uint8_t *)WIFI_PASSWORD, true);
        }
        sys_ms_sleep(5000);  // 每5秒检查一次状态
    }
}


// 网络数据缓存(Wi-Fi接收后存入,供串口发送)
uint8_t net_rx_buf[UART_RX_BUF_SIZE] = {0};
uint16_t net_rx_len = 0;
os_mutex_t net_mutex;  // 网络数据缓冲区互斥锁

/**
 * @brief 解析串口数据帧(提取数据类型与内容)
 * @param buf:串口接收缓冲区
 * @param len:缓冲区数据长度
 * @param data_type:输出数据类型(TCP/UDP)
 * @param data:输出数据内容
 * @param data_len:输出数据长度
 * @return 0:解析成功,-1:解析失败(帧格式错误)
 */
int parse_uart_frame(uint8_t *buf, uint16_t len, uint8_t *data_type, uint8_t *data, uint16_t *data_len) {
    // 检查帧长度(至少7字节:头2+类型1+长度2+尾2)
    if (len < 7) return -1;

    // 检查帧头与帧尾
    if (buf[0] != FRAME_HEADER1 || buf[1] != FRAME_HEADER2 ||
        buf[len-2] != FRAME_TAIL1 || buf[len-1] != FRAME_TAIL2) {
        return -1;
    }

    // 提取数据类型、长度、内容
    *data_type = buf[2];
    *data_len = (buf[3] << 8) | buf[4];  // 大端序转小端序
    if (*data_len > len - 7) return -1;  // 数据长度超出实际内容
    memcpy(data, buf + 5, *data_len);

    return 0;
}

/**
 * @brief 串口数据处理任务(优先级:高)
 * @param ctx:任务上下文(未使用)
 */
void uart_data_task(void *ctx) {
    uint8_t data_type = 0;
    uint8_t data_buf[UART_RX_BUF_SIZE] = {0};
    uint16_t data_len = 0;

    // 初始化网络数据互斥锁
    sys_mutex_init(&net_mutex);

    while (1) {
        sys_mutex_lock(&uart_mutex);
        if (uart_rx_len >= 7) {  // 至少包含完整帧头+类型+长度+帧尾
            // 1. 解析串口数据帧(电脑发送的网络数据)
            if (parse_uart_frame(uart_rx_buf, uart_rx_len, &data_type, data_buf, &data_len) == 0) {
                // 2. 根据数据类型转发至Wi-Fi(TCP/UDP)
                void *net_if = vif_idx_to_net_if(WIFI_VIF_INDEX_DEFAULT);
                if (net_if == NULL || g_wifi_state != WIFI_STA_IP_OBTAINED) {
                    uart340_send((uint8_t *)"Wi-Fi not ready\r\n", 18);
                    goto frame_end;
                }

                if (data_type == DATA_TYPE_TCP) {
                    // TODO:TCP数据转发(需提前建立TCP连接,此处简化为固定目标IP和端口)
                    struct ip_addr dest_ip;
                    ip4addr_aton("192.168.1.101", &dest_ip);  // 目标IP(电脑需与开发板同网段)
                    uint16_t dest_port = 8080;                // 目标端口
                    tcp_send_data(&dest_ip, dest_port, data_buf, data_len);
                } else if (data_type == DATA_TYPE_UDP) {
                    // UDP数据转发(固定目标IP和端口)
                    struct ip_addr dest_ip;
                    ip4addr_aton("192.168.1.101", &dest_ip);
                    uint16_t dest_port = 8081;
                    udp_send_data(&dest_ip, dest_port, data_buf, data_len);
                }

                // 3. 解析成功后清空缓冲区
                memset(uart_rx_buf, 0, uart_rx_len);
                uart_rx_len = 0;
            } else {
                // 帧格式错误,丢弃错误数据(简单处理:清空缓冲区)
                uart340_send((uint8_t *)"Invalid frame format\r\n", 22);
                memset(uart_rx_buf, 0, uart_rx_len);
                uart_rx_len = 0;
            }
        }
frame_end:
        sys_mutex_unlock(&uart_mutex);

        // 4. 将Wi-Fi接收的网络数据打包为帧,通过串口回传电脑
        sys_mutex_lock(&net_mutex);
        if (net_rx_len > 0) {
            uint8_t frame_buf[UART_TX_BUF_SIZE] = {0};
            uint16_t frame_len = 0;

            // 组装帧头
            frame_buf[frame_len++] = FRAME_HEADER1;
            frame_buf[frame_len++] = FRAME_HEADER2;
            // 数据类型(默认TCP,可根据实际接收类型调整)
            frame_buf[frame_len++] = DATA_TYPE_TCP;
            // 数据长度(大端序)
            frame_buf[frame_len++] = (net_rx_len >> 8) & 0xFF;
            frame_buf[frame_len++] = net_rx_len & 0xFF;
            // 数据内容
            memcpy(frame_buf + frame_len, net_rx_buf, net_rx_len);
            frame_len += net_rx_len;
            // 帧尾
            frame_buf[frame_len++] = FRAME_TAIL1;
            frame_buf[frame_len++] = FRAME_TAIL2;

            // 发送帧数据
            uart340_send(frame_buf, frame_len);

            // 清空网络数据缓冲区
            memset(net_rx_buf, 0, net_rx_len);
            net_rx_len = 0;
        }
        sys_mutex_unlock(&net_mutex);

        sys_ms_sleep(10);  // 10ms轮询一次,平衡实时性与CPU占用
    }
}

/**
 * @brief TCP数据接收回调函数(Wi-Fi接收数据后触发)
 * @param arg:用户参数(未使用)
 * @param tpcb:TCP控制块
 * @param pbuf:数据缓冲区
 * @param err:错误码(0为无错误)
 */
err_t tcp_recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *pbuf, err_t err) {
    if (pbuf == NULL) {
        // 连接断开,重新监听
        tcp_close(tpcb);
        tcp_listen(tpcb);
        return ERR_OK;
    }

    // 将数据存入网络缓冲区(加锁保护)
    sys_mutex_lock(&net_mutex);
    if (net_rx_len + pbuf->len <= UART_RX_BUF_SIZE) {
        memcpy(net_rx_buf + net_rx_len, pbuf->payload, pbuf->len);
        net_rx_len += pbuf->len;
    } else {
        uart340_send((uint8_t *)"Net buffer full\r\n", 18);
    }
    sys_mutex_unlock(&net_mutex);

    // 释放pbuf缓冲区
    pbuf_free(pbuf);
    return ERR_OK;
}

/**
 * @brief TCP连接建立回调函数(电脑连接开发板时触发)
 * @param arg:用户参数(未使用)
 * @param tpcb:TCP控制块
 * @param err:错误码(0为无错误)
 */
err_t tcp_accept_callback(void *arg, struct tcp_pcb *new_tpcb, err_t err) {
    if (err != ERR_OK) {
        return err;
    }

    // 注册数据接收回调
    tcp_recv(new_tpcb, tcp_recv_callback);
    return ERR_OK;
}

/**
 * @brief 网络数据透传任务(优先级:低)
 * @param ctx:任务上下文(未使用)
 */
void net_data_task(void *ctx) {
    struct tcp_pcb *tcp_pcb = NULL;
    err_t err;

    // 1. 等待Wi-Fi获取IP(确保网络接口就绪)
    while (g_wifi_state != WIFI_STA_IP_OBTAINED) {
        sys_ms_sleep(1000);
    }

    // 2. 创建TCP服务器(监听本地端口,等待电脑连接)
    tcp_pcb = tcp_new();
    if (tcp_pcb == NULL) {
        uart340_send((uint8_t *)"TCP create failed\r\n", 20);
        while (1) sys_ms_sleep(1000);
    }

    // 绑定本地端口
    err = tcp_bind(tcp_pcb, IP_ADDR_ANY, TCP_LOCAL_PORT);
    if (err != ERR_OK) {
        uart340_send((uint8_t *)"TCP bind failed\r\n", 18);
        while (1) sys_ms_sleep(1000);
    }

    // 开始监听(最大等待连接数:5)
    tcp_pcb = tcp_listen(tcp_pcb);
    tcp_accept(tcp_pcb, tcp_accept_callback);
    uart340_send((uint8_t *)"TCP server listening...\r\n", 25);

    // 3. 循环处理TCP协议栈事件(依赖LwIP的事件调度)
    while (1) {
        sys_ms_sleep(100);  // 触发LwIP事件处理
    }
}

int main(void) {
    // 1. 初始化Systick(1ms滴答)
    systick_config();

    // 2. 初始化340串口
    uart340_init();
    uart340_send((uint8_t *)"GD32VW553 Wi-Fi NIC Start\r\n", 29);

    // 3. 初始化OSAL(RTOS抽象层)
    sys_os_init();

    // 4. 创建核心任务(优先级:uart_data_task > wifi_manager_task > net_data_task)
    sys_task_create_dynamic("uart_data", 2048, 3, uart_data_task, NULL);  // 优先级3(高)
    sys_task_create_dynamic("wifi_mgr", 2048, 2, wifi_manager_task, NULL);  // 优先级2(中)
    sys_task_create_dynamic("net_data", 2048, 1, net_data_task, NULL);     // 优先级1(低)

    // 5. 启动RTOS调度(永不返回)
    sys_os_start();

    return 0;
}






上位机(PC)程序(Python)
import serial
import struct

# 串口配置
ser = serial.Serial('COM3', 115200, timeout=0.1)

def send_net_data(data_type, data):
    """发送网络数据到开发板:帧头(2)+类型(1)+长度(2)+数据+帧尾(2)"""
    data_len = len(data)
    # 组装帧(大端序)
    frame = b'\xAA\xBB' + struct.pack('B', data_type) + struct.pack('>H', data_len) + data + b'\xCC\xDD'
    ser.write(frame)

def recv_net_data():
    """从开发板接收网络数据并解析"""
    while True:
        if ser.in_waiting > 7:
            frame = ser.read(ser.in_waiting)
            # 检查帧头帧尾
            if frame[0] == 0xAA and frame[1] == 0xBB and frame[-2] == 0xCC and frame[-1] == 0xDD:
                data_type = frame[2]
                data_len = struct.unpack('>H', frame[3:5])[0]
                data = frame[5:5+data_len]
                print(f"Received {data_type==1?'TCP':'UDP'} data: {data.hex()}")

# 测试:发送TCP数据("Hello Wi-Fi NIC")
send_net_data(1, b"Hello Wi-Fi NIC")
# 接收数据
recv_net_data()


上位机程序(电脑)Python:


import serial
import struct

# 串口配置
ser = serial.Serial('COM3', 115200, timeout=0.1)

def send_net_data(data_type, data):
    """发送网络数据到开发板:帧头(2)+类型(1)+长度(2)+数据+帧尾(2)"""
    data_len = len(data)
    # 组装帧(大端序)
    frame = b'\xAA\xBB' + struct.pack('B', data_type) + struct.pack('>H', data_len) + data + b'\xCC\xDD'
    ser.write(frame)

def recv_net_data():
    """从开发板接收网络数据并解析"""
    while True:
        if ser.in_waiting > 7:
            frame = ser.read(ser.in_waiting)
            # 检查帧头帧尾
            if frame[0] == 0xAA and frame[1] == 0xBB and frame[-2] == 0xCC and frame[-1] == 0xDD:
                data_type = frame[2]
                data_len = struct.unpack('>H', frame[3:5])[0]
                data = frame[5:5+data_len]
                print(f"Received {data_type==1?'TCP':'UDP'} data: {data.hex()}")

# 测试:发送TCP数据("Hello Wi-Fi NIC")
send_net_data(1, b"Hello Wi-Fi NIC")
# 接收数据
recv_net_data()


注意事项:

  1. 数据粘包:若串口数据出现粘包,可在帧格式中增加 “帧序号” 或 “超时重传” 机制,确保数据完整性。
  2. Wi-Fi 重连:若开发板频繁断开 Wi-Fi,需检查 AP 信号强度(可通过wifi_vif_sta_rssi_get获取 RSSI 值)、密码正确性,或在代码中增加 “重连次数限制” 避免死循环。
  3. 缓冲区溢出:若串口 / 网络缓冲区频繁溢出,需增大缓冲区大小(UART_TX_BUF_SIZE/UART_RX_BUF_SIZE),或在代码中增加 “缓冲区满” 的错误处理(如丢弃旧数据)。
  4. 调试日志:可在关键位置增加uart340_send输出,通过串口工具查看变量值(如g_wifi_stateuart_rx_len),定位问题所在。


资料下载:

需下载以下勾选的文件


原理图:

全部评论
暂无评论
0/144