本帖最后由 aimejia 于 2018-5-29 10:27 编辑
# T. \4 j& B+ }% O' t I9 v5 R% R
9 P" p m' y* ~3 ?7 J. Q6 a1 前言
0 c- p, c6 n1 s, U; ]# v. D% W本文介绍基于CubeMx如何创建一个读取U盘的工程,并通过FAT32文件系统创建和读取文件。
' R" a/ M- _3 k/ B _; E2 G1 @5 I6 b) v7 a1 i
2 创建工程
: h6 ^. j, J5 ]- F0 x' m( I读取U盘的程序在实际项目中经常会用到,这里我们基于STM3240G-EVAL评估板来示例如何创建一个读取U盘的程序。
& E. @$ r; U' q3 ]9 s; @( ]
% |4 o7 n; G% e. b, o4 s6 e0 p在这个示例中,我们将通过一个按键来触发文件的读写。
' ~5 S9 W! e; A* H. z+ Y) [0 P( C* f) m
2.1 硬件介绍' @( u% Y3 c1 M5 L# S- G
在正式创建工程之前,我们首先非常必要了解STM3240G-EVAL评估板相关的电路设计。 , b& H, J& p8 q6 n/ u6 T4 a, z
STM3240G-EVAL使用25M外部HSE,USB相关电路如下:
! Q `" ?4 B- m8 Y# r- aUSB电路3 x; M8 \. ~) \( r0 c1 r
: t% i* g- m" F8 m% h- i3 D+ A7 P. z
6 ?) Z5 d8 ?: ]8 ~( n, Z, u图1 USB电路 如上图所示,MCU通过PH5管脚来控制外部U盘的VBUS,低电平为使能。 4 H2 h, J) C' D+ Z
按键相关电路如下:
; Z' `4 Z8 e i2 l按键相关电路
% ]- R4 I% z5 h9 O Q) T$ U2 {
9 D% `+ Q, _ d/ H' Z3 J: i. U" b" H& h8 N
图2 按键相关电路 2.2 创建CubeMx工程8 }, ~4 z2 a# W9 v( T4 _+ q! S7 c
STM3240G-EVAL评估板使用的是STM32F407IGH6,因此创建对应的CubeMx工程,在pingout页面中使能USB_OTG_FS外设,并设置为Host_Only:
/ n8 t9 n' ~; q. W0 V! [USB_OTG_FS设置为主机模式
3 O& P% R( O1 _& M3 r- m/ e# }
- Y7 C) l' Q+ [! w2 F( A
, u& K) \, r5 |( g图3 USB_OTG_FS设置为主机模式 并见PH5设置为GPIO_Output模式,用作使能VBUS。同时将PH15设置为外部中断,检测下降沿。
* H3 b2 }( x) G0 e3 t; X9 J2 z( ? pinout分布
. r6 O* u4 r+ p; O- k) J; L- b A) M+ P
- j7 q ~) K* l1 E3 C图4 pinout分布 时钟树方面外部25M HSE,主频设置为168M,USB时钟48M,如下所示: 6 K2 P- x$ K; k
时钟树设置
& V- A+ R/ E& h% w1 r9 R' L& S$ V8 [1 I. j, n
0 `! G. R! [+ |& }7 e; L图5 时钟树设置 在配置方面,HAL层GPIO基本没有什么特殊,记得外部中断使用检测下降沿,这个是使用在按键中:
) \+ `' z# Y. t" O0 d按键检测外部中断
6 Y9 z6 Z* _ E- D, w- }* v0 z+ F- O- y! O
3 Z# z/ r" E1 X2 F/ \9 |- S3 [. ]& P
图6 按键检测外部中断 在NVIC中,一定要将USB的中断优先级高于按键中断,否则U盘不能正常读取。这里USB中断优先级设置为1,而外部中断15设置为5,如下图所示:
* t' T( n2 M' x中断优先级配置1 w3 k8 ], Q8 @
/ H" g3 t; U# c6 O5 y6 q
; n. _9 b( j+ R/ }, ^; k, F) U7 u r) a
图7 中断优先级配置 同时为USB选上MSC类,且勾上使用USB disk FAT文件系统: 4 h3 g6 R. }: S/ F( V1 t( c# z
USB MSC类和FAT文件系统
2 I' X- I( Q x" K/ I
& l- X5 }& N: y+ D! k0 m5 e& R1 [! ?
图8 USB MSC类和FAT文件系统 中间件配置方面,其实可以使用默认,不过我们还是修改部分参数。 3 P7 v& ~+ G: l7 y
在文件系统配置方面:
, J9 w" u1 b+ R: k7 y文件系统参数配置" r' W; k% {" X( J6 h1 n! l& ^! D
( e) Q' [0 [! I/ t: E- j2 b* ~% N
) W$ w4 O# q$ e
图9 文件系统参数配置 我们只是简单地将其配置成支持中文,并支持长文件名。 8 g9 S, Q7 s) B/ i" G0 f
最后,将栈和堆的空间大小都设置为2K: C' o# o7 V/ j% d2 F2 J
堆栈大小设置" y: Y O6 X( H% c
, Q) d) v `3 v, P9 i% A# b8 i
2 A" W9 g. V2 b7 C$ q1 u1 m图10 堆栈大小设置 OK,接下来就可以生成工程了。4 ]1 h; Y- Y* D' t) n
; N: q; g0 z# _0 V" v( \
2.3 代码修改- X1 y* S8 N1 w z
在生成的代码中,首先我们对main函数进行修改,在main函数中我们需要做的工作主要有以下几件:1 C1 P' B# k' j+ M n- [
/ n0 P( b1 @' y# x0 {完成系统初始化2 y; L% ?4 G- Z ]4 O
使能VBUS
& \* j" j* f' w: t; @3 B+ L1 X当检测到插入U盘时挂载文件系统( _5 c+ i% S: R" Y, ^! N
当检测到U盘拔出时,卸载文件系统
2 B- g$ e/ i% h/ E2 P$ _6 y于是main函数如下所示:( w7 p* ?- }- ~- Q0 G
- int main(void)$ L6 k# Y4 O% h/ w
- {6 }2 R5 N# Z: }! s% I+ l* [. d
- /* USER CODE BEGIN 1 */
: b5 x2 D- M4 \$ @$ M* | - static ApplicationTypeDef pre_state = APPLICATION_IDLE;* f% ?( f- K1 q: j) D/ Y
- FATFS fs;
' {& j0 ?* P0 r1 T - /* USER CODE END 1 */
$ l2 B- [& K# O% G' v0 x6 H! i1 n - /* MCU Configuration----------------------------------------------------------*/
' @ N8 t, M: B# F - /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
2 n8 U w5 p' G+ S3 H - HAL_Init();& x" j9 J$ Q4 y- U0 \5 e
- /* Configure the system clock */
3 d7 z" `) G2 D" j9 K - SystemClock_Config();
- J$ P9 N% R; Z6 i: ?+ Z4 T - /* Initialize all configured peripherals */
/ ?! T; n' k; i. t: q7 M" x - MX_GPIO_Init();' `# g) k7 a) ~+ c& x7 r5 [
- MX_FATFS_Init();
' k; U6 x1 t. ?+ Y: d, V1 i2 O - MX_USB_HOST_Init();
5 G, ^8 N( {2 x" c - /* USER CODE BEGIN 2 */
7 ?% H" i) f- F - HAL_GPIO_WritePin(GPIOH, GPIO_PIN_5, GPIO_PIN_RESET);//enable usb VBUS
' N* W- |. u- \9 e; c9 w0 G - /* USER CODE END 2 */! w# W0 s0 w! P" S/ N* q
- /* Infinite loop */; Q& ^/ Y. \+ f* @; T
- /* USER CODE BEGIN WHILE */6 ?2 U# w; _8 p* e! a; C
- while (1)
, ]/ ?$ M7 b( n; `$ O6 C - {+ F9 k$ l% y* |
- /* USER CODE END WHILE */& |4 z- K8 p5 X+ b7 E! }% C1 ]" \
- MX_USB_HOST_Process();
- r' j3 c( W1 T+ c" H: j - /* USER CODE BEGIN 3 */
' [3 ^& u6 c$ B8 O - if (pre_state != Appli_state)
) Z$ S0 i4 x3 C - {
9 H* F5 M; z6 z, @+ G1 v - switch(Appli_state)2 v* H! y+ K/ ~3 ~) O& U7 A
- {$ e" U" m1 j( ~) _& d* Z1 S# I
- case APPLICATION_DISCONNECT: //USB flash disk remove
L+ W7 {, d4 F( a - /* Register the file system object to the FatFs module */" f" M' u1 ?! m2 q
- if(f_mount(NULL, "", 0) != FR_OK)) _% t( G8 b) H" X$ w# ]
- {
- H: i: N: N4 c4 u+ w* O3 | - USBH_UsrLog("ERROR : Cannot exit FatFs! \n");
. ?! s/ ]4 k: b& n) q+ l% p6 i! z- ^ - }
m' \ {) d6 x+ v - break;) Q- L- q$ h/ ]+ O# Q8 z
- case APPLICATION_READY: //USB flash disk plugin
+ \0 v8 @+ `2 O; Q: X% \ - /* Open or create a log file and ready to append */, o, A/ K1 r" G4 W1 J- W4 y' Y3 R
- if(f_mount(&fs, "", 0) != FR_OK)
) O2 w% b8 A; g# G - {
S1 ]" z& T. N# z' u7 V" C - break;/ q8 M: j2 e% ^- {4 o" ]6 M
- }) w) ] n/ [# R& r
- break;0 m/ I2 c4 x8 ~$ J. J1 V
- default:
7 o+ H+ Y2 h7 ?+ h - break;) w ]' y x( z0 h" d
- } f) A$ P+ C% @4 I
- }9 I' Q: F) `+ R( X4 @
- pre_state = Appli_state;
& \5 J7 {6 ^' Z5 I2 O4 U" M+ e7 i - }
* N" [" G) d* y( t - /* USER CODE END 3 */" b3 n5 ~. B- M. ?! K/ c1 d
- }
复制代码 如上代码,程序通过拉低PH5管脚来使能VBUS。使用使用一个静态的局部变量pre_state来记录之前的U盘拔插状态,用以跟踪U盘的连接状态,当U盘插入时,立即挂载文件系统。当检测到U盘拔出时则卸载文件系统。( o5 S2 n/ A+ e+ I# E3 Q+ B
- D1 d! j5 ~. ^0 u' A6 i& d接下来看按键中断处理,这里,我们在按键中断中分别做3次测试,写文件测试,和文件扫描测试 :
) y9 d) f, c* O+ ? I- void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
* C3 s/ [5 ]3 m- y3 L/ ` - {1 O9 _. C7 g. h) Q
- static uint8_t step =0;. B% p6 i# h) Y2 X! t& l8 J% M
- if(USBH_MSC_IsReady(&hUsbHostFS))
' b Y7 A" c! O* ? - {: M6 _- l$ C: L% U
- switch(step ++)4 h( _, g1 L; m3 @2 p4 U% |( O8 y
- {
! L4 t% m, K7 W! C& y& w! j8 D8 [ - case 0:7 L- N, D9 k+ D& d8 L* K. k& `
- writefile_test(); //写文件测试& X- b% b- G+ A3 e& w
- break;
; l6 [$ R) e/ d2 X - case 1:
* w. d7 |. ^" W0 ]/ c - readfile_test(); //读文件测试4 R1 b& \+ j, j
- break;
" z! A7 u* S1 @( O - case 2:) k, `2 Y. r7 R8 |+ a6 d2 Q
- Explore_Disk("0:/", 1); //文件扫描测试( g8 O1 Y- c- C E: [
- break;
! b4 |% S: k) m+ o7 B. C - default:7 n! D$ r5 X5 O/ K- ]
- break;* x j5 n! w7 Y. X# K
- }8 [# [! g$ p; V) D/ U- h
- if(step >2)2 H5 ~# G( ^3 r; _! Q, C9 B
- {! n3 \, ]2 N4 h9 p
- step =0;
8 n5 t. t+ U, {7 R9 J7 F - }# n- A" D5 _5 }
- }
2 F V3 ^3 \! g0 O/ c. o! P - else
) v0 V$ @2 M) E' Y, t, g! L( |9 F - {
$ o4 c0 T. v6 {! i! u# F) v - USBH_UsrLog("USB Device is not ready.\n");
) W3 _7 N) C @ - }
+ |3 a8 J* W% k, L& Y - }
复制代码 文件扫面测试,写问津测试,读文件测试:- FRESULT Explore_Disk(char *path, uint8_t recu_level)- }7 E% @4 z) h$ b
- {6 `5 K+ Q/ t% p4 {! e$ h
- FRESULT res = FR_OK;8 ~3 o! b! G5 o
- FILINFO fno;0 ~& Z: T3 w9 d1 M' ]
- DIR dir;
! Q, M; J7 I0 G - char *fn;
, L! y, V/ Y' i; N9 S2 T* j - char tmp[14];
7 l/ R* n. k3 Z. Z A1 k4 X - uint8_t line_idx = 0;
M& \3 ~ y4 j - : j1 j9 [4 h# R
- #if _USE_LFN
% [) Q4 R4 W6 u6 V8 e/ h% p - static char lfn[_MAX_LFN + 1]; /* Buffer to store the LFN */
1 t S+ \& j3 P8 h, G! m7 t - fno.lfname = lfn;
/ V; ?7 O* N2 t& u3 {$ {8 f' b5 } - fno.lfsize = sizeof lfn;2 S3 u$ @+ D" {4 Y' U+ c
- #endif k1 ~9 e5 d9 w1 Z, a r# B, W
$ m0 @2 I+ t+ A* B- res = f_opendir(&dir, path);
, N/ { d% u1 U1 d! E - if(res == FR_OK)
, P9 G3 t, Z; ^& H% m: ]9 \ - {
# i( h7 m' y9 Q6 T - while(USBH_MSC_IsReady(&hUsbHostFS))
2 @% M* \% L5 R) H - {
& m+ b+ f0 R# D5 h& V: P# F" _ - res = f_readdir(&dir, &fno);, X& k0 ?8 [5 J1 y2 H, C+ n" ?+ @
- if(res != FR_OK || fno.fname[0] == 0)
7 p, d7 y8 Z! e7 D( _/ ]4 G - {5 P8 k+ @2 y A, S# m6 t; X
- break;) W9 b! J5 O+ W) A
- }
" P/ z# Y6 e# U/ f& v8 W - if(fno.fname[0] == '.')
3 m. b. }6 z6 d+ Q6 c( R- a8 w7 B - {2 o, G. r5 i4 o1 t+ _; m
- continue;6 j4 T! [1 M$ I( M) M+ Q- ~, o8 o8 _$ Y
- }
! u7 l5 h1 x5 O% Z - 4 x* T2 z2 ^) r- T" L Y
- #if _USE_LFN
`( Q/ j3 z- F# v$ {: r - fn = *fno.lfname ? fno.lfname : fno.fname;" _% ]* y8 R& i
- #else. Z3 H+ r% n; @, ~
- fn = fno.fname;
4 X# w$ X& r( ~ - #endif% i4 [1 Q( x/ D, T. s4 ~) M7 g
- strcpy(tmp, fn);! R# b% @+ G, h5 O
- # V" k- s$ E% j% e& ~& j! b$ j9 m
- line_idx++;
/ R; m; h% p5 P! q5 c! w1 f - if(line_idx > 9)$ |7 E$ t. A3 K6 r; q0 j3 a
- {) [$ ~6 T7 [2 P" i& S
- line_idx = 0;
# k- d6 c; `; u - }3 {; J8 L" ^# J+ I1 q
) e- q" T- Q# P* F$ E- if(recu_level == 1)
7 L! R: I5 o7 b9 F3 {, i - {- V% i# s) |( l1 l8 o* E- k6 e1 L
- USBH_UsrLog(" |__");' K$ y8 J% o! ?$ G7 ?
- }
2 [3 Q) Z' n" H6 @ - else if(recu_level == 2)) F4 X, L0 |/ F
- {
/ `" y5 l5 f- s; c - USBH_UsrLog(" | |__");/ U( p$ H5 `* k# q
- }
1 i1 A* h, C7 b - if((fno.fattrib & AM_MASK) == AM_DIR)0 [. p. C! ~& m4 l) {1 i
- {
. @& L; u3 ~3 Z - strcat(tmp, "\n");- P9 T. Y8 u9 _) j- t. L0 G
- USBH_UsrLog((void *)tmp);' j; R4 W0 i9 N; p
- Explore_Disk(fn, 2);" W$ X1 z6 w7 z6 |4 o/ } K# y3 L
- }- Z& Y: ?3 D( m- X6 y
- else9 q4 x' S+ ]/ n4 b( f8 `. L
- {
8 r* [) P. }3 n4 s+ ^6 i4 a5 @ - strcat(tmp, "\n");4 i" J) M; X- D9 [9 q, o
- USBH_UsrLog((void *)tmp);$ S+ G6 E+ v) Z, U* ` Q2 r
- }
# v2 {: [4 S+ T/ P - / ^. s% Z, i9 s1 r
- if(((fno.fattrib & AM_MASK) == AM_DIR)&&(recu_level == 2))
5 n" e6 W2 h( h' E - {9 e, L7 P/ q9 Z3 C, P- o1 C. l1 M
- Explore_Disk(fn, 2);
. Z$ u$ i) f/ t9 q$ V) w- n# B - }
: v! M) B8 W8 d& D" _8 D" U - }! z. N& }) _7 x; S6 [6 p
- f_closedir(&dir);; J. W5 Y w7 E2 ?6 \2 K
- }
) n4 o. U- n5 b1 q- p" S - return res;
& E- K, l. @7 ]+ H+ o8 g" t4 o - }
* C4 w* L$ N2 {0 n& i& a& I - void writefile_test(void)
$ E5 C# n7 X I& d0 X, \5 l" u - {6 Z D, D5 c# |( ^/ i( L
- FIL fil;) Z$ f% z V V/ a% m- @7 }3 U
- FRESULT fr;9 e4 X8 L3 i, T. ]1 D0 ]
- 9 I6 ]! Q4 Y0 Q0 H# ~' y9 B0 n
- /* Opens an existing file. If not exist, creates a new file. */9 E# y l/ I" B/ ^& ?+ f+ f4 N5 b
- fr = f_open(&fil, "0:/mytest.txt", FA_READ | FA_WRITE | FA_CREATE_ALWAYS);
- p, F5 p8 ?0 |" S4 l - if (fr != FR_OK)1 m7 F4 N6 g+ F: d" `5 b
- {
" @1 W: d2 q& R" i" I - return;3 @( H" U, F$ D/ b4 A
- }
( f- w2 [* R. @5 z3 E- @( c( N - f_printf(&fil, "%s\n", "[USB]write txt OK!0123456789");6 K2 [6 L. s( M& X# c" }, r
- 8 F$ y& C7 I4 [/ B: l7 C, @
- /* Close the file */
: d3 Q5 U1 x, W+ m/ N1 ^( P: \& r - f_close(&fil);
+ r' D! d! R8 R - } k0 S" ?2 c N8 p8 U
- void readfile_test(void)9 _* R2 ?2 Y$ [3 o7 P9 m4 E2 v$ I
- {
8 N. r1 R$ l0 n3 d - FIL fil; K+ g7 p/ s5 p# Y" ? v" v
- FRESULT fr;
! }; q& ]* s9 N( D" F - uint8_t buff[20];
9 \ M' j6 H/ k2 e. M r& P# P7 B - UINT off = 0;9 e; b: r, B5 u5 D9 @
- : K1 E8 N% m5 `( s
- /* Opens an existing file. If not exist, creates a new file. */
0 D, P! V4 m% g8 `& S1 X$ M - fr = f_open(&fil, "0:/mytest.txt", FA_READ);2 o- v5 F" f4 ?* B( l* N0 v! ]
- if (fr != FR_OK)
. ~. g3 N5 P7 l0 X - {- o+ f* W7 C9 Q z/ E- ?7 {
- return;, ?- B5 p0 |9 r0 y/ m7 O6 _
- }
! a/ g, \- U% m8 i( J" |. T - % {0 v P: p* u# o
- f_read(&fil, buff, 16, &off);" y+ Y: H% [, c) o& M
- buff[16] = 0;2 m8 |$ z! h5 i
- 6 @! b9 t# D1 B0 j4 j
- /* Close the file */) c7 j. l2 A1 R' x# g9 r# `
- f_close(&fil);
. T7 Y) h. [$ ?/ \3 M6 I. ?7 N - }
复制代码 ) ?5 x: A' ~" Z8 N% Q2 V
最后将U盘去下来插入到windows查看,可以正常考到测试文件mytest.txt文件,这证明结果是OK的。
F/ E2 M+ L( N- A) G( J+ P
' u4 X; k9 Z5 w3 结论% q: ` h( P7 y8 b! _
按键的中断EXIT15的优先级一定不能过于USB的中断优先级,否则代码运行到操作文件的时候回卡死!
* g/ l$ B% W, U( I0 J
/ I; h1 m/ y9 [! c' S5 X/ N2 n通过CubeMx工具将一个比较复杂的读取文件系统工程大大简化的。
" _1 E$ B. Q5 h y, g8 H9 v' C X: r. G; n" Z T# M# Q
: g- V1 ]7 l {: a! v0 l
) Y5 K4 T+ l0 u f4 @1 [转载自flydream0% m6 o( ]9 l5 Q; `' Q
2 b* f3 d' }0 {7 i; [8 D+ ^: y
: p7 @7 A! |) T4 |. \1 }& a |