首页 > 其他 > 详细

STM32 USB应用笔记

时间:2016-05-26 02:05:53      阅读:974      评论:0      收藏:0      [点我收藏+]

STM32 USB应用笔记

?

USB

作者:

gashero

日期:

2013-02-06

目录

  • 1???简介
  • 2???STM32的USB简介
  • 3???USB实现类
    • 3.1???USB-CDC
  • 4???代码分析
    • 4.1???stm32f4-discovery-usb-cdc-example分析
      • 4.1.1???main.c
      • 4.1.2???usbd_desc.h
      • 4.1.3???usbd_desc.c
      • 4.1.4???stm32f4xx_it.c
      • 4.1.5???usb_bsp.c
      • 4.1.6???usb_conf.h
      • 4.1.7???usbd_conf.h
      • 4.1.8???usbd_usr.c
      • 4.1.9???usbd_cdc.h
      • 4.1.10???usbd_cdc.c
      • 4.1.11???项目文件
    • 4.2???stsw_stm32081.zip中STM32_USB-FS-Device_Lib_V4.0.0的USB库
      • 4.2.1???usb_def.h
      • 4.2.2???usb_type.h
      • 4.2.3???usb_lib.h
      • 4.2.4???usb_core.h/.c
      • 4.2.5???usb_init.h/.c
      • 4.2.6???usb_int.h/.c
      • 4.2.7???usb_mem.h/.c
      • 4.2.8???usb_regs.h/.c
      • 4.2.9???usb_sil.h/.c
    • 4.3???stsw-stm32081.zip中STM32_USB-FS-Device_Lib_V4.0.0的VirtualComport_Loopback
      • 4.3.1???main.c
      • 4.3.2???platform_config.h
      • 4.3.3???hw_config.h/.c
      • 4.3.4???stm32_it.h/.c
      • 4.3.5???usb_conf.h
      • 4.3.6???usb_desc.h/.c
      • 4.3.7???usb_istr.h/.c
      • 4.3.8???usb_prop.h/.c
      • 4.3.9???usb_pwr.h/.c
      • 4.3.10???usb_endp.c
  • 5???应用注记
    • 5.1???使用CDC类与上位机的通信
      • 5.1.1???尝试CTR中断的通信
    • 5.2???使用USB-FS-Device的VirtualComPort_Loopback例子,作为与电脑的USB串口通信
    • 5.3???休眠处理

1???简介

一种还算凑合的通信方式。

参考文献:

  1. [r] 基于STM32的USB程序开发笔记(序): http://bbs.ednchina.com/BLOG_ARTICLE_182000.HTM
  2. [r] 基于STM32的USB程序开发笔记(一): http://bbs.ednchina.com/BLOG_ARTICLE_182060.HTM
  3. [u] 基于STM32的USB程序开发笔记(二): http://bbs.ednchina.com/BLOG_ARTICLE_182085.HTM 直接罗列代码
  4. [u] 基于STM32的USB程序开发笔记(三): http://bbs.ednchina.com/BLOG_ARTICLE_182520.HTM 还是代码
  5. [u] 基于STM32的USB程序开发笔记(四): http://bbs.ednchina.com/BLOG_ARTICLE_182913.HTM 设备枚举上
  6. [u] 基于STM32的USB程序开发笔记(五): http://bbs.ednchina.com/BLOG_ARTICLE_183349.HTM 设备枚举下
  7. [u] 基于STM32的USB程序开发笔记(六): http://bbs.ednchina.com/BLOG_ARTICLE_183470.HTM XP下USB驱动开发
  8. [u] 基于STM32的USB程序开发笔记(七): http://bbs.ednchina.com/BLOG_ARTICLE_183523.HTM XP下USB驱动开发
  9. [i] 基于STM32的USB程序开发笔记.pdf: 2502410字节,就是如上一系列文章整理的
  10. [u] STM32的USB例程修改步骤: http://blog.chinaunix.net/uid-605899-id-3125746.html
  11. [u] 开始学习USB-从STM32的USB-DEMO开始: http://blog.21ic.com/user1/4852/archives/2008/48074.html
  12. [u] stm32 usb学习: http://wenku.baidu.com/view/2f405e105f0e7cd18425365d
  13. [r] 发一个stm32f4的usb虚拟串口程序: http://www.amobbs.com/thread-5514289-1-1.html 一个1.13MB的源码下载,stm32f4 discovery
  14. [u] [CoIDE]_USB_CDC_Example.rar: https://www.dropbox.com/s/tix34ol8k02i88r/%5BCoIDE%5D_USB_CDC_Example.rar 12.19MB没下载
  15. [u] 嵌入式系统的USB虚拟串口设计: http://www.autooo.net/utf8-classid124-id44408.html
  16. [r] USB-Serial on STM32F4: http://vedder.se/2012/07/usb-serial-on-stm32f4/ 很初级的介绍
  17. [u] STM32F4-Discovery: Help transfering data to PC: https://my.st.com/public/STe2ecommunities/mcu/Lists/STM32F4DISCOVERY/DispForm.aspx?ID=351 一些源码例子的下载地址
  18. [u] ericherman/stm32f-discovery-example: https://github.com/ericherman/stm32f4-discovery-example 使用USB-CDC的例子
  19. [u] serialUSB: https://www.das-labor.org/trac/browser/microcontroller/src-stm32f4xx/serialUSB
  20. [u] STM32F4 USB CDC connection: http://www.coocox.org/forum/topic.php?id=1757&page=2
  21. [u] STM32实现USB Video Class开始干活了: http://www.amobbs.com/thread-5262477-1-1.html

2???STM32的USB简介

基本资料是STM32的参考手册、USB2.0规范、USB外设库。

设备(device)只是被动触发的,主机(host)掌握主动权,包括发送什么数据,什么时候发送,读还是写。设备只是配合完成设备枚举、数据方向和大小,之类的。

两个中断向量:

/* 处理USB高优先级或CAN TX中断 */

void USB_HP_CAN_TX_IRQHandler(void) {

? ? USB_HPI();

}

?

/* 处理USB低优先级或CAN RX0中断 */

void USB_LP_CAN_RX0_IRQHandler(void) {

? ? USB_LPI();

}

USB_HPI()USB_LPI() 即转向 usb_core.h/.c 进行处理。中断传输、控制传输、批量传输(bulk)由 USB_LPI() 响应,批量传输也可以由 USB_HPI() 响应,同步传输只由 USB_HPI() 处理。

这样只需要关注 usb_core.cUSB_LPI()USB_HPI() 了。

USB_LPI() 函数的定义: @page 7-10

3???USB实现类

3.1???USB-CDC

CDC协议是通用的USB实现,在很多操作系统都不需要驱动就支持。所以有人实现了基于USB-CDC的串口,倒是个好思路。

例子代码的下载: http://dl.dropbox.com/u/56124886/stm32f4-discovery/stm32f4-discovery-usb-cdc-example.zip

貌似我也应该使用BSP了,方便些。

官方库的 STM32_USB-Host-Device_Lib_V2.1.0/Libraries/STM32_USB_Device_Library/Class/cdc 提供了CDC的支持了。

4???代码分析

4.1???stm32f4-discovery-usb-cdc-example分析

系统文件列表:

  1. startup_stm32f4xx.s:与我用的基本相同,除了注释
  2. stm32f4_discovery.c:有些不同,详见下面
  3. stm32f4_discovery.h:有些不同,详见下面
  4. stm32f4xx_conf.h:与我用的基本相同,除了注释
  5. stm32f4xx_it.c:对方加了好多东西,要看
  6. stm32f4xx_it.h:只有注释不同
  7. system_stm32f4xx.c:版本有所不同,但用我的更靠谱些

对于stm32f4_discovery.c/.h文件,他们在该项目里,但不在 stm32f4_dsp_stdperiph_lib.zip 。存在于 stm32f4discovery_fw.zip 中。不过函数名有些差别。例如该项目的 STM32F4_Discovery_LEDInit() 对应 stm32f4discovery_fw.zip 中的 STM_EVAL_LEDInit() 。以下的区别也是如此。

USB支持文件:

  1. usb_bsp.c:是由USB支持库提供的,版本不同,差异很大
  2. usb_conf.h:与系统自带的差异很大,要看
  3. usbd_cdc.c:官方库没有找到对应内容,要看
  4. usbd_cdc.h:官方库没有找到对应内容,要看
  5. usbd_conf.h:与系统自带的差异很大,要看
  6. usbd_desc.c:配置文件,要看
  7. usbd_desc.h:配置文件,要看
  8. usbd_usr.c:与系统自带的差异很大,要看

用户文件列表:

  1. main.c

4.1.1???main.c

需导入诸多头文件:

  1. stm32f4xx.h
  2. usbd_cdc_core.h
  3. usbd_cdc.h
  4. usbd_usr.h
  5. usbd_desc.h

初始化一堆LED,使用了BSP:

STM32F4_Discovery_LEDInit(LED3);

STM32F4_Discovery_LEDInit(LED4);

STM32F4_Discovery_LEDInit(LED5);

STM32F4_Discovery_LEDInit(LED6);

STM32F4_Discovery_PBInit(BUTTON_USER,BUTTON_MODE_EXTI);

STM32F4_Discovery_LEDOn(LED3);

Delay(0xffff);

USB的初始化,有可能的话,尽量使用OTG_HS(480Mbps):

//外部声明

__ALIGN_BEGIN USB_OTG_CORE_HANDLE USB_OTG_dev __ALIGN_END;

//main()函数中

USBD_Init(&USB_OTG_dev,

#ifdef USB_USB_OTG_HS

USB_OTG_HS_CORE_ID,

#else

USB_OTG_FS_CORE_ID,

#endif

&USR_desc,

&USBD_CDC_cb,

&USR_cb);

注释说,魔术发生在 usbd_cdc.c 文件,其他应该看的还有 usbd_desc.h

最后就是每0x100000个周期让灯闪耀一次。

这里调用的 USBD_Init() 函数,定义于 Libraries/STM32_USB_Device_Library/Core/src/usbd_core.c

4.1.2???usbd_desc.h

实际上是几个常量定义,加一堆函数声明,实际配置内容并不在这里。常量:

#define USB_DEVICE_DESCRIPTOR_TYPE? ? ? ? ? ? ? 0x01

#define USB_CONFIGURATION_DESCRIPTOR_TYPE ? ? ? 0x02

#define USB_STRING_DESCRIPTOR_TYPE? ? ? ? ? ? ? 0x03

#define USB_INTERFACE_DESCRIPTOR_TYPE ? ? ? ? ? 0x04

#define USB_ENDPOINT_DESCRIPTOR_TYPE? ? ? ? ? ? 0x05

#define USB_SIZ_DEVICE_DESC ? ? ? ? ? ? ? ? ? ? 18

#define USB_SIZ_STRING_LANGID ? ? ? ? ? ? ? ? ? 4

4.1.3???usbd_desc.c

317行。常量定义如下:

#define USBD_VID? ? ? ? ? ? ? ? ? ? ? ? 0x304

#define USBD_PID? ? ? ? ? ? ? ? ? ? ? ? 0xe457

#define USBD_LANGID_STRING? ? ? ? ? ? ? 0x40b

#define USBD_MANUFACTURER_STRING? ? ? ? "Roope Kokkoniemi"

#define USBD_PRODUCT_HS_STRING? ? ? ? ? "stm32f4-discovery-usb-cdc-example"

#define USBD_SERIALNUMBER_HS_STRING ? ? "00000000050B"

#define USBD_PRODUCT_FS_STRING? ? ? ? ? "stm32f4-discovery-usb-cdc-example"

#define USBD_SERIALNUMBER_FS_STRING ? ? "00000000050C"

#define USBD_CONFIGURATION_HS_STRING? ? "usb-cdc-example config"

#define USBD_INTERFACE_HS_STRING? ? ? ? "usb-cdc-example Interface"

#define USBD_CONFIGURATION_FS_STRING? ? "usb-cdc-example config"

#define USBD_INTERFACE_FS_STRING? ? ? ? "usb-cdc-example Interface"

由此可见实际的VID、PID,以及定义的各种字符串。

89行定义结构体变量 USR_desc

USBD_Device USR_desc= {

? ? USBD_USR_DeviceDescriptor,

? ? USBD_USR_LangIDStrDescriptor,

? ? USBD_USR_ManufacturerStrDescriptor,

? ? USBD_USR_ProductStrDescriptor,

? ? USBD_USR_SerialStrDescriptor,

? ? USBD_USR_ConfigStrDescriptor,

? ? USBD_USR_InterfaceStrDescriptor,

};

具体函数定义都在下面呢。

107行的结构体 USBD_DeviceDesc 定义了USB设备的详细信息。

135行的结构体 USBD_DeviceQualifierDesc 似乎也是定义USB设备的,但是参数来源未知。10个成员。

155行的结构体 USBD_LangIDDesc 是以字符串描述的设备信息。

一系列不长,甚至仅仅用于返回字符串的函数:

  1. USBD_USR_DeviceDescriptor()
  2. USBD_USR_LangIDStrDescriptor()
  3. USBD_USR_ProductStrDescriptor()
  4. USBD_USR_ManufacturerStrDescriptor()
  5. USBD_USR_SerialStrDescriptor()
  6. USBD_USR_ConfigStrDescriptor()
  7. USBD_USR_InterfaceStrDescriptor()

4.1.4???stm32f4xx_it.c

中断处理的,大部分还是空的,前头有些外部变量定义:

extern USB_OTG_CORE_HANDLE ? ? ? ? ? USB_OTG_dev;

extern uint32_t USBD_OTG_ISR_Handler (USB_OTG_CORE_HANDLE *pdev);

extern void DISCOVERY_EXTI_IRQHandler(void);

?

#ifdef USB_OTG_HS_DEDICATED_EP1_ENABLED

extern uint32_t USBD_OTG_EP1IN_ISR_Handler (USB_OTG_CORE_HANDLE *pdev);

extern uint32_t USBD_OTG_EP1OUT_ISR_Handler (USB_OTG_CORE_HANDLE *pdev);

#endif

OTG_FS_WKUP_IRQHandler() 中断处理函数:

#ifdef USE_USB_OTG_FS

void OTG_FS_WKUP_IRQHandler(void)

{

? if(USB_OTG_dev.cfg.low_power)

? {

? ? *(uint32_t *)(0xE000ED10) &= 0xFFFFFFF9 ;

? ? SystemInit();

? ? USB_OTG_UngateClock(&USB_OTG_dev);

? }

? EXTI_ClearITPendingBit(EXTI_Line18);

}

#endif

OTG_HS_WKUP_IRQHandler() 中断处理函数:

#ifdef USE_USB_OTG_HS

void OTG_HS_WKUP_IRQHandler(void)

{

? if(USB_OTG_dev.cfg.low_power)

? {

? ? *(uint32_t *)(0xE000ED10) &= 0xFFFFFFF9 ;

? ? SystemInit();

? ? USB_OTG_UngateClock(&USB_OTG_dev);

? }

? EXTI_ClearITPendingBit(EXTI_Line20);

}

#endif

然后是将一些中断处理函数映射出去:

#ifdef USE_USB_OTG_HS

void OTG_HS_IRQHandler(void)

#else

void OTG_FS_IRQHandler(void)

#endif

{

? USBD_OTG_ISR_Handler (&USB_OTG_dev);

}

?

#ifdef USB_OTG_HS_DEDICATED_EP1_ENABLED

void OTG_HS_EP1_IN_IRQHandler(void)

{

? USBD_OTG_EP1IN_ISR_Handler (&USB_OTG_dev);

}

void OTG_HS_EP1_OUT_IRQHandler(void)

{

? USBD_OTG_EP1OUT_ISR_Handler (&USB_OTG_dev);

}

#endif

按钮的事件处理:

void EXTI0_IRQHandler(void) {

? ? DISCOVERY_EXTI_IRQHandler();

? ? /* Clear the EXTI line pending bit */

? ? EXTI_ClearITPendingBit(USER_BUTTON_EXTI_LINE);

}

可见基本上就是做一下初始化,然后把实际的中断处理都交给外面去做了。

函数 USBD_OTG_ISR_Handler()USBD_OTG_EP1IN_ISR_Handler()USBD_OTG_EP1OUT_ISR_Handler() 定义于 usb_dcd_int.c 文件。

4.1.5???usb_bsp.c

382行。主要就两个函数:

  1. USB_OTG_BSP_Init() :从90行开始,根据几种开发板,和HS/FS设置相关引脚的功能
  2. USB_OTG_BSP_EnableInterrupt() :从309行开始,配置各种中断

USE_USB_OTG_FS 用于定义STM32F4探索套件以OTG_FS运行。下面只看对STM32F4 Discovery的初始化。

USB_OTG_BSP_Init() 的内容:

  1. 将PA8、PA9、PA11、PA12定义为100MHz,无上拉下拉,GPIO_AF_OTG_FS
  2. 将PA10定义为100MHz、上拉,GPIO_OType_OD、GPIO_AF_OTG_FS
  3. 启用外设时钟:
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
  4. RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_OTG_FS,ENABLE);
  5. USB_OTG_HS的初始化从132行开始到243行,用到的线更多,先不看了
  6. 250行,启用PWR时钟 RCC_APB1PeriphResetCmd(RCC_APB1Periph_PWR,ENABLE);
  7. 最后用了很多行分别配置FS和HS模式的USER_BUTTON的功能,貌似用来唤醒USB的

USB_OTG_BSP_EnableInterrupt() 的内容,就是配置各种NVIC,优先级什么的,一共就29行。

4.1.6???usb_conf.h

271行。定义一些宏,以及各种USB RAM FIFO的大小。

定义的宏,先假设我们定义了宏 USE_USB_OTG_FS

  1. USB_OTG_FS_CORE
  2. RX_FIFO_FS_SIZE=128
  3. TX0_FIFO_FS_SIZE=32
  4. TX1_FIFO_FS_SIZE=128
  5. TX2_FIFO_FS_SIZE=32
  6. TX3_FIFO_FS_SIZE=0
  7. USB_DEVICE_MODE
  8. __ALIGN_BEGIN :对应的是空,但不为空时对应特定编译器的 __align(4) 用于对齐
  9. __ALIGN_END :对应的是空
  10. __packed __attribute__ ((__packed__)) :对GCC的宏

4.1.7???usbd_conf.h

98行。用于USB-CDC的一些设置。常量定义如下,也是假设定义了 USE_USB_OTG_FS

  1. USBD_CFG_MAX_NUM=1
  2. USBD_ITF_MAX_NUM=1
  3. USB_MAX_STR_DESC_SIZ=50
  4. CDC_IN_EP=0x81 :EP1用于data IN
  5. CDC_OUT_EP=0x01 :EP1用于data OUT
  6. CDC_CMD_EP=0x82 :EP2用于CDC命令
  7. CDC_DATA_MAX_PACKET_SIZE=64 :输入输出包最大大小
  8. CDC_CMD_PACKET_SZE=8 :控制端点包大小
  9. CDC_IN_FRAME_INTERVAL=5 :两个IN传输间最大帧数量
  10. APP_RX_DATA_SIZE=2048 :IN缓冲的总大小
  11. APP_FOPS=cdc_fops

4.1.8???usbd_usr.c

189行。先导入几个头文件:

  1. usbd_usr.h
  2. usbd_ioreq.h
  3. stm32f4_discovery.h

定义结构体变量:

USBD_Usr_cb_TypeDef USR_cb= {

? ? USBD_USR_Init,

? ? USBD_USR_DeviceReset,

? ? USBD_USR_DeviceConfigured,

? ? USBD_USR_DeviceSuspended,

? ? USBD_USR_DeviceResumed,

};

这里引用的5个函数都在下面定义的,但是实际都是操作LED5(红色)的:

  1. USBD_USR_Init() :初始化LED5
  2. USBD_USR_DeviceReset() :无内容,会传入 uint8_t speed 可选输出日志
  3. USBD_USR_DeviceConfigured() :点亮LED5
  4. USBD_USR_DeviceSuspended() :熄灭LED5
  5. USBD_USR_DeviceResume() :点亮LED5

4.1.9???usbd_cdc.h

41行。就定义了两个宏:

  1. DEFAULT_CONFIG=0
  2. OTHER_CONFIG=1

4.1.10???usbd_cdc.c

218行。具体的USB-CDC实现。

37-40行定义了收发缓冲区:

extern uint8_t APP_Rx_Buffer[];

extern uint32_t APP_Rx_ptr_in;

53-60行定义结构体变量:

CDC_IF_Prop_TypeDef cdc_fops= {

? ? cdc_Init,

? ? cdc_DeInit,

? ? cdc_Ctrl,

? ? cdc_DataTx,

? ? cdc_DataRx,

};

这里定义的5个函数中 cdc_Init()cdc_DeInit() 很简单,就是直接返回 USBD_OK 即可。

cdc_Ctrl() 是根据输入命令Cmd来用switch做处理的,但是虽然列出了所有命令,但是没有做任何处理,最后直接返回了 USBD_OK

cdc_DataTx() 用于通过IN端点发送数据,实际内部就是把参数的缓冲区内容复制到 APP_Rx_Buffer 中。最后返回 USBD_OK

cdc_DataRx() 用于通过OUT端点接收数据,本例实际就是将接到的数据回发回去而已。该函数会阻塞其他OUT包接收,直到退出该函数。如果在CDC接口完成前退出,会收到更多数据,而之前的却不会发出。

cdc_DataRx() 将参数指定的缓冲区写入内容,当遇到a/A时点亮LED6,当遇到s/S时熄灭LED6,再通过 cdc_DataTx() 函数将内容发出,最后返回 USBD_OK

DISCOVERY_EXTI_IRQHandler() 中断处理函数,用于指定缓冲区内容为 "terve" ,然后发送出去。

4.1.11???项目文件

文件名叫 stm32f4-discovery-usb-cdc-example.elf.launch

其中多处提到 atollic.hardwaredebug ,不知道是什么IDE的。而且也没有提到哪些文件应该一起编译进去,看来又要我自己想办法了。

4.2???stsw_stm32081.zip中STM32_USB-FS-Device_Lib_V4.0.0的USB库

4.2.1???usb_def.h

一些枚举:

typedef enum _RECIPIENT_TYPE {

? ? DEVICE_RECIPIENT,

? ? INTERFACE_RECIPIENT,

? ? ENDPOINT_RECIPIENT,

? ? OTHER_RECIPIENT,

} RECIPIENT_TYPE;

?

typedef enum _STANDARD_REQUESTS {

? ? GET_STATUS=0,

? ? CLEAR_FEATURE,

? ? RESERVED1,

? ? SET_FEATURE,

? ? RESERVED2,

? ? SET_ADDRESS,

? ? GET_DESCRIPTOR,

? ? SET_DESCRIPTOR,

? ? GET_CONFIRURATION,

? ? SET_CONFIRURATION,

? ? GET_INTERFACE,

? ? SET_INTERFACE,

? ? TOTAL_sREQUEST,

? ? SYNCH_FRAME=12

} STAND_REQUESTS; //by gashero

?

typedef enum _DESCRIPTOR_TYPE {

? ? DEVICE_DESCRIPTOR=1,

? ? CONFIG_DESCRIPTOR,

? ? STRING_DESCRIPTOR,

? ? INTERFACE_DESCRIPTOR,

? ? ENDPOINT_DESCRIPTOR,

} DESCRIPTOR_TYPE;

?

typedef enum _FEATURE_SELECTOR {

? ? ENDPOINT_STALL,

? ? DEVICE_REMOTE_WAKEUP,

} FEATURE_SELECTOR;

一些常量定义:

#define REQUEST_TYPE? ? 0x60

#define STANDARD_REQUEST? ? 0x00

#define CLASS_REQUEST ? 0x20

#define VENDOR_REQUEST? 0x40

#define RECIPIENT ? ? ? 0x1f

4.2.2???usb_type.h

内容特别短,如下:

#include "usb_conf.h"

#ifndef NULL

#define NULL ((void*)0)

#endif

?

typedef enum {

? ? FALSE=0, TRUE=!FALSE

} bool;

4.2.3???usb_lib.h

只是导入了一堆其他头文件:

#include "hw_config.h"

#include "usb_type.h"

#include "usb_regs.h"

#include "usb_def.h"

#include "usb_core.h"

#include "usb_init.h"

#include "usb_sil.h"

#include "usb_mem.h"

#include "usb_int.h"

4.2.4???usb_core.h/.c

usb_core.h

定义了一些数据结构:

  1. _CONTROL_STATE 枚举
  2. OneDescriptor 结构体
  3. _RESULT 枚举
  4. _ENDPOINT_INFO 结构体
  5. _DEVICE 结构体
  6. _DEVICE_INFO 结构体
  7. _DEVICE_PROP 结构体
  8. _USER_STANDARD_REQUEST 结构体

一堆导出函数就不写了,在 usb_core.c 里写。

一些从外部导入的变量:

  1. DEVICE_PROP Device_Property
  2. USER_STANDARD_REQUESTS User_Standard_Requests
  3. DEVICE Device_Table
  4. DEVICE_INFO Device_Info
  5. uint16_t SaveRState
  6. uint16_t SaveTState

usb_core.c

@wait

4.2.5???usb_init.h/.c

@wait

4.2.6???usb_int.h/.c

@wait

4.2.7???usb_mem.h/.c

@wait

4.2.8???usb_regs.h/.c

都很长。

usb_regs.h

定义的数据结构:

  1. _EP_DBUF_DIR 枚举
  2. EP_BUF_NUM 枚举

一些地址:

  1. RegBase:USB外设基址
  2. PMAAddr:Packet Memory Area基址
  3. CNTR:寄存器
  4. ISTR:寄存器
  5. FNR:寄存器
  6. DADDR:寄存器
  7. BTABLE:寄存器
  8. EP<N>_OUT:端点寄存器
  9. EP<N>_IN:端点寄存器
  10. ENDP<N>:端点枚举值

一些成段的声明:

  1. ISTR中断事件:105-125行
  2. CNTR寄存器位:130-144行
  3. FNR寄存器位:149-153行
  4. DADDR寄存器位:157-158行
  5. 端点寄存器:163-205行

@wait 看到206行的导出宏,太大了

4.2.9???usb_sil.h/.c

@wait

4.3???stsw-stm32081.zip中STM32_USB-FS-Device_Lib_V4.0.0的VirtualComport_Loopback

分析的是模板,而不是我改出来的。(by gashero)

要改进通信速度,应该从两个方面,一个是usb_endp.c中每秒发送次数,一个是使用CTR中断。

4.3.1???main.c

导入头文件:

#include "hw_config.h"

#include "usb_lib.h"

#include "usb_desc.h"

#include "usb_pwr.h"

一些全局需要使用的变量,从外部导入:

extern __IO uint8_t Receive_Buffer[64];

extern __IO uint32_t Receive_length;

extern __IO uint32_t length;

uint8_t Send_Buffer[64];

uint32_t packet_sent=1;

uint32_t packet_receive=1;

主函数,初始化USB相关的东西,以及按照收到的数据来转发:

int main() {

? ? Set_System();

? ? Set_USBClock();

? ? USB_Interrupts_Config();

? ? USB_Init();

?

? ? while(1) {

? ? ? ? if (bDeviceState==CONFIGURED) {

? ? ? ? ? ? CDC_Receive_DATA();

? ? ? ? ? ? if(Receive_length!=0) {

? ? ? ? ? ? ? ? if(packet_sent==1) {

? ? ? ? ? ? ? ? ? ? CDC_Send_DATA((unsigned char*)Receive_Buffer,Receive_length);

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? Receive_length=0;

? ? ? ? ? ? }

? ? ? ? }

? ? }

}

标准的断言处理:

#ifdef USE_FULL_ASSERT

void assert_failed(uint8_t *file, uint32_t line) {

? ? while(1);

}

#endif

4.3.2???platform_config.h

看来是平台相关配置。

Note

移植过程大量修改这里。

32行到72行,必须声明个开发板什么的,其实无所谓,都删除掉就是了。然后导入 "stm32f10x.h" 。

76行到94行,是声明3个ID,不知干啥用的,反正没改也过去了。

97行到148行,是声明D+上拉电阻控制引脚的。该引脚低电平有效,开启D+的上拉电阻。我是将其全部删掉,然后自己重新定义的:

#define USB_DISCONNECT? ? ? GPIOB

#define USB_DISCONNECT_PIN? GPIO_Pin_1

#define RCC_APB2Periph_GPIO_DISCONNECT RCC_APB2Periph_GPIOB

4.3.3???hw_config.h/.c

hw_config.h

导入头文件:

#include "platform_config.h"

#include "usb_type.h"

几个导出常量,不太清楚:

#define MASS_MEMORY_START ? ? ? 0x04002000

#define BULK_MAX_PACKET_SIZE? ? 0x00000040

#define LED_ON? ? ? ? ? ? ? ? ? 0xf0

#define LED_OFF ? ? ? ? ? ? ? ? 0xff

然后就是声明10个函数,不写了。

hw_config.c

导入头文件:

#include "stm32_it.h"

#include "usb_lib.h"

#include "usb_prop.h"

#include "usb_desc.h"

#include "hw_config.h"

#include "usb_pwr.h"

一堆似有变量声明:

ErrorStatus HSEStartUpStatus;

EXTI_InitTypeDef EXTI_InitStructure;

extern __IO uint32_t packet_sent;

extern __IO uint8_t Send_Buffer[VIRTUAL_COM_PORT_DATA_SIZE];

extern __IO uint32_t packet_receive;

extern __IO uint8_t Receive_length;

uint8_t Receive_Buffer[64];

uint32_t Send_length;

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

extern LINE_CODING linecoding;

各个函数的定义:

  1. Set_System() :63-141行,配置USB使用的GPIO,以及中断线
  2. Set_USBClock() :149-162行,开启USB时钟
  3. Enter_LowPowerMode() :170-174行,进入低功耗模式
  4. Leave_LowPowerMode() :182-198行,离开低功耗模式,需要重新初始化系统
  5. USB_Interrupts_Config() :206-254行,中断配置,包括低优先级,高优先级、唤醒
  6. USB_Cable_Config(NewState) :262-284行,控制D+上拉是否开启
  7. Get_SerialNum() :293-308行,创建一个序列号字符串描述符
  8. IntToUnicode(value,*pbuf,len) :317-336行,看来是数字到Unicode的转换,有必要么
  9. CDC_Send_DATA(*ptrBuffer,Send_length) :345-362行,发送数据,直接调用USB库的3个函数,成功返回1,失败返回0
  10. CDC_Receive_DATA() :371-377行,获取数据,返回1

4.3.4???stm32_it.h/.c

stm32_it.h 就是声明了一堆的中断处理函数,共11个。

stm32_it.c 定义了11个中断处理函数。

导入头文件:

#include "hw_config.h"

#include "stm32_it.h"

#include "usb_lib.h"

#include "usb_istr.h"

中断处理函数中,9个标准的:

  1. NMI_Handler() :空的
  2. HardFault_Handler() :死循环
  3. MemManage_Handler() :死循环
  4. BusFault_Handler() :死循环
  5. UsageFault_Handler() :死循环
  6. SVC_Handler() :空的
  7. DebugMon_Handler() :空的
  8. PendSV_Handler() :空的
  9. SysTick_Handler() :空的

然后就是两个USB相关的,以HD设备为例:

USB_LP_IRQHandler() :内部直接调用 USB_Istr()

USB_FS_WKUP_IRQHandler() :内部直接调用 EXTI_ClearITPendingBit(EXTI_Line18)

4.3.5???usb_conf.h

一些声明。

  1. EP_NUM (4) :使用的端点数量
  2. BTABLE_ADDRESS (0x00) :缓冲表基址
  3. ENDP<N>_RXADDR (0x40) :端点N的接收缓冲基址
  4. ENDP<N>_TXADDR (0x80) :端点N的发送缓冲基址
  5. IMR_MSK :事件屏蔽位
  6. EP<N>_IN_Callback :输入端点回调,全是空的
  7. EP<N>_OUT_Callback :输出端点回调,全是空的

实际的缓冲表部分:

#define BTABLE_ADDRESS? (0x00)

#define ENDP0_RXADDR? ? (0x40)

#define ENDP0_TXADDR? ? (0x80)

#define ENDP1_TXADDR? ? (0xc0)

#define ENDP2_TXADDR? ? (0x100)

#define ENDP3_RXADDR? ? (0x110)

IMR_MSK的声明:

#define IMR_MSK (CNTR_CTRM | CNTR_WKUPM | CNTR_SUSPM | CNTR_ERRM | \

? ? CNTR_SOFM | CNTR_ESOFM | CNTR_RESETM)

4.3.6???usb_desc.h/.c

决定了设备显示的名字,和其他字符串描述。收发缓冲区大小也是在这里。

usb_desc.h 一些常量定义和函数声明:

#define USB_DEVICE_DESCRIPTOR_TYPE? ? ? ? ? 0x01

#define USB_CONFIGURATION_DESCRIPTOR_TYPE ? 0x02

#define USB_STRING_DESCRIPTOR_TYPE? ? ? ? ? 0x03

#define USB_INTERFACE_DESCRIPTOR_TYPE ? ? ? 0x04

#define USB_ENDPOINT_DESCRIPTOR_TYPE? ? ? ? 0x05

?

#define VIRTUAL_COM_PORT_DATA_SIZE? ? ? ? ? 64

#define VIRTUAL_COM_PORT_INT_SIZE ? ? ? ? ? 8

?

#define VIRTUAL_COM_PORT_SIZ_DEVICE_DESC? ? 18

#define VIRTUAL_COM_PORT_SIZ_CONFIG_DESC? ? 67

#define VIRTUAL_COM_PORT_SIZ_STRING_LANGID? 4

#define VIRTUAL_COM_PORT_SIZ_STRING_VENDOR? 38

#define VIRTUAL_COM_PORT_SIZ_STRING_PRODUCT 50

#define VIRTUAL_COM_PORT_SIZ_STRING_SERIAL? 26

?

#define STANDARD_ENDPOINT_DESC_SIZE ? ? ? ? 0x09

usb_desc.c 一些结构体的定义。

导入头文件:

#include "usb_lib.h"

#include "usb_desc.h"

Virtual_Com_Port_DeviceDescriptor 数组,元素是uint8_t类型,18个元素。

Virtual_Com_Port_ConfigDescriptor 数组,元素是uint8_t类型,65个元素。

Virtual_Com_Port_StringLangID 数组,元素是uint8_t类型,4个元素。

Virtual_Com_Port_StringVendor 数组,元素是uint8_t类型,具体长度看VIRTUAL_COM_PORT_SIZ_STRING_VENDOR。

Virtual_Com_Port_StringProduct 数组,元素是uint8_t类型,具体长度看VIRTUAL_COM_PORT_SIZ_STRING_PRODUCT。

Virtual_Com_Port_StringSerial 数组,元素是uint8_t类型,具体长度看VIRTUAL_COM_PORT_SIZ_STRING_SERIAL。

4.3.7???usb_istr.h/.c

分发回调函数的声明和定义。

usb_istr.h 一些声明。

导入头文件:

#include "usb_conf.h"

导出函数 USB_Istr() 。直接声明的回调函数 EP<N>_IN_CallbackEP<N>_IN_Callback 其中N取1~7。

一些需要宏定义才声明的函数,对应宏的名字就是函数名的全大写:

  1. CTR_Callback()
  2. DOVR_Callback()
  3. ERR_Callback()
  4. WKUP_Callback()
  5. SUSP_Callback()
  6. RESET_Callback()
  7. SOF_Callback()
  8. ESOF_Callback()

usb_istr.c 各类回调函数的定义。

导入头文件:

#include "usb_lib.h"

#include "usb_prop.h"

#include "usb_pwr.h"

#include "usb_istr.h"

似有变量声明:

__IO uint16_t wIstr;

__IO uint8_t bIntPackSOF=0; //by gashero

__IO uint32_t esof_counter=0;

__IO uint32_t wCNTR=0;

非控制端点的函数指针 pEpInt_INpEpInt_OUT

USB_Istr() 从77-229行。包括按照各种标识调用各个其他回调函数,相当于一个分派器。从现在看还是针对USB的,而不是虚拟串口的。

4.3.8???usb_prop.h/.c

usb_prop.h 常量定义和14个函数声明。

一个结构体的定义:

typedef struct {

? ? uint32_t bitrate;

? ? uint8_t format;

? ? uint8_t paritytype;

? ? uint8_t datatype;

} LINE_CODING;

常量定义:

#define Virtual_Com_Port_GetConfiguration? ? ? ? ? NOP_Process

//#define Virtual_Com_Port_SetConfiguration? ? ? ? ? NOP_Process

#define Virtual_Com_Port_GetInterface? ? ? ? ? ? ? NOP_Process

#define Virtual_Com_Port_SetInterface? ? ? ? ? ? ? NOP_Process

#define Virtual_Com_Port_GetStatus ? ? ? ? ? ? ? ? NOP_Process

#define Virtual_Com_Port_ClearFeature? ? ? ? ? ? ? NOP_Process

#define Virtual_Com_Port_SetEndPointFeature? ? ? ? NOP_Process

#define Virtual_Com_Port_SetDeviceFeature? ? ? ? ? NOP_Process

//#define Virtual_Com_Port_SetDeviceAddress? ? ? ? ? NOP_Process

?

#define SEND_ENCAPSULATED_COMMAND ? 0x00

#define GET_ENCAPSULATED_RESPONSE ? 0x01

#define SET_COMM_FEATURE? ? ? ? ? ? 0x02

#define GET_COMM_FEATURE? ? ? ? ? ? 0x03

#define CLEAR_COMM_FEATURE? ? ? ? ? 0x04

#define SET_LINE_CODING ? ? ? ? ? ? 0x20

#define GET_LINE_CODING ? ? ? ? ? ? 0x21

#define SET_CONTROL_LINE_STATE? ? ? 0x22

#define SEND_BREAK? ? ? ? ? ? ? ? ? 0x23

usb_prop.c

导入头文件:

#include "usb_lib.h"

#include "usb_conf.h"

#include "usb_prop.h"

#include "usb_desc.h"

#include "usb_pwr.h"

#include "hw_config.h"

定义变量:

uint8_t Request=0;

一些结构体的实例化:

  1. LINE_CODING linecoding :波特率115200,无停止位,无校验位,8位数据
  2. DEVICE Device_Table :就2个字段,EP_NUM和1
  3. DEVICE_PROP Device_Property :12个字段的结构体定义,传入串口的一堆操作函数
  4. USER_STANDARD_REQUESTS User_Standard_Requests :9个字段,也是一堆函数引用
  5. ONE_DESCRIPTOR Device_Descriptor :2个字段,描述符
  6. ONE_DESCRIPTOR Config_Descriptor :2个字段,描述符
  7. ONE_DESCRIPTOR String_Descriptor[4] :4个元素的数组,每个2个字段,字符串描述符

虚拟串口相关的操作函数:

  1. Virtual_Com_Port_init() :121-137行,串口初始化
  2. Virtual_Com_Port_Reset() :146-191行,串口复位
  3. Virtual_Com_Port_SetConfiguration() :200-209行,设置配置
  4. Virtual_Com_Port_SetDeviceAddress() :218-221行,设置设备地址
  5. Virtual_Com_Port_Status_In() :230-236行,IN状态
  6. Virtual_Com_Port_Status_Out() :245-256行,OUT状态,无内容
  7. Virtual_Com_Port_Data_Setup() :255-286行,数据设置
  8. Virtual_Com_Port_NoData_Setup() :295-311行,无数据设置
  9. Virtual_Com_Port_GetDeviceDescriptor() :320-323行,获取设备描述符
  10. Virtual_Com_Port_GetConfigDescriptor() :332-335行,获取配置描述符
  11. Virtual_Com_Port_GetStringDescriptor() :344-355行,获取字符串描述符
  12. Virtual_Com_Port_Get_Interface_Setting() :366-377行,获取接口设置
  13. Virtual_Com_Port_GetLineCoding() :386-394行,获取行编码
  14. Virtual_Com_Port_SetLineCoding() :403-411行,设置行编码

4.3.9???usb_pwr.h/.c

usb_pwr.h

两个枚举定义,恢复状态和设备状态:

typedef enum _RESUME_STATE {

? ? RESUME_EXTERNAL,

? ? RESUME_INTERNAL,

? ? RESUME_LATER,

? ? RESUME_WAIT,

? ? RESUME_START,

? ? RESUME_ON,

? ? RESUME_OFF,

? ? RESUME_ESOF,

} RESUME_STATE;

?

typedef enum _DEVICE_STATE {

? ? UNCONNECTED,

? ? ATTACHED,

? ? POWERED,

? ? SUSPENDED,

? ? ADDRESSED,

? ? CONFIGURED,

} DEVICE_STATE;

然后是5个函数的声明。

两个变量的声明:

extern __IO uint32_t bDeviceState;

extern __IO bool fSuspendEnabled;

usb_pwr.c

导入头文件:

#include "usb_lib.h"

#include "usb_conf.h"

#include "usb_pwr.h"

#include "hw_config.h"

变量和结构体定义:

__IO uint32_t bDeviceState = UNCONNECTED;

__IO bool fSuspendEnabled=TRUE;

__IO uint32_t EP[8];

?

struct {

? ? __IO RESUME_STATE eState;

? ? __IO uint8_t bESOFcnt;

} ResumeS;

?

__IO uint32_t remotewakeupon=0;

函数的定义:

  1. PowerOn() :64-85行,一些基本配置,返回USB_SUCCESS
  2. PowerOff() :94-108行,基本配置,返回USB_SUCCESS
  3. Suspend() :117-213行,挂起支持
  4. Resume_Init() :222-245,恢复的初始化
  5. Resume() :259-316行,恢复

4.3.10???usb_endp.c

导入头文件:

#include "usb_lib.h"

#include "usb_desc.h"

#include "usb_mem.h"

#include "hw_config.h"

#include "usb_istr.h"

#include "usb_pwr.h"

一个常量定义,发送IN数据包的间隔1帧=1mS:

#define VCOMPORT_IN_FRAME_INTERVAL 5

一些变量定义:

extern __IO uint32_t packet_sent;

extern __IO uint32_t packet_receive;

extern __IO uint8_t Receive_Buffer[64];

uint32_t Receive_length;

然后是一堆看起来是回调函数的。不过刚才在 usb_conf.h 中说了都是空的。具体不清楚,先把定义都写了吧:

void EP1_IN_Callback() {

? ? packet_sent=1;

}

?

void EP3_OUT_Callback() {

? ? packet_receive=1;

? ? Receive_length=GetEPRxCount(ENDP3);

? ? PMAToUserBufferCopy((unsigned char*)Receive_Buffer, ENDP3_RXADDR, Receive_length);

}

5???应用注记

5.1???使用CDC类与上位机的通信

已经使用USB库实现了,但是现在通信经常丢包,所以研究接下来的办法。

5.1.1???尝试CTR中断的通信

分析发现CTR关键字在 STM32_USB-FS-Device_Lib_V4.0.0 库中,出现在如下文件中:

  1. inc/usb_int.h:3处
  2. inc/usb_regs.h:20处
  3. src/usb_int.c:27处
  4. src/usb_regs.c:8处

且没有看到具体的中断处理有关语句。

usb_regs.h 中声明的两个函数 ClearEP_CTR_RX(bEpNum)ClearEP_CTR_TX(bEpNum) 看来是用来清除收发两个方向的CTR标识的。

src/usb_int.c 中有关CTR的调用全部在两个函数中,即 CTR_HP()CTR_LP() 。貌似是中断处理函数。

在应用代码中,CTR在 usb_conf.h 中出现两处,用于定义要启用CTR_CALLBACK。应该从这里启用CTR回调。另外在 usb_istr.h 中出现两处,没有意义。在 usb_istr.c 中出现六处,启用回调。

所以要启用CTR回调,分为几个步骤:

  1. usb_conf.h 的75行,启用宏 CTR_CALLBACK
  2. 不要修改 usb_istr.* 文件
  3. 在应用中定义 CTR_Callback() 函数,内容自己玩

确定了每个端点都有自己的CTR_TX和CTR_RX位,我需要的是向上位机发送数据,所以要寻找特定端点的CTR_TX位。

usb_regs.h 中定义了端点寄存器的一些值 EP_CTR_RXEP_CTR_TX 。也许就是 GetEPTxStatus() 函数。实际的实现是在 usb_regs.h:337 的一个宏。当该函数返回0x30时,就是可以发送数据了。

USB外设的基址 0x4000,5c00 。

分析下USB CDC应用中的4个端点:

  1. EP0:USB_EP0R=0x5210,CONTROL端点
  2. EP1:USB_EP1R=0x0031,BULK端点
  3. EP2:USB_EP2R=0x0622,INTERRUPT端点
  4. EP3:USB_EP3R=0x3003,BULK端点

这里几乎看不出东西。那就分析 CDC_Send_DATA() 函数。直接在 hw_config.c 中发现了,是通过EP1发送的。而接收则是EP3。没找到EP2干啥的。

标准做法是收到主机ACK后,通过USB_ISTR寄存器的EP_ID和DIR位识别是哪里产生的事件,然后清除CTR_TX位,然后准备好发送缓冲区。DIR=0时是只有CTR_TX被置位,DIR=1时则CTR_RX被置位,而CTR_TX可能被置位。所以对于只关心CTR_TX的我,可以不看DIR。实际上USB_ISTR中并没有看到任何值,都是0。

5.2???使用USB-FS-Device的VirtualComPort_Loopback例子,作为与电脑的USB串口通信

先从 STM32_USB-FS-Device_Lib_V4.0.0/Projects/VirtualComPort_Loopback 目录打开,其内重要的内容包括inc目录里的头文件和src目录里的C文件。把如下文件拷贝到应用的目录里:

  1. hw_config.h/.c
  2. stm32_it.h/.c
  3. usb_conf.h
  4. usb_desc.h/.c
  5. usb_endp.c
  6. usb_istr.h/.c
  7. usb_prop.h/.c
  8. usb_pwr.h/.c

然后都要编译到程序里。

原装的 platform_config.h 太麻烦了,自己写一个简单的:

#ifndef __PLATFORM_CONFIG_H

#define __PLATFORM_CONFIG_H

?

#ifdef BLUERIDGE13

?

#include <stm32f10x.h>

?

#define USB_DISCONNECT? ? ? ? ? GPIOB

#define USB_DISCONNECT_PIN? ? ? GPIO_Pin_1

#define RCC_APB2Periph_GPIO_DISCONNECT RCC_APB2Periph_GPIOB

#define ? ? ? ? ID1? ? ? ? ? (0x1FFFF7E8)

#define ? ? ? ? ID2? ? ? ? ? (0x1FFFF7EC)

#define ? ? ? ? ID3? ? ? ? ? (0x1FFFF7F0)

?

#endif

?

#endif

所以这里的关键内容就是定义芯片的头文件,USB断开的引脚(PB1),所用外设时钟,以及3各ID,不知干啥的。

另外在自己程序的主文件里需要声明几个全局变量以及头文件,方便后续使用:

#include "hw_config.h"

#include "usb_lib.h"

#include "usb_desc.h"

#include "usb_pwr.h"

#include "usb_regs.h"

?

extern __IO uint8_t Receive_Buffer[64];

extern __IO uint32_t Receive_Length;

extern __IO uint32_t length;

?

__IO uint32_t packet_sent=1;

__IO uint32_t packet_receive=1;

拥有如上信息就能编译成功了。

stm32_it.c 中有一些没必要的中断声明,反倒耽误我做事了,可以直接过去注释掉,比如 SysTick_Handler()

main() 函数里需要加几行初始化内容,然后才能实际的发送内容:

Set_System(); ? ? ? //必须有

Set_USBClock();

USB_Interrupts_Config();

USB_Init();

发送数据到PC的例子,基于SysTick,初始化为 SysTick_Config(9000000);??? //8Hz 。然后实际的代码:

void SysTick_Handler() {

? ? STM_EVAL_LEDToggle(LED2);

? ? if ((GetEPTxStatus(1) & EP_TX_NAK)=EP_TX_NAK) {

? ? ? ? CDC_Send_DATA((uint8_t*)"Hello\r\n",7);

? ? }

}

然后用minicom就可以看到发来的数据了。minicom在数据的发送上是每次一个字符的,务必小心。单片机接收到的也是每次一个字符。而不是在回车时一个完整的。而Python的serial库等,就能一次发送个完整的字符串。

要在单片机上接收上位机发来的信息,使用:

extern __IO uint8_t Receive_Buffer[64];

extern __IO uint32_t Receive_length;? ? ? ? ? ? ? ? //没错,后面的length是全小写的

?

while(1) {

? ? CDC_Receive_DATA(); ? ? //接收信息并更新全局变量

? ? if (Receive_length!=0) {? ? //收到了信息

? ? ? ? CDC_Send_DATA((uint8_t*)Receive_Buffer,Receive_length);

? ? ? ? Receive_length=0; ? //必须写

? ? }

}

5.3???休眠处理

stm32提供的USB库会在特定情况下让芯片进入挂起状态来省电。但一旦进入挂起模式,HSE会停止,导致JTAG/SWD调试也停止了,就没法继续调试了。而这个功能,对于大多数时候并没有什么意义。

挂起模式的实现在 usb_pwr.cSuspend() ,被 usb_istr.c 所调用。逻辑是当变量 fSuspendEnabled=TRUE 时就调用。

最简单方便的解决方法是在主程序的启动文件里声明一下该变量:

extern __IO bool fSuspendEnabled;

然后在主程序里将其设置为不进入挂起:

fSuspendEnabled=FALSE;

然后就不会进入该死的挂起了。

?

这种挂起状态往往是因为USB设置出了问题,USB初始化失败,从而进入了挂起。而正常启动USB设备时不会出现该问题。

STM32 USB应用笔记

原文:http://gashero.iteye.com/blog/2300218

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!