STM32 USB学习笔记3

发布者:CelestialGarden最新更新时间:2019-03-11 来源: eefocus关键字:STM32  USB 手机看文章 扫描二维码
随时随地手机看文章

主机环境:Windows 7 SP1


开发环境:MDK5.14


目标板:STM32F103C8T6


开发库:STM32F1Cube库和STM32_USB_Device_Library


现在开始分析VCP示例代码,从最简单的usbd_desc开始。USB设备使用描述符来报告其功能特性,描述符是一个已知格式的数据结构,USB规范中定义了以下几种描述符:Device(设备)、Device_Qualifier(设备限定)、Configuration(配置)、Other_Speed_Configuration(其他速度配置)、Interface(接口)、Endpoint(端点)、String(字符串)。usbd_desc文件主要提供USB字符串描述符,字符串描述符对于设备来说是可选的,是对其他描述符的文字说明,且字符串描述符使用UNICODE编码,同时字符串描述符支持多国语言,当请求字符串描述符时需要使用16禁止的LANGID来指定所期望的语言。使用字符串索引0来获取设备所支持的语言。字符串描述的数据结构如下所示:



第一个字节是该数据结构的字节长度,第二个字节是字符串描述符类型,后面的字节是字符串内容。有关字符串描述符类型值的定义在USB2.0规范的9.4章节的表9-5中,如下:



可以看到字符串描述符类型的值为常量3,与之相符的在usbd_def.h文件中95行有如下定义:


#define  USB_DESC_TYPE_DEVICE                              1

#define  USB_DESC_TYPE_CONFIGURATION                       2

#define  USB_DESC_TYPE_STRING                              3

#define  USB_DESC_TYPE_INTERFACE                           4

#define  USB_DESC_TYPE_ENDPOINT                            5

#define  USB_DESC_TYPE_DEVICE_QUALIFIER                    6

#define  USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION           7

#define  USB_DESC_TYPE_BOS                                 0x0F


查看usbd_desc.h头文件,内容很简单

/**

  ******************************************************************************

  * @file    USB_Device/CDC_Standalone/Inc/usbd_desc.h

  * @author  MCD Application Team

  * @version V1.2.0

  * @date    31-July-2015

  * @brief   Header for usbd_desc.c module

  ******************************************************************************

  * @attention

  *

  *

© COPYRIGHT(c) 2015 STMicroelectronics

  *

  * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");

  * You may not use this file except in compliance with the License.

  * You may obtain a copy of the License at:

  *

  *        http://www.st.com/software_license_agreement_liberty_v2

  *

  * Unless required by applicable law or agreed to in writing, software

  * distributed under the License is distributed on an "AS IS" BASIS,

  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

  * See the License for the specific language governing permissions and

  * limitations under the License.

  *

  ******************************************************************************

  */

 

/* Define to prevent recursive inclusion -------------------------------------*/

#ifndef __USBD_DESC_H

#define __USBD_DESC_H

 

/* Includes ------------------------------------------------------------------*/

#include "usbd_def.h"

 

/* Exported types ------------------------------------------------------------*/

/* Exported constants --------------------------------------------------------*/

#define         DEVICE_ID1          (0x1FFFF7E8)

#define         DEVICE_ID2          (0x1FFFF7EC)

#define         DEVICE_ID3          (0x1FFFF7F0)

 

#define  USB_SIZ_STRING_SERIAL      0x1A

 

/* Exported macro ------------------------------------------------------------*/

/* Exported functions ------------------------------------------------------- */

extern USBD_DescriptorsTypeDef VCP_Desc;

 

#endif /* __USBD_DESC_H */

 

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/


这里有三个宏定义DEVICE_ID1、DEVICE_ID2、DEVICE_ID3,这三个值是STM32芯片产品唯一身份标识,可以用作USB字符序列号(96位),该寄存器是只读的。详情参考STM32参考手册的设备电子签名章节。此外,该文件还引用了一个数据结构USBD_DescriptorsTypeDef,该结构是一个函数指针集合。


/* USB Device descriptors structure */

typedef struct

{

  uint8_t  *(*GetDeviceDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length);  

  uint8_t  *(*GetLangIDStrDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length); 

  uint8_t  *(*GetManufacturerStrDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length);  

  uint8_t  *(*GetProductStrDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length);  

  uint8_t  *(*GetSerialStrDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length);  

  uint8_t  *(*GetConfigurationStrDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length);  

  uint8_t  *(*GetInterfaceStrDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length); 

#if (USBD_LPM_ENABLED == 1)

  uint8_t  *(*GetBOSDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length); 

#endif  

} USBD_DescriptorsTypeDef;

用于获取各种描述符,主要是获取字符串描述符,与之对应的下标在usbd_def.h的66行


#define  USBD_IDX_LANGID_STR                            0x00 

#define  USBD_IDX_MFC_STR                               0x01 

#define  USBD_IDX_PRODUCT_STR                           0x02

#define  USBD_IDX_SERIAL_STR                            0x03 

#define  USBD_IDX_CONFIG_STR                            0x04 

#define  USBD_IDX_INTERFACE_STR                         0x05 

注意USBD_DescripotrsTypeDef结构中的GetDeviceDescriptor不是获取字符串描述符,同时前面提到使用索引号0来获取设备所支持的语言,因此这里定义USBD_IDX_LANGID_STR为0,至于其他索引号值是否有标准定义,暂时为找到出处。该结构实体定义在usbd_desc.c文件中如下


USBD_DescriptorsTypeDef VCP_Desc = {

  USBD_VCP_DeviceDescriptor,

  USBD_VCP_LangIDStrDescriptor, 

  USBD_VCP_ManufacturerStrDescriptor,

  USBD_VCP_ProductStrDescriptor,

  USBD_VCP_SerialStrDescriptor,

  USBD_VCP_ConfigStrDescriptor,

  USBD_VCP_InterfaceStrDescriptor,  

};


稍后来看各个函数,在usbd_desc.c文件的前面有如下定义


#define USBD_VID                      0x0483

#define USBD_PID                      0x5740

#define USBD_LANGID_STRING            0x409

#define USBD_MANUFACTURER_STRING      "STMicroelectronics"

#define USBD_PRODUCT_FS_STRING        "STM32 Virtual ComPort in FS Mode"

#define USBD_CONFIGURATION_FS_STRING  "VCP Config"

#define USBD_INTERFACE_FS_STRING      "VCP Interface"

USBD_VID和USBD_PID分别是厂商ID、产品ID,这两个ID需要向USB组织申请,不是免费的,当前该ID列表可以在以下网址查看http://www.linux-usb.org/usb.ids,通过查看可知0x0483是STMicroelectronics申请的,0x5740对应的产品是STM32F407,ST完整列表如下:

语言ID0x409指的是English(United States),该值可以在USB_LANGIDs.pdf文档中找到,如下:

现在来分析获取描述符函数,首先从获取语言ID开始,将代码整合一下,方便查看,如下:



#define  USB_LEN_LANGID_STR_DESC                        0x04

 

/* USB Standard Device Descriptor */

const uint8_t USBD_LangIDDesc[USB_LEN_LANGID_STR_DESC]= 

{

  USB_LEN_LANGID_STR_DESC,         

  USB_DESC_TYPE_STRING,       

  LOBYTE(USBD_LANGID_STRING),

  HIBYTE(USBD_LANGID_STRING), 

};

 

/**

  * @brief  Returns the LangID string descriptor.        

  * @param  speed: Current device speed

  * @param  length: Pointer to data length variable

  * @retval Pointer to descriptor buffer

  */

uint8_t *USBD_VCP_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)

{

  *length = sizeof(USBD_LangIDDesc);  

  return (uint8_t*)USBD_LangIDDesc;

}

USBD_VCP_LangIDStrDescriptor函数最终是要返回一个语言ID数组结构,且以UNICODE编码,如下:


由于这里只支持英文,因此这里bLength为4,bDescriptorType为3,LANGID为0x409。产品、厂商、配置、接口字符串描述符所对应的函数是同一种结构,说明一个即可



/**

  * @brief  Returns the manufacturer string descriptor. 

  * @param  speed: Current device speed

  * @param  length: Pointer to data length variable

  * @retval Pointer to descriptor buffer

  */

uint8_t *USBD_VCP_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)

{

  USBD_GetString((uint8_t *)USBD_MANUFACTURER_STRING, USBD_StrDesc, length);

  return USBD_StrDesc;

}

这里是将ASCII编码的字符串转成UNICODE编码的字符串,同时UNICODE编码的字符串不是以NULL作为结束。USBD_GetString()方法定义在usbd_ctlreq.c文件中,如下:


/**

  * @brief  USBD_GetString

  *         Convert Ascii string into unicode one

  * @param  desc : descriptor buffer

  * @param  unicode : Formatted string buffer (unicode)

  * @param  len : descriptor length

  * @retval None

  */

void USBD_GetString(uint8_t *desc, uint8_t *unicode, uint16_t *len)

{

  uint8_t idx = 0;

  

  if (desc != NULL) 

  {

    *len =  USBD_GetLen(desc) * 2 + 2;    

    unicode[idx++] = *len;

    unicode[idx++] =  USB_DESC_TYPE_STRING;

    

    while (*desc != '\0') 

    {

      unicode[idx++] = *desc++;

      unicode[idx++] =  0x00;

    }

  } 

}

 

/**

  * @brief  USBD_GetLen

  *         return the string length

   * @param  buf : pointer to the ascii string buffer

  * @retval string length

  */

static uint8_t USBD_GetLen(uint8_t *buf)

{

    uint8_t  len = 0;

 

    while (*buf != '\0') 

    {

        len++;

        buf++;

    }

 

    return len;

}


接着分析获取序列号字符串描述符方法,整理如下:


/**

  * @brief  Returns the serial number string descriptor.        

  * @param  speed: Current device speed

  * @param  length: Pointer to data length variable

  * @retval Pointer to descriptor buffer

  */

uint8_t *USBD_VCP_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)

{

  *length = USB_SIZ_STRING_SERIAL;

  

  /* Update the serial number string descriptor with the data from the unique ID*/

  Get_SerialNum();

  

  return USBD_StringSerial;

}

 

/**

  * @brief  Create the serial number string descriptor 

  * @param  None 

  * @retval None

  */

static void Get_SerialNum(void)

{

  uint32_t deviceserial0, deviceserial1, deviceserial2;

  

  deviceserial0 = *(uint32_t*)DEVICE_ID1;

  deviceserial1 = *(uint32_t*)DEVICE_ID2;

  deviceserial2 = *(uint32_t*)DEVICE_ID3;

  

  deviceserial0 += deviceserial2;

  

  if (deviceserial0 != 0)

  {

    IntToUnicode (deviceserial0, &USBD_StringSerial[2] ,8);

    IntToUnicode (deviceserial1, &USBD_StringSerial[18] ,4);

  }

}

 

/**

  * @brief  Convert Hex 32Bits value into char 

  * @param  value: value to convert

  * @param  pbuf: pointer to the buffer 

  * @param  len: buffer length

  * @retval None

  */

static void IntToUnicode (uint32_t value , uint8_t *pbuf , uint8_t len)

{

  uint8_t idx = 0;

  

  for( idx = 0 ; idx < len ; idx ++)

  {

    if( ((value >> 28)) < 0xA )

    {

      pbuf[2* idx] = (value >> 28) + '0';

    }

    else

    {

      pbuf[2* idx] = (value >> 28) + 'A' - 10; 

    }

    

    value = value << 4;

    

    pbuf[2* idx + 1] = 0;

  }

}

这里取序列号只取了48位,取了deviceserial0的32位和deviceserial1的高16位,注意IntToUnicode()方法即可,以16进制进行转换,从高位开始,序列号字符串描述符字节长度为0x1A=12*2(序列号内容)+2(前面两个字节)。至此,字符串描述符的获取分析完毕,还剩下一个设备描述符的获取,如下:


/* USB Standard Device Descriptor */

const uint8_t hUSBDDeviceDesc[USB_LEN_DEV_DESC]= {

  0x12,                       /* bLength */

  USB_DESC_TYPE_DEVICE,       /* bDescriptorType */

  0x00,                       /* bcdUSB */

  0x02,   /* USB2.0 spec*/

  0x00,                       /* bDeviceClass */

  0x00,                       /* bDeviceSubClass */

  0x00,                       /* bDeviceProtocol */

  USB_MAX_EP0_SIZE,           /* bMaxPacketSize */

  LOBYTE(USBD_VID),           /* idVendor */

  HIBYTE(USBD_VID),           /* idVendor */

  LOBYTE(USBD_PID),           /* idVendor */

  HIBYTE(USBD_PID),           /* idVendor */

  0x00,                       /* bcdDevice rel. 2.00 */

  0x02,

  USBD_IDX_MFC_STR,           /* Index of manufacturer string */

  USBD_IDX_PRODUCT_STR,       /* Index of product string */

  USBD_IDX_SERIAL_STR,        /* Index of serial number string */

  USBD_MAX_NUM_CONFIGURATION  /* bNumConfigurations */

}; /* USB_DeviceDescriptor */

 

/**

  * @brief  Returns the device descriptor. 

  * @param  speed: Current device speed

  * @param  length: Pointer to data length variable

  * @retval Pointer to descriptor buffer

  */

uint8_t *USBD_VCP_DeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)

{

  *length = sizeof(hUSBDDeviceDesc);

  return (uint8_t*)hUSBDDeviceDesc;

}


设备描述符的获取主要看hUSBDDeviceDesc数组结构,该数组长度为18个字节,与之对应的数据结构在USB2.0规范的9.6章节有说明,一个设备只有一个设备描述符,设备描述符数据结构如下:

根据此结构来分析hUSBDDeviceDesc内容的含义,符合USB2.0规范,bDeviceClass置0,表明一个配置里每个接口指定其各自的类信息同时不同的接口独立地操作。由于bDeviceClass字段置0,因此bDeviceSubClass字段同样置0。bDeviceProtocol置0,设备不使用特定类协议。端点0最大包大小为64字节,接着填充厂商ID和产品ID,bcdDevice标记设备稳定版本,这里为2.0版本,当然可以修改该值。iManufacturer、iProduct、iSerialNumber分别是在字符串描述符中各自的索引号,前面有提到。最后标记配置数为1。USB设备可以有一个或多个配置,每个配置有一个或多个接口,每个接口有一个或多个端点。至此,usbd_desc文件分析完毕。主要为各个描述符的获取。


关键字:STM32  USB 引用地址:STM32 USB学习笔记3

上一篇:STM32 USB学习笔记4
下一篇:STM32 USB学习笔记2

推荐阅读最新更新时间:2024-03-16 16:25

USB3.0的物理层发送端测试方案介绍
  USB简介   USB(Universal Serial Bus)即通用串行总线,用于把键盘、鼠标、打印机、扫描仪、数码相机、MP3、U盘等外围设备连接到计算机,它使计算机与周边设备的接口标准化,从 2000年以后,支持USB2.0版本的计算机和设备已被广泛使用,USB2.0包括了三种速率:高速480Mbps、全速12Mbps、低速 1.5Mbps。目前除了键盘和鼠标为低速设备外,大多数设备都是速率达480M的高速设备。   尽管USB2.0的速度已经相当快,对于目前高清视频和动辄GByte的数据传输还是有些慢,在2008年11月,HP、Intel、微软、NEC、ST-NXP、TI联合起来正式发布了 USB3.0的V1.0
[测试测量]
<font color='red'>USB</font>3.0的物理层发送端测试方案介绍
STM32-串口超时判断方式接收未知长度数据
usart.c串口中断处理函数: view plain copy void USART1_IRQHandler(void) { u8 res; if(USART1- SR&(1 5))//接收到数据 { res=USART1- DR; if(USART1_Recv_Len USART1_MAX_RECV_LEN) //还可以接收数据 { TIM3- CNT=0; //计数器清空 if(Rec_Over_Flag==0)TIM3_Set(1); //使能定
[单片机]
STM32的USART中RTS、CTS的作用和意义
Ⅰ、写在前面 我们都知道USART中RX和TX这两个引脚的功能,这两个引脚是USART串行通信最常见和必不可少的两个引脚。但我们在手册中会发现关于USART的其他引脚:USART_CK、USART_RTS、USART_CTS,如下图: 但我们大部分都没怎么使用过USART_RTS和USART_CTS这两个引脚。下面将给大家简单讲述一下关于USART串口拓展的知识。 Ⅱ、关于DB9串口接头 我们都听说过RS232,说232就知道DB9这个串口接头。 DB9个引脚的功能: 1 CD ← Carrier Detect 载波检测 2 RXD ← Receive Data 接收数据 3 TXD → Transmit Da
[单片机]
<font color='red'>STM32</font>的USART中RTS、CTS的作用和意义
STM32的SPI外设片选只有一个,怎么破?
之前用STM32的SPI需要控制很多外部芯片,可是一个SPI的外设只有一个片选,要实现独立片选一主多从,怎么实现呢? SPI总线拓扑 一般地,SPI总线按照下图方式进行连接,一主多从。 如上图: 每个从设备都有独立的片选引脚,主机同一时间段内,与一个从设备进行通信,也即选中一个从设备。 MOSI/MISO/SCLK并联在一起 MISO须是三态门,当从设备未选中时,该脚须设置为高阻态,而不能是输出态,否则会影响总线 ! 对于MOSI/SCLK,虽然并联在一起,但是由于仅一个输出,多输入。 但是你看STM32的SPI外设,一个SPI仅有一个NSS信号,以STM32F407的SPI2为例: 那么要实现前面说的一主多从
[单片机]
STM32使用LWIP库新建tcp_sever
main函数 区域1是lwip的初始化 void LwIP_Init(void) { struct ip_addr ipaddr; struct ip_addr netmask; struct ip_addr gw; mem_init();//内存堆初始化 memp_init();//内存池初始化 IP4_ADDR(&ipaddr, 192, 168, 16, 211); IP4_ADDR(&netmask, 255, 255 , 255, 0); IP4_ADDR(&gw, 192, 168, 16, 1); netif_add(&netif, &ipaddr, &netmask,
[单片机]
一种STM32的串口控制台的实现
一.背景 曾经玩Linux时非常喜欢这种基于出串口的控制台, 通过简单的串口TX和RX能实现嵌入式硬件的人机交互,非常实用, 那么STM32能否实现通过超级终端与用户互动的构想呢? 答案是肯定的,由于这个UART控制平台就像应用程序套上一层可访问的外科(Shell)故而我将这种基于UART的控制平台简称Shell,构架和效果如下图: 这张图箭头指向的是输入的指令,其余是STM32串口输出的信息,, 可以看到通过这些简单的指令输入我们通过Shell可以做很多事情: 1. 现场设备发生故障,可以通过Shell可以查看设备的故障状态统计信息 2. 能实现串口程序升级(需要Shell+IAP驱动程序支持) 3.
[单片机]
一种<font color='red'>STM32</font>的串口控制台的实现
STM32 GPIO 简单操作函数
STM32库函数太多,而且不能识别大小写,经常记错,GPIO简单记忆。 ---第一步--模式配置 void GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //对应GPIO所在的总线时钟必须打开 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1; //哪个GPIO口 GPIO_InitStructu
[单片机]
使用VSCode搭建STM32开发环境
首先附上一张VS Code图一直都喜欢这种,黑色主题感觉高大上。 一、需要的软件和工具。 下载最新版VS Code: 安装好插件,具有良好的代码补全与调试功能。 “VS Code下载地址:https://code.visualstudio.com/” 下载 LLVM:用于代码补全,其实可以理解为 Clang。因为VS Code 中“C/C++”插件的自动补全功能不太好用。STM32中好多库函数都补全不出来。记得按照好之后,将路径添加到环境变量里。 “LLVM下载地址:http://releases.llvm.org/download.html” 下载安装 Git for Windows: 提供Git支持和MINGW64指令终端
[单片机]
使用VSCode搭建<font color='red'>STM32</font>开发环境
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

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

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

换一换 更多 相关热搜器件
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved