打印

【转】STM32 USB 问题汇总

[复制链接]
楼主: sunmeat
手机看帖
扫描二维码
随时随地手机跟帖
21
sunmeat|  楼主 | 2015-2-26 19:20 | 只看该作者 回帖奖励 |倒序浏览
同时用户并行进行下面处理
count_out = GetEPDblBuf0Count(ENDP3);//读取接收到的字节数
 PMAToUserBufferCopy(buffer_out, ENDP3_BUF0Addr, count_out); 
  }
  else
  {
    FreeUserBuffer(ENDP3, EP_DBUF_OUT);  
    count_out = GetEPDblBuf1Count(ENDP3);
    PMAToUserBufferCopy(buffer_out, ENDP3_BUF1Addr, count_out);
  }
}

使用特权

评论回复
22
sunmeat|  楼主 | 2015-2-26 19:20 | 只看该作者
经过上面的修改,终于解决了STM32在处理接收数据时导致主机等待的情况,用BUS HOUND软件测试了下

    哈哈,这下终于爽了。
PS:上面的FreeUserBuffer(ENDP3, EP_DBUF_OUT); 这句话的上下位置是关键,如果放到函数的后面,则仍旧会有主机等待STM32处理数据的情况,速度仍然是500KB/S!

使用特权

评论回复
23
sunmeat|  楼主 | 2015-2-26 19:20 | 只看该作者
把这句话放在拷贝函数的前面的话就真正把双缓冲PING-PONG机制用起来了。大致算了下PMAToUserBufferCopy(buffer_out, ENDP3_BUF1Addr, count_out);这句话当count_out为最大值64的时候STM32执行需要302个周期,72MHZ情况下约4.2微秒执行时间,而USB传输按照12Mb/s的线速度传输64字节的数据至少也得40微秒,因此只要PMAToUserBufferCopy的时间不超过40微秒,就不会导致缓冲区竞争的情况。

使用特权

评论回复
24
sunmeat|  楼主 | 2015-2-26 19:21 | 只看该作者
汇总3:STM32的USB中断说明,来自:http://bbs.**/BLOG_ARTICLE_238817.HTM
STM32的USB模块可以产生三种中断:USB唤醒中断、USB高优先级中断和USB低优先级中断,在STM32的参考手册中没有详细说明这三种中断对应哪些事件,

使用特权

评论回复
25
sunmeat|  楼主 | 2015-2-26 19:21 | 只看该作者
现说明如下:

1)USB唤醒中断:在中断向量表中的位置是42。这个中断在USB设备从暂停模式唤醒时产生,唤醒事件由USB_ISTR寄存器的WKUP位标识。

使用特权

评论回复
26
sunmeat|  楼主 | 2015-2-26 19:21 | 只看该作者
2)USB高优先级中断:在中断向量表中的位置是19。这个中断仅由USB同步(Isochronous)模式传输或双缓冲块(Bulk)传输模式下的正确传输事件产生,正确传输事件由USB_ISTR寄存器的CTR位标识。

使用特权

评论回复
27
sunmeat|  楼主 | 2015-2-26 19:22 | 只看该作者
3)USB低优先级中断:在中断向量表中的位置是20。这个中断由所有其它的USB事件产生,例如正确传输(不包括同步模式和双缓冲块模式)、USB复位等,事件标志位在USB_ISTR寄存器中。

使用特权

评论回复
28
sunmeat|  楼主 | 2015-2-26 19:22 | 只看该作者
在STM32的USB开发包的例子中包含了上述中断的处理,例如在USB扬声器的例子中,CTR_HP函数处理USB高优先级中断;在所有例子中都有USB_Istr()函数处理USB低优先级中断

使用特权

评论回复
29
sunmeat|  楼主 | 2015-2-26 19:22 | 只看该作者
汇总4:如何使用STM32的USB库支持控制端点0,来自:http://bbs.**/BLOG_ARTICLE_242276.HTM
首先我们先回顾一下控制端点的传输方式:

控制端点的传输有三个阶段,SETUP阶段、数据阶段和状态阶段;数据阶段又分为数据入(DATA IN)和数据出(DATA OUT),控制端点传输可以没有数据阶段;状态阶段有状态入(STATUS IN)和状态出(STATUS OUT)。

使用特权

评论回复
30
sunmeat|  楼主 | 2015-2-26 19:23 | 只看该作者
总结起来,控制端点有如下三种可能的传输过程(以下括号中的0或1表示DATA0或DATA1传输):
一、 SETUP  DATA_IN(0)  DATA_IN(1)  DATA_IN(0)  ......  STATUS_OUT(1)
二、 SETUP  DATA_OUT(0)  DATA_OUT(1)  DATA_OUT(0)  ...... STATUS_IN(1)
三、 SETUP  STATUS_IN(1)

使用特权

评论回复
31
sunmeat|  楼主 | 2015-2-26 19:23 | 只看该作者
这里做一个约定,把上述过程一定义为“数据入过程”,过程二定义为“数据出过程”,过程三定义为“无数据过程”。所有的USB控制端点的数据传输都可以而且只用这三种传输过程表示。HID的SET_REPORT是数据出过程,HID的GET_REPORT是数据入过程,USB的GET DEVICE DESCRIPTOR是数据入过程,USB的SET CONFIGURATION是无数据过程,等等。

使用特权

评论回复
32
sunmeat|  楼主 | 2015-2-26 19:23 | 只看该作者
接下来,我们看看STM32的USB库是如何处理控制端点0的传输。

使用特权

评论回复
33
sunmeat|  楼主 | 2015-2-26 19:24 | 只看该作者
根据USB协议,每个SETUP包都由8个字节构成,用户程序可以通过结构体Device_Info(类型DEVICE_INFO)访问SETUP包的数据,因为在整个的USB处理中都要用到结构体Device_Info的内容,库中定义了一个全局的指针pInformation指向这个结构体,用户可以通过这个指针访问结构体的内容。

使用特权

评论回复
34
sunmeat|  楼主 | 2015-2-26 19:24 | 只看该作者
对应SETUP包的8个字节,用户可以用下述方式访问:
  pInformation->USBbmRequestType (字节类型)
  pInformation->USBbRequest  (字节类型)
  pInformation->USBwValue  (双字节类型)
  pInformation->USBwIndex  (双字节类型)
  pInformation->USBwLength (双字节类型)

使用特权

评论回复
35
sunmeat|  楼主 | 2015-2-26 19:24 | 只看该作者
使用pInformation->USBwValue0访问wValue的低字节,pInformation->USBwValue1访问wValue的高字节。
使用pInformation->USBwIndex0访问USBwIndex的低字节,pInformation->USBwIndex1访问USBwIndex的高字节。
使用pInformation->USBwLength0访问USBwLength的低字节,pInformation->USBwLength1访问USBwLength的高字节。

使用特权

评论回复
36
sunmeat|  楼主 | 2015-2-26 19:25 | 只看该作者
通过分析SETUP包的8个字节,可以判断出一个SETUP的传输过程是属于数据入过程、数据出过程还是无数据过程。STM32的USB库中处理了所有的USB协议文本中定义的标准SETUP命令,对于USB协议文本中未定义的命令,USB库按照数据入过程、数据出过程或无数据过程通过回调函数交给用户程序处理。

使用特权

评论回复
37
sunmeat|  楼主 | 2015-2-26 19:25 | 只看该作者
全局变量Device_Property(DEVICE_PROP类型)封装了所有的回调函数,DEVICE_PROP定义如下:
typedef struct _DEVICE_PROP
{
  void (*Init)(void);        // 设备初始化回调函数
  void (*Reset)(void);       // USB复位回调函数

  void (*Process_Status_IN)(void);  // STATUS_IN阶段处理回调函数
  void (*Process_Status_OUT)(void); // STATUS_OUT阶段处理回调函数

  RESULT (*Class_Data_Setup)(u8 RequestNo);   // 数据入/出过程处理回调函数
  RESULT (*Class_NoData_Setup)(u8 RequestNo); // 无数据过程处理回调函数

  RESULT  (*Class_Get_Interface_Setting)(u8 Interface, u8 AlternateSetting); // GET_INTERFACE 回调函数

  u8* (*GetDeviceDescriptor)(u16 Length); // GET_DEVICE_DESCRIPTION回调函数
  u8* (*GetConfigDescriptor)(u16 Length); // GET_CONFIGURATION_DESCRIPTION回调函数
  u8* (*GetStringDescriptor)(u16 Length); // GET_STRING_DESCRIPTION回调函数
  u8 MaxPacketSize; // 最大包长度
} DEVICE_PROP;

使用特权

评论回复
38
sunmeat|  楼主 | 2015-2-26 19:25 | 只看该作者
结合SETUP的三种传输过程,用户通过实现不同的回调函数即可完成对各种USB类命令的处理,下面以HID的SET REPORT为例说明。

使用特权

评论回复
39
sunmeat|  楼主 | 2015-2-26 19:26 | 只看该作者
在介绍具体实现之前,先介绍一下另一个回调函数CopyRoutine的概念,这个函数的原型是:
   u8 *CopyRoutine(u16 length);    // 返回一个缓冲区指针

使用特权

评论回复
40
sunmeat|  楼主 | 2015-2-26 19:27 | 只看该作者
USB库通过这个函数获得用户的数据缓冲区地址,从而可以在数据出过程中把收到的数据拷贝到用户缓冲区,或在数据入过程中把用户缓冲区的数据拷贝到USB发送缓冲区。每个数据出过程可能有若干次DATA_OUT传输,USB库每完成一次这样的传输都会调用一次回调函数CopyRoutine,参数length是本次传输所收到的数据字节数目,CopyRoutine必须返回一个缓冲区指针,这个缓冲区必须能够容纳length字节的数据,CopyRoutine返回到USB库之后,USB库将把收到的数据拷贝到用户指定的缓冲区。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则