硬件环境
+ J+ Y i+ `7 F3 o" `- _STM32(支持USB)" j1 y/ B# P# i% G# I6 |4 F. Q
存储介质(SPI FLASH、E2P、甚至是片内FLASH均可),本实验使用外部SPI flash(W25Q64)。
& v! R- F+ i: t- k9 G& }& N# _ G2 }使用STM32CubeMX进行配置初始化信息: c8 ]& M6 S! V7 d
1.配置MCU的时钟,外设等信息,可以使用一路串口进行输出Log信息,串口波特率越高越好; m( `! o3 h+ J) Y* P: X
2.MCU管脚配置
6 A: g+ O7 _/ F: b
' j6 Y- {" [, O
2 t. e$ W! X* l' q5 x1 j
& Y2 g1 z! G" Z- N
4. 时钟配置8 t$ A$ Q8 Q6 @6 E! K
我这里的硬件中有8M的HSE,然后需要注意的是,USB时钟需要配置到48M; \) `3 F8 \: R. d- t
( |. m3 M6 R$ |* _# e' M
- N7 Q0 U7 l: \4 p9 b( }( z9 y' H) g$ [' }* g- a
3.USB配置,记得打开中断
* [7 F$ v' d( C+ O2 H- T+ Q5 E
k% I( g- v a7 k: V) ]
\9 L8 p* c4 j( j
% z% a8 h) K5 r# Z
4.串口
2 I8 e% {' P: D- D
6 H+ L3 ?% Y; z# o
4 r4 y J/ ?& X, ~; Z6 w' [% ?) K2 w1 A- k4 |( Q% T
5.存储介质的通信接口,如使用内部flash,则不需要配置
3 z! x/ S, p! W+ u3 E, ~W25Q64 是SPI FLASH,配置SPI接口,这个配置可能需要根据芯片手册进行调整* X8 G/ l! S) o: t
& S" X% f! K4 Z7 a3 ]! o6 C
3 e. x* q# \% Z% X C0 B& V' }, h3 V' A$ ?7 y1 V
6.中间件 USB_DEVICE
4 T+ k! _% W/ L) {4 G: [* yClass for FS IP选择大容量存储,其他可以默认,USB_DEBUG_LEVEL改不改都一样,做USB device时没看到打印出log来。如果配置了非0,那么需要重定向printf,并且勾选USE Micro LIB,否则可能会出现问题。# k. z/ E6 F7 d1 W# m
- D+ a- g6 M; R
9 Q% o0 [8 K& b6 `4 F5 k. U
2 V8 {+ s! N% B( y' v' e7.将堆栈空间改大之后就可以生成代码了
/ b, Q! P9 q0 O) e& H2 d6 T9 J
! f6 N( Y* r( Y& n; F' _, Q& F& f: b修改程序
( O* W. |! `! I生成完程序后,直接编译烧录,然后将USB连接到电脑上,就可以看到已经多出来一个盘符,但是看不到容量和大小,也不能成功格式化磁盘,那是因为我们还没有完成数据的读取和写入
. h8 ]3 f$ E s7 Y: l0 `$ B0 Z9 n6 U, i
修改添加存储介质的驱动代码
& M3 R2 z9 J) A0 n! W4 n这个步骤很关键,如果你的驱动有问题,那么就会导致格式化失败,也就没办法使用。
, K0 p6 Z* f. r. z
9 \5 r$ D7 d, l7 O( m7 T' HW25Q64
! R; }# R3 T: g( q3 C. I( H/ ^: |必要的函数. J8 _6 N5 V2 c g$ T" Y
W25Q64.h; Y k A# {! J0 [
/ T- w+ x9 g$ T4 U9 t- typedef struct
0 G! a5 o9 ~& ~* h - {
. J+ [7 A7 j% @0 D7 W6 y" s - void (*init) (void);
- B7 j) p3 ~0 V4 P7 C: |3 R6 b# X6 W - void (*read_sector) (uint32_t addr, uint8_t *pData, uint32_t length);
7 |( a* ~% Y! ~7 z" |; f - void (*write_sector) (uint32_t addr, uint8_t *pData, uint32_t length);- I0 s. s' B1 t
- uint8_t (*get_status) (void);8 F5 R; @9 K) d4 d, N
- }W25Q64_Dev_T;
: X2 c; T- l* n. C% a+ U. w - extern W25Q64_Dev_T w25q64_drv;
复制代码
1 q# K2 K b" EW25Q64.c4 d5 D6 y( P( D* D: O U# V: Q
$ z1 W; ]4 Q4 ` R+ U8 B
- static void w25q64_write_enable(void);) p5 I6 u; N/ u; h
- static void w25q64_write_disable(void);
( M& Y0 ]0 Q6 ~' U1 k; ^ - static void w25q64_chip_init(void);
5 ~: A, p8 q% F$ y6 { - static void w25q64_page_write(uint32_t addr, uint8_t *pData, uint32_t length); //256byte max
6 U8 N1 i! u( H, x6 C# l& ] - static void w25q64_sector_write(uint32_t addr, uint8_t *pData, uint32_t length);//4kbyte max. b6 C/ T7 F5 N$ y
- static void w25q64_sector_read(uint32_t addr, uint8_t *pData, uint32_t length);
& }! q8 w2 R' _3 m1 _ - static void w25q64_erase_sector(uint32_t addr);
7 D' G' L' r0 q - static uint8_t w25q64_read_satatus(void);7 \5 C7 r9 }* E& M' y
+ u. k6 y' B; E# Z2 l- static uint8_t data_padding = 0xff;, U K/ _. l+ I( o! `. F
2 D L) g5 x6 f2 D- ^( S- W25Q64_Dev_T w25q64_drv =
0 x. _& @6 I! f7 U' H9 _: e - {$ N' F4 |6 O2 y/ R6 S1 Y/ X
- w25q64_chip_init,) w" z! N& ]# |8 ]. P; p A8 h X
- w25q64_sector_read,
8 O2 A7 W( y. G% m# l - w25q64_sector_write,
" ], h: n! V$ T' f - w25q64_read_satatus, W8 t! d( W# f7 K( l6 c
- };
复制代码
+ k& X3 e% I0 J& _" u数据读写函数添加到USB驱动中
7 ]6 L$ e4 O1 C# s9 F1 S我们要修改的文件是 usbd_storage_if.c3 u& e3 b. @/ e. q* K) S7 J3 k
7 D( h" w. [0 ?4 m- #define STORAGE_LUN_NBR 14 S. q: _2 N6 ^5 f
- #define STORAGE_BLK_NBR W25Q64_SECTOR_NBR//改为flash介质的sector 数量3 W2 N3 R$ Z2 R8 G, o
- #define STORAGE_BLK_SIZ W25Q64_SECTOR_SIZE//改为flash介质的sector 大小
复制代码 , t+ `* v! C; c3 R
int8_t STORAGE_Init_FS(uint8_t lun)7 f( ?7 F9 g9 u N+ s
% p4 Z" o$ p$ W
我们可以添加我们刚刚写的存储介质初始化,如果正常,则返回 USBD_OK
& S/ o0 \) L8 b$ E+ j4 x/ W
6 \3 p5 [3 C, T( L5 Y% Iint8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
/ P3 L2 ]0 v w, n9 [! a9 l/ z Y7 R3 d& N5 i! l; o# `
获取存储介质的大小,函数已经填充好,我们可以不动
7 B5 R8 X* \! _4 S4 E6 K! h% P. l2 E4 T4 E5 V5 g& ~& F
int8_t STORAGE_IsReady_FS(uint8_t lun)
1 j9 s! ^8 \- {9 ]
h, F g2 u6 X; k获取介质状态,我们要对存储介质的状态进行判断,这里我们要判断两点,一个是是否正在读写状态中,另外一个就是存储介质是否是不可工作状态
3 X3 }3 T1 }5 Q2 `- ~" k9 _
2 J& ?- E j) v6 N9 ^int8_t STORAGE_IsWriteProtected_FS(uint8_t lun)
) h" ] i' t& O5 C3 T9 y3 H
$ k" y5 g8 M3 H' @4 i8 \判断是否是写保护,我们可以直接返回USBD_OK
/ R& P% H7 z0 ]4 x( V! ~# z
3 I; M+ c i2 yint8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
1 g& l x) i) |0 e2 R( e0 i3 g" o5 R% d1 D* F' F# e) q
读取一个扇区,我们将准备好的读取一个扇区的代码填充进来就好2 i; Z8 d# a$ Y' b
( b$ U( |( N+ ]" N6 S
例上述W25Q64的驱动代码: x4 w4 s E- K0 T
/ r4 A g3 a6 d+ o3 E& k- /**
/ p* W+ ?# N& C9 z1 d g$ E - * @brief .1 K7 u) b* f9 S. h3 a1 R$ T, v: N0 i
- * @param lun: .
+ s! u$ u! D: C# y# r4 k - * @retval USBD_OK if all operations are OK else USBD_FAIL+ Z" k# {8 J, X8 T; h. X6 ^8 I
- */
0 [5 S7 d0 Q- A: y- t - int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
5 y& T/ s; C0 C+ j/ s - {
; h6 S4 {) y! M A; y" \6 [ - /* USER CODE BEGIN 6 */
; D' w2 k" O8 f: J - while(STORAGE_IsReady_FS(1) != 0);
0 w$ _# ~& s6 N5 i - storage_status = 1;
4 R# F' A: @9 Q8 ~ - w25q64_drv.read_sector(blk_addr*W25Q64_SECTOR_SIZE, buf, blk_len*W25Q64_SECTOR_SIZE);
/ |+ E1 o# M+ N' m$ q6 ]% H - storage_status = 0;/ O: K# \$ _4 P2 |# j4 F" B% W
- return (USBD_OK);
% q% b$ U. A. ~9 e - /* USER CODE END 6 */9 S6 n0 `8 u% X% S
- }
复制代码
8 j; k% z* _; G Z/ X. i: l" F6 Oint8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
5 v- y" F7 k9 y- T8 [* {% @& ^2 ^, R( s, ]
写入一个扇区,我们将准备好的读取一个扇区的代码填充进来就好) T% A0 J1 [! u' J- f: H$ g
4 X' _* m- ~: I6 B% l$ D3 O例上述W25Q64的驱动代码:
$ w/ R3 |: J `8 y G# I$ m* n( e) t3 O/ V& ^ _+ V
- /**8 L# W# S3 e- J0 V4 x
- * @brief .4 S* q6 I i8 a H- _" \; V. s& n
- * @param lun: .
' K4 G S& U5 b m3 A - * @retval USBD_OK if all operations are OK else USBD_FAIL$ w% D1 ~6 A1 o( _1 S! t8 S6 v. n
- */
, B0 ?5 I0 g7 M - int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
; I' x8 }- b# u: ^ - {- O+ n% c. f+ n3 Q
- /* USER CODE BEGIN 7 */' D3 X, Q4 V* j) Q
- while(STORAGE_IsReady_FS(1) != 0);
" Y R' t( \; d8 T* V - storage_status = 1;! ]- ?8 j3 a( f8 Q. D, W. }
- w25q64_drv.write_sector(blk_addr*W25Q64_SECTOR_SIZE, buf, blk_len*W25Q64_SECTOR_SIZE);* i& b. a6 N6 L. k! l7 q
- storage_status = 0;+ f/ b; a& D) r3 X z( _
1 ~; | _ p& c [7 I w- return (USBD_OK);. Q4 `1 t# g% I' k' B
- /* USER CODE END 7 */
$ z) f- _) P; e. ?/ S9 J5 L - }
复制代码
( u/ D# S" h1 }8 \int8_t STORAGE_GetMaxLun_FS(void)5 w7 j1 z: t: A9 \; ^ V
( F# e# n! P' p. ~" f. n
读取磁盘介质的个数,一般我们只虚拟出一个来,不用管就行
3 O, W1 `" [ g# R$ V3 Q: h& C( ^9 R/ g5 o+ p2 X
我们使用的spi flash的sector大小为4k,也就是4096,USB这边是520,所以我们需要将其修改一致4 @; @. E5 Y8 p1 M( M
) p6 D/ L7 U" ?( C @修改usbd_msc.h4 n# x7 ]3 v* A& F, W- D8 C1 L8 B
6 j! y. P9 m: \
- /** @defgroup USBD_BOT_Exported_Defines
, R. v9 `/ L3 T" o - * @{+ M, Q* T. W% X1 T5 t$ k. _9 ?
- */
( |6 e+ H X- U0 t/ @' H2 F - /* MSC Class Config */: g& P0 P; ?/ y/ Y/ E& V6 G+ [
- #ifndef MSC_MEDIA_PACKET
. [7 }9 U3 Y* U! P8 p - #define MSC_MEDIA_PACKET 4096U //512U8 Y& }: V+ o0 O- u4 f
- #endif /* MSC_MEDIA_PACKET */7 i/ ?+ e. Y! E0 \
+ O/ L1 y% T; }: F4 T5 W# j- #define MSC_MAX_FS_PACKET 0x40U //0x40
: u; Q9 v) e0 [3 F - #define MSC_MAX_HS_PACKET 0x1000U //0x200$ ]1 A* t7 d. c+ n8 y
0 ^! M0 q3 o( \, e8 y" V m# o- #define BOT_GET_MAX_LUN 0xFE
7 i) }% e4 _5 q3 Z7 i - #define BOT_RESET 0xFF
$ @5 x) `: e0 B% b - #define USB_MSC_CONFIG_DESC_SIZ 32
# |3 B4 P0 p* N4 |) i/ h$ D, Z
) v' m N, e9 V. z) I0 p# E- #define MSC_EPIN_ADDR 0x81U
% P2 c8 u- i$ f8 l, b0 n/ \ - #define MSC_EPOUT_ADDR 0x01U
复制代码
7 ]+ w. [" Z; E' ~修改usbd_conf.h
3 K5 p4 l; i) ^' v6 g: Z S2 F* J& }1 R8 m' S1 [1 _3 {: j
- /** @defgroup USBD_CONF_Exported_Defines USBD_CONF_Exported_Defines
. t/ |" q2 D0 B: G8 O - * @brief Defines for configuration of the Usb device.* n: o5 y! ] K( ^
- * @{/ c% L+ F# k( ]) ~9 h
- */
i8 t1 D& q* r1 F& ^; Y0 Z
# d* p. {1 [7 ^- /*---------- -----------*/
0 i+ U5 F* K: E' F' Z# C: W9 o* { - #define USBD_MAX_NUM_INTERFACES 1( r/ I/ |# T' E5 w
- /*---------- -----------*/
' o% a5 D4 w3 {, _ - #define USBD_MAX_NUM_CONFIGURATION 1# z/ G+ t) B+ B; J. E j/ x& G# e
- /*---------- -----------*/. \/ e* m9 K @5 t/ O5 J% d
- #define USBD_MAX_STR_DESC_SIZ 4096 //512
2 j6 g7 Y) V( Y" e, z9 f% i - /*---------- -----------*/( P( S! s4 L' W
- #define USBD_DEBUG_LEVEL 37 [, Y6 j% o) ^) _" b
- /*---------- -----------*/; t+ r3 e1 V% {8 i1 @$ T( e; h* w
- #define USBD_SELF_POWERED 1& f) a3 a3 H+ M* ^' ]. s
- /*---------- -----------*/
0 o# y. h4 ?4 u! w4 V - #define MSC_MEDIA_PACKET 4096 //512
复制代码
. ?- B1 }9 U" X' B+ q4 o至此,我们的U盘就做好了
/ l5 i! r; `/ i0 ]; p) E* Z————————————————
" h" k% X. R& H4 R/ u3 l& ~& y版权声明:Logan Li7 S* K2 H! L1 }. y- v) B# q
- R# o0 |1 M/ C& y
) F. Q- A9 [5 s. d. \& h |