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

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

[复制链接]
STMCU小助手 发布时间:2022-3-16 11:16
学习stm32已经很长时间了,但是一直没有过多的学习stm32的USB部分,因为实际工作还是用的比较少。说起USB那就有的说了,因为USB的功能很强大,这里主要重点记录一下STM32的USB部分,这个官方给的有专门USB库,笔者目前使用的是Cotex-M3内核的STM32F103系列,实验的芯片为STM32F103C8,这个是目前市场上性价比非常高的芯片,也是用的非常多的芯片。
2 X3 I; Q8 X4 h' g0 ~
% p. p( O: d" Q! f4 TUSB基础知识
  Y' N7 r1 G$ l2 @6 C! KUSB按接口类型分
, j6 W" U) n2 p: b: L: u
控制器/主机(controller/host)
1 W2 y( n+ F& D4 a
6 T6 \4 G! x+ X设备(peripheral)
$ C2 R* Y  X( {, |4 v/ ^
$ W$ i2 J) W% b" d+ b' r2 ]OTG(on-the-go),通过id线确定作为主机还是作为设备" W+ e. D1 s3 s% m

0 t4 H% J2 i- @# l3 K; m2 `按照USB速度分) _3 T% X/ q, P5 n( n

  N2 G6 e2 j. `低速(low speed)8 |0 O' b$ M, {7 ?3 `0 y/ e
: T% ~, {8 @" F. M: k  f8 r8 W
全速(full speed)
/ R* d# Q& T! J  W% C* o" j& N, j3 h( X. n- m  X
高速(high speed). y& y, i# A( Y- k# R6 L

- E  T, B8 e0 X( c+ H8 \USB接口一般是4根线,VCC GND DM(D-) DP(D+)2 _5 R# G9 q: m1 z. M
( S7 |& ~! U3 f
低速设备:在DM线上接入上拉: ]" X1 k2 G& p! d- H; X* f) l

) e8 z& {/ H# H7 i0 _全速设备:在DP线上接入上拉
5 e' U8 m* u3 Z% ~
0 k/ K% [! ]1 f; \高速设备:在DP线上接入上拉,在主机对设备进行复位后进一步的确认
$ x$ x! v9 [: c0 R
# F  J+ w' t) x! \3 h. Y& ?3 j3 z3 g. Q# ^
关于描述符1 ?! t! b& i( f9 y& ?
设备描述符(device description)
7 p4 @' ~' [0 A( y' M& Q5 c1 k& l8 |8 B3 _% J5 R
配置描述符(config description)
+ T" G4 d% u  g
; X2 D  D# Q2 m% C接口描述符 (interface description)
# M! \( S. C; A0 n% o) W' @# Q
" j' C8 V; f0 q9 u0 e' a4 l端点描述符 (endpoint description)
8 L( Q; \( F+ u/ U! y8 B5 p+ P: q
; N& f5 b' @9 d7 N( a5 Q (HQG%R@V4W]{7LOY8DZE{L9.png
: Z/ k- N1 Z* _5 _3 {- B* L5 R
- u' x  A6 R0 Y  ]. D5 H设备的“身份”信息存储在描述符中。每个USB设备中都有如下描述符。需要注意的是一个USB设备只能有一个设备描述符,一个设备描述符可以有多个配置描述符,一个配置描述符可以有多个接口描述符,一个接口描述符可以包含多个端点。
4 |4 S" q& L  p4 V* |4 G, {1 Z- J
关于传输

* O  p) c  s! K, c$ e1.在USB的通讯中,有传输(transfer),事务(Transaction),包(packet)三级。包是最基础的传输单元,与TCP/IP协议中的MAC层协议作用相同。6 w5 o1 z( k: V, X8 p
2.在一次传输中,由多次事务组成,每次的事务又由多个包组成, O" A6 q9 I1 v
3.与众多协议相同,较高级别的协议的报文是基于/内嵌在低级协议的报文当中的,在USB中也不例外,例如,包中预留了DATA位,其目的就是填写报文
4 E9 @( @3 q. y, \2 X- L9 S3 USTM32F103芯片自带了有USB模块,可以用来做从设备,不能用作主机HOST。这里使用USB的目的是讲USB作为一个大容量,这个可以基于官方USB Mass_Storage例程来移植。这里就不过多的介绍里面复杂的通信原理了,要彻彻底底搞明白并灵活运用取来还是有很大难度的,因为内容太多了。这里仅仅介绍下移植过程和描述符相关的重点内容。
& k* `) O: y& c5 ]' V& n: P0 F" u7 _) O4 U
准备工作6 r, X4 _2 q/ T
第一步

. ~* Y0 w' ]% z1 z' m% H8 [; J# E确保自己的KEIL开发环境已经完善。. k" p2 @! M" J6 |8 F

3 L+ M" _3 r( q, P) v第二步3 E* A1 ]: e' b; r$ V9 w) R
下载好了两份ST官方的库文件(我是基于标准库开发的),一个是标准外设库文件,一个是USB库文件。如下图:
8 }. K+ j* ?, A& `3 R  g! c* w( w" n
AZ7H5GQWT4J%{N{6){9E]{3.png & W) i/ R* c8 F
6 [$ E8 h9 I  D* {) }, i
第三步8 b: \+ F; G5 V3 y6 C5 I0 J, R3 A
建立一个带有存储介质驱动的STM32基础工程,存储介质常见有SD卡、外部FLASH芯片、内部的FLASH空间。的我是基于一个外部flash的工程去实现的,芯片具体型号是W25Q64,64Mbit的空间,换成字节就是8MByte。驱动部分如下:7 P! h/ g( S3 ~9 c
- E1 o# i% R" l  F  `
C文件) c% J3 k7 H* _: _- u4 l
  1. #include "fy_w25qxx.h" " c5 g) `& m! K

  2. ) k  z7 L0 a4 ]- b# p8 n
  3. u16 W25QXX_TYPE=0;//W25Q64
    / b0 u# T% P8 a. ~* q# k( k  i

  4. $ d, a5 }, f! G! P9 o0 z! u
  5. //4Kbytes为一个Sector,16个扇区为1个Block,容量为16M字节,共有128个Block,4096个Sector
    & r+ i$ Q! p( c  ]2 l/ n# j
  6. //初始化SPI FLASH的IO口
    + u/ e; l0 k4 j- ]
  7. void W25QXX_Configuration(void)
    : l' n! w4 N) p; X$ B
  8. {        
    6 S+ I' l9 H5 v  J0 K
  9.         SPI2_SetSpeed(SPI_BaudRatePrescaler_2);//设置为18M时钟,高速模式
    6 x/ C1 T  F3 s) X3 Y, F
  10.         W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID.  
    * q# F& u7 D) |% w6 w$ j
  11. }          7 ?* T' G) q# {3 Y5 W* p

  12. 9 i5 _) K7 {3 h: ~
  13. //读取W25QXX的状态寄存器
    ( d' [9 j1 `4 ]
  14. //BIT7  6   5   4   3   2   1   0# ?; P  s2 Z) a3 _
  15. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY
    : n+ l4 `/ O8 k# F& @- p8 Y
  16. //SPR:默认0,状态寄存器保护位,配合WP使用
    / Z. Q9 ^& Q! U) d. Z) `" `* ~% w
  17. //TB,BP2,BP1,BP0:FLASH区域写保护设置! z/ i' f% ~4 {5 k- G/ L0 r
  18. //WEL:写使能锁定& e; m: {9 Q. Z1 W2 }0 V+ G7 M
  19. //BUSY:忙标记位(1,忙;0,空闲); I  q7 z9 p# Q
  20. //默认:0x00
    - I. K2 W5 G$ L$ ~. g# N
  21. u8 W25QXX_ReadSR(void)   
    " C' w" @9 V+ r% R6 |1 g) x, T* v
  22. {  . I* C; ^4 l+ E
  23.         u8 byte=0;   8 C$ v$ W: B6 n! `4 Q1 k
  24.         W25QXX_CS_L();                            //使能器件   
    " G* O! }. y+ b' J# Z% H# v8 q9 q% U
  25.         SPI2_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令    5 ]4 `  w% V3 }9 q! k8 Q8 o2 ^) h
  26.         byte=SPI2_ReadWriteByte(0Xff);          //读取一个字节  ) b2 H4 C( \, G  p% U8 Z: `
  27.         W25QXX_CS_H();                            //取消片选     " [7 N# C. f4 b9 |$ Z) _# i/ M
  28.         return byte;   
    ) |9 N: Y# t4 R8 C' |4 B6 B
  29. }
    8 `( o6 N" P% E8 r) D7 p& a; [
  30. //写W25QXX状态寄存器, [8 H; y" ^& ^4 R  K: r
  31. //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
    ' V& K; m7 y( C2 T* ^1 O
  32. void W25QXX_Write_SR(u8 sr)   & B) [7 G" y/ U- K
  33. {   % S6 `5 u) n+ H( \
  34.         W25QXX_CS_L();                            //使能器件   
    $ A3 u. L5 Y( n, B6 e
  35.         SPI2_ReadWriteByte(W25X_WriteStatusReg);//发送写取状态寄存器命令    ) l+ R, n& q& D# t' ~+ x
  36.         SPI2_ReadWriteByte(sr);                       //写入一个字节  7 Q9 z9 |8 Y8 }
  37.         W25QXX_CS_H();                            //取消片选                   8 i$ T" B2 E! o; e% L
  38. }   
    0 K  T  E. y& J6 G* X
  39. //W25QXX写使能        
    : P( p; B& E8 v9 W" y+ R0 i) o
  40. //将WEL置位   
    , w* Q* T* u* N
  41. void W25QXX_Write_Enable(void)   * g" c% P# u, w( N1 ?* }  ^
  42. {
    4 e# ]6 K4 M6 Q+ g) A
  43.         W25QXX_CS_L();                                  //使能器件     b8 |/ I2 B2 ]/ d$ S- r# _: u" t! F
  44.     SPI2_ReadWriteByte(W25X_WriteEnable);         //发送写使能  3 W' b! Q  j. K3 U$ X, o$ K
  45.         W25QXX_CS_H();                                   //取消片选                   2 n6 r# Q. X: C* K& `* i+ b
  46. }
    ! k5 e+ }, ~9 R# Q" r
  47. //W25QXX写禁止        # P! R4 |7 |. g- ]& J6 n
  48. //将WEL清零  1 |/ y! y$ J! f* |. O/ l% r
  49. void W25QXX_Write_Disable(void)   7 y" v7 p2 m: d9 X. q8 \
  50. {  
    . @' |3 |2 m$ P0 ~7 }1 t
  51.         W25QXX_CS_L();                            //使能器件   - ~( H" D, P6 g- J/ \6 W
  52.     SPI2_ReadWriteByte(W25X_WriteDisable);  //发送写禁止指令    ' x9 t* V* z. h$ L: q
  53.         W25QXX_CS_H();                            //取消片选                   9 U1 L: C  i4 b' C7 y( ]$ I
  54. }                 
    8 Q8 d1 S3 q) Q- x, ^8 W5 k# C& ~' A

  55. 1 B9 ], F' z' z: T- N
  56. //读取芯片ID
    ; l" U7 g2 {% g" n( t
  57. u16 W25QXX_ReadID(void)7 T2 n% w- I$ |( s! z- q9 ]7 g0 A1 ^+ x
  58. {
    8 l: t/ J* Z, l: [, O4 K7 r- l$ q# }
  59.         u16 Temp = 0;         
      Y$ o, {9 U- J% ?; R7 \# |/ M+ I$ N
  60.         W25QXX_CS_L();                                    4 u' @* W$ D+ g" h$ E
  61.         SPI2_ReadWriteByte(0x90);//发送读取ID命令            
    - l& d0 b& U7 I# G) H2 t( i
  62.         SPI2_ReadWriteByte(0x00);             * Z, I7 e/ P4 D# O: f2 v
  63.         SPI2_ReadWriteByte(0x00);            
    2 v  u( m0 {. Z8 q
  64.         SPI2_ReadWriteByte(0x00);                                    
    - a+ c# _4 |4 y& U7 a$ r$ W
  65.         Temp|=SPI2_ReadWriteByte(0xFF)<<8;  
    ( v% w( |# C& R8 X  M7 `
  66.         Temp|=SPI2_ReadWriteByte(0xFF);         # @3 C! h! [1 y# N+ m1 X; k; `
  67.         W25QXX_CS_H();                                    6 x6 V9 Q5 k3 m7 ]
  68.         return Temp;
    " q9 K! M4 F! u  \/ T
  69. }                       
    6 j0 @, t: x* r- D3 C/ @( ]
  70. 0 B# a" a0 V0 R
  71. //在指定地址开始读取指定长度的数据
    ; T- ^( K% {1 p' }6 q2 b
  72. //pBuffer:数据存储区,ReadAddr:开始读取的地址(24bit),NumByteToRead:要读取的字节数(最大65535)- g4 x+ ^; J' h: q, i! I- t
  73. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   4 e7 n( B6 N( Q, Q, b
  74. {
    ) l2 _3 \( ?/ a" ~% U
  75.          u16 i;                                                                                       % x6 D7 `4 [% A3 {. N
  76.         W25QXX_CS_L();                                    //使能器件     U# M2 S3 s7 B2 A' J/ W
  77.         SPI2_ReadWriteByte(W25X_ReadData);                 //发送读取命令   
    , j9 y% z$ `" |$ W
  78.         SPI2_ReadWriteByte((u8)((ReadAddr)>>16));          //发送24bit地址   
    0 K) E% X1 B6 i! F  x8 O8 C* E
  79.         SPI2_ReadWriteByte((u8)((ReadAddr)>>8));   
    0 N7 ~$ ?$ ?' i2 G
  80.         SPI2_ReadWriteByte((u8)ReadAddr);   0 z- @9 h+ T2 C! e: T9 M" w
  81.         for(i=0;i<NumByteToRead;i++)
    ) g) ^0 n( O. H8 U) t
  82.         { 7 l# L& J5 O0 k2 w
  83.         pBuffer=SPI2_ReadWriteByte(0XFF);           //循环读数  
    2 u" P7 F- q% K" Z1 h  O( f
  84.   }
    9 W7 [1 C) m6 A9 m
  85.         W25QXX_CS_H();                                                    0 J7 G' V7 m: F- ]/ R
  86. }" V3 l$ i/ e+ g1 X/ C" S2 U
  87. : J/ H7 y+ g: C! H! M9 B# `
  88. //SPI在一页(0~65535)内写入少于256个字节的数据- O3 [# F, i0 a* N& f; K) x
  89. //在指定地址开始写入最大256字节的数据( d1 P! Q0 K9 _( `3 |% B! L( f
  90. //pBuffer:数据存储区,WriteAddr:开始写入的地址(24bit),NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!           P* ^" x" \1 W# O3 I4 o5 U
  91. void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
    3 H1 R3 ?; t" o( J
  92. {: U4 g$ F  A# N; H. \1 @  T% J- T
  93.          u16 i;  
    1 z; Y) T" Q& R2 v4 o/ z
  94.         W25QXX_Write_Enable();                          //SET WEL
    1 e9 K3 @- R  p9 r7 j2 a
  95.         W25QXX_CS_L();                                    //使能器件   $ I! g2 u* k. ?
  96.         SPI2_ReadWriteByte(W25X_PageProgram);              //发送写页命令   
    ) _9 |5 ?; m% @% O/ G
  97.         SPI2_ReadWriteByte((u8)((WriteAddr)>>16));         //发送24bit地址    ( m) ^2 K9 K' J6 {: m9 _/ F% D! p( O
  98.         SPI2_ReadWriteByte((u8)((WriteAddr)>>8));   2 A8 o2 e) s7 ^7 [
  99.         SPI2_ReadWriteByte((u8)WriteAddr);   3 F/ n8 `. j  Y
  100.         for(i=0;i<NumByteToWrite;i++)SPI2_ReadWriteByte(pBuffer);//循环写数  
    5 k% a6 x/ F3 t" @# G
  101.         W25QXX_CS_H();                                    //取消片选 - n+ [& T! a7 }
  102.         W25QXX_Wait_Busy();                                                           //等待写入结束
    ! ^1 d) k  |; L: }* m$ C
  103. }
    ; c) f( L" W1 e5 o0 L
  104. //无检验写SPI FLASH
    8 Z$ f! x* N- n% _% H
  105. //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!: Q9 z; L* P- Z; l3 q
  106. //具有自动换页功能 8 Z, h9 K, @8 `& n# D7 U
  107. //在指定地址开始写入指定长度的数据,但是要确保地址不越界!
    ! b+ b6 E& s' [' K" Z5 ?
  108. //pBuffer:数据存储区" H$ D8 R" u: U1 {1 M8 _
  109. //WriteAddr:开始写入的地址(24bit)
    6 N/ r. i$ A3 Z+ {
  110. //NumByteToWrite:要写入的字节数(最大65535)
    ' L5 l/ a& ?$ o# e
  111. //CHECK OK5 x+ ]% O# j* i3 I( [# i
  112. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   # @, f. N  q6 g4 q1 L7 P
  113. {                                          
      A, W; L$ w1 X  s7 t
  114.         u16 pageremain;           
    / A5 @; j4 A% L. h
  115.         pageremain=256-WriteAddr%256; //单页剩余的字节数                             5 [1 @& Y( O4 Y" O9 z9 W/ B
  116.         if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节/ E: K8 I6 |3 f' A: g/ D2 w$ X6 S
  117.         while(1)
    ) l! a- O4 e( F/ u8 u  l
  118.         {           ) g1 d8 b  a0 J  ~3 H$ V
  119.                 W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
    ! h# i  c0 w( D. b- t9 m( S0 u& J* k# Q
  120.                 if(NumByteToWrite==pageremain)break;//写入结束了7 a2 p, l  u9 w% @
  121.                  else //NumByteToWrite>pageremain& @; k+ x3 N7 J! R! V+ t, b
  122.                 {  a4 R3 U5 R8 X/ H, J6 G. N
  123.                         pBuffer+=pageremain;
    3 \! B" A8 c2 j  {
  124.                         WriteAddr+=pageremain;        
    ; H% ~+ e8 G- b& f& h
  125. 2 ~& S/ J8 Y/ |9 O" {. Q, M
  126.                         NumByteToWrite-=pageremain;                          //减去已经写入了的字节数4 }: w8 ~1 P) ^
  127.                         if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
    ! S: {' I1 `3 R" ?
  128.                         else pageremain=NumByteToWrite;           //不够256个字节了5 j6 ?$ R$ h- v
  129.                 }
    % p0 b3 \! X- C: K
  130.         };            
    : S3 K- n% b- r1 o2 x% O: _
  131. } & G7 L% K" B* g8 }% y
  132. //写SPI FLASH  
    % B& M2 G. c7 {. i7 O0 w, l" x
  133. //在指定地址开始写入指定长度的数据3 `, @* O. y, X" P' ?) {
  134. //该函数带擦除操作!* a' u, ~% p1 Y3 [
  135. //pBuffer:数据存储区
    4 S- p3 {( n- I" M( K7 ?
  136. //WriteAddr:开始写入的地址(24bit)                                                
    1 H$ D* a  K. r. \2 H
  137. //NumByteToWrite:要写入的字节数(最大65535)   + L8 L; E5 B, V# R% I( u  O
  138. u8 W25QXX_BUFFER[4096];                 
    & L) r3 \. p2 y, x8 ~
  139. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
    ' q4 F$ V- |# [) k$ e
  140. { : h! |3 r8 a1 n. Q
  141.         u32 secpos;
    , n7 K$ B  \! |/ F
  142.         u16 secoff;
    + W6 ]+ D; [; j+ f0 u
  143.         u16 secremain;           
    5 e! [$ Y3 F3 J
  144.          u16 i;   
    # B" @' c2 a6 S+ p: D: @
  145.         u8 *W25QXX_BUF;         
    ( c( c2 c; s) J4 _9 x: D
  146.         + `. ^& r- {- U. f( S7 D4 u( {& V0 C
  147.            W25QXX_BUF=W25QXX_BUFFER;
    3 n2 N8 H; ?( R
  148. //        
    6 B2 o# r+ G1 i6 S# M# o
  149. //    W25QXX_BUF = (u8 *)_mem.Alloc(4096);//申请一个扇区大小的内存
    ; p0 G7 h8 N0 {* }: o) ~
  150. //    if(W25QXX_BUF==NULL)        return;
    0 ~( f; T  e0 E8 v* p- D0 A! c. n3 W
  151.         
    " {8 D5 ^5 {: A; D& ~; O
  152.          secpos=WriteAddr/4096;//扇区地址  
    4 A6 D; u8 s# N1 k4 \! p
  153.         secoff=WriteAddr%4096;//在扇区内的偏移
    2 O5 E2 O% j+ Y0 @
  154.         secremain=4096-secoff;//扇区剩余空间大小  / v: `7 T# D  Q+ l. U

  155. 0 K5 v( b) m2 v" l. u
  156.          //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
    4 p& U( B5 D' `
  157.          if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节
    1 d8 d8 C5 S0 W9 I
  158.         while(1) # c0 @" y2 N# ^; `- K* l! D' W' s
  159.         {        
    9 Q; G# `3 z* L  F% W( |& h5 W! E
  160.                 W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容
    " t% U1 m. w" t2 a* j
  161.                 for(i=0;i<secremain;i++)//校验数据8 K% z# F. i" m  [: Q  j# [
  162.                 {7 Y% Q; n) [& v; ^: F% k/ A$ F
  163.                         if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除            
    2 w& \& I. A5 A/ O& J# j( R
  164.                 }! {/ ?4 V! U# `
  165.                 if(i<secremain)//需要擦除
    ) G+ N/ H, E9 r" i
  166.                 {
    8 G" e( {( M" J8 r& X! N1 p1 i
  167.                         W25QXX_Erase_Sector(secpos);                //擦除这个扇区& t  a' T9 T+ s. K
  168.                         for(i=0;i<secremain;i++)                           //复制2 |- d8 m0 |  J: q) O
  169.                         {
    - D3 n- a0 n3 e3 ~( V/ {: a
  170.                                 W25QXX_BUF[i+secoff]=pBuffer;         
    3 Q1 p' V: d' N
  171.                         }
    ; O1 [5 {, @; _  e3 S% ^$ L6 |9 T* b
  172.                         W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  1 J: v. N6 H1 [' X! `; g; U

  173. 4 g, [3 |: w, B- t; A3 D
  174.                 }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.                                    1 j5 S7 N/ K( m. Z: t
  175.                 if(NumByteToWrite==secremain)break;//写入结束了
    # [2 n9 t  m* J  Y) p4 ~1 y% T
  176.                 else//写入未结束
    & y+ _' r3 t2 N3 Q
  177.                 {! e/ \: \. V3 D! U) }- p) n" x
  178.                         secpos++;//扇区地址增1$ _% D8 Z# i# [+ r* W1 f
  179.                         secoff=0;//偏移位置为0         
    $ t8 U. m3 g! s4 n
  180. * w" m& _* ]& s6 p8 l3 u) h
  181.                            pBuffer+=secremain;                                  //指针偏移
    : x# }5 W/ M% D' V, e; [) o
  182.                         WriteAddr+=secremain;                                //写地址偏移           
    2 H6 d% q8 [4 q* ^6 ~5 l. A- a, s, q
  183.                            NumByteToWrite-=secremain;                        //字节数递减
    8 f) Y5 L- a8 s( S
  184.                         if(NumByteToWrite>4096)secremain=4096;//下一个扇区还是写不完+ Y- h  s9 Y" r
  185.                         else secremain=NumByteToWrite;                //下一个扇区可以写完了
    * F, }$ K; J; M) _& |/ b: D
  186.                 }         " j' W' E0 L1 |/ o; X: Q
  187.         }
    ( N3 }% q8 Z5 t4 B& H
  188. //        _mem.Free(W25QXX_BUF);9 o8 Z& u% g9 Y* j2 B. x( G
  189. }% A: q4 U. z# p- h# S
  190. //擦除整个芯片                  
    2 g1 s2 p4 ]- E
  191. //等待时间超长...& T+ {0 ]3 p* T8 _, `0 o3 y3 Y! C
  192. void W25QXX_Erase_Chip(void)   
    % y" o. O2 u: k6 b$ _3 L
  193. {                                   1 A# `& l5 K  @' `% d. o& m
  194.     W25QXX_Write_Enable();                                  //SET WEL 7 b  {( s1 c; t) ]! M% j
  195.     W25QXX_Wait_Busy();   % u, L- L& g* A* D' ]; ]( I6 A, h
  196.           W25QXX_CS_L();                                    //使能器件   
    % O7 k8 M7 I4 J! t
  197.     SPI2_ReadWriteByte(W25X_ChipErase);                //发送片擦除命令    T3 X8 P- h9 S/ Z
  198.         W25QXX_CS_H();                                    //取消片选                   8 A/ U9 ?9 d; f% g" \: C6 I
  199.         W25QXX_Wait_Busy();                                                      //等待芯片擦除结束
    1 a" H. F5 ]# s: i" A
  200. }   : y& x5 Q3 B& y
  201. //擦除一个扇区
    " [. t  ^# a6 v) B
  202. //Dst_Addr:扇区地址 根据实际容量设置! k# C. E* C' r) B. l
  203. //擦除一个山区的最少时间:150ms
    * z9 z2 C/ h- J$ w; C
  204. void W25QXX_Erase_Sector(u32 Dst_Addr)   6 x$ ]' g( z& `$ @0 r& \3 r
  205. {  
    4 |7 F  k! ^5 Y3 b% Z! W- P! n
  206.         //监视falsh擦除情况,测试用   ; U4 t+ c/ P4 p' V6 m+ }$ S7 \! c
  207. //         printf("fe:%x\r\n",Dst_Addr);          ' [: y7 }3 B' Z1 O, @5 D
  208.          Dst_Addr*=4096;+ E* Q$ Z. s" e8 y' s2 U
  209.     W25QXX_Write_Enable();                          //SET WEL          4 J5 `$ `9 B# g% ?# f
  210.     W25QXX_Wait_Busy();   
    % ?7 t( W  n! l  j2 f: e
  211.           W25QXX_CS_L();                                    //使能器件     @3 k- p; ~# \# O) Z- }0 J! y2 y8 E
  212.     SPI2_ReadWriteByte(W25X_SectorErase);              //发送扇区擦除指令 3 b8 X# r0 ]' ^& ~+ d# x" ~+ D, C
  213.     SPI2_ReadWriteByte((u8)((Dst_Addr)>>16));          //发送24bit地址    - D5 e/ z& J1 q% F; F
  214.     SPI2_ReadWriteByte((u8)((Dst_Addr)>>8));   
    2 f' S( e; O5 K
  215.     SPI2_ReadWriteByte((u8)Dst_Addr);  ! i+ B9 f( t1 i- T0 A- ]
  216.         W25QXX_CS_H();                                    //取消片选                   + R8 D! N' {: y2 T3 v6 T
  217.     W25QXX_Wait_Busy();                                                      //等待擦除完成
    ( i6 D+ C* [$ l6 N' n8 \3 D
  218. }  4 k) O+ l$ E! Q7 `. d
  219. //等待空闲7 g- x1 K" Y0 p1 o2 C# d
  220. void W25QXX_Wait_Busy(void)   
    ; a( \6 F/ y) S- t2 [0 r6 V1 _
  221. {   / ^  B$ R- ]$ b7 A; K! g5 R
  222.         while((W25QXX_ReadSR()&0x01)==0x01);                  // 等待BUSY位清空, s/ B  J) X* s' A
  223. }  / f' C& t. R3 f' r8 s* G
  224. //进入掉电模式0 s7 C: ?# W, |2 ~) z  g& {" m" V
  225. void W25QXX_PowerDown(void)   
    4 d5 i2 A& p4 w4 n1 t8 Z( G; W8 l
  226. { * ~. X# i5 x  y
  227.           W25QXX_CS_L();                                            //使能器件   ; k2 Z7 n8 q' D
  228.     SPI2_ReadWriteByte(W25X_PowerDown);        //发送掉电命令  , g/ p, W4 d! y5 X# ~
  229.         W25QXX_CS_H();                                    //取消片选                   4 P7 {; S, C" m) k! p7 H
  230.     Delay_us(3);                               //等待TPD  
    * G8 o) N0 s8 w9 O, C
  231. }   * Z7 {$ b6 P) {& v3 ~. C
  232. //唤醒
    ( b( W( M- R1 R# e% U; _
  233. void W25QXX_WAKEUP(void)   
    : x# k* c! l4 c5 e( u$ a6 V0 O0 n
  234. {  
    ; L. h" t9 w/ I3 g. ]4 X. l
  235.           W25QXX_CS_L();                                    //使能器件   0 [' G; t/ e/ k9 s  F
  236.     SPI2_ReadWriteByte(W25X_ReleasePowerDown);        //  send W25X_PowerDown command 0xAB   
    6 w. X/ \1 s* ?& |! M' d# x/ [
  237.         W25QXX_CS_H();                                    //取消片选                  
      M# Y+ H' y5 V9 Y9 A
  238.     Delay_us(3);                                    //等待TRES1+ L  K5 o  g- u" B6 r
  239. }   
复制代码

6 s4 k; G. i' Z6 H* y; u6 ]H文件
% d; r) u. W; O8 x; m& N4 R8 `1 k0 x3 Z5 B2 ?: F3 u( ^8 y
  1. #ifndef __FY_W25QXX_H
    ' E1 ?  c) L) {, C: u
  2. #define __FY_W25QXX_H                            & ~9 p- I7 A; S' D

  3. : L. l. ~9 a* G7 q! {
  4. #include "fy_includes.h"
    0 a" ]' \+ F$ V' T8 i  p& n  `, G
  5. $ O. u8 g) A  D3 X
  6. //W25QXX对应唯一识别ID# L2 h* {, ?4 w- J- ]
  7. #define W25Q80         0XEF13
    $ h9 v! n: \- _: N7 O& e0 Z1 b' M+ S9 I
  8. #define W25Q16         0XEF141 n: q! u0 C3 A
  9. #define W25Q32         0XEF15
    7 s' ~* x& W/ {; a9 D. A, F
  10. #define W25Q64         0XEF16
    : @8 `4 W$ z& l- p' p$ ~/ B
  11. #define W25Q128        0XEF17
    ) Y$ _( Y) ]5 Z2 o

  12. * t4 S9 C% X, f0 x
  13. //指令表2 F2 Z6 g: h4 N; K
  14. #define W25X_WriteEnable                0x06
    ( C5 ~# `: s! o; V
  15. #define W25X_WriteDisable                0x04
    8 D, b1 f" i5 @4 k& B+ {8 W
  16. #define W25X_ReadStatusReg                0x05 ! K2 o1 @$ u2 y6 `) r& J
  17. #define W25X_WriteStatusReg                0x01   K6 _) a+ d" ~9 |( y% W3 S# H# U% u
  18. #define W25X_ReadData                        0x03
    4 K' v: s7 z4 o$ W5 h3 T
  19. #define W25X_FastReadData                0x0B
    1 y1 }$ g% N8 x) H
  20. #define W25X_FastReadDual                0x3B 9 ?8 p) \; ]8 h) _- `, G& c& ~* o
  21. #define W25X_PageProgram                0x02
    : P3 P1 G- j* I, w( x% B0 a
  22. #define W25X_BlockErase                        0xD8 7 d9 x( f4 K8 i! M& ^, u
  23. #define W25X_SectorErase                0x20 4 y% `6 U0 q( Q' B2 [. ^
  24. #define W25X_ChipErase                        0xC7
    9 l% X" L! W; ?6 Q" t& T3 j
  25. #define W25X_PowerDown                        0xB9 7 \0 `* T( M! H; W( g! I, B' t6 ?
  26. #define W25X_ReleasePowerDown        0xAB 2 z, }6 M# M) L& _& j0 _
  27. #define W25X_DeviceID                        0xAB
    + }1 _8 S% W7 f( g
  28. #define W25X_ManufactDeviceID        0x90 " ?" X6 J9 a( ^0 P3 R
  29. #define W25X_JedecDeviceID                0x9F : O* s( z. k' S# F

  30. 4 ^& m$ v) j% @4 F
  31. void W25QXX_Configuration(void);
    ; U* v7 n* F1 L1 C4 G
  32. u16  W25QXX_ReadID(void);                              //读取FLASH ID9 ?9 F8 f+ Y5 A2 {5 ?8 t8 C+ @
  33. u8         W25QXX_ReadSR(void);                        //读取状态寄存器 % V, a: T: @) N) L. v
  34. void W25QXX_Write_SR(u8 sr);                          //写状态寄存器4 s3 F  E5 K6 S
  35. void W25QXX_Write_Enable(void);                  //写使能 % {, K  Z% F! X# a: Q
  36. void W25QXX_Write_Disable(void);                //写保护1 a" }, j2 x  \
  37. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);" E2 Q  T5 ]6 Q0 d
  38. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //读取flash. Y' m0 A0 T2 r# a
  39. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写入flash
    5 K  ^+ \! y1 ?
  40. void W25QXX_Erase_Chip(void);                      //整片擦除
    # V& X1 h/ o* F( b; E) y9 C( W
  41. void W25QXX_Erase_Sector(u32 Dst_Addr);        //扇区擦除9 O. z5 s6 g. q1 ^
  42. void W25QXX_Wait_Busy(void);                   //等待空闲, g  r' ~, t$ F, @7 X+ e
  43. void W25QXX_PowerDown(void);                //进入掉电模式
    * U% k8 a; U/ P0 y. h
  44. void W25QXX_WAKEUP(void);                                //唤醒
      f7 }5 b. R, H$ \5 T' E0 ]* o

  45. ) ~3 D4 Z# d# N( i3 [
  46. extern u16 W25QXX_TYPE;                                        //定义W25QXX芯片型号                     a; r; U/ B$ F" b/ d9 |; V

  47. . z& {. i( o8 }+ b1 B8 V
  48. #endif
复制代码

* n0 Z3 i5 ?0 H8 O. J6 q# a基础工程如下:5 B2 m$ r( U: ?" f  P
, S$ }( H; {5 @% O8 Q8 [" Z
CIG]SRYALJ}8)5NQ[MAA~BW.png
: W* q/ b' n  \* ^& c  C3 |1 u. _
+ J; b. w1 L$ J4 g第四步:USB代码移植
) e* h: f$ P" v; h拷贝USB底层库到工程根目录,新建一个目录命名成USB,拷贝Mass_Storage例程下的src和inc目录下的所以文件到USB。
7 S! s7 \: v. M- A2 o4 w& X9 w$ k- G
6Q]9Q9812B]OXMA8GNV[PCC.png
  w+ m, w; ~5 e: `' q6 N
7 H5 @: E; z7 N) h拷贝过来是有37个文件,这个时候需要删除一些不必要的文件,因为官方的例程里面有nand,这里的话先删除以下文件
1 v; t, [2 G; u; @2 v% Q6 n2 P' e# g: K+ x; Z7 m
VN1JDR_I@JO271YJKNV1(HE.png
2 n) R7 p. v# G" m
9 ]/ c& A4 R, y6 d% P9 e  Q4 I* n& U4.打开工程把分组和文件及包含路径添加进去,这里就不细说了,结果如下
( G4 V7 J6 O: ~; W1 `8 p& x2 u/ C5 B6 Y
X$DZH7HHYEC}266_`72~4EH.png
2 p) {: ~: F/ y) k( q# U6 M: m; N( b3 ^( m0 H% w
打开usb下面的main和it 把重要的代码先拷贝出来
) N7 j+ `7 P6 L  b5 _  a# {
- z9 X, s2 X6 A OA]E5J8OQ0[B{UJV@KXPUG2.png
( E3 g5 K; M+ I, \4 e* t4 d( a% I8 ^+ r- ^( ~$ f! m
把main和stm32_it里面的内容拷贝到自己的main里面进行下封装去掉不要的保留剩下的,然后就删除it和main三个文件。+ V* I! c  ~" h& Q1 |1 W

/ f7 `, @6 q" c @G5L9U9`9]${JVP}AD@KN0E.png
: d, ^1 x: B& R
2 [% k  ~2 |* h+ Z然后编译下,这个时候会出现一堆报错,需要慢慢一步步搞定,编译前注意路径包含,
6 _% T% d( U6 A# x0 @2 q4 V( i$ C- g  S9 u, E/ b' r
[~3UDXP_99@A_@~XV14BNPB.png # E# A6 d" d4 I0 B1 u% @1 A  C6 O

2 K* [: W6 N" @) V定位到第一个错误进去
- z0 [$ t, U3 z. G! {8 n2 r6 r3 v  `/ S, s& e6 e
5L28CK8A4)MY@OJR7KK~KOY.png
; Y# Z8 f% B% e& E6 ~
+ s* X  ~  `* [0 g/ a# M& {% G这个定义的是官方的开发板 我们直接修改成自己的公共文件就可以,并且注释掉开发板定义
) h& }5 \1 @4 d7 [/ U' [0 ]  v  A
TR]6[QMF5`A7X$MYZIL])9I.png
+ d1 G# w. L! G0 F: p1 J2 I" K/ w( f  }7 F$ c/ j
公共文件如下,并且添加usb部分的头文件0 d( Y# j! T9 x* Y; j9 e4 w. {( {7 ?
) }: ?- j$ {! |& i
Z7Z6YX`QZR}6~KCC_GDDLD8.png
$ V' e- m' }! X" n& T4 c5 X1 j4 k' a6 L) x% y# O
继续编译,定位第一个错误,这部分主要修改hw_config文件" n. V; ~7 b$ G. t$ `% b
) i7 ]+ ^! U' D+ I
_9RUE5O1M][P)IRX]S2S_$D.png 6 W5 f. f% i8 V5 T8 \4 L( M& Z

2 ]: P1 v( X% g  {3 `' L5 d# d删除Set_System函数
: @+ R& Q; l. D4 `/ G, p& {* z
( G$ G- ~9 n) g N~R76S188E[X(N6NID1WQ$V.png
2 a: n, I5 {, ~2 N# @, [" ]7 Q, n' F3 S9 t2 Q/ e( r
删除Led_Config函数
" L6 g; w& \' {( A5 r* g8 f2 P# g7 E6 d, t
~HA$Z23~@)XV2TEQ8CUTS.png * U2 Q' g: U. ~6 H% Q

2 o, T! [( i( A0 R* n# t& h8 L注释LED相关的内容2 t- S7 G0 v) l7 k
2 T5 X& g& a7 c3 [  `# J5 |$ J9 F2 c
_FKQ$_}1}S8W%PG(2VT2]C7.png
: o/ |0 w. |6 v/ P: d
8 x( N' d2 j; e+ W2 v0 @删除USB_Disconnect_Config. C' X) g0 q% ~/ k( a; y( b

: Q# O/ w" p! S% N' {4 o2 i 4K@_T@@XVZ@P]EEH37}QAJX.png
5 @$ V5 X+ d& ?
. q- @8 s- d) ]& m, x接下来主要修改mass_mal部分,我这里是只有一个flash,所以我修改成如下:1 S; }5 z; {5 y1 a# C% Q
  F; U  U1 R* Y4 i$ m
2_1P4[7IG68PG%9TS@41BI4.png & l6 z% s" O% T# c" u' k: V& H

, J% V" H9 O1 U, H! J/ G头文件- O& G0 Y" D" z1 u
$ N  x7 g0 Y6 k$ E$ s% C
(V0@{X{W8~L[2Z[Y7)VNU.png ' m' L' I8 q* K5 ^( j" P

* c7 t0 i" y! {6 R& y+ v# d下面修改memory文件,主要修改变量定义,C文件和H文件分别如下/ w3 D* j) g" p- ^+ W

' N0 N. _7 O7 a, ?! Q& ^ T]Y~~F2{V(H`V{$X(}FMNNJ.png
1 I- W/ W2 v) `
4 N. g! h8 h0 W; @( ]- v% S6 _" O6 z/ |接下来是usb_scsi部分的变量定义! N& \, i; ]- i  H- {, {7 k, [; h; ~9 b" C/ v

1 e7 j' V* K7 n" ?5 Q R$Z1OIMFO6JWKJE~XX[{2$B.png 6 v' X9 _7 C) a+ L+ a3 p; t

0 Z1 i8 g5 {3 D这时候再次编译就发现没有错误了,但是这个地方USB中断部分还需要配置下,官方用的宏定义方式去实现不同的代码,这里一开始就去掉了宏,所以最终修改如下:
6 O% |- n, d; O6 D7 a1 ?! P& h. X! C* C
  1. /*******************************************************************************2 J$ A4 f, ~4 z- t
  2. * Function Name  : USB_Interrupts_Config
    7 B. x/ p7 e& d- E( D' |* j
  3. * Description    : Configures the USB interrupts% {1 I6 x8 R/ l, y
  4. * Input          : None.
    - _1 ?/ K9 L' `' c! d" t' Q1 X5 X
  5. * Return         : None.
      X! C( I' T2 \2 S9 a6 c4 a
  6. *******************************************************************************/3 }. g2 c7 y2 M8 V4 B& l9 G
  7. void USB_Interrupts_Config(void), U/ E; z/ O! z
  8. {- l6 ?' a. B5 y/ ^5 s/ B
  9.         ' Z5 P' \( C; w5 |7 w
  10.         NVIC_InitTypeDef NVIC_InitStructure;1 g# B2 t: ]1 I8 w
  11.         EXTI_InitTypeDef EXTI_InitStructure;
    / K) |2 p" K. y& x3 k. T0 w3 b
  12.   N$ P8 h4 f2 o, T  C" F
  13. ! Z( M3 W  G$ Q5 Y
  14.         /* Configure the EXTI line 18 connected internally to the USB IP *// s, f) O! a( t* J: q. I8 G0 z
  15.         EXTI_ClearITPendingBit(EXTI_Line18);; s5 D( b: F9 W
  16.                                                                                           //  开启线18上的中断4 G/ q* X, m* n( \
  17.         EXTI_InitStructure.EXTI_Line = EXTI_Line18; // USB resume from suspend mode4 t& I* x, t1 u% X$ V
  18.         EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;        //line 18上事件上升降沿触发
    1 ~$ v& O; J2 P  p  w* e: V
  19.         EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    % K+ E) b& P& U6 T# Y8 {
  20.         EXTI_Init(&EXTI_InitStructure);         
    # V% A+ p1 F& X% S' P

  21. , M: E- b/ `% z) L5 m# _
  22.         /* Enable the USB interrupt */8 w/ W  ]) a1 T4 l! D/ z
  23.         NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;        //组2,优先级次之
    * e/ @3 T9 w8 @! _1 l% S5 [2 R5 u
  24.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    + B1 Z$ G% {- K; L# r% c" [+ @6 U. j
  25.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    ! P& a2 W6 H- K8 w+ P
  26.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;# l% U/ f0 z0 L$ Z% {- F
  27.         NVIC_Init(&NVIC_InitStructure);% i# w0 p7 E, P9 Z
  28.         : l9 {0 m' j& i! z9 _0 p
  29.         /* Enable the USB Wake-up interrupt */- H. ?. C  i! w. w8 \
  30.         NVIC_InitStructure.NVIC_IRQChannel = USBWakeUp_IRQn;   //组2,优先级最高        # O  f) a' C9 Q+ X/ J0 |
  31.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;; h4 B; {/ [6 x; `7 L4 h. D. J
  32.         NVIC_Init(&NVIC_InitStructure);  
    , o7 U: C2 C: M8 h, M, @* u4 l% I( \
  33.   \1 v0 _8 N' K- T  _5 z: E: b
  34. 9 w- i3 \- \" I+ j7 p$ a
  35. }
复制代码

; j- ^" L9 q2 i- `7 H& g6 k再注释掉USB_Cable_Config的内容(DP上有1.5K上拉电阻)并修改中断函数6 `: B. d; i8 u, w' ~. P; m

# h9 t8 @9 V: D& ?7 i: A, H8 p
  1. //USB唤醒中断服务函数) ]: i4 Q* Z% }3 \7 z
  2. void USBWakeUp_IRQHandler(void)
    ; z# ?* S& _" z; d' s8 n/ q, L1 J
  3. {, N9 ^+ P% G1 s" k1 a: N( z
  4.         EXTI_ClearITPendingBit(EXTI_Line18);//清除USB唤醒中断挂起位
    " g  r2 {; R3 U9 e3 V7 A" H# o
  5. }
复制代码
# j) u1 o  M! N3 O
修改pwr的Suspend内容:
. ^8 I/ S4 V5 F, b! ^& U1 l2 x( |
  1. void Suspend(void)6 ?) b* W1 H" ^
  2. {, _. C7 M- q  U" k" I% ~: M+ Q! P
  3.         uint32_t i =0;/ b+ |; O- \" ^4 m; ^( Z$ w
  4.         uint16_t wCNTR;
    : e! m' ^2 N4 O( f5 @. G# \
  5.         __IO uint32_t savePWR_CR=0;
    , U! x8 U% ^2 N/ t# k
  6.         /* suspend preparation */) [8 A' g$ X3 c+ X( N  }
  7.         /* ... */
    " [" h) d+ ^2 [) g* H; t5 z
  8.         
    ) J- l* O# P3 F1 n
  9.         /*Store CNTR value */4 S. X4 `3 r  z% V/ T
  10.         wCNTR = _GetCNTR();  0 [+ G4 z0 v4 Z& s' S5 P$ ?$ _
  11. 5 z  J  E. u6 Z0 A
  12.     /* This a sequence to apply a force RESET to handle a robustness case */8 j; l1 K% F8 _" g; X

  13. ( O+ G& q. t6 ?( r, _
  14.         /*Store endpoints registers status */5 L0 y' _4 a8 X# b$ [( b9 ]
  15.     for (i=0;i<8;i++) EP = _GetENDPOINT(i);
    : B: X1 i6 u- k8 Y& ^' N! F: |+ a5 h+ u/ S
  16.         
    : @. [- J4 ], w" N3 d
  17.         /* unmask RESET flag */' W6 u5 B6 r8 t! Q, Q
  18.         wCNTR|=CNTR_RESETM;1 s- l* a$ V* k
  19.         _SetCNTR(wCNTR);
    " r. K" }0 J& Q, G
  20.         
    4 j% l7 p( k7 o5 \0 b" Q
  21.         /*apply FRES */
    # C+ C3 ^7 V: X& W
  22.         wCNTR|=CNTR_FRES;
    ( R( r) f# N5 {- F8 k* [9 w6 A8 U
  23.         _SetCNTR(wCNTR);9 w& H& N* i1 k$ b
  24.         3 ^) i6 N8 _  ^" V( h& Z! i9 Z, d8 L
  25.         /*clear FRES*/( K: {, S0 p3 u& P. ?
  26.         wCNTR&=~CNTR_FRES;) k. z# }  s$ j. o7 k# m) M
  27.         _SetCNTR(wCNTR);+ _* {1 }& w. e. w: y
  28.         
    ; B2 K% v! m; a  e- u& {6 q+ T
  29.         /*poll for RESET flag in ISTR*/7 I) p' P6 {5 P1 o5 D
  30.         while((_GetISTR()&ISTR_RESET) == 0);# C8 @) ]1 A+ R( j  L9 z+ n/ b9 x2 u
  31.         ; j& e) w" K% S1 l0 h
  32.         /* clear RESET flag in ISTR */# g5 n; n/ B; j$ R: {
  33.         _SetISTR((uint16_t)CLR_RESET);
    . r' j" l% {5 M  t& ^
  34.         
    0 U/ ^: E" _& r8 O- l4 |$ _! O7 q
  35.         /*restore Enpoints*/
    0 f. q+ t' L& k2 _. P
  36.         for (i=0;i<8;i++)
    ' x. I8 S' a! c, [$ T3 Q" ~
  37.         _SetENDPOINT(i, EP);
    6 s" M! A  y* n9 G% f
  38.         
    8 m: V% s; N) m+ k& q
  39.         /* Now it is safe to enter macrocell in suspend mode */
    ! v: ?9 h6 c2 G6 C# C$ a7 `/ w
  40.         wCNTR |= CNTR_FSUSP;9 ]( u' {7 P2 s' ~# A, i8 l
  41.         _SetCNTR(wCNTR);
      N1 ]: {5 k$ F
  42.         ! S" F) ^( A+ l$ _% u3 G8 @
  43.         /* force low-power mode in the macrocell */
    : t- f& T! C& f
  44.         wCNTR = _GetCNTR();
    2 o3 H& V" }0 c! C
  45.         wCNTR |= CNTR_LPMODE;- N8 A! c1 s/ ~1 @# |7 z1 K$ W
  46.         _SetCNTR(wCNTR);
    + }( x; Z; m( R' q6 L
  47.         , d0 p7 }2 v6 D8 s! W, i; k
  48.         Enter_LowPowerMode();  r" W! x1 z" ?) o" V  @
  49. }
    5 p+ Y* d% Y$ O! ~, S& G! p5 v9 _
复制代码
; L! T$ q" ~3 R$ S1 U/ Y
把USB初始化部分不要的注释掉:8 L% B0 w$ I* b5 c

. R. h9 n1 u( C
  1. void USB_MSC_Configuration(void)0 N$ D+ t& P# o$ e) d: V
  2. {( d6 f& b9 j* S
  3. //  Set_System();
      H5 S, h# Y) l, C4 P% `
  4.   Set_USBClock();
    ) m$ k6 Z7 C/ g( l% J; V( f0 w
  5. //  Led_Config();$ k) q) g4 a6 ~7 u- |
  6.   USB_Interrupts_Config();4 H2 Y! w0 R; w3 B: g" e& ~& N# u
  7.   USB_Init();" |8 X& c2 u+ ^% M1 o: ?
  8. //  while (bDeviceState != CONFIGURED);
    # U+ S* J+ r) Z* P/ g( w2 c8 R% O

  9. - h: B1 z' l! _' ^; K# `- Q+ j1 @4 e
  10. //  USB_Configured_LED();# C2 [6 i/ M7 A: a/ f. g

  11. 9 H2 [7 Y+ u+ e% v1 q2 o" K, O
  12. //  while (1). p" ]; C/ X1 h4 d9 l
  13. //  {}
      x5 {, A5 T  J9 y0 C
  14. }
复制代码
9 p2 L' o& @: X* Z- e' H% W
最后在USB初始化之前给U盘定义的数组赋值,不然就只有个盘符
, u6 j; Z; A5 w1 y. Y5 ~
0 I0 G( I( C! R$ V# U, X, L" E
  1.     Mass_Memory_Size[0]=1024*1024*4;        //w25q64->8M 给4M做U盘8 ^, R% `# I% k  v! Z; p) `
  2.     Mass_Block_Size[0] =512;                        //设置SPI FLASH的操作扇区大小为512
    1 U% v# n' ]% }, Q9 i
  3.     Mass_Block_Count[0]=Mass_Memory_Size[0]/Mass_Block_Size[0];7 c" `" c7 N8 Y! \4 W
  4. , W: @! S4 c0 p7 o' p( @
  5.         USB_MSC_Configuration();
复制代码

  ^) L/ m. c1 ~! l+ k SEGUXF{Z3MWXPLSF5KTU9.png
) N% T0 ^/ o' p$ Y9 y$ |4 F+ y' H: Q( t( _/ h" A
编译下载后就可以正常运行了。1 ^" n( u1 U; @7 ]+ {0 n
" g/ h& `5 k2 b! P- T& o
_IXXNOSIUI%KX_{0NPN2T%2.png 2 L2 N1 K: m* n: m" T; T! v

- H2 @2 K% ?  y) B# S# O传输文件、新建文件夹都可以了,就是传输速度感人。2 f% v3 h2 p) g% J# I- u

& n& ^; P* P$ W以上就是USB虚拟U盘的所有内容了,内容太多而且复杂,所以如果真的想亲手试试,还是要点耐心的。虽然能学到的并不是很多,这里主要可以干茶mass_mal部分,可以学到不少东西,方便以后的熟练运用。我这里也是为以后更进一步的开发做铺垫。
  }. o: m/ J( k  o! y% J
. _/ r' T3 y$ p8 Z
  f4 l# l! [$ F! _0 e" m) }5 i; I9 w4 i8 ]; W; j* b
收藏 评论0 发布时间:2022-3-16 11:16

举报

0个回答

所属标签

相似技术帖

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