简介3 E" X @) B2 i/ M) R6 \- q& i% K
最近项目中可能需要使用到SD卡,所以需要对SD卡的配置和使用调研,在配置过程中遇到了一些问题,在此记录一下。
/ Z }& z# g5 U {5 I1 J0 L* \: d- P$ s' j
STM32Cube配置; D/ m3 c' ]. r" W
Pinout
: o. V* d$ E" E% B$ H
5 c5 O- G7 o. V3 N& T' i- B1 Y- m7 b0 L% l+ u* B: N) @
. V) s5 w% M2 R+ V, f
* H# c2 T& ], c1 C$ j6 h
+ f& f" m; A# c2 G9 o" c( T只需要注意绿色部分的设定
' M* M% G% m/ Y( X
* d8 @7 C) Z7 M) U1 _0 oClock配置) A1 d( x& d$ {6 s
: G. S% H/ L# u5 z) {/ n
/ p Q) U) x! M7 n这里使用了最大的Clock,SDMMC1的时钟是48MHz, x+ A; K5 o6 U5 b% g
# N* q1 ^& K: ?9 y6 ^% k
FATFS配置/ U: L" U5 i: H' m ]% A
d, ?) E2 ~* c; k) s
5 C3 P9 ?6 r( r- T+ Y
% K1 f+ ?1 R9 `& h
Freertos配置
5 l$ C3 t0 u7 O4 X
& r4 F( M, ?3 a' @) u3 D& ?/ T
# l9 t$ O. X( k' G z
2 K/ K4 c; F* U这里增大了Heap size,使用了heap_4的内存管理方式。2 `: W' W# E9 d. Q& }
) U9 B- p/ f" ^4 y7 k$ \- w: CSD卡配置
" D7 g5 o9 j, R$ B* i) M, \+ z1 z& G! E; R! a
3 W* x, `- d% t- L9 O
( d9 S e' d" A( U, h$ h1 R; r1 m+ Q0 }- S+ [+ a0 w
( g9 t. Y% o! T! B7 R在这里打开SD的全局中断,并使用DMA2的方式传输数据5 y0 h1 \5 { H( e3 F8 P4 e- [/ b
0 F) k0 Q! i- x8 V8 R
NVIC配置
9 A6 h8 }4 e" i2 h2 h- ]# E6 Y/ ~$ H$ j, r) S; ?
9 q; ]0 o4 F) A" `
2 m: A3 a( S; p$ H% _ c7 h3 oSD的全局中断配置为5,DMA中断配置为6,5的优先级更高。( R' C/ K5 L |* z s3 n( u1 d8 _
$ ?7 B$ U" q7 j5 i工程配置, S; |8 A" U1 Y1 |3 \. V
2 ]! v. X6 L. f- W( U; j. n5 C
% P1 l4 x( k5 n8 Q+ e) \3 p: t; a+ ^! g# {+ d' g0 f
. u) }. z. {9 O z' T
; P0 k4 J& A( {" t
以上就是所有的配置内容了,配置完成后,直接使用Code generate功能就能自动生成keil工程。( ~4 x/ I# K3 ]% X
6 z) S6 B. ]/ c: ?6 l M& [) E# z代码修改3 {( P8 J" e% q' I0 [
初始化SD卡' x, e/ q* U0 p- N! N" r
我生成的工程中需要手动添加BSP_SD_Init()函数,我是在MX_FATFS_Init()之前添加的。4 |1 v+ D' W+ L: J" C8 i8 Q6 a8 U
- /* StartDefaultTask function */0 X8 W! Y$ z- a6 g
- void StartDefaultTask(void const * argument)- P+ v, p' }+ w' C6 u( w
- {! V5 g& j0 h1 m; }1 R
- BSP_SD_Init();
3 o5 D @" C5 K( S5 Q1 N - /* init code for FATFS */
. G( G( W( _* S6 F r6 L6 H# _ - MX_FATFS_Init();
" R9 R3 }. R8 R2 s* z& H( D* K
% m4 e+ M4 F, E$ o- /* USER CODE BEGIN StartDefaultTask */8 r/ `- F. }0 \% a
- /* Infinite loop */
% A- x1 ]9 r s# c/ R8 i - for(;;)* X* [, g, n5 x' a5 H2 [. v
- {
7 [ ]* i& T/ ]' P( T1 p - MX_FATFS_Run();
. N# R! \0 m9 l8 S8 t1 l. \ - vTaskDelay( 5000 );
4 O1 i2 y; N. B) }9 O1 L - }( r. d- A- H5 N! i) X/ s2 S5 ?0 Z5 v
- /* USER CODE END StartDefaultTask */3 u) L, W, o- x A0 O& q. i
- }
复制代码 - p7 Q, [7 ` S: P4 |
添加DMA中断处理函数
2 `# |0 \9 s7 ?+ a& p/ |stm32f7xx_it.c文件中,需要添加下面两个中断处理函数:) R& _/ ^* A; Y, g
- void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
) F$ L8 p1 }. i* `7 _ - {
! p* M* F" R2 m - BSP_SD_WriteCpltCallback();
/ D+ r7 x, L8 I( |, O - }
( G. v) `" }: L7 N) N! B; m
4 i' a3 O& _$ _9 F$ D- void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)" n0 Y. O2 a( I; b y9 Q& _
- {) [ o; u5 ~( G H# s* Q. B j2 F. |
- BSP_SD_ReadCpltCallback();# ~9 y$ ~* J7 ^ g) \0 H
- }
复制代码
; t+ z) A. W4 S2 ]. X9 H% }1 @) O$ v创建Queue! z0 o0 C+ ^4 G/ ?3 U8 G" L, ^
在自动生成的Demo中,使用了SDQueueID这个Queue,但并没有自动创建它,所以需要手动创建一下。我是在SD_initialize()这个函数中创建的,具体创建的地方可能需要再仔细考虑清楚。- t) |7 K3 S0 L' x: D
- DSTATUS SD_initialize(BYTE lun)- n0 \ W: p7 \# K( }
- {
1 g. A* i8 C# o' P; H% U& q8 P( r - osMessageQDef(myQueueSD, QUEUE_SIZE, uint8_t*);
/ {. r' c1 D! i8 T - SDQueueID = osMessageCreate(osMessageQ(myQueueSD), NULL);
( l6 s' w: u0 J/ A j/ B - return SD_CheckStatus(lun);+ ~! @, i D1 ?
- }
复制代码 9 }( r$ W1 }# A6 D
将Pin的配置改为上拉" n; _2 l" P- h, b0 x1 {9 r
在默认配置函数中HAL_SD_MspInit()对SDIO的Pin的默认配置是GPIO_NOPULL,但我在使用过程中发现,程序会卡死在SD_FindSCR()函数的以下这段代码中:, M; {+ j# [0 A U- S
- /*卡在这个循环中出不去*/
- T0 _: N0 g7 d/ x - while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DBCKEND))
9 T4 ~7 }. |, P" ~ - {
4 O* A, L7 ?7 a3 \9 I6 f7 D - if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXDAVL))
0 l. X6 T# q5 D% m" w% ^( j - {
0 M0 u$ W' ~0 |4 j. g - *(tempscr + index) = SDMMC_ReadFIFO(hsd->Instance);6 f7 x- ~4 A' E; y6 K+ v* y( V
- index++;8 }$ I: }) @# K" z0 ?+ {1 X! ]
- }! C& O0 w* H- p ~9 [3 |
- : h; }0 P8 S6 R7 L Y: n
- if((HAL_GetTick() - tickstart) >= SDMMC_DATATIMEOUT)
. h0 \4 v" J( l - {) a0 D F9 r6 z8 `
- return HAL_SD_ERROR_TIMEOUT;8 B6 ~. F2 t" S! p6 Y/ ^0 L
- }
8 ~. F1 f: |$ `3 n2 I( i) l - }
复制代码
: t) m' c7 S0 {4 i/ z后面我将GPIO改为GPIO_PULLUP后解决了这个问题,具体原因暂时还不清楚。
. g7 ?/ k S2 ~
5 M' M- o: a0 {: U7 k- GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
& n7 K8 B. k$ R" n" }1 Q - |GPIO_PIN_12;: J3 v/ g0 E( h* \7 g
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
5 L; E) v* m9 D# C; x: x, x - GPIO_InitStruct.Pull = GPIO_PULLUP;//GPIO_NOPULL;& ?' r5 _$ d) ?' Q: ~# w
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
5 z q. f; I1 d9 x( t5 p5 P" ` - GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;
& s' O; W+ \6 Y9 e - HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);+ r9 m5 o5 |! v( y0 M8 _3 B; d, F
: b! \# |% B; X- GPIO_InitStruct.Pin = GPIO_PIN_2;$ E }8 g# W4 {$ p
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
x) Y! S6 {; F- r8 S( j - GPIO_InitStruct.Pull = GPIO_PULLUP;//GPIO_NOPULL;
/ R' @; h* E1 ?9 W - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;3 x7 I% |5 m! [* [
- GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;
( G3 L, |( F: N, T- M! E - HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
复制代码 + e6 b1 t! v2 d; B' V/ o% `
简单Fatfs应用( m! A \" g- U5 B9 C
- void StartDefaultTask(void const * argument)
, w& y S; L9 {* H+ o0 ~ - {: I1 c( k" U6 D& G) o
- BSP_SD_Init();
! @8 d# y; E; O9 t1 ~/ k# D - /* init code for FATFS */
1 |2 v$ F; z0 V" P( p7 t& p - MX_FATFS_Init();9 V8 { }% x W* n; O
4 M& N5 G0 W1 \+ \/ V- /* USER CODE BEGIN StartDefaultTask */
7 v5 F* F7 t# b/ h - /* Infinite loop */8 N! E4 _1 t* f4 h
: k1 M" |% w& A' O8 a" ~- for(;;)
. n9 v" X1 F! u3 I - {: a% t( ]6 F9 i& p. d0 Y; |
- /*自己实现一个Run方法,每隔5秒往SD卡中的文件写入数据*/
& h4 ?/ v" C( | - MX_FATFS_Run();+ j4 A0 U& a; W6 \! S: x
- vTaskDelay( 5000 );# \9 m- }1 p, ^" f, g' o) ~% p/ g
- }
1 A+ i8 O7 ~! B9 U - /* USER CODE END StartDefaultTask *// \% M- {/ t6 N
- }
' v+ y7 c% {; l - - z) m) N; T( N& b" Q
- void MX_FATFS_Init(void) 0 C' m% l$ w/ _. R3 b* {2 W
- {! t' Y$ H( i/ _6 q4 ^( @% B
- /*## FatFS: Link the SD driver ###########################*/
7 j5 K7 d) ^7 g3 J" p$ o3 i - retSD = FATFS_LinkDriver(&SD_Driver, SDPath);0 D7 h4 h3 k" K5 S5 w
0 \8 O' v1 A& m$ f5 L- /* USER CODE BEGIN Init */- s# T3 t1 S3 e( l
- /* 在Init的时候挂载文件系统 */ # q' z }3 a& u4 u' c) u; g+ m
- if(f_mount(&SDFatFS, SDPath,0) != FR_OK)
( T) p- n% P5 c& y0 I$ O, j - { / L7 O A( R+ D
- while(1);7 z6 X2 F3 g O% f3 U- e
- } - P9 v* s! `- b4 `% W
- /* USER CODE END Init */+ S+ |2 B9 l$ l' D/ n, u
- }
. g1 D$ ]+ ~1 A3 m0 T) S
' X+ l o1 k( d% Z4 q: g2 P- void MX_FATFS_Run(void)/ Y& s g# p! ?4 `& l5 X
- {
" K! d1 a: _$ j$ f' u" J - UINT writeBytes;
( P" h u( Q) d/ D, B% p - /*文件系统基本操作:打开文件,定位到文件结尾,写入内容,关闭文件*/
$ o! ~" I! b. F - if(f_open(&SDFile, "SDTest.txt", FA_READ|FA_WRITE|FA_OPEN_ALWAYS) != FR_OK)
0 g. }. M" ^- O - {6 K8 e3 X7 H5 B7 A; c$ a7 n
- while(1);
7 P' o/ |/ O" D - }
3 {2 S" `$ y* a. Y; \& R7 Z - if(f_lseek(&SDFile, f_size(&SDFile)) != FR_OK)5 M5 |# W+ w( u# s
- {
& P4 E+ A7 m& D/ Y' O4 j! z0 X2 C - while(1);
, Z4 D7 c- g2 z+ _5 o6 z - }$ C/ D) u8 b: e4 `
- if(f_write(&SDFile, "sd Loop", sizeof("sd Loop"), &writeBytes) != FR_OK)$ U3 \, z3 b% I8 F9 d
- {
: k# c$ S( J5 s0 R: ^" j: f8 v% h - while(1);1 j. l4 g( v. W1 J: o6 r
- }
( X0 u: L8 B0 v - if(f_close(&SDFile) != FR_OK)0 o% q4 D/ [$ v' N1 ?
- {
4 n6 @* G. t$ P; _ - while(1);1 v6 y: L7 c8 r @
- } & y% n+ ~$ t3 f, O' @; L7 Z$ F0 O8 ^+ w
- }
复制代码 : P4 j1 `) D4 X' U0 c
SD写入结果:- V" E, T: _/ ~8 i1 o4 A( @6 B" ^- o0 x
( Z) p# v) Q9 c
C) X) C* o! ?. s, u
$ _1 X- ]* S. `* L* a
2 o9 p3 `. g! j' V+ j. e; b3 J |