简介3 p" D4 Q( t" C. v0 }% Q4 Y
最近项目中可能需要使用到SD卡,所以需要对SD卡的配置和使用调研,在配置过程中遇到了一些问题,在此记录一下。1 C! A( D( z" W
7 N R7 ]. B/ e+ F7 k& m$ c
STM32Cube配置
* c, z6 s2 q8 g3 t; {# S0 rPinout5 Z& Y8 r u: ~) B+ j, y
" S0 B0 q5 X+ p( K+ Q1 D. Q
& ]8 K, e/ @5 m6 l8 R% o
: c- W! |2 ~; z% ~0 ~* k
( m! v) e4 P, L4 Y
! I: l- s5 j% e" l6 z5 s# d; C只需要注意绿色部分的设定
) D- V+ V! X' F1 `, l# f. F2 f' K# N3 _( T- k6 y
Clock配置# @# `: U+ p* l' _& v0 X
: w- A$ n) D! y9 O7 ~6 n$ {1 O5 W$ H# n1 f: H0 i9 g) ]
这里使用了最大的Clock,SDMMC1的时钟是48MHz
! P2 x/ v. ?5 r- F0 B6 [4 I: t
- l1 i- @) [ e" J7 jFATFS配置
; {4 }& L( x9 u! O v. |4 u
/ g, H& }; l5 E/ E V/ Q' Z
* h$ s' k+ Y2 @, a4 d1 I) i9 j. M6 Q: s* @$ j& ~
Freertos配置- i/ c/ M! u( p" b8 T
- g! ?6 \8 c# e8 q) |, l
8 r9 h2 S( T( b5 O3 ?9 ~) ?6 n3 r4 |7 x
这里增大了Heap size,使用了heap_4的内存管理方式。 i7 ^2 o; u0 s
8 J( Y, }8 Q4 A! l3 t" }0 ]3 r# F0 j
SD卡配置
7 E$ g: `5 S" o9 ?* w& c" q& \/ M: D; j, w8 R( _- q" r* r( M% p: t
4 z: v+ j& n; }7 B" D$ W
" b+ s2 p) Q; M8 y4 X+ G: M" u* q9 A4 \- z% y s$ g
# V2 I, u$ F* \1 h
在这里打开SD的全局中断,并使用DMA2的方式传输数据
& ?# A4 B4 K& ^# f* f' u. e+ j7 ~4 W8 f+ m3 \) @5 X5 V3 f
NVIC配置0 i" K6 |' ~1 }# ?% O' X" H
+ ^. M* t. J/ V8 g$ h3 J) N
8 t6 L; ]; g# }
0 c9 a: B# }6 h, S, DSD的全局中断配置为5,DMA中断配置为6,5的优先级更高。
/ o! F; t' Y/ z a$ t- [) R+ j% `$ A6 f6 C3 h! F& |
工程配置
' \! w; d! ]5 @' M
- ?* i$ f7 H6 X( v+ ]3 p3 B7 F6 \# L0 u& |+ j a2 r5 C
1 O6 C4 `5 }+ h% B0 M c
. D) W }2 z3 j7 L) H
! P' P0 \8 R' h. ~# @
以上就是所有的配置内容了,配置完成后,直接使用Code generate功能就能自动生成keil工程。4 z2 I! M" S* k. w2 D
3 j- Y" G J! q6 A
代码修改
" p% X6 S7 q5 m+ Z% ~8 M初始化SD卡 }# k' R, v) D5 M6 S) h
我生成的工程中需要手动添加BSP_SD_Init()函数,我是在MX_FATFS_Init()之前添加的。
+ w: g7 b' c2 ]; I- /* StartDefaultTask function */5 _9 ^" t. Y# p; f' J( o& K6 c
- void StartDefaultTask(void const * argument)
* X3 [# }$ y& K3 X6 h/ U1 ? - {
1 A& K2 W7 [# X - BSP_SD_Init();+ s. L3 V' r7 n4 w8 T0 k1 l
- /* init code for FATFS */- P5 f5 D* z7 U4 k1 @8 \
- MX_FATFS_Init();. z8 B1 T- a# t/ c2 Y
- + ^- P- {% E) z1 d/ u
- /* USER CODE BEGIN StartDefaultTask */
: d! i% m" K& i' j - /* Infinite loop */, p9 j% g# J3 m+ @2 X& O
- for(;;)
# t z9 F0 O( Q5 X0 z; d+ R - {
( v& a: m7 T& T7 n j* E* | - MX_FATFS_Run();
( Y a1 C( N( [" p7 o" k3 }/ c6 Q% } - vTaskDelay( 5000 );8 s$ e( e- a9 i2 N' A, w% p" ]
- }* [3 o, T( o. ]- N( ~. _+ h" d
- /* USER CODE END StartDefaultTask */9 x* ]' c l1 F
- }
复制代码 4 O3 M6 H) V4 \
添加DMA中断处理函数$ k, g' W Q$ ~/ w: Q2 k
stm32f7xx_it.c文件中,需要添加下面两个中断处理函数:* E# }& y" ~/ {
- void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)( U+ U' J3 u. b. f7 r' a: O
- {# `: w& p& ] A$ e1 S' x
- BSP_SD_WriteCpltCallback();
" f* k9 u( g! a* T) N! U - }
! ~2 A$ O4 G8 _! Z - ( B1 y' R$ s$ P8 Q3 h
- void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)9 t# t, U& h0 D, O
- {6 T7 |4 g6 A9 [
- BSP_SD_ReadCpltCallback();+ i- U! W, c8 u4 b k! j/ |
- }
复制代码
& f& l' }( r7 R- E7 m创建Queue
$ {' o. B+ v# F% G' G8 i8 w在自动生成的Demo中,使用了SDQueueID这个Queue,但并没有自动创建它,所以需要手动创建一下。我是在SD_initialize()这个函数中创建的,具体创建的地方可能需要再仔细考虑清楚。
; Y. V. i1 Z8 P- DSTATUS SD_initialize(BYTE lun)
- J8 X2 h- q% O - {
3 {4 n2 ^5 M$ x5 m; ^ - osMessageQDef(myQueueSD, QUEUE_SIZE, uint8_t*);
+ y+ v( {6 ], ]4 F3 O1 k - SDQueueID = osMessageCreate(osMessageQ(myQueueSD), NULL);+ l7 I: z C" u3 y( ]( o) @! d
- return SD_CheckStatus(lun);$ v9 V1 X% n; k4 N/ ^* C
- }
复制代码 v* C4 W3 }7 _
将Pin的配置改为上拉; P- Y! k, c' K( F P+ W6 F
在默认配置函数中HAL_SD_MspInit()对SDIO的Pin的默认配置是GPIO_NOPULL,但我在使用过程中发现,程序会卡死在SD_FindSCR()函数的以下这段代码中:1 E) g7 M/ z. r+ k
- /*卡在这个循环中出不去*/ L* n# ]7 G. h+ ~
- while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DBCKEND))( y x1 u6 \3 T" J# F4 T
- {
4 L) y4 _# w6 t9 O - if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXDAVL))' @, g& G' }; ^8 }/ o( |
- {0 | x" f n9 h6 m% j/ {, y
- *(tempscr + index) = SDMMC_ReadFIFO(hsd->Instance);
# B* C, @/ P4 z - index++;
$ s8 \% [7 E: F* C3 k) ` - }
r: D2 c& ^2 y, _ - 6 ]/ J1 L2 @/ o0 ?) ~8 H
- if((HAL_GetTick() - tickstart) >= SDMMC_DATATIMEOUT)9 c' A+ c) D6 q/ c! j+ x% q3 B
- {
9 e9 g; o0 B7 p- n. K8 r. N - return HAL_SD_ERROR_TIMEOUT;8 }9 {7 v Q0 P8 a# \
- }7 G& S+ E' T3 y. J4 f0 G2 R/ p
- }
复制代码
& y4 K: u! \& \! I* a后面我将GPIO改为GPIO_PULLUP后解决了这个问题,具体原因暂时还不清楚。6 t2 F& J" F- Q8 U
) X4 t3 i4 C0 o% ^; o
- GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
' C7 ]% a3 x9 Q8 u - |GPIO_PIN_12;
' K+ v5 ^. d3 v; N6 m6 R% E2 Q! c - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;3 |. h& w6 `5 p; x |$ E
- GPIO_InitStruct.Pull = GPIO_PULLUP;//GPIO_NOPULL;6 q$ X' B5 Y9 R) O* P
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
- Y: \6 w7 ~# T- a3 s4 S - GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;
8 I; m9 b3 c( Q" d1 j - HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);: a5 P7 \! v. H
^$ z/ {7 @" z. ~* [/ X- GPIO_InitStruct.Pin = GPIO_PIN_2;
& C: a" R, ?( J8 n* b( j - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;9 ^8 w, I* ?4 y' H/ w
- GPIO_InitStruct.Pull = GPIO_PULLUP;//GPIO_NOPULL;
* k+ ~ \5 j5 q - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;& q/ C0 W9 h( v1 A& _9 e
- GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;
( N v# k3 [2 r. V& s; C# D - HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
复制代码 3 B; l+ T" X* g# S# g9 m6 O
简单Fatfs应用
: S) V/ i9 P: U" `: ]! h$ ~% \3 h3 }- void StartDefaultTask(void const * argument)
( V" ~3 Q* @) L' Y* e# R" q - {0 `. x& Z: Q3 ~8 B7 ~6 \
- BSP_SD_Init();
' |% |: q# L9 H' w, |" q8 c - /* init code for FATFS */
- R4 R6 }* Z) u( H( r ]! u - MX_FATFS_Init();- ]9 S6 g0 {9 Y# k$ @; o
* ^, p) B0 n& z& ?- /* USER CODE BEGIN StartDefaultTask */
/ T! }1 V4 q' A% b: x - /* Infinite loop */6 V2 s* T8 i) s" B3 X
- , D& @* P; G( e! c( m! y+ p
- for(;;)
4 X, B( ~/ A4 q - {) w+ r% U/ ^7 v a2 L$ O; s
- /*自己实现一个Run方法,每隔5秒往SD卡中的文件写入数据*/7 R+ \+ p5 U6 M5 P
- MX_FATFS_Run();
# F$ g. x. d9 g% H - vTaskDelay( 5000 );
; {. G4 g. A7 y8 P* o - }; A% h# I% j# U) ]8 V2 @
- /* USER CODE END StartDefaultTask */2 N% k0 g% [$ d$ d, A
- }
5 \ Z" ~4 h% i: r - + x/ y O; z/ O5 H% f3 y
- void MX_FATFS_Init(void) 9 A5 p- m+ O* H* W* V
- {% f8 ]* K( f3 N; e/ p
- /*## FatFS: Link the SD driver ###########################*/
' \5 j6 w. I7 S8 f" ~' w - retSD = FATFS_LinkDriver(&SD_Driver, SDPath);
' W+ L3 a' h- w; X, g' W - ! l, e. R; r2 x1 `- A L t! t6 ?! [
- /* USER CODE BEGIN Init */1 X5 v; D, }4 e1 Y: K: v& F+ F. S) z
- /* 在Init的时候挂载文件系统 */
2 U5 x: v. r5 |/ N - if(f_mount(&SDFatFS, SDPath,0) != FR_OK) $ N) u5 [; C. T
- {
* z/ k8 S: i; T' l o" Z - while(1);6 d a: M6 H, @- Q5 W2 ]
- } ; p# N# G# }; D! z ~8 C
- /* USER CODE END Init */" u1 Y% i) K; m# s9 I* q
- }1 ]& N. y+ z+ F( J5 |& p0 }- z1 |" i
% Q" \3 u0 v1 E# c. W2 B) w* a- void MX_FATFS_Run(void)
( ^" R7 R5 p3 c# A: V ` - {! F# V V0 b* }/ H
- UINT writeBytes;3 a# r! K3 b. @6 B5 E4 L( M& x
- /*文件系统基本操作:打开文件,定位到文件结尾,写入内容,关闭文件*/
$ \9 h! ] }$ j2 R* @ - if(f_open(&SDFile, "SDTest.txt", FA_READ|FA_WRITE|FA_OPEN_ALWAYS) != FR_OK)6 i: p; z% l* Q$ b, ^
- {
# g, y0 w P# B0 N5 X - while(1);: q& h( l m& Y
- }
' L; _% z9 x- j; c - if(f_lseek(&SDFile, f_size(&SDFile)) != FR_OK)
9 x n. O) q% p0 _( [. A. T9 z - {* [& o$ r9 l" I, ^0 t
- while(1);. A- a+ E1 p+ G# i4 Q4 S6 u
- }( z8 _: H( J8 c8 k# V
- if(f_write(&SDFile, "sd Loop", sizeof("sd Loop"), &writeBytes) != FR_OK) X& z5 I' P4 h, l9 y2 p# {+ B
- {
# w7 n' Z/ r6 t w - while(1);
* Z2 M4 C Z8 H - }
2 r+ _3 v3 W9 @7 s& h2 B - if(f_close(&SDFile) != FR_OK)
. s7 o7 W; e1 e4 Q6 m2 X9 M - {
) z! a7 K( V$ G W - while(1);4 T e, C! g1 e- ?2 F$ M/ D
- } 4 f* J# {7 ~* }. J. O3 A
- }
复制代码 4 f5 Q; k/ ~5 V- a& F5 ~6 D
SD写入结果:
5 r3 Z) V& P' Z! S4 I
: o' F1 c4 D# ^0 |4 u2 b( T) h5 l" J# t% E0 O% r6 I8 Q( d
' X1 A: }! z1 z0 C9 x8 M
4 f9 ^8 x7 A6 `# {; f y) \
|