学习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 _
- 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
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
- #include "fy_w25qxx.h" , ^7 Q$ p* F0 V8 |- h6 U
- 1 t2 i1 d6 ^! l# E' G' B
- u16 W25QXX_TYPE=0;//W25Q645 `8 X. \! o" d5 T P$ r- V
- 9 Z8 L3 n( D5 Q7 H
- //4Kbytes为一个Sector,16个扇区为1个Block,容量为16M字节,共有128个Block,4096个Sector
+ @/ j& m M& o1 |' ` - //初始化SPI FLASH的IO口
: W9 s6 V7 B: H - void W25QXX_Configuration(void)8 J* _2 L) r* n n
- {
2 |4 Y5 n. o2 c6 E$ f) F& H) u/ H: k- h - SPI2_SetSpeed(SPI_BaudRatePrescaler_2);//设置为18M时钟,高速模式
0 R. H& E! J/ `2 ] Q& M - W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID. 5 `0 K7 R4 h/ d
- } - J* L0 @ G" e8 ?/ ^. D' K
) Y; K; k5 S+ W; G/ }5 A4 U- //读取W25QXX的状态寄存器5 M" f' q' u4 F; O0 |/ R
- //BIT7 6 5 4 3 2 1 0
) c- x/ d: e2 j2 D - //SPR RV TB BP2 BP1 BP0 WEL BUSY( `+ R- a% D: C; q& p; @) A9 m, W% v7 Q
- //SPR:默认0,状态寄存器保护位,配合WP使用: w2 L5 a6 o6 {% X0 S
- //TB,BP2,BP1,BP0:FLASH区域写保护设置
8 f: |, r( _, I0 |* a9 ? - //WEL:写使能锁定9 P2 T, o2 b4 L2 c+ u( f: P
- //BUSY:忙标记位(1,忙;0,空闲)
' y: {& f9 X, F4 A1 ~ - //默认:0x00
: j5 f& ?+ K) e# j% K/ P1 d - u8 W25QXX_ReadSR(void) ; l, H3 a* d3 P3 b& v9 C
- {
8 p+ O* N, L5 i% d' ~9 [# O - u8 byte=0;
( ?9 e; ?5 l9 J% D* q" e - W25QXX_CS_L(); //使能器件 # D+ U( ?, z' c3 Z) t; W
- SPI2_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令
5 ~) g# L; O8 K1 E7 T - byte=SPI2_ReadWriteByte(0Xff); //读取一个字节 + d5 w+ q/ U' ^
- W25QXX_CS_H(); //取消片选
0 t5 a* {$ d2 {4 S5 r( h; a/ _ - return byte; 2 f# v9 @. |3 Q/ S+ K
- }
1 T- F5 A% W* _' @# @& J - //写W25QXX状态寄存器
5 e6 f$ V" u9 e- a+ B - //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
3 V) f V6 y- c. A7 B# q - void W25QXX_Write_SR(u8 sr)
* ]8 d* [1 y6 u/ T - { 4 W V7 C8 p6 w/ [' N
- W25QXX_CS_L(); //使能器件
6 q$ x: l/ V4 D/ F, d& n - SPI2_ReadWriteByte(W25X_WriteStatusReg);//发送写取状态寄存器命令 3 C% s- S! h* ]. w3 a
- SPI2_ReadWriteByte(sr); //写入一个字节
/ J' l( S8 U, g5 D; w8 D( ~$ z - W25QXX_CS_H(); //取消片选
6 U/ [# r! T8 M( }$ ]* p: P - } / p" V/ C2 ~# t) ^# ?- ?; E: U. ~0 w
- //W25QXX写使能 - O) h7 O/ F- J, o
- //将WEL置位 % F/ z2 u6 F) ]
- void W25QXX_Write_Enable(void) . f( k$ g$ g9 S$ T. R
- {6 }% o/ c7 p% |- f7 E
- W25QXX_CS_L(); //使能器件
- ?* I ]/ ^$ f) U$ O - SPI2_ReadWriteByte(W25X_WriteEnable); //发送写使能 ! ^, t) z6 g+ x
- W25QXX_CS_H(); //取消片选
* t3 Y. }# a% Q' z9 c* Q' b" c- @ - } 1 m4 Z. B9 Q7 |( p
- //W25QXX写禁止
m, u3 Q% d9 B - //将WEL清零 4 y+ o9 o, ^4 m1 W( i1 U& X4 I
- void W25QXX_Write_Disable(void) 0 [) j j. O; `: {
- {
( T5 O! x1 J+ N% P* r0 ] - W25QXX_CS_L(); //使能器件 , X# K( C6 i! Z$ i( o* k
- SPI2_ReadWriteByte(W25X_WriteDisable); //发送写禁止指令
$ q% f7 O+ L8 N6 f: l# t4 t - W25QXX_CS_H(); //取消片选
% S" X( O3 \+ m4 n - } ' I3 P- B$ H( m5 I
# g) H9 e7 Z, n: A- |) E2 f- //读取芯片ID 1 r X5 T9 d, M
- u16 W25QXX_ReadID(void)
+ u# J+ V; c( E1 _7 S6 R - {
) o ~* [+ x5 T& K: j- q' T - u16 Temp = 0;
6 d' `8 I/ | |# G4 g - W25QXX_CS_L();
7 [# |: p8 E5 y7 ^6 X: T: T - SPI2_ReadWriteByte(0x90);//发送读取ID命令
& f6 f! J8 _: H& d - SPI2_ReadWriteByte(0x00);
$ \" z5 W8 x/ Y. D - SPI2_ReadWriteByte(0x00);
! I8 a8 T8 e) p& W - SPI2_ReadWriteByte(0x00);
7 q+ K+ {( t) ? H; t& a% q - Temp|=SPI2_ReadWriteByte(0xFF)<<8; $ K! \) y3 v- d* q$ C: g5 ~9 H
- Temp|=SPI2_ReadWriteByte(0xFF);
% `5 S4 X3 [- K; _ - W25QXX_CS_H();
& [$ W$ l! O% _+ N) }. I - return Temp;
% v& p. E* }# k- i1 Y7 a) k - } 8 b7 `# b* a# Y5 K
- ( D- w3 o# q5 ]( S; \0 z+ U
- //在指定地址开始读取指定长度的数据
+ R4 S$ Q; f% x. E# J7 ^- C/ G - //pBuffer:数据存储区,ReadAddr:开始读取的地址(24bit),NumByteToRead:要读取的字节数(最大65535)0 |. d. f: p& k2 S: B3 Y/ u
- void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
% M% x: i- K( o9 W6 @ - {
; d7 \3 s/ ]! }% O4 [: m2 X5 t - u16 i;
8 V* S0 o, d1 u% ~, Z7 ? - W25QXX_CS_L(); //使能器件 # \( E% V7 E5 {- z1 |) d+ B
- SPI2_ReadWriteByte(W25X_ReadData); //发送读取命令
" l( G9 J, Z. k/ j- i - SPI2_ReadWriteByte((u8)((ReadAddr)>>16)); //发送24bit地址 * Y( f4 `1 k7 f! i9 K
- SPI2_ReadWriteByte((u8)((ReadAddr)>>8)); + l1 ~# v4 M0 @( V! Y6 s% p9 R
- SPI2_ReadWriteByte((u8)ReadAddr);
$ t+ q4 S; S D7 |1 t" B - for(i=0;i<NumByteToRead;i++)+ t3 u0 J6 D/ f6 R
- { ' D( ^5 Q3 G! x9 A5 @6 o
- pBuffer=SPI2_ReadWriteByte(0XFF); //循环读数
, Z% W/ L. a; s. ]( O! m0 N6 z - }
( C9 d* U" v9 h3 I% L+ L/ g7 N% W - W25QXX_CS_H();
, v6 J! B8 D; i, L* z; L - }6 W0 q: _0 z9 F8 A
- ( p% }6 F; k+ _; K6 G
- //SPI在一页(0~65535)内写入少于256个字节的数据- o% ]- @* v" V3 A. Q1 z3 p8 p
- //在指定地址开始写入最大256字节的数据3 [: {! B. q! Q. l; y' g$ ?6 f
- //pBuffer:数据存储区,WriteAddr:开始写入的地址(24bit),NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
+ p. B! C$ N7 W - void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)2 S7 x0 f& d! c( G6 {3 ^/ _) r
- {
+ f& z* E% h6 M/ l% K. m; n& j" F5 Y* C' C - u16 i;
" J0 N* Q |- I' Z- i' P% [- q- t. c/ z - W25QXX_Write_Enable(); //SET WEL % Y5 I+ D; G: @' m2 N1 ^$ `/ Z _
- W25QXX_CS_L(); //使能器件
4 M: J2 j; u, x* U) O - SPI2_ReadWriteByte(W25X_PageProgram); //发送写页命令
Y) c* l9 b }8 z' U4 h - SPI2_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址 $ ]( z. S- `4 h* x/ X& F6 c6 o) c
- SPI2_ReadWriteByte((u8)((WriteAddr)>>8));
: h0 U3 W; P- b+ `; Q3 S% V) K6 o - SPI2_ReadWriteByte((u8)WriteAddr);
; t& e; S2 ^3 I/ a' ?7 h3 [ - for(i=0;i<NumByteToWrite;i++)SPI2_ReadWriteByte(pBuffer);//循环写数 $ e) a# C& a Y9 z- ~0 T% I
- W25QXX_CS_H(); //取消片选 " C# `; F. z3 j, {& z# v
- W25QXX_Wait_Busy(); //等待写入结束
! P8 `# e* C; a! [7 L# G- h' B - } $ l8 P7 ^, @0 Z; \
- //无检验写SPI FLASH
- \ O- X7 u; @8 m% V$ Z0 i+ L9 Q - //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
& o$ j9 Z# l. r3 n& O) x. Y - //具有自动换页功能
; U. B+ X7 a! w! d" U! R" ~) B - //在指定地址开始写入指定长度的数据,但是要确保地址不越界!
+ Z* g8 K2 f* J' q - //pBuffer:数据存储区
$ t% C; c/ v1 K; ]9 | - //WriteAddr:开始写入的地址(24bit)
& k+ t# `2 Y' Z4 N5 B5 J7 @ - //NumByteToWrite:要写入的字节数(最大65535)
- u" N. A" s" N! w( ` - //CHECK OK
! o5 w: X; ^; X# w: I( U; T - void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
# ]7 }- s& i. a' R: l - { % e" r: q8 g; Q* _6 @ Z
- u16 pageremain; * O8 o6 A3 X- f
- pageremain=256-WriteAddr%256; //单页剩余的字节数 / U/ G7 D6 C7 m% a6 [
- if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节$ \+ U5 v" F, f, [* V7 W
- while(1)9 A( B. [) C2 X
- { 4 Z- O8 ^+ r* {5 m! c' ^/ k
- W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
: |% y, A+ c2 b! r( c - if(NumByteToWrite==pageremain)break;//写入结束了
' V8 W' `7 G" X5 y5 m/ Z( W5 w - else //NumByteToWrite>pageremain1 x, Q$ U0 f" H, M$ X
- {
. \$ G# w S2 H4 t* i1 F - pBuffer+=pageremain;. @7 N$ v+ D8 o( m; B! H
- WriteAddr+=pageremain;
; R3 [* w+ P: ]2 w* v2 b
6 @$ H$ T; s: `' z- NumByteToWrite-=pageremain; //减去已经写入了的字节数& [: J# ]% r; ?
- if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
; Z0 a/ U) `* A2 T% M/ T" p! \4 W - else pageremain=NumByteToWrite; //不够256个字节了/ l! \( K# j6 d& g
- }; r" @; P: H6 c I+ ~# s
- }; ( L" w, e. s: H T) P
- }
t! D; t+ x0 Z3 C0 m+ }, y - //写SPI FLASH
2 G- | A9 L7 G3 f/ k5 H% M( ~8 E - //在指定地址开始写入指定长度的数据
0 ? c @# z% I: x2 @ - //该函数带擦除操作!
7 x) M1 N8 z0 B5 Z: c6 L8 X* X - //pBuffer:数据存储区. I5 P, e" l7 [# b( t ]+ u
- //WriteAddr:开始写入的地址(24bit) 8 O) g% W4 o# l6 W
- //NumByteToWrite:要写入的字节数(最大65535) ! V1 K; t$ A, s+ r, B& ?( q
- u8 W25QXX_BUFFER[4096]; ; _; @' @ w: u" I
- void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite) 6 R/ y( D2 W2 R* [/ x; y* K: t3 A
- {
, m3 z# d5 `8 {6 S4 p - u32 secpos;
3 v! d- F6 c, g+ Y6 O; E - u16 secoff;& j3 {# _4 [. Y9 ~0 o$ v
- u16 secremain;
5 c: j+ B, _$ q% o - u16 i;
9 a* l' Y; X0 S - u8 *W25QXX_BUF;
4 q( V+ ]+ b3 Z* k" s -
, u% O' A, q4 w - W25QXX_BUF=W25QXX_BUFFER;
$ m% g5 u( `# r - //
& h: J# q+ n8 a7 Q+ c) z - // W25QXX_BUF = (u8 *)_mem.Alloc(4096);//申请一个扇区大小的内存0 r3 s9 a# c1 @; s: P
- // if(W25QXX_BUF==NULL) return;
! C6 C% z' J9 n - # C, `* A- g, p! d& ~9 M
- secpos=WriteAddr/4096;//扇区地址
1 ~# v7 }: ?/ t* } - secoff=WriteAddr%4096;//在扇区内的偏移2 |; T+ i! L: k0 X' o4 _+ [
- secremain=4096-secoff;//扇区剩余空间大小
, V0 A2 p+ k! U1 C - 5 M4 c% D9 G, i- {; g
- //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
. O# D3 R9 y# W1 i! P1 J - if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节% g! `* p6 _4 }3 s+ j: b
- while(1)
0 a! H# T% ~! L9 G/ e9 \ - { : E6 s5 }, m h% ^ {0 D
- W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容5 w7 H: z# p' H: w& ?9 T7 G# x0 o; m; z
- for(i=0;i<secremain;i++)//校验数据. C8 ?' c: X( ]; C8 P6 D/ `" a
- {
* Z h& s, @8 g/ K" G- W7 M - if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除
& b% t& e: F7 t! Y2 t( H - }, A$ V; S# D2 n3 x
- if(i<secremain)//需要擦除3 X5 Q% w' \ t& g3 o e8 y7 A
- { p% K3 L( q+ Q& Z: P5 i+ d9 a
- W25QXX_Erase_Sector(secpos); //擦除这个扇区5 q: a% P4 o/ G
- for(i=0;i<secremain;i++) //复制
. x6 d8 q- e7 [. Y( Q8 |6 F; H4 G - {. Q2 ? Y* n# ^4 R) N9 y, U
- W25QXX_BUF[i+secoff]=pBuffer;
$ |! x6 i3 F4 q - }
8 J7 l* o a+ z9 [! L - W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区 9 B* r! N. C7 W3 R
- 9 ]% }; @$ Z6 b z: B
- }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间. / u9 u4 m0 E) {. G
- if(NumByteToWrite==secremain)break;//写入结束了
! G$ Q% G' |# ~% ~- ~- \ - else//写入未结束; ]7 |* c" T& @' R3 r0 Q7 V
- {
/ w0 n O' L9 n/ M/ v6 m5 ]) s - secpos++;//扇区地址增1
! n7 C/ q' k0 F, O P - secoff=0;//偏移位置为0 + J: z0 i9 B7 d! n( R% K
- " ]; F7 Q$ K" `$ q% G
- pBuffer+=secremain; //指针偏移
# Y0 |! l/ Q, q- A3 r7 g - WriteAddr+=secremain; //写地址偏移 ( I' R+ b7 `% t ?8 X0 O
- NumByteToWrite-=secremain; //字节数递减
- R7 X) v. @7 ~5 B6 d' G: R - if(NumByteToWrite>4096)secremain=4096;//下一个扇区还是写不完2 k4 m7 I4 d. j3 k' X" S/ i
- else secremain=NumByteToWrite; //下一个扇区可以写完了
0 W c Y% z: S6 f0 r+ q - }
- ]1 b$ q3 c! Z: ]/ L - }
1 h- N9 F4 [( Z# a - // _mem.Free(W25QXX_BUF);
' S6 G/ n; h" q) m - }
5 ?' [/ i1 K7 y7 b - //擦除整个芯片 $ G2 d8 N* ~; k& P! C
- //等待时间超长...
; M. A7 ~# c/ p, Y+ B - void W25QXX_Erase_Chip(void) . h1 |5 j( g5 R7 m y) r: O
- {
- }5 Y! r. L8 q( n% k9 [1 T - W25QXX_Write_Enable(); //SET WEL 9 x# O0 ~; m/ W6 { I/ }" I
- W25QXX_Wait_Busy();
% `1 U/ k6 r: E% ?( P+ l - W25QXX_CS_L(); //使能器件
" Q. p7 l: g0 G) v5 d - SPI2_ReadWriteByte(W25X_ChipErase); //发送片擦除命令
; i4 \) `( T9 `, j0 |. d: C - W25QXX_CS_H(); //取消片选
4 n8 d1 ]6 E6 f( N2 o9 \ - W25QXX_Wait_Busy(); //等待芯片擦除结束
$ w5 l* T( Y* R5 ? O/ J/ a+ E - } + M" u! v. G% B/ ^! b9 F
- //擦除一个扇区
+ r% j# N. x1 V1 c5 J( A - //Dst_Addr:扇区地址 根据实际容量设置% m. t& F& N% h: h k
- //擦除一个山区的最少时间:150ms+ S, Y+ R' b% h* S# S: i2 c
- void W25QXX_Erase_Sector(u32 Dst_Addr) . M. ]2 e/ f) E1 V) ~* {+ G
- { $ J8 M. x0 ?/ t+ z9 |- o$ H
- //监视falsh擦除情况,测试用 6 |2 g' e- j- F9 Y0 y0 J
- // printf("fe:%x\r\n",Dst_Addr);
1 K+ L9 o, J4 [6 V/ h% Q- `9 S: |, X - Dst_Addr*=4096;
- J' b% F, ~6 x8 y/ N. h8 l1 E - W25QXX_Write_Enable(); //SET WEL
% b# F ~( x5 h - W25QXX_Wait_Busy(); & [' T& h" V, d* b2 {' ^% b
- W25QXX_CS_L(); //使能器件 ! b3 ^/ n0 H* e) u6 }1 E O
- SPI2_ReadWriteByte(W25X_SectorErase); //发送扇区擦除指令
1 [7 B [/ H% z* l4 ? - SPI2_ReadWriteByte((u8)((Dst_Addr)>>16)); //发送24bit地址 4 y( y! j- V5 s) z7 s" d: D* m
- SPI2_ReadWriteByte((u8)((Dst_Addr)>>8)); & |; `2 |" ^9 w* _$ ]: v; M( Z+ Y- S
- SPI2_ReadWriteByte((u8)Dst_Addr); 1 V% b: k! \5 ], ?; m9 w0 ?# N
- W25QXX_CS_H(); //取消片选 [' B* e. m, r* b* a( l) Q
- W25QXX_Wait_Busy(); //等待擦除完成) \. f& Q4 d8 L0 k6 Y* C; c9 Z
- }
4 g- N* w, ~) L6 b( u" r! A - //等待空闲+ L; s' G6 [7 q$ f0 k; [$ s
- void W25QXX_Wait_Busy(void) X" [# `, g, R0 R* d7 _1 k
- {
, r \3 |2 |7 z3 f - while((W25QXX_ReadSR()&0x01)==0x01); // 等待BUSY位清空4 _) ^/ \' K* B* v8 E
- }
6 R& N9 h# a" |- S! ~: M8 i - //进入掉电模式) V D/ K7 R& J* O
- void W25QXX_PowerDown(void)
5 K( l5 O; O/ \3 y - {
+ @9 d0 q9 E. N; e, _ - W25QXX_CS_L(); //使能器件 - b7 m- E9 M' }
- SPI2_ReadWriteByte(W25X_PowerDown); //发送掉电命令
7 M1 x# C7 c) k - W25QXX_CS_H(); //取消片选 . f0 ^/ v) t' |% F' l' P& B4 h
- Delay_us(3); //等待TPD 4 j2 Z. Q7 m% f& R; @$ q! G
- } 4 U/ b) B. C: Y2 d V# W! w o T
- //唤醒
0 |1 W. r' }3 ~3 X - void W25QXX_WAKEUP(void)
, v! `) {; n0 |- X7 } - { - b- E" U: s& m0 t7 m
- W25QXX_CS_L(); //使能器件 $ D4 i! j: b( W- I" {
- SPI2_ReadWriteByte(W25X_ReleasePowerDown); // send W25X_PowerDown command 0xAB 2 |( q. P/ T* S0 z$ E) C5 _4 ]
- W25QXX_CS_H(); //取消片选
; p' @- n f" P- ~/ Z* O6 j - Delay_us(3); //等待TRES1
. f$ W+ l/ J# r# h) M - }
复制代码
. W1 H. d! `( p6 tH文件
2 ?% t, C: ?0 J5 f# ]* g5 y
5 H4 _4 P% `0 u5 k- #ifndef __FY_W25QXX_H# ]' ~5 }1 \0 b' S2 B
- #define __FY_W25QXX_H
, M8 p' o: `; G3 a- ] - ; w: P; t9 v; [/ M0 ~- [
- #include "fy_includes.h" : e* S- @, {2 i3 w; p, g( a
0 j' D, A6 f5 O" |- o; t G# P- //W25QXX对应唯一识别ID
+ W1 w: s3 c5 F - #define W25Q80 0XEF13( G' k F+ D$ m4 N, w
- #define W25Q16 0XEF141 ^/ X9 I+ j- }5 c, ~: x. J
- #define W25Q32 0XEF15
W1 s% [8 s& Q$ ^2 Y5 ?- m - #define W25Q64 0XEF162 m+ C6 b7 _/ v; ~+ C
- #define W25Q128 0XEF176 A/ E+ i; m# g/ l( o& K! `
- . F/ ~0 U$ T9 Z- J9 M9 `$ N5 y! O
- //指令表
' ^ S' x8 m t7 r+ c2 n' ` - #define W25X_WriteEnable 0x06
$ Z/ U6 v4 Q% [ - #define W25X_WriteDisable 0x04
k$ _0 {! j3 ~( z6 c - #define W25X_ReadStatusReg 0x05 ( J9 ` ^! v. X9 P0 Q
- #define W25X_WriteStatusReg 0x01
4 v& D2 ]- W# j0 X+ H7 s - #define W25X_ReadData 0x03
9 _- T# b* {, p- m( J) Q3 R - #define W25X_FastReadData 0x0B
! l# A: c2 ~! Q- L& e C7 i - #define W25X_FastReadDual 0x3B
8 R6 w( B, N8 U4 g# q# v - #define W25X_PageProgram 0x02 0 e* I' ]: l0 a0 I! q% S
- #define W25X_BlockErase 0xD8
- o* H2 h; L! H( `; d - #define W25X_SectorErase 0x20
# d. R8 \6 d: l6 q1 a - #define W25X_ChipErase 0xC7 ) c" m- O3 O; @+ W+ U
- #define W25X_PowerDown 0xB9
7 F$ ~3 G2 n: k - #define W25X_ReleasePowerDown 0xAB 3 i. d# a+ Z" m/ A* J F4 U5 }
- #define W25X_DeviceID 0xAB
2 d$ P* d. p+ @& v - #define W25X_ManufactDeviceID 0x90
6 }3 J& c; P& @1 L6 l" B. W* _ - #define W25X_JedecDeviceID 0x9F + ]: C& l; y7 E& o6 f" y$ }. q+ V
- 9 Y- c% d7 y+ j6 `; L9 K0 V
- void W25QXX_Configuration(void);# F) [1 c* K+ v1 p! _
- u16 W25QXX_ReadID(void); //读取FLASH ID% g/ D' ]! s9 V% S% S( @: n
- u8 W25QXX_ReadSR(void); //读取状态寄存器
9 p$ w+ U' s7 `( H5 y - void W25QXX_Write_SR(u8 sr); //写状态寄存器% c4 R$ e: e/ w' h
- void W25QXX_Write_Enable(void); //写使能 " r9 C, _. p: h8 d0 q# W% k8 i
- void W25QXX_Write_Disable(void); //写保护
+ r4 \' u6 z% S4 W/ ~& T - void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);
; h" y5 _2 K4 n4 f( k7 F$ d - void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead); //读取flash
: l5 ?2 {; i- S) _2 a5 m - void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写入flash) Z! f! N' ]7 t) b2 _% K C
- void W25QXX_Erase_Chip(void); //整片擦除, h! T$ z/ k v- c+ R
- void W25QXX_Erase_Sector(u32 Dst_Addr); //扇区擦除
4 O, X( ?; ]. d e* U; i8 T - void W25QXX_Wait_Busy(void); //等待空闲2 C. o7 W4 b, _/ _8 P
- void W25QXX_PowerDown(void); //进入掉电模式
* I0 x9 h4 B+ }1 A7 } - void W25QXX_WAKEUP(void); //唤醒4 V3 v8 ~- {# M! ]2 v
- % v1 s' ^1 k0 A$ ~! M* d
- extern u16 W25QXX_TYPE; //定义W25QXX芯片型号
3 a) F. O% W2 n- j3 K - ' [, \- ~- D' m: i5 x+ p6 `) Z
- #endif
复制代码 $ Q! w: v# e8 D1 x# f( X5 z
基础工程如下:
& s( N' f+ Y; x% e* M# [3 g% `, V
, U/ ]* |! ^- r6 ~
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 `
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
' 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. [, \
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
; }, 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
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
# 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
- 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
( `" 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
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
! 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 ]
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
) 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
- 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
, _" 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
: 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
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
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 ]
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- /*******************************************************************************
* Q% u: g# p4 _( {% p - * Function Name : USB_Interrupts_Config
; |- n& }) |' R7 ~ - * Description : Configures the USB interrupts X! g$ H9 E! L, R
- * Input : None.$ K% D! `$ t2 h, Y
- * Return : None.
& I" E v3 u% q- ?6 x9 A - *******************************************************************************/% L! |" B& h+ @$ [
- void USB_Interrupts_Config(void). E, ^/ s5 V8 ^7 M& U
- {
m! S( ~/ s* G* h K* S -
( \: ~9 o; @' u0 p% J3 n# X - NVIC_InitTypeDef NVIC_InitStructure;5 ^$ o: K4 R" J( x" R7 B
- EXTI_InitTypeDef EXTI_InitStructure;
, e# S1 G% y" Q- t7 i* R - 9 c. t0 K, D( ?
- 5 c; N+ h6 n, K0 \3 {1 x) a( P
- /* Configure the EXTI line 18 connected internally to the USB IP */6 W% A/ z" L) V* w+ ^0 w! H
- EXTI_ClearITPendingBit(EXTI_Line18);
( k" Q0 r9 c9 @, l0 R9 t& F - // 开启线18上的中断9 b) R" A9 [6 Z, f( ~& Y6 }) q! c7 @
- EXTI_InitStructure.EXTI_Line = EXTI_Line18; // USB resume from suspend mode
3 w$ q. w% A6 M5 g - EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //line 18上事件上升降沿触发" }+ x ?" k# C( ^
- EXTI_InitStructure.EXTI_LineCmd = ENABLE;
, K2 e0 X a9 v& j! F a$ ?/ Y - EXTI_Init(&EXTI_InitStructure);
2 r8 X$ X* d, V- n S' Q - + X( I) A3 s$ c! z% |
- /* Enable the USB interrupt */
- V# N7 [$ k/ Y" ]- O) E; u' C - NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn; //组2,优先级次之 1 n( O" Z) m6 s# ]5 _2 M+ e* Q
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;$ B. `- L1 s+ Y# E( d( d2 `
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;9 r* B2 z: R! n6 C0 y, m
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
' A* N; s; z4 d8 E - NVIC_Init(&NVIC_InitStructure);
1 |# q+ n2 l8 r2 W( { - ! q; c! K3 E5 w, r# X, Z7 x
- /* Enable the USB Wake-up interrupt */9 A8 `; \# y+ N- h W
- NVIC_InitStructure.NVIC_IRQChannel = USBWakeUp_IRQn; //组2,优先级最高
6 M5 i" I. Q5 a5 c6 h6 Z" L - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
) p' I' l" D0 N$ i2 Z2 [ - NVIC_Init(&NVIC_InitStructure);
0 V" | E+ n% ?+ O! d3 v$ p1 C - - }5 M, e: e/ G: b7 E9 s
- ( ^0 s4 D+ L0 ~/ Y3 N, V$ \
- }
复制代码
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
- //USB唤醒中断服务函数$ B$ F4 o& x t/ b1 l, ] i
- void USBWakeUp_IRQHandler(void) ' W2 f. `: `7 c! i9 @. |3 u0 ]" Z
- {
* V' W q* L0 s1 \$ _$ @ - EXTI_ClearITPendingBit(EXTI_Line18);//清除USB唤醒中断挂起位( x, @" W r1 h* T- R" U0 a
- }
复制代码
+ d3 e* U+ T& y1 b修改pwr的Suspend内容:
+ [/ I1 V( }" j8 \% {
2 ]% V( a l8 \- void Suspend(void)
2 L1 E3 {# D: t9 y$ w7 h - {
$ R, e5 C# r+ C% ~6 X - uint32_t i =0;5 n, P, R/ M; P; s7 ~
- uint16_t wCNTR;
0 X2 M' U) b0 W - __IO uint32_t savePWR_CR=0;
, M$ x& U& k$ b8 w9 p! _* r - /* suspend preparation */% x7 D8 D' w9 M
- /* ... */8 c9 v8 I/ d- U' L1 h
-
( m2 a) o( ^5 l; u% E; z, x - /*Store CNTR value */8 O9 P9 y- c# b& M) w2 a$ p
- wCNTR = _GetCNTR();
* H" l6 X9 A7 `3 u6 Z
: c% h5 M+ J7 k& K, ~$ Z. O- /* This a sequence to apply a force RESET to handle a robustness case */
; c) C O4 b; W - 0 j7 h/ F+ ^8 B& z5 J
- /*Store endpoints registers status */
2 A t$ I* [ w3 \# _ - for (i=0;i<8;i++) EP = _GetENDPOINT(i);" ^" ^% O' N7 @! [# F1 Y
- $ a$ r y( s- u4 s9 F, Q
- /* unmask RESET flag *// J5 A/ X) v& L4 Q2 f
- wCNTR|=CNTR_RESETM;
& |9 O. k; g W6 ]7 [ - _SetCNTR(wCNTR);3 n& m& b) d) }) P9 M# |
-
, j8 f" l) F% b; h - /*apply FRES */! Y, R4 T7 f$ {+ n3 N
- wCNTR|=CNTR_FRES;8 o( k/ \ {3 S% a
- _SetCNTR(wCNTR);
5 K o. }% g5 `. @2 } - . M- P- d J4 [! m! b
- /*clear FRES*/% o5 _6 k! m' j$ W9 N W
- wCNTR&=~CNTR_FRES;$ a& ~& J' j2 {, L v4 M" Q
- _SetCNTR(wCNTR);
3 [0 @4 N( x! A1 N, ? - * C1 z" b2 N% A1 r( j; G
- /*poll for RESET flag in ISTR*/( l. F7 M$ ?7 |( m% _+ [* v' i
- while((_GetISTR()&ISTR_RESET) == 0);: y8 V2 j" [! Y2 F) z
-
8 ^$ H9 ~" V: T1 j% d - /* clear RESET flag in ISTR */& n. r9 a2 u f; S; B6 Y, U8 l* h
- _SetISTR((uint16_t)CLR_RESET);
: a4 \( o/ [4 e- ? -
3 \ n3 _& y* x% N9 W6 Q - /*restore Enpoints*/
8 |+ v$ d! }/ s) W4 C# D - for (i=0;i<8;i++)
% D. I4 W+ c. [6 |0 ~" f - _SetENDPOINT(i, EP); G+ D# L5 {4 ?
-
5 [6 [$ S% p- k: d - /* Now it is safe to enter macrocell in suspend mode */
- a; h0 U' h! T' H. D2 ] - wCNTR |= CNTR_FSUSP;
' [$ y, f. ~9 ~% T - _SetCNTR(wCNTR);2 y. i6 s* z4 B' G9 s4 A$ L/ M1 K
- ) _0 ~5 m$ c) |6 t' u" b
- /* force low-power mode in the macrocell */4 ]$ J' t0 b: `9 F, U/ z- j5 Y
- wCNTR = _GetCNTR();
& f) b) `6 \' Z8 X) r+ q$ w - wCNTR |= CNTR_LPMODE;
) Y" _3 {8 \9 S1 Q - _SetCNTR(wCNTR);
2 M) r. w$ c4 J# @6 c: U6 D -
) T$ W/ s j' U5 ^8 }# b - Enter_LowPowerMode();
" R2 d4 e& q% L2 f K - }
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- void USB_MSC_Configuration(void)* x& K; {# e. C4 u# Y+ h1 y
- {7 H( }1 R$ C; a6 h
- // Set_System();. j: ]/ X9 ~3 }# |; `
- Set_USBClock();
# L* @* B5 s9 n. g' t2 {+ r - // Led_Config(); o: u* S, r: B$ O5 T7 F
- USB_Interrupts_Config();
, u4 i# M f4 ?6 S - USB_Init();
0 Z& ~/ d: U# j. | - // while (bDeviceState != CONFIGURED);
% X. H* l, {. ~ - 1 a& `6 ?; v( h+ X" ]. ~
- // USB_Configured_LED();
2 | t- ?8 d8 m6 Y* W: z - : y: a4 ^+ A8 S2 z
- // while (1), z+ n: S) Y+ E5 a+ J) t% M
- // {}
0 e: r* c/ v$ ~* n - }
复制代码 ( N6 z4 X6 R# e
最后在USB初始化之前给U盘定义的数组赋值,不然就只有个盘符$ ]9 J; |" w1 U0 \
/ A- e# q5 h. _. v, ?8 i- S- Mass_Memory_Size[0]=1024*1024*4; //w25q64->8M 给4M做U盘
6 O3 l: B$ J& z8 y+ E+ S+ Z - Mass_Block_Size[0] =512; //设置SPI FLASH的操作扇区大小为512( H6 S1 [, t$ u8 g
- Mass_Block_Count[0]=Mass_Memory_Size[0]/Mass_Block_Size[0];( L3 B% q3 l- d6 [( }( i6 s: ]( B! Q
T; e& H- r6 X+ c' U$ q- USB_MSC_Configuration();
复制代码
! Z5 t! s. ~2 E4 x. C$ @
& 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
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
|