GD32H759I-EVAL评估板USB虚拟串口通信速率评估
修改固件库 GD32H7xx_Firmware_Library_V1.4.0中的cdc_acm_core.c。连接:评估板的USB_HS0(虚拟串口)连接电脑,USART(串口打印)连接串口。工程文件:在评估板配套资料的27_USB_Device里面的CDC_ACM上。跳线帽把JP68跳至USART即可。
·
评估板:GD32H759I-EVAL
连接:评估板的USB_HS0(虚拟串口)连接电脑,USART(串口打印)连接电脑

工程文件:在评估板配套资料的27_USB_Device里面的CDC_ACM上直接修改main.c代码

跳线帽把JP68跳至USART即可。
工程文件的代码即USB虚拟串口通信,在此基础上加上速率评估的功能:
修改main.c
#include "drv_usb_hw.h"
#include "cdc_acm_core.h"
#include "gd32h759i_eval.h"
#include "system_gd32h7xx.h"
#include <stdio.h>
#include <string.h>
// USB核心驱动实例
usb_core_driver cdc_acm;
// 通信统计配置与变量
#define COMM_STOP_DELAY 2000U // 通信停止判断阈值(ms)
#define USER_DATA_MIN_LEN 1U // 最小有效数据长度(字节)
volatile uint32_t systick_counter = 0; // 毫秒计数器
uint32_t start_time = 0; // 通信开始时间(ms)
uint32_t last_comm_time = 0; // 最后一次通信时间(ms)
uint32_t total_frames = 0; // 总用户数据帧数
uint32_t current_window_frames = 0; // 当前统计窗口内的帧数
uint32_t window_start_time = 0; // 当前统计窗口的开始时间
uint32_t max_fps = 0; // 最大帧率
FlagStatus is_communicating = RESET; // 通信状态标志
FlagStatus has_new_data = RESET; // 新数据标志
// 函数声明
void cache_enable(void);
void usart_init(void);
uint32_t get_current_time(void);
void check_comm_stop(void);
void update_comm_stats(void);
void calculate_final_stats(void);
void systick_config(void);
int fputc(int ch, FILE *f);
void user_data_receive_hook(uint8_t *data, uint32_t len);
/*!
\brief 启用CPU缓存
*/
void cache_enable(void)
{
SCB_EnableICache();
#ifndef USB_INTERNAL_DMA_ENABLED
SCB_EnableDCache();
#endif
}
/*!
\brief 初始化USART用于统计打印
*/
void usart_init(void)
{
gd_eval_com_init(EVAL_COM); // 评估板默认串口
gd_eval_led_init(LED2);
gd_eval_led_off(LED2);
}
/*!
\brief 获取当前时间(毫秒)
*/
uint32_t get_current_time(void)
{
return systick_counter;
}
/*!
\brief 检查通信是否停止
*/
void check_comm_stop(void)
{
if(is_communicating == SET) {
uint32_t current_time = get_current_time();
if((current_time - last_comm_time) >= COMM_STOP_DELAY) {
calculate_final_stats();
uint32_t total_time = last_comm_time - start_time;
if(total_time == 0) total_time = 1;
printf("\r\n===== 通信结束统计 =====");
printf("\r\n总通信时间: %d ms (%.2f 秒)",
total_time, total_time / 1000.0f);
printf("\r\n总用户数据帧数: %d 帧", total_frames);
printf("\r\n平均帧率: %.1f 帧/秒",
(float)total_frames / (total_time / 1000.0f));
printf("\r\n最大帧率: %d 帧/秒", max_fps);
printf("\r\n========================\r\n");
printf("等待用户数据...\r\n");
// 重置统计变量
is_communicating = RESET;
total_frames = 0;
max_fps = 0;
start_time = 0;
current_window_frames = 0;
window_start_time = 0;
gd_eval_led_off(LED2);
}
}
}
/*!
\brief 通信结束时计算最后一段的帧率
*/
void calculate_final_stats(void)
{
if(current_window_frames > 0) {
uint32_t window_duration = last_comm_time - window_start_time;
if(window_duration == 0) window_duration = 1;
uint32_t fps = (current_window_frames * 1000) / window_duration;
if(fps > max_fps) {
max_fps = fps;
}
printf("\r\n[结束前统计] 最后 %.1f 秒内帧率: %d 帧/秒",
window_duration / 1000.0f, fps);
}
}
/*!
\brief 更新通信统计
*/
void update_comm_stats(void)
{
uint32_t current_time = get_current_time();
if(is_communicating == RESET) {
is_communicating = SET;
start_time = current_time;
last_comm_time = current_time;
window_start_time = current_time;
current_window_frames = 0;
max_fps = 0;
printf("\r\n===== 检测到用户数据,开始实时统计 =====");
gd_eval_led_on(LED2);
}
last_comm_time = current_time;
total_frames++;
current_window_frames++;
// 每1秒打印一次实时帧率
if((current_time - window_start_time) >= 1000U) {
uint32_t fps = current_window_frames;
if(fps > max_fps) {
max_fps = fps;
}
printf("\r\n[实时统计] 已通信 %.1f 秒 - 帧率: %d 帧/秒 (累计: %d 帧)",
(current_time - start_time)/1000.0f, fps, total_frames);
window_start_time = current_time;
current_window_frames = 0;
gd_eval_led_toggle(LED2);
}
}
/*!
\brief 重定向printf到USART
*/
int fputc(int ch, FILE *f)
{
usart_data_transmit(EVAL_COM, (uint8_t)ch);
while(RESET == usart_flag_get(EVAL_COM, USART_FLAG_TBE));
return ch;
}
/*!
\brief 配置SysTick定时器(1ms中断)
*/
void systick_config(void)
{
if(SysTick_Config(SystemCoreClock / 1000U)) {
while(1);
}
NVIC_SetPriority(SysTick_IRQn, 0x0FU);
}
/*!
\brief SysTick中断服务程序(毫秒计数)
*/
void SysTick_Handler(void)
{
systick_counter++;
}
/*!
\brief 用户数据接收钩子函数(关键修改:所有数据都计数)
*/
void user_data_receive_hook(uint8_t *data, uint32_t len)
{
// 只要数据长度符合要求,就视为有效帧并计数
if(len >= USER_DATA_MIN_LEN) {
last_comm_time = get_current_time();
has_new_data = SET; // 所有符合长度的数据包都触发统计更新
}
}
/*!
\brief 主函数
*/
int main(void)
{
cache_enable();
usart_init();
systick_config();
printf("USB虚拟串口就绪!请通过串口助手(COM11)发送数据...\r\n");
// USB初始化
usb_rcu_config();
usb_timer_init();
#ifdef USE_USBHS0
#ifdef USE_USB_FS
usb_para_init(&cdc_acm, USBHS0, USB_SPEED_FULL);
#endif
#ifdef USE_USB_HS
usb_para_init(&cdc_acm, USBHS0, USB_SPEED_HIGH);
#endif
#endif
#ifdef USE_USBHS1
#ifdef USE_USB_FS
usb_para_init(&cdc_acm, USBHS1, USB_SPEED_FULL);
#endif
#ifdef USE_USB_HS
usb_para_init(&cdc_acm, USBHS1, USB_SPEED_HIGH);
#endif
#endif
usbd_init(&cdc_acm, &cdc_desc, &cdc_class);
#ifdef USE_USB_HS
#ifndef USE_ULPI_PHY
#ifdef USE_USBHS0
pllusb_rcu_config(USBHS0);
#elif defined USE_USBHS1
pllusb_rcu_config(USBHS1);
#endif
#endif
#endif
usb_intr_config();
// 主循环
while(1) {
if(USBD_CONFIGURED == cdc_acm.dev.cur_status) {
if(0U == cdc_acm_check_ready(&cdc_acm)) {
cdc_acm_data_receive(&cdc_acm);
} else {
cdc_acm_data_send(&cdc_acm);
}
if(has_new_data) {
update_comm_stats();
has_new_data = RESET;
}
}
check_comm_stop();
}
}
修改固件库 GD32H7xx_Firmware_Library_V1.4.0中的cdc_acm_core.c
#include "cdc_acm_core.h"
// 新增:声明用户数据接收钩子函数(与app.c中的函数对应)
extern void user_data_receive_hook(uint8_t *data, uint32_t len);
#define USBD_VID 0x28E9U
#define USBD_PID 0x018AU
/* note:it should use the C99 standard when compiling the below codes */
/* USB standard device descriptor */
__ALIGN_BEGIN const usb_desc_dev cdc_dev_desc __ALIGN_END = {
.header =
{
.bLength = USB_DEV_DESC_LEN,
.bDescriptorType = USB_DESCTYPE_DEV,
},
.bcdUSB = 0x0200U,
.bDeviceClass = USB_CLASS_CDC,
.bDeviceSubClass = 0x00U,
.bDeviceProtocol = 0x00U,
.bMaxPacketSize0 = USB_FS_EP0_MAX_LEN,
.idVendor = USBD_VID,
.idProduct = USBD_PID,
.bcdDevice = 0x0100U,
.iManufacturer = STR_IDX_MFC,
.iProduct = STR_IDX_PRODUCT,
.iSerialNumber = STR_IDX_SERIAL,
.bNumberConfigurations = USBD_CFG_MAX_NUM,
};
/* USB device configuration descriptor */
__ALIGN_BEGIN const usb_cdc_desc_config_set cdc_config_desc __ALIGN_END = {
.config =
{
.header =
{
.bLength = sizeof(usb_desc_config),
.bDescriptorType = USB_DESCTYPE_CONFIG,
},
.wTotalLength = USB_CDC_ACM_CONFIG_DESC_SIZE,
.bNumInterfaces = 0x02U,
.bConfigurationValue = 0x01U,
.iConfiguration = 0x00U,
.bmAttributes = 0x80U,
.bMaxPower = 0x32U
},
.cmd_itf =
{
.header =
{
.bLength = sizeof(usb_desc_itf),
.bDescriptorType = USB_DESCTYPE_ITF
},
.bInterfaceNumber = 0x00U,
.bAlternateSetting = 0x00U,
.bNumEndpoints = 0x01U,
.bInterfaceClass = USB_CLASS_CDC,
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
.bInterfaceProtocol = USB_CDC_PROTOCOL_AT,
.iInterface = 0x00U
},
.cdc_header =
{
.header =
{
.bLength = sizeof(usb_desc_header_func),
.bDescriptorType = USB_DESCTYPE_CS_INTERFACE
},
.bDescriptorSubtype = 0x00U,
.bcdCDC = 0x0110U
},
.cdc_call_managment =
{
.header =
{
.bLength = sizeof(usb_desc_call_managment_func),
.bDescriptorType = USB_DESCTYPE_CS_INTERFACE
},
.bDescriptorSubtype = 0x01U,
.bmCapabilities = 0x00U,
.bDataInterface = 0x01U
},
.cdc_acm =
{
.header =
{
.bLength = sizeof(usb_desc_acm_func),
.bDescriptorType = USB_DESCTYPE_CS_INTERFACE
},
.bDescriptorSubtype = 0x02U,
.bmCapabilities = 0x02U,
},
.cdc_union =
{
.header =
{
.bLength = sizeof(usb_desc_union_func),
.bDescriptorType = USB_DESCTYPE_CS_INTERFACE
},
.bDescriptorSubtype = 0x06U,
.bMasterInterface = 0x00U,
.bSlaveInterface0 = 0x01U,
},
.cdc_cmd_endpoint =
{
.header =
{
.bLength = sizeof(usb_desc_ep),
.bDescriptorType = USB_DESCTYPE_EP,
},
.bEndpointAddress = CDC_CMD_EP,
.bmAttributes = USB_EP_ATTR_INT,
.wMaxPacketSize = USB_CDC_CMD_PACKET_SIZE,
.bInterval = 0x0AU
},
.cdc_data_interface =
{
.header =
{
.bLength = sizeof(usb_desc_itf),
.bDescriptorType = USB_DESCTYPE_ITF,
},
.bInterfaceNumber = 0x01U,
.bAlternateSetting = 0x00U,
.bNumEndpoints = 0x02U,
.bInterfaceClass = USB_CLASS_DATA,
.bInterfaceSubClass = 0x00U,
.bInterfaceProtocol = USB_CDC_PROTOCOL_NONE,
.iInterface = 0x00U
},
.cdc_out_endpoint =
{
.header =
{
.bLength = sizeof(usb_desc_ep),
.bDescriptorType = USB_DESCTYPE_EP,
},
.bEndpointAddress = CDC_DATA_OUT_EP,
.bmAttributes = USB_EP_ATTR_BULK,
.wMaxPacketSize = USB_CDC_DATA_PACKET_SIZE,
.bInterval = 0x00U
},
.cdc_in_endpoint =
{
.header =
{
.bLength = sizeof(usb_desc_ep),
.bDescriptorType = USB_DESCTYPE_EP
},
.bEndpointAddress = CDC_DATA_IN_EP,
.bmAttributes = USB_EP_ATTR_BULK,
.wMaxPacketSize = USB_CDC_DATA_PACKET_SIZE,
.bInterval = 0x00U
}
};
/* USB language ID Descriptor */
__ALIGN_BEGIN static const usb_desc_LANGID usbd_language_id_desc __ALIGN_END = {
.header =
{
.bLength = sizeof(usb_desc_LANGID),
.bDescriptorType = USB_DESCTYPE_STR,
},
.wLANGID = ENG_LANGID
};
/* USB manufacture string */
__ALIGN_BEGIN static const usb_desc_str manufacturer_string __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(10U),
.bDescriptorType = USB_DESCTYPE_STR,
},
.unicode_string = {'G', 'i', 'g', 'a', 'D', 'e', 'v', 'i', 'c', 'e'}
};
/* USB product string */
__ALIGN_BEGIN static const usb_desc_str product_string __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(12U),
.bDescriptorType = USB_DESCTYPE_STR,
},
.unicode_string = {'G', 'D', '3', '2', '-', 'C', 'D', 'C', '_', 'A', 'C', 'M'}
};
/* USB serial string */
__ALIGN_BEGIN static usb_desc_str serial_string __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(12U),
.bDescriptorType = USB_DESCTYPE_STR,
}
};
/* USB string descriptor set */
void *const usbd_cdc_strings[] = {
[STR_IDX_LANGID] = (uint8_t *)&usbd_language_id_desc,
[STR_IDX_MFC] = (uint8_t *)&manufacturer_string,
[STR_IDX_PRODUCT] = (uint8_t *)&product_string,
[STR_IDX_SERIAL] = (uint8_t *)&serial_string
};
usb_desc cdc_desc = {
.dev_desc = (uint8_t *)&cdc_dev_desc,
.config_desc = (uint8_t *)&cdc_config_desc,
.strings = usbd_cdc_strings
};
/* local function prototypes ('static') */
static uint8_t cdc_acm_init(usb_dev *udev, uint8_t config_index);
static uint8_t cdc_acm_deinit(usb_dev *udev, uint8_t config_index);
static uint8_t cdc_acm_req(usb_dev *udev, usb_req *req);
static uint8_t cdc_ctlx_out(usb_dev *udev);
static uint8_t cdc_acm_in(usb_dev *udev, uint8_t ep_num);
static uint8_t cdc_acm_out(usb_dev *udev, uint8_t ep_num);
/* USB CDC device class callbacks structure */
usb_class_core cdc_class = {
.command = NO_CMD,
.alter_set = 0U,
.init = cdc_acm_init,
.deinit = cdc_acm_deinit,
.req_proc = cdc_acm_req,
.ctlx_out = cdc_ctlx_out,
.data_in = cdc_acm_in,
.data_out = cdc_acm_out
};
/*!
\brief check CDC ACM is ready for data transfer
\param[in] udev: pointer to USB device instance
\param[out] none
\retval 0 if CDC is ready, else 1
*/
uint8_t cdc_acm_check_ready(usb_dev *udev)
{
if(NULL != udev->dev.class_data[CDC_COM_INTERFACE]) {
usb_cdc_handler *cdc = (usb_cdc_handler *)udev->dev.class_data[CDC_COM_INTERFACE];
if((1U == cdc->packet_receive) && (1U == cdc->packet_sent)) {
return 0U;
}
}
return 1U;
}
/*!
\brief send CDC ACM data
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
void cdc_acm_data_send(usb_dev *udev)
{
usb_cdc_handler *cdc = (usb_cdc_handler *)udev->dev.class_data[CDC_COM_INTERFACE];
if(0U != cdc->receive_length) {
cdc->packet_sent = 0U;
usbd_ep_send(udev, CDC_DATA_IN_EP, (uint8_t *)(cdc->data), cdc->receive_length);
cdc->receive_length = 0U;
}
}
/*!
\brief receive CDC ACM data
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
void cdc_acm_data_receive(usb_dev *udev)
{
usb_cdc_handler *cdc = (usb_cdc_handler *)udev->dev.class_data[CDC_COM_INTERFACE];
cdc->packet_receive = 0U;
cdc->packet_sent = 0U;
usbd_ep_recev(udev, CDC_DATA_OUT_EP, (uint8_t *)(cdc->data), USB_CDC_DATA_PACKET_SIZE);
}
/*!
\brief initialize the CDC ACM device
\param[in] udev: pointer to USB device instance
\param[in] config_index: configuration index
\param[out] none
\retval USB device operation status
*/
static uint8_t cdc_acm_init(usb_dev *udev, uint8_t config_index)
{
static usb_cdc_handler cdc_handler;
/* initialize the data TX endpoint */
usbd_ep_setup(udev, &(cdc_config_desc.cdc_in_endpoint));
/* initialize the data RX endpoint */
usbd_ep_setup(udev, &(cdc_config_desc.cdc_out_endpoint));
/* initialize the command TX endpoint */
usbd_ep_setup(udev, &(cdc_config_desc.cdc_cmd_endpoint));
/* initialize CDC handler structure */
cdc_handler.packet_receive = 1U;
cdc_handler.packet_sent = 1U;
cdc_handler.receive_length = 0U;
cdc_handler.line_coding = (acm_line) {
.dwDTERate = 115200U,
.bCharFormat = 0U,
.bParityType = 0U,
.bDataBits = 0x08U
};
udev->dev.class_data[CDC_COM_INTERFACE] = (void *)&cdc_handler;
return USBD_OK;
}
/*!
\brief de-initialize the CDC ACM device
\param[in] udev: pointer to USB device instance
\param[in] config_index: configuration index
\param[out] none
\retval USB device operation status
*/
static uint8_t cdc_acm_deinit(usb_dev *udev, uint8_t config_index)
{
/* deinitialize the data TX/RX endpoint */
usbd_ep_clear(udev, CDC_DATA_IN_EP);
usbd_ep_clear(udev, CDC_DATA_OUT_EP);
/* deinitialize the command TX endpoint */
usbd_ep_clear(udev, CDC_CMD_EP);
return USBD_OK;
}
/*!
\brief handle the CDC ACM class-specific requests
\param[in] udev: pointer to USB device instance
\param[in] req: device class-specific request
\param[out] none
\retval USB device operation status
*/
static uint8_t cdc_acm_req(usb_dev *udev, usb_req *req)
{
usb_cdc_handler *cdc = (usb_cdc_handler *)udev->dev.class_data[CDC_COM_INTERFACE];
usb_transc *transc = NULL;
switch(req->bRequest) {
case SEND_ENCAPSULATED_COMMAND:
/* no operation for this driver */
break;
case GET_ENCAPSULATED_RESPONSE:
/* no operation for this driver */
break;
case SET_COMM_FEATURE:
/* no operation for this driver */
break;
case GET_COMM_FEATURE:
/* no operation for this driver */
break;
case CLEAR_COMM_FEATURE:
/* no operation for this driver */
break;
case SET_LINE_CODING:
transc = &udev->dev.transc_out[0];
/* set the value of the current command to be processed */
udev->dev.class_core->alter_set = req->bRequest;
/* enable EP0 prepare to receive command data packet */
transc->remain_len = req->wLength;
transc->xfer_buf = cdc->cmd;
break;
case GET_LINE_CODING:
transc = &udev->dev.transc_in[0];
cdc->cmd[0] = (uint8_t)(cdc->line_coding.dwDTERate);
cdc->cmd[1] = (uint8_t)(cdc->line_coding.dwDTERate >> 8);
cdc->cmd[2] = (uint8_t)(cdc->line_coding.dwDTERate >> 16);
cdc->cmd[3] = (uint8_t)(cdc->line_coding.dwDTERate >> 24);
cdc->cmd[4] = cdc->line_coding.bCharFormat;
cdc->cmd[5] = cdc->line_coding.bParityType;
cdc->cmd[6] = cdc->line_coding.bDataBits;
transc->xfer_buf = cdc->cmd;
transc->remain_len = 7U;
break;
case SET_CONTROL_LINE_STATE:
/* no operation for this driver */
break;
case SEND_BREAK:
/* no operation for this driver */
break;
default:
break;
}
return USBD_OK;
}
/*!
\brief handle CDC ACM control OUT stage
\param[in] udev: pointer to USB device instance
\param[out] none
\retval USB device operation status
*/
static uint8_t cdc_ctlx_out(usb_dev *udev)
{
usb_cdc_handler *cdc = (usb_cdc_handler *)udev->dev.class_data[CDC_COM_INTERFACE];
if(NO_CMD != udev->dev.class_core->alter_set) {
/* process the command data */
cdc->line_coding.dwDTERate = (uint32_t)((uint32_t)cdc->cmd[0] | \
((uint32_t)cdc->cmd[1] << 8) | \
((uint32_t)cdc->cmd[2] << 16) | \
((uint32_t)cdc->cmd[3] << 24));
cdc->line_coding.bCharFormat = cdc->cmd[4];
cdc->line_coding.bParityType = cdc->cmd[5];
cdc->line_coding.bDataBits = cdc->cmd[6];
udev->dev.class_core->alter_set = NO_CMD;
}
return USBD_OK;
}
/*!
\brief handle CDC ACM data IN stage
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint identifier
\param[out] none
\retval USB device operation status
*/
static uint8_t cdc_acm_in(usb_dev *udev, uint8_t ep_num)
{
usb_transc *transc = &udev->dev.transc_in[EP_ID(ep_num)];
usb_cdc_handler *cdc = (usb_cdc_handler *)udev->dev.class_data[CDC_COM_INTERFACE];
if((0U == transc->xfer_len % transc->max_len) && (0U != transc->xfer_len)) {
usbd_ep_send(udev, ep_num, NULL, 0U);
} else {
cdc->packet_sent = 1U;
}
return USBD_OK;
}
/*!
\brief handle CDC ACM data OUT stage(修改部分:添加钩子调用)
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint identifier
\param[out] none
\retval USB device operation status
*/
static uint8_t cdc_acm_out(usb_dev *udev, uint8_t ep_num)
{
usb_cdc_handler *cdc = (usb_cdc_handler *)udev->dev.class_data[CDC_COM_INTERFACE];
// 库原有逻辑:更新接收状态和长度
cdc->packet_receive = 1U;
cdc->receive_length = ((usb_core_driver *)udev)->dev.transc_out[ep_num].xfer_count;
// 新增:当收到有效用户数据时,调用钩子函数
if(cdc->receive_length > 0) {
// cdc->data是接收缓冲区,receive_length是实际长度
user_data_receive_hook(cdc->data, cdc->receive_length);
}
return USBD_OK;
}
打开两个串口调试助手,一个是USB虚拟串口进行通信,另一个是USART进行结果打印:

完整工程文件:时叶彤/MyTest - Gitee.com
更多推荐



所有评论(0)