简介
' M. I- E$ m. [7 A) @/ [% Z) @最近项目中可能需要使用到SD卡,所以需要对SD卡的配置和使用调研,在配置过程中遇到了一些问题,在此记录一下。
0 Q& g4 \5 R5 A+ e# F3 |1 v/ p# \7 [5 S
STM32Cube配置0 U# Q( X+ e, q- v
Pinout# s F- A( _! t3 M
% A+ t o! s s3 M& M9 U5 q% {2 s
, ~0 r) f+ x7 K" k* a5 [- s
: \8 H( e$ }, r) ]" n: g9 Y4 |" `! x3 A) r3 h& b
7 J* w+ q1 h7 Z- _" H8 `只需要注意绿色部分的设定4 ^; H( _8 a* q6 _
- |" a1 a$ T& K Q
Clock配置. A5 C- k4 q H0 B3 R
# p& w Z# E4 l- p' W
& T( f( u# D4 L& x- B2 R! O
这里使用了最大的Clock,SDMMC1的时钟是48MHz( c4 ]& j$ u% ]+ \- _) j" O
- z7 F% A! ?5 u) K8 QFATFS配置' {& Y( x9 P* j5 O9 C% \
* W* D+ G6 _; g
3 N- E/ T5 e4 m, g
2 O% b1 q2 h* O8 _
Freertos配置
3 I$ D# N) `& @" f! n7 s1 C- r' [9 Y. R9 T! E) ] W
% {) _- t/ K' [% |: G
3 z( e0 G% D( [1 j& T$ O这里增大了Heap size,使用了heap_4的内存管理方式。
. W5 u# x) f+ F: V$ l5 }5 j- W9 C. N& U
SD卡配置* t- s' E, N8 A8 k, F, G
; }2 v, [" ^/ A9 t( p6 B
( @- U Q# X' C: s, }
' L. B. |/ m0 B3 i7 q$ O+ x/ \# j; J$ D
! E3 E3 R$ Z) b T& C9 _. H6 G在这里打开SD的全局中断,并使用DMA2的方式传输数据
5 X) N) ^ j* W: ~
. ]$ v! E9 w9 K2 p2 c; tNVIC配置
, H+ x1 ~8 X% {$ i6 j1 `( m |
1 x `8 W1 b" d# G
! H+ x3 c5 J* a8 A# d7 ]6 ^
& Y+ T' a4 H$ e' w3 L% FSD的全局中断配置为5,DMA中断配置为6,5的优先级更高。/ l: H% {, J/ V8 A, M
$ g' f C, X. ~6 k9 Y H工程配置
: a. f8 o. M# U# ?% }4 u7 j: Y0 h. a v5 D* s
! g4 ?& d* E8 r, y9 ]) b; {: e' l1 a6 F
/ b5 s+ K) A% m
# Q+ L4 N2 l6 T, z$ j c# L1 L* a以上就是所有的配置内容了,配置完成后,直接使用Code generate功能就能自动生成keil工程。) }0 v2 V5 ?' p5 Y6 M/ H; M5 c
- I( n2 u2 {$ y6 ?1 W- k代码修改# @! ~5 {. g! ]: g
初始化SD卡
6 Q& U. }* i, v. w% d _1 O. K" G- Q我生成的工程中需要手动添加BSP_SD_Init()函数,我是在MX_FATFS_Init()之前添加的。/ e3 |9 g" h' B; ~ {) F7 m
- /* StartDefaultTask function */* e' Y: G. L8 I( w
- void StartDefaultTask(void const * argument)
$ Y& j* p" Q) T0 e5 Q) d( E. @ - {
' `0 _3 y; R$ I2 `, x - BSP_SD_Init();# o9 B8 I3 y$ O! B$ w/ z; x
- /* init code for FATFS */6 o5 F8 Y# U, u
- MX_FATFS_Init();7 T+ d ^0 n& u$ p
- 2 j2 X' U3 `3 j# I7 a
- /* USER CODE BEGIN StartDefaultTask */) `1 j, p6 G$ w% I
- /* Infinite loop */
2 Q) X/ D2 ^" m5 O+ p! S9 P - for(;;)# U$ D" f* {! J
- {
$ G, o% e, ]+ ~1 F2 z6 {1 V - MX_FATFS_Run();
# v2 \+ r" g M5 L. J - vTaskDelay( 5000 );. `; Y- d: A, |) w4 y, O8 x+ G
- }
% G K1 O* X. V - /* USER CODE END StartDefaultTask */! r. o( a. F4 l \8 A" `
- }
复制代码
* I1 W9 r- B: }% [6 f& k添加DMA中断处理函数( f% Q* W3 ?8 B
stm32f7xx_it.c文件中,需要添加下面两个中断处理函数:' v" z- n8 B6 X* Y
- void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
% ?, Y8 Q1 P' J* e& l8 ]6 O - {
; E$ f) x% e) Z$ O2 S+ z - BSP_SD_WriteCpltCallback();: G# n; ]: c0 W5 N7 O# ~$ R: r4 P# Q
- }# x2 e# w1 t @8 e8 D
0 z$ y, n% o0 h* w# A* \& i- void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)0 N* H. u8 }( V9 S
- {
) i% G+ E x* c - BSP_SD_ReadCpltCallback();* T- e5 T0 M: f" q5 N# A6 \9 W
- }
复制代码
' q5 K; r; q! i: D+ X# U创建Queue0 ]2 D- H* f' l" T' e. `
在自动生成的Demo中,使用了SDQueueID这个Queue,但并没有自动创建它,所以需要手动创建一下。我是在SD_initialize()这个函数中创建的,具体创建的地方可能需要再仔细考虑清楚。
p2 l: B' [3 ^; c1 W, C- DSTATUS SD_initialize(BYTE lun)
2 i# f0 |- [5 O6 T5 ~8 t- _! E - {5 I! L3 u! s: x8 `& x
- osMessageQDef(myQueueSD, QUEUE_SIZE, uint8_t*);
" d6 v. o8 U# n: K/ m - SDQueueID = osMessageCreate(osMessageQ(myQueueSD), NULL);
+ k' y; i9 l: x; z( p N - return SD_CheckStatus(lun);
! z4 u! s8 U0 v1 F - }
复制代码
/ V" ^; l6 R6 s2 n( o/ A* {将Pin的配置改为上拉' n' k" \' b" e% C
在默认配置函数中HAL_SD_MspInit()对SDIO的Pin的默认配置是GPIO_NOPULL,但我在使用过程中发现,程序会卡死在SD_FindSCR()函数的以下这段代码中:& ]5 C0 p0 o5 n& ]& S/ r
- /*卡在这个循环中出不去*/2 y* }/ M. R" e
- while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DBCKEND))" ?: X% z7 _! c; T* Q
- {; I; r) D' g$ L
- if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXDAVL))9 Q1 m1 F7 I9 L: d" C/ u
- {- ?# @" d0 w3 b d2 X* T6 Z! O
- *(tempscr + index) = SDMMC_ReadFIFO(hsd->Instance);# D# U3 l+ ^6 _+ ]6 Z/ t7 r
- index++;
' e1 q Z/ m2 _$ W _ - }
' l/ D# ?. a. A" R/ x1 b$ O; y- @ - ! o+ t4 n2 Z1 d
- if((HAL_GetTick() - tickstart) >= SDMMC_DATATIMEOUT)
9 \6 y! h3 K% Q" t. ^ x, m - {
* }1 f v1 z, O( R7 G+ \ - return HAL_SD_ERROR_TIMEOUT;
6 i- s I: |7 K' U! | - }, ?/ p' Y6 l, M
- }
复制代码
* k8 C/ f) D B. M6 i3 }, W4 H* a0 K后面我将GPIO改为GPIO_PULLUP后解决了这个问题,具体原因暂时还不清楚。
+ Y. }0 L# U; U5 i* G) r) V. l; t! ^" i! y5 h
- GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11 : |1 e. K p, G: u) d) m& o
- |GPIO_PIN_12;
& s1 v3 y1 p# ~, p* y- d - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
4 i& X( ]. m4 J7 [1 O4 F - GPIO_InitStruct.Pull = GPIO_PULLUP;//GPIO_NOPULL;" m& b1 x l- ]% ?1 Y- B7 G
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
# @5 E# `. l# i: c) [- j* x4 z- V - GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;
: l# J+ c- H: o5 r, n2 s - HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
6 l9 \9 r" f* b7 |* b/ ]+ I3 A8 F - 1 _1 n. ~7 q& S3 |1 ?
- GPIO_InitStruct.Pin = GPIO_PIN_2;
) ~$ }. c( K/ L( {: G: s' K; U% K - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;/ Y q' Y' u; d6 n
- GPIO_InitStruct.Pull = GPIO_PULLUP;//GPIO_NOPULL;& _& L0 M. x, z: p
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
% F \3 ^. q+ d+ J - GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;$ a4 W f4 T w! H) _- F
- HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
复制代码
% B% B6 V9 A; f$ G简单Fatfs应用3 V8 \, M, @+ W0 R9 x. M8 j) X# I
- void StartDefaultTask(void const * argument)
( ?6 C% X. |3 s+ b - { D' K, G% J. ?' m/ O
- BSP_SD_Init();; X$ `2 i( y# t4 R# o
- /* init code for FATFS */- a4 K! E5 `( u0 A' L2 f
- MX_FATFS_Init(); z5 k" _- ~: y2 X, @
. L+ ~' ~: d5 d$ U X! F. u" H- /* USER CODE BEGIN StartDefaultTask */
' I" W* s; m6 ^3 h7 } - /* Infinite loop */
& W# O4 T P+ J1 b* x- B - ( p, ~: Z- W9 {# r' G/ I
- for(;;): d2 {0 O, p# C- Z% U6 h
- {2 N, g5 h/ p4 n2 [& _
- /*自己实现一个Run方法,每隔5秒往SD卡中的文件写入数据*/( j1 l' p& {+ f
- MX_FATFS_Run();: o* c4 N6 M4 b
- vTaskDelay( 5000 );
+ k" m) V8 z5 ^4 J# y0 O4 x! g - }
5 l0 d$ d% W, \/ ^' M - /* USER CODE END StartDefaultTask */% b, n' x5 u! g" ~4 W4 V
- }2 S3 o/ O, u6 t& @# e2 S0 {& H
- ! ?* x j+ c D3 i2 _) H0 W
- void MX_FATFS_Init(void)
, t! o& T* f8 X& h$ ~: q. {. M - {' k# g( q4 o% E, W8 ], N
- /*## FatFS: Link the SD driver ###########################*/9 c/ n8 i$ F* ^, a" j3 j5 a# P' h) S; s# O
- retSD = FATFS_LinkDriver(&SD_Driver, SDPath);) R/ \' h: R3 Q, H A4 W
- 1 Z* W. t: B1 M# D: I
- /* USER CODE BEGIN Init */
. N8 I. M4 B% _$ u8 \ - /* 在Init的时候挂载文件系统 */ * R" P" u" {4 R4 N% S9 Q# f4 G
- if(f_mount(&SDFatFS, SDPath,0) != FR_OK) R1 S% w' U) V* `0 ^% W4 m
- {
Q/ x2 w4 S" z( H4 l- t' j0 ~ - while(1);7 H5 _8 s+ W! `" X& }
- }
3 K4 @% I( T$ \9 `. H - /* USER CODE END Init */1 V# n" U4 | y k1 ]. a- u
- }, ?2 O3 G- p0 Y% g
- * Q/ c5 R1 l/ a2 M
- void MX_FATFS_Run(void)
8 @1 {* i' w# `' Y - {
. l$ I g- y! E: |$ A# b D - UINT writeBytes;) B% k& ]1 c0 B5 P1 m
- /*文件系统基本操作:打开文件,定位到文件结尾,写入内容,关闭文件*/& Z5 k0 u% o, N1 ]5 Y; p" i( V
- if(f_open(&SDFile, "SDTest.txt", FA_READ|FA_WRITE|FA_OPEN_ALWAYS) != FR_OK)
; h) K3 I; V# V l - {
# R- y$ b5 d8 F: P2 _/ @ - while(1);* Z' Q3 |+ b4 q* u( n
- }. O" g/ V8 z6 X" E8 [: @
- if(f_lseek(&SDFile, f_size(&SDFile)) != FR_OK)
. T' J/ w+ q5 R; Q, p - {
( v% d7 r J1 }8 G3 f0 [ - while(1);; @) I# U4 s/ i0 y. o
- }8 B7 @3 B! f) c7 `
- if(f_write(&SDFile, "sd Loop", sizeof("sd Loop"), &writeBytes) != FR_OK)% G, l) K K7 R( R2 w7 S
- {. Y8 }6 ?1 `; [2 X8 L2 @
- while(1);
7 j$ n, T3 x# }9 G - }
& v9 E: h. @ W8 ^8 `, p - if(f_close(&SDFile) != FR_OK)
9 ~1 h1 i" ~1 C4 y - { {3 |* H$ H, ], X- z) r+ T
- while(1);9 _" q1 `. ^7 L$ N
- }
: ^* @& Q+ ~/ |$ Q2 l7 v n - }
复制代码 7 c& @4 F. }0 [+ | o9 c3 I
SD写入结果:, l+ K- @: B0 e' X. L
- u' A9 D; L0 W4 r& a5 r
* U7 O, l2 o# d, q. l
& R) Z. g8 w! ?4 c+ A3 L; m" ?/ w( S! j1 N$ U# m- t0 t3 U j# C
|