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

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

[复制链接]
STMCU小助手 发布时间:2022-3-16 11:16
学习stm32已经很长时间了,但是一直没有过多的学习stm32的USB部分,因为实际工作还是用的比较少。说起USB那就有的说了,因为USB的功能很强大,这里主要重点记录一下STM32的USB部分,这个官方给的有专门USB库,笔者目前使用的是Cotex-M3内核的STM32F103系列,实验的芯片为STM32F103C8,这个是目前市场上性价比非常高的芯片,也是用的非常多的芯片。' |# a2 w3 Y5 ~( L( y
- T. H6 z/ Q* I5 m& v- M
USB基础知识! A! c  f( |( W+ i# Q5 e8 f. v3 `9 d
USB按接口类型分
% Y4 }1 X9 ~) E8 u9 M! S
控制器/主机(controller/host), V4 C) s6 y+ v4 x$ O7 f4 N! P6 B
/ ^" h2 o0 _0 b# V
设备(peripheral)
7 }8 o% a& e  t
% d2 Q& g4 {0 A- QOTG(on-the-go),通过id线确定作为主机还是作为设备9 c0 G9 I. L( I4 H% ?" F

- u- V' t5 }- |; t按照USB速度分* N' \) S5 l7 R
8 A; z( C* z* O4 i2 k* u
低速(low speed)1 `3 r% A5 |2 Y# q
& G; U& O+ S1 O  l
全速(full speed)) {$ U" ?( c+ v. P. l/ T+ ~8 y+ R
& O0 K( s$ j3 H% g6 e+ z
高速(high speed): Y$ Z+ d+ ]5 i! n' o

7 Y- \8 W& ?( F, v: {" e: v. E* d* IUSB接口一般是4根线,VCC GND DM(D-) DP(D+)% z/ Z$ _3 t" ^: h" X) T

# d' m/ F/ _. y7 r7 I7 D7 R低速设备:在DM线上接入上拉  g; T: |- ]/ X$ o; F# v  D" e

9 o' F9 j/ o* |7 b4 Q全速设备:在DP线上接入上拉/ P7 f, @. _+ I  F) b8 s$ o

- p+ b' w" e( H5 Q高速设备:在DP线上接入上拉,在主机对设备进行复位后进一步的确认" {7 W3 O+ B0 c2 {( @9 C; |3 J+ x" O
9 S% D& P- e4 m+ {; ?

% a! \: z$ }: o/ D关于描述符' O% ~. ^  {4 x- Y6 u
设备描述符(device description)% r: A$ Y: T) s, K
1 v; V) ]# K, d3 j
配置描述符(config description)
  b/ }. }9 X- h: X- h4 A( c/ B0 g' t3 [6 {) p
接口描述符 (interface description)
. y( I4 I5 u: p6 g9 }& r" T6 m
5 @. n% R- Q5 E  e4 }0 D/ G+ c端点描述符 (endpoint description)/ l2 w3 G( [; _6 t4 Q, {9 @

$ ?! R/ x, @1 Z! \ (HQG%R@V4W]{7LOY8DZE{L9.png
) B( U. Q) N* ?. W
- q/ v$ [' @& O( S* F( I设备的“身份”信息存储在描述符中。每个USB设备中都有如下描述符。需要注意的是一个USB设备只能有一个设备描述符,一个设备描述符可以有多个配置描述符,一个配置描述符可以有多个接口描述符,一个接口描述符可以包含多个端点。1 U' m8 I6 f$ {3 ?+ P! N

4 ]/ C1 i6 |! a- N+ t关于传输

% S0 B, |3 S/ }1.在USB的通讯中,有传输(transfer),事务(Transaction),包(packet)三级。包是最基础的传输单元,与TCP/IP协议中的MAC层协议作用相同。
+ v: o7 g* c5 d9 r2.在一次传输中,由多次事务组成,每次的事务又由多个包组成
% E8 F* R$ m+ E! s0 g3.与众多协议相同,较高级别的协议的报文是基于/内嵌在低级协议的报文当中的,在USB中也不例外,例如,包中预留了DATA位,其目的就是填写报文0 }7 W0 [/ b9 K4 o- ^; L0 H
STM32F103芯片自带了有USB模块,可以用来做从设备,不能用作主机HOST。这里使用USB的目的是讲USB作为一个大容量,这个可以基于官方USB Mass_Storage例程来移植。这里就不过多的介绍里面复杂的通信原理了,要彻彻底底搞明白并灵活运用取来还是有很大难度的,因为内容太多了。这里仅仅介绍下移植过程和描述符相关的重点内容。5 L9 ]8 ?3 }2 O, ]

; P9 B( J& s  s) p$ C( c准备工作
% n; h4 l! j. c2 b第一步
8 d0 a* ]- C. |" P8 `1 O" \
确保自己的KEIL开发环境已经完善。' i3 |/ _7 j3 \  P" R; F
2 Y& L2 f2 ~1 q3 X# y
第二步
3 F! \+ u2 H8 c6 e3 o  O下载好了两份ST官方的库文件(我是基于标准库开发的),一个是标准外设库文件,一个是USB库文件。如下图:
9 Z! S7 `1 p  X4 D6 x
3 j1 K* T# d7 G5 K  W AZ7H5GQWT4J%{N{6){9E]{3.png
2 z7 D& Y3 n7 j. u$ i0 p
) N2 ^1 ~3 G- K2 c/ N第三步
8 U' U2 `( Y+ }$ p! X8 S( g建立一个带有存储介质驱动的STM32基础工程,存储介质常见有SD卡、外部FLASH芯片、内部的FLASH空间。的我是基于一个外部flash的工程去实现的,芯片具体型号是W25Q64,64Mbit的空间,换成字节就是8MByte。驱动部分如下:
. O! |2 r+ }1 {: E7 n0 F. v& F* [* K$ h' O& r
C文件5 ^5 J: C, M( ^' D' T% I0 a
  1. #include "fy_w25qxx.h"
    1 B0 s$ z; d6 m

  2. 9 C' H% @# Q: d# g/ u, Z2 N5 g
  3. u16 W25QXX_TYPE=0;//W25Q64
    # L6 O' @5 c* d$ k! C: {6 O% M

  4. * o0 ]3 }5 p  w7 ]  p
  5. //4Kbytes为一个Sector,16个扇区为1个Block,容量为16M字节,共有128个Block,4096个Sector
    6 K/ a# y9 T- o7 i/ ?' N$ _
  6. //初始化SPI FLASH的IO口
    8 l* v7 y8 R: i4 b
  7. void W25QXX_Configuration(void)) W. P1 V1 |$ y) E" N3 D) v; X
  8. {        
    & _, _) R' i3 Y& ]( H
  9.         SPI2_SetSpeed(SPI_BaudRatePrescaler_2);//设置为18M时钟,高速模式
    % K! C( R( j2 j4 h' g. V1 D: I' a. D
  10.         W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID.  
    4 S) v$ d. B( `8 l  P4 ]/ e
  11. }          9 E0 D( D2 f% |0 c3 M" f
  12. 6 Q: u6 D; M& J# M
  13. //读取W25QXX的状态寄存器  S' v$ i2 b0 |# x' {
  14. //BIT7  6   5   4   3   2   1   06 O. |* V3 K  y) V! L; c
  15. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY
    2 X# _" E& ]; z6 S( S: h
  16. //SPR:默认0,状态寄存器保护位,配合WP使用
      M8 e9 p' A' U
  17. //TB,BP2,BP1,BP0:FLASH区域写保护设置
    5 T! O4 X* z6 j+ _# A6 S! j: H2 P% D( l, h
  18. //WEL:写使能锁定
    5 ^" Y, k; Q  v
  19. //BUSY:忙标记位(1,忙;0,空闲)
    / |/ X# U  ?" J/ V6 V" n% P4 f
  20. //默认:0x00
    5 P6 i  E6 E4 k; a, |1 M
  21. u8 W25QXX_ReadSR(void)   
    0 G8 p7 c2 U0 g3 `) ~* z7 S4 p
  22. {  ' Y- J9 E/ Z7 C/ H2 R; ?7 g  b9 P1 d
  23.         u8 byte=0;   / N* W/ m1 _$ z! G- i, V' t6 R
  24.         W25QXX_CS_L();                            //使能器件   ' A# }% a' [( I  `5 h  K' k$ [! k
  25.         SPI2_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令    $ y8 Z6 S9 D0 W6 U3 l! H
  26.         byte=SPI2_ReadWriteByte(0Xff);          //读取一个字节  $ a6 p7 N3 H4 W2 o
  27.         W25QXX_CS_H();                            //取消片选     1 N# `. M7 F3 ]/ q# _' s
  28.         return byte;   
    1 ~+ q2 q7 x) l* C- x& e
  29. }
    2 d  b4 q- b8 z, i8 J, `
  30. //写W25QXX状态寄存器
      D: W! ]+ r3 N) \
  31. //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!: T2 @1 @, J0 ^& p+ N2 t: R) H& k
  32. void W25QXX_Write_SR(u8 sr)   
    . D2 y8 q2 c/ `" R9 _% }) H
  33. {   4 Y( j* D" j# R% d8 D
  34.         W25QXX_CS_L();                            //使能器件   
    4 z# e9 l8 a* d, j
  35.         SPI2_ReadWriteByte(W25X_WriteStatusReg);//发送写取状态寄存器命令   
    7 s6 R2 n6 h: N- R/ l$ P: K
  36.         SPI2_ReadWriteByte(sr);                       //写入一个字节  2 o: X! Q' ^% q0 Y, W$ v, g
  37.         W25QXX_CS_H();                            //取消片选                  
    , e) x: A( j6 z. |% m% f
  38. }   
    ) u  s. w+ f& E
  39. //W25QXX写使能        6 }" v- r3 ~' w8 Y
  40. //将WEL置位     P) J  Q3 t' s4 o1 m, _) q5 z
  41. void W25QXX_Write_Enable(void)   
    " \8 ~6 H& e" T2 i; K' V
  42. {& ]0 o( S: c* T
  43.         W25QXX_CS_L();                                  //使能器件   
    / [% M6 o8 i+ n$ Z3 Q- R
  44.     SPI2_ReadWriteByte(W25X_WriteEnable);         //发送写使能  
    ; T3 v% K  D; N$ W7 B
  45.         W25QXX_CS_H();                                   //取消片选                   " w2 B( W0 O' `) ], F
  46. } 4 @" t* I5 W" r: [% D+ Q0 \
  47. //W25QXX写禁止        ( ~( s' V  @! A) U
  48. //将WEL清零  
    ! A# @: [9 v* r1 ~; _9 \6 `7 v; d
  49. void W25QXX_Write_Disable(void)   : Z8 b9 A# I9 i% ]" @$ o7 d
  50. {  
    , Z0 H# U& Y# g( f
  51.         W25QXX_CS_L();                            //使能器件   % e. H3 O3 L. I1 n; U
  52.     SPI2_ReadWriteByte(W25X_WriteDisable);  //发送写禁止指令    . C7 c, }0 U8 E$ U3 `- `: _
  53.         W25QXX_CS_H();                            //取消片选                  
    ! h( a) T! `$ R$ A, [, A
  54. }                 9 l. D% c$ r9 X# {, C

  55. * r5 }" \( @' d+ B
  56. //读取芯片ID
    / f, _. X+ s4 ^( n
  57. u16 W25QXX_ReadID(void)
      a" I0 E* {1 b  m7 E! A& V( z6 b
  58. {
    * @1 f# B' @- W& L6 v6 F: n; U0 f
  59.         u16 Temp = 0;         
    + C" }8 [1 v4 m! }5 v
  60.         W25QXX_CS_L();                                    : m7 T! Z8 \5 G/ _. ]* F
  61.         SPI2_ReadWriteByte(0x90);//发送读取ID命令            
    6 Z/ a4 c- c3 O; F- ?
  62.         SPI2_ReadWriteByte(0x00);            
    3 m( r6 F" m4 H. x4 b$ V
  63.         SPI2_ReadWriteByte(0x00);            
    . K+ k$ L+ t4 f" j9 p% P
  64.         SPI2_ReadWriteByte(0x00);                                     # U+ Q8 G$ H4 \4 x# |+ F) ~4 r
  65.         Temp|=SPI2_ReadWriteByte(0xFF)<<8;  . U" Y$ h5 d% @' u3 @: G
  66.         Temp|=SPI2_ReadWriteByte(0xFF);         3 x8 c2 T/ l; M/ }
  67.         W25QXX_CS_H();                                    0 H4 X" r7 t8 d- h" }
  68.         return Temp;4 t+ x# A- O2 Q$ G
  69. }                       
    2 X8 x3 c. Z5 E
  70. . n6 h" s6 b; ^4 ^  n" Q
  71. //在指定地址开始读取指定长度的数据7 f4 i+ p7 o! l3 u8 U
  72. //pBuffer:数据存储区,ReadAddr:开始读取的地址(24bit),NumByteToRead:要读取的字节数(最大65535)! f) ^3 i2 A; W7 m; K/ d/ P
  73. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   ' v2 [& p. W* V3 j9 I
  74. { 6 Z0 o+ e4 l" U" y4 @5 m% F
  75.          u16 i;                                                                                       
    / D' t2 I: T' E) n  h
  76.         W25QXX_CS_L();                                    //使能器件   
    / Y8 o9 n4 a0 U) P6 V- D4 g
  77.         SPI2_ReadWriteByte(W25X_ReadData);                 //发送读取命令   
    7 K* }" [) ]: F8 U. R
  78.         SPI2_ReadWriteByte((u8)((ReadAddr)>>16));          //发送24bit地址   
    + }( ~- v, t+ L6 s. y! X$ F
  79.         SPI2_ReadWriteByte((u8)((ReadAddr)>>8));   
    : |5 D& Y& M1 s  H) h
  80.         SPI2_ReadWriteByte((u8)ReadAddr);   
    - D6 b' j6 c1 O4 {- }7 B. M3 i1 u
  81.         for(i=0;i<NumByteToRead;i++)- n& y1 Z- {% j+ S
  82.         {
    2 b: H4 B9 s3 @+ [8 D
  83.         pBuffer=SPI2_ReadWriteByte(0XFF);           //循环读数  
    7 q4 V) h/ F0 Q" s
  84.   }  @* @. D2 q4 N- Y1 Z- K# Q
  85.         W25QXX_CS_H();                                                    1 m$ X$ f5 X' N% p
  86. }3 ^7 @1 o) I9 v: K# ?: L3 I
  87. 9 L5 C# L. Q4 ]# H! s
  88. //SPI在一页(0~65535)内写入少于256个字节的数据
    $ [- Y* |* E1 I) j
  89. //在指定地址开始写入最大256字节的数据  ?# b% K' {8 w* c
  90. //pBuffer:数据存储区,WriteAddr:开始写入的地址(24bit),NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!         6 G" |* t) v; k' ~" i4 h
  91. void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
    & f& d9 l1 a& M" P- l
  92. {* L$ o- P' L+ S" }3 Q. u1 q
  93.          u16 i;  
    + h. f6 o/ ?$ c( C% J& I6 T3 {
  94.         W25QXX_Write_Enable();                          //SET WEL   x2 n1 W) [& ?) D
  95.         W25QXX_CS_L();                                    //使能器件   ' N5 @1 q8 t) P
  96.         SPI2_ReadWriteByte(W25X_PageProgram);              //发送写页命令   , g9 Q' `9 ^: h) M
  97.         SPI2_ReadWriteByte((u8)((WriteAddr)>>16));         //发送24bit地址    . p2 ]0 N6 p4 Q6 E
  98.         SPI2_ReadWriteByte((u8)((WriteAddr)>>8));   
    : y! h) c( P4 q8 h  d  \# D
  99.         SPI2_ReadWriteByte((u8)WriteAddr);   
    ; V5 G. Y8 n4 k
  100.         for(i=0;i<NumByteToWrite;i++)SPI2_ReadWriteByte(pBuffer);//循环写数  ' {/ T; B6 k8 q7 N
  101.         W25QXX_CS_H();                                    //取消片选 ; K6 b" ?6 K) d. K9 ?
  102.         W25QXX_Wait_Busy();                                                           //等待写入结束2 W2 m& p" E3 |9 Z4 p+ ]" H2 T
  103. }
    6 A5 a4 K: r; l( }0 z
  104. //无检验写SPI FLASH ; F1 }% k4 N' y
  105. //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
    , {) @9 U* k" A1 A& A8 U' h
  106. //具有自动换页功能
    8 k) E% B9 m$ v$ a. ]9 o
  107. //在指定地址开始写入指定长度的数据,但是要确保地址不越界!! j7 y9 q! U8 Y  b, N6 w
  108. //pBuffer:数据存储区# Q2 ^& L$ s! j/ x: g( H4 }) O) F
  109. //WriteAddr:开始写入的地址(24bit)
    1 Z$ }+ ^5 t2 m6 D
  110. //NumByteToWrite:要写入的字节数(最大65535)! u5 q, A; i9 ]9 U; F
  111. //CHECK OK9 C- U" s' L; e! d- p1 D4 B
  112. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   / p4 k* D2 k; g
  113. {                                          
    2 u- d' E# M/ y. f
  114.         u16 pageremain;           
    2 [! L; F" H6 O
  115.         pageremain=256-WriteAddr%256; //单页剩余的字节数                             
    - F* `6 ]: N$ P$ k% H% f
  116.         if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节8 u; z6 ]; c0 _6 t2 X6 m  }; r9 S
  117.         while(1)
    - l: ]* G# u  [
  118.         {           
    ( ^2 v$ }+ J- p! E
  119.                 W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);1 M4 q7 N& Y0 }4 O9 k; q' F- \
  120.                 if(NumByteToWrite==pageremain)break;//写入结束了
    / t; F9 W/ t7 _* `# e
  121.                  else //NumByteToWrite>pageremain
    % I, Z; `- n" H# G) X9 Q# y  i" L* y
  122.                 {4 r0 ?4 K  L: d9 ?2 I' g
  123.                         pBuffer+=pageremain;
    5 K4 @: M/ e# V* s
  124.                         WriteAddr+=pageremain;        
    ; Z% b0 n( o2 C6 A

  125. 0 p1 N& a' |( v. {' k6 r% Z
  126.                         NumByteToWrite-=pageremain;                          //减去已经写入了的字节数; ~6 }& @9 ^, [
  127.                         if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
    " }4 _- i2 ]8 `6 L( Z4 U' _
  128.                         else pageremain=NumByteToWrite;           //不够256个字节了& Z' I1 b4 t' s
  129.                 }8 u, [8 i/ U* Z6 S3 W& Z
  130.         };            " H; w7 m( e1 t0 f6 d! f
  131. } 3 c8 R5 Z3 Z; v1 `% \, f
  132. //写SPI FLASH  
    7 N0 R1 |7 h  Q& ^- v) [8 S
  133. //在指定地址开始写入指定长度的数据
    ( f* m4 {$ S9 i& I5 b
  134. //该函数带擦除操作!
    9 m( ~9 a' C+ Q$ R
  135. //pBuffer:数据存储区/ }2 C$ U8 g  R. ?% V3 @. M# O( A
  136. //WriteAddr:开始写入的地址(24bit)                                                
    - c2 Z: L/ j: ]5 A
  137. //NumByteToWrite:要写入的字节数(最大65535)   
    - `0 e! C$ w3 p7 j+ k
  138. u8 W25QXX_BUFFER[4096];                 
      R* ]/ q+ C! f* C" ?9 v& A
  139. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
    4 @$ ]" M6 K+ U) l
  140. { ( v5 {8 J+ f1 }0 w; ?6 V0 K
  141.         u32 secpos;4 A6 Z8 A$ q/ p3 \7 }$ ?" c( u
  142.         u16 secoff;
    ) ^. e7 c& V% h! s  m+ b
  143.         u16 secremain;           
    + i# @7 [3 K2 b1 `5 ]
  144.          u16 i;      j% g4 F2 P! |, h
  145.         u8 *W25QXX_BUF;          / w% o' W9 V2 d/ q  J; Y/ S
  146.         
    % z" A0 U/ w/ c
  147.            W25QXX_BUF=W25QXX_BUFFER;
    ) T  y# F. N$ t3 v* T: M8 z$ y
  148. //        % z; K2 e, ~& o1 p$ P8 d
  149. //    W25QXX_BUF = (u8 *)_mem.Alloc(4096);//申请一个扇区大小的内存8 T+ F' F6 F0 D. H8 X% l6 N
  150. //    if(W25QXX_BUF==NULL)        return;$ I$ f" ~( t6 r
  151.         
    & p: \( K' v+ \8 m# D
  152.          secpos=WriteAddr/4096;//扇区地址  
    5 `# ?/ y7 ?" x! M( t! Q& U
  153.         secoff=WriteAddr%4096;//在扇区内的偏移, {- L% i7 _6 d' R( c
  154.         secremain=4096-secoff;//扇区剩余空间大小  ) R$ d( k# P8 b+ F& V: l1 L
  155.   c. a" r& C3 m, _7 \& j7 K9 q7 k4 i
  156.          //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
    ) q3 r4 t$ N3 N+ `* d# _
  157.          if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节
    # ~8 Y7 M$ }) {3 G6 Z
  158.         while(1) ! n# P; V: @" @4 Z
  159.         {        
    ! P& ~4 s# Y% m' n  b
  160.                 W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容3 s( v3 I1 ~: ]3 Z9 w4 L
  161.                 for(i=0;i<secremain;i++)//校验数据
    * H+ s: f$ \- l9 Z9 j& L
  162.                 {' m0 O5 z( A% D( t
  163.                         if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除            1 u( N7 Y& e( ]
  164.                 }
    7 b1 W: n. \$ }; Z
  165.                 if(i<secremain)//需要擦除: K% z. Y- q7 H' A! [/ g8 R/ @: y) R# v
  166.                 {
    2 g# U8 R# E% |
  167.                         W25QXX_Erase_Sector(secpos);                //擦除这个扇区
    6 X- o8 O* \& l- {- ~) z
  168.                         for(i=0;i<secremain;i++)                           //复制# a3 [; P/ Z5 W: {. q: f
  169.                         {1 m: K; h! R( E
  170.                                 W25QXX_BUF[i+secoff]=pBuffer;          1 k, ?% I3 h" |2 d: J- R% a, I8 G
  171.                         }9 F; X# D3 e2 W7 n. _( y
  172.                         W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  $ z/ |( \: T4 r  ]5 V
  173. # n6 Z9 q: _: H& p
  174.                 }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.                                    
    ) H+ S5 \, `! ~$ L) R
  175.                 if(NumByteToWrite==secremain)break;//写入结束了
    4 a3 V( t/ b8 U- g5 d% _9 ]3 K
  176.                 else//写入未结束
    * w1 g1 _5 z. O+ z3 Z0 E
  177.                 {
    0 ]* S& J! b( Q0 R* T; P2 {  T8 |
  178.                         secpos++;//扇区地址增1
    & ^5 Q8 W& D! W4 E. e# I
  179.                         secoff=0;//偏移位置为0         
    7 T: S4 \! x# G. ^1 E
  180. . A0 u+ H7 ]1 ^- a, Y7 n! S7 F
  181.                            pBuffer+=secremain;                                  //指针偏移0 T" D: b3 z. k* [9 v
  182.                         WriteAddr+=secremain;                                //写地址偏移           
    3 \' Y8 q# d5 |6 p! u9 ^, z4 ]8 T
  183.                            NumByteToWrite-=secremain;                        //字节数递减
    ! H. u/ I. s4 c) N- s5 W3 g
  184.                         if(NumByteToWrite>4096)secremain=4096;//下一个扇区还是写不完
    . ]$ Z  B( ?$ ~1 P) {
  185.                         else secremain=NumByteToWrite;                //下一个扇区可以写完了/ J( _, G7 R( u$ B
  186.                 }         4 t) p# [) g  b
  187.         }
    % G! |4 X+ _1 R& _3 H
  188. //        _mem.Free(W25QXX_BUF);' X+ K7 T0 X. A9 N2 d! i  s
  189. }
    . k2 |' z$ |. Y) f
  190. //擦除整个芯片                  
    * c. [  r* d7 u6 L' v! q. j
  191. //等待时间超长...- c0 C1 E  h% `: l) @
  192. void W25QXX_Erase_Chip(void)   
    8 P8 }* Y  e; }& m1 r6 B+ N' |- f
  193. {                                   
    8 I& j+ ]. B5 h
  194.     W25QXX_Write_Enable();                                  //SET WEL 8 N, \& Y* B" }$ ?! h+ [& S
  195.     W25QXX_Wait_Busy();   7 ~, \+ a. C9 m: |+ `
  196.           W25QXX_CS_L();                                    //使能器件   % b2 E( r# U# h- G9 l
  197.     SPI2_ReadWriteByte(W25X_ChipErase);                //发送片擦除命令  
    & h9 V* b' y. K- @5 u
  198.         W25QXX_CS_H();                                    //取消片选                  
    # f6 i- n0 A) t
  199.         W25QXX_Wait_Busy();                                                      //等待芯片擦除结束
    & M& ^! t8 F% f* O: S
  200. }   
    ( u( l) r) q: A9 C
  201. //擦除一个扇区( }: T- L* w, B
  202. //Dst_Addr:扇区地址 根据实际容量设置# n$ J5 ?* `0 A; z8 }  a% c& s6 o8 P! _
  203. //擦除一个山区的最少时间:150ms1 v1 X& Z( w' D
  204. void W25QXX_Erase_Sector(u32 Dst_Addr)   6 _7 P. N5 V$ M
  205. {  
    " o  ~. y! `5 \. _1 T
  206.         //监视falsh擦除情况,测试用   . K2 O3 b; _2 d) o+ ~- z
  207. //         printf("fe:%x\r\n",Dst_Addr);            @' V' [" b- J/ Z$ @% ]
  208.          Dst_Addr*=4096;3 _+ u& w" r6 }/ U
  209.     W25QXX_Write_Enable();                          //SET WEL         
    7 ?+ s8 v/ r2 j( \& L) B- k
  210.     W25QXX_Wait_Busy();   
    / ]7 x( s, k0 h+ W
  211.           W25QXX_CS_L();                                    //使能器件   
    8 ?3 o$ R0 V( N0 U
  212.     SPI2_ReadWriteByte(W25X_SectorErase);              //发送扇区擦除指令 * [/ W& n3 O* {" T( v( ~
  213.     SPI2_ReadWriteByte((u8)((Dst_Addr)>>16));          //发送24bit地址    ' Z, F- C* u( s, U4 B- A
  214.     SPI2_ReadWriteByte((u8)((Dst_Addr)>>8));   ) D- Y9 [" Q" b$ w& h9 E: z
  215.     SPI2_ReadWriteByte((u8)Dst_Addr);  
    ' g% W5 ^* J1 l8 |: N% |5 ^  D$ W3 c
  216.         W25QXX_CS_H();                                    //取消片选                  
    $ t/ S" s5 [7 Y1 f3 v9 e- x
  217.     W25QXX_Wait_Busy();                                                      //等待擦除完成
    % T: k" W% q( ?$ R6 Z* ~
  218. }  8 x9 x0 ?9 v: ~' ?  P& r
  219. //等待空闲% X; {- v* U& H3 Y* P
  220. void W25QXX_Wait_Busy(void)   
    1 D2 Y9 W3 n& V) {
  221. {   2 ?! M% I$ o, b3 h  I* D& y0 O1 v2 p
  222.         while((W25QXX_ReadSR()&0x01)==0x01);                  // 等待BUSY位清空; K3 C" R9 a' Q9 ^, F/ c
  223. }  # n: ?# b+ h4 Q
  224. //进入掉电模式
    - m7 `% o  f5 j6 L, s
  225. void W25QXX_PowerDown(void)   
    " D! ~7 `% _  k3 r6 g5 ?( R' a+ S
  226. { 2 h+ D# {1 c" b3 \$ O5 `7 Z
  227.           W25QXX_CS_L();                                            //使能器件   - H0 Q) w$ t( T
  228.     SPI2_ReadWriteByte(W25X_PowerDown);        //发送掉电命令  2 q* e$ k3 T5 R9 w
  229.         W25QXX_CS_H();                                    //取消片选                   ( c* |( Z0 A9 V- y& C" e
  230.     Delay_us(3);                               //等待TPD  / S8 }3 y  z% n- }
  231. }   
    7 U8 s: u, N* F+ i
  232. //唤醒, E& b9 M# U' Q' H: Y* A" b
  233. void W25QXX_WAKEUP(void)   9 V" g  Y( T" m9 ^
  234. {  
    # k0 G" W, _& L9 j# K/ m; }
  235.           W25QXX_CS_L();                                    //使能器件   0 E5 ~. Y3 T' F  A7 O6 O( w
  236.     SPI2_ReadWriteByte(W25X_ReleasePowerDown);        //  send W25X_PowerDown command 0xAB   
    . S* _4 {4 E7 ^( V( f; Y3 T
  237.         W25QXX_CS_H();                                    //取消片选                  
    , T  f7 s) q1 `
  238.     Delay_us(3);                                    //等待TRES1
    ( {* M1 {& K9 Z9 a. C( _/ L
  239. }   
复制代码
( p6 @* @( D* U3 \( L  T
H文件
# w, e: \8 T, W+ i  R& B
. o0 e( X# @$ G: ?$ a. U& i; X: F
  1. #ifndef __FY_W25QXX_H) @2 l1 y, g+ |# M
  2. #define __FY_W25QXX_H                           
    / P6 z. Y" j4 p* ]! n$ ?( x% i# ^

  3. ) r& a& P+ F6 |+ Z
  4. #include "fy_includes.h" 2 @2 n- J' n. \% L) l3 `. b7 D

  5. 9 K; o$ V& s( v' |; [3 z7 ^' N! |
  6. //W25QXX对应唯一识别ID
    & r2 W$ j- q* j" @; m; M* V
  7. #define W25Q80         0XEF134 O4 x, J- d$ g4 J9 o% ?* c
  8. #define W25Q16         0XEF14
    " @' [* M) `9 ]/ @0 T; S
  9. #define W25Q32         0XEF15* n3 X3 G9 j' g& f+ T6 T
  10. #define W25Q64         0XEF16  |, i+ R8 y: h) i7 g
  11. #define W25Q128        0XEF17
    + ?  H+ g) s+ f+ ?: U& U

  12. 8 A' u$ H' f$ e
  13. //指令表
    8 O: @; ^% K: s4 _# s' p
  14. #define W25X_WriteEnable                0x06
    9 v" t2 ]" q4 }7 Y' Y+ B
  15. #define W25X_WriteDisable                0x04
    4 t! O4 L2 ?% b- w$ n! K
  16. #define W25X_ReadStatusReg                0x05 " Q. t3 D$ G$ Q" c# E
  17. #define W25X_WriteStatusReg                0x01
    # Y; B0 _* \( @% ?5 R8 o" I
  18. #define W25X_ReadData                        0x03
    9 L) K. D5 k, e1 Y& i& K6 q# X
  19. #define W25X_FastReadData                0x0B
    1 V% a' M1 q3 v5 V3 ^! f6 b
  20. #define W25X_FastReadDual                0x3B 0 p4 }4 r4 v7 u* B0 k* k
  21. #define W25X_PageProgram                0x02
    ; P0 s  c- P: a! H/ p/ z0 W1 Z
  22. #define W25X_BlockErase                        0xD8 , b0 N$ f7 n7 \9 x. E* Q/ D- M
  23. #define W25X_SectorErase                0x20 " d1 n  Z. c2 f6 O8 f7 U  x; g
  24. #define W25X_ChipErase                        0xC7
    6 J2 F: A: l' J, \2 o
  25. #define W25X_PowerDown                        0xB9
    3 ~3 ]: r& ]- R* s. }& J) S/ O
  26. #define W25X_ReleasePowerDown        0xAB
    0 K5 P, r" a6 y5 U' \8 F) @
  27. #define W25X_DeviceID                        0xAB 1 {% b. p. U& F! B1 O. r
  28. #define W25X_ManufactDeviceID        0x90 * E, ~9 H) V* |' d
  29. #define W25X_JedecDeviceID                0x9F 3 {9 F3 A, X: x! N: p& N

  30. 9 o4 f5 c; M7 U4 F
  31. void W25QXX_Configuration(void);6 x3 U/ c4 ?  l8 c  S* y6 V" d
  32. u16  W25QXX_ReadID(void);                              //读取FLASH ID
      D6 Y: ~. R* A& s/ Q- G
  33. u8         W25QXX_ReadSR(void);                        //读取状态寄存器 9 {3 t1 J  ~- G& V# ^8 r! P7 e$ y3 s
  34. void W25QXX_Write_SR(u8 sr);                          //写状态寄存器& {* H$ `/ J6 I, c2 ^/ f2 m
  35. void W25QXX_Write_Enable(void);                  //写使能
    & E7 d& N* `& l$ c5 Q; a7 \: l
  36. void W25QXX_Write_Disable(void);                //写保护6 j" N  d5 d- W; F& h% y6 }# e! V0 ]
  37. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);
    9 B/ H7 N1 a( `# Z2 Q7 a$ t
  38. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //读取flash; C( f+ `0 e! Z
  39. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写入flash( z# O$ K3 X, T& S
  40. void W25QXX_Erase_Chip(void);                      //整片擦除- [1 }9 j9 g8 R7 o1 H
  41. void W25QXX_Erase_Sector(u32 Dst_Addr);        //扇区擦除; E. y: A/ L- U& |
  42. void W25QXX_Wait_Busy(void);                   //等待空闲) c2 a/ a% L+ @' a  I# ]1 g
  43. void W25QXX_PowerDown(void);                //进入掉电模式) V! Y- m( F2 i' v, |3 B5 ~. [
  44. void W25QXX_WAKEUP(void);                                //唤醒& j/ \6 M8 N( G9 L; q2 u( Y3 j
  45. % s" p5 t  }- S# A! Y
  46. extern u16 W25QXX_TYPE;                                        //定义W25QXX芯片型号                  
    " C. ^3 _- S  Q6 j* o

  47.   Y0 J" k% d" i! b0 i' t( n
  48. #endif
复制代码
9 X9 F! p. }7 ]) K$ N2 {
基础工程如下:
5 h+ E1 `7 A( v! O5 L4 f- m3 X! b" d$ k. n. R% @6 `( G* y1 s
CIG]SRYALJ}8)5NQ[MAA~BW.png % U# T1 D2 s; P, E1 {
8 {) e6 V/ \/ e$ S
第四步:USB代码移植
( {- x8 p( ~0 p. t* i, f0 J) _* M7 G拷贝USB底层库到工程根目录,新建一个目录命名成USB,拷贝Mass_Storage例程下的src和inc目录下的所以文件到USB。
: h8 c1 M: _4 W6 Q1 n. U- m
6 N& Q# d* T, @( c3 g6 Q 6Q]9Q9812B]OXMA8GNV[PCC.png * ~6 |5 q( _# K! n( I
( P; R3 G* J1 v1 P% D
拷贝过来是有37个文件,这个时候需要删除一些不必要的文件,因为官方的例程里面有nand,这里的话先删除以下文件! D2 D, N5 O6 b

2 |" N) Q2 S) u3 x- A! P$ ?* \0 w1 S VN1JDR_I@JO271YJKNV1(HE.png - F$ Z8 t7 d+ `: v7 S
# \. B5 t, S- C  h, t% u; C' j) m- p
4.打开工程把分组和文件及包含路径添加进去,这里就不细说了,结果如下
0 m2 d) [0 D1 y  y+ ^- N6 b4 @  q7 V# }9 C
X$DZH7HHYEC}266_`72~4EH.png
1 P- L$ x. R* H0 E# I: {' {. `$ T$ m) ^# c, o, E
打开usb下面的main和it 把重要的代码先拷贝出来) h8 O2 h9 R" m1 ^4 h; s

+ ]/ o- }$ i$ l. H" O OA]E5J8OQ0[B{UJV@KXPUG2.png
" N1 Y8 V; ~8 H$ B6 W9 ]6 }7 ^) n7 v0 O0 d  ?
把main和stm32_it里面的内容拷贝到自己的main里面进行下封装去掉不要的保留剩下的,然后就删除it和main三个文件。
9 s& g9 J2 B% s0 o( S7 J! a8 M* L! c/ l' y3 f% C
@G5L9U9`9]${JVP}AD@KN0E.png
$ p. T- C- N, e) l& K8 ^  n. [, c$ n% Z5 o
然后编译下,这个时候会出现一堆报错,需要慢慢一步步搞定,编译前注意路径包含,9 F6 @$ e$ d0 A& h6 \  _* j
  `; A9 `9 n. {5 H
[~3UDXP_99@A_@~XV14BNPB.png
: t% w0 Q" n6 H! \( B8 }
  C# J  J9 {5 [) t2 L定位到第一个错误进去
( I& L5 d7 Q* n
( P6 V6 ?0 t" r1 M 5L28CK8A4)MY@OJR7KK~KOY.png
) U( l( R. n: ?9 {# C6 r* p/ S, j" v! k+ u9 A9 j
这个定义的是官方的开发板 我们直接修改成自己的公共文件就可以,并且注释掉开发板定义
6 \0 B8 c9 F& Z) H0 D
+ B4 \) d' \, I, \- P4 q$ B TR]6[QMF5`A7X$MYZIL])9I.png & T  e% d+ k( P" ?4 K
! g8 v* s1 d# F8 x/ d1 |
公共文件如下,并且添加usb部分的头文件# `6 f% S6 p. {. L. @8 Y' w

' |0 u+ m" Z6 L4 _& z' T, ~ Z7Z6YX`QZR}6~KCC_GDDLD8.png
4 c+ R# B& R; K5 W' Q
- f3 b, j& b& k+ C5 {' ]; d继续编译,定位第一个错误,这部分主要修改hw_config文件
3 u+ ]$ e9 ]1 \7 P, S) O$ k! Q7 q; @& Q+ N0 ?# {
_9RUE5O1M][P)IRX]S2S_$D.png
9 q6 M6 m  B0 G, b/ t" \+ W' W+ e
, x/ A# V0 B5 V- P6 l7 {6 I) `* ?) z0 J3 ]删除Set_System函数6 n5 O6 c2 U- ^" ?3 {6 T
' e5 c8 g9 M$ \$ `6 Y6 z- R, x
N~R76S188E[X(N6NID1WQ$V.png
' I! \9 q% x8 Z& z9 G7 _7 N
2 m3 n+ L& \8 O' `/ r- t删除Led_Config函数
: v" d6 t3 y; J' b0 n
$ T1 i& O6 G( L* U, q1 n9 o ~HA$Z23~@)XV2TEQ8CUTS.png
, [( R8 p+ L$ |
/ v' e# @4 L4 U5 A  z' l注释LED相关的内容
3 S! a3 O  B3 w/ f" S  M. c! k9 I; Q3 h- A0 m* R, j
_FKQ$_}1}S8W%PG(2VT2]C7.png
0 q$ S% R4 D& _9 r  Z- c) c& ^
" o- U& L" I/ c% T; p, b* L删除USB_Disconnect_Config0 S+ ~" [* Z4 v" N  Z) U- L

# a1 j, H2 `3 b4 x( P3 b 4K@_T@@XVZ@P]EEH37}QAJX.png
. \4 Z9 W1 i3 H  O# l: X! K6 _3 f& N! e$ L0 C. `4 @
接下来主要修改mass_mal部分,我这里是只有一个flash,所以我修改成如下:
% ~1 V) A) `: e
" K+ `: l# c8 A9 o* J 2_1P4[7IG68PG%9TS@41BI4.png
3 G# k0 d. U6 A, Y& n( k# S# U# b2 V6 [# o
头文件
3 Z6 A1 _3 M5 t  l2 ^/ M
2 G3 ?) M. h' C6 G" T# Z+ D. F8 q" D, K; V (V0@{X{W8~L[2Z[Y7)VNU.png / M; f. z/ G, L& I! q/ G( r

% N- F+ Q: ^$ T$ J3 _& x$ l" s9 [下面修改memory文件,主要修改变量定义,C文件和H文件分别如下
9 x( G/ }0 r/ [: X/ M+ N7 i* a9 {+ p! [" s/ W. ^; v( P
T]Y~~F2{V(H`V{$X(}FMNNJ.png
0 r5 J  D2 E3 c$ T! L2 ?' W- n
! O6 i4 R0 S8 c$ H  s$ H/ q3 h接下来是usb_scsi部分的变量定义& O% p  D0 R! B) [/ a- H
6 T; }2 W$ G+ z' ~7 O
R$Z1OIMFO6JWKJE~XX[{2$B.png
9 U* p6 z0 v. F3 \6 D( I
4 Z0 r- y7 [4 E0 \' M这时候再次编译就发现没有错误了,但是这个地方USB中断部分还需要配置下,官方用的宏定义方式去实现不同的代码,这里一开始就去掉了宏,所以最终修改如下:8 {5 ~' G% N  p! y9 y' M
9 i* L- S6 A& K, b% L( K; V
  1. /*******************************************************************************
    " \6 k; k- s' I6 l
  2. * Function Name  : USB_Interrupts_Config1 \. {2 R3 O1 p$ e' X# M
  3. * Description    : Configures the USB interrupts
    ! ^! F) I# q4 J
  4. * Input          : None.
    7 ~. f7 C  O# c$ N- {% c7 ]
  5. * Return         : None.# T- |# ?! {4 m& J; A
  6. *******************************************************************************/% l$ I$ e# M0 V3 a* j+ }
  7. void USB_Interrupts_Config(void)
    / G; ^! \/ y7 _0 {% |: L
  8. {
    2 A9 Q2 N! Q$ o2 j1 |0 u
  9.         0 S4 L' B2 l1 j. i8 j
  10.         NVIC_InitTypeDef NVIC_InitStructure;
    9 u( m# r) |, Q0 J
  11.         EXTI_InitTypeDef EXTI_InitStructure;' c( z, s, R, P& j) h+ Z

  12. , _7 B$ O! ?/ f1 a  g! S

  13. 7 S4 q/ {3 I! U- V) E6 e9 h
  14.         /* Configure the EXTI line 18 connected internally to the USB IP */8 d# {) O9 h3 Y; ]- p  w( z
  15.         EXTI_ClearITPendingBit(EXTI_Line18);& y1 t" M2 N) F; C7 Q+ q
  16.                                                                                           //  开启线18上的中断( h. @& _4 n) b* y
  17.         EXTI_InitStructure.EXTI_Line = EXTI_Line18; // USB resume from suspend mode
    # I" m. W" ~' V# h, j
  18.         EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;        //line 18上事件上升降沿触发. N8 c; r6 p% a0 M
  19.         EXTI_InitStructure.EXTI_LineCmd = ENABLE;0 B, ?8 j3 ?1 b2 g9 x- C+ l
  20.         EXTI_Init(&EXTI_InitStructure);         
    5 M: `2 R) V; P
  21. ' B4 Q# ]2 \: R7 C2 K* L2 ~
  22.         /* Enable the USB interrupt */; J' Q0 Y. X; ]
  23.         NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;        //组2,优先级次之 " b/ s  w" J. |- L0 }* j
  24.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;: @- z6 u+ i6 m& e; w
  25.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    . S, a& q, ~% E# u% j
  26.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;. [3 u( C. x( m. ~3 u$ [
  27.         NVIC_Init(&NVIC_InitStructure);
    $ F! e# b; h1 b8 c
  28.         3 w# t3 B  J: D1 H3 W1 {3 T
  29.         /* Enable the USB Wake-up interrupt */
    0 p& L: b3 f& |1 I+ ?
  30.         NVIC_InitStructure.NVIC_IRQChannel = USBWakeUp_IRQn;   //组2,优先级最高        ; H* b6 _: d* N) e/ \/ y
  31.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    ( y/ C+ n8 g& t" j" p2 F
  32.         NVIC_Init(&NVIC_InitStructure);  & k8 Y! P: F/ p9 [$ F% r; _/ p: D! a

  33. 1 P0 y4 @  d2 h' I0 R5 M. A" A1 m$ U/ Y
  34. # H: ?$ G( Z" x" I2 ]3 c7 `5 U
  35. }
复制代码

% P" V9 _( p. t$ R! C再注释掉USB_Cable_Config的内容(DP上有1.5K上拉电阻)并修改中断函数; ]! a( Y4 q7 W; p
# D4 [$ x) G3 a# Q
  1. //USB唤醒中断服务函数7 L& {- T) S* A4 X% X
  2. void USBWakeUp_IRQHandler(void)   i  _* O' M# j* _; W
  3. {
    ! Y% K4 t# x, }7 a
  4.         EXTI_ClearITPendingBit(EXTI_Line18);//清除USB唤醒中断挂起位& T% _. ~2 _- t; \
  5. }
复制代码

  I5 B6 \! z) a) I# H9 c修改pwr的Suspend内容:
/ g9 u- T! ~0 m" ^) L: j
; @- m/ {& L" f* ?; V
  1. void Suspend(void)/ R0 Y, [, `* S4 z, `
  2. {
    1 }8 k- U4 ^+ E
  3.         uint32_t i =0;
    " w3 q; Z" \' {1 O# L
  4.         uint16_t wCNTR;
    ; Y! _/ ?  H  h
  5.         __IO uint32_t savePWR_CR=0;
    9 i) h' a2 i% c. ?* _" B0 T
  6.         /* suspend preparation */
    4 D! f9 s! t- t. z( ~
  7.         /* ... */
    1 y+ Q' ]9 m) C
  8.         ' O& J. y: P6 n; ]$ U
  9.         /*Store CNTR value */
    , y. l& O+ h! i& G; x) B  B+ z# b. R
  10.         wCNTR = _GetCNTR();  + }% d- u7 i' m; R# w
  11. 9 n& Z" B% y. B. p' K7 D$ r/ ]! J# V
  12.     /* This a sequence to apply a force RESET to handle a robustness case */
      |# c: K% t. p
  13. 6 i! L" h  S" S( x% H
  14.         /*Store endpoints registers status */7 S5 B' T8 D+ F! L& a8 ]) i
  15.     for (i=0;i<8;i++) EP = _GetENDPOINT(i);
    ! i, t2 A0 b( |. o, V7 c; z
  16.         
    . Z+ w# n, R3 p  i6 b' p) L' j7 ?
  17.         /* unmask RESET flag */  l  @. q5 v0 m6 J+ C) a0 U/ o6 g
  18.         wCNTR|=CNTR_RESETM;
    * O1 g. u4 \: r0 M6 T
  19.         _SetCNTR(wCNTR);
    2 l- ^% d3 R# O; p* `
  20.         - r, O$ @( @0 R% t6 d
  21.         /*apply FRES */1 |) i# B+ l; D+ G8 X" X7 B
  22.         wCNTR|=CNTR_FRES;
    , b3 C# r, w. z( _8 {) F
  23.         _SetCNTR(wCNTR);
    5 }1 m# g. J2 O+ I+ v
  24.         
    4 a" s# r) D- s/ `0 |, K% P, v
  25.         /*clear FRES*/
    : B9 C+ a! e* F  }& z& F
  26.         wCNTR&=~CNTR_FRES;
    ' z/ }* Y3 U( B7 L3 e
  27.         _SetCNTR(wCNTR);
    1 @' {- T: y; L' p- w
  28.         ; G7 ]8 _: p) u
  29.         /*poll for RESET flag in ISTR*/
    ( f1 h* N! O5 i' M  v( {' {+ R4 D1 d
  30.         while((_GetISTR()&ISTR_RESET) == 0);
    ; f# i$ s! |6 Y/ v, w/ C
  31.         
    4 z% d% L  [( b- |/ J
  32.         /* clear RESET flag in ISTR */% W( \$ @. U. w- n' K, b
  33.         _SetISTR((uint16_t)CLR_RESET);
    ' V6 D2 D5 U- {. v4 N- f$ i
  34.         
    ! m( b- k; Q6 A( v
  35.         /*restore Enpoints*/
    : W; G" K1 v: j# q% J9 [6 V
  36.         for (i=0;i<8;i++)  ^' D& w3 F6 v1 Y
  37.         _SetENDPOINT(i, EP);
    / C: r. ~8 M" X% c  p
  38.           b8 F# l( y7 g# w" P" {
  39.         /* Now it is safe to enter macrocell in suspend mode */, Z( Q3 C; R" ?
  40.         wCNTR |= CNTR_FSUSP;$ x+ w3 I( l( X( S0 Y# I
  41.         _SetCNTR(wCNTR);
    4 Y0 _( |7 f/ N: @6 \0 Z/ u+ \
  42.         6 M# g" ^& Z. U+ j* |  |
  43.         /* force low-power mode in the macrocell */
    2 `3 y0 T' f; Y/ K) f- a( Y
  44.         wCNTR = _GetCNTR();/ Q8 E  f, Z  b4 x, e- F; i0 l
  45.         wCNTR |= CNTR_LPMODE;
    7 e. G1 N& g, h  P. w* r  P. @
  46.         _SetCNTR(wCNTR);& `; I1 t; D& h" o6 L9 b- r
  47.         
    - e$ }' y" `. i7 ~7 n! \& M( ^
  48.         Enter_LowPowerMode();, m4 V4 d9 d: C% p) C" F
  49. }- f& N% N+ s5 }2 o$ @, z) {3 ^
复制代码
" _7 j- x, U5 u5 H- }
把USB初始化部分不要的注释掉:$ K1 R+ F- ]' {2 h5 M) F

  |- M/ x5 B3 @. F; H8 k4 V
  1. void USB_MSC_Configuration(void); t7 @, g* h6 u3 r- K0 G; U
  2. {/ H. T( A% `; f
  3. //  Set_System();
    1 ~5 u6 u$ E( N% L
  4.   Set_USBClock();1 i7 K' z2 W  }9 s9 v
  5. //  Led_Config();2 Z' v+ n( a  a3 D7 D
  6.   USB_Interrupts_Config();- T  Y6 n" u3 t/ N  D! u$ Y
  7.   USB_Init();
    # I( n' |* r$ r1 E
  8. //  while (bDeviceState != CONFIGURED);
    ' i- e# T0 S4 X; H& r7 N1 m5 v1 {9 D

  9. # f# g$ j) i/ R$ \; V
  10. //  USB_Configured_LED();
      G- n3 n0 G4 g/ h. T* u

  11. % h  M7 Z2 c+ L' T2 C
  12. //  while (1)
    5 d; T+ T* u3 N' W% L  K, i
  13. //  {}
    . u3 \# m8 w7 I, c! A* j
  14. }
复制代码

8 m* Y0 y8 J  R- }% g% A最后在USB初始化之前给U盘定义的数组赋值,不然就只有个盘符
! Q9 x( {. C/ ?. k$ m
2 S% Z& Q; n$ A
  1.     Mass_Memory_Size[0]=1024*1024*4;        //w25q64->8M 给4M做U盘
    & _2 J& C4 m3 {* }( R
  2.     Mass_Block_Size[0] =512;                        //设置SPI FLASH的操作扇区大小为5120 Z$ @6 T" _! `4 h! e$ w5 \
  3.     Mass_Block_Count[0]=Mass_Memory_Size[0]/Mass_Block_Size[0];
    2 ]0 O- o9 D6 _  Y9 h- F4 w
  4. 8 r% x2 ^5 o) a0 w  R9 o
  5.         USB_MSC_Configuration();
复制代码
; f7 |/ O% [" U" |& E9 y9 f
SEGUXF{Z3MWXPLSF5KTU9.png
, c- _8 K' J$ _8 B( i9 G4 `; g, [) b0 j3 c4 P0 E% Z' n
编译下载后就可以正常运行了。
# [# H1 X6 h4 O2 `* q8 H1 F$ S6 }; W( E( d" ?
_IXXNOSIUI%KX_{0NPN2T%2.png
: l) w( V3 B4 r' n% _8 x& k# C4 U* b) s; |0 f2 j* j
传输文件、新建文件夹都可以了,就是传输速度感人。
- d0 `) ^6 t' E- M3 E# [1 X
- k) o; ~/ t) X0 k- |! @8 k2 p- _以上就是USB虚拟U盘的所有内容了,内容太多而且复杂,所以如果真的想亲手试试,还是要点耐心的。虽然能学到的并不是很多,这里主要可以干茶mass_mal部分,可以学到不少东西,方便以后的熟练运用。我这里也是为以后更进一步的开发做铺垫。
0 u0 H- ]1 o* Q* v' W& D8 q7 G! s. g0 _9 i) X  z

6 k- E7 g9 m7 L. {
% O# ?3 I& C" X. x
收藏 评论0 发布时间:2022-3-16 11:16

举报

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