Chinaunix首页 | 论坛 | 博客
  • 博客访问: 477318
  • 博文数量: 100
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 955
  • 用 户 组: 普通用户
  • 注册时间: 2014-11-21 09:30
文章分类

全部博文(100)

文章存档

2017年(1)

2016年(16)

2015年(83)

我的朋友

分类: Web开发

2015-04-24 19:08:57

整个USB工作流程可以参考
我的基于STM32的USB学习笔记

USB读卡器的基本原理就是向主机提供SD读写功能,并不需要加入文件系统功能。
USB设备的实现步骤:
1、 初始化系统时钟,设置USB时钟
2、 配置USB中断,选择通道,设置优先级,使能中断
3、 配置GPIO
4、 USB的初始化,对描述符、设备的端点接口等的初始化
5、 FLASH的初始化
点击(此处)折叠或打开
  1. sd_size=(long long)SD_GetSectorCount()*512;        //得到SD卡容量,字节.                     
  2.     Mass_Memory_Size[0]=sd_size%4294967296;            //当SD卡容量超过4G的时候,需要用到两个u32来表示
  3.     Mass_Memory_Size[1]=sd_size>>32;             //容量的高32位
  4.     Mass_Block_Size[0] =512;                        //因为我们在Init里面设置了SD卡的操作字节为512个,所以这里一定是512个字节.
  5.     Mass_Block_Count[0]=sd_size/Mass_Block_Size[0];    //得到扇区数
  6.     LCD_ShowString(60,150,"USB Connecting...");        //提示SD卡已经准备了    
  7.     //USB配置
  8.     USB_Interrupts_Config();
  9.     Set_USBClock();
  10.     USB_Init();
1.  第一步,USB_Interrupts_Config函数配置USB中断。

点击(此处)折叠或打开

  1. //USB中断配置
  2. void USB_Interrupts_Config(void)
  3. {
  4.   
  5.     EXTI->IMR|=1<<18;// 开启线18上的中断
  6.      EXTI->RTSR|=1<<18;//line 18上事件上升降沿触发    
  7.     MY_NVIC_Init(1,0,USB_LP_CAN_RX0_IRQChannel,2);//组2,优先级次之
  8.     MY_NVIC_Init(0,0,USBWakeUp_IRQChannel,2); //组2,优先级最高         
  9. }
USB_LP_CAN_RX0_IRQChannel就是USB低优先级通道的配置宏,在汇编代码STM32F10x.s 中配置了中断向量指向的函数地址:
......
DCD     USB_LP_CAN_RX0_IRQHandler ; USB Low  Priority or CAN RX0
......


配置了USB_LP_CAN_RX0_IRQChannel的优先级,相当于做好了USB中断初始化的工作,当触发中断时就会执行服务函数USB_LP_CAN_RX0_IRQHandler,USB的向量中断服务函数在Stm32f10x_it.c (usb\config)文件中定义:
void USB_LP_CAN_RX0_IRQHandler(void)
{
  USB_Istr();
}

void USBWakeUp_IRQHandler(void)
{}
 这是个空函数,所以唤醒中断不会做任何处理。



2. 设置时钟,Set_USBClock()。

点击(此处)折叠或打开

  1. //配置USB时钟,USBclk=48Mhz
  2. void Set_USBClock(void)
  3. {
  4.      RCC->CFGR&=~(1<<22); //USBclk=PLLclk/1.5=48Mhz    
  5.     RCC->APB1ENR|=1<<23; //USB时钟使能                    
  6. }
3.

点击(此处)折叠或打开

  1. //初始化USB
  2. void USB_Init(void)
  3. {
  4.     pInformation = &Device_Info;
  5.     pInformation->ControlState = 2;
  6.     pProperty = &Device_Property;
  7.     pUser_Standard_Requests = &User_Standard_Requests;
  8.     /* Initialize devices one by one */
  9.     pProperty->Init();
这里有几个全局变量,我们一个个分析.

点击(此处)折叠或打开

  1. /* Includes ------------------------------------------------------------------*/
  2. #include "usb_lib.h"

  3. /* Private typedef -----------------------------------------------------------*/
  4. /* Private define ------------------------------------------------------------*/
  5. /* Private macro -------------------------------------------------------------*/
  6. /* Private variables ---------------------------------------------------------*/
  7. /* The number of current endpoint, it will be used to specify an endpoint */
  8.  u8    EPindex;
  9. /* The number of current device, it is an index to the Device_Table */
  10. /* u8    Device_no; */
  11. /* Points to the DEVICE_INFO structure of current device */
  12. /* The purpose of this register is to speed up the execution */
  13. DEVICE_INFO *pInformation;
  14. /* Points to the DEVICE_PROP structure of current device */
  15. /* The purpose of this register is to speed up the execution */
  16. DEVICE_PROP *pProperty;
  17. /* Temporary save the state of Rx & Tx status. */
  18. /* Whenever the Rx or Tx state is changed, its value is saved */
  19. /* in this variable first and will be set to the EPRB or EPRA */
  20. /* at the end of interrupt process */
  21. u16    SaveState ;
  22. u16 wInterrupt_Mask;
  23. DEVICE_INFO    Device_Info;
  24. USER_STANDARD_REQUESTS *pUser_Standard_Requests;

  25. /* Extern variables ----------------------------------------------------------*/
  26. /* Private function prototypes -----------------------------------------------*/
  27. /* Private functions ---------------------------------------------------------*/

  28. //初始化USB
  29. void USB_Init(void)
  30. {
  31.     pInformation = &Device_Info;
  32.     pInformation->ControlState = 2;
  33.     pProperty = &Device_Property;
  34.     pUser_Standard_Requests = &User_Standard_Requests;
  35.     /* Initialize devices one by one */
  36.     pProperty->Init();
  37. }
pProperty->Init实际上是执行Mass_init函数,读取芯片ID,向主机发送描述符的时候会用到芯片ID。
Device_Info是一个_DEVICE_INFO类型的结构体

点击(此处)折叠或打开

  1. typedef struct _DEVICE_INFO
  2. {
  3.   u8 USBbmRequestType; /* bmRequestType */
  4.   u8 USBbRequest; /* bRequest */
  5.   u16_u8 USBwValues; /* wValue */
  6.   u16_u8 USBwIndexs; /* wIndex */
  7.   u16_u8 USBwLengths; /* wLength */

  8.   u8 ControlState; /* of type CONTROL_STATE */
  9.   u8 Current_Feature;
  10.   u8 Current_Configuration; /* Selected configuration */
  11.   u8 Current_Interface; /* Selected interface of current configuration */
  12.   u8 Current_AlternateSetting;/* Selected Alternate Setting of current
  13.                                      interface*/

  14.   ENDPOINT_INFO Ctrl_Info;
  15. }DEVICE_INFO;
Device_Property 是一个DEVICE_PROP 类型的结构体

点击(此处)折叠或打开

  1. DEVICE_PROP Device_Property =
  2.   {
  3.     MASS_init,
  4.     MASS_Reset,
  5.     MASS_Status_In,
  6.     MASS_Status_Out,
  7.     MASS_Data_Setup,
  8.     MASS_NoData_Setup,
  9.     MASS_Get_Interface_Setting,
  10.     MASS_GetDeviceDescriptor,
  11.     MASS_GetConfigDescriptor,
  12.     MASS_GetStringDescriptor,
  13.     0,
  14.     0x40 /*MAX PACKET SIZE*/
  15.   }
Mass_init内容为

  1. void MASS_init()
  2. {
  3.   /* Update the serial number string descriptor with the data from the unique
  4.   ID*/
  5.   Get_SerialNum();

  6.   pInformation->Current_Configuration = 0;

  7.   /* Connect the device */
  8.   PowerOn();

  9.   /* USB interrupts initialization */
  10.   /* clear pending interrupts */
  11.   _SetISTR(0);
  12.   wInterrupt_Mask = IMR_MSK;
  13.   /* set interrupts mask */
  14.   _SetCNTR(wInterrupt_Mask);

  15.   bDeviceState = UNCONNECTED;
  16. }


_SetISTR关闭中断,寄存器地址为0x4000 5C44,见stm32中文参考手册  21.5.1 USB中断状态寄存器(USB_ISTR)



这里终于看到了和SD卡操作的有关代码。
在main函数里只做了一件事情:获取bDeviceState、Usb_Status_Reg的值,做相应处理(LCD显示,重设bDeviceState)。


先了解USB初始化流程:
第一步其实是USB的枚举过程,这个阶段所有事务都属于控制传输。步骤如下:
1、 主机发setup令牌包(通知设备准备接收数据,包含了地址和端点号)->主机输出数据包(请求描述符,会触发端点0 OUT中断)->握手包ACK
数据传送方向:主>从                                                                     主>从                                                               从>主

2、                  IN令牌包->设备返回数据包(描述符)->主机发ACK
数据传送方向:主>从         从>主                                  主>从


这里注意一点,setup令牌包同样会触发设备的端点0输出中断。设备根据收到的数据包(标准请求),在端点0缓冲区中准备好数据(描述符)(具体放什么数据,需要用户编写代码实现),在下一次收到IN令牌包时自动送到总线上(不需要用户干预)。
  1. 主机检测到USB设备接入,唤醒设备。
  2. 设备被触发唤醒中断和复位中断,设备第一次复位
  3. 建立过程:主机发送0地址SETUP令牌包、标准请求包(指明请求的数据长度64字节,若按照USB协议规范,设备接着应该在缓冲区准备64字节,但实际上设备只需要准备8个字节,超过的部分windows不理会,这是微软的BUG,但是成了潮流),触发设备端点0输出中断,设备收到数据后返回ACK。
  4. 数据过程:主机发送OUT令牌包,设备返回描述符(Setup0_Process ->  Data_Setup0),主机发ACK。
  5. 状态过程:主机发OUT令牌包,发0字节数据包,设备返回ACK。

  6. 主机第二次发复位信号,建立过程:主机发送SETUP包,标准请求包(请求设置地址),设备返回ACK。
  7. 数据过程:无
  8. 状态过程:主机发送IN令牌包,设备返回0字节状态数据包,主机发ACK。

  9. 主机第三次发复位信号,建立过程:主机发送新地址SETUP包,标准请求包(请求返回设备描述符,触发设备端点0输出中断,此时设备要在缓冲区中准备设备描述符),设备返回ACK。
  10. 数据过程:主机发IN令牌包(触发设备端点0输入中断),设备返回设备描述符(包含配置集合长度),主机发ACK
  11. 状态过程:主机发OUT令牌包,主机发0字节数据包(表面自己已收到数据),设备发ACK。
  12. 接下来是N次数据传输,和获取设备描述符(12~13)的过程差不多,设备返回配置集合(配置描述符、接口描述符、端点描述符,通过CopyRoutine函数发送)
以上14个步骤中,其中2~14每次来回(主机启动事务、设备返回请求的数据)都是一次控制传输事务(有可能不对,反正我是这么理解的)。


这里重新讲解一下USB的事务,很多USB资料都没讲明白,或者说我没看明白^_^。USB每做一件“事情”都是一个事务,包括主机枚举设备,发送一次数据,主机接收一次数据,都是以事务为单位完成的。
事务分为4种类型:控制、批量(Bulk)、同步(等时)、中断。
其中枚举过程一定是控制传输事务,并且是通过多次次控制传输事务才完成的。
一次事务通常由两个或者三个包组成,令牌包、数据包(可选)、握手包。
令牌包是必须的,用来启动一次事务。
枚举过程基本都是控制传输。

这篇文章讲得很清楚(圈圈都没讲枚举是控制传输):
USB设备枚举过程分析:






 

阅读(7544) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:USB传输协议学习

给主人留下些什么吧!~~