简介2 X4 b7 Z+ V) ~5 e
最近项目中可能需要使用到SD卡,所以需要对SD卡的配置和使用调研,在配置过程中遇到了一些问题,在此记录一下。5 \: F# o/ @6 H
7 Q! N* a& N+ y( h& I4 X7 oSTM32Cube配置
& z4 u& a% z* N* E! J2 IPinout
. J$ ?; k0 R+ c* I$ l' V/ V: x6 M; i" R4 l& Y9 F
8 K/ J0 l3 W6 X6 g- J- l+ `5 d
" m; x: V( ?/ D) l* W5 x( z
: j3 c& b% z6 O/ V9 W) C7 s5 R3 a
. A2 N) o G5 U( ^只需要注意绿色部分的设定9 j' \6 H0 P: l
; ]; \2 J! _& B4 `$ h9 d$ DClock配置
2 ]5 h- Q7 C. \+ x4 J2 Z( @4 T! T% g% ]* [' ]) v
5 Z7 e* B7 [, S
这里使用了最大的Clock,SDMMC1的时钟是48MHz
6 T8 |9 ^8 L0 S
% C/ J, y7 z9 `* N3 Z* |& wFATFS配置
& {3 h) u" E! M/ r" r/ R* ~8 Y' E3 U9 c9 {: i) E
4 z( b% k5 p) d. \
: C4 F5 H5 @. ^% eFreertos配置- |9 U9 h( }5 {0 B5 g
; E8 `2 F9 R9 v' H r* d$ l" J! w0 |$ i; r/ p% ]
( p4 x' h8 o; r+ G2 \这里增大了Heap size,使用了heap_4的内存管理方式。2 S; j h. ~7 K7 y
3 j. O1 t7 y) |/ n* e/ V( ^SD卡配置
7 j5 d- i& Z4 u) l( V
/ z. F9 S8 q- X5 O8 \+ N& ?
& K3 A9 c* i( v/ k9 @+ l3 f
2 q6 x/ T8 x6 {" K
: ^! ]3 `* q D0 c! f
7 X- z2 u5 M& Z& e" [- Z2 ]+ l在这里打开SD的全局中断,并使用DMA2的方式传输数据' q( [( g, i* W5 u( {# @% x
) g0 @3 a. I& F/ @NVIC配置1 ?/ g' b6 Y8 `" m# ^8 `& E% n
7 c* R+ O5 p' n3 _9 I2 D: j
8 G. T6 f7 p0 \: ~) D, c
" n) @) m. Q! @. S7 F5 ^8 l3 jSD的全局中断配置为5,DMA中断配置为6,5的优先级更高。
& D; @6 h/ o* W+ C2 b$ \ i# } X9 Y, |1 {
工程配置
" |' a% @* n- x( h! n, b1 b% Q% a% J+ |9 c6 K6 D) t! b
0 p' _; S6 ]. o J1 K7 ~* X5 d
& a$ m. m; u" l3 T: }* Q( L
( ~; [# k6 H5 {9 u1 }
% u2 V& c; x& g$ ?: [& l1 O \以上就是所有的配置内容了,配置完成后,直接使用Code generate功能就能自动生成keil工程。* |/ H' o$ x9 P8 |3 b. ]: }8 c
5 J, r$ x" T3 @$ T
代码修改" p4 [1 D, J' H" z% H
初始化SD卡
" l* [. `' T0 V我生成的工程中需要手动添加BSP_SD_Init()函数,我是在MX_FATFS_Init()之前添加的。
3 E! q: ?! I0 ? Z% `: [+ B6 }- /* StartDefaultTask function */
+ b( r+ L& {* h+ T! B+ }5 N - void StartDefaultTask(void const * argument)
9 P( l7 t- }- F( H2 p, f - {1 ?, L4 q3 U z) y0 ?8 S$ d# I8 H9 H1 U
- BSP_SD_Init();
. m- y7 |% s- K0 _, K4 M - /* init code for FATFS */
! g% k7 A: X( Q6 V( f - MX_FATFS_Init();, u# C! M1 Z7 b; q$ @7 T) ?
- ; O! N- i4 n t2 P- v4 y; K
- /* USER CODE BEGIN StartDefaultTask */
; M z$ i8 E. P5 P! I - /* Infinite loop */0 p& t5 W. q6 G. L; l
- for(;;)7 B/ D, t( V8 ` L, d% I8 y
- {
, ]6 W+ m& M F- ?3 ]) P4 F& ?6 o - MX_FATFS_Run();
1 k! n- Y0 h6 A3 o+ L, G" D8 V2 b - vTaskDelay( 5000 );
$ g0 B4 ?9 y* S - }
2 F9 O! ]7 x3 Y- h) D3 r - /* USER CODE END StartDefaultTask */
. r! W0 o% }9 e( ~& @1 k - }
复制代码
/ G1 m6 e8 i9 G4 N5 o添加DMA中断处理函数
6 C8 h% @* x& ?, L1 o, U8 v5 ^stm32f7xx_it.c文件中,需要添加下面两个中断处理函数:
; v6 Y% X- E+ Q& n8 C; B- void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
7 j8 b6 W2 t- }! ?# p - {
# X2 W6 l& d3 A3 E+ |$ I - BSP_SD_WriteCpltCallback();1 F1 ~! N9 K, J7 Q( R
- }4 j, z$ i1 m' D# C
' |( r* q( T" v0 S$ @- void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
^0 k$ C: n0 ]' I4 U) { - {
4 R; \9 |+ d& _9 M- ^" a# b) F - BSP_SD_ReadCpltCallback();: O4 r7 a; L( p& ?3 V
- }
复制代码 ! P% ^2 @$ d- Q) c+ \( R
创建Queue
3 ~ t# z) O, |# B2 ~4 ^在自动生成的Demo中,使用了SDQueueID这个Queue,但并没有自动创建它,所以需要手动创建一下。我是在SD_initialize()这个函数中创建的,具体创建的地方可能需要再仔细考虑清楚。
+ h1 z2 Y; P1 K, N% A' V- DSTATUS SD_initialize(BYTE lun)
% m; ~& r2 @9 G, ^$ X7 p Y - {
9 g4 Z6 e- f# e! o- x* D - osMessageQDef(myQueueSD, QUEUE_SIZE, uint8_t*);4 \+ i9 k" d3 [( S2 P' r9 J1 H
- SDQueueID = osMessageCreate(osMessageQ(myQueueSD), NULL);& ]2 B/ i9 J/ n9 F
- return SD_CheckStatus(lun);) M+ s) U. n( }4 D
- }
复制代码
! p4 R h/ e3 {% P: q, {( w' F) k将Pin的配置改为上拉8 K& f( p% F2 v
在默认配置函数中HAL_SD_MspInit()对SDIO的Pin的默认配置是GPIO_NOPULL,但我在使用过程中发现,程序会卡死在SD_FindSCR()函数的以下这段代码中:+ D0 |8 C1 m" T9 q. Q
- /*卡在这个循环中出不去*/0 D, P2 j3 h- l4 u
- while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DBCKEND))
( J5 X% Q: ^3 _* e3 s - {* B5 _! T! L/ g Q
- if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXDAVL))
% j- [; Q: I: Y1 I0 { - {
1 j. ]7 b$ S- V# g - *(tempscr + index) = SDMMC_ReadFIFO(hsd->Instance);
+ l$ k" z0 R/ l. a0 e - index++;
7 a8 W0 H9 _/ u8 v - }" c! i, J1 I$ ]! x: K
- 8 g8 y" Z( p; F3 y* t/ h
- if((HAL_GetTick() - tickstart) >= SDMMC_DATATIMEOUT)
0 X$ _8 o; N2 c. ]8 D: ~/ h. I - {
* T6 w* o& y, C# j" _5 n - return HAL_SD_ERROR_TIMEOUT;, U* d a5 ~" `
- }
+ G" U2 L/ k5 I, R9 a9 y' { - }
复制代码
; {4 E& |3 c& q7 k- V9 z后面我将GPIO改为GPIO_PULLUP后解决了这个问题,具体原因暂时还不清楚。5 S0 w0 C$ h* u- ^# S6 } l$ {
! S- c% k: R0 F- GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
1 g* ?! C( |: R& x% J - |GPIO_PIN_12;! b B& y* {( [. C0 G% p
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;" O/ G: ?! K' e' a% F
- GPIO_InitStruct.Pull = GPIO_PULLUP;//GPIO_NOPULL;
, a% j* _5 x# r+ u+ \: ? - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;4 L' d2 u8 D% a2 }
- GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;
" D( a F8 F- H0 z# W6 I1 |- }* y6 Q - HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);/ C! X8 f; y; p0 H) W. y
- # w+ b2 o' q& Z- _' e6 j
- GPIO_InitStruct.Pin = GPIO_PIN_2;6 s# t# A. i( i
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;2 S" _) z+ V7 U
- GPIO_InitStruct.Pull = GPIO_PULLUP;//GPIO_NOPULL;" b: z$ C3 g$ Y: _
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;" j; O8 y" ?+ m3 q7 N7 w
- GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;
4 U# U; j! j y$ ~* \' ? - HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
复制代码 ) |, v! y( v. @
简单Fatfs应用9 \. u& X4 V$ C5 s2 T8 j% |( r
- void StartDefaultTask(void const * argument)* s+ `+ J/ i5 q, L) z
- {
& H- Q% c5 O6 Z' B& @6 c1 z - BSP_SD_Init();/ g6 ^& `/ a( | A
- /* init code for FATFS */
: v, |1 i$ W- t; ?& @7 I: A - MX_FATFS_Init();
% v P$ a7 x5 A2 }; R
. v7 t" e# G6 y5 [0 S' K) @- /* USER CODE BEGIN StartDefaultTask */+ o- D0 A4 B8 t0 L4 V
- /* Infinite loop */3 |0 Q3 m, @: O) n% b
- 5 B0 @! M% C' B) i+ C6 l" b7 a
- for(;;)
8 L- ~' Z" w# f5 W: p - {% {5 j$ C% `9 y* v
- /*自己实现一个Run方法,每隔5秒往SD卡中的文件写入数据*/
/ t. T% }$ v# c& G - MX_FATFS_Run();/ m' O( ~3 z6 P
- vTaskDelay( 5000 );
5 ?9 W1 g8 t" I, d - }+ S1 M5 c2 l- E2 [# k
- /* USER CODE END StartDefaultTask */' g$ N% ~- y/ ~' v8 j' p7 O6 w
- }
; Y. g$ Q) R6 C5 B# d( S
; j, ^ a5 n, D' G. d: i) s- void MX_FATFS_Init(void)
* f3 a$ E, Y3 t6 }8 g8 {, C - {
3 s7 J0 k7 M; l7 F% Y" m* ] - /*## FatFS: Link the SD driver ###########################*/9 f6 ]/ v! B$ T6 o# m1 b
- retSD = FATFS_LinkDriver(&SD_Driver, SDPath); H/ x: w/ R7 n- \
0 _& p$ z9 [) `1 t- /* USER CODE BEGIN Init */
! M- {0 w- q$ Z: q: ?0 _0 I - /* 在Init的时候挂载文件系统 */ 4 w6 B6 R) d2 U3 E
- if(f_mount(&SDFatFS, SDPath,0) != FR_OK) - w/ G* B# `, _4 @
- {
+ d2 Z) ?% c- C- @) D$ s. M( T! x - while(1);% h6 X$ V: c) C8 o
- }
( ~5 Y; Z O1 O" P. w: C# \! ? - /* USER CODE END Init */( X4 A' v5 l4 B- X- s3 ]
- }, A8 s3 P1 ~' H% K
- - w2 X5 q; |+ p
- void MX_FATFS_Run(void)7 h$ S+ S$ f0 K" O0 {8 Q
- { a; f8 y+ C8 `
- UINT writeBytes;" p* C' X4 S9 W" q) Z. _
- /*文件系统基本操作:打开文件,定位到文件结尾,写入内容,关闭文件*/7 C9 W5 [. c9 ]+ b5 b! Q
- if(f_open(&SDFile, "SDTest.txt", FA_READ|FA_WRITE|FA_OPEN_ALWAYS) != FR_OK)
. C3 M, W( E( I. A9 ], O1 X - {1 @9 }% a2 o& k, ]/ |
- while(1);
* `2 U: z8 Q0 M, Z- U0 l6 }: ` - }8 U/ d" P& J I$ h0 y0 M$ Y1 p g
- if(f_lseek(&SDFile, f_size(&SDFile)) != FR_OK)5 @0 N6 m: ^. o4 O- p* g
- {; n. g5 e5 I6 h' B
- while(1);
* i2 m% c* _7 s( w - }
+ H3 I, h4 \8 {' {, i - if(f_write(&SDFile, "sd Loop", sizeof("sd Loop"), &writeBytes) != FR_OK)& O j" f. m) y" w3 i; O. g) \8 M% Z
- {
' i: G! q1 ]% d! |! |( }& r9 H - while(1);2 u( {% |! E9 v7 O, T+ `
- }
9 z! F! Y' E F* }' D - if(f_close(&SDFile) != FR_OK)4 x9 t- c( d) ^# L, ]$ [
- {
% l* Q4 ~0 l' d, Q4 O$ a - while(1);
7 F, }& h" m2 k5 ]$ E3 j7 u: i - } * [/ Y& z7 i8 g$ `- U: }
- }
复制代码
1 v4 a' ^4 U! A0 L% p/ D8 P8 @2 I! \SD写入结果:& n* o3 }' [6 i5 [
9 [! G+ x! |) U4 {2 l/ j! u' D
1 H# m; G6 n5 M9 }& \
; y: a+ z, L' ^: L
- i2 X" H8 C% L4 Q
|