萤火工场 GD32VW553-IOT-V2 开发板测评
分享作者:wx17590409409927
评测品牌:萤火工场
评测型号:GD32VW553-IOT-V2
发布时间:2025-10-13 11:22:10
2 1
前言
开源口碑分享内容
#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()注意事项:
- 数据粘包:若串口数据出现粘包,可在帧格式中增加 “帧序号” 或 “超时重传” 机制,确保数据完整性。
- Wi-Fi 重连:若开发板频繁断开 Wi-Fi,需检查 AP 信号强度(可通过
wifi_vif_sta_rssi_get获取 RSSI 值)、密码正确性,或在代码中增加 “重连次数限制” 避免死循环。 - 缓冲区溢出:若串口 / 网络缓冲区频繁溢出,需增大缓冲区大小(
UART_TX_BUF_SIZE/UART_RX_BUF_SIZE),或在代码中增加 “缓冲区满” 的错误处理(如丢弃旧数据)。 - 调试日志:可在关键位置增加
uart340_send输出,通过串口工具查看变量值(如g_wifi_state、uart_rx_len),定位问题所在。
资料下载:
需下载以下勾选的文件

原理图:

全部评论
暂无评论
0/144
