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

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

[复制链接]
STMCU小助手 发布时间:2022-3-16 11:16
学习stm32已经很长时间了,但是一直没有过多的学习stm32的USB部分,因为实际工作还是用的比较少。说起USB那就有的说了,因为USB的功能很强大,这里主要重点记录一下STM32的USB部分,这个官方给的有专门USB库,笔者目前使用的是Cotex-M3内核的STM32F103系列,实验的芯片为STM32F103C8,这个是目前市场上性价比非常高的芯片,也是用的非常多的芯片。
( p$ R, U- D) |
$ |3 S- u8 R3 P% y$ X1 V0 {/ y, @USB基础知识
% u- x; W7 o: _$ Z+ v0 @USB按接口类型分
' k% |4 }& J1 F/ z! t' Z6 q
控制器/主机(controller/host)
& K+ `$ L! {& L$ M6 V+ p% J
6 W( T5 {" o& Q- `2 c设备(peripheral)
) ]2 s5 m$ x/ y: V- f; B3 r7 h4 J% N9 E) l1 V0 f/ v8 a+ k9 Z- p
OTG(on-the-go),通过id线确定作为主机还是作为设备
! p7 m. S5 L5 ?
' T, F  y3 ^! X3 g+ J8 X% J/ q按照USB速度分# L9 B  T' h' U% r2 _, t! }
, v- G, k8 @8 {5 o  Y
低速(low speed)5 h  ^8 s9 x5 r2 U

; j0 j$ [% @5 H  o+ G全速(full speed)( ]4 a; y- x5 e) j
2 ^  G, p  L$ Q
高速(high speed)6 s1 _# z! }) T) R% b
! f, x$ o3 q/ J7 _
USB接口一般是4根线,VCC GND DM(D-) DP(D+)
+ @3 ^* [3 M3 u1 _/ n2 r+ b+ {; R* \6 B
低速设备:在DM线上接入上拉
) l  G. \$ P* W( f' s' G; P9 m+ V! Z$ W+ |; V$ d: z! o
全速设备:在DP线上接入上拉& z  Q$ s; I+ \  b' a0 V
1 D; r  ~( _% f: \2 m$ ?
高速设备:在DP线上接入上拉,在主机对设备进行复位后进一步的确认
8 }2 }- m( D, h7 o% V( x
% G0 D$ T! ~  T9 I% v0 d  |" _1 m8 _
关于描述符
; j' y2 Y% m6 s* v7 O设备描述符(device description)" m( X6 u. X# j) Y: c

5 N- ?; u4 D+ _+ z$ X, i/ l2 w9 m' ]配置描述符(config description)
- g( A8 @( a- S' [3 Q- r1 q  k0 l: J# f
接口描述符 (interface description)# {4 u, V- f6 X5 c; Z1 L7 L
- S' k7 R4 L& `% n  z* K. T8 W
端点描述符 (endpoint description)3 ~& M" b( o' w( X1 T7 V

$ p% ], t6 N9 C5 D& |. i( T (HQG%R@V4W]{7LOY8DZE{L9.png 5 O2 c8 {& x/ n

: K6 j3 p5 ?6 \  n+ g7 }. p! ~设备的“身份”信息存储在描述符中。每个USB设备中都有如下描述符。需要注意的是一个USB设备只能有一个设备描述符,一个设备描述符可以有多个配置描述符,一个配置描述符可以有多个接口描述符,一个接口描述符可以包含多个端点。
: O  P- e( a! o, `; F* k
9 f  n! G7 ~! `关于传输

. v$ |1 r6 I8 r* x1 {1.在USB的通讯中,有传输(transfer),事务(Transaction),包(packet)三级。包是最基础的传输单元,与TCP/IP协议中的MAC层协议作用相同。
- j; k, }' @# ?2.在一次传输中,由多次事务组成,每次的事务又由多个包组成9 W+ R, Q4 }, b. P1 Y& [7 S* V
3.与众多协议相同,较高级别的协议的报文是基于/内嵌在低级协议的报文当中的,在USB中也不例外,例如,包中预留了DATA位,其目的就是填写报文
2 |$ Q' x2 e! vSTM32F103芯片自带了有USB模块,可以用来做从设备,不能用作主机HOST。这里使用USB的目的是讲USB作为一个大容量,这个可以基于官方USB Mass_Storage例程来移植。这里就不过多的介绍里面复杂的通信原理了,要彻彻底底搞明白并灵活运用取来还是有很大难度的,因为内容太多了。这里仅仅介绍下移植过程和描述符相关的重点内容。: a# W$ y' X; G* \' H

" y  {2 B* B8 u! k. f0 H准备工作
' ~/ M1 S+ L+ j第一步

; p0 d+ g. }2 r) z3 k确保自己的KEIL开发环境已经完善。' X8 x, z' r; G. \! T5 O
" Q  x3 q6 q. U- }7 }  H
第二步) q! k2 ?# r% M' ]7 v% O
下载好了两份ST官方的库文件(我是基于标准库开发的),一个是标准外设库文件,一个是USB库文件。如下图:0 @+ K! c9 C5 R# S7 k- o" x  k
3 d1 z6 `6 u6 Y. z
AZ7H5GQWT4J%{N{6){9E]{3.png
3 q% C8 m/ W8 E) h" m8 E0 A% K- F; o
第三步
: F+ Y& G: x2 p/ Z9 e建立一个带有存储介质驱动的STM32基础工程,存储介质常见有SD卡、外部FLASH芯片、内部的FLASH空间。的我是基于一个外部flash的工程去实现的,芯片具体型号是W25Q64,64Mbit的空间,换成字节就是8MByte。驱动部分如下:9 F+ g; B8 j0 F! ?' A0 i$ W6 D

$ B( w, ]* B& d3 T& ^; TC文件. o+ {0 l! Y& i) |: S6 Z
  1. #include "fy_w25qxx.h"
    2 K0 e- L) K1 m7 \$ O& i: K# s

  2. 7 M8 u' U, |) v- i, ^: R8 c- D
  3. u16 W25QXX_TYPE=0;//W25Q64# l; I8 ~/ `9 S& S* X* `( q: X
  4. 1 }5 h6 F8 N9 U, L# ~
  5. //4Kbytes为一个Sector,16个扇区为1个Block,容量为16M字节,共有128个Block,4096个Sector ; \) s) Q, d( e! [
  6. //初始化SPI FLASH的IO口- d! e: s( E( a4 C$ s
  7. void W25QXX_Configuration(void)1 v/ H5 p+ E; ]3 m0 t3 `  f, P
  8. {        
    4 G  A' d& H) g
  9.         SPI2_SetSpeed(SPI_BaudRatePrescaler_2);//设置为18M时钟,高速模式
    % N+ E- L, h4 [# |5 y8 i# `( O
  10.         W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID.  2 h/ E8 O$ i" J7 W8 R/ X
  11. }          % n! C* |( l% r+ M

  12. + Y/ \) t* M; c$ e: _- u
  13. //读取W25QXX的状态寄存器
    ; e- q; t3 w4 m. Z
  14. //BIT7  6   5   4   3   2   1   0
    1 H8 s0 x1 V0 x+ j
  15. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY$ G: J% k, ~( J
  16. //SPR:默认0,状态寄存器保护位,配合WP使用2 L0 ~3 C) }! d
  17. //TB,BP2,BP1,BP0:FLASH区域写保护设置9 ~( V5 b( N$ x+ N! Q
  18. //WEL:写使能锁定
    * x4 @. r- M; |0 V8 G7 u( `+ W
  19. //BUSY:忙标记位(1,忙;0,空闲)
    # f( |& q2 p5 `( L
  20. //默认:0x00
    & Q6 V4 |& x- C
  21. u8 W25QXX_ReadSR(void)   
    ( d( {+ _2 x  W; l% e
  22. {  $ M* M  r6 c  W" Q# ~+ ~3 k' j' A3 U
  23.         u8 byte=0;   
    ; B0 C8 `, ^1 t% [  }; F7 T0 G' m
  24.         W25QXX_CS_L();                            //使能器件   / |8 F) v# M. f5 x3 I3 j
  25.         SPI2_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令   
    , S( O, V) Q% |& N) _3 T1 Y
  26.         byte=SPI2_ReadWriteByte(0Xff);          //读取一个字节  6 o6 L+ _% z9 X, O" \# ~
  27.         W25QXX_CS_H();                            //取消片选     
    - C- s9 }7 z. q; w3 N& c
  28.         return byte;   / z1 q- ~- C7 k/ Z$ ^# w
  29. } / b$ t- k9 c# u! _3 Q
  30. //写W25QXX状态寄存器
    2 A4 y8 ~  T' C  s
  31. //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
    ; |4 a. T3 F2 |9 W% J
  32. void W25QXX_Write_SR(u8 sr)   ! z$ [; d/ A" G- y+ D- L
  33. {   ; @* _9 }# j9 ~4 y3 [
  34.         W25QXX_CS_L();                            //使能器件   . r0 a4 |, r( h) }
  35.         SPI2_ReadWriteByte(W25X_WriteStatusReg);//发送写取状态寄存器命令   
    + c  ~* k' j; [+ Y/ y. }" w" d# a
  36.         SPI2_ReadWriteByte(sr);                       //写入一个字节  
    1 Q5 H# J- F) n+ o5 C! Q/ g
  37.         W25QXX_CS_H();                            //取消片选                   / V% K: r  z/ ~$ u2 V9 k: s2 d
  38. }   $ C: R2 A, Q- T+ \
  39. //W25QXX写使能        + A; U  }1 r% R) i& ^) L
  40. //将WEL置位   
    % b  u" k& c& L, ?6 _
  41. void W25QXX_Write_Enable(void)   
    : O1 W2 {6 @8 i( |) Z8 `! n! \
  42. {" l, e3 M9 z0 y. x0 O  ^7 p( o3 c
  43.         W25QXX_CS_L();                                  //使能器件   6 a$ F7 B) a3 ]/ H
  44.     SPI2_ReadWriteByte(W25X_WriteEnable);         //发送写使能  4 E: L5 l9 I( R" w3 ~# n% H( ]6 F
  45.         W25QXX_CS_H();                                   //取消片选                   & j5 X# `8 }3 L7 m5 X9 U2 }  k
  46. }
    ' l; r* |; I2 ]( u  ?
  47. //W25QXX写禁止        
    + j( u0 m6 U. s1 X# F
  48. //将WEL清零  " d4 }% S' M( z3 b! P& _
  49. void W25QXX_Write_Disable(void)   
    ; S6 q, g" [1 }. D& T
  50. {  " d4 a7 d( F; h8 o. n& R
  51.         W25QXX_CS_L();                            //使能器件   ( u5 J8 e9 O- f
  52.     SPI2_ReadWriteByte(W25X_WriteDisable);  //发送写禁止指令   
    8 G; K, ]% D! w& c1 Z6 y0 p7 K
  53.         W25QXX_CS_H();                            //取消片选                   ( x; ^/ j, Q% m6 d  M2 _( f
  54. }                 ( U1 o, y. \) D
  55. ! ^9 v2 t4 [$ t8 {& @* L
  56. //读取芯片ID
    " T% X$ K4 B4 R  o
  57. u16 W25QXX_ReadID(void)2 [2 e2 a$ k' R. T/ M/ A
  58. {. Y: q7 I8 c! h$ U4 J+ K/ r# L: R
  59.         u16 Temp = 0;         
    8 a& D7 t7 E5 b& v' H
  60.         W25QXX_CS_L();                                    , C! H. ?8 R  ~: f( f' a
  61.         SPI2_ReadWriteByte(0x90);//发送读取ID命令            
    7 j0 `& `$ _- {; u: e! V! c& n
  62.         SPI2_ReadWriteByte(0x00);             , v$ b+ g0 _1 _) I/ `7 Y
  63.         SPI2_ReadWriteByte(0x00);            
    : [+ p0 r; }  j
  64.         SPI2_ReadWriteByte(0x00);                                    
    - C6 n! D/ P- M& A7 X) f
  65.         Temp|=SPI2_ReadWriteByte(0xFF)<<8;  ; f4 _" Y, t+ Y8 d* C& S: s
  66.         Temp|=SPI2_ReadWriteByte(0xFF);         # m7 h" |" Q, k
  67.         W25QXX_CS_H();                                    - j% T1 ~! ~7 l- {( t
  68.         return Temp;' n0 F: O, i3 y$ g2 J- z) t. r
  69. }                       
    7 D5 W" E) }# Q0 Q( F0 `  @

  70. , b' }$ N7 u* V0 r1 u9 h- y0 O
  71. //在指定地址开始读取指定长度的数据* {5 V' q- Z0 e7 `
  72. //pBuffer:数据存储区,ReadAddr:开始读取的地址(24bit),NumByteToRead:要读取的字节数(最大65535)1 E2 i& C5 `+ Q
  73. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   
    / R1 n* x; Z% Q9 J6 ?! f
  74. {
    2 _0 ^- `+ A) ~4 o8 T7 z) @
  75.          u16 i;                                                                                       
    ) ~/ H4 S, ~& ?. T* T4 H( t
  76.         W25QXX_CS_L();                                    //使能器件   . u: ^; b% _! s) L' i- i
  77.         SPI2_ReadWriteByte(W25X_ReadData);                 //发送读取命令   4 l; @- H+ U* a$ X- ^) e3 S$ f
  78.         SPI2_ReadWriteByte((u8)((ReadAddr)>>16));          //发送24bit地址    ! q. i* |9 d6 C% `
  79.         SPI2_ReadWriteByte((u8)((ReadAddr)>>8));   
    " {8 i5 `4 A# [& d$ m) x7 U
  80.         SPI2_ReadWriteByte((u8)ReadAddr);   
    ) Q; j5 n- ?: a4 f6 M+ x
  81.         for(i=0;i<NumByteToRead;i++)$ Q2 ?2 g# j4 P, d( C
  82.         { - N8 u5 s! i2 Q7 y0 f0 Y' \$ m  c0 Z
  83.         pBuffer=SPI2_ReadWriteByte(0XFF);           //循环读数  
    8 T7 }  L) J, ?2 T, F
  84.   }
    4 [  U7 {0 y3 l) I: v# x) u  W; m) ?4 a
  85.         W25QXX_CS_H();                                                    6 f' k% w: S: z7 @8 Y! }/ s2 x% a
  86. }' e1 K( \4 i+ c

  87.   U# b. T, F" a/ k; k! p
  88. //SPI在一页(0~65535)内写入少于256个字节的数据
    8 g+ {- F5 {2 s- k) B
  89. //在指定地址开始写入最大256字节的数据
    7 y. E+ q3 r# Z8 I( o1 M7 I
  90. //pBuffer:数据存储区,WriteAddr:开始写入的地址(24bit),NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!         ( N' r$ V3 |8 F% M) ?; K
  91. void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
    . Z) X* l! x* @' G6 X; l
  92. {
    0 \( ^/ x7 c! [0 h0 ?
  93.          u16 i;  
    % F+ C6 }8 G% ^# f9 ]) y  N
  94.         W25QXX_Write_Enable();                          //SET WEL
    # O2 k( j: |- K3 C# Y2 j5 x0 P
  95.         W25QXX_CS_L();                                    //使能器件   * ?; |- y0 L1 c1 b- J
  96.         SPI2_ReadWriteByte(W25X_PageProgram);              //发送写页命令   
    3 K9 U: A3 y# f% }
  97.         SPI2_ReadWriteByte((u8)((WriteAddr)>>16));         //发送24bit地址   
    ) G- A2 ^7 A0 W: W! d
  98.         SPI2_ReadWriteByte((u8)((WriteAddr)>>8));   
    " W! u' n2 ]1 c  Z8 }; b; G
  99.         SPI2_ReadWriteByte((u8)WriteAddr);   " D4 C! |1 h- X( `; {- Y
  100.         for(i=0;i<NumByteToWrite;i++)SPI2_ReadWriteByte(pBuffer);//循环写数  
    " T( D% c9 @) }& c. b7 @
  101.         W25QXX_CS_H();                                    //取消片选 1 v0 [4 ?0 @4 F
  102.         W25QXX_Wait_Busy();                                                           //等待写入结束
    ( `( C8 L3 g9 [; o$ k6 Y! A
  103. }
    * ~. i4 g/ G2 i3 L' C8 T
  104. //无检验写SPI FLASH   f  ?5 p3 ^9 c1 Y/ `
  105. //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
    ) ~" w& Y  X, Q, t
  106. //具有自动换页功能 ' V' z( U  M+ L* j. V: X6 M/ W
  107. //在指定地址开始写入指定长度的数据,但是要确保地址不越界!3 k5 Z* Q8 E  w! m* Y2 v0 h
  108. //pBuffer:数据存储区
    4 z- N- p+ M* B5 ~, l7 [
  109. //WriteAddr:开始写入的地址(24bit)
    ) V0 D0 A; C( g9 k0 G
  110. //NumByteToWrite:要写入的字节数(最大65535)
    ' k$ r: ?% q' I9 w, m: S
  111. //CHECK OK$ w$ W/ @3 y& D; P, ]/ B
  112. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   4 |$ K0 b$ n% _. D
  113. {                                           3 [2 e! N8 P8 {* C) y
  114.         u16 pageremain;           
    $ G: m" E# ]  t  Y
  115.         pageremain=256-WriteAddr%256; //单页剩余的字节数                             4 s: V; w4 F) Z8 V
  116.         if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节+ Q: D' f0 `: ^$ I4 x
  117.         while(1)$ @, v/ }$ ?6 r+ H% F( F0 ~- X
  118.         {           
    . Q/ ^3 \& a8 Z% U% L
  119.                 W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);9 Z6 t# `+ ~* Q* Q* d! T% w7 ^
  120.                 if(NumByteToWrite==pageremain)break;//写入结束了
    8 }) G. L! A. V0 b6 B. ]
  121.                  else //NumByteToWrite>pageremain+ p; A) D) Q* E3 O& P9 {* q/ H
  122.                 {
    * M9 o8 ?* T2 F/ I2 v2 \
  123.                         pBuffer+=pageremain;2 r. b5 ^5 {. I+ a) o6 z- |1 L
  124.                         WriteAddr+=pageremain;        
    5 J. X/ I$ X* Y; c; M+ j2 ?/ f
  125. 9 ?4 m% o# O* U; t: y7 x
  126.                         NumByteToWrite-=pageremain;                          //减去已经写入了的字节数- A0 M, D* a" k9 K& A0 H2 n
  127.                         if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节1 H( F$ h* u7 o" S
  128.                         else pageremain=NumByteToWrite;           //不够256个字节了
    ( s$ F. r6 E0 _+ T- {
  129.                 }
    3 p5 x( I% ~) i9 c
  130.         };            6 z: N( p% y. R9 i7 [: E8 ]+ S
  131. }
    & W( f1 x0 h, ]$ v% C. S
  132. //写SPI FLASH  
    9 I$ Q7 V" `' Z) G2 i: D' I" a0 w
  133. //在指定地址开始写入指定长度的数据
    # s3 s4 l( Q# L  K( u
  134. //该函数带擦除操作!
    : j6 q6 C( G" ?6 N$ y; ^
  135. //pBuffer:数据存储区
    . ?  w: r* O: y& m
  136. //WriteAddr:开始写入的地址(24bit)                                                * D# D0 i' k/ R/ i" b" k: k1 F! _
  137. //NumByteToWrite:要写入的字节数(最大65535)   % W. D- U$ C6 q& \9 |/ K
  138. u8 W25QXX_BUFFER[4096];                 8 l& a$ D% P, H: m( y
  139. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   0 D9 l4 H5 G3 W8 D2 D; ]
  140. {
    + R) j$ S8 y3 X" {9 x# \4 b/ j
  141.         u32 secpos;' k7 [4 U7 R: ]6 d- q2 Y
  142.         u16 secoff;0 I" j' k! O( L. J4 x+ \- _; `5 ^- y+ E
  143.         u16 secremain;           
    4 Y& e& x2 w0 ^1 w* ^/ x$ B# T* i
  144.          u16 i;   
    : q: g6 R% S% }; |  m
  145.         u8 *W25QXX_BUF;         
    1 a; P' U3 X- q* g5 c4 v0 `
  146.         ! {- v7 k( l. C, ]' i& d) B
  147.            W25QXX_BUF=W25QXX_BUFFER;* [3 Q$ h5 i8 g- g4 E
  148. //        
    , a1 f! L% n4 H6 O6 w1 w: p+ z- X* K
  149. //    W25QXX_BUF = (u8 *)_mem.Alloc(4096);//申请一个扇区大小的内存0 W% W5 Z' X3 E" I  ^& f$ A
  150. //    if(W25QXX_BUF==NULL)        return;
    % L+ X' K; M: h/ L
  151.         
    / l6 ]# K) `9 J7 F6 u, l6 E
  152.          secpos=WriteAddr/4096;//扇区地址  
    ) s" Y+ T: Q- m
  153.         secoff=WriteAddr%4096;//在扇区内的偏移
    * u, C, P$ X8 @
  154.         secremain=4096-secoff;//扇区剩余空间大小  # k7 w: g( T% O2 @  X/ {
  155. 8 \& J+ e1 @) @% A
  156.          //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用6 C3 ]$ L0 D3 C
  157.          if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节- n3 b! C2 x6 a7 l" n( g; t% J
  158.         while(1)
    1 U8 d/ ]; o# R; U5 O" o9 y" s
  159.         {        
    # D9 I* q( X6 h3 @$ @! v6 v
  160.                 W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容
    : D1 Q6 H) d( G
  161.                 for(i=0;i<secremain;i++)//校验数据6 ?$ E1 C) P; k5 F. [
  162.                 {
    $ ~9 @# H1 x- ^0 W+ n- b% i2 w
  163.                         if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除            ; R+ I. q9 l3 P# t, ^7 ^1 I8 W, R
  164.                 }. q! i- w9 b6 ^
  165.                 if(i<secremain)//需要擦除
    ( ~: b3 r9 N3 }% h7 d7 U0 T: N
  166.                 {* j- d" B# a' F0 Q! ?9 l- _# J: |
  167.                         W25QXX_Erase_Sector(secpos);                //擦除这个扇区
    0 X- N: R/ J. S/ f
  168.                         for(i=0;i<secremain;i++)                           //复制
    1 ~" c4 I& `6 D( F* F2 W
  169.                         {8 e7 ~0 z) o$ z/ m; B
  170.                                 W25QXX_BUF[i+secoff]=pBuffer;         
    . J4 X: {- u* y. o8 s, `
  171.                         }/ Q0 g& z) l) e1 d
  172.                         W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  $ y- D" H# V' T8 v5 n% _

  173. " I2 r* B& f9 f9 e7 X, ?, J
  174.                 }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.                                    0 V) U' m/ {: G. h& s# k
  175.                 if(NumByteToWrite==secremain)break;//写入结束了6 K( C$ \. E% Z
  176.                 else//写入未结束: ]8 f! H1 ?6 R- q
  177.                 {
    5 Z* ~& r$ ?' u( t" l
  178.                         secpos++;//扇区地址增1# @8 D) A3 q- f6 p. T; c- j+ ~& ]+ n
  179.                         secoff=0;//偏移位置为0          7 R3 E9 K) {: V0 \

  180. 5 q5 O, G6 U2 d$ V" ^; t
  181.                            pBuffer+=secremain;                                  //指针偏移+ R- l: a. ]* V
  182.                         WriteAddr+=secremain;                                //写地址偏移           
    # O2 ?' l4 F4 V
  183.                            NumByteToWrite-=secremain;                        //字节数递减9 Q* J' i" N' A$ Z* ~
  184.                         if(NumByteToWrite>4096)secremain=4096;//下一个扇区还是写不完
    , ]/ j8 e" d3 O/ g2 l
  185.                         else secremain=NumByteToWrite;                //下一个扇区可以写完了
    ; z5 _% X+ \! Y, X
  186.                 }         : N" Q$ j% h0 g% ~3 q
  187.         }
    , s% M9 V* c; l; R6 N
  188. //        _mem.Free(W25QXX_BUF);
    2 r+ y+ g. i( T; |7 Q, G' c8 ~
  189. }- R3 a4 A0 j: n. n3 q5 w' ]
  190. //擦除整个芯片                  
    " e& ^) R/ }) Q, B/ V8 v$ W
  191. //等待时间超长...
    4 @) E3 K' f! a+ c" E# f! U& C
  192. void W25QXX_Erase_Chip(void)   
    ; g* l+ T* k0 U
  193. {                                   ; f# m- n! q- W9 Q/ `
  194.     W25QXX_Write_Enable();                                  //SET WEL
    9 V, P+ a; N9 B0 n+ Y* L* [6 u
  195.     W25QXX_Wait_Busy();   
    ; {* o- N% _2 J5 [$ K0 Z
  196.           W25QXX_CS_L();                                    //使能器件   
    ! r" {3 U. T' X6 a7 J! A& F9 x
  197.     SPI2_ReadWriteByte(W25X_ChipErase);                //发送片擦除命令  
    ) \! @5 R' ^) z9 X
  198.         W25QXX_CS_H();                                    //取消片选                  
    + Q  `  b( c# `& a
  199.         W25QXX_Wait_Busy();                                                      //等待芯片擦除结束0 ?% A3 Y* B. t. y0 A
  200. }   
    2 N# ^7 G! C# B$ \
  201. //擦除一个扇区0 D# C  `9 r* U8 r% a  M
  202. //Dst_Addr:扇区地址 根据实际容量设置; z! y& E' }/ F, ~  i2 n- O3 W
  203. //擦除一个山区的最少时间:150ms
    6 W) y* p' |# F0 @9 ~
  204. void W25QXX_Erase_Sector(u32 Dst_Addr)   ! ^' s& h( y+ U# q  l
  205. {  + g9 T& @( F9 Q
  206.         //监视falsh擦除情况,测试用   0 v2 ~* q5 N2 T- |# ?: ]  \1 w: g
  207. //         printf("fe:%x\r\n",Dst_Addr);          1 S0 m3 P$ u2 A
  208.          Dst_Addr*=4096;
    3 |  ^% d- b- `! m
  209.     W25QXX_Write_Enable();                          //SET WEL          ( H) x. @; \; i% j6 n
  210.     W25QXX_Wait_Busy();   
      N2 M+ E4 N0 E9 s1 i5 X0 Z
  211.           W25QXX_CS_L();                                    //使能器件   
    ) T; a0 E# _) c) c7 Y
  212.     SPI2_ReadWriteByte(W25X_SectorErase);              //发送扇区擦除指令
    7 g; `6 O6 g- L9 m( m
  213.     SPI2_ReadWriteByte((u8)((Dst_Addr)>>16));          //发送24bit地址   
    * l; k/ E- [& I/ {  a# ]
  214.     SPI2_ReadWriteByte((u8)((Dst_Addr)>>8));   3 H, I! v" G6 {# Z2 a+ j
  215.     SPI2_ReadWriteByte((u8)Dst_Addr);  5 f2 E6 L+ d; t% I& f
  216.         W25QXX_CS_H();                                    //取消片选                   " O; U+ N; `7 B3 V2 ]" l
  217.     W25QXX_Wait_Busy();                                                      //等待擦除完成
    ; o( G' K7 _+ T' L3 ?9 g  g5 W' E
  218. }  
    ; i1 [. l+ F6 w1 V
  219. //等待空闲
    . E, y) j8 ^9 q3 [
  220. void W25QXX_Wait_Busy(void)   # p' {- f5 I. `* X4 f
  221. {   0 J7 U8 W) d0 h/ z
  222.         while((W25QXX_ReadSR()&0x01)==0x01);                  // 等待BUSY位清空
    , o3 c' D( T: I. F( |* s3 K% w4 a
  223. }  - }# f" K9 R1 C
  224. //进入掉电模式% W0 O% B  f8 ?& N4 a: v/ t; e
  225. void W25QXX_PowerDown(void)   ( B4 ?( R0 Z% S8 ?: O# a$ o" T
  226. {
    $ k3 K0 C5 |. p  E: m" Y* t
  227.           W25QXX_CS_L();                                            //使能器件   
    . Z7 E5 ]3 s+ k
  228.     SPI2_ReadWriteByte(W25X_PowerDown);        //发送掉电命令  
    * I7 D) a: G% v
  229.         W25QXX_CS_H();                                    //取消片选                   1 J6 A: B' d. A1 k' R
  230.     Delay_us(3);                               //等待TPD  ( G; x2 J9 P: }+ p
  231. }   6 d/ A3 P! w* X
  232. //唤醒  y, p. r# y8 i$ |# M& V! ~/ M
  233. void W25QXX_WAKEUP(void)   1 }* t/ H4 C8 ]0 D$ t3 n- V
  234. {  
    : q; x* _! l0 y! ^: E. {
  235.           W25QXX_CS_L();                                    //使能器件   
    & G4 `' K4 X0 \/ E1 U. [: V: W' K9 k
  236.     SPI2_ReadWriteByte(W25X_ReleasePowerDown);        //  send W25X_PowerDown command 0xAB   
    3 T+ s1 D" P" T) b0 X; @
  237.         W25QXX_CS_H();                                    //取消片选                  
    3 I! K; ~. r" g: O1 [) g
  238.     Delay_us(3);                                    //等待TRES1: ^" g/ r% z# ~; M  w
  239. }   
复制代码

0 ?6 V4 |" ]+ R& |+ x  vH文件
! m$ O+ ]$ G+ x1 W7 q6 s
7 T0 Z0 _- O' N) |* y$ V
  1. #ifndef __FY_W25QXX_H
    + W/ n4 q7 {4 u" F9 ]
  2. #define __FY_W25QXX_H                            . ~: F6 t- H. T1 h) }
  3. - \0 f: u" A! a; E
  4. #include "fy_includes.h"
    4 Q% x7 w; L' S4 u+ ~# O

  5. ' u+ i2 V/ {( t
  6. //W25QXX对应唯一识别ID
    ; z( E! I" X! {# U" M$ @
  7. #define W25Q80         0XEF13
    ; p2 M; o: \5 I1 `1 ]
  8. #define W25Q16         0XEF14' p/ [  \5 H# L# H
  9. #define W25Q32         0XEF15
    3 R, E6 u- R* H! Z9 @% P- ^* Q0 e
  10. #define W25Q64         0XEF16
    2 q5 G. i" s  x- l6 X& i" {
  11. #define W25Q128        0XEF17
    ! }3 T4 Y! B+ m

  12. " f4 [' ]8 H) O2 [2 a9 R% [
  13. //指令表
    % a7 i; s* C# g( y0 Q. k4 A- R# B( P
  14. #define W25X_WriteEnable                0x06
    5 F- ~' W  r- r* r
  15. #define W25X_WriteDisable                0x04
    7 _0 n- Z  m9 r7 t
  16. #define W25X_ReadStatusReg                0x05
      q0 Q! |3 N, Q3 n4 w' _
  17. #define W25X_WriteStatusReg                0x01
    1 u, R: j3 O; c2 L1 ?7 Z# `1 Z
  18. #define W25X_ReadData                        0x03 * e& w8 N/ e/ e( `( B; U6 `5 e
  19. #define W25X_FastReadData                0x0B
    ) J/ v$ ?2 b; P  u# m6 u
  20. #define W25X_FastReadDual                0x3B
    ; w2 X* O2 H9 D$ Q- ^
  21. #define W25X_PageProgram                0x02 ; B8 \  `5 D& D9 e
  22. #define W25X_BlockErase                        0xD8
    ; ^! q, x" \/ Y, w+ j
  23. #define W25X_SectorErase                0x20
    + v. d8 a# g; [) |% s! L
  24. #define W25X_ChipErase                        0xC7
    5 E3 L  k! |% [) `! B8 C
  25. #define W25X_PowerDown                        0xB9 2 p; U2 T9 H- |. g# |# \; `
  26. #define W25X_ReleasePowerDown        0xAB
    ) S$ w) L2 c( Z: t
  27. #define W25X_DeviceID                        0xAB
    2 k% w8 F. H9 A8 d) [
  28. #define W25X_ManufactDeviceID        0x90
      m- T# N$ I/ i: m) M. W3 m5 A
  29. #define W25X_JedecDeviceID                0x9F * q- t5 ~1 A* t/ g0 t: ]

  30. $ `8 y) _! a( [! g$ e6 L
  31. void W25QXX_Configuration(void);( [+ Q! ~$ n  g: S" ^1 m# h
  32. u16  W25QXX_ReadID(void);                              //读取FLASH ID
    - ]; ~7 J" Q6 u1 [7 ]$ y
  33. u8         W25QXX_ReadSR(void);                        //读取状态寄存器   }7 s, {+ [' V) g$ ?1 E
  34. void W25QXX_Write_SR(u8 sr);                          //写状态寄存器
      p& I9 `4 C2 M2 }' j# F7 P3 Z
  35. void W25QXX_Write_Enable(void);                  //写使能
    $ X2 g1 f0 [- Y, C- X
  36. void W25QXX_Write_Disable(void);                //写保护) O: w9 Q* T- s* i% |
  37. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);
    . x* z6 N' N: l) X+ ]% j
  38. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //读取flash
    - }* @5 {2 l/ G9 m! M
  39. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写入flash
    ( c: d9 R* |# {1 |& i  K, z& h9 Z0 i: g
  40. void W25QXX_Erase_Chip(void);                      //整片擦除+ @% [4 W9 @4 x" ]
  41. void W25QXX_Erase_Sector(u32 Dst_Addr);        //扇区擦除; K' n1 w; x4 U7 F
  42. void W25QXX_Wait_Busy(void);                   //等待空闲
    6 P: a& {! C& z
  43. void W25QXX_PowerDown(void);                //进入掉电模式' ^. g' F. P. [4 ~2 N: y
  44. void W25QXX_WAKEUP(void);                                //唤醒; @( \5 A& M" I5 a  p3 o
  45. + y3 `7 O% u( G3 B# b; M) o# a, U$ [
  46. extern u16 W25QXX_TYPE;                                        //定义W25QXX芯片型号                  
    ' E8 d7 t/ L5 T  X# G
  47. # S8 b( g. P) h: V4 v( M
  48. #endif
复制代码
. C+ f4 V9 q: _# Y
基础工程如下:
) O  h, r+ y/ Z( ]& z
1 U0 Q4 x8 a, Q) P7 ~2 ?! k& e CIG]SRYALJ}8)5NQ[MAA~BW.png $ v, B' U6 p* m+ C( v

; L  w- _# x$ J2 M1 u: A第四步:USB代码移植, U7 |( r* Q/ |, e3 i5 D# G
拷贝USB底层库到工程根目录,新建一个目录命名成USB,拷贝Mass_Storage例程下的src和inc目录下的所以文件到USB。
  A: Z+ N4 j/ ~% u4 @0 A
& j. w+ u! x% t/ x. S  d, q4 @ 6Q]9Q9812B]OXMA8GNV[PCC.png . T" t; b2 c2 X! ]3 T

4 W# r* f; Y2 P拷贝过来是有37个文件,这个时候需要删除一些不必要的文件,因为官方的例程里面有nand,这里的话先删除以下文件3 p( C8 B: _- c1 h* G5 N0 B

+ A6 A& u% P3 l VN1JDR_I@JO271YJKNV1(HE.png
9 h, M+ ~4 r" T, D* m& P5 [9 W% L$ F5 e) z- f
4.打开工程把分组和文件及包含路径添加进去,这里就不细说了,结果如下4 H4 e, Q5 q6 d! K: W$ w( d0 Z+ C

" B5 d  s/ f! T8 a X$DZH7HHYEC}266_`72~4EH.png # ~+ h& i* V8 k" t3 v6 x: e
7 R9 N$ B; R$ _: c3 z+ x3 T
打开usb下面的main和it 把重要的代码先拷贝出来
7 M) _; a. P4 K- z# c
: n% z$ k. h+ x OA]E5J8OQ0[B{UJV@KXPUG2.png * R) H. d% ?* |# I; p. u; c
2 I. ?' Y& _( T* g
把main和stm32_it里面的内容拷贝到自己的main里面进行下封装去掉不要的保留剩下的,然后就删除it和main三个文件。) Z& E' F% }9 V4 |
, _& I' e4 |; N; ?3 Y4 d: Q4 N- b
@G5L9U9`9]${JVP}AD@KN0E.png
5 Y9 m4 M1 F4 }  |
' G$ z" P! |# C1 X3 i5 X然后编译下,这个时候会出现一堆报错,需要慢慢一步步搞定,编译前注意路径包含,( e  m7 \. K! n" V- n% v5 f

* o" r) G& t+ }# n5 u* F* K [~3UDXP_99@A_@~XV14BNPB.png
3 s( f. L3 a8 g9 r: H5 ]
% |/ u8 F' S& g% O# K$ [0 X' ?定位到第一个错误进去4 J* a. i: Z9 S0 o2 Z8 k- |
# c3 `# ?# y2 L2 U4 U' D0 s
5L28CK8A4)MY@OJR7KK~KOY.png
7 P' A- u! `6 E0 S, e* P8 _, \1 F0 y: |$ ^" u/ p6 Y
这个定义的是官方的开发板 我们直接修改成自己的公共文件就可以,并且注释掉开发板定义
! j, P/ h7 i9 o+ w- W$ }. [3 U% }9 T2 m6 B6 m' E& @
TR]6[QMF5`A7X$MYZIL])9I.png ( G# F+ n4 C6 Q1 m

6 ~; I9 a: F) Y, [* X: F公共文件如下,并且添加usb部分的头文件3 X6 d1 |7 O; b3 o
5 k$ T5 ?6 I1 J# e. }
Z7Z6YX`QZR}6~KCC_GDDLD8.png
5 e/ I+ S( b) A( ~  K7 r8 ^, @. H: L7 {8 v, A6 v' n
继续编译,定位第一个错误,这部分主要修改hw_config文件2 b7 S- `+ N, N7 X" B
3 T- i( U8 B( ?4 Y2 P% g
_9RUE5O1M][P)IRX]S2S_$D.png
3 y# d8 p; ~5 F" X: n9 f: ~, |6 ~9 c- Q: W; c
删除Set_System函数. m9 N  z% G1 @& }( v) l) J  m1 P
2 f7 u$ J4 J, h* @
N~R76S188E[X(N6NID1WQ$V.png . Z% Y% A- O& Q0 B; r; u
2 |* p& C0 B3 t+ r5 f" S5 d
删除Led_Config函数
5 R1 K  Q( }. n$ s5 r. O
9 b7 ]2 T  r7 v) x ~HA$Z23~@)XV2TEQ8CUTS.png   v, s! ?2 g* i$ b9 O' [; y3 K6 O# C, J/ _

3 D8 V2 V. M+ ~) ?注释LED相关的内容
7 k* M& R4 M, D3 x# s& b6 _6 L: I1 y- x; O2 q! [8 B: d
_FKQ$_}1}S8W%PG(2VT2]C7.png 3 Q/ H6 S) K$ K4 w
: w# l8 s$ J- ]+ F9 ^  N
删除USB_Disconnect_Config. L  B1 G/ m- Q2 T. l2 S" b

8 F/ [8 {6 o! e' c, s* j# m 4K@_T@@XVZ@P]EEH37}QAJX.png - g: j# }6 U! Y/ N; h5 }
& E2 ^: ?! s) D: Y7 w& M( ?
接下来主要修改mass_mal部分,我这里是只有一个flash,所以我修改成如下:
: b! N* U2 i" c8 p$ V$ y3 \2 g! q+ H8 F+ @( l* ?. @
2_1P4[7IG68PG%9TS@41BI4.png
' G- _4 b9 [5 u6 G9 k
- n7 D5 L! S! n- a头文件
; g+ O& v' b) e# P3 g) O8 a
; O9 O( q* T! R6 G1 k  t/ A (V0@{X{W8~L[2Z[Y7)VNU.png ; x$ Q* @, S" N3 ?0 s
+ R) j& E: @. L
下面修改memory文件,主要修改变量定义,C文件和H文件分别如下9 z  J2 [5 w- p* c, P/ L

* W2 p, a4 x8 q; Z3 ~; i3 l; F T]Y~~F2{V(H`V{$X(}FMNNJ.png ( p0 z8 C; K6 O9 U

/ q2 ^1 R5 W) U: ~- P2 i% z2 _" |6 Q( B接下来是usb_scsi部分的变量定义
# P# L0 R! \  G$ @) X& u( x0 B; q5 j0 ~1 `, P0 Q* _
R$Z1OIMFO6JWKJE~XX[{2$B.png : {" b% W1 D4 L1 \' \) _2 q

, V( J  [  }2 m+ B( _这时候再次编译就发现没有错误了,但是这个地方USB中断部分还需要配置下,官方用的宏定义方式去实现不同的代码,这里一开始就去掉了宏,所以最终修改如下:1 u9 W7 W  b2 B4 ~: N

3 ]/ x$ u5 f$ W% [
  1. /*******************************************************************************2 o- l6 L3 {- B) n" H  W! s
  2. * Function Name  : USB_Interrupts_Config( n; z6 b- ~! A* S9 E
  3. * Description    : Configures the USB interrupts+ j5 w8 G0 A- A1 R  N  u
  4. * Input          : None.. H+ d% x/ C) }5 x- L' Z, Y
  5. * Return         : None.& I; R3 G* \0 f
  6. *******************************************************************************/# e+ x  R1 F) h$ [* ]9 [* a
  7. void USB_Interrupts_Config(void)
    0 N* k( {+ C& H
  8. {3 X5 `: \( _) Q0 e
  9.         
    4 e% u; y  i4 c* \
  10.         NVIC_InitTypeDef NVIC_InitStructure;; ~) B! W: Y3 @* L* R; a3 k& w
  11.         EXTI_InitTypeDef EXTI_InitStructure;
    ' {4 X5 i8 Q4 Y9 o5 Y  Q6 e
  12. 7 p4 l5 Q  f1 S. ^$ _( t/ i

  13. : p  y4 r1 j. D1 x) W8 e& v2 C
  14.         /* Configure the EXTI line 18 connected internally to the USB IP */! D+ G; P  P) v- Y
  15.         EXTI_ClearITPendingBit(EXTI_Line18);
    + Q+ H  }) h% L5 z- k2 T
  16.                                                                                           //  开启线18上的中断
    / z/ k( W5 K* R, o% Q# G9 g( B
  17.         EXTI_InitStructure.EXTI_Line = EXTI_Line18; // USB resume from suspend mode6 h: C8 K; W* s* `
  18.         EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;        //line 18上事件上升降沿触发
    % F) q6 y$ H9 ]5 |8 X5 A+ m, A
  19.         EXTI_InitStructure.EXTI_LineCmd = ENABLE;
      E4 F2 r* x# V3 M6 @
  20.         EXTI_Init(&EXTI_InitStructure);         
    0 {8 [9 J4 S% \4 d, l
  21. - _7 ]; b; G4 A) m
  22.         /* Enable the USB interrupt */
    2 i" d8 X9 T- [# m; T
  23.         NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;        //组2,优先级次之 3 C$ l/ s2 }2 W0 ~$ y8 I7 ~
  24.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    / n  }- P8 e. U& V( T' W
  25.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;" i7 R4 Z. t6 e. r$ @0 F9 r
  26.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    9 M- s3 m( g7 p* S5 x7 Z3 |6 K
  27.         NVIC_Init(&NVIC_InitStructure);
    $ K) H- U0 ^2 F8 P6 g
  28.         ! G  l' h0 t% u$ u8 W
  29.         /* Enable the USB Wake-up interrupt *// U+ S5 m+ I" W5 v  W
  30.         NVIC_InitStructure.NVIC_IRQChannel = USBWakeUp_IRQn;   //组2,优先级最高        1 z8 b0 H0 ~+ `) t2 t
  31.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    ; |% v8 i0 @0 B9 T1 T$ x* u: l" {; u/ L1 S4 w
  32.         NVIC_Init(&NVIC_InitStructure);  
    " a& t. `; n8 l% s/ F
  33. : P! F  C0 j& d$ D, N

  34. 2 V$ d5 W- E% W2 O/ `" `; C
  35. }
复制代码

- S" f( n/ h' ^( \再注释掉USB_Cable_Config的内容(DP上有1.5K上拉电阻)并修改中断函数
; J+ T5 _1 @7 ~6 z2 G3 |+ X/ O% S. {0 q7 D  K
  1. //USB唤醒中断服务函数
    1 O$ B/ j4 p$ @$ J) R) P- C. q
  2. void USBWakeUp_IRQHandler(void) 4 T; A( z+ Y7 U! ]
  3. {5 h- G- n& T% P, l( y6 V
  4.         EXTI_ClearITPendingBit(EXTI_Line18);//清除USB唤醒中断挂起位& T5 H0 c% B  N& y% {
  5. }
复制代码

! b4 l2 k9 |3 d修改pwr的Suspend内容:$ _" C( {8 T% H: o, c# f+ G

3 ^1 F8 v  g" D5 f) M
  1. void Suspend(void)
    ! j' P5 N  ^/ m3 ?) o2 [" Z8 L
  2. {
    6 n2 w( z* _/ R" R2 y$ M- n
  3.         uint32_t i =0;
    & ]5 j. |" F1 J% n$ d7 Y
  4.         uint16_t wCNTR;/ B* Q* C. R! P( j' D4 o! _8 \
  5.         __IO uint32_t savePWR_CR=0;) U# i7 \) U/ a9 I* o* q
  6.         /* suspend preparation */
    7 X  {7 F! K& @4 a# s* L) A
  7.         /* ... */
    0 [6 F) o8 Q" Z: Q9 ]  h
  8.         
    - W& r1 F( c# m& f
  9.         /*Store CNTR value */0 ]9 T5 f5 t+ q6 x5 K& A# _' O+ P
  10.         wCNTR = _GetCNTR();  7 E/ {, W0 g$ s$ S

  11. 4 n- d1 J! h- @7 G* v
  12.     /* This a sequence to apply a force RESET to handle a robustness case */
    ( e# {- E& z3 K6 a, W+ b
  13. 2 `  C" j. a' p! p) {: p8 z$ p
  14.         /*Store endpoints registers status */
    0 ^6 R2 C  A- `3 @
  15.     for (i=0;i<8;i++) EP = _GetENDPOINT(i);% ^! `5 _4 M" i. m) z; ^7 v
  16.         9 H$ R! M2 Y4 R' T7 V8 Z
  17.         /* unmask RESET flag */
    6 u) _7 `; I2 o* ^
  18.         wCNTR|=CNTR_RESETM;0 \, y& _* v3 a, z. E3 V/ T
  19.         _SetCNTR(wCNTR);
    2 s' j( Q4 X' O6 `+ Z
  20.         : p6 |' l! C- g+ p4 D: y
  21.         /*apply FRES */, A. O, {! V- y! |
  22.         wCNTR|=CNTR_FRES;3 T7 O( |0 }+ n: ?4 R! i) D
  23.         _SetCNTR(wCNTR);! _5 }. z: D; b& w2 X- H6 c
  24.         9 z, b2 j: l0 K2 ~: ?5 O7 i4 p
  25.         /*clear FRES*/4 U. F; W; s  W5 g1 T$ G
  26.         wCNTR&=~CNTR_FRES;8 W$ v2 @, n7 ?8 C8 M8 P) v
  27.         _SetCNTR(wCNTR);9 l" x6 B9 Z3 z  {# F2 Q
  28.         
    ; G+ P1 _2 h# D( h. z3 r5 \3 I
  29.         /*poll for RESET flag in ISTR*/% _0 F/ ?; b( M$ v6 a
  30.         while((_GetISTR()&ISTR_RESET) == 0);$ X" L0 m1 L; L% l+ [
  31.         
    $ Q! M) C8 h/ _4 i; `) {# {
  32.         /* clear RESET flag in ISTR */
    4 ~1 L0 d! s3 f2 q7 k: B0 k
  33.         _SetISTR((uint16_t)CLR_RESET);
    & m+ A" L# O! ?8 ]
  34.         % `' k3 l0 s- m8 t: C7 q4 Q" _
  35.         /*restore Enpoints*/
    # {; ?# w7 M( r2 n- |5 D2 e
  36.         for (i=0;i<8;i++)- h( Q3 n' `: |* f9 b
  37.         _SetENDPOINT(i, EP);' B' `* k9 B1 ^2 ~
  38.         9 L$ a1 p% P# X3 J+ Q
  39.         /* Now it is safe to enter macrocell in suspend mode */: h) O3 Z+ t5 {. `8 m
  40.         wCNTR |= CNTR_FSUSP;
    9 C( q- G# c8 }% U8 z$ d; b5 {) d+ e
  41.         _SetCNTR(wCNTR);
    + Q1 q* }3 |  b4 M+ P
  42.         3 b4 W9 x! |3 e) c" W7 a
  43.         /* force low-power mode in the macrocell */$ W" \, _# N3 g, P% ~1 `
  44.         wCNTR = _GetCNTR();$ i: s/ y  R( M5 X8 M; x- T% V# o
  45.         wCNTR |= CNTR_LPMODE;$ t* h' n! n  ~$ {7 q% N
  46.         _SetCNTR(wCNTR);
    $ k1 [  L/ [7 `6 W/ T3 R( C* P
  47.         
    & V. C) h4 q1 Y
  48.         Enter_LowPowerMode();! Q  R) n, C$ @( ?) X8 {
  49. }* S- x1 `5 v2 h
复制代码

  K  f3 T8 p& u4 m* o3 {+ B把USB初始化部分不要的注释掉:' M8 P5 j, R, J! L# X/ v( W& M" R
) g# z, P; @5 @8 D' o& n3 l5 q
  1. void USB_MSC_Configuration(void)- d) ?- I$ j& Y9 a) r1 @
  2. {
    + H4 i/ q% f7 M5 c. b
  3. //  Set_System();
    ! a/ V( E/ ]& _0 ~
  4.   Set_USBClock();
    # t- v9 c! P" n% L# ^4 X
  5. //  Led_Config();" L7 J" R& \9 ]" c
  6.   USB_Interrupts_Config();% h: @- p/ o$ {7 H$ h
  7.   USB_Init();
    + ~: u- _( r, r
  8. //  while (bDeviceState != CONFIGURED);
    , {4 r9 a" m7 L! w

  9. / T: L: Y1 G& [+ H0 ^- I2 t
  10. //  USB_Configured_LED();
    ( h. f- e" v& p' x9 f. x

  11. $ d" V2 ~( O6 x& \7 V2 c0 @: y6 R
  12. //  while (1); m& E+ |0 q% k& d5 m" j
  13. //  {}% C# u, e" J& D, O( _
  14. }
复制代码
9 C9 i6 J% d, m
最后在USB初始化之前给U盘定义的数组赋值,不然就只有个盘符  i. v# P" ~3 F

+ L/ W& q* u% U4 E' [5 {
  1.     Mass_Memory_Size[0]=1024*1024*4;        //w25q64->8M 给4M做U盘
    ' S; W3 L  k! g7 L# r) r0 W+ }' F
  2.     Mass_Block_Size[0] =512;                        //设置SPI FLASH的操作扇区大小为512
    1 p+ D0 V3 e1 C9 D6 R6 q
  3.     Mass_Block_Count[0]=Mass_Memory_Size[0]/Mass_Block_Size[0];
    2 Y1 w! P" N4 I2 |

  4. # y' n, B+ [' j
  5.         USB_MSC_Configuration();
复制代码

  z8 l+ i$ G( f; I: c5 K) j' i9 H SEGUXF{Z3MWXPLSF5KTU9.png * i! ~! ?1 \+ H3 A$ d( I% I

7 {+ m% Y6 s9 l- [编译下载后就可以正常运行了。
/ m1 y* m* w8 |+ a& I9 A* ?* ~+ g1 Z8 P3 r% O* ?: Y
_IXXNOSIUI%KX_{0NPN2T%2.png
  G; ]8 p  l% F/ w: e
7 L# [5 _8 _( C2 I& x& f传输文件、新建文件夹都可以了,就是传输速度感人。9 T" `: y4 P' j3 I
3 q6 ~( F- d5 N  e! L! p
以上就是USB虚拟U盘的所有内容了,内容太多而且复杂,所以如果真的想亲手试试,还是要点耐心的。虽然能学到的并不是很多,这里主要可以干茶mass_mal部分,可以学到不少东西,方便以后的熟练运用。我这里也是为以后更进一步的开发做铺垫。) _, i8 Z/ D  R

' g3 e# e8 r* k4 R, T9 f
+ n! r& @+ v: b) H* L8 M' a
# L* U# i- u( p6 K  N6 d/ j* X
收藏 评论0 发布时间:2022-3-16 11:16

举报

0个回答

所属标签

相似技术帖

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