整个USB工作流程可以参考
《
我的基于STM32的USB学习笔记》
USB读卡器的基本原理就是向主机提供SD读写功能,并不需要加入文件系统功能。
USB设备的实现步骤:
1、 初始化系统时钟,设置USB时钟
2、 配置USB中断,选择通道,设置优先级,使能中断
3、 配置GPIO
4、 USB的初始化,对描述符、设备的端点接口等的初始化
5、 FLASH的初始化
点击(此处)折叠或打开
-
sd_size=(long long)SD_GetSectorCount()*512; //得到SD卡容量,字节.
-
Mass_Memory_Size[0]=sd_size%4294967296; //当SD卡容量超过4G的时候,需要用到两个u32来表示
-
Mass_Memory_Size[1]=sd_size>>32; //容量的高32位
-
Mass_Block_Size[0] =512; //因为我们在Init里面设置了SD卡的操作字节为512个,所以这里一定是512个字节.
-
Mass_Block_Count[0]=sd_size/Mass_Block_Size[0]; //得到扇区数
-
LCD_ShowString(60,150,"USB Connecting..."); //提示SD卡已经准备了
-
//USB配置
-
USB_Interrupts_Config();
-
Set_USBClock();
-
USB_Init();
1. 第一步,USB_Interrupts_Config函数配置USB中断。
-
//USB中断配置
-
void USB_Interrupts_Config(void)
-
{
-
-
EXTI->IMR|=1<<18;// 开启线18上的中断
-
EXTI->RTSR|=1<<18;//line 18上事件上升降沿触发
-
MY_NVIC_Init(1,0,USB_LP_CAN_RX0_IRQChannel,2);//组2,优先级次之
-
MY_NVIC_Init(0,0,USBWakeUp_IRQChannel,2); //组2,优先级最高
-
}
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()。
-
//配置USB时钟,USBclk=48Mhz
-
void Set_USBClock(void)
-
{
-
RCC->CFGR&=~(1<<22); //USBclk=PLLclk/1.5=48Mhz
-
RCC->APB1ENR|=1<<23; //USB时钟使能
-
}
3.
-
//初始化USB
-
void USB_Init(void)
-
{
-
pInformation = &Device_Info;
-
pInformation->ControlState = 2;
-
pProperty = &Device_Property;
-
pUser_Standard_Requests = &User_Standard_Requests;
-
/* Initialize devices one by one */
-
pProperty->Init();
这里有几个全局变量,我们一个个分析.
-
/* Includes ------------------------------------------------------------------*/
-
#include "usb_lib.h"
-
-
/* Private typedef -----------------------------------------------------------*/
-
/* Private define ------------------------------------------------------------*/
-
/* Private macro -------------------------------------------------------------*/
-
/* Private variables ---------------------------------------------------------*/
-
/* The number of current endpoint, it will be used to specify an endpoint */
-
u8 EPindex;
-
/* The number of current device, it is an index to the Device_Table */
-
/* u8 Device_no; */
-
/* Points to the DEVICE_INFO structure of current device */
-
/* The purpose of this register is to speed up the execution */
-
DEVICE_INFO *pInformation;
-
/* Points to the DEVICE_PROP structure of current device */
-
/* The purpose of this register is to speed up the execution */
-
DEVICE_PROP *pProperty;
-
/* Temporary save the state of Rx & Tx status. */
-
/* Whenever the Rx or Tx state is changed, its value is saved */
-
/* in this variable first and will be set to the EPRB or EPRA */
-
/* at the end of interrupt process */
-
u16 SaveState ;
-
u16 wInterrupt_Mask;
-
DEVICE_INFO Device_Info;
-
USER_STANDARD_REQUESTS *pUser_Standard_Requests;
-
-
/* Extern variables ----------------------------------------------------------*/
-
/* Private function prototypes -----------------------------------------------*/
-
/* Private functions ---------------------------------------------------------*/
-
-
//初始化USB
-
void USB_Init(void)
-
{
-
pInformation = &Device_Info;
-
pInformation->ControlState = 2;
-
pProperty = &Device_Property;
-
pUser_Standard_Requests = &User_Standard_Requests;
-
/* Initialize devices one by one */
-
pProperty->Init();
-
}
pProperty->Init实际上是执行Mass_init函数,读取芯片ID,向主机发送描述符的时候会用到芯片ID。
Device_Info是一个_DEVICE_INFO类型的结构体
-
typedef struct _DEVICE_INFO
-
{
-
u8 USBbmRequestType; /* bmRequestType */
-
u8 USBbRequest; /* bRequest */
-
u16_u8 USBwValues; /* wValue */
-
u16_u8 USBwIndexs; /* wIndex */
-
u16_u8 USBwLengths; /* wLength */
-
-
u8 ControlState; /* of type CONTROL_STATE */
-
u8 Current_Feature;
-
u8 Current_Configuration; /* Selected configuration */
-
u8 Current_Interface; /* Selected interface of current configuration */
-
u8 Current_AlternateSetting;/* Selected Alternate Setting of current
-
interface*/
-
-
ENDPOINT_INFO Ctrl_Info;
-
}DEVICE_INFO;
Device_Property 是一个
DEVICE_PROP 类型的结构体:
-
DEVICE_PROP Device_Property =
-
{
-
MASS_init,
-
MASS_Reset,
-
MASS_Status_In,
-
MASS_Status_Out,
-
MASS_Data_Setup,
-
MASS_NoData_Setup,
-
MASS_Get_Interface_Setting,
-
MASS_GetDeviceDescriptor,
-
MASS_GetConfigDescriptor,
-
MASS_GetStringDescriptor,
-
0,
-
0x40 /*MAX PACKET SIZE*/
-
}
Mass_init内容为
-
void MASS_init()
-
{
-
/* Update the serial number string descriptor with the data from the unique
-
ID*/
-
Get_SerialNum();
-
-
pInformation->Current_Configuration = 0;
-
-
/* Connect the device */
-
PowerOn();
-
-
/* USB interrupts initialization */
-
/* clear pending interrupts */
-
_SetISTR(0);
-
wInterrupt_Mask = IMR_MSK;
-
/* set interrupts mask */
-
_SetCNTR(wInterrupt_Mask);
-
-
bDeviceState = UNCONNECTED;
-
}
_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令牌包时自动送到总线上(不需要用户干预)。
-
主机检测到USB设备接入,唤醒设备。
-
设备被触发唤醒中断和复位中断,设备第一次复位。
-
建立过程:主机发送0地址SETUP令牌包、标准请求包(指明请求的数据长度64字节,若按照USB协议规范,设备接着应该在缓冲区准备64字节,但实际上设备只需要准备8个字节,超过的部分windows不理会,这是微软的BUG,但是成了潮流),触发设备端点0输出中断,设备收到数据后返回ACK。
-
数据过程:主机发送OUT令牌包,设备返回描述符(Setup0_Process -> Data_Setup0),主机发ACK。
-
状态过程:主机发OUT令牌包,发0字节数据包,设备返回ACK。
-
-
主机第二次发复位信号,建立过程:主机发送SETUP包,标准请求包(请求设置地址),设备返回ACK。
-
数据过程:无
-
状态过程:主机发送IN令牌包,设备返回0字节状态数据包,主机发ACK。
-
-
主机第三次发复位信号,建立过程:主机发送新地址SETUP包,标准请求包(请求返回设备描述符,触发设备端点0输出中断,此时设备要在缓冲区中准备设备描述符),设备返回ACK。
-
数据过程:主机发IN令牌包(触发设备端点0输入中断),设备返回设备描述符(包含配置集合长度),主机发ACK
-
状态过程:主机发OUT令牌包,主机发0字节数据包(表面自己已收到数据),设备发ACK。
-
接下来是N次数据传输,和获取设备描述符(12~13)的过程差不多,设备返回配置集合(配置描述符、接口描述符、端点描述符,通过CopyRoutine函数发送)
以上14个步骤中,其中2~14每次来回(主机启动事务、设备返回请求的数据)都是一次控制传输事务(有可能不对,反正我是这么理解的)。
这里重新讲解一下USB的事务,很多USB资料都没讲明白,或者说我没看明白^_^。USB每做一件“事情”都是一个事务,包括主机枚举设备,发送一次数据,主机接收一次数据,都是以事务为单位完成的。
事务分为4种类型:控制、批量(Bulk)、同步(等时)、中断。
其中枚举过程一定是控制传输事务,并且是通过多次次控制传输事务才完成的。
一次事务通常由两个或者三个包组成,令牌包、数据包(可选)、握手包。
令牌包是必须的,用来启动一次事务。
枚举过程基本都是控制传输。
这篇文章讲得很清楚(圈圈都没讲枚举是控制传输):
USB设备枚举过程分析:
阅读(7544) | 评论(0) | 转发(0) |