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

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

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

6 D% e" [# h3 C1 U' _USB基础知识% H$ \3 u+ E- H+ q& n$ J
USB按接口类型分

1 C0 Y2 c( V4 o  z# v. z/ v控制器/主机(controller/host)
$ W, u* o. O# y% o! U8 e: q' O2 [
设备(peripheral)
4 F1 f: W; ]6 u- K! L* X3 Q6 c- _9 Z$ b0 D& m. y; ]
OTG(on-the-go),通过id线确定作为主机还是作为设备! A) Z7 m! M0 }$ W9 r; X0 }

  ~0 N$ p* P. w+ J! _1 K8 p按照USB速度分# p$ _6 M, j2 n& F/ ^+ {2 `6 H& b6 V% b
  Q. T1 Q. |2 X9 ]3 u6 r; G6 P, q2 T4 I
低速(low speed)7 N( j# F' o: n: L. ?* i" ^; U0 k

& G" E9 |8 @6 t, q0 C( r全速(full speed)5 n* Y, X! a$ ~
$ Y4 `, \" T$ n3 N" i
高速(high speed): w9 A7 y0 r- k4 ~# i. G4 x: q

3 {' `. y/ C8 |! a- ~% L: tUSB接口一般是4根线,VCC GND DM(D-) DP(D+)& S0 @3 D, }' ~& m' }( A! {8 A
' {* ], A2 y; I  |
低速设备:在DM线上接入上拉' c/ s. q7 L+ H- p' M3 i4 [7 G
2 l0 E8 i$ r1 J- ?" j
全速设备:在DP线上接入上拉
3 U: V. ^& ]1 F
2 L/ v0 |9 j7 e3 O( U高速设备:在DP线上接入上拉,在主机对设备进行复位后进一步的确认. y+ B3 Z! X8 Y1 ?! M( [

: R3 ^/ y, ]& [/ S# m2 g3 f2 m/ U% o5 l$ l
关于描述符3 G. R* {6 a( r6 H5 r
设备描述符(device description)2 e+ ?' x3 T6 f, `8 i
" u. L) r" \; p% B" S' U% |8 g
配置描述符(config description)3 p! U+ ~5 X5 y! z1 V- q
3 C8 O% H- X' e8 _& d6 X
接口描述符 (interface description)
( M4 d6 Z7 k9 `! g( z9 E9 S
* n/ f  `1 c9 `% v. T; `7 L端点描述符 (endpoint description)
' _0 p9 Q+ ]( o( }2 P! d5 [8 s+ p1 _
(HQG%R@V4W]{7LOY8DZE{L9.png
- r2 x% ^3 \4 @  t
7 W- _/ ?; q0 |! q设备的“身份”信息存储在描述符中。每个USB设备中都有如下描述符。需要注意的是一个USB设备只能有一个设备描述符,一个设备描述符可以有多个配置描述符,一个配置描述符可以有多个接口描述符,一个接口描述符可以包含多个端点。3 A; O2 X3 G- V) @. e9 {3 o, V

) f( H! @" ~( H( r) g% a* f7 u8 F关于传输

" q" Y$ Q) H% ^, O* X" W4 _1.在USB的通讯中,有传输(transfer),事务(Transaction),包(packet)三级。包是最基础的传输单元,与TCP/IP协议中的MAC层协议作用相同。: Y& }. z) A$ J7 c( r9 n, r2 M
2.在一次传输中,由多次事务组成,每次的事务又由多个包组成* ~5 f4 @; w) L3 k( W  a
3.与众多协议相同,较高级别的协议的报文是基于/内嵌在低级协议的报文当中的,在USB中也不例外,例如,包中预留了DATA位,其目的就是填写报文, p! T: S  w' X/ f0 T% T* I" p+ c
STM32F103芯片自带了有USB模块,可以用来做从设备,不能用作主机HOST。这里使用USB的目的是讲USB作为一个大容量,这个可以基于官方USB Mass_Storage例程来移植。这里就不过多的介绍里面复杂的通信原理了,要彻彻底底搞明白并灵活运用取来还是有很大难度的,因为内容太多了。这里仅仅介绍下移植过程和描述符相关的重点内容。" [5 R* z& E* ~$ u/ @* ~; S
% T3 Q. w* c( f+ \4 G
准备工作" @4 q, D# b7 H8 ^
第一步
$ i4 h& K" ^' g) i
确保自己的KEIL开发环境已经完善。! Y, C# Q$ y2 D3 F2 E" @6 G

+ ^0 W9 j; K% G+ H第二步, G  d  F  t! E9 o- V
下载好了两份ST官方的库文件(我是基于标准库开发的),一个是标准外设库文件,一个是USB库文件。如下图:
! D5 C, S6 @8 l( i& a  n0 C5 M7 k+ U0 {3 Q7 e4 J$ q' h0 g
AZ7H5GQWT4J%{N{6){9E]{3.png 9 E; y1 O2 c' |& d1 r

. b  v7 C1 ?0 V4 v- J2 D& e) ?第三步
6 w" H/ A; S$ b' f: c+ C建立一个带有存储介质驱动的STM32基础工程,存储介质常见有SD卡、外部FLASH芯片、内部的FLASH空间。的我是基于一个外部flash的工程去实现的,芯片具体型号是W25Q64,64Mbit的空间,换成字节就是8MByte。驱动部分如下:
4 L' s5 K/ L1 A- U, t) k6 m; p0 `; u2 A) o1 D/ z
C文件7 B& Y  V* [) i. m2 i. k# @5 ]3 M
  1. #include "fy_w25qxx.h" , ^7 Q$ p* F0 V8 |- h6 U
  2. 1 t2 i1 d6 ^! l# E' G' B
  3. u16 W25QXX_TYPE=0;//W25Q645 `8 X. \! o" d5 T  P$ r- V
  4. 9 Z8 L3 n( D5 Q7 H
  5. //4Kbytes为一个Sector,16个扇区为1个Block,容量为16M字节,共有128个Block,4096个Sector
    + @/ j& m  M& o1 |' `
  6. //初始化SPI FLASH的IO口
    : W9 s6 V7 B: H
  7. void W25QXX_Configuration(void)8 J* _2 L) r* n  n
  8. {        
    2 |4 Y5 n. o2 c6 E$ f) F& H) u/ H: k- h
  9.         SPI2_SetSpeed(SPI_BaudRatePrescaler_2);//设置为18M时钟,高速模式
    0 R. H& E! J/ `2 ]  Q& M
  10.         W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID.  5 `0 K7 R4 h/ d
  11. }          - J* L0 @  G" e8 ?/ ^. D' K

  12. ) Y; K; k5 S+ W; G/ }5 A4 U
  13. //读取W25QXX的状态寄存器5 M" f' q' u4 F; O0 |/ R
  14. //BIT7  6   5   4   3   2   1   0
    ) c- x/ d: e2 j2 D
  15. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY( `+ R- a% D: C; q& p; @) A9 m, W% v7 Q
  16. //SPR:默认0,状态寄存器保护位,配合WP使用: w2 L5 a6 o6 {% X0 S
  17. //TB,BP2,BP1,BP0:FLASH区域写保护设置
    8 f: |, r( _, I0 |* a9 ?
  18. //WEL:写使能锁定9 P2 T, o2 b4 L2 c+ u( f: P
  19. //BUSY:忙标记位(1,忙;0,空闲)
    ' y: {& f9 X, F4 A1 ~
  20. //默认:0x00
    : j5 f& ?+ K) e# j% K/ P1 d
  21. u8 W25QXX_ReadSR(void)   ; l, H3 a* d3 P3 b& v9 C
  22. {  
    8 p+ O* N, L5 i% d' ~9 [# O
  23.         u8 byte=0;   
    ( ?9 e; ?5 l9 J% D* q" e
  24.         W25QXX_CS_L();                            //使能器件   # D+ U( ?, z' c3 Z) t; W
  25.         SPI2_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令   
    5 ~) g# L; O8 K1 E7 T
  26.         byte=SPI2_ReadWriteByte(0Xff);          //读取一个字节  + d5 w+ q/ U' ^
  27.         W25QXX_CS_H();                            //取消片选     
    0 t5 a* {$ d2 {4 S5 r( h; a/ _
  28.         return byte;   2 f# v9 @. |3 Q/ S+ K
  29. }
    1 T- F5 A% W* _' @# @& J
  30. //写W25QXX状态寄存器
    5 e6 f$ V" u9 e- a+ B
  31. //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
    3 V) f  V6 y- c. A7 B# q
  32. void W25QXX_Write_SR(u8 sr)   
    * ]8 d* [1 y6 u/ T
  33. {   4 W  V7 C8 p6 w/ [' N
  34.         W25QXX_CS_L();                            //使能器件   
    6 q$ x: l/ V4 D/ F, d& n
  35.         SPI2_ReadWriteByte(W25X_WriteStatusReg);//发送写取状态寄存器命令    3 C% s- S! h* ]. w3 a
  36.         SPI2_ReadWriteByte(sr);                       //写入一个字节  
    / J' l( S8 U, g5 D; w8 D( ~$ z
  37.         W25QXX_CS_H();                            //取消片选                  
    6 U/ [# r! T8 M( }$ ]* p: P
  38. }   / p" V/ C2 ~# t) ^# ?- ?; E: U. ~0 w
  39. //W25QXX写使能        - O) h7 O/ F- J, o
  40. //将WEL置位   % F/ z2 u6 F) ]
  41. void W25QXX_Write_Enable(void)   . f( k$ g$ g9 S$ T. R
  42. {6 }% o/ c7 p% |- f7 E
  43.         W25QXX_CS_L();                                  //使能器件   
    - ?* I  ]/ ^$ f) U$ O
  44.     SPI2_ReadWriteByte(W25X_WriteEnable);         //发送写使能  ! ^, t) z6 g+ x
  45.         W25QXX_CS_H();                                   //取消片选                  
    * t3 Y. }# a% Q' z9 c* Q' b" c- @
  46. } 1 m4 Z. B9 Q7 |( p
  47. //W25QXX写禁止        
      m, u3 Q% d9 B
  48. //将WEL清零  4 y+ o9 o, ^4 m1 W( i1 U& X4 I
  49. void W25QXX_Write_Disable(void)   0 [) j  j. O; `: {
  50. {  
    ( T5 O! x1 J+ N% P* r0 ]
  51.         W25QXX_CS_L();                            //使能器件   , X# K( C6 i! Z$ i( o* k
  52.     SPI2_ReadWriteByte(W25X_WriteDisable);  //发送写禁止指令   
    $ q% f7 O+ L8 N6 f: l# t4 t
  53.         W25QXX_CS_H();                            //取消片选                  
    % S" X( O3 \+ m4 n
  54. }                 ' I3 P- B$ H( m5 I

  55. # g) H9 e7 Z, n: A- |) E2 f
  56. //读取芯片ID 1 r  X5 T9 d, M
  57. u16 W25QXX_ReadID(void)
    + u# J+ V; c( E1 _7 S6 R
  58. {
    ) o  ~* [+ x5 T& K: j- q' T
  59.         u16 Temp = 0;         
    6 d' `8 I/ |  |# G4 g
  60.         W25QXX_CS_L();                                    
    7 [# |: p8 E5 y7 ^6 X: T: T
  61.         SPI2_ReadWriteByte(0x90);//发送读取ID命令            
    & f6 f! J8 _: H& d
  62.         SPI2_ReadWriteByte(0x00);            
    $ \" z5 W8 x/ Y. D
  63.         SPI2_ReadWriteByte(0x00);            
    ! I8 a8 T8 e) p& W
  64.         SPI2_ReadWriteByte(0x00);                                    
    7 q+ K+ {( t) ?  H; t& a% q
  65.         Temp|=SPI2_ReadWriteByte(0xFF)<<8;  $ K! \) y3 v- d* q$ C: g5 ~9 H
  66.         Temp|=SPI2_ReadWriteByte(0xFF);         
    % `5 S4 X3 [- K; _
  67.         W25QXX_CS_H();                                    
    & [$ W$ l! O% _+ N) }. I
  68.         return Temp;
    % v& p. E* }# k- i1 Y7 a) k
  69. }                       8 b7 `# b* a# Y5 K
  70. ( D- w3 o# q5 ]( S; \0 z+ U
  71. //在指定地址开始读取指定长度的数据
    + R4 S$ Q; f% x. E# J7 ^- C/ G
  72. //pBuffer:数据存储区,ReadAddr:开始读取的地址(24bit),NumByteToRead:要读取的字节数(最大65535)0 |. d. f: p& k2 S: B3 Y/ u
  73. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   
    % M% x: i- K( o9 W6 @
  74. {
    ; d7 \3 s/ ]! }% O4 [: m2 X5 t
  75.          u16 i;                                                                                       
    8 V* S0 o, d1 u% ~, Z7 ?
  76.         W25QXX_CS_L();                                    //使能器件   # \( E% V7 E5 {- z1 |) d+ B
  77.         SPI2_ReadWriteByte(W25X_ReadData);                 //发送读取命令   
    " l( G9 J, Z. k/ j- i
  78.         SPI2_ReadWriteByte((u8)((ReadAddr)>>16));          //发送24bit地址    * Y( f4 `1 k7 f! i9 K
  79.         SPI2_ReadWriteByte((u8)((ReadAddr)>>8));   + l1 ~# v4 M0 @( V! Y6 s% p9 R
  80.         SPI2_ReadWriteByte((u8)ReadAddr);   
    $ t+ q4 S; S  D7 |1 t" B
  81.         for(i=0;i<NumByteToRead;i++)+ t3 u0 J6 D/ f6 R
  82.         { ' D( ^5 Q3 G! x9 A5 @6 o
  83.         pBuffer=SPI2_ReadWriteByte(0XFF);           //循环读数  
    , Z% W/ L. a; s. ]( O! m0 N6 z
  84.   }
    ( C9 d* U" v9 h3 I% L+ L/ g7 N% W
  85.         W25QXX_CS_H();                                                   
    , v6 J! B8 D; i, L* z; L
  86. }6 W0 q: _0 z9 F8 A
  87. ( p% }6 F; k+ _; K6 G
  88. //SPI在一页(0~65535)内写入少于256个字节的数据- o% ]- @* v" V3 A. Q1 z3 p8 p
  89. //在指定地址开始写入最大256字节的数据3 [: {! B. q! Q. l; y' g$ ?6 f
  90. //pBuffer:数据存储区,WriteAddr:开始写入的地址(24bit),NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!         
    + p. B! C$ N7 W
  91. void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)2 S7 x0 f& d! c( G6 {3 ^/ _) r
  92. {
    + f& z* E% h6 M/ l% K. m; n& j" F5 Y* C' C
  93.          u16 i;  
    " J0 N* Q  |- I' Z- i' P% [- q- t. c/ z
  94.         W25QXX_Write_Enable();                          //SET WEL % Y5 I+ D; G: @' m2 N1 ^$ `/ Z  _
  95.         W25QXX_CS_L();                                    //使能器件   
    4 M: J2 j; u, x* U) O
  96.         SPI2_ReadWriteByte(W25X_PageProgram);              //发送写页命令   
      Y) c* l9 b  }8 z' U4 h
  97.         SPI2_ReadWriteByte((u8)((WriteAddr)>>16));         //发送24bit地址    $ ]( z. S- `4 h* x/ X& F6 c6 o) c
  98.         SPI2_ReadWriteByte((u8)((WriteAddr)>>8));   
    : h0 U3 W; P- b+ `; Q3 S% V) K6 o
  99.         SPI2_ReadWriteByte((u8)WriteAddr);   
    ; t& e; S2 ^3 I/ a' ?7 h3 [
  100.         for(i=0;i<NumByteToWrite;i++)SPI2_ReadWriteByte(pBuffer);//循环写数  $ e) a# C& a  Y9 z- ~0 T% I
  101.         W25QXX_CS_H();                                    //取消片选 " C# `; F. z3 j, {& z# v
  102.         W25QXX_Wait_Busy();                                                           //等待写入结束
    ! P8 `# e* C; a! [7 L# G- h' B
  103. } $ l8 P7 ^, @0 Z; \
  104. //无检验写SPI FLASH
    - \  O- X7 u; @8 m% V$ Z0 i+ L9 Q
  105. //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
    & o$ j9 Z# l. r3 n& O) x. Y
  106. //具有自动换页功能
    ; U. B+ X7 a! w! d" U! R" ~) B
  107. //在指定地址开始写入指定长度的数据,但是要确保地址不越界!
    + Z* g8 K2 f* J' q
  108. //pBuffer:数据存储区
    $ t% C; c/ v1 K; ]9 |
  109. //WriteAddr:开始写入的地址(24bit)
    & k+ t# `2 Y' Z4 N5 B5 J7 @
  110. //NumByteToWrite:要写入的字节数(最大65535)
    - u" N. A" s" N! w( `
  111. //CHECK OK
    ! o5 w: X; ^; X# w: I( U; T
  112. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
    # ]7 }- s& i. a' R: l
  113. {                                           % e" r: q8 g; Q* _6 @  Z
  114.         u16 pageremain;           * O8 o6 A3 X- f
  115.         pageremain=256-WriteAddr%256; //单页剩余的字节数                             / U/ G7 D6 C7 m% a6 [
  116.         if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节$ \+ U5 v" F, f, [* V7 W
  117.         while(1)9 A( B. [) C2 X
  118.         {           4 Z- O8 ^+ r* {5 m! c' ^/ k
  119.                 W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
    : |% y, A+ c2 b! r( c
  120.                 if(NumByteToWrite==pageremain)break;//写入结束了
    ' V8 W' `7 G" X5 y5 m/ Z( W5 w
  121.                  else //NumByteToWrite>pageremain1 x, Q$ U0 f" H, M$ X
  122.                 {
    . \$ G# w  S2 H4 t* i1 F
  123.                         pBuffer+=pageremain;. @7 N$ v+ D8 o( m; B! H
  124.                         WriteAddr+=pageremain;        
    ; R3 [* w+ P: ]2 w* v2 b

  125. 6 @$ H$ T; s: `' z
  126.                         NumByteToWrite-=pageremain;                          //减去已经写入了的字节数& [: J# ]% r; ?
  127.                         if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
    ; Z0 a/ U) `* A2 T% M/ T" p! \4 W
  128.                         else pageremain=NumByteToWrite;           //不够256个字节了/ l! \( K# j6 d& g
  129.                 }; r" @; P: H6 c  I+ ~# s
  130.         };            ( L" w, e. s: H  T) P
  131. }
      t! D; t+ x0 Z3 C0 m+ }, y
  132. //写SPI FLASH  
    2 G- |  A9 L7 G3 f/ k5 H% M( ~8 E
  133. //在指定地址开始写入指定长度的数据
    0 ?  c  @# z% I: x2 @
  134. //该函数带擦除操作!
    7 x) M1 N8 z0 B5 Z: c6 L8 X* X
  135. //pBuffer:数据存储区. I5 P, e" l7 [# b( t  ]+ u
  136. //WriteAddr:开始写入的地址(24bit)                                                8 O) g% W4 o# l6 W
  137. //NumByteToWrite:要写入的字节数(最大65535)   ! V1 K; t$ A, s+ r, B& ?( q
  138. u8 W25QXX_BUFFER[4096];                 ; _; @' @  w: u" I
  139. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   6 R/ y( D2 W2 R* [/ x; y* K: t3 A
  140. {
    , m3 z# d5 `8 {6 S4 p
  141.         u32 secpos;
    3 v! d- F6 c, g+ Y6 O; E
  142.         u16 secoff;& j3 {# _4 [. Y9 ~0 o$ v
  143.         u16 secremain;           
    5 c: j+ B, _$ q% o
  144.          u16 i;   
    9 a* l' Y; X0 S
  145.         u8 *W25QXX_BUF;         
    4 q( V+ ]+ b3 Z* k" s
  146.         
    , u% O' A, q4 w
  147.            W25QXX_BUF=W25QXX_BUFFER;
    $ m% g5 u( `# r
  148. //        
    & h: J# q+ n8 a7 Q+ c) z
  149. //    W25QXX_BUF = (u8 *)_mem.Alloc(4096);//申请一个扇区大小的内存0 r3 s9 a# c1 @; s: P
  150. //    if(W25QXX_BUF==NULL)        return;
    ! C6 C% z' J9 n
  151.         # C, `* A- g, p! d& ~9 M
  152.          secpos=WriteAddr/4096;//扇区地址  
    1 ~# v7 }: ?/ t* }
  153.         secoff=WriteAddr%4096;//在扇区内的偏移2 |; T+ i! L: k0 X' o4 _+ [
  154.         secremain=4096-secoff;//扇区剩余空间大小  
    , V0 A2 p+ k! U1 C
  155. 5 M4 c% D9 G, i- {; g
  156.          //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
    . O# D3 R9 y# W1 i! P1 J
  157.          if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节% g! `* p6 _4 }3 s+ j: b
  158.         while(1)
    0 a! H# T% ~! L9 G/ e9 \
  159.         {        : E6 s5 }, m  h% ^  {0 D
  160.                 W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容5 w7 H: z# p' H: w& ?9 T7 G# x0 o; m; z
  161.                 for(i=0;i<secremain;i++)//校验数据. C8 ?' c: X( ]; C8 P6 D/ `" a
  162.                 {
    * Z  h& s, @8 g/ K" G- W7 M
  163.                         if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除            
    & b% t& e: F7 t! Y2 t( H
  164.                 }, A$ V; S# D2 n3 x
  165.                 if(i<secremain)//需要擦除3 X5 Q% w' \  t& g3 o  e8 y7 A
  166.                 {  p% K3 L( q+ Q& Z: P5 i+ d9 a
  167.                         W25QXX_Erase_Sector(secpos);                //擦除这个扇区5 q: a% P4 o/ G
  168.                         for(i=0;i<secremain;i++)                           //复制
    . x6 d8 q- e7 [. Y( Q8 |6 F; H4 G
  169.                         {. Q2 ?  Y* n# ^4 R) N9 y, U
  170.                                 W25QXX_BUF[i+secoff]=pBuffer;         
    $ |! x6 i3 F4 q
  171.                         }
    8 J7 l* o  a+ z9 [! L
  172.                         W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  9 B* r! N. C7 W3 R
  173. 9 ]% }; @$ Z6 b  z: B
  174.                 }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.                                    / u9 u4 m0 E) {. G
  175.                 if(NumByteToWrite==secremain)break;//写入结束了
    ! G$ Q% G' |# ~% ~- ~- \
  176.                 else//写入未结束; ]7 |* c" T& @' R3 r0 Q7 V
  177.                 {
    / w0 n  O' L9 n/ M/ v6 m5 ]) s
  178.                         secpos++;//扇区地址增1
    ! n7 C/ q' k0 F, O  P
  179.                         secoff=0;//偏移位置为0          + J: z0 i9 B7 d! n( R% K
  180. " ]; F7 Q$ K" `$ q% G
  181.                            pBuffer+=secremain;                                  //指针偏移
    # Y0 |! l/ Q, q- A3 r7 g
  182.                         WriteAddr+=secremain;                                //写地址偏移           ( I' R+ b7 `% t  ?8 X0 O
  183.                            NumByteToWrite-=secremain;                        //字节数递减
    - R7 X) v. @7 ~5 B6 d' G: R
  184.                         if(NumByteToWrite>4096)secremain=4096;//下一个扇区还是写不完2 k4 m7 I4 d. j3 k' X" S/ i
  185.                         else secremain=NumByteToWrite;                //下一个扇区可以写完了
    0 W  c  Y% z: S6 f0 r+ q
  186.                 }         
    - ]1 b$ q3 c! Z: ]/ L
  187.         }
    1 h- N9 F4 [( Z# a
  188. //        _mem.Free(W25QXX_BUF);
    ' S6 G/ n; h" q) m
  189. }
    5 ?' [/ i1 K7 y7 b
  190. //擦除整个芯片                  $ G2 d8 N* ~; k& P! C
  191. //等待时间超长...
    ; M. A7 ~# c/ p, Y+ B
  192. void W25QXX_Erase_Chip(void)   . h1 |5 j( g5 R7 m  y) r: O
  193. {                                   
    - }5 Y! r. L8 q( n% k9 [1 T
  194.     W25QXX_Write_Enable();                                  //SET WEL 9 x# O0 ~; m/ W6 {  I/ }" I
  195.     W25QXX_Wait_Busy();   
    % `1 U/ k6 r: E% ?( P+ l
  196.           W25QXX_CS_L();                                    //使能器件   
    " Q. p7 l: g0 G) v5 d
  197.     SPI2_ReadWriteByte(W25X_ChipErase);                //发送片擦除命令  
    ; i4 \) `( T9 `, j0 |. d: C
  198.         W25QXX_CS_H();                                    //取消片选                  
    4 n8 d1 ]6 E6 f( N2 o9 \
  199.         W25QXX_Wait_Busy();                                                      //等待芯片擦除结束
    $ w5 l* T( Y* R5 ?  O/ J/ a+ E
  200. }   + M" u! v. G% B/ ^! b9 F
  201. //擦除一个扇区
    + r% j# N. x1 V1 c5 J( A
  202. //Dst_Addr:扇区地址 根据实际容量设置% m. t& F& N% h: h  k
  203. //擦除一个山区的最少时间:150ms+ S, Y+ R' b% h* S# S: i2 c
  204. void W25QXX_Erase_Sector(u32 Dst_Addr)   . M. ]2 e/ f) E1 V) ~* {+ G
  205. {  $ J8 M. x0 ?/ t+ z9 |- o$ H
  206.         //监视falsh擦除情况,测试用   6 |2 g' e- j- F9 Y0 y0 J
  207. //         printf("fe:%x\r\n",Dst_Addr);         
    1 K+ L9 o, J4 [6 V/ h% Q- `9 S: |, X
  208.          Dst_Addr*=4096;
    - J' b% F, ~6 x8 y/ N. h8 l1 E
  209.     W25QXX_Write_Enable();                          //SET WEL         
    % b# F  ~( x5 h
  210.     W25QXX_Wait_Busy();   & [' T& h" V, d* b2 {' ^% b
  211.           W25QXX_CS_L();                                    //使能器件   ! b3 ^/ n0 H* e) u6 }1 E  O
  212.     SPI2_ReadWriteByte(W25X_SectorErase);              //发送扇区擦除指令
    1 [7 B  [/ H% z* l4 ?
  213.     SPI2_ReadWriteByte((u8)((Dst_Addr)>>16));          //发送24bit地址    4 y( y! j- V5 s) z7 s" d: D* m
  214.     SPI2_ReadWriteByte((u8)((Dst_Addr)>>8));   & |; `2 |" ^9 w* _$ ]: v; M( Z+ Y- S
  215.     SPI2_ReadWriteByte((u8)Dst_Addr);  1 V% b: k! \5 ], ?; m9 w0 ?# N
  216.         W25QXX_CS_H();                                    //取消片选                     [' B* e. m, r* b* a( l) Q
  217.     W25QXX_Wait_Busy();                                                      //等待擦除完成) \. f& Q4 d8 L0 k6 Y* C; c9 Z
  218. }  
    4 g- N* w, ~) L6 b( u" r! A
  219. //等待空闲+ L; s' G6 [7 q$ f0 k; [$ s
  220. void W25QXX_Wait_Busy(void)     X" [# `, g, R0 R* d7 _1 k
  221. {   
    , r  \3 |2 |7 z3 f
  222.         while((W25QXX_ReadSR()&0x01)==0x01);                  // 等待BUSY位清空4 _) ^/ \' K* B* v8 E
  223. }  
    6 R& N9 h# a" |- S! ~: M8 i
  224. //进入掉电模式) V  D/ K7 R& J* O
  225. void W25QXX_PowerDown(void)   
    5 K( l5 O; O/ \3 y
  226. {
    + @9 d0 q9 E. N; e, _
  227.           W25QXX_CS_L();                                            //使能器件   - b7 m- E9 M' }
  228.     SPI2_ReadWriteByte(W25X_PowerDown);        //发送掉电命令  
    7 M1 x# C7 c) k
  229.         W25QXX_CS_H();                                    //取消片选                   . f0 ^/ v) t' |% F' l' P& B4 h
  230.     Delay_us(3);                               //等待TPD  4 j2 Z. Q7 m% f& R; @$ q! G
  231. }   4 U/ b) B. C: Y2 d  V# W! w  o  T
  232. //唤醒
    0 |1 W. r' }3 ~3 X
  233. void W25QXX_WAKEUP(void)   
    , v! `) {; n0 |- X7 }
  234. {  - b- E" U: s& m0 t7 m
  235.           W25QXX_CS_L();                                    //使能器件   $ D4 i! j: b( W- I" {
  236.     SPI2_ReadWriteByte(W25X_ReleasePowerDown);        //  send W25X_PowerDown command 0xAB    2 |( q. P/ T* S0 z$ E) C5 _4 ]
  237.         W25QXX_CS_H();                                    //取消片选                  
    ; p' @- n  f" P- ~/ Z* O6 j
  238.     Delay_us(3);                                    //等待TRES1
    . f$ W+ l/ J# r# h) M
  239. }   
复制代码

. W1 H. d! `( p6 tH文件
2 ?% t, C: ?0 J5 f# ]* g5 y
5 H4 _4 P% `0 u5 k
  1. #ifndef __FY_W25QXX_H# ]' ~5 }1 \0 b' S2 B
  2. #define __FY_W25QXX_H                           
    , M8 p' o: `; G3 a- ]
  3. ; w: P; t9 v; [/ M0 ~- [
  4. #include "fy_includes.h" : e* S- @, {2 i3 w; p, g( a

  5. 0 j' D, A6 f5 O" |- o; t  G# P
  6. //W25QXX对应唯一识别ID
    + W1 w: s3 c5 F
  7. #define W25Q80         0XEF13( G' k  F+ D$ m4 N, w
  8. #define W25Q16         0XEF141 ^/ X9 I+ j- }5 c, ~: x. J
  9. #define W25Q32         0XEF15
      W1 s% [8 s& Q$ ^2 Y5 ?- m
  10. #define W25Q64         0XEF162 m+ C6 b7 _/ v; ~+ C
  11. #define W25Q128        0XEF176 A/ E+ i; m# g/ l( o& K! `
  12. . F/ ~0 U$ T9 Z- J9 M9 `$ N5 y! O
  13. //指令表
    ' ^  S' x8 m  t7 r+ c2 n' `
  14. #define W25X_WriteEnable                0x06
    $ Z/ U6 v4 Q% [
  15. #define W25X_WriteDisable                0x04
      k$ _0 {! j3 ~( z6 c
  16. #define W25X_ReadStatusReg                0x05 ( J9 `  ^! v. X9 P0 Q
  17. #define W25X_WriteStatusReg                0x01
    4 v& D2 ]- W# j0 X+ H7 s
  18. #define W25X_ReadData                        0x03
    9 _- T# b* {, p- m( J) Q3 R
  19. #define W25X_FastReadData                0x0B
    ! l# A: c2 ~! Q- L& e  C7 i
  20. #define W25X_FastReadDual                0x3B
    8 R6 w( B, N8 U4 g# q# v
  21. #define W25X_PageProgram                0x02 0 e* I' ]: l0 a0 I! q% S
  22. #define W25X_BlockErase                        0xD8
    - o* H2 h; L! H( `; d
  23. #define W25X_SectorErase                0x20
    # d. R8 \6 d: l6 q1 a
  24. #define W25X_ChipErase                        0xC7 ) c" m- O3 O; @+ W+ U
  25. #define W25X_PowerDown                        0xB9
    7 F$ ~3 G2 n: k
  26. #define W25X_ReleasePowerDown        0xAB 3 i. d# a+ Z" m/ A* J  F4 U5 }
  27. #define W25X_DeviceID                        0xAB
    2 d$ P* d. p+ @& v
  28. #define W25X_ManufactDeviceID        0x90
    6 }3 J& c; P& @1 L6 l" B. W* _
  29. #define W25X_JedecDeviceID                0x9F + ]: C& l; y7 E& o6 f" y$ }. q+ V
  30. 9 Y- c% d7 y+ j6 `; L9 K0 V
  31. void W25QXX_Configuration(void);# F) [1 c* K+ v1 p! _
  32. u16  W25QXX_ReadID(void);                              //读取FLASH ID% g/ D' ]! s9 V% S% S( @: n
  33. u8         W25QXX_ReadSR(void);                        //读取状态寄存器
    9 p$ w+ U' s7 `( H5 y
  34. void W25QXX_Write_SR(u8 sr);                          //写状态寄存器% c4 R$ e: e/ w' h
  35. void W25QXX_Write_Enable(void);                  //写使能 " r9 C, _. p: h8 d0 q# W% k8 i
  36. void W25QXX_Write_Disable(void);                //写保护
    + r4 \' u6 z% S4 W/ ~& T
  37. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);
    ; h" y5 _2 K4 n4 f( k7 F$ d
  38. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //读取flash
    : l5 ?2 {; i- S) _2 a5 m
  39. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写入flash) Z! f! N' ]7 t) b2 _% K  C
  40. void W25QXX_Erase_Chip(void);                      //整片擦除, h! T$ z/ k  v- c+ R
  41. void W25QXX_Erase_Sector(u32 Dst_Addr);        //扇区擦除
    4 O, X( ?; ]. d  e* U; i8 T
  42. void W25QXX_Wait_Busy(void);                   //等待空闲2 C. o7 W4 b, _/ _8 P
  43. void W25QXX_PowerDown(void);                //进入掉电模式
    * I0 x9 h4 B+ }1 A7 }
  44. void W25QXX_WAKEUP(void);                                //唤醒4 V3 v8 ~- {# M! ]2 v
  45. % v1 s' ^1 k0 A$ ~! M* d
  46. extern u16 W25QXX_TYPE;                                        //定义W25QXX芯片型号                  
    3 a) F. O% W2 n- j3 K
  47. ' [, \- ~- D' m: i5 x+ p6 `) Z
  48. #endif
复制代码
$ Q! w: v# e8 D1 x# f( X5 z
基础工程如下:
& s( N' f+ Y; x% e* M# [3 g% `, V
, U/ ]* |! ^- r6 ~ CIG]SRYALJ}8)5NQ[MAA~BW.png 9 Q& p, T$ c2 O. _5 S

( t& O) g- a7 Y2 m第四步:USB代码移植
+ A& {: x) h& ~& g5 D; d/ u拷贝USB底层库到工程根目录,新建一个目录命名成USB,拷贝Mass_Storage例程下的src和inc目录下的所以文件到USB。
4 ^: E  [' V3 c# u3 j8 ^8 Z% |& W( j- o! k0 M9 \3 `
6Q]9Q9812B]OXMA8GNV[PCC.png 3 D- ]3 z7 U6 S( D
9 U0 j# o5 S' v% g9 [3 }% n
拷贝过来是有37个文件,这个时候需要删除一些不必要的文件,因为官方的例程里面有nand,这里的话先删除以下文件' V9 v7 a3 b" ]: E) O

- q- S3 l/ m3 q; |6 W" G% E VN1JDR_I@JO271YJKNV1(HE.png
' s* b6 r) T8 B: c. w* J( ?4 a* |" D  Z
4.打开工程把分组和文件及包含路径添加进去,这里就不细说了,结果如下$ M# X. ?* E6 C  k7 j7 D2 @  G0 M

9 G5 Y# C" b, E- e. [, \ X$DZH7HHYEC}266_`72~4EH.png
7 F/ F9 ~0 w8 R9 \( |4 J& d) L1 g) m6 u# k* A* T3 y: A  d9 `
打开usb下面的main和it 把重要的代码先拷贝出来! L& {! H: I2 J$ B9 J- C" Q* J
3 R# M3 N' z/ t2 Q4 A
OA]E5J8OQ0[B{UJV@KXPUG2.png
; }, k3 d& l- ]/ q/ ~9 F" Z
# R" W* q" ?* n+ ?# H5 R: ^把main和stm32_it里面的内容拷贝到自己的main里面进行下封装去掉不要的保留剩下的,然后就删除it和main三个文件。3 l; J: C4 z& r2 j1 ?& B

& B- i, s8 z" C+ w @G5L9U9`9]${JVP}AD@KN0E.png 0 d2 E7 {' s) b( R, G5 S6 D+ e
  `" r( W- t: Y
然后编译下,这个时候会出现一堆报错,需要慢慢一步步搞定,编译前注意路径包含,
! i) U0 p/ x" J$ r. I: R; |
0 h! H! k. B: \: M [~3UDXP_99@A_@~XV14BNPB.png # a8 ~4 f& }& ?5 T& T
1 |/ G! e  B2 L" h5 ~
定位到第一个错误进去% x4 d- ~8 k  [" Q/ `% s& i' V9 I

) h6 S, e- m1 r) z 5L28CK8A4)MY@OJR7KK~KOY.png - s$ r4 D7 B: N$ \* U4 T
/ R+ c8 ~0 c6 P& H  d) @
这个定义的是官方的开发板 我们直接修改成自己的公共文件就可以,并且注释掉开发板定义
- i* v. b0 F" [9 @7 C" ~8 }' h( p8 ]. R8 c9 f5 w) a
TR]6[QMF5`A7X$MYZIL])9I.png ( `" e. f" V$ k4 x/ x- L' G' y9 N
3 o7 ~$ h' c8 B8 R# ?& m7 s# i2 k' E) S
公共文件如下,并且添加usb部分的头文件6 G. T& x$ ?& d( u
2 O4 v* M: Z* w
Z7Z6YX`QZR}6~KCC_GDDLD8.png 1 \0 a  j# _2 v. h

$ V' i) T; i: T9 j  x7 u继续编译,定位第一个错误,这部分主要修改hw_config文件, Q0 f0 V( R/ C* m; N

: G" C( J8 [% O) x _9RUE5O1M][P)IRX]S2S_$D.png
! o) d: d, t1 ]% ]2 n& }5 s$ y" j' `: N  i$ z) G; I7 n
删除Set_System函数1 L, R, h& M# M
1 @5 K7 O* n- d4 i* B6 ]
N~R76S188E[X(N6NID1WQ$V.png
6 Y  J# {; l3 r5 h1 `1 U
! g' e+ p2 q( b" Q3 |8 M9 i6 e删除Led_Config函数6 n# U3 O  W3 w- \* |

$ @8 g( F8 [( U# T" \, F ~HA$Z23~@)XV2TEQ8CUTS.png
) a& Z' v3 C; L- I* |8 e, ?# u
7 N8 Y  E# K# L6 J注释LED相关的内容
. |* F( d$ d. ^  B2 E+ B9 q3 n
! O( d; J% d# `, C+ s, O* @( z _FKQ$_}1}S8W%PG(2VT2]C7.png
- B5 |5 `  o( ^8 m
) g! W1 Y7 M, s/ ?$ i' a% p删除USB_Disconnect_Config
# H- m* u7 Q6 b# F; @
5 _' a% j' q4 F5 I 4K@_T@@XVZ@P]EEH37}QAJX.png , _" e. q7 r3 z
: s% x% B7 q5 _( r
接下来主要修改mass_mal部分,我这里是只有一个flash,所以我修改成如下:7 |( i& D: k) _5 e' N
8 F4 d  g( L3 v# k6 D" n, @" t
2_1P4[7IG68PG%9TS@41BI4.png
: k4 |6 n. I( f6 B/ D( ]7 r2 i0 E6 O
头文件
  B: A, k' e% L8 x) a4 z$ ]: o7 O
' m* d* F* L% Q1 H3 Z (V0@{X{W8~L[2Z[Y7)VNU.png
3 B) j1 m1 l4 _4 b1 K- I" U# o+ i5 k0 ?' h4 [( b1 n
下面修改memory文件,主要修改变量定义,C文件和H文件分别如下
$ q1 N2 H; `( d) }9 E) r: u. B* d( \( B' E4 G5 ^  d6 A- J6 E
T]Y~~F2{V(H`V{$X(}FMNNJ.png
7 v/ E; c# D- i/ d! w- ], L, k( D; Q+ p5 _: X! ^4 p  j
接下来是usb_scsi部分的变量定义
6 }- k- c. m4 W' ]: H" ?6 s- j
# g# A! O/ m* H2 ] R$Z1OIMFO6JWKJE~XX[{2$B.png 4 E0 v( L, r9 v! g0 `
: B% I, A0 k+ d2 X0 F* {- i. Q! F
这时候再次编译就发现没有错误了,但是这个地方USB中断部分还需要配置下,官方用的宏定义方式去实现不同的代码,这里一开始就去掉了宏,所以最终修改如下:
+ Y* q0 P( \) ^5 W
' ~; H, E( [, K) f3 [9 B  d' [& U; Z
  1. /*******************************************************************************
    * Q% u: g# p4 _( {% p
  2. * Function Name  : USB_Interrupts_Config
    ; |- n& }) |' R7 ~
  3. * Description    : Configures the USB interrupts  X! g$ H9 E! L, R
  4. * Input          : None.$ K% D! `$ t2 h, Y
  5. * Return         : None.
    & I" E  v3 u% q- ?6 x9 A
  6. *******************************************************************************/% L! |" B& h+ @$ [
  7. void USB_Interrupts_Config(void). E, ^/ s5 V8 ^7 M& U
  8. {
      m! S( ~/ s* G* h  K* S
  9.         
    ( \: ~9 o; @' u0 p% J3 n# X
  10.         NVIC_InitTypeDef NVIC_InitStructure;5 ^$ o: K4 R" J( x" R7 B
  11.         EXTI_InitTypeDef EXTI_InitStructure;
    , e# S1 G% y" Q- t7 i* R
  12. 9 c. t0 K, D( ?
  13. 5 c; N+ h6 n, K0 \3 {1 x) a( P
  14.         /* Configure the EXTI line 18 connected internally to the USB IP */6 W% A/ z" L) V* w+ ^0 w! H
  15.         EXTI_ClearITPendingBit(EXTI_Line18);
    ( k" Q0 r9 c9 @, l0 R9 t& F
  16.                                                                                           //  开启线18上的中断9 b) R" A9 [6 Z, f( ~& Y6 }) q! c7 @
  17.         EXTI_InitStructure.EXTI_Line = EXTI_Line18; // USB resume from suspend mode
    3 w$ q. w% A6 M5 g
  18.         EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;        //line 18上事件上升降沿触发" }+ x  ?" k# C( ^
  19.         EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    , K2 e0 X  a9 v& j! F  a$ ?/ Y
  20.         EXTI_Init(&EXTI_InitStructure);         
    2 r8 X$ X* d, V- n  S' Q
  21. + X( I) A3 s$ c! z% |
  22.         /* Enable the USB interrupt */
    - V# N7 [$ k/ Y" ]- O) E; u' C
  23.         NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;        //组2,优先级次之 1 n( O" Z) m6 s# ]5 _2 M+ e* Q
  24.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;$ B. `- L1 s+ Y# E( d( d2 `
  25.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;9 r* B2 z: R! n6 C0 y, m
  26.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    ' A* N; s; z4 d8 E
  27.         NVIC_Init(&NVIC_InitStructure);
    1 |# q+ n2 l8 r2 W( {
  28.         ! q; c! K3 E5 w, r# X, Z7 x
  29.         /* Enable the USB Wake-up interrupt */9 A8 `; \# y+ N- h  W
  30.         NVIC_InitStructure.NVIC_IRQChannel = USBWakeUp_IRQn;   //组2,优先级最高        
    6 M5 i" I. Q5 a5 c6 h6 Z" L
  31.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    ) p' I' l" D0 N$ i2 Z2 [
  32.         NVIC_Init(&NVIC_InitStructure);  
    0 V" |  E+ n% ?+ O! d3 v$ p1 C
  33. - }5 M, e: e/ G: b7 E9 s
  34. ( ^0 s4 D+ L0 ~/ Y3 N, V$ \
  35. }
复制代码

3 X0 u# L4 |: c8 U/ Q再注释掉USB_Cable_Config的内容(DP上有1.5K上拉电阻)并修改中断函数
9 S. f0 v% D3 i% s' R9 I0 a& W1 a  ]/ C! b( F8 M
  1. //USB唤醒中断服务函数$ B$ F4 o& x  t/ b1 l, ]  i
  2. void USBWakeUp_IRQHandler(void) ' W2 f. `: `7 c! i9 @. |3 u0 ]" Z
  3. {
    * V' W  q* L0 s1 \$ _$ @
  4.         EXTI_ClearITPendingBit(EXTI_Line18);//清除USB唤醒中断挂起位( x, @" W  r1 h* T- R" U0 a
  5. }
复制代码

+ d3 e* U+ T& y1 b修改pwr的Suspend内容:
+ [/ I1 V( }" j8 \% {
2 ]% V( a  l8 \
  1. void Suspend(void)
    2 L1 E3 {# D: t9 y$ w7 h
  2. {
    $ R, e5 C# r+ C% ~6 X
  3.         uint32_t i =0;5 n, P, R/ M; P; s7 ~
  4.         uint16_t wCNTR;
    0 X2 M' U) b0 W
  5.         __IO uint32_t savePWR_CR=0;
    , M$ x& U& k$ b8 w9 p! _* r
  6.         /* suspend preparation */% x7 D8 D' w9 M
  7.         /* ... */8 c9 v8 I/ d- U' L1 h
  8.         
    ( m2 a) o( ^5 l; u% E; z, x
  9.         /*Store CNTR value */8 O9 P9 y- c# b& M) w2 a$ p
  10.         wCNTR = _GetCNTR();  
    * H" l6 X9 A7 `3 u6 Z

  11. : c% h5 M+ J7 k& K, ~$ Z. O
  12.     /* This a sequence to apply a force RESET to handle a robustness case */
    ; c) C  O4 b; W
  13. 0 j7 h/ F+ ^8 B& z5 J
  14.         /*Store endpoints registers status */
    2 A  t$ I* [  w3 \# _
  15.     for (i=0;i<8;i++) EP = _GetENDPOINT(i);" ^" ^% O' N7 @! [# F1 Y
  16.         $ a$ r  y( s- u4 s9 F, Q
  17.         /* unmask RESET flag *// J5 A/ X) v& L4 Q2 f
  18.         wCNTR|=CNTR_RESETM;
    & |9 O. k; g  W6 ]7 [
  19.         _SetCNTR(wCNTR);3 n& m& b) d) }) P9 M# |
  20.         
    , j8 f" l) F% b; h
  21.         /*apply FRES */! Y, R4 T7 f$ {+ n3 N
  22.         wCNTR|=CNTR_FRES;8 o( k/ \  {3 S% a
  23.         _SetCNTR(wCNTR);
    5 K  o. }% g5 `. @2 }
  24.         . M- P- d  J4 [! m! b
  25.         /*clear FRES*/% o5 _6 k! m' j$ W9 N  W
  26.         wCNTR&=~CNTR_FRES;$ a& ~& J' j2 {, L  v4 M" Q
  27.         _SetCNTR(wCNTR);
    3 [0 @4 N( x! A1 N, ?
  28.         * C1 z" b2 N% A1 r( j; G
  29.         /*poll for RESET flag in ISTR*/( l. F7 M$ ?7 |( m% _+ [* v' i
  30.         while((_GetISTR()&ISTR_RESET) == 0);: y8 V2 j" [! Y2 F) z
  31.         
    8 ^$ H9 ~" V: T1 j% d
  32.         /* clear RESET flag in ISTR */& n. r9 a2 u  f; S; B6 Y, U8 l* h
  33.         _SetISTR((uint16_t)CLR_RESET);
    : a4 \( o/ [4 e- ?
  34.         
    3 \  n3 _& y* x% N9 W6 Q
  35.         /*restore Enpoints*/
    8 |+ v$ d! }/ s) W4 C# D
  36.         for (i=0;i<8;i++)
    % D. I4 W+ c. [6 |0 ~" f
  37.         _SetENDPOINT(i, EP);  G+ D# L5 {4 ?
  38.         
    5 [6 [$ S% p- k: d
  39.         /* Now it is safe to enter macrocell in suspend mode */
    - a; h0 U' h! T' H. D2 ]
  40.         wCNTR |= CNTR_FSUSP;
    ' [$ y, f. ~9 ~% T
  41.         _SetCNTR(wCNTR);2 y. i6 s* z4 B' G9 s4 A$ L/ M1 K
  42.         ) _0 ~5 m$ c) |6 t' u" b
  43.         /* force low-power mode in the macrocell */4 ]$ J' t0 b: `9 F, U/ z- j5 Y
  44.         wCNTR = _GetCNTR();
    & f) b) `6 \' Z8 X) r+ q$ w
  45.         wCNTR |= CNTR_LPMODE;
    ) Y" _3 {8 \9 S1 Q
  46.         _SetCNTR(wCNTR);
    2 M) r. w$ c4 J# @6 c: U6 D
  47.         
    ) T$ W/ s  j' U5 ^8 }# b
  48.         Enter_LowPowerMode();
    " R2 d4 e& q% L2 f  K
  49. }
    5 D) G6 X; c" H# j
复制代码

% E. q3 q/ W3 Y7 m" b# u6 b" b把USB初始化部分不要的注释掉:
% }, I0 g. `. B, Q
$ Q0 l5 }+ o  I
  1. void USB_MSC_Configuration(void)* x& K; {# e. C4 u# Y+ h1 y
  2. {7 H( }1 R$ C; a6 h
  3. //  Set_System();. j: ]/ X9 ~3 }# |; `
  4.   Set_USBClock();
    # L* @* B5 s9 n. g' t2 {+ r
  5. //  Led_Config();  o: u* S, r: B$ O5 T7 F
  6.   USB_Interrupts_Config();
    , u4 i# M  f4 ?6 S
  7.   USB_Init();
    0 Z& ~/ d: U# j. |
  8. //  while (bDeviceState != CONFIGURED);
    % X. H* l, {. ~
  9. 1 a& `6 ?; v( h+ X" ]. ~
  10. //  USB_Configured_LED();
    2 |  t- ?8 d8 m6 Y* W: z
  11. : y: a4 ^+ A8 S2 z
  12. //  while (1), z+ n: S) Y+ E5 a+ J) t% M
  13. //  {}
    0 e: r* c/ v$ ~* n
  14. }
复制代码
( N6 z4 X6 R# e
最后在USB初始化之前给U盘定义的数组赋值,不然就只有个盘符$ ]9 J; |" w1 U0 \

/ A- e# q5 h. _. v, ?8 i- S
  1.     Mass_Memory_Size[0]=1024*1024*4;        //w25q64->8M 给4M做U盘
    6 O3 l: B$ J& z8 y+ E+ S+ Z
  2.     Mass_Block_Size[0] =512;                        //设置SPI FLASH的操作扇区大小为512( H6 S1 [, t$ u8 g
  3.     Mass_Block_Count[0]=Mass_Memory_Size[0]/Mass_Block_Size[0];( L3 B% q3 l- d6 [( }( i6 s: ]( B! Q

  4.   T; e& H- r6 X+ c' U$ q
  5.         USB_MSC_Configuration();
复制代码

! Z5 t! s. ~2 E4 x. C$ @ SEGUXF{Z3MWXPLSF5KTU9.png
& I6 a; R: ?. A+ }' x$ }& P
4 I6 I9 ~4 Z$ {: i编译下载后就可以正常运行了。
/ f* o; ?5 o; k, n  s. z: Z! r- f8 P8 C0 m- x
_IXXNOSIUI%KX_{0NPN2T%2.png
1 I( Q0 \- M$ o# E9 h% j$ c% T$ ~$ L, G$ i
传输文件、新建文件夹都可以了,就是传输速度感人。
8 [, ]' Y" ~' z: }; Y/ s4 {0 q4 L& _" {9 L! h6 v" A" I
以上就是USB虚拟U盘的所有内容了,内容太多而且复杂,所以如果真的想亲手试试,还是要点耐心的。虽然能学到的并不是很多,这里主要可以干茶mass_mal部分,可以学到不少东西,方便以后的熟练运用。我这里也是为以后更进一步的开发做铺垫。1 F: n9 F* w8 Y4 |. L

/ z; K; H; b# L' N# Z) c* {. z% g8 L% E
7 O# y$ U. U& R+ k8 M, I7 J
赞 收藏 评论0 发布时间:2022-3-16 11:16

举报

0个回答

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版