简介( t) Y" C; ?2 t9 _% {$ Y( [" h
最近项目中可能需要使用到SD卡,所以需要对SD卡的配置和使用调研,在配置过程中遇到了一些问题,在此记录一下。
8 l. \- F8 Z! V
) u8 x# a8 @9 S2 h; e5 X( o: tSTM32Cube配置
6 u. }( E# f; R, \: J/ x9 i7 qPinout3 P% M3 e2 z! O% U' q7 ~& e4 I. a
2 r1 ~/ i e/ `9 C9 R O+ T$ \* y# ]0 t* B
3 s' Y) k( T! j7 g: e$ E3 T
( k U4 }, k1 o1 J! r& U3 h; U
只需要注意绿色部分的设定
- ^9 J3 s2 R/ U& g3 I
/ r& ~% U( z2 V; e; C5 Q; YClock配置 G4 W0 l; O t1 u. o: {/ ^ U% @
7 K; B' G9 p Z$ N) H. E! _' c
; V5 r" W9 v9 e, X& c; N& ]这里使用了最大的Clock,SDMMC1的时钟是48MHz6 a0 o( ?- s% e1 O4 T
: P1 {7 g" L6 V7 n" P! |FATFS配置
* \# t7 _$ B6 C r; {
1 M- q& n( X$ \- p1 W# c# \
! W V' [$ o, E9 q1 c4 X1 ^9 e1 d
# i2 A* k9 I, T7 c/ [, v* IFreertos配置
, L+ c' j' D# U6 F) R
! k9 n6 Y& j( a
9 ~0 R. h2 f* u$ i3 x4 A: w2 k. U5 `7 D* {, E% c8 z
这里增大了Heap size,使用了heap_4的内存管理方式。+ F1 Z& I7 J6 w( s1 ]2 [7 ` c* ~7 b
9 u& m9 Z; `2 ~; n- hSD卡配置
% |# x5 k% [" k; M, {, @* l' O
4 [4 e/ N8 O8 y2 b4 t. f% q! B( O* m' u* B
2 S; [! u2 }) D. B; D L
' _1 c& m) n, e! Q9 n; W% C" G& m5 j7 c' _ Z3 |8 G! c
在这里打开SD的全局中断,并使用DMA2的方式传输数据, }/ g' n z4 \+ }: k, T8 K
. A, @. U2 n9 p; g) `- Y! [( T
NVIC配置
1 _. }, X. Q8 @& `+ k8 j
" D; d3 t' z; n$ v7 b* V
8 x) m6 g* e1 K M: _, M( k3 P8 K6 W* g9 _) }" \! Y4 s Z1 C5 G7 i
SD的全局中断配置为5,DMA中断配置为6,5的优先级更高。
* ?/ p# D5 t$ X" z, ^" Z$ n
2 T! u1 B8 [2 i: F5 L5 }工程配置
- U1 w" ^4 ]& U$ |( O
* B0 X% Y* S2 F7 x6 D0 ^
! p. \* f: J }: }9 e" F/ k; S& }5 [8 Q$ z* J7 e- O4 E k
( O8 y3 o& Y& w
8 r$ C* Y2 e; {9 d以上就是所有的配置内容了,配置完成后,直接使用Code generate功能就能自动生成keil工程。
V3 x# l' U2 ^- z4 j) M) t( Q; }7 K& y4 t
代码修改: | O ]& }8 W, Y
初始化SD卡. g: R1 K6 S) \
我生成的工程中需要手动添加BSP_SD_Init()函数,我是在MX_FATFS_Init()之前添加的。+ ^% Y- {: H* o- j/ p- M
- /* StartDefaultTask function */' f. H q+ I* N" B2 H& }6 u
- void StartDefaultTask(void const * argument)* f" r, j! \% J, g3 f. r5 |
- {
5 Q% |! Q; B9 ]0 c8 } - BSP_SD_Init();
' Z; _3 C( h2 N6 }' {' M5 L# z# j - /* init code for FATFS */
0 p: C9 u2 G J0 L - MX_FATFS_Init();8 P+ u# z( R$ w
- 1 C# ]8 M. B8 F( B4 r, e5 t4 U% w
- /* USER CODE BEGIN StartDefaultTask */; G! A: `0 F5 e
- /* Infinite loop */
: s5 M" a) X5 s$ S' J - for(;;)1 h/ V+ ?. F# m
- {
8 |* L5 ]! X! q3 Q5 X2 u2 K* T3 n - MX_FATFS_Run();
1 }" @% y+ U/ X$ [/ p/ I, F& O: } o - vTaskDelay( 5000 );, M- L- U: P% H
- }7 K) R- Z8 C R* M7 ?9 [8 s& g
- /* USER CODE END StartDefaultTask */
/ E2 k4 y/ b: e9 h0 k" `. S - }
复制代码 6 @: [5 M2 {& h# ?. T& K
添加DMA中断处理函数) @: v3 ?6 {9 | u* R8 h
stm32f7xx_it.c文件中,需要添加下面两个中断处理函数:
3 O4 I" A( y7 C5 w) x4 e+ D8 z) S- void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
$ ~1 U# c' S2 z u - {
% q' S( K3 w+ h! B) J( D - BSP_SD_WriteCpltCallback();
( w# j6 {0 C( A6 M h- u/ @ - }
3 E) v9 e- q1 v. y2 H4 \5 F& i
7 }3 ?$ y3 ?9 N) m- void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
1 C c' r9 L$ f+ X - {9 T' Q, U0 G4 K. z2 a' c
- BSP_SD_ReadCpltCallback();
) B- U6 g" o- {1 |' t B( b1 h - }
复制代码 6 l6 F8 E) t6 w; ?
创建Queue
- l. t& r$ ~% K$ @# m7 R在自动生成的Demo中,使用了SDQueueID这个Queue,但并没有自动创建它,所以需要手动创建一下。我是在SD_initialize()这个函数中创建的,具体创建的地方可能需要再仔细考虑清楚。8 {1 M$ H/ x. P8 q
- DSTATUS SD_initialize(BYTE lun)
% s1 D# F" P0 k/ Y T0 B - {4 R2 R& ~0 X; M' c+ E" s# ?7 R
- osMessageQDef(myQueueSD, QUEUE_SIZE, uint8_t*);) |% X o7 f5 u
- SDQueueID = osMessageCreate(osMessageQ(myQueueSD), NULL);# {3 |$ X8 l3 |' m" q
- return SD_CheckStatus(lun);
4 P& l! I) O; w! _ - }
复制代码 : X" z; P u: M: i$ b
将Pin的配置改为上拉. ~5 x! F- l* L. F# h0 u& w
在默认配置函数中HAL_SD_MspInit()对SDIO的Pin的默认配置是GPIO_NOPULL,但我在使用过程中发现,程序会卡死在SD_FindSCR()函数的以下这段代码中:
- O0 z3 \0 u- T: Y/ I+ c- j* p8 S- /*卡在这个循环中出不去*/ `4 i) r2 S u; d
- while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DBCKEND))* V) ^6 `5 U$ J6 [- N
- {' U: E+ \, @5 h3 G7 {8 h
- if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXDAVL))
( A- h. I' f: I: X - {& r' W p* t( m% k& x) x+ }$ ?
- *(tempscr + index) = SDMMC_ReadFIFO(hsd->Instance);* q) t% X9 S7 e. M2 f6 [' p
- index++;
% s& w W6 m F- Q& B, c& e - }
% e9 m, U+ _0 J7 m
0 w5 J: A: U+ C- if((HAL_GetTick() - tickstart) >= SDMMC_DATATIMEOUT)
+ V- [% m! r9 @+ E9 P0 h: h - {1 F/ Z% v7 g. i1 P8 h$ K" `. [
- return HAL_SD_ERROR_TIMEOUT;
- O6 z* _ X4 j/ ?! q7 | - }
, ~) X( I% ~: U - }
复制代码
. E; r- c0 b. k$ S$ C( W后面我将GPIO改为GPIO_PULLUP后解决了这个问题,具体原因暂时还不清楚。' H4 @+ L* y: B A/ m, P& p; C
/ g5 @1 t* G: J7 j( t; Z( I
- GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11 / j9 G7 E- ]. q+ E, y( r8 G0 T
- |GPIO_PIN_12;
* I$ w" T# Z" I8 l - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;* ~) X( b" z* T0 e% e8 i$ u, Y
- GPIO_InitStruct.Pull = GPIO_PULLUP;//GPIO_NOPULL;' L; c; j$ e# \! c. K, i$ l
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
' u: c& p3 {4 g8 V) n- G3 c8 L - GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;
& ~7 h% C4 V# f0 s* u C+ f - HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
% z, C0 p& [4 h8 R - 2 s+ s! Y- \% b k7 Q
- GPIO_InitStruct.Pin = GPIO_PIN_2;
/ ?/ R( x, e+ w - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;2 k" b; s+ N, K, o7 o# u# S u
- GPIO_InitStruct.Pull = GPIO_PULLUP;//GPIO_NOPULL;! T5 j& p/ ^3 n1 \
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;7 J4 D( ~$ ~. e, N8 q% s3 V
- GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;
+ J9 ]; m, ]+ s' ^4 O - HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
复制代码
+ k, f2 Q% _7 j简单Fatfs应用
' j3 @+ g/ a7 Y+ J- void StartDefaultTask(void const * argument)
# S+ q+ A8 X/ d& F - {
! o2 @1 X0 M( _. {; h - BSP_SD_Init();; n! @& L$ Z+ I& s( O0 j8 w
- /* init code for FATFS */
# [7 c& }: Y; I7 V( f0 l4 O& E) Z - MX_FATFS_Init();' c: y' F& ]8 ^
- 5 F7 S' p/ z8 J# q% Y( A. v
- /* USER CODE BEGIN StartDefaultTask */. X$ L! f5 J4 Y4 c y `7 { l
- /* Infinite loop */
% _# p& j/ f6 u1 |* N" N5 l - ) t0 ?( T" v8 Q9 y% O/ P4 q
- for(;;)
; b; a/ F& n4 s, h) B6 }% g& W - {6 v% @1 u) l1 f& Y7 {9 j4 P$ m# a
- /*自己实现一个Run方法,每隔5秒往SD卡中的文件写入数据*/9 X: w, V: J& @# u+ q* E5 Q' }
- MX_FATFS_Run();
6 Z; z I" c6 t' c! V* Z - vTaskDelay( 5000 );
( c3 g! A/ e9 @+ \9 l - }- j% Y. O L0 l
- /* USER CODE END StartDefaultTask */
$ H* m8 x y$ n; a2 ]* L) L - }/ A W: l6 f `% V
+ j- m* I8 H$ S; o7 G+ n- void MX_FATFS_Init(void) ) u% P3 F, K% ~
- {
6 t& ^* O. G u. e v3 _$ ]; W+ c/ M6 g - /*## FatFS: Link the SD driver ###########################*/6 D* o% `2 d+ F& r/ A& Y
- retSD = FATFS_LinkDriver(&SD_Driver, SDPath);
5 ~7 @2 M: m: j' }. T. k3 U* r - 3 K+ h& V$ V3 X
- /* USER CODE BEGIN Init */$ z* T: ~- |3 ^% T! K
- /* 在Init的时候挂载文件系统 */
+ F: I/ v% v) |3 ?& c* E - if(f_mount(&SDFatFS, SDPath,0) != FR_OK) ( L- p- v7 k# `/ E& u, Q
- { 9 ~7 p8 a0 }4 b/ @
- while(1);
3 L* ?: U1 g6 J - }
# }" |( S) @9 d: h7 e - /* USER CODE END Init */
& W; I/ W0 n3 d: m% B - }
/ Z8 M- e% I$ W- _4 F$ P7 w9 v# d
+ z8 }" y" U3 X8 I8 \- void MX_FATFS_Run(void)/ e9 b; I; E, m$ [8 d
- {) g$ d; v4 c( p3 l: Q9 \, O
- UINT writeBytes;
5 V! G: j$ D0 b% B - /*文件系统基本操作:打开文件,定位到文件结尾,写入内容,关闭文件*/
0 l& i4 X( U& i. u( H5 M - if(f_open(&SDFile, "SDTest.txt", FA_READ|FA_WRITE|FA_OPEN_ALWAYS) != FR_OK)
* L- k7 v y3 w- u. t - {
; ?- Y1 Z& c* P) d* I" A$ d$ A - while(1);
' O* B4 D# N1 ~$ v* R O - }, N: D, @3 Y/ A- p
- if(f_lseek(&SDFile, f_size(&SDFile)) != FR_OK)% f, r- z2 {, i. V9 g; j
- {
9 Z0 ?) N! \0 |% x - while(1);3 s% j* E# m& Y/ B6 J5 z
- }
% k; L/ u9 v) o5 U$ ^- q - if(f_write(&SDFile, "sd Loop", sizeof("sd Loop"), &writeBytes) != FR_OK)3 ~9 r2 D% M# G8 y( _( N% P" ]
- {
2 K( d4 \: W+ Q1 \/ F, E8 M; ] - while(1);/ V0 E' }/ s" I. J1 z
- }
0 }2 f& S K7 R# G - if(f_close(&SDFile) != FR_OK)
9 D& V& z$ ?7 o- }( u- X1 P - {. b1 A3 Y- O* S- N- x$ p6 E
- while(1);' r0 r& F7 K* \3 e" U
- } 3 Z, c) g' p. {' V8 u( N7 k
- }
复制代码 0 j0 x; V1 H4 h$ A
SD写入结果:3 B8 {' \" _# X2 G& A
+ ^; g! g& c- l- n8 ^) c7 l" k0 p- d) o- j
" S, ^3 S& \* Q( ]8 @( u# ]. o
3 x; {3 _( ~, e9 g$ [
|