原创 USB Mass Storage学习笔记-STM32+FLASH实现U盘

2009-6-3 09:22 27428 11 65 分类: MCU/ 嵌入式

USB Mass Storage学习笔记-STM32FLASH实现U<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />


 


一、            内容概述


采用STM32内部自带USB控制器外加大页NAND FLASH K<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />9F1G08U0A实现一个128MU盘。


 


   1STM32USB控制器


    STM32F103MCU自带USB从控制器,符合USB规范的通信连接;PC主机和微控制器之间的数据传输是通过共享一专用的数据缓冲区来完成的,该数据缓冲区能被USB外设直接访问。这块专用数据缓冲区的大小由所使用的端点数目和每个端点最大的数据分组大小所决定,每个端点最大可使用512字节缓冲区,最多可用于16个单向或8个双向端点。USB模块同PC主机通信,根据USB规范实现令牌分组的检测,数据发送/接收的处理,和握手分组的处理。整个传输的格式由硬件完成,其中包括CRC的生成和校验。


每个端点都有一个缓冲区描述块,描述该端点使用的缓冲区地址、大小和需要传输的字节数。


USB模块识别出一个有效的功能/端点的令牌分组时,(如果需要传输数据并且端点已配置)随之发生相关的数据传输。USB模块通过一个内部的16位寄存器实现端口与专用缓冲区的数据交换。在所有的数据传输完成后,如果需要,则根据传输的方向,发送或接收适当的握手分组。


在数据传输结束时,USB模块将触发与端点相关的中断,通过读状态寄存器和/或者利用不同的中断来处理。


USB的中断映射单元:将可能产生中断的USB事件映射到三个不同的NVIC请求线上:


1USB低优先级中断(通道20):可由所有USB事件触发(正确传输,USB复位等)。固件在处理中断前应当首先确定中断源。


2USB高优先级中断(通道19):仅能由同步和双缓冲批量传输的正确传输事件触发,目的是保证最大的传输速率。


3USB唤醒中断(通道42):由USB挂起模式的唤醒事件触发。


<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />


138a833f-4310-41c2-b82e-58ecdafe73d1.JPG


1USB设备框图


 


   2、大页NAND K9F1G08


      Nand flash 以页为单位读写数据,而以块为单位擦除数据。根据NAND的容量等级又将NANDFLASH分为大页NAND和小页NANDK9F1G08就是大页NAND,它的页大小为(2K+64)Byte,块大小为(128K4KByteK9F1208U0M为小页NAND,它的页大小为(512+16)Byte,块大小为(16K512Byte


      由于写数据至FLASH时,只能将指定的位变为0,而不能将指定的位变位1。因此在写一个页的数据前,必须先擦除(将所有的位全部置1),否则写数据会失败。


      在编制FLASH的读写程序时,需要传递三个参数,要操作的地址,要操作的数据缓存,要操作的数据长度;在写操作时,还要有擦写和坏块管理。


 


 


3USB Mass storage Bulk Only


Mass Storage类支持两个传输协议:


1Bulk-Only 传输(BOT


2Control/Bulk/Interrupt传输(CBI


Mass Storage类规范定义了两个类规定的请求:Get_Max_LUNMass Storage Reset,所有的Mass Storage类设备都必须支持这两个请求。


Bulk-Only Mass Storage ResetbmRequestType=00100001b and bRequest= 11111111b)用来复位Mass Storage设备及其相关接口。


Get_Max_LUNbmRequestType= 10100001b and bRequest= 11111110b)用来确认设备支持的逻辑单元数。Max LUN的值必须是0~15。注意:LUN是从0开始的。主机不能向不存在的LUN发送CBW


 


支持BOT传输的Mass Storage设备接口描述符要求如下:


接口类代码bInterfaceClass=08h,表示为Mass Storage设备;


接口类子代码bInterfaceSubClass=06h,表示设备支持SCSI Primary Command-2SPC-2);


协议代码bInterfaceProtocol3种:0x000x010x50,前两种需要使用中断传输,最后一种仅使用批量传输(BOT)。


 


支持BOT的设备必须支持最少3endpointControl, Bulk-InBulk-OutUSB2.0的规范定义了控制端点0Bulk-In端点用来从设备向主机传送数据。Bulk-Out端点用来从主机向设备传送数据。


 


Bulk-Only传输(BOT


像控制传输一样,BOT也是由Command阶段,可选的数据阶段和状态阶段组成。所有的command请求都可能有或没有Data阶段。下图说明了BOTCommand传输,Data-InData-Out传输及Status传输。



5ba6dfa4-8072-45aa-b49e-4d4ce3515985.JPG


2Bulk-Only传输示意图


CBW是由31个字节组成的短包。CBW和后续的数据以及CSW都是从新封包开始的。要注意的是所有CBW传输都是little-endian模式。


CBW中,dCBWSignature必须是“43425355h”,表示是CBW封包。dCBWTagCB标签,会通过对应的CSW的标签反馈回来。


CSW中,dCSWSignature必须是“53425355h,表示是CSW包。


 


 


二、            系统的初始化


1、初始化系统时钟,设置USB时钟为48MHz


2、USB中断配制,


选择通道、设置优先级、使能中断。


 


3、USB初始化:连接USBUSB硬件复位、配制CNTR寄存器使能和屏蔽中断、清零中断状态寄存器。


4、FLASH初始化。


 


三、            USB的枚举


USB连接时,进入USB低优先级中断。首先获取中断状态(读ISTR寄存器),在MASS STORAGE中有USB复位中断、USB挂起中断和正确的数据传输中断。


注:在usb_istr.c void USB_Istr(void)函数中。


 


1、USB总线复位:


           设置分组缓冲区描述表起始地址;


                  初始化端点:端点0为控制端点、端点12为批量端点;设置发送和接收状态,设置发送和接收缓冲区地址。


            设置CBW签名, CBW.dSignature=0x43425355;


            初始化BOT状态机。


            注:在usb_prop.cvoid MASS_Reset()函数中。


2USB总线挂起:Xms总线上无数据传输,USB总线挂起,进入低功耗模式。


           注:在usb_pwr.cvoid Suspend()函数中。


    


        3、正确的数据传输中断(usb_int.c   CTR_LP();


           清除中断标志;


           获取端点标识符;


           控制端点处理:读端点寄存器,用来判断是数据输入、输出还是建立包。


           非控制端点处理:下一节介绍。


           详见软件流程图。



点击看大图


3USB枚举软件流程图


四、            非控制端点处理(usb_endp.c > usb.bot.c


 端点2输出中断:usb主机传数据或命令包至mcu


端点1输入中断:mcu传数据或描述符至usb主机。


1、端点2输出中断


(1)    将主机传过来的数据从USB端点缓存区copyMCU内存;


(2)    判断BOT状态,根据BOT状态做出相应的处理:当BOT状态位为0时,CBW包解析,并处理SCSI命令;当BOT状态为1时,表示数据输出,执行WRITE10命令处理。


2、端点1输入中断


判断BOT状态,如果BOT状态为2,表示数据输入,执行READ10命令处理;如果BOT状态为4,则表示数据输入完成,则返回CSW,进行到命令状态。


 


3、BOT状态机软件流程图


1)端点2输出中断流程图(usb_bot.c -> Mass_Storage_Out()


点击看大图



4、端点2输出中断


     


2CBW包解析软件流程图(usb_bot.c -> CBW_Decode()


 


                     图 5CBW包解析


点击看大图 


3READ10命令软件流程图(usb_scsi.c -> SCSI_Read10_Cmd(lun , LBA , BlockNbr)



点击看大图


6READ10命令


 


4WRITE10命令软件流程图(usb_scsi.c -> SCSI_Write10_Cmd(lun , LBA , BlockNbr)



点击看大图


7WRITE10命令


 


5)端点1输入中断流程图(usb_bot.c -> Mass_Storage_In()



点击看大图


8、端点1输入中断


4USBMemory的操作


   MASS STORAGE中,USBMEMORY的操作是以扇区(在FAT中,一个扇区为512字节)为单位的,而USB端点的最大包长为64字节,因此要发送或接收的数据会先放到内存,假设是CPU向端点1写数据,则首先从FLASHSD卡中读取一扇区数据,再按最大包长分8次向USB端点发送。如果是端点2输出数据,则CPU将收到的数据先放至内存,并累加,当是512字节的整数倍时,再将数据写入FLASHSD,软件操作流程如下:


777035b3-9392-4cfe-be34-fa7f7bd0e047.JPG



9READ_MEMORY流程图


     在对FLASHSD卡的读写操作中,该函数需要三个入口参数,第一个是逻辑单元号,用来告诉CPU主机是对哪个磁盘操作(假设有多个磁盘);第二个是逻辑块地址,这里是指第几个扇区,而存储器的地址为字节地址,所以这个地址需要转换;第三个为数据长度,可以是多少个扇区,也可以是多少个字节,这个可以在CBW包中获得。


当数据发送完后,BOT的状态值置为4,进入到状态阶段。


五、            FLASH读写


USBFLASH的操用是任意的,以扇区为单位,由于不是按连续地址写数据,所以FLASH的写函数中要有擦写管理。这里我是直接移植圈圈的FLASH读写函数,所以这里引用他的说明:


    (以下文字出自圈圈的BLOG


    由于NAND FLASH擦除时,只能按按块擦除,因此在写扇区时,首先要擦除一个块。在擦除块前,必须将块内其他数据复制出来,由于一个块比较大(128KB),无法在MCU内开辟如此大的缓冲区。只好借助该NAND FLASH内的页复制命令,将原来的块暂时复制到一个交换用的交换块中。但是如果仅用一个块作为交换的话,它就会被频繁擦写,因而寿命会大大降低。所以在该系统中,保留了10个块用来作为交换区,轮流使用。另外对扇区的连续写进行了优化,当连续写扇区时,就不必每次重复上面的操作,只有当地址跨块时,才需要重新擦除。连续写扇区的实现原理如下:当检测到扇区地址跨块时,就把原来的整块数据复制到交换块中,然后将该块内当前所写地址的前面部分页面复制到原来的块中,接着就从交换块中取出当前扇区地址所在页(使用页复制-随机写入命令),再把一个扇区的数据写入,并接着写入其他扇区,当扇区地址跨页时,就把该页写入到原来的块中,直到扇区被写完(当然如果写的过程中跨块了,还需要重复前面的块擦除以及复制过程)。接着把交换块中剩余的页再复制回原来的块中,这样一个连续写过程就完成了。


 


    因为NAND FLASH在生产和使用过程中,会产生坏块,所以必须增加坏块管理。在该系统中,保留了50个块用做坏块管理。当上述的擦、写过程中,如果发现坏块,那么就把该块的地址重新影射到一个保留的块中。以后每次对该坏块地址操作时,都被重新定位到新的块地址。使用一个二维数组来保存该影射关系。二维数组的前半部分为坏块地址,后半部分为重新影射过后的块地址。每当发现坏块时,就需要重建这张表(主要是增加新的影射并排序,方便地址重新影射的二分查表法),并将其三份一样的写入到专门为此而保留的三个块中。之所以使用三个备份保存,是考虑到这些数据的重要性,因为一旦这个影射关系被破坏,后果将会是灾难性的。在保存这个表格时,做了特殊处理,首先标志他们准备擦除,然后才依次擦除并写入数据,这样即使在操作过程中突然断电,也至少有两个块的备份数据是可以用的,在系统开机初始化时,可以将这些数据恢复。数据使用了特殊标志(0x0055AAFF)以及累加和校验来判断是否有效。对于新出厂的FALSH,在加载坏块表时,就会校验失败,程序就会假设没有坏块并初始化该数组后保存。每个备用块还由另外一个数组用来标志其状态(未用、已损坏、已用等)。当需要使用新的备用块时,就从该数组中查找未用的,并标志为已用。如果备用块本身是坏的,那么就标志它已损坏。该数组与坏块重影射表一并保存。此外还保存了当前坏块的数量。


 


    地址的重新影射过程:当对一个地址进行读写操作时,首先要对其进行重影射。首先判断是否有坏块,当坏块数量为0时,就直接返回原来的地址即可。当坏块数量不为0时,先判断最后一次访问和本次访问的地址是否属于同一页,如果属于,那么就直接影射到上一次影射过的块地址。如果不属于,那么就需要去查坏块影射表了。如果只有一个坏块,只要直接比较即可,不用查表。如果坏块数量大于2,那么就需要查表。由于表中地址是按从小到大的顺序排列的,所以可以先和第一个和最后一个判断,如果不在该范围内,那么也不用重新影射,返回原来的地址即可。如果在该范围内,就使用二


分查表法查表,搜索它是否在坏块表中。如果是的话,就重新影射地址,并将这个地址保存,以备下一次重影射时地址未跨块直接使用。最大支持50个坏块,在最坏的情况下,该二分查表法需要判断6次。


 


 

                                 


六、          USB文件说明
    1、USB固件库文件
    usb_conf.h      USB库文件配制;
    usb_type.h      USB库文件类型声明,使USB库文件具有独立性;
    usb_def.h       USB库文件公用的宏定义;
    usb_regs.h      USB控制器寄存器描述;
    usb_regs.c      USB控制器寄存器底层操作函数;
    usb_init.c    USB控制器初始化;
    usb_int.c       USB高优先级中断和低优先级中断处理函数,在本例中没有用到高优先级中级,所以去掉了;
    usb_mem.c       这个函数用于将USB端点的数据传送给主机和主机的数据传送到USB端点;
    usb_croe.c    USB2.0协议处理;


                  以上文件具有很强的独立性,除特殊情况,不需要用户修改,直接调用内部的函数即可.


        2、USB Mass Storage Bulk Only实现


           以下文件也是出自STM32官方,但要根据实际的应用作修改.


            usb_pwr.c         USB控制器的电源管理函数;
    usb_istr.c        USB低优先级中断入;
    usb_endp.c        非控制端点处理(大容量数据存储输入和输出函数);
    usb_prop.c        Mass Storage相关属性:mass初始化、复位等等;
    usb_bot.c         BOT状态机,CBW解析和调用SCSI处理(批量数据输入输出的状态转换通过BOT状态机实现),
                               这个程序负责接收主机的CBW包,并解析,调用SCSI命令处理函数,返回CSW;
    usb_scsi.c        SCSI命令处理;
    usb_desc.c        USB描述符;


 


七、结语


       第一次用32位的ARM单片机,第一次玩USB,能够花两个多星期的时间实现这个U盘,心情还是蛮激动的,当然我参考了很多网友的和官方的代码;在英蓓特的STM32开发板中就有MASS STORAGE的例程,是对SD进行读写,对NAND不能操作,每次在PC端点NAND磁盘时,都提示要格式化磁盘,但总是格式化不成功。后来发现在写NAND的程序中,没有擦写管理,导致数据每次写入都失败,后来我移植了圈圈的NAND擦写管理函数,换了大页的NAND FLASH,终于调试成功。


在调试的过程中,了解了STM32USB控制器,了解了USB MASS STORAGE 批量传输协议,学会使用NAND FLASH,现将自已两个多星期来的学习成果进行总结,希望能与大家交流。


在这两个多星期来,得到了很多网友的支持,特别要感谢圈圈,帮助我解答了很多疑难问题,纠正了我很多错误的概念,使我能顺利的理清思路;还要感谢21ICBBS的网友们,还有香水城,给我解答了很多关于STM32 的技术问题,还有很多不知名的网友,感谢你们无私贡献的资料。


 


由于初学,以上难免会有不对之处,还请大侠们批评指正。


 


                                                  


             


  Liu_xf


Eeliu88@gmail.com


                                                     2009-6-2


 


PDF文档和源码下载https://static.assets-stash.eet-china.com/album/old-resources/2009/6/3/9a5a45dc-5c4b-4307-80dd-1018dc2e0d9b.rar


 


 

文章评论54条评论)

登录后参与讨论

chenbingjy_241491525 2015-7-27 16:33

编译通不过。

用户377235 2014-6-8 10:16

很好啊

用户377235 2013-12-5 11:28

楼主你好,我是在阿莫论坛上移植的程序,用在k9f2G上,可以读出容量,可以格式化,可以在电脑上写入文件。但是每次写入文件,再连接USB的时候都提示要格式化,这是哪里出问题了?我的FLASH里面还没有文件系统

用户202640 2012-5-23 17:05

博主您好!不知您的NAND FLASH跟STM32是如何连接的,不知是不是采用FSMC接口。另外,想请教一下FSMC接口是否能连接两片NAND,就像SSD硬盘一样。谢谢!

用户377235 2011-12-22 14:30

博主你好! 我想写一个stm32驱动NAND 的程序,带有坏块管理和负载均衡的,你能把STM32+FLASH实现U盘的例程的发给我吗?我的邮箱是quanhaidong1@163.com

用户185393 2011-5-18 16:50

顶,下载研究,正在做。多谢

用户1278632 2011-4-26 20:25

你是用的3.24以上版本的mdk吧 用3.24就不会有这个问题,以上版本的要修改一下stm32f103_it.h和stm32f103_it.c

用户1005696 2011-4-26 09:04

怎么编译后提示: compiling hw_config.c... .\common\inc\stm32f10x_it.h(36): error: #1268-D: declaration aliased to undefined entity "SysTickHandler" .\common\inc\stm32f10x_it.h(35): error: #1268-D: declaration aliased to undefined entity "PendSVC" .\common\inc\stm32f10x_it.h(34): error: #1268-D: declaration aliased to undefined entity "SVCHandler" .\common\inc\stm32f10x_it.h(33): error: #1268-D: declaration aliased to undefined entity "DebugMonitor" .\common\inc\stm32f10x_it.h(32): error: #1268-D: declaration aliased to undefined entity "UsageFaultException" .\common\inc\stm32f10x_it.h(31): error: #1268-D: declaration aliased to undefined entity "BusFaultException" .\common\inc\stm32f10x_it.h(30): error: #1268-D: declaration aliased to undefined entity "MemManageException" .\common\inc\stm32f10x_it.h(29): error: #1268-D: declaration aliased to undefined entity "HardFaultException" .\common\inc\stm32f10x_it.h(28): error: #1268-D: declaration aliased to undefined entity "NMIException"

zoozo 2011-3-18 09:49

已经移植到我的k9f2g08上 ,usb功能好用,不过格式化还没成功。

用户244424 2011-2-17 16:11

此贴很火
相关推荐阅读
用户1278632 2011-09-15 12:49
解决FPGA配置成功,但不能初始化运行的BUG
摘要:    遇到两次FPGA配置完成,却不能正常运行的问题,一次是ALTERA的A1C3,另一次是XILINX的XC3S700A。两次都是DONE信号的问题。问题虽不大,但却很折腾人,今天在这里作下...
用户1278632 2011-09-15 12:41
Code Edit的神器UltraEdit
摘要:      我不是一个专职的程序员,但经常会要写一些单片机底层的code和hdl code,起初用UE,是因为查找和批量修改很方便,而且不会额外的生成一些“垃圾”文件;现在用UE三年多了,一直都...
用户1278632 2010-12-29 09:29
ISE与EDK联合设计报错 ERROR:NgdBuild:604 logical block
做一个很简单的测试在ISE的SCH里调用EDK的symbol,EDK的功能也很简单,就是CPU通过串品打印一串字符首先,我建一个ISE工程再建一source  sch类型,并设置为顶层,取名为top....
用户1278632 2010-12-22 14:49
EDK12.2中 mch_emc IP的时序问题
mch_emc IP可以将PLB总线时序转为inter 8080时序 下面是我用chipscope抓到的波形Mem_DQ_O_In :  数据输入 Mem_OEN:      读信号  Mem_A: ...
用户1278632 2010-12-22 14:29
xilinx FPGA的配制与应用程序引导-范例
两个Xilinx FPGA应用程序引导的范例1、run_in_flash是直接在NOR FLASH里运行程序2、spi_flash_boot是将BIT和bootloader和APP全部固化到SPI F...
用户1278632 2010-12-19 13:48
EDK下sram IP的使用
EDK软件的memory and memory controller中有一个xps multi-channel external memory controller(sram/flash)的IP,用来...
我要评论
54
11
关闭 站长推荐上一条 /2 下一条