GD32F3x0 USB CDC应用
本文有点长,描述了从0开始移植驱动到应用的过程和思路
准备工作:
因项目需求这两天需要做个USB的虚拟COM口发卡器,实现双向通讯,由于功能较为简单我们选择GD32F350来开发。
先跑跑官方例程:
GD32F3x0_Firmware_Library_V2.2.1\Examples\USBFS\USB_Device\cdc_acm
安装GD32 USB驱动:
USB_Virtual_Com_Port_Driver_v2.0.2.2673
我这里采用keil MDK5来开发,keil的安装这里省略。
安装GD32的DFP包:
https://www.gd32mcu.com/cn/download?kw=GD32F3x&lan=cn
GD32F3x0 AddOn 3.0.0
由于我是用的MDK5,例程采用MDK4,这里我们修改工程后缀
\Examples\USBFS\USB_Device\cdc_acm\MDK-ARM\cdc_acm.uvproj
复制 cdc_acm.uvproj,修改为 cdc_acm.uvprojx
打开项目后是无法编译的(原因MDK5是采用CMSIS驱动),按以下方法添加CMSIS
接下来就可以正常编译和下载了
运行起来,能正常打开COM口,发数据能正常接收,验证板子和例程都没问题。
阅读代码:
阅读例程,不难发现CDC用到了USB类文件
\Firmware\GD32F3x0_usbfs_library\device\class\cdc\Source\cdc_acm_core.c
正式开始阅读:找到app.c main()函数
int main(void)
{
usb_rcu_config(); //初始化时钟
usb_timer_init(); //初始化定时器器,USB需要用到定时器做精准延时
usbd_init(&cdc_acm, USB_CORE_ENUM_FS, &cdc_desc, &cdc_class); //初始化USB
usb_intr_config(); //初始化中断
while(1) {
/* main loop */
if(USBD_CONFIGURED == cdc_acm.dev.cur_status) {
//检查USB是否准备就续
if(0U == cdc_acm_check_ready(&cdc_acm)) {
//检查数据是否准备好,当为0时说明有数据需要接
cdc_acm_data_receive(&cdc_acm); //接收数据,这里不难发现我们不知道接收到的数据在哪里
} else {
cdc_acm_data_send(&cdc_acm); //发送数据,这里也不知道发的数据在哪里,\
或者说我们想法自己的数据该 怎么发?
}}}} //为了节约点文章篇幅,我们改改格式
接下来我们把收发的三个函数贴上来
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)) {
//这里发现接收和发送都为1才就续--为什么?
return 0U;
}
}
return 1U;
}
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; //接收数据前已经把这两个标识置为0了
cdc->packet_sent = 0U;
//不难发现这个是从数据out端点读数据,数据存放在cdc->data中,每个包最大接收64Byte。
//实际收到多少数据我们知道吗? -》NO,这里先不管吧,先大致过一下程序
usbd_ep_recev(udev, CDC_DATA_OUT_EP, (uint8_t*)(cdc->data), USB_CDC_DATA_PACKET_SIZE);//这里才是接收数据
}