你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】基于STM32F103C8的 USB 外部flash虚拟U盘

[复制链接]
STMCU小助手 发布时间:2022-3-16 11:16
学习stm32已经很长时间了,但是一直没有过多的学习stm32的USB部分,因为实际工作还是用的比较少。说起USB那就有的说了,因为USB的功能很强大,这里主要重点记录一下STM32的USB部分,这个官方给的有专门USB库,笔者目前使用的是Cotex-M3内核的STM32F103系列,实验的芯片为STM32F103C8,这个是目前市场上性价比非常高的芯片,也是用的非常多的芯片。

USB基础知识
USB按接口类型分

控制器/主机(controller/host)

设备(peripheral)

OTG(on-the-go),通过id线确定作为主机还是作为设备

按照USB速度分

低速(low speed)

全速(full speed)

高速(high speed)

USB接口一般是4根线,VCC GND DM(D-) DP(D+)

低速设备:在DM线上接入上拉

全速设备:在DP线上接入上拉

高速设备:在DP线上接入上拉,在主机对设备进行复位后进一步的确认


关于描述符
设备描述符(device description)

配置描述符(config description)

接口描述符 (interface description)

端点描述符 (endpoint description)

(HQG%R@V4W]{7LOY8DZE{L9.png

设备的“身份”信息存储在描述符中。每个USB设备中都有如下描述符。需要注意的是一个USB设备只能有一个设备描述符,一个设备描述符可以有多个配置描述符,一个配置描述符可以有多个接口描述符,一个接口描述符可以包含多个端点。

关于传输

1.在USB的通讯中,有传输(transfer),事务(Transaction),包(packet)三级。包是最基础的传输单元,与TCP/IP协议中的MAC层协议作用相同。
2.在一次传输中,由多次事务组成,每次的事务又由多个包组成
3.与众多协议相同,较高级别的协议的报文是基于/内嵌在低级协议的报文当中的,在USB中也不例外,例如,包中预留了DATA位,其目的就是填写报文
STM32F103芯片自带了有USB模块,可以用来做从设备,不能用作主机HOST。这里使用USB的目的是讲USB作为一个大容量,这个可以基于官方USB Mass_Storage例程来移植。这里就不过多的介绍里面复杂的通信原理了,要彻彻底底搞明白并灵活运用取来还是有很大难度的,因为内容太多了。这里仅仅介绍下移植过程和描述符相关的重点内容。

准备工作
第一步

确保自己的KEIL开发环境已经完善。

第二步
下载好了两份ST官方的库文件(我是基于标准库开发的),一个是标准外设库文件,一个是USB库文件。如下图:

AZ7H5GQWT4J%{N{6){9E]{3.png

第三步
建立一个带有存储介质驱动的STM32基础工程,存储介质常见有SD卡、外部FLASH芯片、内部的FLASH空间。的我是基于一个外部flash的工程去实现的,芯片具体型号是W25Q64,64Mbit的空间,换成字节就是8MByte。驱动部分如下:

C文件
  1. #include "fy_w25qxx.h"

  2. u16 W25QXX_TYPE=0;//W25Q64

  3. //4Kbytes为一个Sector,16个扇区为1个Block,容量为16M字节,共有128个Block,4096个Sector
  4. //初始化SPI FLASH的IO口
  5. void W25QXX_Configuration(void)
  6. {        
  7.         SPI2_SetSpeed(SPI_BaudRatePrescaler_2);//设置为18M时钟,高速模式
  8.         W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID.  
  9. }         

  10. //读取W25QXX的状态寄存器
  11. //BIT7  6   5   4   3   2   1   0
  12. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY
  13. //SPR:默认0,状态寄存器保护位,配合WP使用
  14. //TB,BP2,BP1,BP0:FLASH区域写保护设置
  15. //WEL:写使能锁定
  16. //BUSY:忙标记位(1,忙;0,空闲)
  17. //默认:0x00
  18. u8 W25QXX_ReadSR(void)   
  19. {  
  20.         u8 byte=0;   
  21.         W25QXX_CS_L();                            //使能器件   
  22.         SPI2_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令   
  23.         byte=SPI2_ReadWriteByte(0Xff);          //读取一个字节  
  24.         W25QXX_CS_H();                            //取消片选     
  25.         return byte;   
  26. }
  27. //写W25QXX状态寄存器
  28. //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
  29. void W25QXX_Write_SR(u8 sr)   
  30. {   
  31.         W25QXX_CS_L();                            //使能器件   
  32.         SPI2_ReadWriteByte(W25X_WriteStatusReg);//发送写取状态寄存器命令   
  33.         SPI2_ReadWriteByte(sr);                       //写入一个字节  
  34.         W25QXX_CS_H();                            //取消片选                  
  35. }   
  36. //W25QXX写使能        
  37. //将WEL置位   
  38. void W25QXX_Write_Enable(void)   
  39. {
  40.         W25QXX_CS_L();                                  //使能器件   
  41.     SPI2_ReadWriteByte(W25X_WriteEnable);         //发送写使能  
  42.         W25QXX_CS_H();                                   //取消片选                  
  43. }
  44. //W25QXX写禁止        
  45. //将WEL清零  
  46. void W25QXX_Write_Disable(void)   
  47. {  
  48.         W25QXX_CS_L();                            //使能器件   
  49.     SPI2_ReadWriteByte(W25X_WriteDisable);  //发送写禁止指令   
  50.         W25QXX_CS_H();                            //取消片选                  
  51. }                 

  52. //读取芯片ID
  53. u16 W25QXX_ReadID(void)
  54. {
  55.         u16 Temp = 0;         
  56.         W25QXX_CS_L();                                    
  57.         SPI2_ReadWriteByte(0x90);//发送读取ID命令            
  58.         SPI2_ReadWriteByte(0x00);            
  59.         SPI2_ReadWriteByte(0x00);            
  60.         SPI2_ReadWriteByte(0x00);                                    
  61.         Temp|=SPI2_ReadWriteByte(0xFF)<<8;  
  62.         Temp|=SPI2_ReadWriteByte(0xFF);         
  63.         W25QXX_CS_H();                                    
  64.         return Temp;
  65. }                       

  66. //在指定地址开始读取指定长度的数据
  67. //pBuffer:数据存储区,ReadAddr:开始读取的地址(24bit),NumByteToRead:要读取的字节数(最大65535)
  68. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   
  69. {
  70.          u16 i;                                                                                       
  71.         W25QXX_CS_L();                                    //使能器件   
  72.         SPI2_ReadWriteByte(W25X_ReadData);                 //发送读取命令   
  73.         SPI2_ReadWriteByte((u8)((ReadAddr)>>16));          //发送24bit地址   
  74.         SPI2_ReadWriteByte((u8)((ReadAddr)>>8));   
  75.         SPI2_ReadWriteByte((u8)ReadAddr);   
  76.         for(i=0;i<NumByteToRead;i++)
  77.         {
  78.         pBuffer=SPI2_ReadWriteByte(0XFF);           //循环读数  
  79.   }
  80.         W25QXX_CS_H();                                                   
  81. }

  82. //SPI在一页(0~65535)内写入少于256个字节的数据
  83. //在指定地址开始写入最大256字节的数据
  84. //pBuffer:数据存储区,WriteAddr:开始写入的地址(24bit),NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!         
  85. void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
  86. {
  87.          u16 i;  
  88.         W25QXX_Write_Enable();                          //SET WEL
  89.         W25QXX_CS_L();                                    //使能器件   
  90.         SPI2_ReadWriteByte(W25X_PageProgram);              //发送写页命令   
  91.         SPI2_ReadWriteByte((u8)((WriteAddr)>>16));         //发送24bit地址   
  92.         SPI2_ReadWriteByte((u8)((WriteAddr)>>8));   
  93.         SPI2_ReadWriteByte((u8)WriteAddr);   
  94.         for(i=0;i<NumByteToWrite;i++)SPI2_ReadWriteByte(pBuffer);//循环写数  
  95.         W25QXX_CS_H();                                    //取消片选
  96.         W25QXX_Wait_Busy();                                                           //等待写入结束
  97. }
  98. //无检验写SPI FLASH
  99. //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
  100. //具有自动换页功能
  101. //在指定地址开始写入指定长度的数据,但是要确保地址不越界!
  102. //pBuffer:数据存储区
  103. //WriteAddr:开始写入的地址(24bit)
  104. //NumByteToWrite:要写入的字节数(最大65535)
  105. //CHECK OK
  106. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
  107. {                                          
  108.         u16 pageremain;           
  109.         pageremain=256-WriteAddr%256; //单页剩余的字节数                             
  110.         if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节
  111.         while(1)
  112.         {           
  113.                 W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
  114.                 if(NumByteToWrite==pageremain)break;//写入结束了
  115.                  else //NumByteToWrite>pageremain
  116.                 {
  117.                         pBuffer+=pageremain;
  118.                         WriteAddr+=pageremain;        

  119.                         NumByteToWrite-=pageremain;                          //减去已经写入了的字节数
  120.                         if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
  121.                         else pageremain=NumByteToWrite;           //不够256个字节了
  122.                 }
  123.         };            
  124. }
  125. //写SPI FLASH  
  126. //在指定地址开始写入指定长度的数据
  127. //该函数带擦除操作!
  128. //pBuffer:数据存储区
  129. //WriteAddr:开始写入的地址(24bit)                                                
  130. //NumByteToWrite:要写入的字节数(最大65535)   
  131. u8 W25QXX_BUFFER[4096];                 
  132. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
  133. {
  134.         u32 secpos;
  135.         u16 secoff;
  136.         u16 secremain;           
  137.          u16 i;   
  138.         u8 *W25QXX_BUF;         
  139.         
  140.            W25QXX_BUF=W25QXX_BUFFER;
  141. //        
  142. //    W25QXX_BUF = (u8 *)_mem.Alloc(4096);//申请一个扇区大小的内存
  143. //    if(W25QXX_BUF==NULL)        return;
  144.         
  145.          secpos=WriteAddr/4096;//扇区地址  
  146.         secoff=WriteAddr%4096;//在扇区内的偏移
  147.         secremain=4096-secoff;//扇区剩余空间大小  

  148.          //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
  149.          if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节
  150.         while(1)
  151.         {        
  152.                 W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容
  153.                 for(i=0;i<secremain;i++)//校验数据
  154.                 {
  155.                         if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除            
  156.                 }
  157.                 if(i<secremain)//需要擦除
  158.                 {
  159.                         W25QXX_Erase_Sector(secpos);                //擦除这个扇区
  160.                         for(i=0;i<secremain;i++)                           //复制
  161.                         {
  162.                                 W25QXX_BUF[i+secoff]=pBuffer;         
  163.                         }
  164.                         W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  

  165.                 }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.                                    
  166.                 if(NumByteToWrite==secremain)break;//写入结束了
  167.                 else//写入未结束
  168.                 {
  169.                         secpos++;//扇区地址增1
  170.                         secoff=0;//偏移位置为0         

  171.                            pBuffer+=secremain;                                  //指针偏移
  172.                         WriteAddr+=secremain;                                //写地址偏移           
  173.                            NumByteToWrite-=secremain;                        //字节数递减
  174.                         if(NumByteToWrite>4096)secremain=4096;//下一个扇区还是写不完
  175.                         else secremain=NumByteToWrite;                //下一个扇区可以写完了
  176.                 }         
  177.         }
  178. //        _mem.Free(W25QXX_BUF);
  179. }
  180. //擦除整个芯片                  
  181. //等待时间超长...
  182. void W25QXX_Erase_Chip(void)   
  183. {                                   
  184.     W25QXX_Write_Enable();                                  //SET WEL
  185.     W25QXX_Wait_Busy();   
  186.           W25QXX_CS_L();                                    //使能器件   
  187.     SPI2_ReadWriteByte(W25X_ChipErase);                //发送片擦除命令  
  188.         W25QXX_CS_H();                                    //取消片选                  
  189.         W25QXX_Wait_Busy();                                                      //等待芯片擦除结束
  190. }   
  191. //擦除一个扇区
  192. //Dst_Addr:扇区地址 根据实际容量设置
  193. //擦除一个山区的最少时间:150ms
  194. void W25QXX_Erase_Sector(u32 Dst_Addr)   
  195. {  
  196.         //监视falsh擦除情况,测试用   
  197. //         printf("fe:%x\r\n",Dst_Addr);         
  198.          Dst_Addr*=4096;
  199.     W25QXX_Write_Enable();                          //SET WEL         
  200.     W25QXX_Wait_Busy();   
  201.           W25QXX_CS_L();                                    //使能器件   
  202.     SPI2_ReadWriteByte(W25X_SectorErase);              //发送扇区擦除指令
  203.     SPI2_ReadWriteByte((u8)((Dst_Addr)>>16));          //发送24bit地址   
  204.     SPI2_ReadWriteByte((u8)((Dst_Addr)>>8));   
  205.     SPI2_ReadWriteByte((u8)Dst_Addr);  
  206.         W25QXX_CS_H();                                    //取消片选                  
  207.     W25QXX_Wait_Busy();                                                      //等待擦除完成
  208. }  
  209. //等待空闲
  210. void W25QXX_Wait_Busy(void)   
  211. {   
  212.         while((W25QXX_ReadSR()&0x01)==0x01);                  // 等待BUSY位清空
  213. }  
  214. //进入掉电模式
  215. void W25QXX_PowerDown(void)   
  216. {
  217.           W25QXX_CS_L();                                            //使能器件   
  218.     SPI2_ReadWriteByte(W25X_PowerDown);        //发送掉电命令  
  219.         W25QXX_CS_H();                                    //取消片选                  
  220.     Delay_us(3);                               //等待TPD  
  221. }   
  222. //唤醒
  223. void W25QXX_WAKEUP(void)   
  224. {  
  225.           W25QXX_CS_L();                                    //使能器件   
  226.     SPI2_ReadWriteByte(W25X_ReleasePowerDown);        //  send W25X_PowerDown command 0xAB   
  227.         W25QXX_CS_H();                                    //取消片选                  
  228.     Delay_us(3);                                    //等待TRES1
  229. }   
复制代码

H文件

  1. #ifndef __FY_W25QXX_H
  2. #define __FY_W25QXX_H                           

  3. #include "fy_includes.h"

  4. //W25QXX对应唯一识别ID
  5. #define W25Q80         0XEF13
  6. #define W25Q16         0XEF14
  7. #define W25Q32         0XEF15
  8. #define W25Q64         0XEF16
  9. #define W25Q128        0XEF17

  10. //指令表
  11. #define W25X_WriteEnable                0x06
  12. #define W25X_WriteDisable                0x04
  13. #define W25X_ReadStatusReg                0x05
  14. #define W25X_WriteStatusReg                0x01
  15. #define W25X_ReadData                        0x03
  16. #define W25X_FastReadData                0x0B
  17. #define W25X_FastReadDual                0x3B
  18. #define W25X_PageProgram                0x02
  19. #define W25X_BlockErase                        0xD8
  20. #define W25X_SectorErase                0x20
  21. #define W25X_ChipErase                        0xC7
  22. #define W25X_PowerDown                        0xB9
  23. #define W25X_ReleasePowerDown        0xAB
  24. #define W25X_DeviceID                        0xAB
  25. #define W25X_ManufactDeviceID        0x90
  26. #define W25X_JedecDeviceID                0x9F

  27. void W25QXX_Configuration(void);
  28. u16  W25QXX_ReadID(void);                              //读取FLASH ID
  29. u8         W25QXX_ReadSR(void);                        //读取状态寄存器
  30. void W25QXX_Write_SR(u8 sr);                          //写状态寄存器
  31. void W25QXX_Write_Enable(void);                  //写使能
  32. void W25QXX_Write_Disable(void);                //写保护
  33. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);
  34. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //读取flash
  35. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写入flash
  36. void W25QXX_Erase_Chip(void);                      //整片擦除
  37. void W25QXX_Erase_Sector(u32 Dst_Addr);        //扇区擦除
  38. void W25QXX_Wait_Busy(void);                   //等待空闲
  39. void W25QXX_PowerDown(void);                //进入掉电模式
  40. void W25QXX_WAKEUP(void);                                //唤醒

  41. extern u16 W25QXX_TYPE;                                        //定义W25QXX芯片型号                  

  42. #endif
复制代码

基础工程如下:

CIG]SRYALJ}8)5NQ[MAA~BW.png

第四步:USB代码移植
拷贝USB底层库到工程根目录,新建一个目录命名成USB,拷贝Mass_Storage例程下的src和inc目录下的所以文件到USB。

6Q]9Q9812B]OXMA8GNV[PCC.png

拷贝过来是有37个文件,这个时候需要删除一些不必要的文件,因为官方的例程里面有nand,这里的话先删除以下文件

VN1JDR_I@JO271YJKNV1(HE.png

4.打开工程把分组和文件及包含路径添加进去,这里就不细说了,结果如下

X$DZH7HHYEC}266_`72~4EH.png

打开usb下面的main和it 把重要的代码先拷贝出来

OA]E5J8OQ0[B{UJV@KXPUG2.png

把main和stm32_it里面的内容拷贝到自己的main里面进行下封装去掉不要的保留剩下的,然后就删除it和main三个文件。

@G5L9U9`9]${JVP}AD@KN0E.png

然后编译下,这个时候会出现一堆报错,需要慢慢一步步搞定,编译前注意路径包含,

[~3UDXP_99@A_@~XV14BNPB.png

定位到第一个错误进去

5L28CK8A4)MY@OJR7KK~KOY.png

这个定义的是官方的开发板 我们直接修改成自己的公共文件就可以,并且注释掉开发板定义

TR]6[QMF5`A7X$MYZIL])9I.png

公共文件如下,并且添加usb部分的头文件

Z7Z6YX`QZR}6~KCC_GDDLD8.png

继续编译,定位第一个错误,这部分主要修改hw_config文件

_9RUE5O1M][P)IRX]S2S_$D.png

删除Set_System函数

N~R76S188E[X(N6NID1WQ$V.png

删除Led_Config函数

~HA$Z23~@)XV2TEQ8CUTS.png

注释LED相关的内容

_FKQ$_}1}S8W%PG(2VT2]C7.png

删除USB_Disconnect_Config

4K@_T@@XVZ@P]EEH37}QAJX.png

接下来主要修改mass_mal部分,我这里是只有一个flash,所以我修改成如下:

2_1P4[7IG68PG%9TS@41BI4.png

头文件

(V0@{X{W8~L[2Z[Y7)VNU.png

下面修改memory文件,主要修改变量定义,C文件和H文件分别如下

T]Y~~F2{V(H`V{$X(}FMNNJ.png

接下来是usb_scsi部分的变量定义

R$Z1OIMFO6JWKJE~XX[{2$B.png

这时候再次编译就发现没有错误了,但是这个地方USB中断部分还需要配置下,官方用的宏定义方式去实现不同的代码,这里一开始就去掉了宏,所以最终修改如下:

  1. /*******************************************************************************
  2. * Function Name  : USB_Interrupts_Config
  3. * Description    : Configures the USB interrupts
  4. * Input          : None.
  5. * Return         : None.
  6. *******************************************************************************/
  7. void USB_Interrupts_Config(void)
  8. {
  9.         
  10.         NVIC_InitTypeDef NVIC_InitStructure;
  11.         EXTI_InitTypeDef EXTI_InitStructure;


  12.         /* Configure the EXTI line 18 connected internally to the USB IP */
  13.         EXTI_ClearITPendingBit(EXTI_Line18);
  14.                                                                                           //  开启线18上的中断
  15.         EXTI_InitStructure.EXTI_Line = EXTI_Line18; // USB resume from suspend mode
  16.         EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;        //line 18上事件上升降沿触发
  17.         EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  18.         EXTI_Init(&EXTI_InitStructure);         

  19.         /* Enable the USB interrupt */
  20.         NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;        //组2,优先级次之
  21.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  22.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  23.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  24.         NVIC_Init(&NVIC_InitStructure);
  25.         
  26.         /* Enable the USB Wake-up interrupt */
  27.         NVIC_InitStructure.NVIC_IRQChannel = USBWakeUp_IRQn;   //组2,优先级最高        
  28.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  29.         NVIC_Init(&NVIC_InitStructure);  


  30. }
复制代码

再注释掉USB_Cable_Config的内容(DP上有1.5K上拉电阻)并修改中断函数

  1. //USB唤醒中断服务函数
  2. void USBWakeUp_IRQHandler(void)
  3. {
  4.         EXTI_ClearITPendingBit(EXTI_Line18);//清除USB唤醒中断挂起位
  5. }
复制代码

修改pwr的Suspend内容:

  1. void Suspend(void)
  2. {
  3.         uint32_t i =0;
  4.         uint16_t wCNTR;
  5.         __IO uint32_t savePWR_CR=0;
  6.         /* suspend preparation */
  7.         /* ... */
  8.         
  9.         /*Store CNTR value */
  10.         wCNTR = _GetCNTR();  

  11.     /* This a sequence to apply a force RESET to handle a robustness case */

  12.         /*Store endpoints registers status */
  13.     for (i=0;i<8;i++) EP = _GetENDPOINT(i);
  14.         
  15.         /* unmask RESET flag */
  16.         wCNTR|=CNTR_RESETM;
  17.         _SetCNTR(wCNTR);
  18.         
  19.         /*apply FRES */
  20.         wCNTR|=CNTR_FRES;
  21.         _SetCNTR(wCNTR);
  22.         
  23.         /*clear FRES*/
  24.         wCNTR&=~CNTR_FRES;
  25.         _SetCNTR(wCNTR);
  26.         
  27.         /*poll for RESET flag in ISTR*/
  28.         while((_GetISTR()&ISTR_RESET) == 0);
  29.         
  30.         /* clear RESET flag in ISTR */
  31.         _SetISTR((uint16_t)CLR_RESET);
  32.         
  33.         /*restore Enpoints*/
  34.         for (i=0;i<8;i++)
  35.         _SetENDPOINT(i, EP);
  36.         
  37.         /* Now it is safe to enter macrocell in suspend mode */
  38.         wCNTR |= CNTR_FSUSP;
  39.         _SetCNTR(wCNTR);
  40.         
  41.         /* force low-power mode in the macrocell */
  42.         wCNTR = _GetCNTR();
  43.         wCNTR |= CNTR_LPMODE;
  44.         _SetCNTR(wCNTR);
  45.         
  46.         Enter_LowPowerMode();
  47. }
复制代码

把USB初始化部分不要的注释掉:

  1. void USB_MSC_Configuration(void)
  2. {
  3. //  Set_System();
  4.   Set_USBClock();
  5. //  Led_Config();
  6.   USB_Interrupts_Config();
  7.   USB_Init();
  8. //  while (bDeviceState != CONFIGURED);

  9. //  USB_Configured_LED();

  10. //  while (1)
  11. //  {}
  12. }
复制代码

最后在USB初始化之前给U盘定义的数组赋值,不然就只有个盘符

  1.     Mass_Memory_Size[0]=1024*1024*4;        //w25q64->8M 给4M做U盘
  2.     Mass_Block_Size[0] =512;                        //设置SPI FLASH的操作扇区大小为512
  3.     Mass_Block_Count[0]=Mass_Memory_Size[0]/Mass_Block_Size[0];

  4.         USB_MSC_Configuration();
复制代码

SEGUXF{Z3MWXPLSF5KTU9.png

编译下载后就可以正常运行了。

_IXXNOSIUI%KX_{0NPN2T%2.png

传输文件、新建文件夹都可以了,就是传输速度感人。

以上就是USB虚拟U盘的所有内容了,内容太多而且复杂,所以如果真的想亲手试试,还是要点耐心的。虽然能学到的并不是很多,这里主要可以干茶mass_mal部分,可以学到不少东西,方便以后的熟练运用。我这里也是为以后更进一步的开发做铺垫。



收藏 评论0 发布时间:2022-3-16 11:16

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版