注:文中提及的寄存器都是STM32H7的寄存器。其他单片机操作同理。
一、介绍
1、SD卡种类
SD 卡主要分为两个版本,有1.x版和现在普遍使用的2.0 版,2.0 版是为了适应大容量SD 卡提出的标准,这个标准把SD
卡分为SDSC 卡和SDHC 卡。其中SDSC 卡为标准容量,容量最大不超过2G ;超过2G 的都属于高容量的SDHC 卡。
注:SDHC卡最大只能到32G,所以又出现了SDXC卡,最大支持到2T,SDXC可以支持300MB/S的速度,现在支持设备并不多,而且SDXC不可向下兼容,不支持普通的SD和SDHC卡槽和读卡器,故本文不做介绍。
2、SDIO接口
SDIO接口包含CLK、CMD 及4 条DAT[3:0]信号线。这6 条信号线都是共用的总线,即新加入的设备可以并联接入到SDIO 接口。SDIO 主机是通过命令和SD 从设备的响应来寻址的,所以不需要片选信号线。
1) 、CLK 是卡的时钟信号线,由主机产生时钟信号,SD 卡和SDIO 卡的时钟频率可为0~25MHz。在命令和数据线上,每个时钟周期在传输1 位命令或数据。
2) 、CMD为命令信号线,SDIO 的所有由主机发出的命令及从机对命令的响应,都是在这个信号线上传输的。
3) 、DAT[3:0]表示4 条数据线,主机和从机的数据信号在这4 条线上传输。
二、SDIO的命令与响应
1、SDIO命令
SDIO的命令分为应用相关命令(ACMD)和通用命令(CMD)两部分,应用相关命令(ACMD)的发送,必须先发送通用命令(CMD55),然后才能发送应用相关命令(ACMD)。
SDIO的所有命令和响应都是通过CMD引脚传输的,任何命令的长度都是固定为48位,命令格式如下:
所有的命令都是由主机发出,其中起始位、传输位、CRC7和结束位由SDIO硬件控制,我们需要设置的就只有命令索引和参数部分。命令索引是命令的ID号,比如CMD0、CMD1之类的。有些命令会包含参数,如读命令就包含要读数据的地址,这些命令参数就存放在参数[39:8]。
2、SDIO响应
一般情况下,选中的SD卡在接收到主机命令后,都会回复一个应答(注意CMD0是无应答的),这个应答我们称之为响应,响应也是在CMD线上串行传输的。对于不同的命令,会有不同的响应格式,共有7种,简称为R1-R7。按照响应的字节长度不同又可以把这7种分为两类,即:短响应(48位)和长响应(136位)。这两类响应类型都带CRC错误检测(注意不带CRC的响应应该忽略CRC错误标志,如CMD1的响应)。
短响应的格式:
长响应的格式:
3、响应格式的例子
拿R1(属于短响应)来举例,格式为:
命令索引的内容为它响应的命令的编码,如当我们向SD卡发送CMD55(编码:110111)命令时,它返回的响应R1的命令索引内容即为110111。
[39:8]的内容为命令响应参数,如这个响应R1的内容即为卡状态。
再拿R6(属于短响应)来举例,格式为:
command index 的内容为它响应的命令的编码,如当我们向SD卡发送CMD3(编码:000011)命令时,它返回的响应R6的command index 内容即为000011。
Argument field 的内容为命令响应参数,如这个响应R6的内容即为RCA(卡的相对地址)及card status (卡的状态)。
所以当我们需要知道RCA 和卡状态时,我们可以向卡发送CMD3 命令,然后等待SD卡对命令的响应。SDIO接口通过CMD信号线接收到响应后,由硬件去除响应的头尾信息,把command index 保存到SDMMC_RESPCMD寄存器,把Argument field 内容保存存储到SDMMC_RESP1寄存器中。然后软件读取这两个寄存器即可获得所需的信息。
4、数据的读取和写入
SD卡的数据写入、读取的最小单位是块,每块的大小为512 字节。下图为多个数据块的写入过程。首先软件通过SDIO 接口的CMD 信号线发送多块写入的命令,接收到正常的响应后,要写入的数据线从4 根DAT 信号线传输出去,每块结束后是CRC校验码。接着要检测忙状态,数据传输到SD卡后,SD卡启动内部时序保存数据,这时SD卡会把DAT0信号线拉低,表示于“忙”状态,忙状态结束后,主机才能发送下一个数据块的数据。
下图是读的过程,从机在收到主机相关命令后,开始发送数据块给主机,所有数据块都带有CRC校验值(CRC由SDMMC硬件自动处理),单个数据块读的时候,在收到1个数据块以后即可以停止了,不需要发送停止命令(CMD12)。但是多块数据读的时候,SD卡将一直发送数据给主机,直到接到主机发送的STOP命令(CMD12)。
三、SD卡初始化流程
从上图可以看到,不管什么卡(这里我们将卡分为4类:SD2.0高容量卡(SDHC,最大32G),SD2.0 标准容量卡(SDSC,最大2G),SD1x 卡和MMC卡),首先我们要执行的是卡上电(需要设置SDMMC_POWER[1:0]=11),上电后发送CMD0,对卡进行软复位,之后发送CMD8命令,用于区分SD卡2.0,只有2.0及以后的卡才支持CMD8命令,MMC卡和V1.x卡都不支持该命令。CMD8的格式如下:
我们在发送CMD8的时候,可以顺便通过其所带的参数(VHS)告诉SD卡主机的供电情况,VHS定义如下:
这里我们使用参数0x1AA,即告诉SD卡,主机供电为2.7~3.6V之间,如果SD卡支持CMD8,且支持该电压范围,则会通过CMD8的响应(R7)将参数部分原本返回给主机,如果不支持CMD8,或者不支持这个电压范围,则不响应。
在发送CMD8后,发送ACMD41(注意发送ACMD41之前要先发送CMD55),来进一步确认卡的操作电压范围,并通过HCS位来告诉SD卡,主机是不是支持高容量卡(SDHC)。ACMD41的命令格式如下:
ACMD41得到的响应(R3)包含SD卡OCR寄存器内容,OCR寄存器内容定义如下:
对于支持CMD8指令(有响应)的卡,主机通过ACMD41的参数设置HCS位为1,来告诉SD卡主机支持SDHC卡,如果设置为0,则表示主机不支持SDHC卡。SDHC卡如果接收到HCS为0,则永远不会返回卡就绪状态,就会被主机判断为不支持的卡。对于不支持CMD8的卡,HCS位设置为0即可。
SD卡在接收到ACMD41后,返回OCR寄存器内容,如果是2.0的卡,主机可以通过判断OCR的CCS位来判断是SDHC还是SDSC。如果是1.x的卡,则忽略该位。OCR寄存器的最后一个位用于告诉主机SD卡是否上电完成,如果上电完成,该位将会被置1。
对于MMC卡,则不支持ACMD41,不响应CMD55,对MMC卡,我们只需要在发送CMD0后,在发送CMD1(作用同ACMD41),检查MMC卡的OCR寄存器,实现MMC卡的初始化。
至此,我们完成了SD卡类型的区分。最后发送CMD2和CMD3命令,用于获得卡CID寄存器数据和卡相对地址(RCA)。
CMD2,用于获得CID寄存器的数据,CID 寄存器数据各位定义如下:
SD卡在收到CMD2后,将返回R2长响应(136位),其中包含128位有效数据(CID寄存器内容),存放在SDMMC_RESP1~4等4个寄存器里面。通过读取这四个寄存器,就可以获得SD卡的CID信息。
CMD3,用于设置卡相对地址(RCA,必须为非0),对于SD卡(非MMC卡),在收到CMD3后,将返回一个新的RCA给主机,方便主机寻址。RCA的存在允许一个SDMMC接口挂多个SD卡,通过RCA来区分主机要操作的是哪个卡。而对于MMC卡,则不是由SD卡自动返回RCA,而是主机主动设置MMC卡的RCA,即通过CMD3参数(高16位用于RCA设置),实现RCA设置。同样MMC卡也支持一个SDMMC接口挂多个MMC卡,不同于SD卡的是所有的RCA都是由主机主动设置的,而SD卡的RCA则是SD卡发给主机的。
在获得卡RCA之后,我们便可以发送CMD9(带RCA参数),获得SD卡的CSD寄存器内容,从CSD寄存器,我们可以得到SD卡的容量和扇区大小等十分重要的信息。
至此,我们的SD卡初始化基本就结束了,最后通过CMD7命令,选中我们要操作的SD卡,即可开始对SD卡的读写操作了。
四、开发经验
1、发送ACMD41前要先发送CMD55,有些卡第一次发送CMD55会返回卡状态未就绪,需要多发送几次,直到卡返回就绪状态。所以一定要接收CMD55的响应,判断卡状态,否则初始化不成功。笔者使用256MB的卡时,遇到过一次。
五、简要总结
首先给卡上电,然后发送CMD0让卡进入IDLE STAGE模式。
接着发送CMD8,如果有响应,卡就是2.0卡,继续发送ACMD41,接收响应,如果响应中的CCS位为1,卡就是SDHC,如果CCS为0,卡就是SDSC。如果CMD8无响应,那么卡就是1.0卡,发送ACMD41,接收响应,根据响应中的31位判断卡是否进入准备状态。
卡分类完成后,发送CMD2来获取卡信息,发送CMD3来设置相对地址,卡的初始化结束。
六、初始化代码示例
代码来自正点原子的STM32H7的SD卡例程。建议复制到notepad++等地方看。
主要关注 SD_PowerON() 和SD_InitializeCards() 函数,这两个函数包含初始化流程,和上面的流程图是完全对应的,可以帮助理解。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130#include "sdmmc_sdcard.h" #include "string.h" #include "usart.h" // //本程序只供学习使用,未经作者许可,不得用于其它任何用途 //ALIENTEK STM32H7开发板 //SDMMC 驱动代码 (仅提供查询模式驱动代码) //正点原子@ALIENTEK //技术论坛:www.openedv.com //创建日期:2018/7/31 //版本:V1.0 //版权所有,盗版必究。 //Copyright(C) 广州市星翼电子科技有限公司 2014-2024 //All rights reserved //******************************************************************************** //升级说明 //无 // static u8 CardType=STD_CAPACITY_SD_CARD_V1_1; //SD卡类型(默认为1.x卡) static u32 CSD_Tab[4],CID_Tab[4],RCA=0; //SD卡CSD,CID以及相对地址(RCA)数据 SD_CardInfo SDCardInfo; //SD卡信息 //SD_ReadDisk/SD_WriteDisk函数专用buf,当这两个函数的数据缓存区地址不是4字节对齐的时候, //需要用到该数组,确保数据缓存区地址是4字节对齐的. __align(4) u8 SDMMC_DATA_BUFFER[512]; //初始化SD卡 //返回值:错误代码;(0,无错误) SD_Error SD_Init(void) { SD_Error errorstatus=SD_OK; u8 clkdiv=0; //SDMMC1 IO口初始化 GPIO_InitTypeDef GPIO_Initure; __HAL_RCC_SDMMC1_CLK_ENABLE(); //使能SDMMC1时钟 __HAL_RCC_GPIOC_CLK_ENABLE(); //使能GPIOC时钟 __HAL_RCC_GPIOD_CLK_ENABLE(); //使能GPIOD时钟 //PC8,9,10,11,12 GPIO_Initure.Pin=GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12; GPIO_Initure.Mode=GPIO_MODE_AF_PP; //推挽复用 GPIO_Initure.Pull=GPIO_NOPULL; //无上下拉 GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH; //高速 GPIO_Initure.Alternate=GPIO_AF12_SDIO1; //复用为SDIO HAL_GPIO_Init(GPIOC,&GPIO_Initure); //初始化 //PD2 GPIO_Initure.Pin=GPIO_PIN_2; HAL_GPIO_Init(GPIOD,&GPIO_Initure); //初始化 //SDMMC外设寄存器设置为默认值 SDMMC1->POWER=0x00000000; SDMMC1->CLKCR=0x00000000; SDMMC1->ARG=0x00000000; SDMMC1->CMD=0x00000000; SDMMC1->DTIMER=0x00000000; SDMMC1->DLEN=0x00000000; SDMMC1->DCTRL=0x00000000; SDMMC1->ICR=0X1FE00FFF; SDMMC1->MASK=0x00000000; // HAL_NVIC_SetPriority(SDMMC1_IRQn,2,0); //配置SDMMC1中断,抢占优先级2,子优先级0 // HAL_NVIC_EnableIRQ(SDMMC1_IRQn); //使能SDMMC1中断 errorstatus=SD_PowerON(); //SD卡上电 if(errorstatus==SD_OK)errorstatus=SD_InitializeCards(); //初始化SD卡 if(errorstatus==SD_OK)errorstatus=SD_GetCardInfo(&SDCardInfo); //获取卡信息 if(errorstatus==SD_OK)errorstatus=SD_SelectDeselect((u32)(SDCardInfo.RCA<<16));//选中SD卡 if(errorstatus==SD_OK)errorstatus=SD_EnableWideBusOperation(1); //4位宽度,如果是MMC卡,则不能用4位模式 if((errorstatus==SD_OK)||(MULTIMEDIA_CARD==CardType)) { if(SDCardInfo.CardType==STD_CAPACITY_SD_CARD_V1_1||SDCardInfo.CardType==STD_CAPACITY_SD_CARD_V2_0) { clkdiv=SDMMC_TRANSFER_CLK_DIV+2; //V1.1/V2.0卡,设置最高48/4=12Mhz }else clkdiv=SDMMC_TRANSFER_CLK_DIV; //SDHC等其他卡,设置最高48/2=24Mhz SDMMC_Clock_Set(clkdiv); //设置时钟频率,SDMMC时钟计算公式:SDMMC_CK时钟=SDMMCCLK/[clkdiv+2];其中,SDMMCCLK固定为48Mhz } return errorstatus; } //SDMMC时钟初始化设置 //clkdiv:时钟分频系数 //CK时钟=sdmmc_ker_ck/[2*clkdiv];(sdmmc_ker_ck钟固定为400Mhz) void SDMMC_Clock_Set(u16 clkdiv) { u32 tmpreg=SDMMC1->CLKCR; tmpreg&=0XFFFFFC00; tmpreg|=clkdiv; SDMMC1->CLKCR=tmpreg; } //SDMMC发送命令函数 //cmdindex:命令索引,低六位有效 //waitrsp:期待的相应.00/10,无响应;01,短响应;11,长响应 //arg:参数 void SDMMC_Send_Cmd(u8 cmdindex,u8 waitrsp,u32 arg) { u32 tmpreg=0; SDMMC1->ARG=arg; tmpreg|=cmdindex&0X3F; //设置新的index tmpreg|=(u32)waitrsp<<8;//设置新的wait rsp tmpreg|=0<<10; //无等待 tmpreg|=1<<12; //命令通道状态机使能 SDMMC1->CMD=tmpreg; } //SDMMC发送数据配置函数 //datatimeout:超时时间设置 //datalen:传输数据长度,低25位有效,必须为块大小的整数倍 //blksize:块大小.实际大小为:2^blksize字节 //dir:数据传输方向:0,控制器到卡;1,卡到控制器; void SDMMC_Send_Data_Cfg(u32 datatimeout,u32 datalen,u8 blksize,u8 dir) { u32 tmpreg; SDMMC1->DTIMER=datatimeout; SDMMC1->DLEN=datalen&0X1FFFFFF; //低25位有效 tmpreg=SDMMC1->DCTRL; tmpreg&=0xFFFFFF00; //清除之前的设置. tmpreg|=blksize<<4; //设置块大小 tmpreg|=0<<2; //块数据传输 tmpreg|=(dir&0X01)<<1; //方向控制 tmpreg|=1<<0; //数据传输使能,DPSM状态机 SDMMC1->DCTRL=tmpreg; } //卡上电 //查询所有SDMMC接口上的卡设备,并查询其电压和配置时钟 //返回值:错误代码;(0,无错误) SD_Error SD_PowerON(void) { u8 i=0; u32 tempreg=0; SD_Error errorstatus=SD_OK; u32 response=0,count=0,validvoltage=0; u32 SDType=SD_STD_CAPACITY; //配置CLKCR寄存器 tempreg|=0<<12; //PWRSAV=0,非省电模式 tempreg|=0<<14; //WIDBUS[1:0]=0,1位数据宽度 tempreg|=0<<16; //NEGEDGE=0,SDMMCCK下降沿更改命令和数据 tempreg|=0<<17; //HWFC_EN=0,关闭硬件流控制 SDMMC1->CLKCR=tempreg; SDMMC_Clock_Set(SDMMC_INIT_CLK_DIV);//设置时钟频率(初始化的时候,不能超过400Khz) SDMMC1->POWER=0X03; //上电状态,开启卡时钟 for(i=0;i<74;i++) { SDMMC_Send_Cmd(SD_CMD_GO_IDLE_STATE,0,0);//发送CMD0进入IDLE STAGE模式命令. errorstatus=CmdError(); if(errorstatus==SD_OK)break; } if(errorstatus)return errorstatus;//返回错误状态 SDMMC_Send_Cmd(SD_SDMMC_SEND_IF_COND,1,SD_CHECK_PATTERN);//发送CMD8,短响应,检查SD卡接口特性. //arg[11:8]:01,支持电压范围,2.7~3.6V //arg[7:0]:默认0XAA //返回响应7 errorstatus=CmdResp7Error(); //等待R7响应 if(errorstatus==SD_OK) //R7响应正常 { CardType=STD_CAPACITY_SD_CARD_V2_0; //SD 2.0卡 SDType=SD_HIGH_CAPACITY; //高容量卡 } SDMMC_Send_Cmd(SD_CMD_APP_CMD,1,0); //发送CMD55,短响应 errorstatus=CmdResp1Error(SD_CMD_APP_CMD); //等待R1响应 if(errorstatus==SD_OK)//SD2.0/SD 1.1,否则为MMC卡 { //SD卡,发送ACMD41 SD_APP_OP_COND,参数为:0x80100000 while((!validvoltage)&&(count<SD_MAX_VOLT_TRIAL)) { SDMMC_Send_Cmd(SD_CMD_APP_CMD,1,0); //发送CMD55,短响应 errorstatus=CmdResp1Error(SD_CMD_APP_CMD); //等待R1响应 if(errorstatus!=SD_OK)return errorstatus; //响应错误 SDMMC_Send_Cmd(SD_CMD_SD_APP_OP_COND,1,SD_VOLTAGE_WINDOW_SD|SDType);//发送ACMD41,短响应 errorstatus=CmdResp3Error(); //等待R3响应 if(errorstatus!=SD_OK)return errorstatus; //响应错误 response=SDMMC1->RESP1;; //得到响应 validvoltage=(((response>>31)==1)?1:0); //判断SD卡上电是否完成 count++; } if(count>=SD_MAX_VOLT_TRIAL) { errorstatus=SD_INVALID_VOLTRANGE; return errorstatus; } if(response&=SD_HIGH_CAPACITY) { CardType=HIGH_CAPACITY_SD_CARD; } }else//MMC卡 { //MMC卡,发送CMD1 SDMMC_SEND_OP_COND,参数为:0x80FF8000 while((!validvoltage)&&(count<SD_MAX_VOLT_TRIAL)) { SDMMC_Send_Cmd(SD_CMD_SEND_OP_COND,1,SD_VOLTAGE_WINDOW_MMC);//发送CMD1,短响应 errorstatus=CmdResp3Error(); //等待R3响应 if(errorstatus!=SD_OK)return errorstatus; //响应错误 response=SDMMC1->RESP1;; //得到响应 validvoltage=(((response>>31)==1)?1:0); count++; } if(count>=SD_MAX_VOLT_TRIAL) { errorstatus=SD_INVALID_VOLTRANGE; return errorstatus; } CardType=MULTIMEDIA_CARD; } return(errorstatus); } //SD卡 Power OFF //返回值:错误代码;(0,无错误) SD_Error SD_PowerOFF(void) { SDMMC1->POWER&=~(3<<0);//SDMMC电源关闭,时钟停止 return SD_OK; } //初始化所有的卡,并让卡进入就绪状态 //返回值:错误代码 SD_Error SD_InitializeCards(void) { SD_Error errorstatus=SD_OK; u16 rca = 0x01; if((SDMMC1->POWER&0X03)==0)return SD_REQUEST_NOT_APPLICABLE;//检查电源状态,确保为上电状态 if(SECURE_DIGITAL_IO_CARD!=CardType) //非SECURE_DIGITAL_IO_CARD { SDMMC_Send_Cmd(SD_CMD_ALL_SEND_CID,3,0); //发送CMD2,取得CID,长响应 errorstatus=CmdResp2Error(); //等待R2响应 if(errorstatus!=SD_OK)return errorstatus; //响应错误 CID_Tab[0]=SDMMC1->RESP1; CID_Tab[1]=SDMMC1->RESP2; CID_Tab[2]=SDMMC1->RESP3; CID_Tab[3]=SDMMC1->RESP4; } if((STD_CAPACITY_SD_CARD_V1_1==CardType)||(STD_CAPACITY_SD_CARD_V2_0==CardType)||(SECURE_DIGITAL_IO_COMBO_CARD==CardType)||(HIGH_CAPACITY_SD_CARD==CardType))//判断卡类型 { SDMMC_Send_Cmd(SD_CMD_SET_REL_ADDR,1,0); //发送CMD3,短响应 errorstatus=CmdResp6Error(SD_CMD_SET_REL_ADDR,&rca);//等待R6响应 if(errorstatus!=SD_OK)return errorstatus; //响应错误 } if (MULTIMEDIA_CARD==CardType) { SDMMC_Send_Cmd(SD_CMD_SET_REL_ADDR,1,(u32)(rca<<16));//发送CMD3,短响应 errorstatus=CmdResp2Error(); //等待R2响应 if(errorstatus!=SD_OK)return errorstatus; //响应错误 } if (SECURE_DIGITAL_IO_CARD!=CardType) //非SECURE_DIGITAL_IO_CARD { RCA = rca; SDMMC_Send_Cmd(SD_CMD_SEND_CSD,3,(u32)(rca<<16));//发送CMD9+卡RCA,取得CSD,长响应 errorstatus=CmdResp2Error(); //等待R2响应 if(errorstatus!=SD_OK)return errorstatus; //响应错误 CSD_Tab[0]=SDMMC1->RESP1; CSD_Tab[1]=SDMMC1->RESP2; CSD_Tab[2]=SDMMC1->RESP3; CSD_Tab[3]=SDMMC1->RESP4; } return SD_OK;//卡初始化成功 } //得到卡信息 //cardinfo:卡信息存储区 //返回值:错误状态 SD_Error SD_GetCardInfo(SD_CardInfo *cardinfo) { SD_Error errorstatus=SD_OK; u8 tmp=0; cardinfo->CardType=(u8)CardType; //卡类型 cardinfo->RCA=(u16)RCA; //卡RCA值 tmp=(u8)((CSD_Tab[0]&0xFF000000)>>24); cardinfo->SD_csd.CSDStruct=(tmp&0xC0)>>6; //CSD结构 cardinfo->SD_csd.SysSpecVersion=(tmp&0x3C)>>2; //2.0协议还没定义这部分(为保留),应该是后续协议定义的 cardinfo->SD_csd.Reserved1=tmp&0x03; //2个保留位 tmp=(u8)((CSD_Tab[0]&0x00FF0000)>>16); //第1个字节 cardinfo->SD_csd.TAAC=tmp; //数据读时间1 tmp=(u8)((CSD_Tab[0]&0x0000FF00)>>8); //第2个字节 cardinfo->SD_csd.NSAC=tmp; //数据读时间2 tmp=(u8)(CSD_Tab[0]&0x000000FF); //第3个字节 cardinfo->SD_csd.MaxBusClkFrec=tmp; //传输速度 tmp=(u8)((CSD_Tab[1]&0xFF000000)>>24); //第4个字节 cardinfo->SD_csd.CardComdClasses=tmp<<4; //卡指令类高四位 tmp=(u8)((CSD_Tab[1]&0x00FF0000)>>16); //第5个字节 cardinfo->SD_csd.CardComdClasses|=(tmp&0xF0)>>4;//卡指令类低四位 cardinfo->SD_csd.RdBlockLen=tmp&0x0F; //最大读取数据长度 tmp=(u8)((CSD_Tab[1]&0x0000FF00)>>8); //第6个字节 cardinfo->SD_csd.PartBlockRead=(tmp&0x80)>>7; //允许分块读 cardinfo->SD_csd.WrBlockMisalign=(tmp&0x40)>>6; //写块错位 cardinfo->SD_csd.RdBlockMisalign=(tmp&0x20)>>5; //读块错位 cardinfo->SD_csd.DSRImpl=(tmp&0x10)>>4; cardinfo->SD_csd.Reserved2=0; //保留 if((CardType==STD_CAPACITY_SD_CARD_V1_1)||(CardType==STD_CAPACITY_SD_CARD_V2_0)||(MULTIMEDIA_CARD==CardType))//标准1.1/2.0卡/MMC卡 { cardinfo->SD_csd.DeviceSize=(tmp&0x03)<<10; //C_SIZE(12位) tmp=(u8)(CSD_Tab[1]&0x000000FF); //第7个字节 cardinfo->SD_csd.DeviceSize|=(tmp)<<2; tmp=(u8)((CSD_Tab[2]&0xFF000000)>>24); //第8个字节 cardinfo->SD_csd.DeviceSize|=(tmp&0xC0)>>6; cardinfo->SD_csd.MaxRdCurrentVDDMin=(tmp&0x38)>>3; cardinfo->SD_csd.MaxRdCurrentVDDMax=(tmp&0x07); tmp=(u8)((CSD_Tab[2]&0x00FF0000)>>16); //第9个字节 cardinfo->SD_csd.MaxWrCurrentVDDMin=(tmp&0xE0)>>5; cardinfo->SD_csd.MaxWrCurrentVDDMax=(tmp&0x1C)>>2; cardinfo->SD_csd.DeviceSizeMul=(tmp&0x03)<<1;//C_SIZE_MULT tmp=(u8)((CSD_Tab[2]&0x0000FF00)>>8); //第10个字节 cardinfo->SD_csd.DeviceSizeMul|=(tmp&0x80)>>7; cardinfo->CardCapacity=(cardinfo->SD_csd.DeviceSize+1);//计算卡容量 cardinfo->CardCapacity*=(1<<(cardinfo->SD_csd.DeviceSizeMul+2)); cardinfo->CardBlockSize=1<<(cardinfo->SD_csd.RdBlockLen);//块大小 cardinfo->CardCapacity*=cardinfo->CardBlockSize; }else if(CardType==HIGH_CAPACITY_SD_CARD) //高容量卡 { tmp=(u8)(CSD_Tab[1]&0x000000FF); //第7个字节 cardinfo->SD_csd.DeviceSize=(tmp&0x3F)<<16;//C_SIZE tmp=(u8)((CSD_Tab[2]&0xFF000000)>>24); //第8个字节 cardinfo->SD_csd.DeviceSize|=(tmp<<8); tmp=(u8)((CSD_Tab[2]&0x00FF0000)>>16); //第9个字节 cardinfo->SD_csd.DeviceSize|=(tmp); tmp=(u8)((CSD_Tab[2]&0x0000FF00)>>8); //第10个字节 cardinfo->CardCapacity=(long long)(cardinfo->SD_csd.DeviceSize+1)*512*1024;//计算卡容量 cardinfo->CardBlockSize=512; //块大小固定为512字节 } cardinfo->SD_csd.EraseGrSize=(tmp&0x40)>>6; cardinfo->SD_csd.EraseGrMul=(tmp&0x3F)<<1; tmp=(u8)(CSD_Tab[2]&0x000000FF); //第11个字节 cardinfo->SD_csd.EraseGrMul|=(tmp&0x80)>>7; cardinfo->SD_csd.WrProtectGrSize=(tmp&0x7F); tmp=(u8)((CSD_Tab[3]&0xFF000000)>>24); //第12个字节 cardinfo->SD_csd.WrProtectGrEnable=(tmp&0x80)>>7; cardinfo->SD_csd.ManDeflECC=(tmp&0x60)>>5; cardinfo->SD_csd.WrSpeedFact=(tmp&0x1C)>>2; cardinfo->SD_csd.MaxWrBlockLen=(tmp&0x03)<<2; tmp=(u8)((CSD_Tab[3]&0x00FF0000)>>16); //第13个字节 cardinfo->SD_csd.MaxWrBlockLen|=(tmp&0xC0)>>6; cardinfo->SD_csd.WriteBlockPaPartial=(tmp&0x20)>>5; cardinfo->SD_csd.Reserved3=0; cardinfo->SD_csd.ContentProtectAppli=(tmp&0x01); tmp=(u8)((CSD_Tab[3]&0x0000FF00)>>8); //第14个字节 cardinfo->SD_csd.FileFormatGrouop=(tmp&0x80)>>7; cardinfo->SD_csd.CopyFlag=(tmp&0x40)>>6; cardinfo->SD_csd.PermWrProtect=(tmp&0x20)>>5; cardinfo->SD_csd.TempWrProtect=(tmp&0x10)>>4; cardinfo->SD_csd.FileFormat=(tmp&0x0C)>>2; cardinfo->SD_csd.ECC=(tmp&0x03); tmp=(u8)(CSD_Tab[3]&0x000000FF); //第15个字节 cardinfo->SD_csd.CSD_CRC=(tmp&0xFE)>>1; cardinfo->SD_csd.Reserved4=1; tmp=(u8)((CID_Tab[0]&0xFF000000)>>24); //第0个字节 cardinfo->SD_cid.ManufacturerID=tmp; tmp=(u8)((CID_Tab[0]&0x00FF0000)>>16); //第1个字节 cardinfo->SD_cid.OEM_AppliID=tmp<<8; tmp=(u8)((CID_Tab[0]&0x000000FF00)>>8); //第2个字节 cardinfo->SD_cid.OEM_AppliID|=tmp; tmp=(u8)(CID_Tab[0]&0x000000FF); //第3个字节 cardinfo->SD_cid.ProdName1=tmp<<24; tmp=(u8)((CID_Tab[1]&0xFF000000)>>24); //第4个字节 cardinfo->SD_cid.ProdName1|=tmp<<16; tmp=(u8)((CID_Tab[1]&0x00FF0000)>>16); //第5个字节 cardinfo->SD_cid.ProdName1|=tmp<<8; tmp=(u8)((CID_Tab[1]&0x0000FF00)>>8); //第6个字节 cardinfo->SD_cid.ProdName1|=tmp; tmp=(u8)(CID_Tab[1]&0x000000FF); //第7个字节 cardinfo->SD_cid.ProdName2=tmp; tmp=(u8)((CID_Tab[2]&0xFF000000)>>24); //第8个字节 cardinfo->SD_cid.ProdRev=tmp; tmp=(u8)((CID_Tab[2]&0x00FF0000)>>16); //第9个字节 cardinfo->SD_cid.ProdSN=tmp<<24; tmp=(u8)((CID_Tab[2]&0x0000FF00)>>8); //第10个字节 cardinfo->SD_cid.ProdSN|=tmp<<16; tmp=(u8)(CID_Tab[2]&0x000000FF); //第11个字节 cardinfo->SD_cid.ProdSN|=tmp<<8; tmp=(u8)((CID_Tab[3]&0xFF000000)>>24); //第12个字节 cardinfo->SD_cid.ProdSN|=tmp; tmp=(u8)((CID_Tab[3]&0x00FF0000)>>16); //第13个字节 cardinfo->SD_cid.Reserved1|=(tmp&0xF0)>>4; cardinfo->SD_cid.ManufactDate=(tmp&0x0F)<<8; tmp=(u8)((CID_Tab[3]&0x0000FF00)>>8); //第14个字节 cardinfo->SD_cid.ManufactDate|=tmp; tmp=(u8)(CID_Tab[3]&0x000000FF); //第15个字节 cardinfo->SD_cid.CID_CRC=(tmp&0xFE)>>1; cardinfo->SD_cid.Reserved2=1; return errorstatus; } //设置SDMMC总线宽度(MMC卡不支持4bit模式) //wmode:位宽模式.0,1位数据宽度;1,4位数据宽度;2,8位数据宽度 //返回值:SD卡错误状态 SD_Error SD_EnableWideBusOperation(u32 wmode) { SD_Error errorstatus=SD_OK; u16 clkcr=0; if(MULTIMEDIA_CARD==CardType)return SD_UNSUPPORTED_FEATURE;//MMC卡不支持 else if((STD_CAPACITY_SD_CARD_V1_1==CardType)||(STD_CAPACITY_SD_CARD_V2_0==CardType)||(HIGH_CAPACITY_SD_CARD==CardType)) { if(wmode>=2)return SD_UNSUPPORTED_FEATURE;//不支持8位模式 else { errorstatus=SDEnWideBus(wmode); if(SD_OK==errorstatus) { clkcr=SDMMC1->CLKCR; //读取CLKCR的值 clkcr&=~(3<<14); //清除之前的位宽设置 clkcr|=(u32)wmode<<14; //1位/4位总线宽度 clkcr|=0<<17; //不开启硬件流控制 SDMMC1->CLKCR=clkcr; //重新设置CLKCR值 } } } return errorstatus; } //选卡 //发送CMD7,选择相对地址(rca)为addr的卡,取消其他卡.如果为0,则都不选择. //addr:卡的RCA地址 SD_Error SD_SelectDeselect(u32 addr) { SDMMC_Send_Cmd(SD_CMD_SEL_DESEL_CARD,1,addr); //发送CMD7,选择卡,短响应 return CmdResp1Error(SD_CMD_SEL_DESEL_CARD); } //SD卡读取单个/多个块 //buf:读数据缓存区 //addr:读取地址 //blksize:块大小 //nblks:要读取的块数,1,表示读取单个块 //返回值:错误状态 SD_Error SD_ReadBlocks(u8 *buf,long long addr,u16 blksize,u32 nblks) { SD_Error errorstatus=SD_OK; u32 count=0; u32 timeout=SDMMC_DATATIMEOUT; u32 *tempbuff=(u32*)buf; //转换为u32指针 SDMMC1->DCTRL=0x0; //数据控制寄存器清零(关DMA) if(CardType==HIGH_CAPACITY_SD_CARD)//大容量卡 { blksize=512; addr>>=9; } SDMMC_Send_Cmd(SD_CMD_SET_BLOCKLEN,1,blksize); //发送CMD16+设置数据长度为blksize,短响应 errorstatus=CmdResp1Error(SD_CMD_SET_BLOCKLEN); //等待R1响应 if(errorstatus!=SD_OK) { //printf("SDMMC_Send_Cmd=%drn",errorstatus); return errorstatus; //响应错误 } SDMMC_Send_Data_Cfg(SD_DATATIMEOUT,nblks*blksize,9,1); //nblks*blksize,块大小恒为512,卡到控制器 SDMMC1->CMD|=1<<6; //CMDTRANS=1,产生一个数据传输命令 if(nblks>1) //多块读 { SDMMC_Send_Cmd(SD_CMD_READ_MULT_BLOCK,1,addr); //发送CMD18+从addr地址出读取数据,短响应 errorstatus=CmdResp1Error(SD_CMD_READ_MULT_BLOCK); //等待R1响应 if(errorstatus!=SD_OK) { //printf("SD_CMD_READ_MULT_BLOCK Errorrn"); return errorstatus; //响应错误 } }else //单块读 { SDMMC_Send_Cmd(SD_CMD_READ_SINGLE_BLOCK,1,addr); //发送CMD17+从addr地址出读取数据,短响应 errorstatus=CmdResp1Error(SD_CMD_READ_SINGLE_BLOCK);//等待R1响应 if(errorstatus!=SD_OK)return errorstatus; //响应错误 } INTX_DISABLE();//关闭总中断(POLLING模式,严禁中断打断SDMMC读写操作!!!) while(!(SDMMC1->STA&((1<<5)|(1<<1)|(1<<3)|(1<<8))))//无上溢/CRC/超时/完成(标志) { if(SDMMC1->STA&(1<<15)) //接收区半满,表示至少存了8个字 { for(count=0;count<8;count++) //循环读取数据 { *(tempbuff+count)=SDMMC1->FIFO; } tempbuff+=8; timeout=0X7FFFFF; //读数据溢出时间 }else //处理超时 { if(timeout==0)return SD_DATA_TIMEOUT; timeout--; } } SDMMC1->CMD&=~(1<<6); //CMDTRANS=0,结束数据传输 INTX_ENABLE(); //开启总中断 if(SDMMC1->STA&(1<<3)) //数据超时错误 { SDMMC1->ICR|=1<<3; //清错误标志 return SD_DATA_TIMEOUT; }else if(SDMMC1->STA&(1<<1))//数据块CRC错误 { SDMMC1->ICR|=1<<1; //清错误标志 if(nblks>1) //针对可能出现的CRC错误,如果是多块读取,必须发送结束传输命令! { SDMMC_Send_Cmd(SD_CMD_STOP_TRANSMISSION,1,0); //发送CMD12+结束传输 errorstatus=CmdResp1Error(SD_CMD_STOP_TRANSMISSION);//等待R1响应 } return SD_DATA_CRC_FAIL; }else if(SDMMC1->STA&(1<<5))//接收fifo上溢错误 { SDMMC1->ICR|=1<<5; //清错误标志 return SD_RX_OVERRUN; } if((SDMMC1->STA&(1<<8))&&(nblks>1))//多块接收结束,发送结束指令 { if((STD_CAPACITY_SD_CARD_V1_1==CardType)||(STD_CAPACITY_SD_CARD_V2_0==CardType)||(HIGH_CAPACITY_SD_CARD==CardType)) { SDMMC_Send_Cmd(SD_CMD_STOP_TRANSMISSION,1,0); //发送CMD12+结束传输 errorstatus=CmdResp1Error(SD_CMD_STOP_TRANSMISSION);//等待R1响应 if(errorstatus!=SD_OK)return errorstatus; } } SDMMC1->ICR=0X1FE00FFF; //清除所有标记 return errorstatus; } //SD卡写单个/多个块 //buf:数据缓存区 //addr:写地址 //blksize:块大小 //nblks:要读取的块数,1,表示读取单个块 //返回值:错误状态 SD_Error SD_WriteBlocks(u8 *buf,long long addr,u16 blksize,u32 nblks) { SD_Error errorstatus = SD_OK; u8 cardstate=0; u32 timeout=0,bytestransferred=0; u32 cardstatus=0,count=0,restwords=0; u32 tlen=nblks*blksize; //总长度(字节) u32*tempbuff=(u32*)buf; if(buf==NULL)return SD_INVALID_PARAMETER; //参数错误 SDMMC1->DCTRL=0x0; //数据控制寄存器清零(关DMA) if(CardType==HIGH_CAPACITY_SD_CARD) //大容量卡 { blksize=512; addr>>=9; } SDMMC_Send_Cmd(SD_CMD_SET_BLOCKLEN,1,blksize); //发送CMD16+设置数据长度为blksize,短响应 errorstatus=CmdResp1Error(SD_CMD_SET_BLOCKLEN); //等待R1响应 if(errorstatus!=SD_OK)return errorstatus; //响应错误 if(nblks>1) //多块写 { if(nblks*blksize>SD_MAX_DATA_LENGTH)return SD_INVALID_PARAMETER; if((STD_CAPACITY_SD_CARD_V1_1==CardType)||(STD_CAPACITY_SD_CARD_V2_0==CardType)||(HIGH_CAPACITY_SD_CARD==CardType)) { //提高性能 SDMMC_Send_Cmd(SD_CMD_APP_CMD,1,(u32)RCA<<16); //发送ACMD55,短响应 errorstatus=CmdResp1Error(SD_CMD_APP_CMD); //等待R1响应 if(errorstatus!=SD_OK)return errorstatus; SDMMC_Send_Cmd(SD_CMD_SET_BLOCK_COUNT,1,nblks); //发送CMD23,设置块数量,短响应 errorstatus=CmdResp1Error(SD_CMD_SET_BLOCK_COUNT);//等待R1响应 if(errorstatus!=SD_OK)return errorstatus; } SDMMC_Send_Cmd(SD_CMD_WRITE_MULT_BLOCK,1,addr); //发送CMD25,多块写指令,短响应 errorstatus=CmdResp1Error(SD_CMD_WRITE_MULT_BLOCK); //等待R1响应 }else //单块写 { SDMMC_Send_Cmd(SD_CMD_SEND_STATUS,1,(u32)RCA<<16); //发送CMD13,查询卡的状态,短响应 errorstatus=CmdResp1Error(SD_CMD_SEND_STATUS); //等待R1响应 if(errorstatus!=SD_OK)return errorstatus; cardstatus=SDMMC1->RESP1; timeout=SD_DATATIMEOUT; while(((cardstatus&0x00000100)==0)&&(timeout>0)) //检查READY_FOR_DATA位是否置位 { timeout--; SDMMC_Send_Cmd(SD_CMD_SEND_STATUS,1,(u32)RCA<<16);//发送CMD13,查询卡的状态,短响应 errorstatus=CmdResp1Error(SD_CMD_SEND_STATUS); //等待R1响应 if(errorstatus!=SD_OK)return errorstatus; cardstatus=SDMMC1->RESP1; } if(timeout==0)return SD_ERROR; SDMMC_Send_Cmd(SD_CMD_WRITE_SINGLE_BLOCK,1,addr); //发送CMD24,写单块指令,短响应 errorstatus=CmdResp1Error(SD_CMD_WRITE_SINGLE_BLOCK);//等待R1响应 } if(errorstatus!=SD_OK)return errorstatus; SDMMC_Send_Data_Cfg(SD_DATATIMEOUT,nblks*blksize,9,0); //blksize,块大小恒为512字节,控制器到卡 SDMMC1->CMD|=1<<6; //CMDTRANS=1,产生一个数据传输命令 timeout=SDMMC_DATATIMEOUT; INTX_DISABLE();//关闭总中断(POLLING模式,严禁中断打断SDMMC读写操作!!!) while(!(SDMMC1->STA&((1<<4)|(1<<1)|(1<<8)|(1<<3))))//下溢/CRC/数据结束/超时 { if(SDMMC1->STA&(1<<14)) //发送区半空,表示至少存了8字(32字节) { if((tlen-bytestransferred)<SD_HALFFIFOBYTES)//不够32字节了 { restwords=((tlen-bytestransferred)%4==0)?((tlen-bytestransferred)/4):((tlen-bytestransferred)/4+1); for(count=0;count<restwords;count++,tempbuff++,bytestransferred+=4) { SDMMC1->FIFO=*tempbuff; } }else //发送区半空,可以发送至少8字(32字节)数据 { for(count=0;count<SD_HALFFIFO;count++) { SDMMC1->FIFO=*(tempbuff+count); } tempbuff+=SD_HALFFIFO; bytestransferred+=SD_HALFFIFOBYTES; } timeout=0X3FFFFFFF; //写数据溢出时间 }else { if(timeout==0)return SD_DATA_TIMEOUT; timeout--; } } SDMMC1->CMD&=~(1<<6); //CMDTRANS=0,结束数据传输 INTX_ENABLE(); //开启总中断 if(SDMMC1->STA&(1<<3)) //数据超时错误 { SDMMC1->ICR|=1<<3; //清错误标志 return SD_DATA_TIMEOUT; }else if(SDMMC1->STA&(1<<1)) //数据块CRC错误 { SDMMC1->ICR|=1<<1; //清错误标志 if(nblks>1) //针对可能出现的CRC错误,如果是多块读取,必须发送结束传输命令! { SDMMC_Send_Cmd(SD_CMD_STOP_TRANSMISSION,1,0); //发送CMD12+结束传输 errorstatus=CmdResp1Error(SD_CMD_STOP_TRANSMISSION);//等待R1响应 } return SD_DATA_CRC_FAIL; }else if(SDMMC1->STA&(1<<4)) //接收fifo下溢错误 { SDMMC1->ICR|=1<<4; //清错误标志 return SD_TX_UNDERRUN; } if((SDMMC1->STA&(1<<8))&&(nblks>1))//多块发送结束,发送结束指令 { if((STD_CAPACITY_SD_CARD_V1_1==CardType)||(STD_CAPACITY_SD_CARD_V2_0==CardType)||(HIGH_CAPACITY_SD_CARD==CardType)) { SDMMC_Send_Cmd(SD_CMD_STOP_TRANSMISSION,1,0); //发送CMD12+结束传输 errorstatus=CmdResp1Error(SD_CMD_STOP_TRANSMISSION);//等待R1响应 if(errorstatus!=SD_OK)return errorstatus; } } SDMMC1->ICR=0X1FE00FFF; //清除所有标记 errorstatus=IsCardProgramming(&cardstate); while((errorstatus==SD_OK)&&((cardstate==SD_CARD_PROGRAMMING)||(cardstate==SD_CARD_RECEIVING))) { errorstatus=IsCardProgramming(&cardstate); } return errorstatus; } //检查CMD0的执行状态 //返回值:sd卡错误码 SD_Error CmdError(void) { SD_Error errorstatus = SD_OK; u32 timeout=SDMMC_CMD0TIMEOUT; while(timeout--) { if(SDMMC1->STA&(1<<7))break;//命令已发送(无需响应) } if(timeout==0)return SD_CMD_RSP_TIMEOUT; SDMMC1->ICR=0X1FE00FFF; //清除标记 return errorstatus; } //检查R7响应的错误状态 //返回值:sd卡错误码 SD_Error CmdResp7Error(void) { SD_Error errorstatus=SD_OK; u32 status; u32 timeout=SDMMC_CMD0TIMEOUT; while(timeout--) { status=SDMMC1->STA; if(status&((1<<0)|(1<<2)|(1<<6)))break;//CRC错误/命令响应超时/已经收到响应(CRC校验成功) } if((timeout==0)||(status&(1<<2))) //响应超时 { errorstatus=SD_CMD_RSP_TIMEOUT; //当前卡不是2.0兼容卡,或者不支持设定的电压范围 SDMMC1->ICR|=1<<2; //清除命令响应超时标志 return errorstatus; } if(status&1<<6) //成功接收到响应 { errorstatus=SD_OK; SDMMC1->ICR|=1<<6; //清除响应标志 } return errorstatus; } //检查R1响应的错误状态 //cmd:当前命令 //返回值:sd卡错误码 SD_Error CmdResp1Error(u8 cmd) { u32 status; while(1) { status=SDMMC1->STA; if(status&((1<<0)|(1<<2)|(1<<6)))break;//CRC错误/命令响应超时/已经收到响应(CRC校验成功) } if(status&(1<<2)) //响应超时 { SDMMC1->ICR=1<<2; //清除命令响应超时标志 SDMMC1->ICR=0X1FE00FFF; //清除标记 return SD_CMD_RSP_TIMEOUT; } if(status&(1<<0)) //CRC错误 { SDMMC1->ICR=1<<0; //清除标志 return SD_CMD_CRC_FAIL; } if(SDMMC1->RESPCMD!=cmd)return SD_ILLEGAL_CMD;//命令不匹配 SDMMC1->ICR=0X1FE00FFF; //清除标记 return (SD_Error)(SDMMC1->RESP1&SD_OCR_ERRORBITS);//返回卡响应 } //检查R3响应的错误状态 //返回值:错误状态 SD_Error CmdResp3Error(void) { u32 status; while(1) { status=SDMMC1->STA; if(status&((1<<0)|(1<<2)|(1<<6)))break;//CRC错误/命令响应超时/已经收到响应(CRC校验成功) } if(status&(1<<2)) //响应超时 { SDMMC1->ICR|=1<<2; //清除命令响应超时标志 return SD_CMD_RSP_TIMEOUT; } SDMMC1->ICR=0X1FE00FFF; //清除标记 return SD_OK; } //检查R2响应的错误状态 //返回值:错误状态 SD_Error CmdResp2Error(void) { SD_Error errorstatus=SD_OK; u32 status; u32 timeout=SDMMC_CMD0TIMEOUT; while(timeout--) { status=SDMMC1->STA; if(status&((1<<0)|(1<<2)|(1<<6)))break;//CRC错误/命令响应超时/已经收到响应(CRC校验成功) } if((timeout==0)||(status&(1<<2))) //响应超时 { errorstatus=SD_CMD_RSP_TIMEOUT; SDMMC1->ICR|=1<<2; //清除命令响应超时标志 return errorstatus; } if(status&1<<0) //CRC错误 { errorstatus=SD_CMD_CRC_FAIL; SDMMC1->ICR|=1<<0; //清除响应标志 } SDMMC1->ICR=0X1FE00FFF; //清除标记 return errorstatus; } //检查R6响应的错误状态 //cmd:之前发送的命令 //prca:卡返回的RCA地址 //返回值:错误状态 SD_Error CmdResp6Error(u8 cmd,u16*prca) { SD_Error errorstatus=SD_OK; u32 status; u32 rspr1; while(1) { status=SDMMC1->STA; if(status&((1<<0)|(1<<2)|(1<<6)))break;//CRC错误/命令响应超时/已经收到响应(CRC校验成功) } if(status&(1<<2)) //响应超时 { SDMMC1->ICR|=1<<2; //清除命令响应超时标志 return SD_CMD_RSP_TIMEOUT; } if(status&1<<0) //CRC错误 { SDMMC1->ICR|=1<<0; //清除响应标志 return SD_CMD_CRC_FAIL; } if(SDMMC1->RESPCMD!=cmd) //判断是否响应cmd命令 { return SD_ILLEGAL_CMD; } SDMMC1->ICR=0X1FE00FFF; //清除所有标记 rspr1=SDMMC1->RESP1; //得到响应 if(SD_ALLZERO==(rspr1&(SD_R6_GENERAL_UNKNOWN_ERROR|SD_R6_ILLEGAL_CMD|SD_R6_COM_CRC_FAILED))) { *prca=(u16)(rspr1>>16); //右移16位得到,rca return errorstatus; } if(rspr1&SD_R6_GENERAL_UNKNOWN_ERROR)return SD_GENERAL_UNKNOWN_ERROR; if(rspr1&SD_R6_ILLEGAL_CMD)return SD_ILLEGAL_CMD; if(rspr1&SD_R6_COM_CRC_FAILED)return SD_COM_CRC_FAILED; return errorstatus; } //SDMMC使能宽总线模式 //enx:0,不使能;1,使能; //返回值:错误状态 SD_Error SDEnWideBus(u8 enx) { SD_Error errorstatus = SD_OK; u32 scr[2]={0,0}; u8 arg=0X00; if(enx)arg=0X02; else arg=0X00; if(SDMMC1->RESP1&SD_CARD_LOCKED)return SD_LOCK_UNLOCK_FAILED;//SD卡处于LOCKED状态 errorstatus=FindSCR(RCA,scr); //得到SCR寄存器数据 if(errorstatus!=SD_OK)return errorstatus; if((scr[1]&SD_WIDE_BUS_SUPPORT)!=SD_ALLZERO) //支持宽总线 { SDMMC_Send_Cmd(SD_CMD_APP_CMD,1,(u32)RCA<<16); //发送CMD55+RCA,短响应 errorstatus=CmdResp1Error(SD_CMD_APP_CMD); if(errorstatus!=SD_OK)return errorstatus; SDMMC_Send_Cmd(SD_CMD_APP_SD_SET_BUSWIDTH,1,arg);//发送ACMD6,短响应,参数:10,4位;00,1位. errorstatus=CmdResp1Error(SD_CMD_APP_SD_SET_BUSWIDTH); return errorstatus; }else return SD_REQUEST_NOT_APPLICABLE; //不支持宽总线设置 } //检查卡是否正在执行写操作 //pstatus:当前状态. //返回值:错误代码 SD_Error IsCardProgramming(u8 *pstatus) { vu32 respR1 = 0, status = 0; SDMMC_Send_Cmd(SD_CMD_SEND_STATUS,1,(u32)RCA<<16); //发送CMD13 status=SDMMC1->STA; while(!(status&((1<<0)|(1<<6)|(1<<2))))status=SDMMC1->STA;//等待操作完成 if(status&(1<<0)) //CRC检测失败 { SDMMC1->ICR|=1<<0; //清除错误标记 return SD_CMD_CRC_FAIL; } if(status&(1<<2)) //命令超时 { SDMMC1->ICR|=1<<2; //清除错误标记 return SD_CMD_RSP_TIMEOUT; } if(SDMMC1->RESPCMD!=SD_CMD_SEND_STATUS)return SD_ILLEGAL_CMD; SDMMC1->ICR=0X1FE00FFF; //清除所有标记 respR1=SDMMC1->RESP1; *pstatus=(u8)((respR1>>9)&0x0000000F); return SD_OK; } //读取当前卡状态 //pcardstatus:卡状态 //返回值:错误代码 SD_Error SD_SendStatus(uint32_t *pcardstatus) { SD_Error errorstatus = SD_OK; if(pcardstatus==NULL) { errorstatus=SD_INVALID_PARAMETER; return errorstatus; } SDMMC_Send_Cmd(SD_CMD_SEND_STATUS,1,RCA<<16); //发送CMD13,短响应 errorstatus=CmdResp1Error(SD_CMD_SEND_STATUS); //查询响应状态 if(errorstatus!=SD_OK)return errorstatus; *pcardstatus=SDMMC1->RESP1;//读取响应值 return errorstatus; } //返回SD卡的状态 //返回值:SD卡状态 SDCardState SD_GetState(void) { u32 resp1=0; if(SD_SendStatus(&resp1)!=SD_OK)return SD_CARD_ERROR; else return (SDCardState)((resp1>>9) & 0x0F); } //查找SD卡的SCR寄存器值 //rca:卡相对地址 //pscr:数据缓存区(存储SCR内容) //返回值:错误状态 SD_Error FindSCR(u16 rca,u32 *pscr) { SD_Error errorstatus = SD_OK; u32 tempscr[2]={0,0}; SDMMC_Send_Cmd(SD_CMD_SET_BLOCKLEN,1,8); //发送CMD16,短响应,设置Block Size为8字节 errorstatus=CmdResp1Error(SD_CMD_SET_BLOCKLEN); if(errorstatus!=SD_OK)return errorstatus; SDMMC_Send_Cmd(SD_CMD_APP_CMD,1,(u32)rca<<16); //发送CMD55,短响应 errorstatus=CmdResp1Error(SD_CMD_APP_CMD); if(errorstatus!=SD_OK)return errorstatus; SDMMC_Send_Data_Cfg(SD_DATATIMEOUT,8,3,1); //8个字节长度,block为8字节,SD卡到SDMMC. SDMMC_Send_Cmd(SD_CMD_SD_APP_SEND_SCR,1,0); //发送ACMD51,短响应,参数为0 errorstatus=CmdResp1Error(SD_CMD_SD_APP_SEND_SCR); if(errorstatus!=SD_OK)return errorstatus; while(!(SDMMC1->STA&(SDMMC_STA_RXOVERR|SDMMC_STA_DCRCFAIL|SDMMC_STA_DTIMEOUT|SDMMC_STA_DBCKEND|SDMMC_STA_DATAEND))) { if(!(SDMMC1->STA&(1<<19))) //接收FIFO数据可用 { tempscr[0]=SDMMC1->FIFO; //读取FIFO内容 tempscr[1]=SDMMC1->FIFO; //读取FIFO内容 break; } } if(SDMMC1->STA&(1<<3)) //接收数据超时 { SDMMC1->ICR|=1<<3; //清除标记 return SD_DATA_TIMEOUT; }else if(SDMMC1->STA&(1<<1))//已发送/接收的数据块CRC校验错误 { SDMMC1->ICR|=1<<1; //清除标记 return SD_DATA_CRC_FAIL; }else if(SDMMC1->STA&(1<<5))//接收FIFO溢出 { SDMMC1->ICR|=1<<5; //清除标记 return SD_RX_OVERRUN; } SDMMC1->ICR=0X1FE00FFF; //清除标记 //把数据顺序按8位为单位倒过来. *(pscr+1)=((tempscr[0]&SD_0TO7BITS)<<24)|((tempscr[0]&SD_8TO15BITS)<<8)|((tempscr[0]&SD_16TO23BITS)>>8)|((tempscr[0]&SD_24TO31BITS)>>24); *(pscr)=((tempscr[1]&SD_0TO7BITS)<<24)|((tempscr[1]&SD_8TO15BITS)<<8)|((tempscr[1]&SD_16TO23BITS)>>8)|((tempscr[1]&SD_24TO31BITS)>>24); return errorstatus; } //读SD卡 //buf:读数据缓存区 //sector:扇区地址 //cnt:扇区个数 //返回值:错误状态;0,正常;其他,错误代码; u8 SD_ReadDisk(u8*buf,u32 sector,u32 cnt) { u8 sta=SD_OK; long long lsector=sector; u32 n; if(CardType!=STD_CAPACITY_SD_CARD_V1_1)lsector<<=9; if((u32)buf%4!=0) { for(n=0;n<cnt;n++) { sta=SD_ReadBlocks(SDMMC_DATA_BUFFER,lsector+512*n,512,1);//单个sector的读操作 memcpy(buf,SDMMC_DATA_BUFFER,512); buf+=512; } }else sta=SD_ReadBlocks(buf,lsector,512,cnt); //单个/多个sector return sta; } //写SD卡 //buf:写数据缓存区 //sector:扇区地址 //cnt:扇区个数 //返回值:错误状态;0,正常;其他,错误代码; u8 SD_WriteDisk(u8*buf,u32 sector,u32 cnt) { u8 sta=SD_OK; u32 n; long long lsector=sector; if(CardType!=STD_CAPACITY_SD_CARD_V1_1)lsector<<=9; if((u32)buf%4!=0) { for(n=0;n<cnt;n++) { memcpy(SDMMC_DATA_BUFFER,buf,512); sta=SD_WriteBlocks(SDMMC_DATA_BUFFER,lsector+512*n,512,1);//单个sector的写操作 buf+=512; } }else sta=SD_WriteBlocks(buf,lsector,512,cnt); //单个/多个sector return sta; }
参考:
野火 《零死角玩转STM32》
正点原子《STM32H7开发指南-HAL库版本_V1.0》
最后
以上就是善良樱桃最近收集整理的关于SD卡驱动的全部内容,更多相关SD卡驱动内容请搜索靠谱客的其他文章。
发表评论 取消回复