硬件环境+ A. |* n5 d* N
STM32(支持USB)
( C( R) r0 X- ?4 ~. f, t# V存储介质(SPI FLASH、E2P、甚至是片内FLASH均可),本实验使用外部SPI flash(W25Q64)。
, P) C1 N# M$ V3 M0 @/ H使用STM32CubeMX进行配置初始化信息- N$ s7 ]' F; v. ]
1.配置MCU的时钟,外设等信息,可以使用一路串口进行输出Log信息,串口波特率越高越好/ {9 I4 z1 g$ r% H V
2.MCU管脚配置% o" F+ _+ m! B9 {6 e9 o) t
- P8 I4 u6 ?$ I' v
$ J- h: p- L$ G/ V" C3 C% c
/ [% G3 v7 Z# j% t& \4. 时钟配置4 S+ x" P8 ]; H
我这里的硬件中有8M的HSE,然后需要注意的是,USB时钟需要配置到48M
% L) b# } ~: a8 s9 ~9 u% t. _% E# a8 C, j
+ W1 Z: J% ?5 B3 r+ Y: b3 |: [
- N1 z0 w/ `- K( h5 @: y' p
3.USB配置,记得打开中断
% P0 [: b) p/ a& e+ O- Y$ b; g/ F' |! O% X {1 W
% z- q8 R. F0 V& K+ x4 m' L, i
8 M+ M+ q+ n" M6 c9 l+ G. \4.串口
% T! e C. r- c/ U/ Y1 e3 v6 X Q6 d ^' J# K; z
- \ [" O8 K( B E7 K8 G
" \: T5 L8 Q3 t, s2 Y2 q- t: q
5.存储介质的通信接口,如使用内部flash,则不需要配置0 |2 C( T$ A' G! q, _+ v7 {
W25Q64 是SPI FLASH,配置SPI接口,这个配置可能需要根据芯片手册进行调整
. _3 X- l; e: y' `8 q# V+ O% x7 h% A" w/ k* t
& ~" C& n7 ?/ R Z2 C3 i9 _2 \' V
7 R/ ~/ Q$ Z" }& o9 s r" D" y# z6.中间件 USB_DEVICE( M- O% W. N1 x" _/ K' \% ]) R
Class for FS IP选择大容量存储,其他可以默认,USB_DEBUG_LEVEL改不改都一样,做USB device时没看到打印出log来。如果配置了非0,那么需要重定向printf,并且勾选USE Micro LIB,否则可能会出现问题。
: `( ^6 \0 R: A
4 ^3 l2 q' Q& S' i# Q$ g& p6 A" t
6 ~$ l6 \) p. [, P/ K7 H
8 p6 K6 ` @! U) g- z+ v7.将堆栈空间改大之后就可以生成代码了6 S2 a: V- z3 R I$ P
7 ^* g2 f# r& @$ G
修改程序
- J# }- q0 @$ e8 n) q生成完程序后,直接编译烧录,然后将USB连接到电脑上,就可以看到已经多出来一个盘符,但是看不到容量和大小,也不能成功格式化磁盘,那是因为我们还没有完成数据的读取和写入2 I) X; @& Z6 L9 { C4 r
- O+ P8 i; q( G; f; ^
修改添加存储介质的驱动代码
! x# T5 s" G' ?8 [# \/ \这个步骤很关键,如果你的驱动有问题,那么就会导致格式化失败,也就没办法使用。9 O1 B0 H. ]8 O/ t; E% J8 n0 d6 G# g
& ~. u4 K. p1 k# u4 h
W25Q64
# C& n* [/ F& O$ T+ G4 `7 ~必要的函数& N" R$ W7 q7 m" V$ J2 P9 l% S
W25Q64.h. ^7 H$ J0 e; ~- p% I
T- h; ^7 M& q+ G; {' s `
- typedef struct
8 s3 U8 F1 m2 w5 ^1 Z - {
- u1 n/ m9 M. ~ - void (*init) (void);/ C2 D, q, ^) x+ q
- void (*read_sector) (uint32_t addr, uint8_t *pData, uint32_t length);, r9 i$ d5 J- u
- void (*write_sector) (uint32_t addr, uint8_t *pData, uint32_t length);
2 z* N" R- z0 ^% z* x! M - uint8_t (*get_status) (void);! M& a4 _0 m s) }
- }W25Q64_Dev_T;1 K5 N. m$ I/ s3 L: ]4 D
- extern W25Q64_Dev_T w25q64_drv;
复制代码
; ?* N0 ~' j3 g5 B; c# ~- eW25Q64.c0 u1 y% E3 ] a% z. C* d r
0 ^; ^; a( l( s- F. ?
- static void w25q64_write_enable(void);
! {. }) i) X( m! w9 j - static void w25q64_write_disable(void);
' q( O: B- c/ \: j1 j/ U/ Q& C% ? - static void w25q64_chip_init(void);
& x! s) R; L. J9 q( i, F9 x - static void w25q64_page_write(uint32_t addr, uint8_t *pData, uint32_t length); //256byte max
2 ]1 `8 c: W! i( s( l - static void w25q64_sector_write(uint32_t addr, uint8_t *pData, uint32_t length);//4kbyte max
4 r k/ { ~9 B0 H - static void w25q64_sector_read(uint32_t addr, uint8_t *pData, uint32_t length);/ a% Q% l" K2 }6 u) @
- static void w25q64_erase_sector(uint32_t addr);& N. y8 w& S+ ~, s% ^2 Q$ r
- static uint8_t w25q64_read_satatus(void);; d+ o0 V) z; v2 B h# e- d
4 n% D8 k8 w3 I: A) Q- static uint8_t data_padding = 0xff;# |+ u+ E8 R8 _
- , b5 B. l! @! Q2 z3 P2 z
- W25Q64_Dev_T w25q64_drv = + F% q2 t- P! o4 `. q7 I7 C
- {
, y7 X' w6 B4 }& a, u - w25q64_chip_init,
) b9 z: |3 t6 [4 U# u# ]# U. R& {: p - w25q64_sector_read,% Q- F. P9 _: w# J8 v2 u* |
- w25q64_sector_write,9 h+ X$ B9 O* L7 W( L* E3 N& V+ _
- w25q64_read_satatus,& d& B) c8 o2 E l1 C" k
- };
复制代码
: o! t; i& s8 p. h2 ] I数据读写函数添加到USB驱动中6 {. d9 e* p1 }5 k: ]! M
我们要修改的文件是 usbd_storage_if.c, g+ {, k* K- D2 ?( G0 C
2 D3 I9 y# j7 C K; _) D9 B- #define STORAGE_LUN_NBR 16 a+ U( `# {9 J; h, @
- #define STORAGE_BLK_NBR W25Q64_SECTOR_NBR//改为flash介质的sector 数量
. `* f/ Q: s' M1 a% l; ^# N - #define STORAGE_BLK_SIZ W25Q64_SECTOR_SIZE//改为flash介质的sector 大小
复制代码 1 R7 }+ Y3 C. u: i
int8_t STORAGE_Init_FS(uint8_t lun)
. U8 T' w0 U0 d/ v# h
, f% X, F, U- }" c/ e$ R2 A7 _2 @我们可以添加我们刚刚写的存储介质初始化,如果正常,则返回 USBD_OK
9 ^* q; ]" p) L6 t! ^* d$ U" K% J$ M7 D3 \4 X- d
int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)5 E; F4 w2 B/ m9 Q" h c; u
3 z; O/ p! O8 ~! f
获取存储介质的大小,函数已经填充好,我们可以不动; @* I- W. z" c8 ^# t$ l
/ a5 g6 ~3 N' a" v0 {int8_t STORAGE_IsReady_FS(uint8_t lun)
7 F! I3 S1 m7 N' ?, l
3 k: X$ ^) m- u3 ?' m获取介质状态,我们要对存储介质的状态进行判断,这里我们要判断两点,一个是是否正在读写状态中,另外一个就是存储介质是否是不可工作状态 L+ X- G4 b& u2 m/ \. K
( L% {. L* J: _7 ?+ [
int8_t STORAGE_IsWriteProtected_FS(uint8_t lun)
$ b! ?4 B* W; j9 q5 t; k
" t: A) f7 K; V \+ t9 y判断是否是写保护,我们可以直接返回USBD_OK, M1 _* [2 U C0 b
& ~! v7 T, s% w: R: v+ @# B
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
0 ?& i$ E& M$ R1 u" J) P |3 Z( X* Y' }
读取一个扇区,我们将准备好的读取一个扇区的代码填充进来就好
7 }* K8 r% C% r/ ]& S$ A# j1 M# o* J& G" ]
例上述W25Q64的驱动代码:2 W% w+ ?- W' F5 _" Q
+ ]* F/ t$ k2 g; L& v- /**0 U; h4 b0 n' x Y n
- * @brief .* D- r& |4 f N' C
- * @param lun: .* A0 {4 u4 d+ L7 `4 v' |
- * @retval USBD_OK if all operations are OK else USBD_FAIL6 G/ b) i) H# H6 W0 S1 ]
- */
+ R. i/ b: g% a3 ?; u; c - int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
4 @) W$ U: f2 e7 Q2 K d - { K- j& F1 s4 w; O" J3 Z
- /* USER CODE BEGIN 6 */& P8 J1 h8 g3 V- }( \- f9 A
- while(STORAGE_IsReady_FS(1) != 0);; V4 H1 K! q( \3 K7 O
- storage_status = 1;
* f6 l! K# z* H0 J4 t; [' A9 @ - w25q64_drv.read_sector(blk_addr*W25Q64_SECTOR_SIZE, buf, blk_len*W25Q64_SECTOR_SIZE);
0 A4 Q! M# ]6 ]7 }1 ?! d- A - storage_status = 0;
! M; W5 I3 K# o+ y$ p3 a - return (USBD_OK);1 }0 T" \. l- i
- /* USER CODE END 6 */+ o/ `' F5 b' L' J. h
- }
复制代码 ) y" f7 R3 o- g# v9 s( c8 v7 |
int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
3 G6 \* N4 c7 [9 c& k
; Q: L7 L* k3 z. T) A2 Q写入一个扇区,我们将准备好的读取一个扇区的代码填充进来就好; f* S1 y0 O: E
+ Y4 P) i/ h9 P5 `- v
例上述W25Q64的驱动代码:- ~7 @( b; o( r5 ~
6 K- Y) m3 Y; Z6 |8 t8 Z
- /**- u- D7 n% p3 n1 @! k
- * @brief .
# U9 q$ t% a( b ~4 G+ H8 r( c% t - * @param lun: .% d, k7 N- e! G( l- @6 I
- * @retval USBD_OK if all operations are OK else USBD_FAIL
) ?7 `* G- j4 g8 s" y - */
& g3 z" g5 b: z" T; q - int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
" G0 f. i) o. y6 o5 D% Y& l2 | - {
6 ?- D5 X& d. j0 o) s - /* USER CODE BEGIN 7 */
8 x" N2 g. ~. T; }& B/ Z - while(STORAGE_IsReady_FS(1) != 0);- }* S: c5 {% l. O/ @, F
- storage_status = 1;
- j: E& ?3 f$ t0 v" Y - w25q64_drv.write_sector(blk_addr*W25Q64_SECTOR_SIZE, buf, blk_len*W25Q64_SECTOR_SIZE);# k) B0 H( h# i
- storage_status = 0;
& N7 _2 @5 Q' ^; w& }! I3 i! m - ' U" N/ n: x* [6 N4 F; r
- return (USBD_OK);
% Z8 Z% f9 x+ _. T8 u( w - /* USER CODE END 7 */ V' L% w+ Q; t" \$ o, @
- }
复制代码
1 S9 R7 D/ Q% T" `& ~int8_t STORAGE_GetMaxLun_FS(void)" g/ m( O1 t% h9 M: q% P
6 q) D! ^- ?4 O1 T0 B! m8 D: @4 S读取磁盘介质的个数,一般我们只虚拟出一个来,不用管就行
. g- s! C v6 i& o, F& P
, q% _1 p# G- I0 L4 k" A1 Z我们使用的spi flash的sector大小为4k,也就是4096,USB这边是520,所以我们需要将其修改一致
# ~( k# h8 {9 ~, W6 M# y
! m% F3 e' d* m% ]- `! u修改usbd_msc.h6 ~, u5 r7 X' V: N
2 E' o5 J" E( O! b$ X/ S4 N- /** @defgroup USBD_BOT_Exported_Defines3 f" y) G& g" I8 r) M! h0 [
- * @{: J# u% t2 q- q" U" D3 B& G
- */5 B# ^+ N9 {! r/ A7 K
- /* MSC Class Config */
$ p! a$ H% E5 R - #ifndef MSC_MEDIA_PACKET- ^8 l/ `! p6 K3 s
- #define MSC_MEDIA_PACKET 4096U //512U! O+ B. w* ?9 \# E
- #endif /* MSC_MEDIA_PACKET */# i: K5 U/ k& q2 x/ P1 I& o
- - a& g. I; }3 B- w/ i4 z
- #define MSC_MAX_FS_PACKET 0x40U //0x40
2 I% _3 | C; _0 Z) v - #define MSC_MAX_HS_PACKET 0x1000U //0x2000 S: T: Q2 P5 C' l$ V
. i( j) N. c* L- #define BOT_GET_MAX_LUN 0xFE
1 G5 W2 U% y3 d8 }3 x - #define BOT_RESET 0xFF
- m9 N4 Y% I+ h: U* q. q( a: W7 p - #define USB_MSC_CONFIG_DESC_SIZ 32. l* U9 ~, D+ z5 r0 T, O
- - Y% ?- J. x; [/ c
- #define MSC_EPIN_ADDR 0x81U1 Y/ ?( Q/ N7 d5 p/ U
- #define MSC_EPOUT_ADDR 0x01U
复制代码
, w8 c) ]( ~; }! X# L- m3 B5 ]修改usbd_conf.h5 m& s! V. P% \! @$ C2 S6 h( a0 x
! s, _# O. v' {
- /** @defgroup USBD_CONF_Exported_Defines USBD_CONF_Exported_Defines
9 L, G: Y- L7 Q. Q) x* u - * @brief Defines for configuration of the Usb device.. X6 \/ }5 k3 Q) }+ Q5 q
- * @{
$ M0 m& `$ w6 |' x - */
) V, |$ {. A' V. Q- O
; d7 k7 x [" `" Z2 A/ f/ Y* {0 x- /*---------- -----------*/5 a# L5 x. e) s8 Z
- #define USBD_MAX_NUM_INTERFACES 1
- V+ q, d0 E( J S- w - /*---------- -----------*/
/ o1 {$ n$ Q- t( |$ Y/ ` - #define USBD_MAX_NUM_CONFIGURATION 12 z$ f) f0 V8 ^) O. J% z
- /*---------- -----------*/
( E M `& w. M, K - #define USBD_MAX_STR_DESC_SIZ 4096 //512
1 y. F! K' V- f4 K, H - /*---------- -----------*/
3 Y% @8 j& x& z - #define USBD_DEBUG_LEVEL 3
: ]/ L! \3 R- r2 x6 W - /*---------- -----------*/+ n# {* U& l: d* ^
- #define USBD_SELF_POWERED 1* w% X# h* w2 |3 u* p7 L4 z
- /*---------- -----------*/
- w" V, B5 }( G* K+ j - #define MSC_MEDIA_PACKET 4096 //512
复制代码
- J5 r7 s* m! Z: c/ H2 z至此,我们的U盘就做好了
+ h( I5 ]' Z% C3 i* c. V3 |. p————————————————
; W7 @- K8 Q2 D$ A版权声明:Logan Li7 S! ]; `4 [% P8 l; d% A
9 G* H0 i( T5 n
7 W0 [' Q( Q1 X [% D+ H |