硬件环境* \; l; ^* \' l/ Q" Q
STM32(支持USB)
$ Z+ I+ v' F5 q, h0 }$ d5 _存储介质(SPI FLASH、E2P、甚至是片内FLASH均可),本实验使用外部SPI flash(W25Q64)。
4 S* c4 D: A' T& w8 f3 G. h使用STM32CubeMX进行配置初始化信息
- @/ u0 u6 v" i3 x1.配置MCU的时钟,外设等信息,可以使用一路串口进行输出Log信息,串口波特率越高越好
3 |0 ^2 x0 y/ \6 J# \: z. L4 A! t5 {2.MCU管脚配置5 d7 X. _9 k0 s4 v% }3 G$ E$ p9 u! S
4 }4 ~& D' B' {
" K3 w, T* H# U) A8 P" _
# S% x) a/ S, I1 g! V! ^: t4. 时钟配置( a; n% N+ B# ^& g6 ]7 z" I
我这里的硬件中有8M的HSE,然后需要注意的是,USB时钟需要配置到48M
) F0 F4 M! I3 b& T2 K; G6 K: ?8 V) b2 G9 J
1 A7 d/ @8 ?7 [% i6 Z
; L2 ~; C- }" p( W3.USB配置,记得打开中断
) B/ P2 @. h& s" O( h( o; Q9 L+ ~: z7 X
. P* {' |; i# z3 B" @2 q
6 R* _0 w& W3 x* q9 {4.串口
" u* B2 J- v( h% U9 r) q: h
R. L' M: K! G" S2 ~. W7 U
+ f( I% ~" ]* V" e5 y
9 r4 W n9 s2 y
5.存储介质的通信接口,如使用内部flash,则不需要配置
0 y: Y& a& n* U6 x8 [ ~W25Q64 是SPI FLASH,配置SPI接口,这个配置可能需要根据芯片手册进行调整
. T$ }. c) A. I
+ q3 S0 A; T/ [8 t3 P
- f: M$ c# t9 p; m( i n* e! e% h; U# U& x& ?0 D
6.中间件 USB_DEVICE% t0 `; z3 W' d1 _& H/ P
Class for FS IP选择大容量存储,其他可以默认,USB_DEBUG_LEVEL改不改都一样,做USB device时没看到打印出log来。如果配置了非0,那么需要重定向printf,并且勾选USE Micro LIB,否则可能会出现问题。( ~$ d' c# \& }, N2 S/ O' Z1 U
4 ^+ I8 ?: N$ Z6 Z9 Y
% w; I% o* S! b7 o" }2 L9 u, x2 {
7.将堆栈空间改大之后就可以生成代码了
$ t" S8 O* p2 D: F5 n; a
0 e4 O7 l$ o0 Z: K修改程序) b- Z3 o- J) e6 W: K) X: o+ Z8 q
生成完程序后,直接编译烧录,然后将USB连接到电脑上,就可以看到已经多出来一个盘符,但是看不到容量和大小,也不能成功格式化磁盘,那是因为我们还没有完成数据的读取和写入
6 u/ J) o' u; n2 e
) l3 `" I8 v- G% j: X, a# M修改添加存储介质的驱动代码
7 m" S4 W4 Z. } o这个步骤很关键,如果你的驱动有问题,那么就会导致格式化失败,也就没办法使用。3 \$ ~- \* T; R; W" C
5 Q! O% Y0 y8 f- ZW25Q644 `7 T" m ~+ J6 R* ?# A& K
必要的函数, V% T& ?9 J4 J$ W# ^$ Y
W25Q64.h
8 Q* Q1 |6 G! r/ z0 p# [/ Z
, j3 |( X9 [- H: N1 D- typedef struct
4 F1 I2 Y! U2 g9 z8 b# M - {! R- g: m1 X6 l6 f& u
- void (*init) (void);
) G! v5 r5 X- t - void (*read_sector) (uint32_t addr, uint8_t *pData, uint32_t length);. X& Z# f( m' p9 R0 ^
- void (*write_sector) (uint32_t addr, uint8_t *pData, uint32_t length);8 O; Q1 P% A9 H% a8 N$ I
- uint8_t (*get_status) (void);" ^, m7 c9 S) B% t ^$ L
- }W25Q64_Dev_T;
) f; H) ?- }$ @! k& z1 l! R' k0 e - extern W25Q64_Dev_T w25q64_drv;
复制代码
$ w4 A# F3 Z" Q1 oW25Q64.c- p; m( f6 v6 l4 g3 D
k1 }- d3 ], ?! q
- static void w25q64_write_enable(void);
3 V; E5 V& n: d! w) Q - static void w25q64_write_disable(void);. s, h- V" C1 u( D
- static void w25q64_chip_init(void);* ^6 ]- j/ ~: |* q: F
- static void w25q64_page_write(uint32_t addr, uint8_t *pData, uint32_t length); //256byte max
. J9 f4 C0 `, s) C/ V5 t) C. v( X - static void w25q64_sector_write(uint32_t addr, uint8_t *pData, uint32_t length);//4kbyte max
7 ]2 G& {! {$ I - static void w25q64_sector_read(uint32_t addr, uint8_t *pData, uint32_t length);4 x3 [. _4 F( u# q h
- static void w25q64_erase_sector(uint32_t addr);; |: Z9 p; E! @5 }. ]4 A
- static uint8_t w25q64_read_satatus(void);
8 @) O( q: S' I' X( q' |8 C - {+ e2 q: ?, w# g9 ^1 l% _
- static uint8_t data_padding = 0xff;
8 h, o$ A" {* p/ S& E, i
/ r( e+ Z9 V- A+ I, n- Z- W25Q64_Dev_T w25q64_drv =
4 x0 N& E" X+ |$ @, o1 _4 b' ^ - {
' d/ o1 q* B- R - w25q64_chip_init,
9 v/ C' f( }) m! [. L, ] z, ^9 u - w25q64_sector_read,
) J" |8 b7 N- W5 x2 I - w25q64_sector_write,
# x+ V8 q& \0 `- v& a' I - w25q64_read_satatus,
! N$ }! {2 ]+ B9 g$ ] - };
复制代码
/ ^7 b% p: ~8 X/ n/ ]数据读写函数添加到USB驱动中
# f7 n$ P' r- S$ a9 M6 H# b我们要修改的文件是 usbd_storage_if.c
- w8 q% [& A% L5 O. u* Y% I
# t# W, E3 L, [' x% s1 f4 |; t$ ^- #define STORAGE_LUN_NBR 1- p% B% b' O# r" {3 i" F; B! Q3 |5 A
- #define STORAGE_BLK_NBR W25Q64_SECTOR_NBR//改为flash介质的sector 数量
$ t" o% e8 D' c, s1 L' K - #define STORAGE_BLK_SIZ W25Q64_SECTOR_SIZE//改为flash介质的sector 大小
复制代码
7 u% X! t4 }: a* {# yint8_t STORAGE_Init_FS(uint8_t lun)
, R% D, F+ ^( H" y" ]3 f; u% b. Y+ k+ j. W7 N1 D2 N
我们可以添加我们刚刚写的存储介质初始化,如果正常,则返回 USBD_OK
: f# o9 Q9 b1 D, U. A- N' L5 |0 R( d+ K/ b# m2 e# K4 R. S
int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)( n6 E( g* f3 P3 G
& {2 d3 ]9 {5 ^* I5 S% `4 ~
获取存储介质的大小,函数已经填充好,我们可以不动
$ {2 Z( F% q4 h. l$ \/ {& k+ b& d3 ]: V. T4 @, O
int8_t STORAGE_IsReady_FS(uint8_t lun)
+ M m+ d! W- L# h9 {
# H( }! T, c3 C1 f( \6 g* j4 w3 g获取介质状态,我们要对存储介质的状态进行判断,这里我们要判断两点,一个是是否正在读写状态中,另外一个就是存储介质是否是不可工作状态
0 \( _! N' _! u# w4 Q' U2 X, e
' J/ H" s2 o( ?int8_t STORAGE_IsWriteProtected_FS(uint8_t lun)) w2 ^9 \. p p% M2 o3 t$ _
+ I+ j0 \& Y: ~& t5 I4 d判断是否是写保护,我们可以直接返回USBD_OK
) t0 E- o' f$ O2 I$ y
7 x- }( ]: ]! x) Eint8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)+ ~/ Z9 E% j) V, ?
$ x! e, ~% U1 u读取一个扇区,我们将准备好的读取一个扇区的代码填充进来就好
" ^) G* C/ v1 K5 M% R7 ^/ E1 r5 V+ w# f3 e8 X
例上述W25Q64的驱动代码:+ S- L- h& t1 w, g8 v( R
2 }1 H$ F1 ?5 ~) x3 l" C; W
- /** [1 Z0 b3 K" d9 g" B# s$ l
- * @brief .
: t" l5 v4 R/ o# B! `% F - * @param lun: .
' F7 D4 Q9 s6 M - * @retval USBD_OK if all operations are OK else USBD_FAIL: Y% _) E( p$ W( I( c' f4 F' A5 e
- */
& W: Z# S2 d* L- J: ~6 c# f x! b - int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)( u" O; V; M) F: w5 ^
- {; v/ n) B' g4 Y
- /* USER CODE BEGIN 6 */
/ p& t+ R, ~6 G* U( {$ s - while(STORAGE_IsReady_FS(1) != 0);
. f+ v! n7 q# S6 j. b4 |2 h - storage_status = 1; Y* U8 q+ e6 c( \* h7 D
- w25q64_drv.read_sector(blk_addr*W25Q64_SECTOR_SIZE, buf, blk_len*W25Q64_SECTOR_SIZE);2 `0 ?3 d: X$ q! b z. S2 S ~
- storage_status = 0;
. C+ T' P" p! ?: S X1 ]' L - return (USBD_OK);; y# V" q0 D! N) K( N
- /* USER CODE END 6 */: E' u+ j& g' T$ ^6 B! C& ]
- }
复制代码
3 V! y1 a, I* Wint8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)1 W! @( m6 B% N& M
7 u1 n! d$ |0 P4 H( o3 ]. F% L
写入一个扇区,我们将准备好的读取一个扇区的代码填充进来就好
+ x) v8 t# W3 r1 u$ H) L1 [, I! v2 S; E( E( K5 C- _$ k
例上述W25Q64的驱动代码:
( A, c* J2 H# Y# Z' c' v! e6 T& H* R2 _" C$ }0 X3 W+ H: X
- /**
; F6 b- p. U, Q: O3 h - * @brief .0 l, S: C* y% W r9 p5 _
- * @param lun: .
( A0 L' R# h8 s; P' g& A" m8 Q - * @retval USBD_OK if all operations are OK else USBD_FAIL! e- a9 J: D2 @1 u
- */ A% c' G, M8 v( K
- int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
! r7 e9 w5 r$ n( w3 a1 K) a# s - {) L; K" ?: Y/ ~4 M2 j& K
- /* USER CODE BEGIN 7 */
# o3 p. \5 t- B6 u) l8 [- b - while(STORAGE_IsReady_FS(1) != 0);
. T7 o% z4 Y/ N - storage_status = 1;/ g" A; Z# @$ V# S" U
- w25q64_drv.write_sector(blk_addr*W25Q64_SECTOR_SIZE, buf, blk_len*W25Q64_SECTOR_SIZE);0 `9 p* r* f9 X$ z& O
- storage_status = 0;. |' a' ^ M6 F
( \/ C1 ?' d0 P I! s! [- return (USBD_OK);3 U# N( _9 A& y: k& o$ k8 \& K
- /* USER CODE END 7 */
$ V& y( Q- V5 ] b7 Q9 }7 h - }
复制代码
3 v, z, \9 z8 I. sint8_t STORAGE_GetMaxLun_FS(void)6 q. l2 ~/ h8 s$ x# W1 B- Q
x; X0 T6 ]4 ~. q+ C: s
读取磁盘介质的个数,一般我们只虚拟出一个来,不用管就行) L4 y6 b! ~" F! G) g7 B
: M8 Z3 F- f4 L: u4 }3 J
我们使用的spi flash的sector大小为4k,也就是4096,USB这边是520,所以我们需要将其修改一致
6 M% y! {, F2 J8 I/ ^5 T) d9 X
( G4 h n9 k+ b3 P修改usbd_msc.h
" X" l; S, M4 v3 G2 S1 Y" s g5 {
- /** @defgroup USBD_BOT_Exported_Defines q9 H; ?8 `) q
- * @{
& z3 n& U9 _3 J! A - */
% @8 _0 X) N' Y2 I( { - /* MSC Class Config */8 W8 Q& y+ h; J: k+ M4 r' h$ R
- #ifndef MSC_MEDIA_PACKET3 @* R% @/ A7 @7 F/ ~
- #define MSC_MEDIA_PACKET 4096U //512U
! x3 t! R6 u1 s' n - #endif /* MSC_MEDIA_PACKET */5 D: c4 l) B* ~2 ?# x( P( U
( f6 \3 |( X9 I' J' {5 e- #define MSC_MAX_FS_PACKET 0x40U //0x40, k; e6 j- I$ `2 d( t9 ~6 q
- #define MSC_MAX_HS_PACKET 0x1000U //0x200
3 Z+ V6 @0 e) o- F9 v# c, V( K
- r' {! F: J% J1 H# o2 P+ d/ m" w- #define BOT_GET_MAX_LUN 0xFE7 g/ U* h6 z8 b: [. h; \
- #define BOT_RESET 0xFF' u$ T5 S7 b- j8 l: N' |. K
- #define USB_MSC_CONFIG_DESC_SIZ 327 r* A y0 t8 o g$ r: k
! w* |4 F% g0 c5 d$ F6 x- #define MSC_EPIN_ADDR 0x81U+ {/ H/ F1 `8 O* d4 f; e- F @; h
- #define MSC_EPOUT_ADDR 0x01U
复制代码 7 |1 L+ f" D" [5 h1 f7 \
修改usbd_conf.h) k/ _. v1 ]* r) B" @* Z/ N
+ p/ ?7 A, ]5 e/ q0 i5 s- /** @defgroup USBD_CONF_Exported_Defines USBD_CONF_Exported_Defines/ J# I& R3 g! i: I
- * @brief Defines for configuration of the Usb device.' q: f+ e3 `. Y9 g
- * @{
5 j1 j+ O* }% M - */
4 w( ^. V( t8 [4 x) z, e% c
5 r3 g3 e5 D [ r& |* t( L- /*---------- -----------*/
" P7 w2 V, n; m7 n6 P - #define USBD_MAX_NUM_INTERFACES 1# K N4 H2 r" r
- /*---------- -----------*/
+ P! h) F1 ~& S* ?3 P - #define USBD_MAX_NUM_CONFIGURATION 1
9 G/ `5 S" V4 \9 U4 [- \ - /*---------- -----------*/
) R: g% }6 r; h; I7 y0 G+ \ - #define USBD_MAX_STR_DESC_SIZ 4096 //512
% p' ~5 I8 B2 T" Z - /*---------- -----------*/& R7 V* J1 F* ]0 l# A+ |8 \
- #define USBD_DEBUG_LEVEL 3
7 ^; Q! l3 l8 p5 Q+ m - /*---------- -----------*/ I; `( ?, e, j: ` i
- #define USBD_SELF_POWERED 1
1 j% q/ C' a' y- n: a - /*---------- -----------*/
7 s6 a9 \8 d/ f) D& L' Y - #define MSC_MEDIA_PACKET 4096 //512
复制代码
" ]! N7 X: P. ~: ?至此,我们的U盘就做好了$ B5 I' z# @$ B5 m8 ?
————————————————8 s2 A+ r( |- u) L
版权声明:Logan Li
5 I2 u( k( V& q9 d% @' ^" q! r
) a/ N. `; D% y6 `# u& U
3 q" [2 W* I' J$ I$ a |