正点原子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启动代码分析

推荐阅读最新更新时间:2026-03-23 15:35

STM32 USB SD卡读卡器和NAND FLASH模拟U盘
本次工程是要同时实现SD卡读卡器和NAND Flash模拟U盘的功能。结合之前的两个工程,稍稍修改下就可以了。 既然要实现两个盘,当然在usb_prop.c中的Max_Lun变量赋值为1,在USB_User组中同时添加fsmc_nand.c和sdio_sdcard.c这两个文件,在外设库中挺尸添加stm32f10x_sdio.c和stm32f10x_fsmc.c两个文件。 添加完之后,可能会有些许的错误,解决完后,我们就要修改下mass_mal.c文件了,这个文件本次要同时实现SD卡和NAND Flash的相关驱动代码的挂接,代码如下: uint32_t Mass_Memory_Size ; uint32_t Mass_Block
[单片机]
<font color='red'>STM32</font> <font color='red'>USB</font> SD卡<font color='red'>读卡器</font>和NAND FLASH模拟U盘
STM32 USB SD读卡器(SDIO)
这次是做一个SD卡的USB读卡器的功能,我们就在上次NAND Flash模拟出的U盘的工程上修改了,这样的话只要修改一小部分了。 工程的绝大部分不需要修改,只要将fsmc_nand.c文件移除工程,添加上官方的关于SDIO的SD卡的驱动代码文件stm32_eval_sdio_sd.c,我讨厌出现eval,所以将该文件直接改为sdio_sdcard.c。同时,既然我们使用SDIO来驱动SD卡,所以在外设库文件组里要添加stm32_sdio.c文件,否则会出现很多未定错误。 我们接下去修改的就只有mass_mal.c这个文件了。更NAND Flash工程一样,我们改成简洁点: uint32_t Mass_Memory_Si
[单片机]
<font color='red'>STM32</font> <font color='red'>USB</font> SD<font color='red'>读卡器</font>(SDIO)
正点原子STM32学习笔记——通用定时器基本原理
以下描述适用于整个STM32FM10xxx系列。 本文依据《STM32中文参考手册_V10》 STM32 通用定时器简介 STM32 的通用定时器是一个通过可编程预分频器(PSC)驱动的 16 位自动装载计数器(CNT)构成。STM32 的通用定时器可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和 PWM)等。 使用定时器预分频器和 RCC 时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。STM32 的每个通用定时器都是完全独立的,没有互相共享的任何资源。 STM3 的通用 TIMx (TIM2、TIM3、TIM4 和 TIM5)定时器功能包括: 1)16 位向上、向下、向上
[单片机]
<font color='red'>正点原子</font><font color='red'>STM32</font>学习笔记——通用定时器基本原理
stm32正点原子学习笔记(29)独立看门狗实验
iwdg.c 1 #include iwdg.h 2 3 void iwdg_init(uint8_t IWDG_Prescaler,uint16_t Reload) 4 { 5 IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); 6 IWDG_SetPrescaler(IWDG_Prescaler); 7 IWDG_SetReload(Reload); 8 IWDG_ReloadCounter(); 9 IWDG_Enable(); 10 11 } iwdg.h 1 #ifndef __IWDG_H 2
[单片机]
<font color='red'>stm32</font><font color='red'>正点原子</font>学习笔记(29)独立看门狗实验
正点原子STM32F4外部中断实验
最多只能有7个外部中断。 下面这个是GPIO口初始化。 void KEY_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOE, ENABLE);//使能GPIOA,GPIOE时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4; //KEY0 KEY1 KEY2对应引脚 GPIO_InitStructure.GPIO_Mode = GPIO_Mod
[单片机]
<font color='red'>正点原子</font>STM32F4外部中断实验
正点原子STM32F4中断优先级管理
今天讲解,STM32F4开发指南 STM32F4中文参考手册,f4只使用了其中的一部分中断可以找到F4的中文参考手册第10节 有一个表格STM32F405XX和STM32F407XX和STM32F415XX/17XX的向量表 有0-81个可屏蔽中断 如何管理这几十个中断?就是利用NVIC ,首先看一下中断优先级分组,分组就是利用SCB- AIRCR中断配置,设置他的8-10位来设置中断优先级分组。设置了分组之后,就确定了 抢占优先级和相应优先级,每一个中断都有一个IP寄存器,这个IP寄存器的低位是4-7,当我们利用AIRCR设置好分组后他的IP寄存器也就配置好了。 这里有个问题 这么分组有什么意义?抢占优先级和响应
[单片机]
<font color='red'>正点原子</font>STM32F4中断优先级管理
正点原子STM32F4笔记 串口使用
1、初始化 时钟 串口时钟,引脚时钟 RCC- AHB1ENR|=1 0; //使能PORTA口时钟 RCC- APB2ENR|=1 4; //使能串口1时钟 引脚复用初始化 GPIO_Set(GPIOA,PIN9|PIN10,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_50M,GPIO_PUPD_PU);//PA9,PA10,复用功能,上拉输出 GPIO_AF_Set(GPIOA,9,7); //PA9,AF7 GPIO_AF_Set(GPIOA,10,7);//PA10,AF7 波特率设置 //波特率设置 USART1- BRR=mantissa;
[单片机]
<font color='red'>正点原子</font>STM32F4笔记 串口使用
如何用 STM32 FLASH 实现等效 100 万次擦写的 EEPROM 功能?
在单片机开发中, 数据存储 是一个绕不开的话题。EEPROM因其非易失性存储特性,常用于保存配置参数等数据。 然而,EEPROM的擦写次数通常有限,以STM32为例,STM32L0、STM32L4自带的EEPROM一般 10万次 左右,而很多单片机并不内置EEPROM,这时候,利用单片机的FLASH存储器来模拟EEPROM就成为了一个高性价比的解决方案,但,FLASH的擦写次数一般在 1万次 左右,这个我们可以通过ST的官方数据手册看到: 1万次,在很多场景下并不够。 今天,老宇哥跟大家一起探讨,如何用单片机FLA
[嵌入式]
如何用 <font color='red'>STM32</font> FLASH 实现等效 100 万次擦写的 EEPROM 功能?
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

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

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

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