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

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

[复制链接]
STMCU小助手 发布时间:2022-3-16 11:16
学习stm32已经很长时间了,但是一直没有过多的学习stm32的USB部分,因为实际工作还是用的比较少。说起USB那就有的说了,因为USB的功能很强大,这里主要重点记录一下STM32的USB部分,这个官方给的有专门USB库,笔者目前使用的是Cotex-M3内核的STM32F103系列,实验的芯片为STM32F103C8,这个是目前市场上性价比非常高的芯片,也是用的非常多的芯片。
6 `- x. ]2 i) A. y* y% C/ j3 b' L* N3 k- c$ b
USB基础知识. n+ [  a8 ?, O/ I# \/ A4 ]7 C2 p( k# y
USB按接口类型分

, X- ?" H. F( J2 e+ I1 N控制器/主机(controller/host)
* ?9 w& V+ t  N1 X& O, r1 V8 L5 |6 s+ T  o) q6 c- v
设备(peripheral)
* v: z6 Z! M, _; s9 K4 M9 p- u* V, n
OTG(on-the-go),通过id线确定作为主机还是作为设备
& I* q5 X: y" ~# L+ ~% Q" k* W+ _5 [4 m% E5 l* w0 w% J
按照USB速度分
3 b( N$ j. _; [$ q2 Y6 X) d5 j6 w5 o: w" d  F3 M* m
低速(low speed)
/ L7 U% h; x; n1 r+ w+ B9 c" q% a  x- I8 e& F2 S. J6 b1 y
全速(full speed)
4 |2 `  t) o1 P  b; |4 ~* P* J/ D! x- s6 s2 V; Q/ V: S" e
高速(high speed)9 P3 z, C9 n6 c. U  F9 s
: g, Q  c. S7 q5 m6 |0 w
USB接口一般是4根线,VCC GND DM(D-) DP(D+)! W3 y) n7 m% N4 ^$ V! u' J

& E% C5 W, }& C1 i& W4 ~  Y/ D5 J低速设备:在DM线上接入上拉
/ g  E$ M2 P6 t- ^/ s) v; U$ k, w, W; t
全速设备:在DP线上接入上拉( i7 L& P/ i4 B, C! l

! G& u; P' g! i8 F7 v- D5 N高速设备:在DP线上接入上拉,在主机对设备进行复位后进一步的确认+ U: l" o3 S/ C. F3 Z" p6 ~4 o& a+ @
: H$ |; x' W% N2 g/ r

- Z+ h9 C& v7 L  ]/ u! v关于描述符  a- B% s' V, D* y  t! J
设备描述符(device description)
, Q$ |4 q# U' `( Z
# G8 e( X# U7 ?) J$ z! e7 p3 L  j$ ]配置描述符(config description)4 \: P) {5 W, \5 a' F

5 O; w0 m9 G! d# u8 c& E( m% k接口描述符 (interface description)8 z3 `! h/ F+ N" }6 G8 v+ `* x9 `
4 F) \0 E. v. f! H- `
端点描述符 (endpoint description)
6 S9 j7 o) I: n, _8 u
0 p' c8 }$ {9 T6 [6 Y- S (HQG%R@V4W]{7LOY8DZE{L9.png
  K# I* z+ }* W$ Q
7 j* @% o  f9 G# ]设备的“身份”信息存储在描述符中。每个USB设备中都有如下描述符。需要注意的是一个USB设备只能有一个设备描述符,一个设备描述符可以有多个配置描述符,一个配置描述符可以有多个接口描述符,一个接口描述符可以包含多个端点。+ [3 n2 u4 z9 C% Z
7 |$ E" J. M2 F% q4 R  h" P7 h
关于传输
0 o  {7 m8 U2 {7 d! x
1.在USB的通讯中,有传输(transfer),事务(Transaction),包(packet)三级。包是最基础的传输单元,与TCP/IP协议中的MAC层协议作用相同。, z$ k& Q6 R5 C2 L' L2 p- g$ \; G; e
2.在一次传输中,由多次事务组成,每次的事务又由多个包组成4 i  N4 }" q: J' Q" T) B
3.与众多协议相同,较高级别的协议的报文是基于/内嵌在低级协议的报文当中的,在USB中也不例外,例如,包中预留了DATA位,其目的就是填写报文. N) f6 J! t( }
STM32F103芯片自带了有USB模块,可以用来做从设备,不能用作主机HOST。这里使用USB的目的是讲USB作为一个大容量,这个可以基于官方USB Mass_Storage例程来移植。这里就不过多的介绍里面复杂的通信原理了,要彻彻底底搞明白并灵活运用取来还是有很大难度的,因为内容太多了。这里仅仅介绍下移植过程和描述符相关的重点内容。
; e6 e: X* G+ k: t( i8 k: m6 G/ [$ E/ i, m# n) g
准备工作' l# K1 N! ~* w' C* H) |
第一步
( {! v5 l( _8 y" d7 x+ S
确保自己的KEIL开发环境已经完善。; [2 w( ~% R5 J0 A( w
" `+ M* G* c% S1 x6 z# c
第二步0 j& {8 ?' J, ~7 v
下载好了两份ST官方的库文件(我是基于标准库开发的),一个是标准外设库文件,一个是USB库文件。如下图:- S9 z$ w. ~% H6 M$ Y5 M: P9 N

6 l2 j' i* B: j1 | AZ7H5GQWT4J%{N{6){9E]{3.png
) D; ~2 A/ n% m3 i  M. U, P3 O; f) H* z
第三步
8 V2 I% y' ?% m- |; a. @建立一个带有存储介质驱动的STM32基础工程,存储介质常见有SD卡、外部FLASH芯片、内部的FLASH空间。的我是基于一个外部flash的工程去实现的,芯片具体型号是W25Q64,64Mbit的空间,换成字节就是8MByte。驱动部分如下:$ o2 R$ P2 M2 B; X1 e3 I
$ O, x# x1 e7 N7 s9 h( p% G# ?* W/ e
C文件
+ n5 B* }  O  B/ m3 y5 G$ s2 w
  1. #include "fy_w25qxx.h"
      N) r5 {# z( h( V8 X) s& t

  2. 1 \% @$ U6 L, A' {+ Y5 n
  3. u16 W25QXX_TYPE=0;//W25Q64
    4 r- l+ g9 y! |- b# P8 ^, L7 X

  4. 4 U, A4 ]4 u. P% j5 v( d' r3 x' v9 i
  5. //4Kbytes为一个Sector,16个扇区为1个Block,容量为16M字节,共有128个Block,4096个Sector
    * j$ L$ [% Z& W
  6. //初始化SPI FLASH的IO口6 B9 E2 t: S2 z- w
  7. void W25QXX_Configuration(void)6 A# Q) S% u" X, C
  8. {        
    3 J$ ^4 o( Y) C2 ~$ T5 W9 ?1 V
  9.         SPI2_SetSpeed(SPI_BaudRatePrescaler_2);//设置为18M时钟,高速模式; t/ G: h* F3 g
  10.         W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID.  : }% V( K( `3 R4 a, }; G
  11. }         
    ; `( d( p2 D3 k  S
  12. 9 [; Y6 T8 Z9 j
  13. //读取W25QXX的状态寄存器& L0 x$ O6 {6 X! c( y5 l
  14. //BIT7  6   5   4   3   2   1   0
    5 @+ o1 s4 T6 y& c! E+ w
  15. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY' r- m/ ~' e: V9 W2 g5 _4 q6 T: @
  16. //SPR:默认0,状态寄存器保护位,配合WP使用- K  {# W. j; P5 G% k  v! K1 U
  17. //TB,BP2,BP1,BP0:FLASH区域写保护设置- D1 |5 X4 }! g2 i* N  w
  18. //WEL:写使能锁定
    : d( s  \+ a& {9 h
  19. //BUSY:忙标记位(1,忙;0,空闲)
    / M& r7 o/ j/ V9 D% A/ G
  20. //默认:0x00
    : _0 |8 r8 `5 Q7 N, b
  21. u8 W25QXX_ReadSR(void)   ' |( y! t' o3 M
  22. {  + A: a3 _6 x, f& F) V3 K2 O- j7 ]3 s
  23.         u8 byte=0;   ' U% `: S9 u/ V  |
  24.         W25QXX_CS_L();                            //使能器件   6 j8 Y0 ~4 I' J- W; @& Z+ C, u
  25.         SPI2_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令    + ~& @' g; Z, D" W: O2 Q* v+ p3 T
  26.         byte=SPI2_ReadWriteByte(0Xff);          //读取一个字节  
    7 z# O5 H% {; S
  27.         W25QXX_CS_H();                            //取消片选     1 w" I+ U' u* d" ]0 o' f" ^- j2 @
  28.         return byte;   
    ( W% K, i& z4 b4 L9 Y- Z0 q3 c4 n
  29. } 8 m* H# ~  z( X; G- D& t
  30. //写W25QXX状态寄存器  B- a6 [8 [* j  |0 s7 P# ~
  31. //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
    / E' Z: |- D. o3 ?
  32. void W25QXX_Write_SR(u8 sr)   9 R! v  \8 L8 P6 a4 |0 n" K
  33. {   * i# V% K3 x1 m
  34.         W25QXX_CS_L();                            //使能器件   
    , E$ K/ H2 X9 g0 t1 o
  35.         SPI2_ReadWriteByte(W25X_WriteStatusReg);//发送写取状态寄存器命令    % }; Z; _5 ?; s+ |' X9 f
  36.         SPI2_ReadWriteByte(sr);                       //写入一个字节  9 n+ m( f  `+ a+ Y4 `
  37.         W25QXX_CS_H();                            //取消片选                   / R  V) J6 |! ]4 K. S0 m$ i5 l, r
  38. }   2 E1 J7 D5 x# \( o: S
  39. //W25QXX写使能        3 c6 }3 ?- p3 H: A, N7 x
  40. //将WEL置位   
    6 D- v2 v7 F7 k8 J  e8 b
  41. void W25QXX_Write_Enable(void)   - N) r$ @) @# \. X  M7 p
  42. {% G6 G( s' N3 ?4 p
  43.         W25QXX_CS_L();                                  //使能器件   # e! O3 z% |9 j% ^4 S- `
  44.     SPI2_ReadWriteByte(W25X_WriteEnable);         //发送写使能  
    4 l( ^% _0 f9 G* V  x1 l: @, U
  45.         W25QXX_CS_H();                                   //取消片选                   / c  m) Q; K% ^* W+ Y6 O
  46. } * u. E0 k3 k3 v) k# j/ V+ i
  47. //W25QXX写禁止        
    # d% ]& v- U2 y2 f2 O5 V7 M
  48. //将WEL清零  
    # ~* S. h" b/ M/ ?0 p) j! A
  49. void W25QXX_Write_Disable(void)   1 X8 D, L/ V* D, l
  50. {  
    1 m0 t2 R+ e4 d7 G2 r7 K* n5 |
  51.         W25QXX_CS_L();                            //使能器件     D& m) A4 P: I4 m% B
  52.     SPI2_ReadWriteByte(W25X_WriteDisable);  //发送写禁止指令   
    0 S7 z  V" b% K$ a* T
  53.         W25QXX_CS_H();                            //取消片选                  
    9 d; K% n* k2 M2 p1 ^! R6 K
  54. }                 
    $ H8 W* _8 J2 h1 r" o6 M

  55. ( k8 ^- Y3 J! R# g% @
  56. //读取芯片ID
    , D* k3 A' m9 k* g
  57. u16 W25QXX_ReadID(void)- ?' L& D  H, D1 b
  58. {7 y: |- Q- r9 ]* @8 q
  59.         u16 Temp = 0;         
    ! [% b2 t5 ?8 E) e' _
  60.         W25QXX_CS_L();                                    # j( O# o; g1 Q4 w" x% O8 c( P
  61.         SPI2_ReadWriteByte(0x90);//发送读取ID命令            1 [# x, m, l/ P, u: w7 B
  62.         SPI2_ReadWriteByte(0x00);             & c- x/ r% k0 ~4 o
  63.         SPI2_ReadWriteByte(0x00);            
    2 f# c) s5 Z- L. u# l0 v* b: m4 o
  64.         SPI2_ReadWriteByte(0x00);                                    
    + i% f& Z7 J* S$ y- I$ G/ {4 D
  65.         Temp|=SPI2_ReadWriteByte(0xFF)<<8;  3 ?) S1 N' |2 E5 K
  66.         Temp|=SPI2_ReadWriteByte(0xFF);         
    0 u" f4 v; D9 H7 ^( w0 _2 u
  67.         W25QXX_CS_H();                                    
    . `3 {; u: x) A/ d, V! }
  68.         return Temp;
    . M3 j& m: G+ n5 G# m- S( T0 Y
  69. }                       6 z5 _% l9 f; F) H
  70. ' B7 [6 ?% i3 e3 P. \. J8 m; P
  71. //在指定地址开始读取指定长度的数据( `5 E1 p; I$ S( e, T
  72. //pBuffer:数据存储区,ReadAddr:开始读取的地址(24bit),NumByteToRead:要读取的字节数(最大65535)
    * S8 p! v9 l3 C/ g: i
  73. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   + ?, N, k+ K! B3 t5 v
  74. {
    $ b! B9 c( u2 m: {; |. L7 ]
  75.          u16 i;                                                                                       
    7 E, @: c/ z8 J2 d) D6 h
  76.         W25QXX_CS_L();                                    //使能器件   
    , z* @& @( K9 \5 U3 l& Y% s
  77.         SPI2_ReadWriteByte(W25X_ReadData);                 //发送读取命令   
    * e" j% o# {2 t& p9 W4 E
  78.         SPI2_ReadWriteByte((u8)((ReadAddr)>>16));          //发送24bit地址    ( l8 W: B4 b6 h& e8 t- n6 _
  79.         SPI2_ReadWriteByte((u8)((ReadAddr)>>8));   . l/ p1 S, l) v- j
  80.         SPI2_ReadWriteByte((u8)ReadAddr);   
    2 R$ J/ w: z! @# o; P- f* @' d0 t; G
  81.         for(i=0;i<NumByteToRead;i++), E  j3 r+ e! k. Q: a
  82.         {
    0 P" z4 C* p3 h6 x2 l9 z
  83.         pBuffer=SPI2_ReadWriteByte(0XFF);           //循环读数  8 l9 e3 k  j- L  {0 z3 w; a
  84.   }
    5 G; D1 @" h# T
  85.         W25QXX_CS_H();                                                    1 [: l1 O+ @0 w2 l
  86. }1 K1 c' u; n: R/ c5 u0 L8 s$ @2 V$ P" }
  87. 5 s/ i! `  I. ]
  88. //SPI在一页(0~65535)内写入少于256个字节的数据+ r( T. b" ]* N0 _5 V% J! p
  89. //在指定地址开始写入最大256字节的数据; P2 w/ o6 q9 e. d
  90. //pBuffer:数据存储区,WriteAddr:开始写入的地址(24bit),NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!         0 R/ v9 h/ M0 n" R. V, }
  91. void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)0 z8 d( Y+ S- Z6 p
  92. {
    * u5 c2 D  S7 w: B! t. u
  93.          u16 i;  
    / a! q  F4 N$ j7 Y2 z$ [
  94.         W25QXX_Write_Enable();                          //SET WEL $ a1 ?4 g2 s2 G+ Q+ T( g) A
  95.         W25QXX_CS_L();                                    //使能器件   7 X- u; R, f$ M8 C9 t" w
  96.         SPI2_ReadWriteByte(W25X_PageProgram);              //发送写页命令   
    8 U* P8 P8 Q# w( h9 y  a' ?0 o
  97.         SPI2_ReadWriteByte((u8)((WriteAddr)>>16));         //发送24bit地址   
    4 J8 c* O, C1 Q" u
  98.         SPI2_ReadWriteByte((u8)((WriteAddr)>>8));   
    2 W% K8 D4 j9 D# _9 ~) z% n  V  h, Z
  99.         SPI2_ReadWriteByte((u8)WriteAddr);   ) i. y8 B% x- s
  100.         for(i=0;i<NumByteToWrite;i++)SPI2_ReadWriteByte(pBuffer);//循环写数  " H9 y8 H8 o4 K' M$ a/ G/ p* N
  101.         W25QXX_CS_H();                                    //取消片选 ( a# |& ]1 Q8 A6 C
  102.         W25QXX_Wait_Busy();                                                           //等待写入结束) \5 S5 |8 `0 V4 d2 }8 h' x
  103. }
    % o& C% `2 q1 N4 S
  104. //无检验写SPI FLASH
    + w7 A( _3 ^; l
  105. //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!7 W! ~; Q. ?* e& V# b+ N
  106. //具有自动换页功能 * Z4 J" ^. Q" H0 d& ~/ G( ~. q
  107. //在指定地址开始写入指定长度的数据,但是要确保地址不越界!9 _: Q% B6 l# I& x+ c2 o
  108. //pBuffer:数据存储区7 T, O. h, B6 r% L/ x2 Y
  109. //WriteAddr:开始写入的地址(24bit)! W5 l( H" {4 u1 D" Z/ `+ L8 u3 G
  110. //NumByteToWrite:要写入的字节数(最大65535)
    ; Q2 }$ ]0 \; P; {3 b
  111. //CHECK OK
    ' ?, E( X& z) |. h4 g1 }0 s
  112. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   ) b+ v" W5 q0 V) f
  113. {                                          
    - S3 s* c6 I% R
  114.         u16 pageremain;           $ s7 F% k1 m# y6 I- a+ R
  115.         pageremain=256-WriteAddr%256; //单页剩余的字节数                             
    $ Q# o5 \) z; K* K) p
  116.         if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节
    # _2 T2 ]: y7 b, o4 |- f
  117.         while(1)
    % s! Y* ~/ y! K; [8 l; x  B1 a: b
  118.         {           % V. U! j$ L+ C3 w2 ]. w
  119.                 W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);) h9 T+ ?- f3 O( q$ `& O" r' A$ m
  120.                 if(NumByteToWrite==pageremain)break;//写入结束了
    . G: I2 @9 l* _3 L6 O
  121.                  else //NumByteToWrite>pageremain8 ^- S) r7 Z9 Q. V" H- m. ?
  122.                 {
    % i1 h8 W" A# u1 I% m& ~
  123.                         pBuffer+=pageremain;
    ' Y+ l( b: ~+ B
  124.                         WriteAddr+=pageremain;        
    ' K& t2 h3 z7 K# O& u) Q! c% \

  125. " v% R3 ]& Z9 ?. k
  126.                         NumByteToWrite-=pageremain;                          //减去已经写入了的字节数
    0 X6 K" O2 {, l" I7 t
  127.                         if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
    . h  Q( b" Y, N% x  _/ F2 M& J# a
  128.                         else pageremain=NumByteToWrite;           //不够256个字节了
      x2 b7 W- H9 }: r
  129.                 }
    0 f1 V! }& X' \9 U" X  [, Z# ?- }
  130.         };            9 Y; r! f6 [2 ^6 ?' d4 U$ n, F
  131. } ; d3 n8 D* i: Q: }
  132. //写SPI FLASH  ; v. g  [: O+ ?; h
  133. //在指定地址开始写入指定长度的数据
    & j% m: @- v% z" D, Y
  134. //该函数带擦除操作!
    + s, O# s6 o* I* e8 u% x
  135. //pBuffer:数据存储区
    - T, N$ i1 w# h1 r
  136. //WriteAddr:开始写入的地址(24bit)                                                
      N) Y1 c- g1 \% o
  137. //NumByteToWrite:要写入的字节数(最大65535)   
    1 M6 |" V4 j1 L' y  h# I
  138. u8 W25QXX_BUFFER[4096];                 
    7 d/ B1 Q- s+ t9 E9 |
  139. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   1 s2 A, T5 C6 I' ?0 S  E, H
  140. {
    6 g8 x$ b% w/ n& p  ~
  141.         u32 secpos;
    3 o. E1 N6 e4 p1 l- X8 H' v
  142.         u16 secoff;" f# \% }6 X  A- @9 i  k6 U
  143.         u16 secremain;           
    . @2 E5 L$ g( O! o8 ?! z4 O) P  I
  144.          u16 i;   
    , y: L- O6 G; U# U/ ?) D* X+ A
  145.         u8 *W25QXX_BUF;            `, p  Q% x' ~! ]; |" \* T
  146.         
    ' l( I3 K7 y+ y2 P3 P( j: D
  147.            W25QXX_BUF=W25QXX_BUFFER;0 V2 B9 V) S" l3 N# V! U0 X9 X6 }
  148. //        5 L9 E$ e5 i  _
  149. //    W25QXX_BUF = (u8 *)_mem.Alloc(4096);//申请一个扇区大小的内存1 U0 @6 E$ B  P
  150. //    if(W25QXX_BUF==NULL)        return;
    ! {7 W$ b# W" U9 }- e
  151.         . z* N/ T% O. c4 ]
  152.          secpos=WriteAddr/4096;//扇区地址  
    3 s$ Q$ r1 d$ h% A1 {% z7 s* T( b
  153.         secoff=WriteAddr%4096;//在扇区内的偏移, ^+ J6 o% |7 h+ V4 {
  154.         secremain=4096-secoff;//扇区剩余空间大小  
    ( \* J% x$ Q( S. o

  155. 5 M) I4 }; c" }, r8 }% ^" X
  156.          //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
    + I$ v; l) Q  R) p! \* i# r6 Z
  157.          if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节* p) [4 w" _* f* Q
  158.         while(1)
    9 A  k9 }) S  T; G
  159.         {        
    ) m2 e  j$ J0 Q3 H  \: W
  160.                 W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容  r: W, K8 F# z6 y6 x& S" }
  161.                 for(i=0;i<secremain;i++)//校验数据
    5 c3 ~' ?) j" s
  162.                 {1 d" {! F3 g1 o1 M5 v# m
  163.                         if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除              f2 `! K6 K) V! C7 W) ^
  164.                 }9 O, U/ X9 B3 y
  165.                 if(i<secremain)//需要擦除$ l$ a9 G1 z) X. @
  166.                 {; W2 n1 b& u0 Z, I5 J' G" F( t
  167.                         W25QXX_Erase_Sector(secpos);                //擦除这个扇区) Z6 V2 i7 _6 u+ {# ^7 l
  168.                         for(i=0;i<secremain;i++)                           //复制0 U1 n& S4 X& P- n% q+ W& S+ z
  169.                         {" g& w  Z* d5 L4 g/ R- v
  170.                                 W25QXX_BUF[i+secoff]=pBuffer;          9 x5 g9 L+ z9 A; ^  h9 g! d
  171.                         }- N" }# Z7 f  J% Z
  172.                         W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  3 \) L9 \  r" ?

  173. + q9 m; O7 B- p9 Q' L) o
  174.                 }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.                                    # `# u* i- m1 A; f0 c
  175.                 if(NumByteToWrite==secremain)break;//写入结束了- W. u; n& s0 }4 p5 g) e6 [' V$ x  U
  176.                 else//写入未结束
    1 o  ]: @. w9 I# }4 a
  177.                 {
      j) C& ?' Q$ |+ I
  178.                         secpos++;//扇区地址增1! p( T/ y. B2 C- E
  179.                         secoff=0;//偏移位置为0          * \' w! ~# c3 ]
  180. ) ?# F# k% V$ F' k9 Q+ K1 g
  181.                            pBuffer+=secremain;                                  //指针偏移
    0 i+ T; z! o+ E2 x; O+ e
  182.                         WriteAddr+=secremain;                                //写地址偏移           
    8 X- w6 O) _; J  F) Y. B3 [
  183.                            NumByteToWrite-=secremain;                        //字节数递减
    * R2 Z. c; p0 J
  184.                         if(NumByteToWrite>4096)secremain=4096;//下一个扇区还是写不完$ q9 f* u( w; [/ r! r5 e
  185.                         else secremain=NumByteToWrite;                //下一个扇区可以写完了$ K& x8 |2 M3 n1 K, X
  186.                 }         
    & O3 P) M6 D" d0 D$ ~. _
  187.         }4 Y# y4 D- k: d4 R
  188. //        _mem.Free(W25QXX_BUF);
    0 I* |8 @2 h" W: g, y" i
  189. }
    . j" ]* G9 s" I  b/ Y2 e5 N
  190. //擦除整个芯片                  
    9 \) G( k2 Z9 q+ d$ S# I7 o: I& d0 u
  191. //等待时间超长...
    4 ]& |6 s. Y9 x3 {' {$ ?2 J" j+ i
  192. void W25QXX_Erase_Chip(void)   
    # I3 x- h/ G5 {% [
  193. {                                   * t% W( P+ H5 I: z0 o
  194.     W25QXX_Write_Enable();                                  //SET WEL
    % w- \) z7 o2 M. j
  195.     W25QXX_Wait_Busy();   , H7 K# S; ]* {% l- s
  196.           W25QXX_CS_L();                                    //使能器件   1 Z# S# z2 i% M7 ?4 s/ B
  197.     SPI2_ReadWriteByte(W25X_ChipErase);                //发送片擦除命令  % `) \2 h+ h; E8 U1 d9 f4 B
  198.         W25QXX_CS_H();                                    //取消片选                  
    - P; V& V. l- t9 L
  199.         W25QXX_Wait_Busy();                                                      //等待芯片擦除结束  P, q, d: f0 q0 A2 k7 X. c
  200. }   5 G3 H" G; O3 ^* ^: q: J& i+ M: m1 Q) M
  201. //擦除一个扇区6 F) V+ L; X* g8 Y
  202. //Dst_Addr:扇区地址 根据实际容量设置+ g( u( i. \- j) e, m$ {
  203. //擦除一个山区的最少时间:150ms4 l2 U) y/ M9 D4 k( Z. s' E
  204. void W25QXX_Erase_Sector(u32 Dst_Addr)   1 Z! e, W. T3 `5 z9 R, z5 [
  205. {  ; G2 c$ E- X7 b! M
  206.         //监视falsh擦除情况,测试用   ; r5 |' a. c: |& j; }6 A, ^" V
  207. //         printf("fe:%x\r\n",Dst_Addr);          . f; s1 Z% R/ i  b# h: Z7 d" l
  208.          Dst_Addr*=4096;, \4 f. C- g& S# F
  209.     W25QXX_Write_Enable();                          //SET WEL         
    8 @2 Z+ _+ e, T0 h
  210.     W25QXX_Wait_Busy();   
    , ?) b7 a) \' o6 b
  211.           W25QXX_CS_L();                                    //使能器件   ' k) r! O- w, c4 u  Q" Z
  212.     SPI2_ReadWriteByte(W25X_SectorErase);              //发送扇区擦除指令 . b. |) y9 q% Y8 C, k: Q8 i
  213.     SPI2_ReadWriteByte((u8)((Dst_Addr)>>16));          //发送24bit地址   
    8 {2 U- H7 }/ |* F3 |8 K
  214.     SPI2_ReadWriteByte((u8)((Dst_Addr)>>8));   ; Z% t9 z4 ?. A4 ]: K
  215.     SPI2_ReadWriteByte((u8)Dst_Addr);  1 ^1 z- Q% g  }8 B' K3 ^8 V
  216.         W25QXX_CS_H();                                    //取消片选                   ( k- t5 w* u7 w- s4 t
  217.     W25QXX_Wait_Busy();                                                      //等待擦除完成/ n8 }" z7 i3 U4 F) ]
  218. }  ; E: S! X; s- f9 |! [- S* Z
  219. //等待空闲7 J- e5 c& j0 J2 k' E* F
  220. void W25QXX_Wait_Busy(void)   ' X4 t" j% a! v5 _& R; p2 k
  221. {   9 V" Z, d: H; b/ k9 [
  222.         while((W25QXX_ReadSR()&0x01)==0x01);                  // 等待BUSY位清空
    % u0 k, `. O! l- {# Z
  223. }  
    * j/ f% R# f' x3 m
  224. //进入掉电模式
    3 x. m3 y5 t8 M! P1 Z
  225. void W25QXX_PowerDown(void)   # H) b/ q+ ]8 d1 v
  226. {
    1 c2 `. v' E0 \4 m8 M) c% h1 H
  227.           W25QXX_CS_L();                                            //使能器件   8 i5 E/ R$ u. B. e0 U# B
  228.     SPI2_ReadWriteByte(W25X_PowerDown);        //发送掉电命令  
    . J" m0 \% ~1 F/ I1 [3 V5 ]
  229.         W25QXX_CS_H();                                    //取消片选                     o3 C0 R0 M/ ], z' H' x
  230.     Delay_us(3);                               //等待TPD  3 \6 r0 t% A# G! E3 f
  231. }   
    / |$ C- a: }2 F7 o" D& A7 N
  232. //唤醒% C/ p+ O5 k3 S1 {# e9 v
  233. void W25QXX_WAKEUP(void)   1 Y" }0 g2 ^# Z2 ]3 r) I. L' |
  234. {  
    / v0 l2 ^, x! e( g2 n
  235.           W25QXX_CS_L();                                    //使能器件   6 z, s2 A# g' {' b
  236.     SPI2_ReadWriteByte(W25X_ReleasePowerDown);        //  send W25X_PowerDown command 0xAB   
    * @; [! K2 X, Z- w5 K9 L6 {. I1 c8 e
  237.         W25QXX_CS_H();                                    //取消片选                  
    ' W. |! h. ^! s. `+ Z/ C
  238.     Delay_us(3);                                    //等待TRES1
    ' r1 m- ]0 U7 y4 p- R
  239. }   
复制代码

2 y& U- f" |- [- |$ k8 OH文件
& S' `$ h$ V* E! l% s, m- j. \4 m4 V, G; W
  1. #ifndef __FY_W25QXX_H
    ; v. l1 Z8 k1 L% Q' D
  2. #define __FY_W25QXX_H                            / p3 Q6 h* `& J# K
  3. + f- k) z3 w' Z
  4. #include "fy_includes.h"
    1 E# }5 _. f$ h5 a$ `7 O% z  U' K
  5. % P0 u$ P$ K9 `' G& X0 S
  6. //W25QXX对应唯一识别ID
    + _3 `! P* w! ]5 ~
  7. #define W25Q80         0XEF13- m, \1 G5 _1 F% R  i4 u/ Y
  8. #define W25Q16         0XEF14
    ' s+ l/ G6 Q# c* U1 J$ y
  9. #define W25Q32         0XEF15
    9 [3 f  c" j5 Y. S4 m1 v
  10. #define W25Q64         0XEF16: S, k, D: c) l
  11. #define W25Q128        0XEF17
    : b$ B! o; L! I' s6 L$ r) x5 t3 e. y

  12. 2 I/ x$ U, z* L' ^" I, i! s
  13. //指令表
    2 m5 O# g/ G9 V9 o2 t
  14. #define W25X_WriteEnable                0x06 / J3 A% ?; v/ P4 Q' B0 y
  15. #define W25X_WriteDisable                0x04 : p7 b& I6 X1 b1 P. m- `5 B
  16. #define W25X_ReadStatusReg                0x05 ) {  W% p( q* m- k+ ?+ t1 y( V$ l& P
  17. #define W25X_WriteStatusReg                0x01
    + N, H& i. T2 S8 O2 c; Z6 D& c
  18. #define W25X_ReadData                        0x03 / ~1 `- @+ y& Q8 H& ~) L- C
  19. #define W25X_FastReadData                0x0B
    : a. g7 [! ~9 W- A
  20. #define W25X_FastReadDual                0x3B
    * Z+ c) O0 Y6 @" h( y( A
  21. #define W25X_PageProgram                0x02 8 J( I! B9 f/ B8 k. c$ [0 k+ J
  22. #define W25X_BlockErase                        0xD8 - s& Z. w' S( ]7 ]( t
  23. #define W25X_SectorErase                0x20 & X! Q3 r/ ]/ u; j  d! {* O
  24. #define W25X_ChipErase                        0xC7
    & a' }  @$ w6 Q( b3 @, m& C% R
  25. #define W25X_PowerDown                        0xB9
    . {* D& ]4 z, E5 O6 |2 K
  26. #define W25X_ReleasePowerDown        0xAB 7 M& L, ]$ R4 ?- h) b: F6 M/ ]
  27. #define W25X_DeviceID                        0xAB . W( C  D( m9 p# A, q7 P
  28. #define W25X_ManufactDeviceID        0x90 + t5 e; S7 q/ A4 R; u! ?
  29. #define W25X_JedecDeviceID                0x9F
    4 M9 @& Y, ~1 g' g3 z9 j  u( I
  30. # K( x4 u" n' e* V
  31. void W25QXX_Configuration(void);/ A! _3 Y: b5 r* X
  32. u16  W25QXX_ReadID(void);                              //读取FLASH ID
    8 k' f2 ~( o. }- D3 P9 w' {
  33. u8         W25QXX_ReadSR(void);                        //读取状态寄存器 8 P  x  g2 }6 r4 u$ j7 j
  34. void W25QXX_Write_SR(u8 sr);                          //写状态寄存器
    % k* Y' b; b0 f
  35. void W25QXX_Write_Enable(void);                  //写使能 - O. [/ W6 `* r3 n7 ~! D
  36. void W25QXX_Write_Disable(void);                //写保护
    5 C  Y0 W) q7 K4 F( W* V
  37. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);4 n' N. f" T' e% B
  38. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //读取flash
    + U% g9 {7 W: x% C& S; V: B2 H
  39. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写入flash
    2 j& d- l, n8 U5 t' m9 D
  40. void W25QXX_Erase_Chip(void);                      //整片擦除
    + `: w. f+ S8 x" e1 p
  41. void W25QXX_Erase_Sector(u32 Dst_Addr);        //扇区擦除/ [! ]; C( l4 g/ \3 b
  42. void W25QXX_Wait_Busy(void);                   //等待空闲
    ( A& K" d% J6 K1 `' y  i9 }
  43. void W25QXX_PowerDown(void);                //进入掉电模式
    . o0 W: P6 L  [  M! ]
  44. void W25QXX_WAKEUP(void);                                //唤醒
    3 K, ~/ m* G# z+ w8 r  o  C6 [

  45. $ [# |8 U& r: }7 J) `* t# H
  46. extern u16 W25QXX_TYPE;                                        //定义W25QXX芯片型号                  
    / w+ e7 w0 f  E' k

  47. ( }7 i; \2 y" _+ }) _  q# V
  48. #endif
复制代码

+ O7 `) l2 u. z( e/ {. q4 n基础工程如下:
: v3 {2 x# q2 L+ G
* E+ M" O' D" X CIG]SRYALJ}8)5NQ[MAA~BW.png
2 `1 \6 l9 A# b$ e' A4 E# G/ F% Z& P. r
第四步:USB代码移植3 g4 ]9 b9 I0 U- y, |
拷贝USB底层库到工程根目录,新建一个目录命名成USB,拷贝Mass_Storage例程下的src和inc目录下的所以文件到USB。
: N. \) a  I& Q" O
, i0 z9 t" c/ _* x8 E; C% G  u3 b 6Q]9Q9812B]OXMA8GNV[PCC.png ( h- v5 C1 r) @% c
* Z8 `( H9 v+ p: I# c8 [
拷贝过来是有37个文件,这个时候需要删除一些不必要的文件,因为官方的例程里面有nand,这里的话先删除以下文件
2 |& t* m) y9 y% D% ]( u2 I5 a" @5 @$ K$ H% ^2 V' p
VN1JDR_I@JO271YJKNV1(HE.png # V9 F  L0 E+ c+ _7 ^
/ i( J/ f# l, I3 l6 ^8 M) T9 D
4.打开工程把分组和文件及包含路径添加进去,这里就不细说了,结果如下
$ n: B9 L( X  `
, X* a% V8 [/ k$ V. L X$DZH7HHYEC}266_`72~4EH.png
: a4 s/ p5 ~2 Z9 ?: U, u5 |5 \6 v6 M6 O4 i( u; f& |, k' F# z9 @$ ?
打开usb下面的main和it 把重要的代码先拷贝出来
- n. a1 z; S3 ]& G% r" g; h& C3 w5 V$ A# |$ m, ~+ e
OA]E5J8OQ0[B{UJV@KXPUG2.png
2 E3 O1 ~1 o8 r
; i  W2 P; s+ Q4 ?& I1 |" q' [) I把main和stm32_it里面的内容拷贝到自己的main里面进行下封装去掉不要的保留剩下的,然后就删除it和main三个文件。8 w. r5 r5 m: R: T2 K
! g2 Z' [( g3 v& s
@G5L9U9`9]${JVP}AD@KN0E.png 8 [8 _3 ?* e8 L8 F* Z1 r0 |# ~. ^# A

( W2 z$ h) z' a% D然后编译下,这个时候会出现一堆报错,需要慢慢一步步搞定,编译前注意路径包含,' E1 p8 H6 n4 Y' e' |4 P0 ~

* g. I6 s+ @# w# j; J" C& T$ E [~3UDXP_99@A_@~XV14BNPB.png , H9 w: O6 s) Q( `3 x; s& C
7 B; S: I& J0 a2 r. Y$ X
定位到第一个错误进去: L- p+ W9 p0 n! K/ ?

+ F: ~' r* ?& d! R 5L28CK8A4)MY@OJR7KK~KOY.png % a" r: ?$ {: L7 X

2 }: V0 O; D7 m$ {( |8 b4 Z这个定义的是官方的开发板 我们直接修改成自己的公共文件就可以,并且注释掉开发板定义1 Z- O2 }6 [) |# A& S5 d7 `
% H% s1 `: K. w. J
TR]6[QMF5`A7X$MYZIL])9I.png
$ D9 R) ~; `% _3 _8 y1 X
2 f6 J: [) C0 Y公共文件如下,并且添加usb部分的头文件1 [- v7 N9 Q3 Y3 G& s
$ S, [$ |" e# V2 L2 c: `
Z7Z6YX`QZR}6~KCC_GDDLD8.png . R- K. c# b/ w! K! i$ x
  Z, c* V  V7 I5 D* S: ~. @
继续编译,定位第一个错误,这部分主要修改hw_config文件( t; @9 N0 [& B+ c! [, {  p

: E9 \6 ]1 D3 F6 f( Q# J _9RUE5O1M][P)IRX]S2S_$D.png
: V* _3 N8 g+ j4 G5 h8 Q# L1 S( I5 n- l7 b, S2 M' {% d
删除Set_System函数; i, m& r. A0 g. i% f4 _/ Y

. k. ^) P& U- @( ]. J) c- a$ z N~R76S188E[X(N6NID1WQ$V.png 8 R1 n* v( Q4 F

* N* H# S( R3 F! g& \删除Led_Config函数
3 v* q& L3 ^; B, n
! _5 b% T7 w7 p; a ~HA$Z23~@)XV2TEQ8CUTS.png
) [2 y% ^" @* B7 P, h2 k# V7 n$ n+ Q. q; y
注释LED相关的内容
0 d/ f8 ~! h6 R9 k$ M. g2 P6 `; \% ?+ U2 C) I# Y/ h) L
_FKQ$_}1}S8W%PG(2VT2]C7.png
/ b! t. z' j; T7 V3 E, e" M0 o8 E9 S" g( m  W2 E- Y
删除USB_Disconnect_Config
% b1 c0 t8 p+ P8 H
; i" ?" J8 _0 ]4 w1 _+ O 4K@_T@@XVZ@P]EEH37}QAJX.png * K4 T0 R% l5 [& J
4 t  ]& c: y6 d5 ?
接下来主要修改mass_mal部分,我这里是只有一个flash,所以我修改成如下:) H- S: ?/ i4 m" P/ p+ N
* W* R6 t# v# }  B) V/ V
2_1P4[7IG68PG%9TS@41BI4.png
7 l( A# ^5 }& {( ~6 ~1 ?8 l9 y
' l0 a8 I$ G# M  u) C  N  V; j; f头文件
# L, y: o9 S0 G2 G: j/ B( T* L7 o4 e7 N$ _; y
(V0@{X{W8~L[2Z[Y7)VNU.png " f# Y, W. M- g7 ~. L; M
* _3 U; |( V0 l7 l2 Z
下面修改memory文件,主要修改变量定义,C文件和H文件分别如下
4 v, W* m1 r  j7 X
' J. O' O# c) T& s( x1 t7 ]! y2 e3 c' @" } T]Y~~F2{V(H`V{$X(}FMNNJ.png
' @" Y' a0 e5 o6 w$ @* V' M% u# y# X
接下来是usb_scsi部分的变量定义, g+ ?* v2 [2 M4 X. |2 @$ h0 O2 i

0 _$ v; `/ }( o) ?( w! C R$Z1OIMFO6JWKJE~XX[{2$B.png ' ^2 B6 Z. Y5 V

( V" W% ^2 e% N这时候再次编译就发现没有错误了,但是这个地方USB中断部分还需要配置下,官方用的宏定义方式去实现不同的代码,这里一开始就去掉了宏,所以最终修改如下:$ A8 c/ f* P6 B7 f/ I
9 E0 C5 g) X$ u; y- }' ]+ ?2 i
  1. /*******************************************************************************8 u9 `# e- ]* z3 f
  2. * Function Name  : USB_Interrupts_Config) \* T) K8 L% x3 \
  3. * Description    : Configures the USB interrupts
    & s, G7 M- i, ~( t+ ^# ^- B- {
  4. * Input          : None.
    ( w0 j9 o1 @; V- Q4 \/ e
  5. * Return         : None.
    6 k( s" v6 T* n& m5 d
  6. *******************************************************************************/
    % ^7 v# I; d# h
  7. void USB_Interrupts_Config(void)3 ?, o8 t7 I/ ^5 J* v! ^$ _8 i! T" q$ C
  8. {
    ( l! W9 n1 E+ `- Z' I
  9.         
    0 i' X% v; b5 Y$ Y+ Y, w
  10.         NVIC_InitTypeDef NVIC_InitStructure;
    : l6 C& W, S0 [+ r- V! g- [
  11.         EXTI_InitTypeDef EXTI_InitStructure;
    9 a" Q6 D& ^! f4 o5 v6 X8 j' W8 W& `2 y
  12. $ D. ]0 y2 P8 W+ I* L& z, D
  13. ! m& o3 K9 p3 Q4 x) R
  14.         /* Configure the EXTI line 18 connected internally to the USB IP */. e! L) \( Y- Y  H: Y) y& @
  15.         EXTI_ClearITPendingBit(EXTI_Line18);
    8 d9 z! [) |% V, f5 ^
  16.                                                                                           //  开启线18上的中断
    ) m" C/ O* H' K1 E
  17.         EXTI_InitStructure.EXTI_Line = EXTI_Line18; // USB resume from suspend mode7 r% r: _3 i3 W
  18.         EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;        //line 18上事件上升降沿触发
    ( ^2 Y) G0 O1 D8 h' D5 m
  19.         EXTI_InitStructure.EXTI_LineCmd = ENABLE;6 K; |& G5 |) K$ b( l
  20.         EXTI_Init(&EXTI_InitStructure);          + X! x" a* P, N$ w4 G

  21. , [9 B/ t; Q& V' ]
  22.         /* Enable the USB interrupt */' P! e: J) u+ m7 A9 d4 v1 ?
  23.         NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;        //组2,优先级次之 * R3 l7 p4 D9 s) k
  24.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;% d. {! _, V) m- n/ C
  25.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  j; x+ P8 x: r
  26.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;9 r! O8 w. v+ E
  27.         NVIC_Init(&NVIC_InitStructure);9 n( g# G0 @8 `5 W2 c
  28.         
    3 A0 p/ `* [& W- n: z5 `
  29.         /* Enable the USB Wake-up interrupt */
    * S( O, q( t: I1 y
  30.         NVIC_InitStructure.NVIC_IRQChannel = USBWakeUp_IRQn;   //组2,优先级最高        , t* @( m9 P; c7 |" r
  31.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    ) C$ e7 |# G# N
  32.         NVIC_Init(&NVIC_InitStructure);  
    ; o2 Q& }% `1 t# m4 v0 n5 D
  33. 7 I0 m) l9 {2 Z6 [& m( E! L

  34. : j- {6 M( ]2 u9 c3 ?( }- k! q
  35. }
复制代码

; [3 e- W+ C& S# I  y' |* v$ I6 b再注释掉USB_Cable_Config的内容(DP上有1.5K上拉电阻)并修改中断函数/ u* q: Q& Z6 k9 y. @# D9 v1 B& q; E
3 Y7 v# y$ K7 o- f8 l
  1. //USB唤醒中断服务函数7 E6 j- I: t$ n2 n, d0 G7 s
  2. void USBWakeUp_IRQHandler(void) ; `; O/ a  [3 s* H% M' Q6 l5 T6 s
  3. {
    7 w9 E+ K) W' Y& T6 I% h
  4.         EXTI_ClearITPendingBit(EXTI_Line18);//清除USB唤醒中断挂起位1 r; R% G+ b4 @: m; h& [: A
  5. }
复制代码
9 p0 d$ m# y7 b" [
修改pwr的Suspend内容:
( d2 E9 h: U! G$ }9 ]; D+ ?* E5 ^8 y  _) s" ~0 t/ j; d( U- Y" @
  1. void Suspend(void)
    % z( {8 N* Y$ m: ^2 H
  2. {
    $ U) E( f9 ~' T& [$ C! u
  3.         uint32_t i =0;/ w+ Q+ Z( ~8 ?2 g/ o, W* l# x
  4.         uint16_t wCNTR;
    - s6 S( S) Y% V. W" w4 m6 E2 M
  5.         __IO uint32_t savePWR_CR=0;( A2 T. {3 k: U. k4 ]9 r
  6.         /* suspend preparation *// o1 H' I# E- g+ p
  7.         /* ... */
    3 {& q9 Z/ j5 z* m3 g) a
  8.         
      W  W+ s0 @3 G6 D3 X5 o; ?
  9.         /*Store CNTR value */" k* Y6 [* A; f  _3 }
  10.         wCNTR = _GetCNTR();  
    9 b5 ~7 ~- ]* F3 X4 @; u
  11. % M8 T9 j8 F- X% b# d
  12.     /* This a sequence to apply a force RESET to handle a robustness case */, f" w6 L% H4 f+ J7 U' d

  13. 0 A+ C- G+ _# u5 d: \5 v1 r3 W3 ^
  14.         /*Store endpoints registers status */
    : ]( B7 R$ y  a7 O/ N8 e* v
  15.     for (i=0;i<8;i++) EP = _GetENDPOINT(i);
    & J( d5 f' i, }3 e- O2 r
  16.         
      B+ |8 I7 t: U9 J6 f
  17.         /* unmask RESET flag */
    4 Z) b% E" q6 ^4 s3 d  J
  18.         wCNTR|=CNTR_RESETM;
    : K* }; j/ n) m6 [, L$ H
  19.         _SetCNTR(wCNTR);
    3 h7 L4 F! Y% R8 U( x6 S
  20.         
    6 P" O( X( |' |
  21.         /*apply FRES */
    & E2 Y# ^0 _+ A  S
  22.         wCNTR|=CNTR_FRES;4 ^) O% k  ^+ N& P3 _9 S
  23.         _SetCNTR(wCNTR);6 E2 K8 N1 l9 T
  24.         9 h' g7 V* f7 a' j) ~$ Y
  25.         /*clear FRES*/. ~7 C5 I1 d% z) d9 U# o2 G5 e
  26.         wCNTR&=~CNTR_FRES;0 \4 G1 F! W) V. `
  27.         _SetCNTR(wCNTR);
    % U7 l9 t6 |, u6 A  E8 v+ g; u2 y9 }
  28.         
      g/ D7 C- I  `& e
  29.         /*poll for RESET flag in ISTR*/
    9 P& b1 @" g$ ?% R/ k
  30.         while((_GetISTR()&ISTR_RESET) == 0);
    : N0 C0 ~! C; _7 x% P2 [" u1 I
  31.         6 O& ^* t2 j. l2 F" S% U  w
  32.         /* clear RESET flag in ISTR */" f  o" o; z9 S2 D1 ?* ]
  33.         _SetISTR((uint16_t)CLR_RESET);/ X$ g7 l2 N; S* G
  34.         # R- s) v# o. ~; R  I( F
  35.         /*restore Enpoints*/0 @, b7 m# }1 Q1 ?# i
  36.         for (i=0;i<8;i++)
    ' j8 L7 ?  u% T
  37.         _SetENDPOINT(i, EP);
    ( e$ a5 m3 W) z
  38.         % O' [+ Q! I8 s4 n
  39.         /* Now it is safe to enter macrocell in suspend mode */
    ; M2 n% e0 m1 e7 D
  40.         wCNTR |= CNTR_FSUSP;# K* _* S& o; E; M. U) _6 _
  41.         _SetCNTR(wCNTR);" E6 _9 f' A5 N5 f" i. |
  42.         
    8 d% c$ ^6 _5 }6 |" }! s
  43.         /* force low-power mode in the macrocell */3 a1 ]6 {' z5 M
  44.         wCNTR = _GetCNTR();
    . ?1 r) E! o! N1 A$ S1 h
  45.         wCNTR |= CNTR_LPMODE;! g5 C) L; s  S$ H; i
  46.         _SetCNTR(wCNTR);- U9 \9 L. ?( o% ~3 {/ d% U
  47.         : V2 s7 X0 P7 _' q
  48.         Enter_LowPowerMode();
    7 a0 `) Y% w; \6 y- [6 ~; u
  49. }
    9 f; h0 {  R8 R* M3 u
复制代码

7 C( |' @& w  ?4 V. T  d把USB初始化部分不要的注释掉:
3 T- a$ x  ?* c" y. Z+ W5 O) s. V9 B- S- U4 E* T# @9 O5 D
  1. void USB_MSC_Configuration(void)
    7 N( L9 W* N$ U$ v8 m
  2. {$ N3 E, o& B6 x/ E
  3. //  Set_System();
    % q6 D( i. U2 a% N/ i( E
  4.   Set_USBClock();$ k: t5 W* F$ J6 x4 r3 e$ L4 Z
  5. //  Led_Config();! t+ ^6 u0 l$ F" n9 V# D- `. Z
  6.   USB_Interrupts_Config();: _1 `: B0 k0 s8 U( s
  7.   USB_Init();
    1 W/ D. Y: [) U- A! L4 f
  8. //  while (bDeviceState != CONFIGURED);
    ( g/ s! n7 c7 U; M9 _; G% W& y1 Y
  9. 8 l7 A; a: u2 l7 }( D
  10. //  USB_Configured_LED();  H2 }/ H0 |! O) z( M8 D
  11. 9 P+ M( a* e" p
  12. //  while (1)1 R% E4 h: ~2 w0 t% u- r  a
  13. //  {}0 `3 y$ N+ ^. b: M: j- j$ g
  14. }
复制代码

' {. W7 h3 g/ n% {- [1 t9 V最后在USB初始化之前给U盘定义的数组赋值,不然就只有个盘符
# S9 q9 e( }  K, Z
. j. `( i% d$ F- L3 @8 m: T, ]
  1.     Mass_Memory_Size[0]=1024*1024*4;        //w25q64->8M 给4M做U盘
    0 p4 _3 g9 i4 W% e; A5 N
  2.     Mass_Block_Size[0] =512;                        //设置SPI FLASH的操作扇区大小为512
      E; E4 Z' v* M8 ~; @  W# a) E
  3.     Mass_Block_Count[0]=Mass_Memory_Size[0]/Mass_Block_Size[0];
    3 X! [  |9 _* q4 g, N0 D- B
  4. 3 ]& n5 v1 u( R& a0 Y
  5.         USB_MSC_Configuration();
复制代码
  G/ y- \. A5 v# b  E' b% R
SEGUXF{Z3MWXPLSF5KTU9.png
$ {  O/ `2 a, r+ _% f0 L: Z( }: v7 g' [" H* ]& I) J2 X# u
编译下载后就可以正常运行了。
! c7 [) X2 f4 ^3 ]; I; x( ~( W$ Y0 }2 X7 X; a8 X
_IXXNOSIUI%KX_{0NPN2T%2.png ( |: Y  x# H5 Q" B  B6 x8 D. t

. O1 y- B5 f( @! m$ N: z传输文件、新建文件夹都可以了,就是传输速度感人。
  }! r( F& j) X9 a% s
: W0 F7 V% }) |3 {  D7 ]以上就是USB虚拟U盘的所有内容了,内容太多而且复杂,所以如果真的想亲手试试,还是要点耐心的。虽然能学到的并不是很多,这里主要可以干茶mass_mal部分,可以学到不少东西,方便以后的熟练运用。我这里也是为以后更进一步的开发做铺垫。6 g  _+ v6 {, C- c

$ ]/ L! J# i5 l4 {% M( _  T9 _$ W
% C$ y% M$ p3 V1 R) C) g3 V# P7 D$ ~2 Q. `3 A+ M) m
收藏 评论0 发布时间:2022-3-16 11:16

举报

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