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 (usbconfig)文件中定义:
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
数据传送方向:主>从 从>主 主>从
主机检测到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)、同步(等时)、中断。
其中枚举过程一定是控制传输事务,并且是通过多次次控制传输事务才完成的。
一次事务通常由两个或者三个包组成,令牌包、数据包(可选)、握手包。
令牌包是必须的,用来启动一次事务。
枚举过程基本都是控制传输。
上一篇:stm32+uln2003驱动步进电机程序(28BYJ-48)
下一篇:iar下的stm32启动代码分析
推荐阅读最新更新时间:2024-11-12 13:16
设计资源 培训 开发板 精华推荐
- LTC3838EUHF-1 4.5V 至 14V 输入、3.3V/25A 输出、2MHz、RSENSE、降压转换器的典型应用电路
- MIC2025-1YM单通道配电开关MM8典型应用
- C3024478_CH376T芯片方案验证板
- LT1506IR-3.3 双路输出 SEPIC 转换器的典型应用电路
- LDK130M12R 1.2V、300 mA 低静态电流、极低噪声 LDO 的典型应用固定版本电路
- LT6656BIS6-1.25、1.25V 电压基准作为微功率稳压器的典型应用
- 使用 DC-DC 转换器配置(来自 VDCDC 的 PAVDD) EFR32FG12 SoC 应用处理器的典型应用电路
- LTC3126IUFD 5V、2A 电源的典型应用电路,由墙上适配器和铅酸备用电池供电
- 【51单片机】VFD时钟控制板
- 应用示例 - 15 个 STM32F101xx 和 STM32F103xx 内核和系统外设