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