微雪官网产品资料
立即注册 找回密码

QQ登录

只需一步,快速开始

微雪课堂

搜索
微雪课堂 首页 STM32 STM32CubeMX系列教程 查看内容

STM32CubeMX系列教程25:USB Device

2016-5-23 17:21| 发布者: waveshare-admin| 查看: 44938| 评论: 18

摘要: 本章讲解USB作为设备使用的例程。
本章不打算详细讲解USB的协议,本章只是介绍如何通过STM32CubeMX软件生成应用程序。
在看本教程之前建议先看ST官方关于USB的培训视频,示例


一、USB简介
      stm32F746系列芯片有USB_OTG_FS和USB_OTG_HS两种接口,FS为全速,速度12M Bit/s,HS为高速,最高速度为480M Bit/s,此时需要外接USB HS PHY,例如USB3300。HS接口也可以作为FS接口使用。由于FS和HS接口使用是相同的USB设备库,只是初始化时配置的引脚不一样,本章以FS接口为例讲解USB设备库的使用。以下为USB OTG FS的电路图:


USB只要由USB_DM和USB_DP两根线差分传输。OTG_ID线用来判断为主设备或时从设备的,作为OTG设备的时候使用。MIC2025/75为USB电源管理芯片,当作为设备是,从外部取电源,VBUSEN(PE2)要设置为高。当作为主机时,要拉低,USB口输出电压给从设备。

二、USB CDC

        这一节介绍usb作为通讯设备类Communication Device Class),通过USB虚拟串口通信。程序在LCD滚动显示字符工程的基础上修改,复制工程修改文件夹名。打开STM32cubeMX的工程文件重新配置,USB_OTG_FS选择设备。


配置系统时钟为216MHz,USB的时钟频率为48MHz.


开启USB中间件,选择虚拟串口。


USB参数配置中 VBUS  sensing 选择失能,其他为默认设置。



中间件USB设备配置也不用修改,默认的配置。


设备描述符设置也不需修改,为默认设置。


软件会默认开启USB中断,此处也为默认优先级,不作修改。


生成报告以及初始化代码,编译程序。工程中多出如下文件,其中最后四个为USB设备的库文件。


usb_device.c里面仅包含一个USB设备函数初始化函数 MX_USB_DEVICE_Init(),在程序开始时调用。
usbd_cdc_if.c为USB的CDC类应用层文件,里面包含虚拟串口的接收,发送和控制等函数。
usb_desc.c包含USB的描述符,以及USB枚举处理等函数。
usb_conf.com为USB管脚配置文件,包含引USB引脚初始化以及参数设置,中断回调函数等。

打开usbd_cdc_if.c文件,找到虚拟串口接收函数。

static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 6 */
  USBD_CDC_SetRxBuffer(hUsbDevice_0, &Buf[0]);
  USBD_CDC_ReceivePacket(hUsbDevice_0);
  return (USBD_OK);
  /* USER CODE END 6 */ 
}


修改接收处理函数,接收到的字符打印输出在LCD屏幕上。

static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 6 */
  uint8_t result = USBD_OK;
    uint8_t msg[Len[0]+1];
    uint32_t i;
    result = USBD_CDC_ReceivePacket(&hUsbDeviceFS);
    for(i=0;i


如下为发送函数,程序中先设置发送字符,然后发送包。这里注意一点,Cube软件初始化的USB结构体是hUsbDeviceFS,这里操作的结构体是hUsbDevice_0。故这个函数不能直接调用,必须先CDC_Init_FS()函数初始化才能用这个函数,初始化中包含有这个语句hUsbDevice_0 = &hUsbDeviceFS。

uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)
{
  uint8_t result = USBD_OK;
  /* USER CODE BEGIN 7 */ 
  USBD_CDC_SetTxBuffer(hUsbDevice_0, Buf, Len);   
  result = USBD_CDC_TransmitPacket(hUsbDevice_0);
  /* USER CODE END 7 */ 
  return result;
}


本教程不调用这个发送函数。在main函数中while循环中添加语法每秒发送一次字符串。

  /* USER CODE BEGIN WHILE */
    while (1)
    {
  /* USER CODE END WHILE */
 
  /* USER CODE BEGIN 3 */
        USBD_CDC_SetTxBuffer(&hUsbDeviceFS, (uint8_t*)&UserTxBuffer, sizeof(UserTxBuffer));
        USBD_CDC_TransmitPacket(&hUsbDeviceFS);
 
        HAL_Delay(1000);
    }
  /* USER CODE END 3 */


在前面声明发送字符串。

  /* USER CODE BEGIN 1 */
  uint8_t UserTxBuffer[] = "WaveShare Open7XXI-C Board STM32 Virtual COM Port Driver \r\n";
  /* USER CODE END 1 */


在main函数中,while循环前面添加程式初始化LCD。

  /* USER CODE BEGIN 2 */
    /* Initialize the SDRAM */
    BSP_SDRAM_Init(); 
    /* Initialize the LCD */  
    BSP_LCD_Init();
 
    BSP_LCD_SetLayerVisible(1, DISABLE);
    BSP_LCD_SelectLayer(0);
 
    /* Initialize LCD Log module */
    LCD_LOG_Init();
 
    /* Show Header and Footer texts */
    LCD_LOG_SetHeader((uint8_t *)"Waveshare Electronics");
    LCD_LOG_SetFooter((uint8_t *)"WaveShare Open7XXI-C board");
  /* USER CODE END 2 */


最后添加usbd_cdc.h头文件。

/* USER CODE BEGIN Includes */
#include "stm32746g_sdram.h"
#include "stm32746g_LCD.h"
#include "lcd_log.h"
#include "usbd_cdc.h"
/* USER CODE END Includes */

最后编译程序,并下载到开发板,电脑usb线接到Open746I-C的核心板的USB接口中。打开串口助手会接到开发板发送的字符串,串口助手发送的字符会在LCD上显示。设置的串口传输格式是无效的,程序中没有设置串口传输格式,可以修改usbd_cdc_if.c文件的CDC_Control_FS()函数设置。

注:电脑要安装ST虚拟串口驱动才能设别虚拟串口。如下为虚拟串口驱动:


三、USB HID
    本节介绍USB作为人机接口设备(Human Interface Device),开发板模拟鼠标设备。
复制上一节的工程文件,修改文件夹名。打开STM32cubeMX的工程文件重新配置,USB_OTG_FS选择设备,USB设备选择Human Interface Device。



USB参数配置中 VBUS  sensing 选择失能,其他为默认设置。


USB设备中间层不需要修改,为默认设置。



生成报告以及初始化代码,编译程序。工程中多出如下文件,其中最后四个为USB设备的库文件。


和前面的CDC对比一下,发现USB设备库文件中,usbd_cdc.c替换为usbd_hid.c文件。usbd_cdc_if.c为CDC应用层文件也去掉了。
删掉原来的应用程序,重新编写应用程序。在main.c文件最后面添加应用程序。程序GetPointerData()为读取五向摇杆按键状态更新坐标,CURSOR_STEP为每次移动的步长,输入参数为当前的坐标位置。

/* USER CODE BEGIN 4 */
/**
  * @brief  Gets Pointer Data.
  * @param  pbuf: Pointer to report
  * @retval None
  */
static void GetPointerData(uint8_t *pbuf)
{
  int8_t  x = 0, y = 0 ;
   
  switch(BSP_JOY_GetState())
  {
    case JOY_LEFT:
      x -= CURSOR_STEP;
      break;
       
    case JOY_RIGHT:
      x += CURSOR_STEP;
      break;
       
    case JOY_UP:
      y -= CURSOR_STEP;
      break;
       
    case JOY_DOWN:
      y += CURSOR_STEP;
      break;
       
    default:
      break;
  }
  pbuf[0] = 0;
  pbuf[1] = x;
  pbuf[2] = y;
  pbuf[3] = 0;
}
 
/**
  * @brief  SYSTICK callback.
  * @retval None
  */
void HAL_SYSTICK_Callback(void)
{
  /* NOTE : This function Should not be modified, when the callback is needed,
            the HAL_SYSTICK_Callback could be implemented in the user file
   */
    static __IO uint32_t counter=0;
    /* check Joystick state every polling interval (10ms) */
  if (counter++ == USBD_HID_GetPollingInterval(&hUsbDeviceFS))
  {
    GetPointerData(HID_Buffer);
     
    /* send data though IN endpoint*/
    if((HID_Buffer[1] != 0) || (HID_Buffer[2] != 0))
    {
      USBD_HID_SendReport(&hUsbDeviceFS, HID_Buffer, 4);
    }
    counter =0;
  }
  BSP_LED_Toggle(LED1);
}
/* USER CODE END 4 */


HAL_SYSTICK_Callback()为SysTick定时器中断回调函数,时间为1ms。程序中先调用USBD_HID_GetPollingInterval函数读取HID轮询间隔。每隔10MS根据五向摇杆按键更新坐标,并通过USB发到电脑。

添加变量声明
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
#define CURSOR_STEP     5
uint8_t HID_Buffer[4];
/* USER CODE END PV */


添加usbd_hid.h头文件

/* USER CODE BEGIN Includes */
#include "stm32746g_sdram.h"
#include "stm32746g_LCD.h"
#include "lcd_log.h"
#include "usbd_hid.h"
/* USER CODE END Includes */



编译程序,并下载到开发板,电脑usb线接到Open746I-C的核心板的USB接口中。按五向遥控按键电脑上的鼠标会移动。

三、USB MSC
    
       
本节介绍USB大容量存储设备类(Mass Storage Class),开
发板作为U盘,用SDMMC接SD卡存储数据。
复制上一节CDC的工程文件,修改文件夹名。打开STM32cubeMX的工程文件重新配置,USB_OTG_FS选择设备,USB设备选择Mass Storage Class


选择SDMMC接口为4线。


配置系统时钟频率为216MHZ,USB,SDMMC频率均为48MHz。


SDMMC添加收发DMA,其他为默认不作修改。

开启SDMMC中断,注意必须开启SDMMC中断,SDMMC的DMA才能正常工作,而且SDMMC中断优先级要比DMA中断要高,且比USB中断高。


增大堆栈空间的大小,否则会程序会触发硬件错误中断(HardFault)。


生成报告以及初始化代码,编译程序。工程中多出如下文件,其中最后七个为USB设备的库文件。


其中usbd_storage_if为应用层文件,里面为USB大容量存储设备类底层操作,包括获取存储器容量,块读写等操作。实际上usbd_storage_if里面这些操作都是空的,需要我们移植底层。由于SD的操作是由电脑同过USB镜像操作的,故开发板程序是不需要移植文件系统的,我们只需在usbd_storage_if文件中添加SD卡的底层操作既可。

打开usbd_storage_if文件,我们只需修改三个函数既可,第一个是获取存储器容量大小函数,返回块大小,已经块数目。

/*******************************************************************************
* Function Name  : STORAGE_GetCapacity_FS
* Description    : 
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
int8_t STORAGE_GetCapacity_FS (uint8_t lun, uint32_t *block_num, uint16_t *block_size)
{
  /* USER CODE BEGIN 3 */   
    HAL_SD_CardInfoTypedef info;
    int8_t ret = USBD_FAIL;  
     
//  if(BSP_SD_IsDetected() != SD_NOT_PRESENT)
    {
         
        HAL_SD_Get_CardInfo(&hsd1, &info);
        *block_num = (info.CardCapacity)/STORAGE_BLK_SIZ  - 1;
        *block_size = info.CardBlockSize;
        ret = USBD_OK;
    }
  return ret;
  /* USER CODE END 3 */ 
}


第二个是块读取函数,SD卡是通过DMA传输数据。

/*******************************************************************************
* Function Name  : STORAGE_Read_FS
* Description    : 
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
int8_t STORAGE_Read_FS (uint8_t lun, 
                        uint8_t *buf, 
                        uint32_t blk_addr,                       
                        uint16_t blk_len)
{
  /* USER CODE BEGIN 6 */ 
      int8_t ret = USBD_FAIL;  
   
//  if(BSP_SD_IsDetected() != SD_NOT_PRESENT)
  {  
    /* Read block(s) in DMA transfer mode */
        if(HAL_SD_ReadBlocks_DMA(&hsd1,(uint32_t *)buf, blk_addr * STORAGE_BLK_SIZ, STORAGE_BLK_SIZ, blk_len) == SD_OK)
        {
            ret = USBD_OK; 
        }
 
        /* Wait until transfer is complete */
        if(ret == USBD_OK)
        {
            if(HAL_SD_CheckReadOperation(&hsd1, (uint32_t)100000000) != SD_OK)
            {
                ret = USBD_FAIL; 
            }
            else
            {
                ret = USBD_OK;
            }
        }
        ret = USBD_OK;
  }
  return ret;
  /* USER CODE END 6 */ 
}

第三个是写操作函数

/*******************************************************************************
* Function Name  : STORAGE_Write_FS
* Description    :
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
int8_t STORAGE_Write_FS (uint8_t lun, 
                         uint8_t *buf, 
                         uint32_t blk_addr,
                         uint16_t blk_len)
{
  /* USER CODE BEGIN 7 */ 
    int8_t ret = USBD_FAIL;   
   
//  if(BSP_SD_IsDetected() != SD_NOT_PRESENT)
  { 
    /* Write block(s) in DMA transfer mode */
        if(HAL_SD_WriteBlocks_DMA(&hsd1, (uint32_t *)buf, blk_addr * STORAGE_BLK_SIZ, STORAGE_BLK_SIZ, blk_len) == SD_OK)
        {
            ret = USBD_OK;
        }
 
        /* Wait until transfer is complete */
        if(ret == USBD_OK)
        {
            if(HAL_SD_CheckWriteOperation(&hsd1, (uint32_t)100000000) != SD_OK)
            {
                ret = USBD_FAIL;
            }
            else
            {
                ret = USBD_OK;
            }
        }
  }
  return ret;
  /* USER CODE END 7 */ 
}



注意:Open746I-C中可以通过PC13管脚判断SD卡是否插入到卡槽中,这里为了方便没有用到这个功能。所以上面程序中注释掉了检测SD卡是否准备好这一步。



编译程序,并下载到开发板,电脑usb线接到Open746I-C的核心板的USB接口中。电脑会提示有U盘插入。

288

顶一下

刚表态过的朋友 (288 人)

发表评论

最新评论

引用 zhh89757 2018-9-14 16:54
你好 配置的cdc例程可以接收到来自pc的数据但是无法发送到pc 调试程序发现hcdc->TxState一直为1
引用 游客 2017-12-5 13:04
我的板子需要连接电脑,接收电脑数据,既有控制数据,又有大批量数据, 该用哪种类? 或者是不能套用模板,需要自己开发。求指导。qq285620308
引用 游客 2017-9-5 17:29
: 我用stm32f429开发板,做了一个spi flahs 的U盘,提示要格式化,也无法访问,求大神指点!
问题解决了吗?另外有没有个交流群大家一起交流一下
引用 游客 2017-8-29 17:36
USB_MSC中,目前最新的4.22的库,和微雪的例子有点区别,调了一周都没有搞定,微雪能更新下新版例程吗?
引用 游客 2017-8-29 08:50
如果可以看到u盘,不可以打开多数是sd底层读写函数没有移植好,最新的库可以参考最新的固件库。
引用 游客 2017-8-28 17:41
: 你的现象与我一样,解决了吗?
没有呀,另外一个问题是由于我使用的最新的库4.22版本,我把HAL_SD_CheckWriteOperation都注释掉了,这个库已经没有这个函数了,不知道是不是这个原因引起的,新库有对应的函数替换吗?
引用 游客 2017-8-28 10:49
: 你好 按照你的例程使用USB MSC时  烧写程序 可以看到可移动磁盘 但是无法格式化 请问有遇到这种现象吗
你的现象与我一样,解决了吗?
引用 游客 2017-8-28 10:49
: HAL_SD_WriteBlocks_DMA 我的这个函数只有4个参数了怎么办?
新的CUBE生成的参数与这个文章中介绍的不一样,希望微雪可以进一步更新说明
引用 游客 2017-8-27 14:02
你好 按照你的例程使用USB MSC时  烧写程序 可以看到可移动磁盘 但是无法格式化 请问有遇到这种现象吗
引用 游客 2017-8-17 18:09
我用stm32f429开发板,做了一个spi flahs 的U盘,提示要格式化,也无法访问,求大神指点!
引用 游客 2017-7-21 13:40
HAL_SD_WriteBlocks_DMA 我的这个函数只有4个参数了怎么办?
引用 游客 2017-6-18 21:21
: 请问用OTG_USB_HS的时候怎么设置PLL_Q使能48MHz时钟,我打开了设备但PLL_Q和48MHz还是灰色的,设置不了 ...
你没设置外部时钟吧
引用 游客 2017-6-18 21:20
怎么做复合设备呀,既是hid又是U盘
引用 游客 2017-1-19 14:04
请问用OTG_USB_HS的时候怎么设置PLL_Q使能48MHz时钟,我打开了设备但PLL_Q和48MHz还是灰色的,设置不了 ...
引用 MyMX1213 2016-11-24 09:21
: 使用CDC传输,win10系统无法识别USB设备,怎么解决?(已安装教程中给的虚拟串口驱动) ...
你是用什么开发板的?如果是用我们的板子,直接下载我们的程序看下行不行。如果用其他板子,可能会有一个USB使能控制引脚的,这个一般也是要配置的。如还是不行可以加下我的QQ.2853906288
引用 游客 2016-11-21 14:02
使用CDC传输,win10系统无法识别USB设备,怎么解决?(已安装教程中给的虚拟串口驱动) ...
引用 MyMX1213 2016-11-21 09:35
: USB_MSC中,如果想读取SD卡中的东西,是不是需要移植一个文件系统
是的,USB_MSC中的是用电脑主机上的文件系统,如果单片机要读SD卡的东西要移植文件系统
引用 游客 2016-9-28 15:24
USB_MSC中,如果想读取SD卡中的东西,是不是需要移植一个文件系统

查看全部评论(18)

CubeMX教程

微雪官网|产品资料|手机版|小黑屋|微雪课堂. ( 粤ICP备05067009号  

GMT+8, 2019-1-18 17:40 , Processed in 0.089883 second(s), 23 queries .

Powered by Discuz! X3.2 © 2001-2013 Comsenz Inc & Style Design

返回顶部