基于HAL库移植GD的USB协议栈(Device/CDC_ACM)

前言

本篇文章用到的库文件

1、cubemx-只打开usb接口

2、在keil工程中添加文件

2.1 初始化keil工程设置(老手可跳过)

 2.2 添加文件

3、编译和修改

3.1 修改usb_conf.h文件

3.2 添加预处理符号 preprocessor symbols

 3.3 修改头文件

 4、让USB(用)跑起来

 4.1 结构体定义与初始化

4.2 USB接收

4.3 USB发送

4.4 修改VID和PID


前言

前面说过,cubemx生成的程序在GD芯片上跑是没问题的,但是USB发送和接收必须要控制在64字节一包,而且识别问题解决起来比较复杂。

所以,这篇文章整理一下,在cubemx生成的程序上移植GD协议栈。

本篇文章使用的芯片是GD32F450VGT6系列

本篇文章用到的库文件

如下所示,

        一个是用来替换cubemx生成的Drivers文件夹;(内容其实是老版本的HAL库代码(V1.7.12),因为GD芯片跑最新版的代码总是有各种奇奇怪怪的问题,懒得解决所以一直沿用老版本的)

        二个是GD32的USB协议栈的代码;(内容来自于GD官方的USB_Device_cdc_acm demo代码,官网上都是可以找得到的,别跟我说找不到

链接:https://pan.baidu.com/s/1tNbCkL422lIc4tAKa3-5qA?pwd=edl4 
提取码:edl4

1、cubemx-只打开usb接口

记住:只打开Connectvity的USB接口,Middleware中的USB_DEVICE取消掉

2、在keil工程中添加文件

2.1 初始化keil工程设置(老手可跳过)

1)勾选C库使用和浮点单元不使用

 2)关闭索引编译,防止编译速度过慢

 3)设置优化等级0,GNU扩展和头文件路径包含,3个内容

 4)设置调试工具如JLink,以及其中设置的SW接口与复位重启功能

 2.2 添加文件

如下图,创建两个Groups,分别添加内核code与app代码。

以此类推:

        只要是device端,lib部分代码都是一样的。不同的app如HID、DFU、MSC,只是app端代码不同,只能参考GD的官方demo来裁剪(GD32F4系列固件库\GD32F4xx_Firmware_Library\GD32F4xx_Firmware_Library_V2.0.1\Examples\USB\USB_Device)。

        Host端没做过。。。,参考着官方demo来吧。

3、编译和修改

3.1 修改usb_conf.h文件

//1、修改包含的头文件
//#include "gd32f4xx.h"
//#include "gd32f450i_eval.h"
#include "stm32f4xx.h"
 
 
//2、添加几个USB协议栈需要用到的定义
#define REG32(addr)                  (*(volatile uint32_t *)(uint32_t)(addr))
#define REG16(addr)                  (*(volatile uint16_t *)(uint32_t)(addr))
#define REG8(addr)                   (*(volatile uint8_t *)(uint32_t)(addr))
#define BIT(x)                       ((uint32_t)((uint32_t)0x01U<<(x)))
#define BITS(start, end)             ((0xFFFFFFFFUL << (start)) & (0xFFFFFFFFUL >> (31U - (uint32_t)(end)))) 
#define GET_BITS(regval, start, end) (((regval) & BITS((start),(end))) >> (start))
 
//3、文件末尾__ALIGN_BEGIN 定义后面加上__align(4)
#else
    #define __ALIGN_BEGIN __align(4)
    #define __ALIGN_END
#endif /* USBHS_INTERNAL_DMA_ENABLED */
 
#endif /* USB_CONF_H */

3.2 添加预处理符号 preprocessor symbols

 3.3 修改头文件

此时编译,发现仍有50个error,是与HAL库的"stm32f4xx_ll_usb.h"中的定义存在重复,对于"stm32f4xx_ll_usb.h"中的重复定义逐个消除即可。

改动很小,如下:只要将"stm32f4xx_ll_usb.h"文件中两个枚举定义注释掉即可。

 4、让USB(用)跑起来

 4.1 结构体定义与初始化

void delay_3us(uint32_t count)
{
	for(uint32_t i=0;i<70;i++)
		__NOP();	
}

usb_core_handle_struct cdc_acm =
{
    .dev = 
    {
        .dev_desc = (uint8_t *)&device_descriptor,
        .config_desc = (uint8_t *)&configuration_descriptor,
        .strings = usbd_strings,
        .class_init = cdc_acm_init,
        .class_deinit = cdc_acm_deinit,
        .class_req_handler = cdc_acm_req_handler,
        .class_data_handler = cdc_acm_data_handler
    },

    .udelay = delay_3us,
    .mdelay = HAL_Delay
};

void UsbFsInit(void)
{
	//MX_USB_DEVICE_Init();
	usbd_init(&cdc_acm,USB_FS_CORE_ID);
	//cdc_acm_data_receive(&cdc_acm);
}

4.2 USB接收

1、修改中断调用函数:将原来的 “HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS)” 替换成 “usbd_isr(&cdc_acm)” 。

/**
  * @brief This function handles USB On The Go FS global interrupt.
  */
void OTG_FS_IRQHandler(void)
{
  /* USER CODE BEGIN OTG_FS_IRQn 0 */

  /* USER CODE END OTG_FS_IRQn 0 */
  usbd_isr(&cdc_acm);
  /* USER CODE BEGIN OTG_FS_IRQn 1 */

  /* USER CODE END OTG_FS_IRQn 1 */
}

2、初始化一段时间之后,记得使能接收 cdc_acm_data_receive(&cdc_acm);

同时记得定义接收回调函数 void UsbFSRxDataCallBack(uint8_t *pData,uint32_t *pLen);,并在下图位置调用

3、USB重新建立连接时,要记得使能接收 cdc_acm_data_receive(&cdc_acm);

可参考如下代码(可放置到USB监听线程中)。

while(1)
{
	OSTimeDlyHMSM(0,0,0,100);
	
	if(GetUsbFSConnectState() != usb_status)
	{
		if(usb_status == 0)
		{
			printf("USB_STATUS_ON\r\n");
			cdc_acm_data_receive(&cdc_acm);
		}
		else
			printf("USB_STATUS_OFF\r\n");
		
		usb_status = usb_status?0:1; 
	}
}

4.3 USB发送

发送函数处理如下:

void UsbFsSend(uint8_t *pData,uint16_t unDataLen)
{
	uint16_t i;
	uint32_t ulTimeOut;
	
	for(i=0;i<unDataLen/64+1;i++)
	{
		ulTimeOut = 0;
		
		if(i == unDataLen/64)
		{
			if(unDataLen%64 != 0)
			{
				packet_sent = 0;
				usbd_ep_tx(&cdc_acm,CDC_ACM_DATA_IN_EP,pData+i*64,unDataLen%64);
			}
			else
				break;
		}
		else
		{
			packet_sent = 0;
			usbd_ep_tx(&cdc_acm,CDC_ACM_DATA_IN_EP,pData+i*64,64);
		}
		
		while(1 != packet_sent)
		{
//			delay_10us(10);
//			if (++ulTimeOut > 0xFFF)
			if (++ulTimeOut > 0X0FFFFFFF)	
			{
				log_output(LOG_TAG_POWER,LOG_LEVEL_NORMAL,"usbd_ep_tx fail\r\n");
				return;
			}
		}
//		
//		packet_sent = 0;
//		usbd_ep_tx(&cdc_acm,CDC_ACM_DATA_IN_EP,NULL,0);
	}
	
	//log_output(LOG_TAG_POWER,LOG_LEVEL_NORMAL," usbd_ep_tx %d %d\r\n",unDataLen,ulTimeOut);
}

4.4 修改VID和PID

Over,这时候程序烧录进去基本就可以在电脑端检测到USB串口了,然后测试收发通讯。

  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猪熊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值