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

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

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

6 J# H# Y) r; R8 ~# v7 iUSB基础知识( ]8 u$ [$ q4 K& B' N
USB按接口类型分
: l# K. N8 s- J; T; b% N4 v
控制器/主机(controller/host)
, ?8 i1 t1 ~' v* M- c# q6 T, p* ^$ J! r0 n! W+ c
设备(peripheral)* t7 p9 b$ Y8 L  m8 d; k8 S; ~% V" o
" X) O/ n9 n: |4 j- e7 g; s. }  u/ n) p
OTG(on-the-go),通过id线确定作为主机还是作为设备2 Q( ]# I4 e/ x+ U7 ^" E' N. a
- T+ n- Q$ k6 F- l
按照USB速度分9 b: L. `$ w) T9 A: j- ?3 G9 t4 j

. L4 N+ I3 g8 Y) \) d低速(low speed)
7 C0 r2 A5 t# f& [
; ?) M) N7 V1 B2 K  {4 K全速(full speed)- G& [- j" X4 O
3 ~9 e- O; p$ L8 a" d6 f% A# l3 u
高速(high speed)
: M2 Q1 g. W; U% H0 w/ A7 w; [! l8 ]0 U
6 @" t4 G! B# oUSB接口一般是4根线,VCC GND DM(D-) DP(D+)
; _% T) l, J. F: C( |* q+ `! i* L6 T( ]% V% N
低速设备:在DM线上接入上拉
  ]) j( `9 S! W; k; l% _2 |( z0 z8 {) }1 b5 i5 u& x! v
全速设备:在DP线上接入上拉( f5 S$ j9 X3 Y9 Q/ a  C
3 O+ N. h/ d5 l% R, C$ i( W% `9 T
高速设备:在DP线上接入上拉,在主机对设备进行复位后进一步的确认
# R! \9 b0 B/ L2 q- f
% b" e& Z( N& q  o, n6 c: G2 ^; ^2 q% P
关于描述符+ H* @# X6 c0 ?7 L
设备描述符(device description)8 |' q% M8 h' n, x

  I3 e2 ^3 d0 a& @5 _8 ]$ _配置描述符(config description)
9 D+ c) A* I$ H3 n! R) Z
, M+ C7 E5 W! e% Q9 z接口描述符 (interface description)
* F' z6 i1 s# i
# A" C5 V2 X* N& c* n9 G端点描述符 (endpoint description)
9 a2 q  g, l% m! p' P/ U* J' O8 G4 Q' ]- c6 ^. h
(HQG%R@V4W]{7LOY8DZE{L9.png
5 ~8 b" _6 v# X3 v% x; X, V) u( q/ e& h$ G$ u
设备的“身份”信息存储在描述符中。每个USB设备中都有如下描述符。需要注意的是一个USB设备只能有一个设备描述符,一个设备描述符可以有多个配置描述符,一个配置描述符可以有多个接口描述符,一个接口描述符可以包含多个端点。0 |6 R' }: c4 v+ `; N, {- \
: y/ ^, z; }# @* q$ V
关于传输
$ ^9 w: [1 ^3 O% o# m* J3 V
1.在USB的通讯中,有传输(transfer),事务(Transaction),包(packet)三级。包是最基础的传输单元,与TCP/IP协议中的MAC层协议作用相同。- v. D1 T8 s' ?" z2 D8 x& V
2.在一次传输中,由多次事务组成,每次的事务又由多个包组成
$ O% p3 }4 M+ D: \' b2 L3.与众多协议相同,较高级别的协议的报文是基于/内嵌在低级协议的报文当中的,在USB中也不例外,例如,包中预留了DATA位,其目的就是填写报文
5 x( |6 ]0 y5 w! v  ?2 H7 hSTM32F103芯片自带了有USB模块,可以用来做从设备,不能用作主机HOST。这里使用USB的目的是讲USB作为一个大容量,这个可以基于官方USB Mass_Storage例程来移植。这里就不过多的介绍里面复杂的通信原理了,要彻彻底底搞明白并灵活运用取来还是有很大难度的,因为内容太多了。这里仅仅介绍下移植过程和描述符相关的重点内容。1 P  Q& J6 ]- X+ }/ Q: T

; w1 k/ a" l" N1 ^, B) Q6 ^6 C4 l准备工作
* Z8 P. p; }0 e4 H第一步
7 u, k6 X/ j& i0 c' ]; ^4 l
确保自己的KEIL开发环境已经完善。
! p3 c8 @/ }  f8 T6 G
$ M. t3 h* y; T# Q7 t; r第二步# K% ]5 g$ d) T' C
下载好了两份ST官方的库文件(我是基于标准库开发的),一个是标准外设库文件,一个是USB库文件。如下图:9 y9 }  O, h! f0 w! k" P
7 s: ?. n/ N! {6 j+ _) B
AZ7H5GQWT4J%{N{6){9E]{3.png   ]0 n/ w% R: X* l; E7 B0 u

2 Z% M9 F& G; S- s8 o第三步
8 x; g4 i2 w% C9 B. o建立一个带有存储介质驱动的STM32基础工程,存储介质常见有SD卡、外部FLASH芯片、内部的FLASH空间。的我是基于一个外部flash的工程去实现的,芯片具体型号是W25Q64,64Mbit的空间,换成字节就是8MByte。驱动部分如下:) ]. f, M6 B. Z1 v

7 f' P2 P& S0 a$ B1 ]C文件
) x  }$ C" g0 N8 P9 d8 b  i
  1. #include "fy_w25qxx.h" 7 K+ I& G1 b* {& D; }  K* e+ S
  2. . F5 F5 e- t+ @6 ^; _& q! [8 T
  3. u16 W25QXX_TYPE=0;//W25Q64
    ; [2 s" ^4 n: B$ q

  4. 1 W- w$ c5 ^* F: [
  5. //4Kbytes为一个Sector,16个扇区为1个Block,容量为16M字节,共有128个Block,4096个Sector
    7 z3 Y% p* v9 B9 J
  6. //初始化SPI FLASH的IO口
    0 A; n: Q3 q( G2 f% O2 V
  7. void W25QXX_Configuration(void). L+ \# o8 e. n, s% a$ |9 P
  8. {        $ S" n2 q( B. b
  9.         SPI2_SetSpeed(SPI_BaudRatePrescaler_2);//设置为18M时钟,高速模式
    * K6 _$ h( E! y3 C/ _! w
  10.         W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID.  9 p* D/ R3 b, j
  11. }          ) |9 K7 C2 b! D5 T9 [' |; N

  12. 5 S1 O' Y' P# r( D8 n/ q6 `
  13. //读取W25QXX的状态寄存器# E4 [. I/ K3 W$ H% C$ M
  14. //BIT7  6   5   4   3   2   1   08 U9 N  C9 |+ G! V% K0 ~! b
  15. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY/ d. O1 X. P3 [
  16. //SPR:默认0,状态寄存器保护位,配合WP使用
    6 l! k- m, k, F2 _
  17. //TB,BP2,BP1,BP0:FLASH区域写保护设置1 }) ?) G5 }; h5 ]! o' S/ M1 V0 A* `
  18. //WEL:写使能锁定
    ( I1 s/ ~" ?$ I6 S) {; \* \* x8 l
  19. //BUSY:忙标记位(1,忙;0,空闲)
    1 u( S: Z2 Y: Q2 h9 A7 p
  20. //默认:0x007 ?% v% _# c0 v7 y& P
  21. u8 W25QXX_ReadSR(void)   
    : J' n. ^1 H$ o9 G+ J
  22. {  
    ' A# H3 ]1 _$ U) Z
  23.         u8 byte=0;   
    : B1 o; P! k4 \  w
  24.         W25QXX_CS_L();                            //使能器件   ' C. R$ p" L8 `* j: p% R
  25.         SPI2_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令   
    " e  {% K4 `& i* Z% u
  26.         byte=SPI2_ReadWriteByte(0Xff);          //读取一个字节  1 V1 w  v. ^& C3 k
  27.         W25QXX_CS_H();                            //取消片选     
    ( U( n+ b% d) y/ N9 o3 |
  28.         return byte;   % P% J+ l4 P) V: Y: ^
  29. } 3 [5 G: j. X1 h: `; R; [) z4 W/ p9 [+ g
  30. //写W25QXX状态寄存器; F# r5 q% y, {  @0 {6 r, a4 w
  31. //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!" ?8 k1 I5 U+ I8 P0 q- J. V% W
  32. void W25QXX_Write_SR(u8 sr)   # g3 |; S, p# X+ ]5 Q- R
  33. {     o" e5 Q2 e/ N7 d+ b. d
  34.         W25QXX_CS_L();                            //使能器件   
    ' y8 F  Q' L, }$ S' `8 L
  35.         SPI2_ReadWriteByte(W25X_WriteStatusReg);//发送写取状态寄存器命令   
    : e1 |7 B! ]6 ?8 V4 b* [4 ^4 ~; A# _
  36.         SPI2_ReadWriteByte(sr);                       //写入一个字节  , y& {9 L, J! n2 L4 e7 |2 g1 j& x
  37.         W25QXX_CS_H();                            //取消片选                   $ t' m1 X; X, k8 V1 z& }
  38. }   9 ]& c4 M/ ^" T0 }" f
  39. //W25QXX写使能        
    / M5 r1 g% c" O/ x2 e9 C2 `: x
  40. //将WEL置位   : I" h* |# Z4 R7 J3 O# U2 E0 Z+ [
  41. void W25QXX_Write_Enable(void)   
    3 T  V: c% e: k$ l  t0 Q; X
  42. {' V8 {% t3 U  L: K, k# E, C4 V/ B# P" Y6 J
  43.         W25QXX_CS_L();                                  //使能器件   
    - m; P: M3 J1 a4 E% ^
  44.     SPI2_ReadWriteByte(W25X_WriteEnable);         //发送写使能  . e5 S$ k+ S) u0 q. Q
  45.         W25QXX_CS_H();                                   //取消片选                   + \2 X8 I7 _/ b, H
  46. }
    & O2 i1 d- s; X3 [
  47. //W25QXX写禁止        
    / U( R8 B9 C' Q% g! ?6 T- J6 M  Y
  48. //将WEL清零  9 ~, L; Y$ e& p$ }8 j1 k: `. p8 K
  49. void W25QXX_Write_Disable(void)   : k: ^8 @2 {- v" t- ^2 I
  50. {  0 ^# @4 B- c, C% `
  51.         W25QXX_CS_L();                            //使能器件   
    5 l2 l# n$ C% Q- Z' @8 B) d
  52.     SPI2_ReadWriteByte(W25X_WriteDisable);  //发送写禁止指令   
    # I4 ]. D7 i2 Q$ s
  53.         W25QXX_CS_H();                            //取消片选                  
    % q. h* ^% g- \* H3 d$ S3 _
  54. }                 
    - ~! X- W! R7 L  j
  55. 3 @: m( g) T$ p* I( @2 t0 f
  56. //读取芯片ID
    6 ~: e2 h) S) N, [1 J
  57. u16 W25QXX_ReadID(void)9 l2 `- A$ n: b) f2 x: e9 N% H7 c
  58. {( u: }2 F% ]* e1 X* C2 P0 L5 E
  59.         u16 Temp = 0;          ' l8 }9 N1 \- Q0 @# {! `8 }) k
  60.         W25QXX_CS_L();                                    # Y' F" ~6 B$ d1 V; b2 x! X
  61.         SPI2_ReadWriteByte(0x90);//发送读取ID命令              ]/ x. }4 a) c& J0 g$ X: G
  62.         SPI2_ReadWriteByte(0x00);             ! a* {0 ]* \/ h7 v4 d
  63.         SPI2_ReadWriteByte(0x00);            
    1 y$ E# s; c5 R! G) I* I
  64.         SPI2_ReadWriteByte(0x00);                                     5 }1 n4 m* v9 J- `; m: ^
  65.         Temp|=SPI2_ReadWriteByte(0xFF)<<8;  
    6 ?6 `& I# K6 ~! r# N- G
  66.         Temp|=SPI2_ReadWriteByte(0xFF);         : w; v! n1 P8 V* B
  67.         W25QXX_CS_H();                                    
    4 B5 Q3 m! a2 u/ s5 y! T; {
  68.         return Temp;
    6 ~, k  D' u/ X5 Q$ O
  69. }                       
    ) v; i) o/ V; E& y& P9 N( y

  70. " O$ |/ Q# O, Y  P9 a
  71. //在指定地址开始读取指定长度的数据' A2 [. ~7 ~2 c  F1 x) J
  72. //pBuffer:数据存储区,ReadAddr:开始读取的地址(24bit),NumByteToRead:要读取的字节数(最大65535)
    3 h$ w7 y# g$ `6 w- m6 S
  73. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   
    3 j. `9 f9 b- m8 l2 t$ _. e
  74. {
    . @5 w0 v! U5 e, Y1 \' C8 V0 d
  75.          u16 i;                                                                                       
    ( K( k# P& b% p1 F5 Y4 \2 a! b$ [8 W/ p
  76.         W25QXX_CS_L();                                    //使能器件   
    7 Z1 y! q: p' |# _  K
  77.         SPI2_ReadWriteByte(W25X_ReadData);                 //发送读取命令   
    + A( [- I( M/ j( \
  78.         SPI2_ReadWriteByte((u8)((ReadAddr)>>16));          //发送24bit地址    2 B9 `% c) y1 w
  79.         SPI2_ReadWriteByte((u8)((ReadAddr)>>8));   
    + t( g5 Y, S" [$ R
  80.         SPI2_ReadWriteByte((u8)ReadAddr);   $ w/ z/ f7 K: U" h9 p# g1 V( a# L
  81.         for(i=0;i<NumByteToRead;i++)& ]7 o" P! a# P# }2 t
  82.         { 0 U2 b/ q' R$ m$ T2 D- q: y
  83.         pBuffer=SPI2_ReadWriteByte(0XFF);           //循环读数  
    4 _1 \6 Y6 }; o% c
  84.   }
    0 S8 G3 J, C! l
  85.         W25QXX_CS_H();                                                    " I9 w/ v- s5 s. L6 P+ Z# r* w5 Z
  86. }
    ( D, }2 q8 F. p* E6 E' j

  87. 5 ]5 I3 K# x, E% D7 @
  88. //SPI在一页(0~65535)内写入少于256个字节的数据  F8 B) ^0 e. \( r
  89. //在指定地址开始写入最大256字节的数据2 B  k, r0 U! ~0 j' x$ H
  90. //pBuffer:数据存储区,WriteAddr:开始写入的地址(24bit),NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!         
    8 K9 ?, X4 |" Z* i6 f/ Z
  91. void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
    3 r: E% L  r' i, K3 e* O: M
  92. {
    ' S3 q: [, d7 I( R& W0 {* X
  93.          u16 i;  . h  ~9 K5 h  t
  94.         W25QXX_Write_Enable();                          //SET WEL ! D. v  t% B" ]4 C* j  g
  95.         W25QXX_CS_L();                                    //使能器件   
    % Y  n& d4 R# y+ t6 U( U
  96.         SPI2_ReadWriteByte(W25X_PageProgram);              //发送写页命令   
    ' M# ~$ d% s! ^" q5 ]
  97.         SPI2_ReadWriteByte((u8)((WriteAddr)>>16));         //发送24bit地址   
    & X4 p5 b" O3 u
  98.         SPI2_ReadWriteByte((u8)((WriteAddr)>>8));   
    , x2 t4 S+ s0 r4 v
  99.         SPI2_ReadWriteByte((u8)WriteAddr);   7 s4 Q3 S& M1 x0 _
  100.         for(i=0;i<NumByteToWrite;i++)SPI2_ReadWriteByte(pBuffer);//循环写数  8 t* \7 @5 j1 E; D  w- H. A( }
  101.         W25QXX_CS_H();                                    //取消片选 % J! i' e) ^8 K: p+ C/ M6 W6 G5 F
  102.         W25QXX_Wait_Busy();                                                           //等待写入结束% d5 N9 M% j! a& \- F8 n+ _3 ]
  103. } 0 r) Z$ w: I4 C$ A
  104. //无检验写SPI FLASH
    - N9 R- Q/ \1 _: \, ?
  105. //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!+ ?" M* o% m+ U
  106. //具有自动换页功能
    ) G  @( F# H' {1 [2 ^+ x  o% z
  107. //在指定地址开始写入指定长度的数据,但是要确保地址不越界!4 c/ R: o6 p0 V4 e% _
  108. //pBuffer:数据存储区5 J, a7 `, w' r" ^; X
  109. //WriteAddr:开始写入的地址(24bit)  m5 [% U" d9 @7 q5 H
  110. //NumByteToWrite:要写入的字节数(最大65535): h8 P) J+ ~# w# M/ u
  111. //CHECK OK
    , a/ P- V/ i6 D$ P
  112. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
    . G' c/ x! ?- z2 u; c. C3 M
  113. {                                           2 x( Z4 z9 j& Z
  114.         u16 pageremain;           
    5 m! T6 {( W' [7 [" M7 }
  115.         pageremain=256-WriteAddr%256; //单页剩余的字节数                             
    3 j3 [: Z& ]/ V% l
  116.         if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节
    & e9 I; `. d. @; b+ D2 D4 _
  117.         while(1)( ]  N& k" m- |# ?: T% M
  118.         {           
    # \% k- ?  q& U' p% l8 f: f
  119.                 W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
    + F5 X! F; K% a
  120.                 if(NumByteToWrite==pageremain)break;//写入结束了
    5 R0 z, ?2 V  y9 G
  121.                  else //NumByteToWrite>pageremain1 _5 e7 M& h; t5 P
  122.                 {
    & ]  l' Q& H1 Z" `/ u/ u
  123.                         pBuffer+=pageremain;
    + k/ G. s7 C: Q/ A0 I* B4 x
  124.                         WriteAddr+=pageremain;        $ f  T/ n' T& O7 r6 F0 r# Z0 c
  125. : y0 c3 X  U& [" M
  126.                         NumByteToWrite-=pageremain;                          //减去已经写入了的字节数
    % ]  ?0 X! ]2 G( D: P: V
  127.                         if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
    8 I+ d# l# i& c! V' l; ?2 |6 r
  128.                         else pageremain=NumByteToWrite;           //不够256个字节了& J$ |! O% N. w
  129.                 }: R! X& n  A9 I
  130.         };            ( V1 q0 p! T/ M' b" K& q
  131. }
    ' e- [. x  t  }$ L+ ?
  132. //写SPI FLASH  
    4 C$ ^2 C: k# ~* I. c" Y2 t. z
  133. //在指定地址开始写入指定长度的数据. }: Z1 f4 a6 h' i1 z* w% _( m
  134. //该函数带擦除操作!; D6 e% `. w& _, R7 {
  135. //pBuffer:数据存储区
    9 o) K( ]. a5 @3 a3 C
  136. //WriteAddr:开始写入的地址(24bit)                                                * I: N6 x! L4 e6 \1 n$ ?
  137. //NumByteToWrite:要写入的字节数(最大65535)   
    ; z. e. ]/ Y2 G/ B2 w" L) s' D% A
  138. u8 W25QXX_BUFFER[4096];                   n/ r% ~/ h2 F2 [7 {3 }3 t# R2 r
  139. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   7 m4 ^  v- F/ H7 n) @
  140. { 4 H0 `. c8 y$ d+ P2 C5 A* S' i' Z
  141.         u32 secpos;# u( {7 H" {7 x* F
  142.         u16 secoff;
      h. k$ f; v( ^9 h( t
  143.         u16 secremain;           4 L0 g, e" y2 ^2 z# X3 ?3 v  K9 G
  144.          u16 i;    & N$ ^) K" |1 G  ~: G
  145.         u8 *W25QXX_BUF;         
    ( ^2 i- U$ M: G1 {0 M
  146.         + s! l3 ~# }6 x
  147.            W25QXX_BUF=W25QXX_BUFFER;
    ) j6 J6 F. r8 t
  148. //        
    - q3 _& A3 s8 |
  149. //    W25QXX_BUF = (u8 *)_mem.Alloc(4096);//申请一个扇区大小的内存
    ' k4 c5 i& V7 A0 E2 l6 U% C6 Y
  150. //    if(W25QXX_BUF==NULL)        return;! X. e0 q; I% `# L
  151.         
    + r* J  ~$ h( Z( q* v6 L
  152.          secpos=WriteAddr/4096;//扇区地址  5 b+ c& P# w3 u. S7 S# g
  153.         secoff=WriteAddr%4096;//在扇区内的偏移
    . x1 k1 M2 I" M) v5 M9 u
  154.         secremain=4096-secoff;//扇区剩余空间大小  
    / ^2 P8 C; h( J$ s0 v; c
  155. ( N: i" F; r; Q& @& a) k
  156.          //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用5 o: G- g, E( W- y
  157.          if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节: P  A% c0 w/ }- d" ?  I8 T
  158.         while(1)
    % W% Y3 s& F- ]& j7 m# Y( ]6 ~$ _* Q
  159.         {        
    0 m, V# @6 ]7 k/ J- B4 b! z2 m
  160.                 W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容+ P/ T+ f# a# V. e0 ~4 S
  161.                 for(i=0;i<secremain;i++)//校验数据
    3 }, O$ Z; A0 N; N# A
  162.                 {: V  q1 S  B8 Z6 @, C8 N
  163.                         if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除            
    8 j% n/ }* j5 J1 P' F  m6 M4 y# v
  164.                 }3 n) {# K3 {% S4 ^9 L! Q8 V8 v: G& [, Q
  165.                 if(i<secremain)//需要擦除: [; W5 ?; `; S0 O
  166.                 {
    : k: G3 {" e5 J) D
  167.                         W25QXX_Erase_Sector(secpos);                //擦除这个扇区0 s! U" {" K  j$ Q3 Z! \
  168.                         for(i=0;i<secremain;i++)                           //复制
    . G# g  v: S- A% F" B; _2 d& j: a3 @
  169.                         {
    ) @: }8 G7 T" q2 j# A
  170.                                 W25QXX_BUF[i+secoff]=pBuffer;          0 y3 x" ?9 s  y, X
  171.                         }
    8 X1 ]( K2 ]& i  d
  172.                         W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  
    9 D' b+ L' e+ I% t3 q

  173. ! R" O, a% @8 v
  174.                 }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.                                    
    * g. v, }1 t! S5 w2 J
  175.                 if(NumByteToWrite==secremain)break;//写入结束了4 @9 [. D. q/ W
  176.                 else//写入未结束& o8 B& _  c. F" \
  177.                 {
    " `2 H5 P' ^' Z, B. j/ \
  178.                         secpos++;//扇区地址增12 t1 N( {$ k( x: j7 u
  179.                         secoff=0;//偏移位置为0         
    $ ?8 F( Y6 M: C% A

  180. ) {9 M2 @4 ]9 w6 b, t
  181.                            pBuffer+=secremain;                                  //指针偏移9 E) j9 ?. A! e" v/ {
  182.                         WriteAddr+=secremain;                                //写地址偏移           
    ! ?! N9 _2 S+ l- W
  183.                            NumByteToWrite-=secremain;                        //字节数递减1 ]4 y6 }- v, g8 g6 P/ L1 W. N$ `( c
  184.                         if(NumByteToWrite>4096)secremain=4096;//下一个扇区还是写不完
    + C+ O1 E  x4 K
  185.                         else secremain=NumByteToWrite;                //下一个扇区可以写完了' ?; b  t2 R- g/ H& Q
  186.                 }         
    # x( O* Y6 J# O8 \
  187.         }0 b/ s% y8 N" \0 h( Y
  188. //        _mem.Free(W25QXX_BUF);# c# }. O/ E+ d' d: u0 F
  189. }
    4 l& ?# T. b. A9 N: ~6 q
  190. //擦除整个芯片                  
    : e( i) k- d# P0 C4 Y/ \
  191. //等待时间超长...3 }7 E1 y5 Q, p  c8 a& k9 x* q3 ?
  192. void W25QXX_Erase_Chip(void)   & \- F( G! W9 m
  193. {                                   
    7 c# D! x8 J4 |& ?% H5 k
  194.     W25QXX_Write_Enable();                                  //SET WEL
    5 E# @; r2 D# F$ b7 `
  195.     W25QXX_Wait_Busy();   
    2 r0 J: P+ s. \: F/ s
  196.           W25QXX_CS_L();                                    //使能器件   6 r7 H% u9 F, F3 b
  197.     SPI2_ReadWriteByte(W25X_ChipErase);                //发送片擦除命令  ) t! _, U' A( T
  198.         W25QXX_CS_H();                                    //取消片选                   - o, B' Y% o! K6 ?8 l
  199.         W25QXX_Wait_Busy();                                                      //等待芯片擦除结束' _  R* k  D% Z' b
  200. }   
    & _4 ], N9 v# |2 N+ S0 N
  201. //擦除一个扇区6 k6 G+ I5 c6 M1 I# n4 \$ j- s+ K
  202. //Dst_Addr:扇区地址 根据实际容量设置4 v8 L5 c' t) n0 q2 ?% V
  203. //擦除一个山区的最少时间:150ms
    ; [% z: D( k  n3 ]
  204. void W25QXX_Erase_Sector(u32 Dst_Addr)   
    1 |; i# X0 M% U* i
  205. {    I2 M* G) M4 c" U" T  `
  206.         //监视falsh擦除情况,测试用   3 ^, K& I& G  o( t! J+ W
  207. //         printf("fe:%x\r\n",Dst_Addr);          4 a) P+ w, h+ U
  208.          Dst_Addr*=4096;
    4 n8 Z$ J; Y4 S
  209.     W25QXX_Write_Enable();                          //SET WEL          7 e$ z; s6 F' {  t4 k
  210.     W25QXX_Wait_Busy();   
    * R9 s6 ~: R( X: o1 W4 c8 O$ C2 U
  211.           W25QXX_CS_L();                                    //使能器件   ; X# c  d1 L% a8 u/ M
  212.     SPI2_ReadWriteByte(W25X_SectorErase);              //发送扇区擦除指令
    9 s9 I' J( Z2 N) Z  E
  213.     SPI2_ReadWriteByte((u8)((Dst_Addr)>>16));          //发送24bit地址    5 u# O0 U0 p" t5 V- e8 h1 ~6 o! z
  214.     SPI2_ReadWriteByte((u8)((Dst_Addr)>>8));   ( n7 z( \+ x7 q4 Y
  215.     SPI2_ReadWriteByte((u8)Dst_Addr);  
    ; w" P6 g8 Q: r1 r4 y+ s1 Q5 U
  216.         W25QXX_CS_H();                                    //取消片选                  
    6 p: B8 p' T. A, t
  217.     W25QXX_Wait_Busy();                                                      //等待擦除完成" q0 `3 K0 F; I& ]5 W' s' q- C; q
  218. }  
    2 O  @# r9 I6 c) S' L6 n5 k" @
  219. //等待空闲
    & Q- Z  `3 V7 l( S( w
  220. void W25QXX_Wait_Busy(void)   ) K7 k0 l$ S% `) o; k
  221. {   
    & N4 T7 ]' x) f: u, V2 E, C
  222.         while((W25QXX_ReadSR()&0x01)==0x01);                  // 等待BUSY位清空
    9 `7 v: \" Q' ]' `
  223. }  
    0 h; I* q6 \1 H1 k! Y
  224. //进入掉电模式+ V( e7 ^. r+ p6 @) n# a
  225. void W25QXX_PowerDown(void)   
    + B8 ?9 U6 q( W/ r0 h6 M
  226. {
      X4 F, [1 C2 A$ I( o3 [
  227.           W25QXX_CS_L();                                            //使能器件   0 {/ f$ M# Q7 H2 S
  228.     SPI2_ReadWriteByte(W25X_PowerDown);        //发送掉电命令  ! K/ A, \4 s( M9 e( m
  229.         W25QXX_CS_H();                                    //取消片选                   ( d: m/ N& ?% Q0 @) y* ^
  230.     Delay_us(3);                               //等待TPD  4 P" a# c  g6 h% x8 ]; X
  231. }     e2 a5 I' F3 d8 M/ X
  232. //唤醒% _. K( m8 _6 m
  233. void W25QXX_WAKEUP(void)   
    + p7 N" l- h3 h: e( q4 w
  234. {  
    $ Y5 r  G7 z+ e, n- P/ B) O& |
  235.           W25QXX_CS_L();                                    //使能器件   & a, t, X/ h" e6 A( k
  236.     SPI2_ReadWriteByte(W25X_ReleasePowerDown);        //  send W25X_PowerDown command 0xAB    * e& y, b7 ]  w. e' h+ Y  `
  237.         W25QXX_CS_H();                                    //取消片选                   ( N* S' m9 S  W( o$ K; _
  238.     Delay_us(3);                                    //等待TRES1
      ~3 S' P7 X2 J6 L' I& c. n$ v! |
  239. }   
复制代码

- e5 }" Y& o/ o& _2 SH文件
* M( P) e3 V  \) X/ X
& c" Q. X/ |: B( K7 |
  1. #ifndef __FY_W25QXX_H, m0 W7 s! b/ A8 e0 C7 c
  2. #define __FY_W25QXX_H                           
    2 ]  U0 L$ N  d# ~7 U

  3. ; {6 e: B; u4 C& `" b& O( k/ U
  4. #include "fy_includes.h"
    4 F4 a( u. j6 K2 y7 }1 a. g. b1 G

  5. 4 K+ [" [% L" u7 D2 u; d
  6. //W25QXX对应唯一识别ID% b9 s, f3 m0 b: c6 l& N
  7. #define W25Q80         0XEF132 @0 z2 p/ m& x8 w% ?4 Q" s+ V7 J/ V
  8. #define W25Q16         0XEF14
    " c5 p: }3 }4 c- m1 ~% v8 T
  9. #define W25Q32         0XEF15" Q# `/ Q* V* {- U; D6 U
  10. #define W25Q64         0XEF161 T, p  b- ~1 P4 j, k
  11. #define W25Q128        0XEF17
    & c4 Y7 x  J; o8 @: K) I( f- X

  12. 1 L% u5 Q3 N+ x+ d
  13. //指令表
      B0 B5 K1 w# X0 M9 ]# l9 G- M5 d
  14. #define W25X_WriteEnable                0x06 . g! A' G; _8 w4 }: e/ ?# X
  15. #define W25X_WriteDisable                0x04 ; G0 A" p- K* j) H
  16. #define W25X_ReadStatusReg                0x05 ) p) \( W8 @" I  o7 g
  17. #define W25X_WriteStatusReg                0x01 / P/ I  W  ~6 W9 q* W! e8 A2 r
  18. #define W25X_ReadData                        0x03
    $ X4 o0 A* B$ j: S5 W! {6 {
  19. #define W25X_FastReadData                0x0B * k" d' q. [) ~+ `. g/ c
  20. #define W25X_FastReadDual                0x3B ( F- }/ F3 {/ ?1 M
  21. #define W25X_PageProgram                0x02
    4 y- r- Q* _" o5 R5 D
  22. #define W25X_BlockErase                        0xD8
    % j8 `, J8 S& E# D6 q( r9 Q
  23. #define W25X_SectorErase                0x20 " M0 P& o6 k7 h
  24. #define W25X_ChipErase                        0xC7 0 N% d. L6 F) j
  25. #define W25X_PowerDown                        0xB9 ) f& r, H$ U! D. k8 \1 f' E* C
  26. #define W25X_ReleasePowerDown        0xAB
    , H  V' ]+ j9 L; [2 f- n; f4 @
  27. #define W25X_DeviceID                        0xAB
    7 x8 @$ c$ e# j4 Y  G% c( }
  28. #define W25X_ManufactDeviceID        0x90 - f% E7 Y2 g- a; C0 H1 `
  29. #define W25X_JedecDeviceID                0x9F
    3 Q( F& F6 ^) N. Q( j$ H: _8 {4 n
  30. $ S3 x& E: q6 B3 J3 b: N
  31. void W25QXX_Configuration(void);
    3 w& u0 G' L* S0 s" K
  32. u16  W25QXX_ReadID(void);                              //读取FLASH ID1 ^& L, C3 g2 ~) i, }6 q- W7 R
  33. u8         W25QXX_ReadSR(void);                        //读取状态寄存器 7 X* J3 g4 ~4 O7 M6 g+ Z) P/ H
  34. void W25QXX_Write_SR(u8 sr);                          //写状态寄存器: S' y- W. W  P0 T7 P
  35. void W25QXX_Write_Enable(void);                  //写使能 0 r8 ]! H$ J- q& K- _4 S
  36. void W25QXX_Write_Disable(void);                //写保护
    4 @: f& e& P5 Y0 G6 q4 ^0 F
  37. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);- X" W3 t* K1 Z
  38. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //读取flash
    # w( K: Z# D4 _- M4 P
  39. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写入flash
    , @* ^: @8 y! }: l$ n# T9 d- J
  40. void W25QXX_Erase_Chip(void);                      //整片擦除* ~4 r- `6 a$ j1 v3 g, h, N1 y
  41. void W25QXX_Erase_Sector(u32 Dst_Addr);        //扇区擦除
    ) V9 W" `: r) _( s& ?$ n2 b( @7 p+ x4 v8 M
  42. void W25QXX_Wait_Busy(void);                   //等待空闲) u  U+ q0 Y: P! V% |8 I
  43. void W25QXX_PowerDown(void);                //进入掉电模式
      K1 `7 E' U! c/ C2 O
  44. void W25QXX_WAKEUP(void);                                //唤醒
    * ~. p0 l5 `0 o7 O+ W( {

  45. . L8 K6 w3 n& m: S$ V3 }! G- I- _
  46. extern u16 W25QXX_TYPE;                                        //定义W25QXX芯片型号                  
    ) g  n+ k7 x1 E+ V& F6 E
  47. 5 ?% _0 T7 }3 X8 f/ g  F
  48. #endif
复制代码

1 T; N0 J5 M# b基础工程如下:
: f% W4 w  a( M" B: C' H
5 K4 c, e0 Y) O) T! E8 ~' { CIG]SRYALJ}8)5NQ[MAA~BW.png
" |3 |/ `4 r0 f$ k/ X2 L7 H5 T: {
: f% }  g8 l' L6 h第四步:USB代码移植7 d* g6 ]& @. u, c. J
拷贝USB底层库到工程根目录,新建一个目录命名成USB,拷贝Mass_Storage例程下的src和inc目录下的所以文件到USB。
" h" t5 o; u8 O/ T; R1 l3 o8 i
& ^  Z- A; [3 {$ c3 [) { 6Q]9Q9812B]OXMA8GNV[PCC.png 5 G) M" e  m8 |' E/ M( G$ `0 H

+ I! |( r' w& O拷贝过来是有37个文件,这个时候需要删除一些不必要的文件,因为官方的例程里面有nand,这里的话先删除以下文件
  q( }2 V$ x; M0 c4 n# i' V' u' p- L
VN1JDR_I@JO271YJKNV1(HE.png
7 p0 r4 t6 s- ]! i1 H
: ~* S- V  R) c4 F8 j: G4.打开工程把分组和文件及包含路径添加进去,这里就不细说了,结果如下* `; A  [6 p6 u. r
. G, d! ]" U0 }. E
X$DZH7HHYEC}266_`72~4EH.png
( j5 r; s6 g: x. p2 S& [- V" s$ a4 R' g- V
打开usb下面的main和it 把重要的代码先拷贝出来
3 R6 I$ _( ?$ j% p3 e, Y9 N
' m1 P0 O- e. ^* D OA]E5J8OQ0[B{UJV@KXPUG2.png
5 G# `* p4 v0 d9 F2 V6 ?! X9 A* F' F3 \) s0 _
把main和stm32_it里面的内容拷贝到自己的main里面进行下封装去掉不要的保留剩下的,然后就删除it和main三个文件。
' x' h7 i0 f5 U' K' V- F" f- y$ i! t9 h8 {
@G5L9U9`9]${JVP}AD@KN0E.png
' a# {/ F4 e* \& m$ ?0 W7 o) I3 l
然后编译下,这个时候会出现一堆报错,需要慢慢一步步搞定,编译前注意路径包含,
0 n8 P6 Z7 Z2 Z0 A7 F, D* E
  u# n  J% c+ i& S4 L [~3UDXP_99@A_@~XV14BNPB.png + N6 r* M8 \/ I" ~

; |) ?8 o2 B: M$ a+ S( |. K! m定位到第一个错误进去* @2 v. y# P' |8 @
6 K: f3 Z, @" o* @- d+ X% Y
5L28CK8A4)MY@OJR7KK~KOY.png ; h: j; |4 s/ l2 S
8 ~  a$ `) A1 P/ l6 {8 G9 x
这个定义的是官方的开发板 我们直接修改成自己的公共文件就可以,并且注释掉开发板定义. P  T9 k+ e2 U6 V/ g
8 L" Q1 G$ a) d/ Z
TR]6[QMF5`A7X$MYZIL])9I.png
! K, n- a7 j" [
7 t0 W6 A* {- \% t- i, x. x公共文件如下,并且添加usb部分的头文件( F- W" F; \4 ]: {9 g

) m  @5 Z) m1 J2 @7 W, l% p5 ? Z7Z6YX`QZR}6~KCC_GDDLD8.png 7 \: ?3 ?; B, X) P$ K% m3 d

+ d  k, J) m0 K1 Q, y$ a3 G3 `" |继续编译,定位第一个错误,这部分主要修改hw_config文件
  C! @' Q+ ^. V9 O+ O7 w; `: ?# j$ B# Z+ S% F
_9RUE5O1M][P)IRX]S2S_$D.png 0 c! U+ ~/ W2 @8 ?% O# a% r( ?  w

& P* s6 h9 x) g6 u. H删除Set_System函数6 t* x3 c6 a0 \

5 p, ~' g: a6 K; w N~R76S188E[X(N6NID1WQ$V.png $ Y/ |- y1 m2 O: H" b; R
5 _4 _! s) K& u2 I. W
删除Led_Config函数
6 M" w- w* M$ `$ h3 Q5 @; I7 _( P! k, L/ [7 T
~HA$Z23~@)XV2TEQ8CUTS.png / b1 c' R0 i; Y. P! F; K
. L5 w- @' W$ r! z
注释LED相关的内容$ E$ n$ L* z# m" ]7 x7 a

5 a* T3 {! ~- L" i6 _6 t# P/ M _FKQ$_}1}S8W%PG(2VT2]C7.png 5 E# p' _. e0 g# D7 H

- ^& ?3 V6 q) G; N2 T删除USB_Disconnect_Config
/ e- k" g" v; o4 m9 k. ~5 j( i: u
- U* O# N9 X! m! h2 [- U 4K@_T@@XVZ@P]EEH37}QAJX.png , y' K# t; Y! [+ m
6 h1 I* e1 b7 Z2 H2 j
接下来主要修改mass_mal部分,我这里是只有一个flash,所以我修改成如下:7 D8 ?% X% Z, ]% E

! r& W, Y3 _% e0 m 2_1P4[7IG68PG%9TS@41BI4.png
% k1 U+ C, v- V$ I$ O% ]5 i6 U/ z3 P
头文件
% i/ h) K- U' `0 }2 u' c$ i! d
$ N* Z) x+ H7 _& Y/ O! S; U' A( [ (V0@{X{W8~L[2Z[Y7)VNU.png
: a) C8 S8 _! H' |9 z1 d5 ~8 H, p' f6 X% ^
下面修改memory文件,主要修改变量定义,C文件和H文件分别如下
- D, |$ f5 |9 h, z, y  M# I4 n7 `. w
T]Y~~F2{V(H`V{$X(}FMNNJ.png ! X3 ^4 P; k5 W2 u  x! J

; H" c- Y3 h. `0 h# L接下来是usb_scsi部分的变量定义, E: n- B. o* b8 u, ~: D. W# m
, w! u8 x5 n2 \- h6 c- j
R$Z1OIMFO6JWKJE~XX[{2$B.png
7 B. v$ o5 s9 F& w* z4 o7 z. @9 u% x, Q2 R- D4 `; l0 }0 E; D
这时候再次编译就发现没有错误了,但是这个地方USB中断部分还需要配置下,官方用的宏定义方式去实现不同的代码,这里一开始就去掉了宏,所以最终修改如下:1 [% Q( c4 _3 N- A( g* X0 g( h. T

( J- l4 n, L, f2 A/ A8 _
  1. /*******************************************************************************
    2 s& K- f& c8 y0 r: a. ~
  2. * Function Name  : USB_Interrupts_Config+ ~/ W8 W' J' \8 B" E
  3. * Description    : Configures the USB interrupts" p! p; n6 [# B* V% q( }
  4. * Input          : None.
    % W) _6 h9 ^7 P) ]6 t
  5. * Return         : None.9 L, l+ z+ n1 W  f8 u3 J
  6. *******************************************************************************/$ H$ r9 f* A0 r3 B, o
  7. void USB_Interrupts_Config(void)5 u5 z, k2 Q: |* o3 D
  8. {
    ) E2 t1 J( s0 V
  9.         ! b- u' P- {4 |$ U4 e, J# ^& F
  10.         NVIC_InitTypeDef NVIC_InitStructure;
    * k* m1 [1 R% W& O
  11.         EXTI_InitTypeDef EXTI_InitStructure;
    # N4 e; x. `0 A. s+ d1 i) m- ?
  12. ; z* j; v; _1 r! v! D, I$ K
  13. " J9 V" X( `! q  u
  14.         /* Configure the EXTI line 18 connected internally to the USB IP */1 x9 P: x& |7 H) a3 |
  15.         EXTI_ClearITPendingBit(EXTI_Line18);
    / w1 V8 W* N" w8 o) w
  16.                                                                                           //  开启线18上的中断
    5 N+ I( H2 h: t) z, g
  17.         EXTI_InitStructure.EXTI_Line = EXTI_Line18; // USB resume from suspend mode
    4 ~  z) y" X7 x6 @* |7 G$ w8 W. f1 m
  18.         EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;        //line 18上事件上升降沿触发
    7 P' \( c! }- l9 ?0 C3 N! F
  19.         EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    $ k$ ]# ^5 E: L. l8 m" R# j! S9 c2 Z  q
  20.         EXTI_Init(&EXTI_InitStructure);         
    7 p1 X/ u) A) \, q# H1 {# h
  21. 5 A) v% W7 P/ ^, @; o
  22.         /* Enable the USB interrupt */
    , Q) n" C2 X. F# D! u8 k$ j4 h6 M& j+ G
  23.         NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;        //组2,优先级次之
    ) e  j7 H4 |& w; v( q0 d( y5 H
  24.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    $ A# ], m/ j  N5 ~5 c% o
  25.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    8 z, q6 m5 B3 t7 f0 @- m
  26.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    & d) }! P7 ]/ V7 I+ T
  27.         NVIC_Init(&NVIC_InitStructure);. ^& [# g( U9 m/ o8 _5 s( P
  28.         , D4 @- x9 {" r3 H, U0 t1 v% v
  29.         /* Enable the USB Wake-up interrupt */
    # m2 l0 P! b" ?9 I% f
  30.         NVIC_InitStructure.NVIC_IRQChannel = USBWakeUp_IRQn;   //组2,优先级最高        * U$ |5 W; q5 g5 h' |% @5 W
  31.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;% K& `1 u0 l% A7 V) x
  32.         NVIC_Init(&NVIC_InitStructure);  1 m$ s- N+ @) E6 X9 _

  33. 4 D# l4 w6 H$ j- Y- h$ J2 `( Q6 |

  34. / _6 D7 P* `* ~% i2 G+ F: h
  35. }
复制代码
( b; W- U/ e+ q
再注释掉USB_Cable_Config的内容(DP上有1.5K上拉电阻)并修改中断函数
8 T- \/ g* a3 |0 g' K- k7 T" C" o3 @3 t) c) z9 i$ l
  1. //USB唤醒中断服务函数: s# _; p7 U7 N, e2 K, V! M
  2. void USBWakeUp_IRQHandler(void)
    : i& p* ^1 ]2 h, {7 `  g) l$ x2 Z
  3. {
    8 K* R) z( f6 d" n
  4.         EXTI_ClearITPendingBit(EXTI_Line18);//清除USB唤醒中断挂起位
    3 W2 B  X1 [) \, E# S' }* H
  5. }
复制代码
, H0 c. i; ?8 u7 @
修改pwr的Suspend内容:& v7 X% |- s; L2 M
* W& v  }7 J, J. L! g2 ?. Q
  1. void Suspend(void)+ f2 F+ x4 Y( p: w: H( m
  2. {2 I) t4 y* k1 t( V, }
  3.         uint32_t i =0;
    & \$ F1 c. l. v9 d7 J
  4.         uint16_t wCNTR;
    ( W2 [% z( q1 v" K0 E% H$ ^
  5.         __IO uint32_t savePWR_CR=0;& [/ J' e' H+ C. y5 m( a
  6.         /* suspend preparation */( v$ W* ?2 T7 q; K) K0 A( G) O
  7.         /* ... */- }# O) w2 c9 F# T" U9 M% n
  8.         , j# c' D4 C. ^  F* O+ Q
  9.         /*Store CNTR value */
    8 J( A; y% z( W. d; S! d( G
  10.         wCNTR = _GetCNTR();  # s8 h0 V' k% w0 }4 j& f  E

  11. * f8 B( y9 |  U9 `. q
  12.     /* This a sequence to apply a force RESET to handle a robustness case */
      A+ ?5 _$ E: U  W4 v* _8 _0 ~

  13. 5 A+ O6 N" L5 c6 Q' W5 d
  14.         /*Store endpoints registers status */
      v& f8 a7 G- \3 r, S' i2 c
  15.     for (i=0;i<8;i++) EP = _GetENDPOINT(i);. {  p! Q' z) o2 h  p% w* e" y
  16.         2 R9 F& f0 E' N) G! \3 F- l: _
  17.         /* unmask RESET flag */& x* N/ ~! k: l4 ^5 g0 x
  18.         wCNTR|=CNTR_RESETM;' x  i$ E7 |! H  H2 O. h
  19.         _SetCNTR(wCNTR);; ~, |) Q9 a0 P: c' `  S& b  `  A+ Q( Y
  20.         
    , n; M% K7 F( ]" h. D! p* X
  21.         /*apply FRES */$ S* d8 z  x8 t/ m5 ^4 _  I
  22.         wCNTR|=CNTR_FRES;3 @5 @7 c0 a" \) L. d7 r
  23.         _SetCNTR(wCNTR);5 \+ z/ ^: s5 Z5 l0 c- U
  24.         3 x( k$ [* T4 u: u
  25.         /*clear FRES*/: E6 M9 e7 S# [1 @# S3 z) K! j
  26.         wCNTR&=~CNTR_FRES;( [0 i+ N) P: g$ Y& t7 ]
  27.         _SetCNTR(wCNTR);
    & p' r, p# U4 S8 }1 ]+ Z
  28.         
    / Y9 m- n' u6 s
  29.         /*poll for RESET flag in ISTR*/. N! J4 N% ?& V3 \' ~  `
  30.         while((_GetISTR()&ISTR_RESET) == 0);7 U; X4 Y! D  z% t* O  S* R
  31.         
    0 r4 |; \8 i6 Z
  32.         /* clear RESET flag in ISTR */) _9 M9 ~$ t3 c( S. h3 {( o
  33.         _SetISTR((uint16_t)CLR_RESET);' q! a* l) d) s, r' J1 ^  n: O
  34.         8 x8 W7 y# K3 _$ ^
  35.         /*restore Enpoints*/
    7 f1 s$ u: r) `* H
  36.         for (i=0;i<8;i++)% N- {& X* f: @8 ^; h) A
  37.         _SetENDPOINT(i, EP);4 Y2 Y5 b6 m+ |+ s) A- P4 I, H( z4 {
  38.         
    / S! Q0 @+ {* P4 J
  39.         /* Now it is safe to enter macrocell in suspend mode */5 h7 w0 ^0 p, X1 ]  \
  40.         wCNTR |= CNTR_FSUSP;
    / r* Y, s9 F" l$ t% ]4 I3 ?
  41.         _SetCNTR(wCNTR);
    3 S% u8 q* x4 T/ {% D+ T  P
  42.         + b8 r' a! P8 N/ B
  43.         /* force low-power mode in the macrocell */
    : f3 A7 c# w  p
  44.         wCNTR = _GetCNTR();% ^6 B8 `7 p( S6 q5 C- t; Y6 _) g
  45.         wCNTR |= CNTR_LPMODE;
    9 ~( m9 s# w% r, R5 y( I
  46.         _SetCNTR(wCNTR);
    . b$ K6 W, U9 r) ^
  47.         
    . B( L% L8 u4 e1 l
  48.         Enter_LowPowerMode();
    7 z( P! ?, g! \3 h+ C$ h8 v
  49. }, ?# o! e6 _) o+ V9 ?$ M
复制代码

" Z; y. l( t  V+ b, L, o把USB初始化部分不要的注释掉:
' k; L' ?  O6 M; A( P& G, k+ k) }2 p; I2 N6 K
  1. void USB_MSC_Configuration(void)
    5 {* Q/ `6 j: l6 ~5 ^$ R) W
  2. {( ?+ ^' ~  L: d8 w7 O
  3. //  Set_System();
    # U9 m* N5 k) _5 C& R5 \
  4.   Set_USBClock();/ O; a" E1 f8 ?6 [
  5. //  Led_Config();6 `$ Z, h' X6 _2 c( J
  6.   USB_Interrupts_Config();
    , r& Q# G6 H! u% W% {% g$ @
  7.   USB_Init();/ L* d; {! P" s% N- E( @% T0 v  i" a
  8. //  while (bDeviceState != CONFIGURED);
    * m1 r, h  T- |0 B. W/ p
  9. & G5 i& z5 l( t( u
  10. //  USB_Configured_LED();
    . C% v: I! y! e; T7 f( X& v2 m

  11. ) O' i7 n; g+ q
  12. //  while (1)
    7 Q) K: m* Z$ ^9 F( m
  13. //  {}4 ^. @# @( u2 e; o( M
  14. }
复制代码

. [4 C. G- S& G/ l0 @最后在USB初始化之前给U盘定义的数组赋值,不然就只有个盘符
# ~' N1 V6 l5 T1 Z
6 ]1 N& F- S8 y
  1.     Mass_Memory_Size[0]=1024*1024*4;        //w25q64->8M 给4M做U盘
    , d0 t! ]- ^* _; n9 P1 g
  2.     Mass_Block_Size[0] =512;                        //设置SPI FLASH的操作扇区大小为5129 }1 m' H; Y2 Z3 C$ {. @$ R6 a
  3.     Mass_Block_Count[0]=Mass_Memory_Size[0]/Mass_Block_Size[0];
      j' K' _" U: P8 ^3 l0 l
  4. 5 P- \" ~5 ^2 f& ?6 j" U
  5.         USB_MSC_Configuration();
复制代码

- i2 p( M3 m9 f, C; D6 F1 a SEGUXF{Z3MWXPLSF5KTU9.png ! L" i9 }: v8 L) W; h
$ h, \, B6 e6 i7 W) Z( ~
编译下载后就可以正常运行了。: q1 x$ C3 B, t. a9 T1 ?
/ T0 v9 E" W) W# u0 y) e- `
_IXXNOSIUI%KX_{0NPN2T%2.png 4 b9 `/ D+ Q6 Z! h

3 D$ d6 q' r* `" J& b  K传输文件、新建文件夹都可以了,就是传输速度感人。. J* `5 Z6 b3 Z6 v- C/ c
( }4 _5 c& L0 b* s3 l1 o- e' O
以上就是USB虚拟U盘的所有内容了,内容太多而且复杂,所以如果真的想亲手试试,还是要点耐心的。虽然能学到的并不是很多,这里主要可以干茶mass_mal部分,可以学到不少东西,方便以后的熟练运用。我这里也是为以后更进一步的开发做铺垫。
1 }( D/ u6 O+ B
6 K; S+ P3 g8 R( P! s6 z
3 ]4 W7 d1 ?/ a6 x3 a6 @2 a& M2 O7 _
6 F* |4 K0 R6 \( I/ V3 I* E
收藏 评论0 发布时间:2022-3-16 11:16

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版