正点原子STM32 USB读卡器代码分析

发布者:Huayu8888最新更新时间:2024-10-15 来源: cnblogs关键字:正点原子  STM32 手机看文章 扫描二维码
随时随地手机看文章

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 (usbconfig)文件中定义:
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)、同步(等时)、中断。
其中枚举过程一定是控制传输事务,并且是通过多次次控制传输事务才完成的。
一次事务通常由两个或者三个包组成,令牌包、数据包(可选)、握手包。
令牌包是必须的,用来启动一次事务。
枚举过程基本都是控制传输。


关键字:正点原子  STM32 引用地址:正点原子STM32 USB读卡器代码分析

上一篇:stm32+uln2003驱动步进电机程序(28BYJ-48)
下一篇:iar下的stm32启动代码分析

推荐阅读最新更新时间:2024-11-12 13:16

STM32 SPI接口的简单实现
通常SPI通过4个引脚与外部器件相连: ● MISO:主设备输入/从设备输出引脚。该引脚在从模式下发送数据,在主模式下接收数据。 ● MOSI:主设备输出/从设备输入引脚。该引脚在主模式下发送数据,在从模式下接收数据。 ● SCK:串口时钟,作为主设备的输出,从设备的输入 ●NSS:从设备选择。这是一个可选的引脚,用来选择主/从设备。它的功能是用来作为 片选引脚 ,让主设备可以单独地与特定从设备通讯,避免数据线上的冲突。从设备的NSS引脚可以由主设备的一个标准I/O引脚来驱动。一旦被使能(SSOE位),NSS引脚也可以作为输出引脚,并在SPI处于主模式时拉低;此时,所有的SPI设备,如果它们的NSS引脚连接到主设备的NSS引脚
[单片机]
stm32学习笔记之win8系统下,keil4出现黑块的解决方法
前不久,笔者安装keil4启动会出现黑块,如图所示 当时询问了不少技术群都没有找到解决办法,并且还在百度贴吧发贴,最终都无果而终 这是当时发贴地址 http://tieba.baidu.com/p/3176578044 后来重做了个系统,才勉强能使用。直至今天又出现了同样的状况。在此之间笔者发现当keil4出现黑块,win8自带的记事本也会出现未响应状况,于是上网找解决方法,最终网友 oafaq给了我思路 这是他的原文地址 http://blog.sina.com.cn/key9928 。原来我今天更新了QQ五笔输入法,当卸载了QQ五笔之后,所有问题都解决了。这是与keil4不兼容的QQ输入法版本
[单片机]
<font color='red'>stm32</font>学习笔记之win8系统下,keil4出现黑块的解决方法
STM32之戒毒篇
为了方便广大网友,各种网站也应运而生。当网络的建设和发展正进行的如火如荼,喧闹之中,搭配学习这壶美酒的,竟是一瓶名叫资料下载的毒药,更糟糕的是,美酒和毒药已经被灌到了同一个杯子里,浑然一体 ,叫人在畅美中不知不觉走进地狱。简单的设置,方便的软件,FTP给资料的传播和个人资料的交流开了一道大门。从今年年初各大论坛交流的更多是学习心得,到一夜之间,下载成了论坛人气的聚集力量,各大论坛的站长也纷纷拉FTP,开下载,斑竹也不再是要有水平能给大家解答疑问了,只要能提供大量资料,只要能够开FTP,就是座上客。谁家的资料多,谁家就门庭若市。而细细交流学习的栏目很少有人问津。有多少人真正提高了?偶尔有清醒者提出这个问题,也被我要,我要的下载声浪所淹
[单片机]
STM32如何分配原理图IO
在画原理图之前,一般的做法是先把引脚分类好,然后才开始画原理图。 要想根据功能来分配 IO,那就得先知道每个 IO 的功能说明,这个我们可以从官方的数据手册里面找到。在学习的时候,有两个官方资料我们会经常用到,一个是参考手册(英文叫 Referencemanual),另外一个是数据手册(英文叫 Data Sheet)。两者的具体区别见下表。 数据手册主要用于芯片选型和设计原理图时参考,参考手册主要用于在编程的时候查阅。在数据手册中,有关引脚定义的部分在 Pinouts and pin description 这个小节中。数据手册中对引脚定义具体定义见下表。 对上表中引脚定义的解读,见下图。 举例,如果MCU 型号是 S
[单片机]
<font color='red'>STM32</font>如何分配原理图IO
滴答时钟开刀了解STM32库操作
STM32的库函数操作给设计开发人员带来了诸多的便利,开发人员不必十分了解STM32的内部寄存器及硬件机制,只要有C语言基础,即可完成单片机的开发,缩短了开发周期,降低了开发难度,因而备受工程师喜爱。 基于库函数的开发模式,与基于API(ApplicationProgrammingInterface)的软件开发有着异曲同工之处,程序员通过调用 API 函数对应用程序进行开发,而又无需访问源码,或理解内部工作机制的细节,可以减轻编程任务。STM32的基于函数库的开发模式也是一样的道理,因此对于有单片机开发经验的工程师来说,学习STM32,很容易就可以上手。 虽然可以不考虑库函数内部的细节,不考虑如何实现硬件寄存器的配置,但是
[单片机]
stm32学习笔记——按键(扫描法)
目的:利用扫描IO口的方式直接操作按键 配置文件:#include stm32f10x_gpio.h #include stm32f10x_rcc.h 寄存器、结构体定义以及库函数参看流水灯一节 程序代码分析:(代码出自FIRE) Led.c文件再次不在叙述,参看流水灯一节 重点分析key.c文件,其基本结构如下: 1、按键io口初始化,这里有两个按键key1和key2,初始化过程完全相同 void Key1_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure;//定义GPIO初始化结构体
[单片机]
CM3(STM32) 内核复位与系统复位区别及应用
Ⅰ写在前面 某些系统允许复位,但对外设又有特殊要求:某一个IO状态不能因为复位而改变,某一个定时器计数器不能改变等。 例子:我一个A系统通过一个IO控制另一个B系统的电源,而这个IO置高时才开启B系统的电源。 正常工作过程中,B系统只有收到A系统关机命令任务才会进行关机(也就是说不能掉电关机),而A系统在工作过程中有复位的需求。 这个时候如果我使用常规的引脚复位,就会使IO置低,不符合要求,就需要使用到本文说到的内核复位。 Ⅱ关于复位 说到复位,我们都不会陌生,学习时,开发板上基本都有一个复位按键。 复位的种类有很多:上电复位、掉电复位、复位引脚复位、看门狗复位、软件复位等。 上面说的复位按键,也就是对应复位引脚复位;而本文说
[单片机]
UCGUI在STM32平台移植经验(无操作系统)
ucgui 移植的前提是已经具备了LCD驱动函数,已经能够实现点亮LCD屏幕,并实现画点以及获取指定点颜色值的功能。一般的显示屏供应商会提供对应的驱动函数。主要有初始化函数void LCD_Init(),屏幕画点函数 Void LCD_DrawPoint(u16 x,u16 y,u16 color),以及获取指定点颜色值的U16 LCD_ReadPoint(u16 x,u16 y)函数。移植的关键在于把这三个函数与ucgui提供的接口函数匹配。 打开GUILCDDriver中的LCDDummy.c文件,找到int LCD_L0_Init(void)初始化函数,LCD_L0_SetPixelIndex(int x, int y,
[单片机]
UCGUI在<font color='red'>STM32</font>平台移植经验(无操作系统)
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved