简介: `+ }, y8 N" [$ y' C0 j
最近项目中可能需要使用到SD卡,所以需要对SD卡的配置和使用调研,在配置过程中遇到了一些问题,在此记录一下。
) ^' T% R5 M' `) D1 I& [* i+ R/ W" J6 U! S' T+ B) K$ H
STM32Cube配置
: x! `) n8 g8 R# T9 wPinout( h& w, s& g+ k9 g0 Y
4 ~+ X4 y3 j* u. D; Z
3 a* }5 P0 h! F7 d$ @
' J6 ~- V! y6 F! G2 w5 p$ o3 ~& t$ c. u! d5 u
% e* I& V% d! c8 m6 I+ I只需要注意绿色部分的设定
+ U3 W1 _, J5 M& s) L5 w1 x
; n9 ?- h9 B. Y# t9 u4 S: J2 D) dClock配置! C9 y2 B: Q8 o8 `* N
% c0 A* J$ A: `0 `7 k2 ~
0 Q; Q5 P; m a2 n
这里使用了最大的Clock,SDMMC1的时钟是48MHz. `8 F# L2 ^$ y L- e9 p
, [4 Q) [, e2 Y
FATFS配置! F' r. F( X v! t7 Z
. D3 V3 F2 P P7 |* z5 h7 l' g6 T0 D& z9 p$ g; y
& I+ B9 W. c' q' A5 W9 PFreertos配置1 x0 \' ?3 f# g3 H, a
- X' c# E& f( s/ o7 g' X% d
2 S' B; {' i1 S9 o. _; a! D8 U* A+ {9 H y& _
这里增大了Heap size,使用了heap_4的内存管理方式。
$ C# z' u) H; V% a$ x( Y& c, }2 ~' K8 M" S2 w
SD卡配置/ c6 |) P7 S h6 M
4 V! X7 x# F2 V! G: K8 x. `
* m5 `( e2 F( R, c# n% `. P6 i& t: w6 w( P2 I5 _; m% M2 Z2 d; h
, {6 q/ r6 ^* L/ g( ]" `
8 i! b8 y( f7 R+ W% h# Q! a在这里打开SD的全局中断,并使用DMA2的方式传输数据' x2 t8 ^5 R7 g" l
1 h5 b# w% D+ C# q7 \5 w$ Y
NVIC配置
) |; `3 B6 [0 a F* u1 `
2 ~5 ?& N4 C/ X- k1 P6 ?: M7 v
+ K! O, P9 T& f* z' Q- n) q" ~1 e) W5 [- V# U9 o! u
SD的全局中断配置为5,DMA中断配置为6,5的优先级更高。
& {/ p1 ^' Q: E2 O; M
3 A' k7 |0 v4 C) h5 g$ m9 c- r工程配置
+ x' o {# @: Y; p% M" q; ?0 t9 W# o6 T. \: Y
' Z% ]3 y/ h" C7 N( U5 x0 h) `& m1 N6 E% O( j
% I- D. G5 d8 l J1 c V
& L& p1 V! i: w! R- G
以上就是所有的配置内容了,配置完成后,直接使用Code generate功能就能自动生成keil工程。) h `+ L# m. H$ o, {: [( |
$ G; V( y7 _& O: S1 \代码修改
6 c' g6 c9 n4 a4 C; ~, ^4 g' e初始化SD卡
4 D- `+ E: ]$ c2 }我生成的工程中需要手动添加BSP_SD_Init()函数,我是在MX_FATFS_Init()之前添加的。
+ g4 U C6 F8 a8 b- /* StartDefaultTask function */
- ^+ R. k( B" v% Y4 r - void StartDefaultTask(void const * argument)
2 |1 u$ P' p, b1 J* H* _* K - {5 S4 m; m4 x7 O2 i/ h p! [1 I
- BSP_SD_Init();
8 f8 B \# _( p$ B6 R - /* init code for FATFS */
: |# J8 }8 i0 [* o+ `/ { - MX_FATFS_Init();. u% Z$ d7 k) N& H
# g& S. ^, B2 g- /* USER CODE BEGIN StartDefaultTask */$ B# p$ O: Q; R; L9 g1 n2 o
- /* Infinite loop *// C( D* U9 ?1 m* n# c2 \- i
- for(;;)+ F* ]$ K# z1 L5 e1 ?, \) Z1 v
- {
% ?) i; O0 f: M - MX_FATFS_Run();' h* a0 Y/ A3 }; W I# q+ S# X4 H
- vTaskDelay( 5000 );
( s6 A" j: ?! Z5 l2 v - }
, Q8 _! F9 a; Z/ H! k - /* USER CODE END StartDefaultTask */
0 `7 M2 }) Q; T( a- {/ V4 i, J: ^ - }
复制代码
" [+ S8 B% ?" X# Z5 E: F7 O添加DMA中断处理函数
/ Z- `7 q) R/ ]/ g% c- S6 m' Cstm32f7xx_it.c文件中,需要添加下面两个中断处理函数:& d! G3 _$ G! F3 c- L
- void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
1 v/ V) v% x- u8 o' o; E - {% e, ?$ o, X' s! `$ ~ Q
- BSP_SD_WriteCpltCallback();& c2 Z# r! ?4 x* m- ~
- }
& L. y, s# U# c; I9 l' k6 b
& @, N; r# T. k7 @" n) t- void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd) @: n$ W) y9 r( b9 R2 G# i1 o1 P0 M0 r
- {; G* b# ^4 G, g4 _+ O
- BSP_SD_ReadCpltCallback();
* W$ f+ S) w$ B; Z - }
复制代码
# K5 Y% E4 X: H% M创建Queue* H0 }( P% i" P- v C1 ~& T
在自动生成的Demo中,使用了SDQueueID这个Queue,但并没有自动创建它,所以需要手动创建一下。我是在SD_initialize()这个函数中创建的,具体创建的地方可能需要再仔细考虑清楚。- J& R# x. v4 B' T: v
- DSTATUS SD_initialize(BYTE lun)
: T* A% }, }5 u+ s% m3 q6 x% ^1 ^( ` - {+ v6 I; z6 R* L, |* t
- osMessageQDef(myQueueSD, QUEUE_SIZE, uint8_t*);
( D$ h _6 V, h4 Q @8 U( w - SDQueueID = osMessageCreate(osMessageQ(myQueueSD), NULL);
, _0 P: m$ H7 @/ o3 @6 p4 ?' w - return SD_CheckStatus(lun);/ N2 {, H9 `; [4 Z
- }
复制代码
" A* `6 i E% t; U/ P; m将Pin的配置改为上拉! ?8 b3 q' Y9 h% j& R+ e8 g
在默认配置函数中HAL_SD_MspInit()对SDIO的Pin的默认配置是GPIO_NOPULL,但我在使用过程中发现,程序会卡死在SD_FindSCR()函数的以下这段代码中:. ?. E8 [* \6 @
- /*卡在这个循环中出不去*/+ O. J' ~% B5 h
- while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DBCKEND))/ t% c/ r( n6 Q+ B
- {
# S$ V0 i/ k" g0 m" H - if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXDAVL))8 t7 ]6 ]) ~1 I/ B- L; n/ S, M. ]- [
- {. Z3 N. T% L& ^4 Q
- *(tempscr + index) = SDMMC_ReadFIFO(hsd->Instance);
" x5 _* G1 B" |( n - index++;
; n( k$ R e! [" O - }
" d3 p8 t' V7 V/ l* `* c - 1 V# P% U- b& u* \1 X- ~
- if((HAL_GetTick() - tickstart) >= SDMMC_DATATIMEOUT)
% h* E" K+ s' U - {
* _8 r& b s' q" } - return HAL_SD_ERROR_TIMEOUT;
% L2 M$ ~3 S% h! B0 s: @ - }
8 [3 T) m; d8 \4 g5 h; [* k - }
复制代码 8 g# l+ h J8 B) L3 ]
后面我将GPIO改为GPIO_PULLUP后解决了这个问题,具体原因暂时还不清楚。
3 }6 J1 g& R8 s2 {
6 u$ s+ Q# k6 h- GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11 # o: D% U# f. K+ ~* l% b$ C x
- |GPIO_PIN_12;
7 R0 G# F4 k4 |8 s6 r1 T - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;$ y1 J' s% ]; n W9 n: M" M
- GPIO_InitStruct.Pull = GPIO_PULLUP;//GPIO_NOPULL;
, B8 N6 X3 g! I- i+ g" `) B - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
% A2 c! R% v( |! C' H+ L, b - GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;
$ ^, c' L5 O4 q2 \& m# ? - HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
& G8 N% c% H; B" ?
3 o5 \; @6 ?* [+ }# ^! k! w/ e* a- GPIO_InitStruct.Pin = GPIO_PIN_2;
# ^' |6 k a, A, I3 {# J - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
6 ~) S1 v ] g& h% e# V - GPIO_InitStruct.Pull = GPIO_PULLUP;//GPIO_NOPULL;- r2 K r! E8 e p
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
& F, g' d0 i7 l6 F& D ? - GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;) _9 o% D2 `) j/ ^0 T: D- Z2 `
- HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
复制代码
; B0 k( @9 b; \& Q# L, v简单Fatfs应用
" L {8 I6 x( d' m4 _- void StartDefaultTask(void const * argument)( g+ S( i) \7 N6 W5 ^5 x& j; e3 h
- {
9 f- V A/ I) o. c; ~ - BSP_SD_Init();
; C' t, P5 `: k3 R" J - /* init code for FATFS */! z6 w2 A0 _6 c" M1 a0 b
- MX_FATFS_Init();
3 M4 N1 v# ^: L4 _! g, w. a
5 `- _: q! E! i% s) e% b- /* USER CODE BEGIN StartDefaultTask */8 P/ W1 i; S4 d1 J% ^5 O0 E
- /* Infinite loop */, [& Z& x( T% a8 ?* |) M
- & ` ?7 \7 j& S' i9 q# E0 _$ p
- for(;;)
* `" h5 n0 {6 T! |2 T8 @& J - {7 m0 a9 M8 J' ~/ ]3 `: c
- /*自己实现一个Run方法,每隔5秒往SD卡中的文件写入数据*/9 |/ `6 D8 p6 y& Y. Y
- MX_FATFS_Run();7 [) S8 l. L0 e5 p4 h* y
- vTaskDelay( 5000 );: ~/ [7 Y/ Z* u1 z
- }& N5 v2 J |! U3 x" J& L( c2 v
- /* USER CODE END StartDefaultTask */
, ~1 o3 [( T' d - }
$ c9 L- c4 Z% Z, @- \3 ` - ( J- \& K# e9 S# b. U6 D8 T
- void MX_FATFS_Init(void)
, I- \3 {0 D% C$ f# |+ s8 H. a - {4 \$ w2 j/ _* b( e% K4 m. `8 d
- /*## FatFS: Link the SD driver ###########################*/
, Q% Z- c) [2 _9 A/ B: e - retSD = FATFS_LinkDriver(&SD_Driver, SDPath);
; W% i+ m7 J, x - ( Z4 E/ A7 V3 z1 r& l
- /* USER CODE BEGIN Init */
! c- n+ W1 \# R+ f) z& m* S - /* 在Init的时候挂载文件系统 */ 6 C, x# b% ]9 y
- if(f_mount(&SDFatFS, SDPath,0) != FR_OK)
! }2 _! s- }8 |& n! F5 Z9 X - { $ Y* E# a4 K. V ~- y, Q: S+ N3 E
- while(1);
, y% _: u! f2 K& ]- @ - } ; f* e, x! D0 \" t( [4 m
- /* USER CODE END Init */, X5 G9 C0 L* d9 @: K
- }" \2 @* X/ O' U# v) Y
& F' M9 a. |! ^! ^* H, K7 P" c/ P: S" |- void MX_FATFS_Run(void)
! V. A; `* z! d- v7 g$ [4 N e) V - {. S% x8 G; g& c8 x2 Y9 w. U
- UINT writeBytes;
" \1 q4 n# g; H. D7 G - /*文件系统基本操作:打开文件,定位到文件结尾,写入内容,关闭文件*/) M+ ^5 L( k G: q& c
- if(f_open(&SDFile, "SDTest.txt", FA_READ|FA_WRITE|FA_OPEN_ALWAYS) != FR_OK)) a }% J7 R4 ]9 i
- {6 ?7 N6 I* P2 @$ E# x4 x2 }8 g
- while(1);
/ F; l4 L6 v8 ^ W5 R - }
) S( ?6 f& K! Z - if(f_lseek(&SDFile, f_size(&SDFile)) != FR_OK)- j# z; X) N! p
- {- Z% K \- b0 } Y" ]
- while(1);$ c) G9 A( D& w! R
- }* S$ _ S: Y4 c# a) D) M" a* Z
- if(f_write(&SDFile, "sd Loop", sizeof("sd Loop"), &writeBytes) != FR_OK). \; z k C. I
- {
9 i- O2 u( [. n4 H - while(1);
! _, g1 d5 c6 W. t+ _% n! W - }# z$ g) Q, z% Z2 J# s/ a
- if(f_close(&SDFile) != FR_OK)) S' }/ V9 G2 t- Y2 Q2 S' G5 [
- {7 R$ C: a' ?% r% B# |6 C2 W# B5 H
- while(1);2 D8 G0 j/ m8 f7 B; U+ m& N: o
- }
% V4 i' }0 B4 _8 C& B. u) O( G5 N - }
复制代码
; X/ Z4 X) {/ q$ I9 @SD写入结果:% x+ G q/ d! f' f: @- g
! O }5 b( E) H2 ^
( p! ?8 h C% B; T- U8 ^
; A( p& G! F4 q" A& _3 e K
! ?9 A& J" _: [' \- R |