简介9 E% I# b% T" U+ j
最近项目中可能需要使用到SD卡,所以需要对SD卡的配置和使用调研,在配置过程中遇到了一些问题,在此记录一下。
5 H! U0 z6 D, s) ?, J/ ~) S% J& }4 i. W: ? |& E& ~
STM32Cube配置
7 g* G( G$ `/ f$ LPinout
$ N5 x4 u5 r/ S: N/ X# N# c
7 f5 ~& A' @9 M. o9 o4 T( t2 e2 x7 o+ V. \
& c( G, K' B7 }: |
3 H# ~4 {+ u6 H& ` }: l
! c7 H0 r5 W3 }9 \# |# f
只需要注意绿色部分的设定
2 i) R* v/ D7 R) c: z! D1 |0 M1 o* i! }+ R; j5 ?5 \. R
Clock配置' a1 }. j8 E5 L0 n. {/ o
- O( a4 J% {1 U+ ^. ]
2 p" ]: G' k9 E" c, N- W1 G这里使用了最大的Clock,SDMMC1的时钟是48MHz: r# E' b& i& s! Y; c- X
8 w! _: J; p7 `! ]$ C: yFATFS配置( K& h- |$ Q7 v' S" f Y- ^! a
% S6 x; C9 A t
' v; N6 R) A3 j2 U
7 e9 \8 K0 o3 K
Freertos配置
- U% _5 R# A) k6 q8 t9 f
: I3 l: Z8 v# k3 _ X' n, Z
7 }9 S3 F# U* s' G( g3 w/ k7 g* ]
6 S1 p' W o2 k8 z* d _7 }4 l% @0 k这里增大了Heap size,使用了heap_4的内存管理方式。
# q& E m H3 d* B
% h `. T$ e9 ]% W8 {SD卡配置
9 w- c6 x$ t' o3 g$ T% a+ |) V3 c* R# \. t
$ R3 ^& Z7 i3 b( ?. w5 F
) P) F- V' g8 v7 ? {* Q0 V# Q$ t9 P+ ~9 K
* D* D, J# ^! \7 e9 Y
在这里打开SD的全局中断,并使用DMA2的方式传输数据4 y5 w, j% v' _* k3 O4 v5 ^0 o# M& I
+ u4 q4 R p# e4 H+ aNVIC配置
0 @7 \/ w5 ^7 U9 }, Q9 W- _8 x* Q1 D
9 o: I3 k! t' M. ]5 r. I0 C
) F' ^! b: _" o1 xSD的全局中断配置为5,DMA中断配置为6,5的优先级更高。3 @/ h8 g6 J2 e- T- B. r8 S( W+ C
. |+ p/ i: t7 z+ O% i
工程配置
- j1 C$ U* ~7 n% g5 g: f4 |) f1 U+ J
: i5 W- Q ?# @; a* a) |! S0 H: N/ V4 p, V7 `. t
- I9 S7 X1 h( d/ D. n! d3 E
' Z9 r/ X! L% g0 @( N! I以上就是所有的配置内容了,配置完成后,直接使用Code generate功能就能自动生成keil工程。
7 D _$ E" }9 q* _
' P9 F( E6 }6 s! z& W- X& n! N代码修改2 \( n2 b/ i2 |" ]/ [3 S- ]
初始化SD卡
% m1 B1 T* A6 E; C# ]. ^4 |) E我生成的工程中需要手动添加BSP_SD_Init()函数,我是在MX_FATFS_Init()之前添加的。
/ r0 i8 J6 Z6 p. ]1 K4 |- /* StartDefaultTask function */. R; g9 c4 w: y% H5 l! g
- void StartDefaultTask(void const * argument)
^& h4 Z( a& k V: `! k* g( d - {
- K! Q9 ~/ E1 k6 M - BSP_SD_Init();9 Y+ F4 C9 m; o# u/ F) e
- /* init code for FATFS */8 K9 f( b$ S8 `! K
- MX_FATFS_Init();
0 a( B& x$ y) s+ t6 d0 k c9 K
( }9 s- i4 i& g- p2 v+ i! I- /* USER CODE BEGIN StartDefaultTask */
0 f; P; B. F1 p8 Y: [8 C - /* Infinite loop */
) C N9 H; c% k) [ [ - for(;;)
4 \* u+ v3 Z f6 x$ H - {3 t) f; O* a$ `, x+ \# w' C+ P
- MX_FATFS_Run();
# r2 K$ S2 [9 T3 X' M# X2 Y - vTaskDelay( 5000 );9 X+ u# i$ n [% r& y) m6 l
- }
2 h+ b2 b6 u0 A4 S. i+ W - /* USER CODE END StartDefaultTask */# T# @# s+ `% l1 N) U3 E
- }
复制代码
2 [" Y+ w" K; q1 Q4 l5 Y/ S添加DMA中断处理函数
' G( [+ I7 {3 [- |7 |5 vstm32f7xx_it.c文件中,需要添加下面两个中断处理函数:
8 i! C8 S! \9 G$ m" n9 K; \. e- void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
" l. z3 e# W: L, i - {, O# Y; ~- Z* v7 x) k( X L
- BSP_SD_WriteCpltCallback();
/ z6 m2 l S" _, u5 G - }
9 s5 H6 k3 t& o% A+ g2 g' {/ ~; F) N - 9 k) r; j/ A' e6 j* ?
- void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
& T0 i" E3 Y8 d2 ]$ A2 X: ~, e - {
% W2 R. E$ c# @ - BSP_SD_ReadCpltCallback();
5 c/ q7 I* F( Y) X0 X - }
复制代码
, q! e# `2 R( d$ ^+ v0 T( S创建Queue6 E% M9 D( _% ~
在自动生成的Demo中,使用了SDQueueID这个Queue,但并没有自动创建它,所以需要手动创建一下。我是在SD_initialize()这个函数中创建的,具体创建的地方可能需要再仔细考虑清楚。. q8 z* a) Y* V9 C
- DSTATUS SD_initialize(BYTE lun)% z F6 n8 x; a' H; d: s/ h
- { \" }! ^7 ]# W& U; q
- osMessageQDef(myQueueSD, QUEUE_SIZE, uint8_t*);
! @% b' \) e- c" K - SDQueueID = osMessageCreate(osMessageQ(myQueueSD), NULL);" O, U6 ?& O- i0 a- L& `
- return SD_CheckStatus(lun);* j1 b, U/ h+ G: F
- }
复制代码 . }" G! m9 h3 i' D S4 m: V
将Pin的配置改为上拉. G5 R' |) Y' o2 c- n4 S3 j8 q% h, U
在默认配置函数中HAL_SD_MspInit()对SDIO的Pin的默认配置是GPIO_NOPULL,但我在使用过程中发现,程序会卡死在SD_FindSCR()函数的以下这段代码中:' x6 Z( s6 H+ r+ {- x0 j# V
- /*卡在这个循环中出不去*/2 s: x2 Y" ^/ }$ T7 J3 O$ }
- while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DBCKEND))* N/ @% z6 `0 \
- {
8 `8 o2 _$ m) m& f# J/ X2 f - if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXDAVL))" \; Y/ R5 c' J j$ K& l- C
- {
: e) d. |: j0 F- d8 [% e" O, ~ - *(tempscr + index) = SDMMC_ReadFIFO(hsd->Instance);
G* l: g, K8 U% ?8 ~1 \( e# X - index++;7 o( v2 K8 w! t- v# |
- }
! P0 J. A4 j3 T! o - 2 |& R" w$ I3 R+ c1 C9 i; k
- if((HAL_GetTick() - tickstart) >= SDMMC_DATATIMEOUT)
$ _' K8 T2 k7 p% ?2 X- } - {
6 h3 u3 i# l' O; D - return HAL_SD_ERROR_TIMEOUT;
: d1 L7 q1 e% n" y4 X& L - }0 `& y& J ~& O& x+ W) H
- }
复制代码 # x3 p8 `' l2 g
后面我将GPIO改为GPIO_PULLUP后解决了这个问题,具体原因暂时还不清楚。) c7 F" k9 Q. q' O. O' W- [5 `
; R7 X- s6 [% q- GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
4 V+ ]2 W! O% ]: t+ U6 `/ \ - |GPIO_PIN_12;: \: G% u9 u, W7 Y
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
- Z$ T7 s# o6 n, p- J/ ^ - GPIO_InitStruct.Pull = GPIO_PULLUP;//GPIO_NOPULL;
$ D0 q% x B% f1 W8 I/ g+ m - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+ _: `4 z7 f8 t! p3 A* V3 p: e - GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;
6 b B. w' J% ~0 V3 M" O4 j - HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);+ N/ L7 S) `$ i) B" |
3 _* N' G; k& p1 [' {* }$ g( l- GPIO_InitStruct.Pin = GPIO_PIN_2;
" A7 U1 v6 D: u - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
, l/ S2 ]3 Y% p - GPIO_InitStruct.Pull = GPIO_PULLUP;//GPIO_NOPULL;
- Y+ \ M5 r1 O' |$ C; C - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;7 r/ F1 J9 l. y5 O0 R$ C
- GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;1 U/ w9 h7 ~! o( p; L* N# Q R
- HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
复制代码 ) M$ ?0 t+ L0 F
简单Fatfs应用- Y; `& J1 M# N" q
- void StartDefaultTask(void const * argument)
' Z6 d. I2 [9 q3 _0 v% A - {
( }7 x8 w* L! n - BSP_SD_Init();
: {% E' _' O7 S; a - /* init code for FATFS */
! _1 w% a& B9 y# k) ?& F0 e( P - MX_FATFS_Init();
4 R' u7 A+ T; K - ( W9 D& K v( h0 B* Q
- /* USER CODE BEGIN StartDefaultTask */8 i. ^# H B1 v
- /* Infinite loop */5 H% `# A, Y5 C. ?8 g [
, S4 v" f- m% I- _5 H: [- K5 |4 A- for(;;)
9 z, H1 a( `) q$ x/ z- R/ Z( f - {* j- q4 n- r" n* X( Q
- /*自己实现一个Run方法,每隔5秒往SD卡中的文件写入数据*/! Q2 u! p6 }6 v1 x. ~- v
- MX_FATFS_Run();8 n9 X' Y" E8 Z6 r. Q; ~/ J7 v( W7 h
- vTaskDelay( 5000 );
* O$ T, N4 z* E& D m/ H: V - }
& z9 C# f3 I9 C3 }6 e - /* USER CODE END StartDefaultTask */
) [# e, s6 ], H6 H5 r1 h/ S* L1 n - }& _ |! C& @1 H7 K C: X
+ {* e; T1 G S' b8 Q- void MX_FATFS_Init(void)
5 i' {: O, p/ b( y4 J6 P4 o - {( f- [. C& Z( _! e$ o
- /*## FatFS: Link the SD driver ###########################*/
( F2 Z& m/ d( u! J5 z - retSD = FATFS_LinkDriver(&SD_Driver, SDPath);( J% Y& W9 Y) w1 U: f
- . Y `. R1 S6 ]1 I) b
- /* USER CODE BEGIN Init */) T' v* J" s g, q# y% A2 h
- /* 在Init的时候挂载文件系统 */ , Y4 s5 _! z+ z$ S+ p( P4 [
- if(f_mount(&SDFatFS, SDPath,0) != FR_OK)
0 d$ L$ }7 ^* D6 u( ] - {
- Y) p0 u+ e8 G/ m& y/ U - while(1);# p, v, r8 L$ A
- }
2 r4 f' S9 P4 c2 o+ R" ~ - /* USER CODE END Init */
0 n8 P$ L. Q Z' | L# o2 d - }) V2 x2 ^8 Y$ i9 x3 h0 Q
- - E8 Y. N+ Q" k& t* R8 c" N% ?
- void MX_FATFS_Run(void)# D8 K y8 V( R h
- {0 s5 X- U% `* P) p6 X% W
- UINT writeBytes;
7 \1 |5 \3 K: H6 O - /*文件系统基本操作:打开文件,定位到文件结尾,写入内容,关闭文件*/
4 z) {6 s. G V6 o' C - if(f_open(&SDFile, "SDTest.txt", FA_READ|FA_WRITE|FA_OPEN_ALWAYS) != FR_OK)
- v% i7 H6 R: Z0 S# Z - {! j7 h. C. V) A9 y0 @
- while(1);
; `/ B% f. y1 }9 k - }
. x0 A' ]# @, H1 G+ T - if(f_lseek(&SDFile, f_size(&SDFile)) != FR_OK)# W7 X7 R5 P3 b& U7 L- V( H' d
- {
L8 a V9 Q1 l" \! } - while(1);6 R* e% j" b& ]8 w, U# ^! x! s
- }: `1 F2 R4 K9 ~2 ]: h
- if(f_write(&SDFile, "sd Loop", sizeof("sd Loop"), &writeBytes) != FR_OK)$ @6 r! i8 n' k' u0 s7 e. x1 v
- {
, g6 F4 y& t6 \ - while(1);
0 p0 f3 A+ Z" u$ L8 Z q - }& W5 U d. A3 }7 w7 ]" o
- if(f_close(&SDFile) != FR_OK)
% I* w/ ^' `8 l% i" n - {% S# F0 ]) ?! T7 p- E$ C3 \
- while(1);
8 I+ u5 _9 A4 [/ ` - } * E6 M4 v ]7 ?: v1 F& `3 Z+ e; _8 z
- }
复制代码
0 Z! I' y" V. A* L5 F9 Z/ `SD写入结果: t$ D+ \1 V3 }7 I3 T
1 ^ U" _; m) [' E1 A# T2 H
9 O$ P: o4 `- g. R, Y1 N% |& ^) y0 q
% @! N2 ~+ Q$ O, F' j7 g |