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

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

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

! Q2 r. @' }, U! YUSB基础知识
+ Y4 e! L$ y% p! j8 Q# EUSB按接口类型分
2 v+ ^% K' R: N+ q# F
控制器/主机(controller/host)& k8 y# r1 e. e: U5 j
& X: n1 O. g" b8 V2 c
设备(peripheral), h) o# k7 B- f& `1 o% w# l! |

& t7 \$ T0 J+ o7 K( m* cOTG(on-the-go),通过id线确定作为主机还是作为设备
7 s8 x4 Q* B- `$ T. N
8 q. ~1 A2 \2 d) ]8 C8 T6 X2 ~按照USB速度分
: t+ G% p; j# A/ g6 c
3 N, u3 o& x- h低速(low speed)4 Q1 a  O9 z! P" e+ f2 h

$ L# W6 s, p) K0 Y& @$ a全速(full speed)
4 \: q" q6 M6 k! ]5 S( h; N
& W# u& ]) `1 }; Y! D& d; i7 x高速(high speed)' ?" ~! ?5 x9 ~) C" ^5 |
0 Q, v3 Z' s+ b& \% [3 {
USB接口一般是4根线,VCC GND DM(D-) DP(D+)7 M, O4 d  D1 N( P$ @. `

6 q# [' F/ I. p4 A* k低速设备:在DM线上接入上拉
  M5 k+ [8 S/ v& q
' f* X9 i" `5 _4 Z全速设备:在DP线上接入上拉# q# Q- G) R. T0 Y1 R- [

' X: A) \1 _) D9 {6 |高速设备:在DP线上接入上拉,在主机对设备进行复位后进一步的确认& V: T% A5 i+ s8 {; j& t/ S
0 P' h/ z- [& _* {
& I% \+ E0 q7 b6 ^
关于描述符' Z  w7 {# |4 n' E
设备描述符(device description)
! F. m1 |1 \6 ]3 M' W
1 F# R( Q) N3 `9 t+ z配置描述符(config description)
* ^; F3 N. q7 W, r! _( r" P" d5 U! H! J9 M
接口描述符 (interface description)
  `5 H1 T/ U1 I5 |2 v7 N7 a3 b; S# b  y8 B
端点描述符 (endpoint description)9 H  {. h+ _6 @& _# `. W
, p8 j; G! t' n& `+ w6 X0 g
(HQG%R@V4W]{7LOY8DZE{L9.png
3 }1 N( O( v! a" ~4 E
" P7 f) ~; z% h" U) V; S设备的“身份”信息存储在描述符中。每个USB设备中都有如下描述符。需要注意的是一个USB设备只能有一个设备描述符,一个设备描述符可以有多个配置描述符,一个配置描述符可以有多个接口描述符,一个接口描述符可以包含多个端点。' i; m, r- F+ S8 L: m

- N8 C) R; t& n' ?关于传输

! }4 v6 W. M8 f4 q" H! y' P5 k1.在USB的通讯中,有传输(transfer),事务(Transaction),包(packet)三级。包是最基础的传输单元,与TCP/IP协议中的MAC层协议作用相同。
. Z0 K' o9 ?' [# s# s& d" ?. q2.在一次传输中,由多次事务组成,每次的事务又由多个包组成, [# `: w0 |/ Q6 g9 {" {  N
3.与众多协议相同,较高级别的协议的报文是基于/内嵌在低级协议的报文当中的,在USB中也不例外,例如,包中预留了DATA位,其目的就是填写报文/ _( }  _( }' v  @8 S( e/ G7 W
STM32F103芯片自带了有USB模块,可以用来做从设备,不能用作主机HOST。这里使用USB的目的是讲USB作为一个大容量,这个可以基于官方USB Mass_Storage例程来移植。这里就不过多的介绍里面复杂的通信原理了,要彻彻底底搞明白并灵活运用取来还是有很大难度的,因为内容太多了。这里仅仅介绍下移植过程和描述符相关的重点内容。
! J' ]# J1 S+ u; Z7 M
+ x4 {" x9 y5 p" S, ?- ^准备工作
; R) J/ s1 w( Z6 X1 Z% B. s第一步

/ g- u' l' a5 {# U确保自己的KEIL开发环境已经完善。
. b9 w5 a- X3 l  y6 F7 A1 H' p$ {6 ~8 K3 ]; Q) l+ d
第二步6 T+ O- H2 X. K0 P9 j; H
下载好了两份ST官方的库文件(我是基于标准库开发的),一个是标准外设库文件,一个是USB库文件。如下图:. |/ o+ O+ Y; k; S% A4 z* _+ p6 ?
7 R1 V% _* t1 m" q, P+ M1 }
AZ7H5GQWT4J%{N{6){9E]{3.png 5 {3 L7 g3 D* b+ g, l
5 H$ u; E  m( j, C9 z
第三步% t$ j( V" G& j! U, v; q2 _; }
建立一个带有存储介质驱动的STM32基础工程,存储介质常见有SD卡、外部FLASH芯片、内部的FLASH空间。的我是基于一个外部flash的工程去实现的,芯片具体型号是W25Q64,64Mbit的空间,换成字节就是8MByte。驱动部分如下:
$ Y: |0 _/ J, |
4 {5 J) E$ Y; L- }2 kC文件5 ]) x" a, A, |4 j- V
  1. #include "fy_w25qxx.h" 4 r2 y, Q# _4 [" r0 Q% w

  2. % r6 u! ~& z7 x2 Z- m
  3. u16 W25QXX_TYPE=0;//W25Q647 j1 x! z! m6 r0 ^8 j$ a

  4. 1 t( Q; m" H, j) D1 n! y" R
  5. //4Kbytes为一个Sector,16个扇区为1个Block,容量为16M字节,共有128个Block,4096个Sector
    # A8 E, T( A8 j+ G% N0 @1 I- Z
  6. //初始化SPI FLASH的IO口" q* i% w3 `* f2 z1 z& D: a) f
  7. void W25QXX_Configuration(void)
      j  _  L+ a! S- A" l! j6 }7 [9 X
  8. {        
    6 H% F9 E" q9 A8 _8 }* t( ?
  9.         SPI2_SetSpeed(SPI_BaudRatePrescaler_2);//设置为18M时钟,高速模式9 a& v" |' N+ i+ d
  10.         W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID.  2 E" W: q; Z* \3 J
  11. }         
    , c* z7 C$ n, Z7 x- q

  12. " ~: L" [% t% p! A3 r7 d
  13. //读取W25QXX的状态寄存器; i- ?- i8 M( r3 s) Y, U/ k$ R
  14. //BIT7  6   5   4   3   2   1   0. W, l9 G* K' M  \6 P0 A" r; O1 _
  15. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY
    # R- W9 U" P$ q
  16. //SPR:默认0,状态寄存器保护位,配合WP使用
    2 M2 [0 j" [, O4 X
  17. //TB,BP2,BP1,BP0:FLASH区域写保护设置3 J; y- `# L! B$ B# G- {
  18. //WEL:写使能锁定4 ]. N2 p) ^, Q' O+ H, W" j' a8 H
  19. //BUSY:忙标记位(1,忙;0,空闲)4 a7 i2 e/ o/ M: s
  20. //默认:0x008 l% ~( p0 N0 [) H' x8 Y
  21. u8 W25QXX_ReadSR(void)   ! n& s3 Y6 s7 v( q& f
  22. {  ! f- D. t& Q& A6 r! ], L
  23.         u8 byte=0;   $ v7 z3 r5 D# v, f% ]
  24.         W25QXX_CS_L();                            //使能器件   
    7 A# j/ S% ~% n
  25.         SPI2_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令   
    4 n2 M8 {: W' c. Z
  26.         byte=SPI2_ReadWriteByte(0Xff);          //读取一个字节  
    6 n  C" d+ n1 x8 O
  27.         W25QXX_CS_H();                            //取消片选     + N: r) B* O+ i/ _- X
  28.         return byte;   + ]# p% C1 q! P
  29. } 7 T3 @" u) t" `8 J/ f
  30. //写W25QXX状态寄存器
    3 s# O0 x  m$ F  Q/ M9 _
  31. //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!+ D& }& o4 }, {6 `3 c: H. k! B1 a
  32. void W25QXX_Write_SR(u8 sr)   & z2 z' E/ h( g3 H: P
  33. {   
    $ i  [+ @! J3 t. @
  34.         W25QXX_CS_L();                            //使能器件   
    ( [- z, M6 T" A+ s
  35.         SPI2_ReadWriteByte(W25X_WriteStatusReg);//发送写取状态寄存器命令      a* l! a5 z: }5 ]
  36.         SPI2_ReadWriteByte(sr);                       //写入一个字节  ; u  V* [/ ~0 l! X( _3 Z
  37.         W25QXX_CS_H();                            //取消片选                  
    - n/ r, O0 E6 T' |3 c8 U- w3 f9 R$ a
  38. }   
    4 O/ u+ F$ I2 a6 p  B5 W2 Q
  39. //W25QXX写使能        8 m# z3 U) h: [4 i3 d. W8 `& n( R
  40. //将WEL置位   
    % ?8 b. c% ~# l) ?8 x
  41. void W25QXX_Write_Enable(void)   
    ! |3 Q" e4 u8 D  y1 _# B2 S5 [
  42. {+ N8 b. t1 Y$ M) H1 f4 W" w
  43.         W25QXX_CS_L();                                  //使能器件   
    * H! K2 e2 C! f8 p7 d. ]
  44.     SPI2_ReadWriteByte(W25X_WriteEnable);         //发送写使能  
    9 r2 Z; Y9 F$ D9 t4 H# b) B1 K
  45.         W25QXX_CS_H();                                   //取消片选                  
    8 E3 f, j7 n+ U! n) Y. c8 Y  H+ Z; I
  46. }
    9 ?; p" Z) S2 q6 }  P8 ^' M
  47. //W25QXX写禁止        9 p4 V4 i0 G! n# d! A. O% R5 ?6 P" c
  48. //将WEL清零  " V  D8 T. [4 ~0 n
  49. void W25QXX_Write_Disable(void)     j7 F$ e& D* c
  50. {  . p* a# o8 m6 ^, I0 M
  51.         W25QXX_CS_L();                            //使能器件     I* D* f) y1 D/ v) G
  52.     SPI2_ReadWriteByte(W25X_WriteDisable);  //发送写禁止指令   
    . {% J  `; J2 T* s0 v* S% B
  53.         W25QXX_CS_H();                            //取消片选                   4 @0 C9 f  \8 d" W1 x2 y: M
  54. }                 
    / T- p: Z3 C) X6 Z- B
  55. 4 \, w' R2 Q0 S
  56. //读取芯片ID 5 C; X+ L5 r$ v; F. p0 s' Y$ b
  57. u16 W25QXX_ReadID(void)
    . g+ o/ U9 I$ j) o* S: n, E
  58. {
    ) N% H* O  w: s1 o9 z
  59.         u16 Temp = 0;          : S* h2 m% N5 `6 @* \
  60.         W25QXX_CS_L();                                    " p, o4 Y0 S) m; J/ A! O# a
  61.         SPI2_ReadWriteByte(0x90);//发送读取ID命令            
    : e4 [" a8 c; Y8 g
  62.         SPI2_ReadWriteByte(0x00);             : d9 u* b6 I. t- F7 O
  63.         SPI2_ReadWriteByte(0x00);             $ w7 }6 |( \8 n
  64.         SPI2_ReadWriteByte(0x00);                                    
    ' x% @8 S. p) c3 l& V' z+ z
  65.         Temp|=SPI2_ReadWriteByte(0xFF)<<8;  
    # E( L& [: {. B2 W
  66.         Temp|=SPI2_ReadWriteByte(0xFF);         
    ! G" F, l: j" Q% P! f" @* l
  67.         W25QXX_CS_H();                                    
    0 `& e7 z0 Y% ^
  68.         return Temp;
    # p6 H3 \  j" |) {) `' g8 w
  69. }                       4 M$ \; p2 r) z8 j0 ^
  70. % X* A# @6 m3 ~. a1 }& ]7 v8 h
  71. //在指定地址开始读取指定长度的数据
    - }; Z! f) A3 J# \$ o5 ^7 j
  72. //pBuffer:数据存储区,ReadAddr:开始读取的地址(24bit),NumByteToRead:要读取的字节数(最大65535)
    % t$ A5 T1 w3 F" T$ _; }' E
  73. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   4 u& r0 N2 \5 M0 w
  74. {
    ; h0 l+ H; @" G9 V0 p6 o
  75.          u16 i;                                                                                       
    / l! k- y' T; p
  76.         W25QXX_CS_L();                                    //使能器件   
    & S" m. }2 V1 F; l+ G' u& r& y
  77.         SPI2_ReadWriteByte(W25X_ReadData);                 //发送读取命令   ! ?7 e' s2 p- d' F, @
  78.         SPI2_ReadWriteByte((u8)((ReadAddr)>>16));          //发送24bit地址    + t* m# C+ y# F
  79.         SPI2_ReadWriteByte((u8)((ReadAddr)>>8));   
    : R- J3 T6 R% e3 n
  80.         SPI2_ReadWriteByte((u8)ReadAddr);   6 i8 S4 J* L4 _; f
  81.         for(i=0;i<NumByteToRead;i++)5 _. Z7 A6 ~$ ~9 G9 R8 F
  82.         {
    $ m8 n9 S+ K, h  ~: m; R8 \% d
  83.         pBuffer=SPI2_ReadWriteByte(0XFF);           //循环读数  
    6 d* Y% G  t9 I5 W4 ^& g- y
  84.   }% M0 X% }. r) w' W
  85.         W25QXX_CS_H();                                                   
    0 C1 c$ i4 M; Q5 W6 o
  86. }
    * |6 z$ E$ g8 o# J- b+ V
  87. / v. p6 |( s* X: i9 t& L3 p1 M
  88. //SPI在一页(0~65535)内写入少于256个字节的数据# }$ h# {$ U% I( W' O/ C
  89. //在指定地址开始写入最大256字节的数据5 E5 [& s! u3 V8 r- _
  90. //pBuffer:数据存储区,WriteAddr:开始写入的地址(24bit),NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!         ( C& O) b7 N8 h- d' M) Y- a
  91. void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
    8 a1 @7 U* |6 O+ \  K
  92. {
    ( r( ?  c4 Q, x0 W
  93.          u16 i;  
    ; b# T4 G7 m* a) z, `6 Y. e* `
  94.         W25QXX_Write_Enable();                          //SET WEL
    * o) v1 k- ]3 ^( \) `* i
  95.         W25QXX_CS_L();                                    //使能器件   5 g& v: B+ J* g
  96.         SPI2_ReadWriteByte(W25X_PageProgram);              //发送写页命令   
    : n& Q9 y$ u' s6 ^6 M$ x( H, n% y2 p
  97.         SPI2_ReadWriteByte((u8)((WriteAddr)>>16));         //发送24bit地址   
    * c, I, H9 q% F' f- p# Y
  98.         SPI2_ReadWriteByte((u8)((WriteAddr)>>8));   
    $ T7 d. G, ^! v5 ^4 _
  99.         SPI2_ReadWriteByte((u8)WriteAddr);   9 J9 h4 E( a9 e3 D' i4 R" q
  100.         for(i=0;i<NumByteToWrite;i++)SPI2_ReadWriteByte(pBuffer);//循环写数  % a% O" z! h9 K- w& e
  101.         W25QXX_CS_H();                                    //取消片选
    . m1 }) y8 _+ G
  102.         W25QXX_Wait_Busy();                                                           //等待写入结束
    - V# `1 e- M/ H
  103. } : ]  z3 E* @" d3 [
  104. //无检验写SPI FLASH 0 l7 j4 A- I3 _! o- }3 o! }6 k
  105. //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
    6 r' s1 m& F- M
  106. //具有自动换页功能 0 X: c. P# t4 Z6 a1 G
  107. //在指定地址开始写入指定长度的数据,但是要确保地址不越界!
    4 q' \2 v" Q5 v9 f# M* f# I
  108. //pBuffer:数据存储区& f2 ~' }7 k5 d; E( [0 Q5 n
  109. //WriteAddr:开始写入的地址(24bit)
    ! Q) _/ q. B' b) B  a
  110. //NumByteToWrite:要写入的字节数(最大65535)- |0 S3 d, s: E# j
  111. //CHECK OK6 t# W, ]9 U+ m1 D9 v4 ^
  112. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   / P; n2 t. j2 Y* q9 g
  113. {                                           8 I6 C& P9 }, m! Z, ?" v
  114.         u16 pageremain;           
    6 F3 _3 ]$ o  ?  V/ r- ^
  115.         pageremain=256-WriteAddr%256; //单页剩余的字节数                             
    8 G1 l, m+ m8 A5 c9 q4 M" P: Z
  116.         if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节. B+ v$ N2 D+ h5 Y+ M0 D
  117.         while(1)/ f% H$ d- q4 F5 {; I4 ?
  118.         {           
    ) I9 W( p8 _) c1 U& v
  119.                 W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);( S0 q  A9 U& u2 P
  120.                 if(NumByteToWrite==pageremain)break;//写入结束了0 g. p0 Q# _8 r" Y6 R( l
  121.                  else //NumByteToWrite>pageremain
    2 x- R: ?4 f# ^, W
  122.                 {
    " p5 D5 n( r! j3 L( I8 q$ M
  123.                         pBuffer+=pageremain;
    # _: z" I# R4 y' f  m
  124.                         WriteAddr+=pageremain;        + c8 `& ]% V% {9 O% `. U7 b
  125. 8 Q/ k$ E  w4 H- k8 m+ F
  126.                         NumByteToWrite-=pageremain;                          //减去已经写入了的字节数
    # M) z* J- x/ x9 f
  127.                         if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节; G8 {2 i; g1 o3 u
  128.                         else pageremain=NumByteToWrite;           //不够256个字节了
    6 i, @, A! ^- R# k2 v; C* |
  129.                 }
    ! n4 |  ]6 I+ e8 v
  130.         };            
    4 @8 i3 o, d) H+ P) f; Q, e7 W3 N
  131. } # g6 i7 k" z  @5 Y2 M: F
  132. //写SPI FLASH    T6 W: d  q: d( t+ R  X
  133. //在指定地址开始写入指定长度的数据
    ' x& F  n  J! x4 m5 U, D' x. W
  134. //该函数带擦除操作!
    2 s  X7 f$ ]+ r. Q; S9 @1 C
  135. //pBuffer:数据存储区
    . f3 c, J$ w5 }/ C, ?6 l! |
  136. //WriteAddr:开始写入的地址(24bit)                                                
    $ Y( Z$ w7 ?2 g! i8 a( P
  137. //NumByteToWrite:要写入的字节数(最大65535)   5 B9 t0 A  X/ r0 H" A8 q+ f+ m
  138. u8 W25QXX_BUFFER[4096];                   E8 Q0 \! L" [1 J9 g1 [
  139. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   ' |# c. R1 `# y- l3 v: f
  140. {
    * H! W' P' E6 V6 S! G: X
  141.         u32 secpos;
    , |/ K( {$ J! h; q
  142.         u16 secoff;
    4 K5 [8 U5 x% p2 h0 x7 d
  143.         u16 secremain;           1 n+ {. o  z- V
  144.          u16 i;   
    : D( W% Z) g: Y' N7 G, i3 d2 B
  145.         u8 *W25QXX_BUF;          0 o( y* `0 f, v5 p7 v6 H
  146.         
    - Q8 w5 d( A- \$ \# w* u' q
  147.            W25QXX_BUF=W25QXX_BUFFER;
    " q3 v) e$ Q' w3 o! L
  148. //        
    3 Z6 N- o/ G' Y! M; C
  149. //    W25QXX_BUF = (u8 *)_mem.Alloc(4096);//申请一个扇区大小的内存4 v- S% K- ?0 v8 P' G7 e  c
  150. //    if(W25QXX_BUF==NULL)        return;; C' V6 I5 k: ]* y1 \) ^( y. e
  151.         0 [. m0 J! D: L8 l" F: x2 H4 A7 ~2 Q
  152.          secpos=WriteAddr/4096;//扇区地址  
    , o$ g, k  Z+ ~9 Q! s; R
  153.         secoff=WriteAddr%4096;//在扇区内的偏移7 G/ n- I2 H# v& z. z
  154.         secremain=4096-secoff;//扇区剩余空间大小    @7 ~  y' U- a4 m9 H
  155. ) k! c  o6 ~' O9 Y1 n0 }; O: b
  156.          //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用, C6 `# s: k) V: y  L
  157.          if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节) p- ?9 w# f$ d' v! E
  158.         while(1) 7 u" A1 }2 c" Q3 M
  159.         {        2 `- l1 L8 ^% ~2 O4 c
  160.                 W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容
      d+ w$ ?) f, c
  161.                 for(i=0;i<secremain;i++)//校验数据
    & q0 h3 M; f) Q* c" i: m  I2 s- q
  162.                 {
    7 \9 h0 _% H9 G( ?1 L! U# z0 c2 q
  163.                         if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除            
    # ?; x1 O8 {( ?, K
  164.                 }
    " y  f, m8 T0 o, y' m4 m7 W- M, }0 E
  165.                 if(i<secremain)//需要擦除+ h1 W# _. j0 q/ Q/ W8 r3 }
  166.                 {
    $ w  C% h9 f( d* |0 m
  167.                         W25QXX_Erase_Sector(secpos);                //擦除这个扇区8 b" `( s+ P2 l  G
  168.                         for(i=0;i<secremain;i++)                           //复制/ t3 R! m  \$ z! }1 H% o
  169.                         {
    + S1 `! L" T, A2 k% R4 ?, a
  170.                                 W25QXX_BUF[i+secoff]=pBuffer;         
    4 |8 s4 `7 E9 l5 ?, a
  171.                         }2 p) M; }2 C4 T0 a
  172.                         W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  
    ' ?$ X5 F, c; u4 v) t; D& U
  173. ; u- J# @! ^# L2 e: ^- k* F3 s- L# Z
  174.                 }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.                                    5 ~8 W0 v; E# B
  175.                 if(NumByteToWrite==secremain)break;//写入结束了2 K$ H1 M) F4 e( `$ r
  176.                 else//写入未结束) w% w  P& t! `% N; U7 x$ i
  177.                 {. ?$ l% _) l5 U) C) s
  178.                         secpos++;//扇区地址增1
    ( \! z! X+ P  c: o
  179.                         secoff=0;//偏移位置为0         
    7 N" \/ O% j* D' ?  I0 n
  180. 1 @  Y4 [- u# p) L
  181.                            pBuffer+=secremain;                                  //指针偏移" w7 }! J" U7 J9 g4 M$ P& y: n9 \' B
  182.                         WriteAddr+=secremain;                                //写地址偏移           
      ^5 c% p* l$ x' j8 T) C
  183.                            NumByteToWrite-=secremain;                        //字节数递减
    5 i; D6 V+ Q+ n4 T# y4 d8 W- z
  184.                         if(NumByteToWrite>4096)secremain=4096;//下一个扇区还是写不完
    8 L: F( w9 x3 l
  185.                         else secremain=NumByteToWrite;                //下一个扇区可以写完了& I# |7 u' {! H& H/ ]
  186.                 }         
    9 ^- J& z2 r/ o& w
  187.         }
    6 v4 K& D8 C3 W2 l& Z
  188. //        _mem.Free(W25QXX_BUF);9 c. L/ n% f! d  _! A
  189. }# T) K; G  m7 O) S
  190. //擦除整个芯片                  
    8 t, _/ I, D9 e! Q& ?0 D
  191. //等待时间超长..., z' q4 N, [/ _: W# ^8 A
  192. void W25QXX_Erase_Chip(void)   
    # e: ]+ u% S/ Z0 a, I; F
  193. {                                   
    ( x  a& X6 K. D, @" X+ s  H  ?
  194.     W25QXX_Write_Enable();                                  //SET WEL ! @7 k$ A* {* D3 ~, ~
  195.     W25QXX_Wait_Busy();   % a% Q1 \" z% S( }
  196.           W25QXX_CS_L();                                    //使能器件   # b( z. h9 H$ H
  197.     SPI2_ReadWriteByte(W25X_ChipErase);                //发送片擦除命令  0 M7 P- U/ m% y! c. `
  198.         W25QXX_CS_H();                                    //取消片选                   & h' k" o  r% G$ `0 w' K# p. Q' x
  199.         W25QXX_Wait_Busy();                                                      //等待芯片擦除结束
    . I2 o8 V2 Z5 g, ]$ O* d% ]8 d5 J
  200. }   ) n6 n/ ?: Y" h; }
  201. //擦除一个扇区) J, _8 q& B5 g+ z/ p8 H2 T3 y
  202. //Dst_Addr:扇区地址 根据实际容量设置: {! {" h% M; `0 D
  203. //擦除一个山区的最少时间:150ms
    8 b& W- d8 `7 {
  204. void W25QXX_Erase_Sector(u32 Dst_Addr)   " M$ c8 U! E! j$ @; p2 N
  205. {  
    - N$ x9 k( h# D( O! W( h0 c
  206.         //监视falsh擦除情况,测试用   
    " K1 D( F# C$ r  x, \2 d
  207. //         printf("fe:%x\r\n",Dst_Addr);          , D8 `% X0 W$ W( p8 x, @
  208.          Dst_Addr*=4096;
    3 \! e! n1 O9 \, u! f
  209.     W25QXX_Write_Enable();                          //SET WEL         
    2 p  c. |  j$ Z/ Z$ g" t) }) v6 {
  210.     W25QXX_Wait_Busy();   
    : z6 M, S  N+ e
  211.           W25QXX_CS_L();                                    //使能器件   6 N: M( y0 w% A
  212.     SPI2_ReadWriteByte(W25X_SectorErase);              //发送扇区擦除指令
    * a! O4 f1 D; a6 K2 c
  213.     SPI2_ReadWriteByte((u8)((Dst_Addr)>>16));          //发送24bit地址    5 _0 a% Q2 J/ p- z) p0 S  g( D
  214.     SPI2_ReadWriteByte((u8)((Dst_Addr)>>8));   , U6 R( x" r4 D: Z: J7 K1 _4 N" Y; y
  215.     SPI2_ReadWriteByte((u8)Dst_Addr);  4 H  P6 g" ^. B! Q
  216.         W25QXX_CS_H();                                    //取消片选                  
    & A5 E7 R$ [7 b' D+ M, U4 Q8 w
  217.     W25QXX_Wait_Busy();                                                      //等待擦除完成
    6 J8 G% Q" I5 n% u8 V
  218. }  
    - m0 a; P1 ?0 Y% m  p/ n
  219. //等待空闲
      b# e4 I3 @+ d* [
  220. void W25QXX_Wait_Busy(void)   ' T! f/ Z8 n! o# ]- \
  221. {   
    . X8 D0 P: i4 Q
  222.         while((W25QXX_ReadSR()&0x01)==0x01);                  // 等待BUSY位清空" O/ D( x: V& f" X/ h
  223. }  
    9 C# r/ M' T  Z# W* F) }3 o
  224. //进入掉电模式
    7 n1 o0 J1 }- W6 q  i
  225. void W25QXX_PowerDown(void)   % q7 q4 U+ B; b5 Z: N( J7 i
  226. { ' u- @5 c$ f6 c; e$ b
  227.           W25QXX_CS_L();                                            //使能器件   / |, h1 l4 Q/ i1 y, u
  228.     SPI2_ReadWriteByte(W25X_PowerDown);        //发送掉电命令  ( I" ~# X! {# d
  229.         W25QXX_CS_H();                                    //取消片选                  
    / e4 P1 o) L* V% f7 F  F; ~, ~: [
  230.     Delay_us(3);                               //等待TPD  
    9 N" A) A* J! a: j
  231. }   
    ' F: e) v1 p" u# S  L
  232. //唤醒; r, w2 o5 S3 s& o( C7 q, ^
  233. void W25QXX_WAKEUP(void)   
    , S& |! _0 K8 F2 L
  234. {  ' X, d2 }+ ]) ]6 r, g  F
  235.           W25QXX_CS_L();                                    //使能器件   ( A- V1 Q/ C( b  Q; c! f
  236.     SPI2_ReadWriteByte(W25X_ReleasePowerDown);        //  send W25X_PowerDown command 0xAB   
    % w: J; E( H8 T+ p& e6 T
  237.         W25QXX_CS_H();                                    //取消片选                   5 E7 O+ B( |. {, P6 B9 m* K
  238.     Delay_us(3);                                    //等待TRES1
    7 Q5 E/ H) }+ n. Q1 y3 M
  239. }   
复制代码
2 ]3 u& m9 |* H* r4 b  Z
H文件% H  Z& q8 _% I! W
. ^7 C, k9 T! e+ ~4 @( z0 i- n. g8 c
  1. #ifndef __FY_W25QXX_H
    # k% v  g7 V8 m
  2. #define __FY_W25QXX_H                            - F2 [1 W, _! i0 t: T5 E1 b
  3. : C" L$ R( u; P/ V
  4. #include "fy_includes.h" ( Q$ s8 [+ ]7 S( F' m. B
  5. ; [; J. j5 L( o, Q: y! Z
  6. //W25QXX对应唯一识别ID; Z6 y# m, h1 ^
  7. #define W25Q80         0XEF13+ {+ a; w. @; R3 P" ~  v
  8. #define W25Q16         0XEF14
    ! V( I! _1 f4 `# d. h$ O' x$ c
  9. #define W25Q32         0XEF157 d, `8 U5 p7 Z* O3 J
  10. #define W25Q64         0XEF16
    . {# O% L$ Q6 B- ^  `
  11. #define W25Q128        0XEF179 }5 }" S/ }  r
  12. ( c7 A- F4 H7 B  q' A' J0 a
  13. //指令表
    9 r9 e& c7 h8 d, Q
  14. #define W25X_WriteEnable                0x06 ' v( F4 y6 r# B( E: A/ w3 ]( g7 x
  15. #define W25X_WriteDisable                0x04
    9 h% p8 G8 s( k9 v7 ?% Q: m
  16. #define W25X_ReadStatusReg                0x05
    4 A8 _) k( y4 c) {" C- T7 z
  17. #define W25X_WriteStatusReg                0x01
    9 U" Z/ m/ r" q" b
  18. #define W25X_ReadData                        0x03 0 B: x/ X% ^2 Q( l- w7 m8 i' G
  19. #define W25X_FastReadData                0x0B
    $ ]9 f6 W. l' Y* O
  20. #define W25X_FastReadDual                0x3B
    9 N1 S! e- p" G" t& z
  21. #define W25X_PageProgram                0x02 3 s+ l! Q- i) n! f
  22. #define W25X_BlockErase                        0xD8 / O0 `' s+ C1 I; @- n1 p
  23. #define W25X_SectorErase                0x20
    ! E# l& F6 e9 [: c# }
  24. #define W25X_ChipErase                        0xC7 ' `- l. r. U$ ?+ j
  25. #define W25X_PowerDown                        0xB9 / j: N# X: O/ g+ E: E. C
  26. #define W25X_ReleasePowerDown        0xAB 0 o3 s2 ?+ F) u1 r6 [8 b( e
  27. #define W25X_DeviceID                        0xAB
    : L! ^% P3 q; Z; _- s4 Z
  28. #define W25X_ManufactDeviceID        0x90
    5 n* |2 v! `- d; R5 @8 n
  29. #define W25X_JedecDeviceID                0x9F
    ) m# @$ O8 m0 V& _

  30. 2 a' v! b! v/ I, ^
  31. void W25QXX_Configuration(void);  O0 a7 H) w2 S$ V
  32. u16  W25QXX_ReadID(void);                              //读取FLASH ID4 ?& p- z( ~% V6 [; k( {  w
  33. u8         W25QXX_ReadSR(void);                        //读取状态寄存器 # M+ _: w. m$ s9 K* q$ n- }
  34. void W25QXX_Write_SR(u8 sr);                          //写状态寄存器- _/ [2 @$ E+ M/ E
  35. void W25QXX_Write_Enable(void);                  //写使能
    ) M4 R7 Y" \) p) v: z+ _
  36. void W25QXX_Write_Disable(void);                //写保护
    # H: o. g4 b) M1 S3 N
  37. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);8 f3 j: y( D8 [/ ~
  38. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //读取flash
    4 |9 n4 F1 J+ q. p3 ]
  39. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写入flash
    3 }; N3 U# J  b% J1 G2 V
  40. void W25QXX_Erase_Chip(void);                      //整片擦除/ o8 b- ~* Z% ~; B  c8 m+ y
  41. void W25QXX_Erase_Sector(u32 Dst_Addr);        //扇区擦除
    0 u  e% Z& Y, C; H
  42. void W25QXX_Wait_Busy(void);                   //等待空闲
    / q1 ^+ Y1 d; A
  43. void W25QXX_PowerDown(void);                //进入掉电模式  `- ~* x. b  H4 f$ M/ N
  44. void W25QXX_WAKEUP(void);                                //唤醒7 q0 f6 R+ K$ a& W" I$ F$ t

  45. 0 R% D6 R2 g5 ?6 r, @
  46. extern u16 W25QXX_TYPE;                                        //定义W25QXX芯片型号                  
    ; I+ L( B9 Q: T  i
  47. 5 i+ [6 X+ n8 o* N! E
  48. #endif
复制代码
/ ^. q& v% P+ v# E+ Y5 O% s
基础工程如下:
, u4 W4 Y. V; K  R5 n" Y! G( x# J. ^) {0 ^+ ]7 g! U
CIG]SRYALJ}8)5NQ[MAA~BW.png + r4 [. v* Q% g& }5 [3 \

3 b2 f* i5 g' Y/ m+ T9 \第四步:USB代码移植
  s! O8 l7 V) q2 a拷贝USB底层库到工程根目录,新建一个目录命名成USB,拷贝Mass_Storage例程下的src和inc目录下的所以文件到USB。
% d- S" J' r8 V% b
' E3 T7 z9 [% b3 K 6Q]9Q9812B]OXMA8GNV[PCC.png
0 V! H! b, g1 Z
  [  M* f! l% o* b% M( w- o, O拷贝过来是有37个文件,这个时候需要删除一些不必要的文件,因为官方的例程里面有nand,这里的话先删除以下文件( {3 t  t2 H" c

3 c" F5 a( Q" M2 @# i9 Y7 m+ Z3 ~$ V VN1JDR_I@JO271YJKNV1(HE.png . F: ?0 U( e4 {: C0 O

: E, W% K9 N, U4.打开工程把分组和文件及包含路径添加进去,这里就不细说了,结果如下
9 ~: ?  v; k" ^
. m9 ?3 f5 c+ p  y& h, ?( Q+ O X$DZH7HHYEC}266_`72~4EH.png 6 r- B9 M4 d! j
2 q$ |" n  X  U
打开usb下面的main和it 把重要的代码先拷贝出来7 O1 \2 O+ G8 U, b8 R

) \. i; w  ]. o9 K1 z$ N6 l, N OA]E5J8OQ0[B{UJV@KXPUG2.png
) i3 }  ~- m9 x5 p; d' e  L0 Y4 O* f+ U. b/ g% G2 C% O' h
把main和stm32_it里面的内容拷贝到自己的main里面进行下封装去掉不要的保留剩下的,然后就删除it和main三个文件。
, q5 o. j1 u; c; v8 J' V- l
9 x) R- A( N  f+ n @G5L9U9`9]${JVP}AD@KN0E.png
9 c" X* B' ~' w; H, W9 |2 a# o# R$ g+ ?: p% W% G# A4 q4 @; \
然后编译下,这个时候会出现一堆报错,需要慢慢一步步搞定,编译前注意路径包含,
5 X1 p7 }7 i( F! V% W9 @4 P* |5 N( c& w5 M- J6 |
[~3UDXP_99@A_@~XV14BNPB.png # T' |5 p, A; U0 L! k# ^2 s
5 e" L' H$ }3 w, ?+ d0 j
定位到第一个错误进去: J' D2 U/ D4 x5 }/ |4 L3 L. h

) M% z; C' B) @* d. H 5L28CK8A4)MY@OJR7KK~KOY.png   F- R. B1 {$ b+ [' ?! }$ ?' U
1 x, `2 M  C- j1 q
这个定义的是官方的开发板 我们直接修改成自己的公共文件就可以,并且注释掉开发板定义
: s( `# _+ X& P! C6 }7 M: @; S' [# z% q+ O# r& C8 t8 |; e( m7 I
TR]6[QMF5`A7X$MYZIL])9I.png
9 Y, F5 @: B: `& ^6 ?6 m; h
0 L% q  q- ~6 m$ C* i公共文件如下,并且添加usb部分的头文件7 z- ^# w5 F, O6 G* M/ K; D3 s5 X
* u, C' x6 k$ \  g5 v7 g  N
Z7Z6YX`QZR}6~KCC_GDDLD8.png . s: d; c4 p  a  _( Q6 U* a

5 g* l3 Z8 [+ K. \$ B* D/ b6 W继续编译,定位第一个错误,这部分主要修改hw_config文件2 R& H; \7 J  V
( D3 H# p- ?8 w
_9RUE5O1M][P)IRX]S2S_$D.png
  H  v" P- {" w9 b! H+ F5 J; L0 A. d/ T3 @$ ]& a( B
删除Set_System函数9 W( ?8 |3 R1 E' a' o3 O4 z& [
+ U: z" @$ C: r1 _
N~R76S188E[X(N6NID1WQ$V.png
+ r. J) t' d8 t. ?* W7 R) `
/ P2 |# \- E7 o# Y2 i" ]删除Led_Config函数* ]$ [) @3 D$ p3 x

9 j! [1 X& B! V& V* b ~HA$Z23~@)XV2TEQ8CUTS.png . ~6 k& N/ `, B0 z
( @# H% T6 j9 P7 E" O
注释LED相关的内容6 U# E* ^1 ~" Z

9 m3 B7 s5 j% D _FKQ$_}1}S8W%PG(2VT2]C7.png
/ A" }% l8 _8 V  x5 [* v8 M4 G( m" q9 L& c$ [' w
删除USB_Disconnect_Config
7 q8 U. e: s- y) @4 N8 s; R+ U6 b( U& }* R" {- ~4 v
4K@_T@@XVZ@P]EEH37}QAJX.png
7 H- |. z( R% w) k0 ?# a) F0 Q- T5 k" g4 w) i) h6 L& V9 O
接下来主要修改mass_mal部分,我这里是只有一个flash,所以我修改成如下:
  F2 ^  W" q" v9 o5 _$ i8 J. e- @" D
2_1P4[7IG68PG%9TS@41BI4.png ) P% q) i5 u7 C( i
) Z# w2 K  y! D7 q4 ]
头文件
% H! M" \* [! D& o( O( k% _9 c9 ?# M- n4 h# D
(V0@{X{W8~L[2Z[Y7)VNU.png
( l$ ]6 u4 A8 y% T' j8 F' g) P% N9 V; B0 h3 N
下面修改memory文件,主要修改变量定义,C文件和H文件分别如下
# L% |1 g* a, p; B: k; S, z  o
, ^0 o+ d) X0 q& t, K+ f T]Y~~F2{V(H`V{$X(}FMNNJ.png
6 y; P) i. ~, p2 Q9 i
" |( s+ t& s9 w* j8 D) w接下来是usb_scsi部分的变量定义
* x4 e5 _: k: u: Y# f" a# _- J/ x) o& [! G
R$Z1OIMFO6JWKJE~XX[{2$B.png * r; A1 Q9 E5 B1 a7 R

& ?0 K7 X4 f' J/ s& m7 A7 M这时候再次编译就发现没有错误了,但是这个地方USB中断部分还需要配置下,官方用的宏定义方式去实现不同的代码,这里一开始就去掉了宏,所以最终修改如下:
7 m5 F2 {7 x& V" B2 X) r# U
& N& ~0 i3 H5 _" V. @# p& p& }- }
  1. /*******************************************************************************9 M3 X% [+ H& ?3 K
  2. * Function Name  : USB_Interrupts_Config
    " B, ~  q; g8 P4 `2 o# i- G+ k
  3. * Description    : Configures the USB interrupts
    . c+ t  z% u$ G! x* J$ k3 f: R
  4. * Input          : None.# t5 T/ n3 e3 _% [$ V
  5. * Return         : None.
    : b% O2 ]7 d0 e- c" Z# z  C
  6. *******************************************************************************/
    8 e% L1 s7 g4 c% |, t
  7. void USB_Interrupts_Config(void)
    - R7 ^% z0 s8 Y( j( u2 d" @
  8. {( u6 V4 D5 l, z$ N! R) k6 [
  9.         5 Y9 O) C) e# E0 @/ h$ @
  10.         NVIC_InitTypeDef NVIC_InitStructure;
    ' }5 z6 ?' t) B9 `8 _; z
  11.         EXTI_InitTypeDef EXTI_InitStructure;/ l! o6 _( R( b% X9 l$ A
  12. : i5 [4 }- x9 {4 ^9 g* w, R- x

  13. 2 `3 K1 k; Y; J. J+ t
  14.         /* Configure the EXTI line 18 connected internally to the USB IP */
    0 S" ?& |) ?2 Z
  15.         EXTI_ClearITPendingBit(EXTI_Line18);
    7 v# B/ s4 p6 f6 W1 j! ]
  16.                                                                                           //  开启线18上的中断& l' m  b- ]+ A4 X
  17.         EXTI_InitStructure.EXTI_Line = EXTI_Line18; // USB resume from suspend mode* T' e9 _4 B( x  t: K( `; I
  18.         EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;        //line 18上事件上升降沿触发
    ' s; ]. A9 ^& C' O: P% R$ h
  19.         EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    / d6 Y# `4 `/ G
  20.         EXTI_Init(&EXTI_InitStructure);         
    5 J9 A$ [% B. }, e7 K# d2 \- k- g( J
  21. 2 N9 [: u& O6 ?3 Z! v1 Y2 C
  22.         /* Enable the USB interrupt */3 y. }  P$ X& ?
  23.         NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;        //组2,优先级次之
    & Q/ B& d  p( F" l
  24.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
      m$ {; W6 D1 V0 o5 ~
  25.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;4 M( E5 _1 k; a& g2 y- n# L  h* J
  26.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    9 E/ G8 v+ T1 r: Y: r6 w$ F# Y6 z) R
  27.         NVIC_Init(&NVIC_InitStructure);3 ]$ X  U/ n4 C
  28.         4 c3 T0 J, N( x* `8 j
  29.         /* Enable the USB Wake-up interrupt */
    " `' N4 N( ^" f% Y* l
  30.         NVIC_InitStructure.NVIC_IRQChannel = USBWakeUp_IRQn;   //组2,优先级最高        0 r, o$ C. O6 U
  31.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    + z: E( c) P- |* {7 ~, k" A* u
  32.         NVIC_Init(&NVIC_InitStructure);  
    6 L- ?2 R7 P% |( t3 a; _

  33. ( U$ j0 A: s9 X( }, z, E

  34. 3 M6 p; z% O& e5 {8 O+ ?9 D/ q
  35. }
复制代码
* C0 N4 J7 E7 j
再注释掉USB_Cable_Config的内容(DP上有1.5K上拉电阻)并修改中断函数% T' J* b+ X5 C1 \2 X
6 F- R% [, F. S7 j, P& c
  1. //USB唤醒中断服务函数7 C/ P" M% h& u; \3 Q9 a
  2. void USBWakeUp_IRQHandler(void) : p* R3 D  F5 x3 y
  3. {5 G$ Y" J( l7 t8 C: C
  4.         EXTI_ClearITPendingBit(EXTI_Line18);//清除USB唤醒中断挂起位' S7 \* a4 D# C  B
  5. }
复制代码

" g8 n' f- V1 C4 v3 A( W9 ^4 U! t修改pwr的Suspend内容:
( t9 b$ X# \/ y# N: k: v
( J- v6 {: F5 l: d6 @, _3 i
  1. void Suspend(void)
    9 J) S; u: c2 R2 r1 C: V8 f9 A
  2. {
    + e9 r: `& l, m$ q
  3.         uint32_t i =0;
    2 E- j- F& U: T0 C! f/ ]' m% R$ [) s
  4.         uint16_t wCNTR;
    % M& @8 J( _) e" Y# K
  5.         __IO uint32_t savePWR_CR=0;/ [  H* y0 e% u' V% M, D
  6.         /* suspend preparation */
    % W2 ]: q6 i6 w& N4 V# k
  7.         /* ... */" S5 V) `6 W# k/ z7 N7 I
  8.         + `7 `/ e; \4 W6 b% k
  9.         /*Store CNTR value */
    2 u! P% S  e! u- w3 x! @8 `8 P7 o
  10.         wCNTR = _GetCNTR();  / s2 D1 }9 G0 R+ h5 X& n

  11. 2 a9 y/ l$ ?: `3 [* o# V
  12.     /* This a sequence to apply a force RESET to handle a robustness case */+ n* C( ]3 c* g
  13. * }7 T. u# |9 q; a# I5 A
  14.         /*Store endpoints registers status */
    1 a# n) r3 B5 q* K2 d. Y& o
  15.     for (i=0;i<8;i++) EP = _GetENDPOINT(i);
    $ y, B- i. p7 l! b# h* }. @; |' y
  16.         * `" m, F+ Y+ g- ]
  17.         /* unmask RESET flag */* v+ T" c$ b, x9 _
  18.         wCNTR|=CNTR_RESETM;- f) E6 |' f/ L4 V
  19.         _SetCNTR(wCNTR);+ U* ]0 d! g3 |
  20.         , p/ j5 w% j# K6 }  s. F
  21.         /*apply FRES */
    , {6 r  O5 C( S- L( O/ }
  22.         wCNTR|=CNTR_FRES;
    4 J5 N: E  _& c; ?
  23.         _SetCNTR(wCNTR);
    * R- @" P. V, K  G" I5 V2 @
  24.         / S4 l, Q, p: c( x8 ^/ c  d0 J
  25.         /*clear FRES*/) k8 g4 _8 T9 P- i$ p2 _; g  S
  26.         wCNTR&=~CNTR_FRES;7 F' x4 z$ h  S* K' W( W4 ^: f
  27.         _SetCNTR(wCNTR);. F3 u% \5 n. c9 p, Y
  28.         : h; S" v$ g4 m6 `8 I3 ]9 G/ h" [+ f
  29.         /*poll for RESET flag in ISTR*/
    " S1 W2 P& m" H+ Z
  30.         while((_GetISTR()&ISTR_RESET) == 0);
    . H; h6 j6 O1 U# _% D! ~8 G
  31.         
    / L' G4 K( o; u2 `2 w, j
  32.         /* clear RESET flag in ISTR */- E3 Q& `+ t% y; ?3 J7 E5 ^) b, ?
  33.         _SetISTR((uint16_t)CLR_RESET);
    9 z! X0 y0 K6 J: P
  34.         
    / b) g8 j- i2 I1 D0 t+ S
  35.         /*restore Enpoints*/% v* V+ a- t. U9 y
  36.         for (i=0;i<8;i++); i7 N* K$ o4 l" f
  37.         _SetENDPOINT(i, EP);, A/ A2 b7 }1 a, N+ p
  38.         
    8 R6 \  b$ ^. V& _
  39.         /* Now it is safe to enter macrocell in suspend mode */' M* e1 n. h" ]: X7 M
  40.         wCNTR |= CNTR_FSUSP;
    * a$ ^% I# W7 ?7 s1 Z
  41.         _SetCNTR(wCNTR);+ p- v. v( L8 \: ^+ I, s1 c
  42.         + u! ], R) b) J2 h2 h
  43.         /* force low-power mode in the macrocell */+ k; N5 S4 D( F
  44.         wCNTR = _GetCNTR();, M5 G9 m0 q; |8 N# q: G
  45.         wCNTR |= CNTR_LPMODE;
    ! x6 {6 K1 c* {  X$ _4 Y7 g& ~1 c
  46.         _SetCNTR(wCNTR);# ]5 ?  n" C' i$ \5 @& n* ^% U' u
  47.         
    & J* ?( l" G" a1 ~
  48.         Enter_LowPowerMode();
    5 c9 y8 h4 _+ ^1 Y5 _
  49. }
    8 h9 g+ p( a% A& U" b+ ~
复制代码
; O$ o9 c7 Q1 e
把USB初始化部分不要的注释掉:
- {: c6 C8 J, q2 e
" ]# h9 X) K' L$ |" T7 O
  1. void USB_MSC_Configuration(void)
    * ^3 r5 _' g. s: g! J
  2. {
    6 K' B, C+ K& W
  3. //  Set_System();
    5 p1 ]# T2 Z, e* d1 O* q! T) D* |
  4.   Set_USBClock();
    * p0 b) ~# @6 f) O' S+ o' `6 _( H
  5. //  Led_Config();
    ( ]+ l& r  ]$ Q/ X9 _
  6.   USB_Interrupts_Config();
    & ?5 P* D( o  n. x2 R7 S& v3 p
  7.   USB_Init();6 E$ X% F  a! a0 h* H. D
  8. //  while (bDeviceState != CONFIGURED);
    0 e% f6 w( t0 Q% r1 a/ b

  9. 1 q8 E/ k# C; n  N: Q, h$ u  l1 y
  10. //  USB_Configured_LED();) E  n, ~" h+ R( e2 ~
  11. 4 `/ w" t2 [# J" n
  12. //  while (1)3 y, ?* B& ], b) {0 N' ^, {
  13. //  {}! q+ B  M$ U5 w% N
  14. }
复制代码
, m6 a+ H. B- X2 s7 ~" }: s0 ?
最后在USB初始化之前给U盘定义的数组赋值,不然就只有个盘符; [9 }" v5 T$ u' v# t9 `* h

8 Q+ v/ I1 R$ C% f& z
  1.     Mass_Memory_Size[0]=1024*1024*4;        //w25q64->8M 给4M做U盘; i5 Z: p7 t6 @
  2.     Mass_Block_Size[0] =512;                        //设置SPI FLASH的操作扇区大小为512% O- W# u2 J1 |9 |8 p' a% N+ W- R  w
  3.     Mass_Block_Count[0]=Mass_Memory_Size[0]/Mass_Block_Size[0];* e) f9 D0 \- ^# |' H

  4. ( L5 u0 H" O  L4 e. W  t. u
  5.         USB_MSC_Configuration();
复制代码

" \- R/ I( {# c& Y SEGUXF{Z3MWXPLSF5KTU9.png 2 {( ^' c& t6 D( T* ?2 e( `# D8 V
! B3 Q+ ^/ d" [8 n9 G
编译下载后就可以正常运行了。- B) ^  V$ D# }, x0 s/ Q1 N: ]8 _
* B8 B0 r, j* j1 Y
_IXXNOSIUI%KX_{0NPN2T%2.png - a% u# y$ X0 A* z/ S% |

' {# o5 k+ q. j传输文件、新建文件夹都可以了,就是传输速度感人。
2 b8 [9 o0 `1 B2 {& `, K
: C3 [7 f5 e: x+ }" i以上就是USB虚拟U盘的所有内容了,内容太多而且复杂,所以如果真的想亲手试试,还是要点耐心的。虽然能学到的并不是很多,这里主要可以干茶mass_mal部分,可以学到不少东西,方便以后的熟练运用。我这里也是为以后更进一步的开发做铺垫。3 E* G' l9 T3 y$ X+ L& f3 B

; j9 b  C8 C! X) {; y# [& R8 P, v# F' B6 C5 i! j

$ S) J( T. k5 N$ L+ o+ b* H
收藏 评论0 发布时间:2022-3-16 11:16

举报

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