用stm32Cube生成103c8t6的usb驱动

发布者:Yuexiang888最新更新时间:2019-06-13 来源: eefocus关键字:stm32Cube  103c8t6  usb驱动 手机看文章 扫描二维码
随时随地手机看文章

开发板是用的stm32f103c8t6的核心板淘宝上最便宜最常见的那种(还是上图吧) 

这里写图片描述

stm32f1系列的hal驱动库中把usb驱动放在了“Middlewares(中间件)”文件夹下,且有“STM32_USB_Device_Library”和“STM32_USB_Host_Library”两个驱动库。若是想直接利用驱动库新建工程可在这两个库的目录下复制Core文件夹和Class文件夹下所需文件。


使用stm32cube直接生成工程: 

这里写图片描述


先勾选Peripherals的USB->Device才能在MiddleWares下的USB_Devics下选择IP核,下拉后有六个选项,分别对应USB设备的六大分类:


分类 功能

AUDIO USB音频设备

CDC(communication device class) 虚拟串口

Download Firmware Update 固件更新

Human Interface Device 人机接口

Custom Human Interface Device 传统人机接口(键盘鼠标类)

Mass Storage Class 大容量存储设备

此处选择CDC设备。需要注意的是,在RCC分类下的HSE必须被使能成外部晶振->Crystal/ceramic Resonator。要开启USB核主时钟必须达到48M,因为USB区域的时钟为48M。这点可在时钟树的配置中很清楚看到。不过,若主时钟没有配置成48M或以上,在STM32Cube的Clock Configuartion界面会直接报错,点进去看会提示是否自动设置时钟,直接确认就OK。 


这里写图片描述

PLL倍频数位6的话,USB分频设为1即可,若要开到72M的最高频率,分频数则要设为1.5。其余配置均保持默认即可。


此处还开启了SWD引脚用于代码的下载。最终的引脚配置如图: 


这里写图片描述

最后点击生成代码,工程名称为:USBCDC 使用MDK-ARM/V5。下面的Stack Size可以改大些,USB CDC的RX和TX的Buffer默认大小为2048,只是单纯的收发测试的话,默认大小也够用,为避免造成内存溢出导致的跑飞,此处设为0x1000。在Code generator界面最好勾选->为每个外设生成单独的.c和.h文件 这个选项。这样生成的外设驱动能比较方便的移植,另外分类清晰,可读性也较好,否则全部代码都堆在main文件中。 


这里写图片描述

生成代码后直接打开工程编译。 


这里写图片描述

Middlewares分类下为USB内核代码,这些文件都不用改动。 

Application下的stm32f1xx_it.c文件中含有目前工程中所开启的所有中断处理函数代码,打开翻到这个文件的最下面可以看到,STM32Cube默认为USB开启了中断:


/**

    * @brief This function handles USB low priority or CAN RX0 interrupts.

*/

void USB_LP_CAN1_RX0_IRQHandler(void)

{

  /* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 0 */


  /* USER CODE END USB_LP_CAN1_RX0_IRQn 0 */

  HAL_PCD_IRQHandler(&hpcd_USB_FS);

  /* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 1 */


  /* USER CODE END USB_LP_CAN1_RX0_IRQn 1 */

}


从中断函数函数名可以看出,USB和CAN是公用一个中断向量的。在f1系列的参考文档中也可以查到,CAN和USB占用同一段RAM,这两个外设不能同时使用。


stm32f1xx_hal_msp.c文件只含有HAL_MspInit()这一个函数,实现NVIC分组和系统中断优先级的设定(都是最高优先级),并禁用JTAG和SWD使能(若在Cube配置中没有开启SYS->Serial Wire,则不会有这一句)。


usb_device.c文件初始化了一个USB句柄。由于是HAL库的驱动,整体的代码对于外设初始化和调用都是通过句柄。


/* USB Device Core handle declaration */

USBD_HandleTypeDef hUsbDeviceFS;

/* init function */                                        

void MX_USB_DEVICE_Init(void)

{

    USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS);

    USBD_RegisterClass(&hUsbDeviceFS, &USBD_CDC);

    USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_Interface_fops_FS);

    USBD_Start(&hUsbDeviceFS);

}


去掉多余部分注释(实话说Cube生成的工程注释太多挺烦的),可以看出这是USB外设的初始化函数;USBD_Init()里面包含了,检测主机是否存在,复位USB类,设定USB设备描述符,还有USB设备状态几个步骤,最后初始化了单片机上面的USB底层驱动:端点号啊,全速模式啊,失能低功耗,外部供电关闭啊,blabalbal…然后会跑到USBD_LL_MspInit()中去开启USB的引脚。USB引脚比较特殊,只要单片机内的USB区域时钟开启,那PA12和PA11就只能作为USB的数据引脚使用。


USBD_RegisterClass()会连接CDC类驱动到USB核,这点在HAL固件库里有所体现,USB的设备库分成了两层,一层为内核文件,就是只要开启了USB就要调用的,还有一层为“类”,对应它的不同功能,详见上面那个表格,只需根据需要将一类代码加入工程即可。至于函数内具体的细节,则需要对应驱动库的说明看了(不知是我人品不好还是怎么,在哪都找不到hal库 usb的说明文档,只能找到以前的标准库的)。


USBD_CDC_RegisterInterface()则是不知道要干嘛了,进入函数定义只能看到简短的描述,@brief USBD_CDC_RegisterInterface(找不到文档的痛(ㄒ_ㄒ))。最后一个倒是很容易看懂了‘USBD_Start()’开启外设。


usb_conf.c文件则是关于USB的配置,这里除开HAL库的部分,还有部分代码是使用LL库写的(底层驱动部分)。但USB协议部分都是包含在stm32f1x_hal_pcd.c和stm32f1x_hal_pcd_ex.cz这两组文件中(为嘛不用stm32f1x_hal_usb.c文件名害得我一顿好找)。有兴趣的可以详细看看分析,只是要使用这个接口的话,也不用深究了。 

然后usb_desc.c文件也是关于协议这一块(目前学的不深就不多说了)。但有一点要补充的,这个两个文件里的代码——不要动!不要动!不要动!


usb_cdc_if.c这里就是对与USB的CDC类的直接调用了。文件不是很大,里面定义了用户的收发缓存,都是2048byte。然后有4个静态函数和一个可供外部调用的函数:


static int8_t CDC_Init_FS     (void);

static int8_t CDC_DeInit_FS   (void);

static int8_t CDC_Control_FS  (uint8_t cmd, uint8_t* pbuf, uint16_t length);

static int8_t CDC_Receive_FS  (uint8_t* pbuf, uint32_t *Len);

uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)


见名生意,全速设备CDC类初始化、CDC类复位、CDC类控制、CDC类接收还有CDC类发送函数。初始化复位和控制这3个函数都没什么好说的。


重点是CDC_Receive_FS 和CDC_Transmit_FS 这两个函数。这里必须强调CDC_Receive_FS 是USB接收中断回调函数。有必要提一下HAL库和标准库的一个区别:使用标准库时,在配置中断时,一般都是在启动文件中寻找中断向量名,以此名字编写中断处理函数。函数里面有两个重要的部分,一是清除中断标志位,二是实现用户需要的功能。内容部分都是要自己写的。HAL库中,对于中断处理函数做了封装,函数名和函数体都已经写好,进入中断处理函数就判断中断源,然后清除中断标志位,接着进入中断回调函数。唯一允许改动的则是这个中断回调函数,它在库中使用weak修饰,即允许user在工程的任何地方对该函数重定义,以实现所需的功能。CDC_Receive_FS则是CDC类对应USB库的中断回调函数。


/**

  * @brief  CDC_Receive_FS

  *         Data received over USB OUT endpoint are sent over CDC interface 

  *         through this function.

  *           

  *         @note

  *         This function will block any OUT packet reception on USB endpoint 

  *         untill exiting this function. If you exit this function before transfer

  *         is complete on CDC interface (ie. using DMA controller) it will result 

  *         in receiving more data while previous ones are still not sent.

  *                 

  * @param  Buf: Buffer of data to be received

  * @param  Len: Number of data received (in bytes)

  * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL

  */

static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)

{

  /* USER CODE BEGIN 6 */

  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);

  USBD_CDC_ReceivePacket(&hUsbDeviceFS);

  return (USBD_OK);

  /* USER CODE END 6 */ 

}


从注释也能看出,在从USB虚拟串口接收到任何数据后,都会将接收到的数组的指针和数组长度传回此函数。若需要实现某些功能,只需在重设接收缓存前新增函数或者直接处理即可。 

而允许外部调用的那个CDC_Transmit_FS(),则是像串口发送函数那样直接调用即可,输入要发送的数组的指针和要发送的长度,然后就可以在电脑端的虚拟串口处直接接收到所发送的数据。


我有尝试过像串口输出重定向那样,使用CDC_Transmit_FS()函数重定向fputc欲实现printf函数通过虚拟串口的打印。但实测每次发送字符串或者长一点的符号什么的,就只能接收到第一个字符。重定向代码还是贴出来吧,思路应该是正确的,肯定还有哪些点没注意到…如果有大佬看到的话,还劳烦指点迷津,不胜感激。


int fputc(int ch, FILE *f)   

{

  CDC_Transmit_FS((uint8_t *)&ch, 1); 

  return ch;

}


回到正题,剩下gpio.c文件还没说,这里面只有一个MX_GPIO_Init() 函数,用来开启GPIOA和GPIOD两组引脚的时钟,GPIOA是USB的D+和D-引脚以及下载口SWDIO和SWCLK的时钟,GPIOD则是HSE主时钟的晶振引脚的时钟了。


工程内的主要代码说完,再看主函数main()(依旧去除烦人的注释)


int main(void)

{

  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();

  MX_USB_DEVICE_Init();

  while (1)

  {

  }

}


使用STM32Cube生成的代码,都是一个样:首先初始化hal库,然后配置系统时钟,开启要用的GPIO口时钟,然后初始化外设,再加一个空的while循环,只是帮助user初始化要用到的外设,要实现其他功能,还要自己往里加。MX_USB_DEVICE_Init() 函数在前面说usb_device.c的时候讲过,不再重复。


这里实现一个简单的功能:把从虚拟串口接收到的数据,原样从虚拟串口输出。只需在usb接收中断回调函数中做简单的处理,修改代码如下:


static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)

{

  /* USER CODE BEGIN 6 */

  CDC_Transmit_FS(Buf,*Len);

  CDC_Transmit_FS((uint8_t *)"n",sizeof("n"));


  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);

  USBD_CDC_ReceivePacket(&hUsbDeviceFS);

  return (USBD_OK);

  /* USER CODE END 6 */ 

}


然后编译。由于STM32Cube生成的代码默认调试工具为ST-LINK如果使用J-LINK,还需在Options中更改调试工具,并选成SW模式。下载完成后用一根micro-B接口的数据线连到电脑。补充一点,需要先装stm32的虚拟串口驱动,电脑才能识别(安装完后重新插拔USB上电)。 

 这里写图片描述 

这时电脑上就会识别到虚拟串口(我也不知道我电脑为什么不是显示stmicroxxx visual com port的标志,可能win10系统的原因吧)。打开任意串口调试助手(波特率什么的随意,因为实际情况是通过usb总线传输的数据,与波特率什么的无关) 

 这里写图片描述 

任意发送即可看到回复。 

[注]:在使用CDC_Transmit_FS函数发送数据时,若len为64的整数倍,则需要在发送完成后在发送一个空帧;否则电脑端会直接丢去此次发送的数据,这是由usb的协议所决定的,有兴趣的可以看看官方这里给出的解释在进行USB CDC类开发时,无法发送64整数倍的数据,或详细分析usb传输协议。


关键字:stm32Cube  103c8t6  usb驱动 引用地址:用stm32Cube生成103c8t6的usb驱动

上一篇:STM32时钟树与启动过程时钟设置分析
下一篇:STM32使用TCP透传方式与ONENET进行连接的简单教程

推荐阅读最新更新时间:2024-11-11 08:48

STM32Cube SWV实时跟踪调试方法
之前的文章在讲到调试的时候,一般都是大家熟悉的调试方法:通过打断点,让程序运行到某一个地方停下来,查看某些变量、寄存器等的状态;单步运行,看程序的执行、跳转是否跟预期一致;比较高级的是设置软断点,比如在读写某一个变量,或是某一变量值到达某种状态时停下。这些方法有一个共同的特征,就是程序要停下来,让我们观察各种信息。 有没有一种方法,程序在持续的运行,某些变量的值能够持续的输出给我们,甚至是通过图表的形式给我们观察,甚至是后期保存下来。这样在某些情况下是更直观的、更有效的一种调试方法。 这种方法当然是存在的,比如通常会利用单片机的某一个串口向外发送数据,再通过上位机串口调试软件接收并显示。除此之外,还有另一种方法,通过STM
[单片机]
<font color='red'>STM32Cube</font> SWV实时跟踪调试方法
STM32Cube的串口配置与使用
1、串口的配置 1)首先打开你的工程,在STM32Cube里面找到USART1,此时串口默认是Disanle的,我们要使能它,选择Asynchronous. 2)然后点击Configuration,就会有一个串口的配置图框,点进去配置串口,如果需要用到中断,选择NVIC进去进行相关配置。 串口配置: 串口中断使能: --------------------------------------------------------------------------------------------------------------------------------------------
[单片机]
<font color='red'>STM32Cube</font>的串口配置与使用
STM32Cube HAL库中断处理机制,以及回调函数实现原理
1写在前面 很多人都知道STM32CubeMX这套工具的一个目的:减少开发者对STM32底层驱动的开发时间,把重心放在应用代码上。 但是,STM32CubeMX只是生成了底层驱动的初始化代码。所以,我们还需要掌握:应用层代码如何调用HAL库函数接口,以及HAL库中断处理机制等相关知识。 HAL库牵涉的内容较多,本文拿HAL库中断处理来讲解,以及相关的回调函数。 HAL库中断处理机制 之前使用标准外设库开发时,中断程序(函数)由我们自己实现。 而HAL库的中断处理函数是按照HAL处理机制来实现,如USART1,统一由HAL_UART_IRQHandler来进行处理,如下图: 其它大部分外设(TIM、SPI、CAN
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved