SD卡(Secure Digital Memory Card)具有体积小、容量大、数据传输快、可插拔、安全性好等优点,被广泛应用于便携式设备上。例如作为数码相机的存储卡,作为手机、平板多媒体扩展卡用的TF卡(micro sd)。 笔者此处就s3c2416 sd卡驱动的实现作一个简单的介绍。

1、sd卡概述

sd卡技术是在MMC卡的基础上发展起来的,其尺寸与MMC卡一样,只是比MMC卡厚了0.7mm,因此sd设备可以识别并存取MMC卡。sd卡接口除了保留MMC卡的7针外,还在两边加了2针,作为数据线,目的是通过把传输方式由串行变成并行,以提高传输速率。 此时的规范为sd1.0版本,最高容量只能到4GB。为了跟进产品的更新换代,sd联合协会在06年发布了容量更大、存储更快的下一代sd卡规范sd2.0。该规范重新定义了sd卡的速度等级,分为三档:Class 2、4、6,分别对应写入速度2MB/s、4MB/s、6MB/s。根据卡容量又分为标准卡(小于2GB)和高容量卡(2GB ~ 32GB),目前市面上应用的sd卡绝大部分都是sd2.0版本的卡。为了让储存卡更加迷你,通过sd卡规范标准,又衍生了MiniSD卡和Micro SD卡,这些卡均比标准sd卡尺寸小,通过sd转接卡可以当作一般的sd卡使用。尤其是Micro SD卡,可以算是最小的存储卡了,超小的体积可以极大的节省消费电子产品内部设计的空间,基本目前的android手机均是选用Micro SD卡作为多媒体扩展储存卡。随着科技的进步,sd2.0规范sd卡也渐渐无法满足应用的需求,在10年sd联合协会又发布了新的sd3.0规范,该规范定义了sdxc和uhs,并增加了Class10,容量范围为32GB ~ 2TB。在sdxc卡仍需进一步坐等其价格下降的情况下,sd4.0规范已经开始在紧张的制订中,这已超出本文的讨论范围内了。

2、sd卡驱动编写

sd卡共支持三种传输模式:spi模式、1位sd模式、4位sd模式。所有的sd卡都必须支持较老的spi/mmc模式,这个模式支持慢速的四线spi接口,使很多微控制器都可以通过spi或模拟spi接口来读写sd卡。由于s3c2416具有sd总线控制器,并且兼容sd2.0的sd卡,因此此处只分析4位sd模式、sd2.0及sd1.0版本的sd卡驱动实现,sd2.0以上版本sd卡、MMC卡、spi方式读写sd卡在本文不适用。 sd卡驱动的编写必须参考sd2.0规范,此处只根据sd2.0规范讲解几个重要的过程或概念,这些过程具体的实现请参考sd驱动模块中相应的函数实现。

2.1、sd卡初始化及识别过程

sd卡上电后,将进入idle状态,此时的sd卡为1位sd模式。通过拉低CS线将可使sd进入spi模式(不再讨论范围内),在sd模式下卡的初始化及识别过程见图2.1.1,其步骤如下: 1) 发送CMD0软件复位所有的卡到idle状态。 2) 发送CMD8来检查卡是否支持主机电压(2.7v ~ 3.3v),这个命令在sd2.0以上才被定义,若没有收到回复信号,则可能为sd1.0或MMC卡,若接收到卡回复信号,说明为sd2.0版本卡,跳转到步骤5 3) CMD8没有收到回复信号,可进一步发送ACMD41(CMD55+CMD41),参数HCS位为0(非高容量卡),如果没有回复信号,说明是MMC卡或其它不能识别的卡,可进一步发送CMD1确定是否MMC卡(此处不再分析) 4) ACMD41能收到回复,并且从回复中确定sd卡己准备好,即可确定这是sd1.x版本的卡,若回复中表明sd卡未准备好,则需重复发送ACMD41等待卡准备好,可通过超时(卡一直busy)判断卡不支持主机电压,此时表明卡不可用。判断出sd1.x的卡后,跳转到步骤9 5) CMD8有回复说明为sd2.0以上的卡,从回复中确定卡是否能在该电压下工作,不能则认为卡不可用。 6) 回复中确定卡能在2.7v~3.3v电压工作后,进一步发送ACMD41(CMD55+CMD41),参数HCS位为1表明主机支持高容量的卡 7) 检查ACMD41卡回复中忙标志,若卡处于忙状态,则重复发送ACDM41,直到卡准备好,可通过超时(卡一直忙状态)可认为该卡不可用。 8) ACMD41回复准备好后,再检查回复中的CCS位,该位为1说明是sd2.0高容量sdhc卡,若为0,则说明为sd2.0标准容量卡。 9) 在识别出sd1.x、sd2.0标准卡或sd2.0高容量卡后,此时卡进入ready态。进一步通过CMD2请求卡发送其CID(Card Identification),此时卡进入Identification态。 10) 卡在Identification态后,发送CMD3请求卡发布一个16位新的相对地址(RCA),以后主机与卡之间的点对点通信均会以这个RCA地址来进行,此时卡进入Stand-by态。 11) 至此,卡的初始化及识别过程结束,此时卡进入数据传输模式(data transfer mode)

16_SD卡驱动实现 - 图1

图2.1.1. sd卡初始化及识别流程

2.2、 数据传输模式

sd卡主控制器是一个非常典型的状态机,每个状态只会响应该个状态下的特定命令,不要尝试在某个状态下发送这个状态不支持的命令,sd卡不会对该命令进行响应,命令只会超时。应该通过特定的触发条件转变状态或等待状态迁移完成后,再发送对应状态的命令。如图2.2.1,要想写一个块的数据到sd卡,在stand-by态的情况下,必须通过CMD7选择卡,让卡进入transfer态,然后再发送CMD24单块写命令,再发送一块的数据,此时卡进入Programming态,这时如果又紧接发送CMD24进行单块写将不会成功,必须等待sd卡编程完,从Programming态返回到transfer态才能再次接收下一个块写命令。同样,在transfer态想通过CMD9来获得Card-Specific Data(CSD),必须通过CMD7取消选择卡,此时卡进入stand-by态后,即可通过CMD9来获得卡信息。

16_SD卡驱动实现 - 图2

图2.2.1. sd卡数据传输模式

2.3、主机控制器具体对卡的初始化

任何cpu的sd卡主机控制器都可以根据sd2.0规范给出的卡初始化及识别流程进行卡的初始化,对于具体的cpu,需要进行一些与控制器相关的设置,主要有以下几点,具体的实现可参考Hsmmc_Init()这个初始化函数。 1) 设置功能引脚,把相应引脚配置成sd接口用引脚 2) 设置sd卡时钟在100k ~ 400k,sd卡在识别阶段必须用慢速时钟进行访问 3) 按照规范给出的卡初始化流程对卡进行发送相应的命令并处理回复,成功后卡进入stand-by态 4) 通过发送CMD7选择卡,使卡进入transfer态,因为卡的大部分操作如读、写、擦除等均是在这个状态下来进行的,此时卡已完全准备好接收读写命令了。 5) 设置sd卡的时钟到一个较高值,sd卡默认支持最高25M时钟,可以设置成高速模式,最高支持50M,频率越高,数据传输速率越快 6) 通过ACMD6(CMD55+CMD6)来设置sd模式的位宽为4,sd卡初始化后默认是1线宽,更多的数据线将有更大的带宽,数据传输速率最高12.5MB/s(25M、4线)或25MB/s(50M、4线)。 7) 发送CMD16设置块长度,对于标准卡,可通过CMD16来设置块命令(如块读、块写)所操作块的长度(以字节数计),可实现字节的读写,但对于高容量卡这个命令将被忽略,高容量卡一个块的长度均是固定512字节的。通常通过CMD16设置块长度为512字节。至此卡初始化完成。

2.4、主机命令的发送

sd规范对命令包格式、回复包、数据的传输方式等均作了详细的要求。虽然sd卡主机控制器可以帮我们对命令进行打包,对回复进行解包,产生CRC,并在sd总线上输出相应的时序。我们仍需要告诉sd卡主机控制器需发送的命令、这个命令的参数、这个命令发送后是否需要使用data线, sd卡的回复类型。具体到s3c2416的sd卡主机控制器,这些设置通过CMDREG寄存器来实现。主要有以下几点,具体的实现可参考Hsmmc_IssueCommand()这个命令发送函数。 1) 命令发送时,需检查命令线是否已被使用,若是,则等待正在发送的命令发送完才能发送这个命令 2) 如果命令回复会带忙信号(如R1b回复),则需检查数据线是否已被使用,若是,则等待数据线空闲,带忙回复命令发送后,sd卡会拉低DAT[0]线表明sd卡正忙,数据线不可用。 3) 把命令参数写入ARGUMENT这个寄存器中 4) 在CMDREG中设置命令值[13:8] 5) 设置是否需使用data线,如块读、块写等命令发送后,会紧接着在data线上传输数据,其它不需传输数据的命令不要设置使用data线CMDREG[5] 6) 设置sd卡的回复类型,绝大部分命令在sd卡正确响应后,都会对主机进行回复(R1-R7,R1b),每个命令对应的回复类型请参考sd卡规范。回复类型长度可能为136或48,回复中是否包含CRC或命令值的反馈,如果包含,则告诉主控制器检查回复中相应的CRC或命令值反馈是否正确,以确定传输正确。CMDREG设置好后,主控制器就会发送命令并接收设定长度的回复并根据设定检查CRC、命令值反馈是否正确(若回复中包含CRC或命令值反馈的话) 7) 等待命令完成,检查中断状态位NORINTSTS[15]以确定命令是否有错误,若没有错误并且检测到NORINTSTS[0]命令完成位为1,则说明命令发送成功。其它情况说明命令未能成功发送。

2.5、主机对sd卡的读写

通常对于一个sd卡驱动模块,至少实现卡初始化、块读、块写这三个接口函数。块读、块写必须在卡初始化完成后,在transfer态下才有效。通常有以下几点需要注意,具体可参考Hsmmc_ReadBlock()和Hsmmc_WriteBlock()这两个函数的实现。 1) 通过发送CMD13获得目前卡的状态,块读、块写时必须在transfer态,不然需等待状态转换的完成 2) 设置特定主机控制器的传输方式,例如s3c2416可支持DMA传输数据,通过FIFO传输数据,笔者采用DMA传输数据,则需设置DMA传输内存的首地址,传输的块数等寄存器,在开了cache的情况下,DMA传输时必须注意cache与主存数据一致性的问题。 3) 注意块写的地址,对于标准卡(小于2GB),其块地址应为字节地址(最大寻址4GB),但对于高容量SDHC卡,其块地址为512字节为单位。 4) 判断读写的块数,对于读写1块,则需发送命令CMD17(单块读)或CMD24(单块写),若多块读写则应发送命令CMD18(多块读)或CMD25(多块写)。通常上层的应用不应把数据块拆分成多个单块进行读写,这样总的读写性能会很差,应该一次性进行多个数据块的读写。 5) 等待读写数据传输完成,检查中断状态位NORINTSTS[15]以确定传输是否有错误,若没有错误并且检测到NORINTSTS[1]传输完成位为1,则说明数据传输成功。其它情况说明数据传输出错。

2.6、驱动模块的其它接口

一个完善的sd卡驱动模块还应提供一些与文件系统相关的接口实现,如从卡CSD中获取容量等信息函数Hsmmc_Get_CSD(),块擦除函数Hsmmc_EraseBlock()等。具体的实现可以参考下面完整的驱动源码,为调试sd卡驱动,需用串口进行打印跟踪sd卡的状态及相关的寄存器值,串口打印调试的驱动在前面章节有详细的介绍。 Sd卡驱动模块实现Hsmmc.c如下:

  1. #include "s3c2416.h"
  2. #include "Hsmmc.h"
  3. #include "UART0.h"
  4. #define DEBUG_HSMMC
  5. #ifdef DEBUG_HSMMC
  6. #define Debug(x...) Uart0_Printf(x)
  7. #else
  8. #define Debug(x...)
  9. #endif
  10. static unsigned char CardType; // 卡类型
  11. static unsigned int RCA; // 卡相对地址
  12. static unsigned char Hsmmc_Buffer[16*1024]
  13. __attribute__((__aligned__(4),section(".no_cache")));
  14. static void Delay_us(unsigned int nCount)
  15. {
  16. //延时1us,共延时nCount us
  17. __asm__ __volatile__ (
  18. "000:\n"
  19. "ldr r1, =100\n" // Arm clock为400M
  20. "111:\n"
  21. "subs r1, r1,#1\n" // 一个Arm clock
  22. "bne 111b\n" // 跳转会清流水线,3个Arm clock
  23. "subs %0, %1,#1\n" // 调用者确保nCount不为0
  24. "bne 000b\n"
  25. :"=r"(nCount) // nCount寄存器的值会自减改变
  26. :"0"(nCount) // 使用与输出对象相同的寄存器
  27. : "r1"// 临时使用了r1寄存器
  28. );
  29. }
  30. static void Hsmmc_ClockOn(unsigned char On)
  31. {
  32. if (On) {
  33. rHM1_CLKCON |=(1<<2); // sd时钟使能
  34. while(!(rHM1_CLKCON & (1<<3))) {
  35. // 等待SD输出时钟稳定
  36. }
  37. }
  38. else {
  39. rHM1_CLKCON &=~(1<<2); // sd时钟禁止
  40. }
  41. }
  42. static void Hsmmc_SetClock(unsigned int Div)
  43. {
  44. Hsmmc_ClockOn(0); // 关闭时钟
  45. // 选择SCLK_HSMMC:EPLLout
  46. rCLKSRC &=~(1<<17); // HSMMC1 EPLL(96M)
  47. rHM1_CONTROL2 =0xc0000120; // SCLK_HSMMC
  48. rHM1_CONTROL3 =(0<<31) | (0<<23) | (0<<15) | (0<<7);
  49. // SDCLK频率值并使能内部时钟
  50. rHM1_CLKCON &=~(0xff<<8);
  51. rHM1_CLKCON |=(Div<<8) | (1<<0);
  52. while (!(rHM1_CLKCON& (1<<1))) {
  53. // 等待内部时钟振荡稳定
  54. }
  55. Hsmmc_ClockOn(1); // 全能时钟
  56. }
  57. static int Hsmmc_WaitForCommandDone()
  58. {
  59. unsigned int i;
  60. int ErrorState;
  61. // 等待命令发送完成
  62. for (i=0;i<20000000; i++) {
  63. if (rHM1_NORINTSTS& (1<<15)) { // 出现错误
  64. break;
  65. }
  66. if (rHM1_NORINTSTS& (1<<0)) {
  67. do {
  68. rHM1_NORINTSTS= (1<<0); // 清除命令完成位
  69. } while(rHM1_NORINTSTS & (1<<0));
  70. return 0; // 命令发送成功
  71. }
  72. }
  73. ErrorState =rHM1_ERRINTSTS & 0x1ff; // 可能通信错误,CRC检验错误,超时等
  74. rHM1_NORINTSTS =rHM1_NORINTSTS; // 清除中断标志
  75. rHM1_ERRINTSTS =rHM1_ERRINTSTS; // 清除错误中断标志
  76. do {
  77. rHM1_NORINTSTS =(1<<0); // 清除命令完成位
  78. } while(rHM1_NORINTSTS & (1<<0));
  79. Debug("Commanderror, rHM1_ERRINTSTS = 0x%x ", ErrorState);
  80. return ErrorState; // 命令发送出错
  81. }
  82. static int Hsmmc_WaitForTransferDone()
  83. {
  84. int ErrorState;
  85. unsigned int i;
  86. // 等待数据传输完成
  87. for (i=0;i<20000000; i++) {
  88. if (rHM1_NORINTSTS& (1<<15)) { // 出现错误
  89. break;
  90. }
  91. if (rHM1_NORINTSTS& (1<<1)) { // 数据传输完
  92. do {
  93. rHM1_NORINTSTS|= (1<<1); // 清除传输完成位
  94. } while(rHM1_NORINTSTS & (1<<1));
  95. rHM1_NORINTSTS= (1<<3); // 清除DMA中断标志
  96. return 0;
  97. }
  98. Delay_us(1);
  99. }
  100. ErrorState =rHM1_ERRINTSTS & 0x1ff; // 可能通信错误,CRC检验错误,超时等
  101. rHM1_NORINTSTS =rHM1_NORINTSTS; // 清除中断标志
  102. rHM1_ERRINTSTS =rHM1_ERRINTSTS; // 清除错误中断标志
  103. Debug("Transfererror, rHM1_ERRINTSTS = 0x%04x\n\r", ErrorState);
  104. do {
  105. rHM1_NORINTSTS =(1<<1); // 出错后清除数据完成位
  106. } while(rHM1_NORINTSTS & (1<<1));
  107. return ErrorState; // 数据传输出错
  108. }
  109. static int Hsmmc_IssueCommand(unsigned char Cmd, unsigned intArg, unsigned char Data, unsigned char Response)
  110. {
  111. unsigned int i;
  112. unsigned int Value;
  113. unsigned intErrorState;
  114. // 检查CMD线是否准备好发送命令
  115. for (i=0;i<1000000; i++) {
  116. if (!(rHM1_PRNSTS& (1<<0))) {
  117. break;
  118. }
  119. }
  120. if (i == 1000000) {
  121. Debug("CMDline time out, rHM1_PRNSTS: %04x\n\r", rHM1_PRNSTS);
  122. return -1; // 命令超时
  123. }
  124. // 检查DAT线是否准备好
  125. if (Response ==Response_R1b) { // R1b回复通过DAT0反馈忙信号
  126. for (i=0;i<1000000; i++) {
  127. if(!(rHM1_PRNSTS & (1<<1))) {
  128. break;
  129. }
  130. }
  131. if (i == 1000000){
  132. Debug("Dataline time out, rHM1_PRNSTS: %04x\n\r", rHM1_PRNSTS);
  133. return -2;
  134. }
  135. }
  136. rHM1_ARGUMENT = Arg;// 写入命令参数
  137. Value = (Cmd <<8); // command index
  138. // CMD12可终止传输
  139. if (Cmd == 0x12) {
  140. Value |= (0x3<< 6); // command type
  141. }
  142. if (Data) {
  143. Value |= (1<< 5); // 需使用DAT线作为传输等
  144. }
  145. switch (Response) {
  146. case Response_NONE:
  147. Value |=(0<<4) | (0<<3) | 0x0; // 没有回复,不检查命令及CRC
  148. break;
  149. case Response_R1:
  150. case Response_R5:
  151. case Response_R6:
  152. case Response_R7:
  153. Value |=(1<<4) | (1<<3) | 0x2; // 检查回复中的命令,CRC
  154. break;
  155. case Response_R2:
  156. Value |=(0<<4) | (1<<3) | 0x1; // 回复长度为136位,包含CRC
  157. break;
  158. case Response_R3:
  159. case Response_R4:
  160. Value |=(0<<4) | (0<<3) | 0x2; // 回复长度48位,不包含命令及CRC
  161. break;
  162. case Response_R1b:
  163. Value |=(1<<4) | (1<<3) | 0x3; // 回复带忙信号,会占用Data[0]线
  164. break;
  165. default:
  166. break;
  167. }
  168. rHM1_CMDREG = Value;
  169. ErrorState =Hsmmc_WaitForCommandDone();
  170. if (ErrorState) {
  171. Debug("Command= %d\r\n", Cmd);
  172. }
  173. return ErrorState; // 命令发送出错
  174. }
  175. // 512位的sd卡扩展状态位
  176. int Hsmmc_GetSdState(unsigned char *pState)
  177. {
  178. int ErrorState;
  179. unsigned int i;
  180. if (CardType == SD_HC|| CardType == SD_V2 || CardType == SD_V1) {
  181. if(Hsmmc_GetCardState() != 4) { // 必需在transfer status
  182. return -1; // 卡状态错误
  183. }
  184. Hsmmc_IssueCommand(CMD55,RCA<<16, 0, Response_R1);
  185. rHM1_SYSAD =(unsigned int)Hsmmc_Buffer; // 缓存地址
  186. rHM1_BLKSIZE =(7<<12) | (64<<0); // 最大DMA缓存大小,block为512位64字节
  187. rHM1_BLKCNT = 1;// 写入这次读1block的sd状态数据
  188. rHM1_ARGUMENT = 0;// 写入命令参数
  189. // DMA传输使能,读单块
  190. rHM1_TRNMOD =(0<<5) | (1<<4) | (0<<2) | (1<<1) | (1<<0);
  191. // 设置命令寄存器,读状态命令CMD13,R1回复
  192. rHM1_CMDREG =(CMD13<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;
  193. ErrorState =Hsmmc_WaitForCommandDone();
  194. if (ErrorState) {
  195. Debug("CMD13error\r\n");
  196. returnErrorState;
  197. }
  198. ErrorState =Hsmmc_WaitForTransferDone();
  199. if (ErrorState) {
  200. Debug("Getsd status error\r\n");
  201. returnErrorState;
  202. }
  203. for (i=0; i<64;i++) {
  204. *pState++ =Hsmmc_Buffer[i];
  205. }
  206. return 0;
  207. }
  208. return -1; // 非sd卡
  209. }
  210. int Hsmmc_Get_CSD(unsigned char *pCSD)
  211. {
  212. unsigned int i;
  213. unsigned intResponse[4];
  214. int State = 1;
  215. if (CardType != SD_HC&& CardType != SD_V1 && CardType != SD_V2) {
  216. return State; // 未识别的卡
  217. }
  218. // 取消卡选择,任何卡均不回复,已选择的卡通过RCA=0取消选择,
  219. // 卡回到stand-by状态
  220. Hsmmc_IssueCommand(CMD7,0, 0, Response_NONE);
  221. for (i=0; i<1000;i++) {
  222. if(Hsmmc_GetCardState() == 3) { // CMD9命令需在standy-by status
  223. Debug("GetCSD: Enter to the Stand-by State\n\r");
  224. break; // 状态正确
  225. }
  226. Delay_us(100);
  227. }
  228. if (i == 1000) {
  229. return State; // 状态错误
  230. }
  231. // 请求已标记卡发送卡特定数据(CSD),获得卡信息
  232. if(!Hsmmc_IssueCommand(CMD9, RCA<<16, 0, Response_R2)) {
  233. pCSD++; // 路过第一字节,CSD中[127:8]位对位寄存器中的[119:0]
  234. Response[0] =rHM1_RSPREG0;
  235. Response[1] =rHM1_RSPREG1;
  236. Response[2] =rHM1_RSPREG2;
  237. Response[3] =rHM1_RSPREG3;
  238. Debug("CSD:");
  239. for (i=0; i<15;i++) { // 拷贝回复寄存器中的[119:0]到pCSD中
  240. *pCSD++ =((unsigned char *)Response)[i];
  241. Debug("%02x",*(pCSD-1));
  242. }
  243. State = 0; // CSD获取成功
  244. }
  245. Hsmmc_IssueCommand(CMD7,RCA<<16, 0, Response_R1); // 选择卡,卡回到transfer状态
  246. return State;
  247. }
  248. // R1回复中包含了32位的card state,卡识别后,可在任一状态通过CMD13获得卡状态
  249. int Hsmmc_GetCardState(void)
  250. {
  251. if(Hsmmc_IssueCommand(CMD13, RCA<<16, 0, Response_R1)) {
  252. return -1; // 卡出错
  253. } else {
  254. return((rHM1_RSPREG0>>9) & 0xf); // 返回R1回复中的[12:9]卡状态
  255. }
  256. }
  257. static int Hsmmc_SetBusWidth(unsigned char Width)
  258. {
  259. int State;
  260. if ((Width != 1) ||(Width != 4)) {
  261. return -1;
  262. }
  263. State = -1; // 设置初始为未成功
  264. rHM1_NORINTSTSEN&= ~(1<<8); // 关闭卡中断
  265. Hsmmc_IssueCommand(CMD55,RCA<<16, 0, Response_R1);
  266. if (Width == 1) {
  267. if(!Hsmmc_IssueCommand(CMD6, 0, 0, Response_R1)) { // 1位宽
  268. rHM_HOSTCTL&= ~(1<<1);
  269. State = 0; // 命令成功
  270. }
  271. } else {
  272. if(!Hsmmc_IssueCommand(CMD6, 2, 0, Response_R1)) { // 4位宽
  273. rHM_HOSTCTL |=(1<<1);
  274. State = 0; // 命令成功
  275. }
  276. }
  277. rHM1_NORINTSTSEN |=(1<<8); // 打开卡中断
  278. return State; // 返回0为成功
  279. }
  280. int Hsmmc_EraseBlock(unsigned int StartBlock, unsigned intEndBlock)
  281. {
  282. unsigned int i;
  283. if (CardType == SD_V1|| CardType == SD_V2) {
  284. StartBlock<<= 9; // 标准卡为字节地址
  285. EndBlock <<=9;
  286. } else if (CardType !=SD_HC) {
  287. return -1; // 未识别的卡
  288. }
  289. Hsmmc_IssueCommand(CMD32,StartBlock, 0, Response_R1);
  290. Hsmmc_IssueCommand(CMD33,EndBlock, 0, Response_R1);
  291. if(!Hsmmc_IssueCommand(CMD38, 0, 0, Response_R1b)) {
  292. for (i=0;i<10000; i++) {
  293. if (Hsmmc_GetCardState()== 4) { // 擦除完成后返回到transfer状态
  294. Debug("erasingcomplete!\n\r");
  295. return 0;// 擦除成功
  296. }
  297. Delay_us(1000);
  298. }
  299. }
  300. Debug("Eraseblock failed\n\r");
  301. return 1; // 擦除失败
  302. }
  303. int Hsmmc_ReadBlock(unsigned char *pBuffer, unsigned intBlockAddr, unsigned int BlockNumber)
  304. {
  305. unsigned int Address =0;
  306. unsigned intReadBlock;
  307. unsigned int i;
  308. int ErrorState;
  309. if (pBuffer == 0 ||BlockNumber == 0) {
  310. return -1;
  311. }
  312. // 均不中断使能,产生相应的中断信号
  313. rHM1_NORINTSIGEN&= ~0xffff; // 清除所有中断使能
  314. rHM1_NORINTSIGEN |=(1<<1); // 命令完成中断使能
  315. while (BlockNumber> 0) {
  316. for (i=0;i<1000; i++) {
  317. if(Hsmmc_GetCardState() == 4) { // 读写数据需在transfer status
  318. break; // 状态正确
  319. }
  320. Delay_us(100);
  321. }
  322. if (i == 1000) {
  323. return -2; // 状态错误
  324. }
  325. if (BlockNumber<= sizeof(Hsmmc_Buffer)/512) {
  326. ReadBlock =BlockNumber; // 读取的块数小于缓存32 Block(16k)
  327. BlockNumber =0; // 剩余读取块数为0
  328. } else {
  329. // 读取的块数大于32 Block,分多次读
  330. ReadBlock =sizeof(Hsmmc_Buffer)/512;
  331. BlockNumber -=ReadBlock;
  332. }
  333. // 根据sd主机控制器标准,按顺序写入主机控制器相应的寄存器
  334. // 缓存地址,内存区域为关闭cache,作DMA传输
  335. rHM1_SYSAD =(unsigned int)Hsmmc_Buffer;
  336. rHM1_BLKSIZE =(7<<12) | (512<<0); // 最大DMA缓存大小,block为512字节
  337. rHM1_BLKCNT =ReadBlock; // 写入这次读block数目
  338. if (CardType ==SD_HC) {
  339. Address =BlockAddr; // SDHC卡写入地址为block地址
  340. } else if(CardType == SD_V1 || CardType == SD_V2) {
  341. Address =BlockAddr << 9; // 标准卡写入地址为字节地址
  342. }
  343. BlockAddr +=ReadBlock; // 下一次读块的地址
  344. rHM1_ARGUMENT =Address; // 写入命令参数
  345. if (ReadBlock ==1) {
  346. // 设置传输模式,DMA传输使能,读单块
  347. rHM1_TRNMOD =(0<<5) | (1<<4) | (0<<2) | (1<<1) | (1<<0);
  348. // 设置命令寄存器,单块读CMD17,R1回复
  349. rHM1_CMDREG =(CMD17<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;
  350. } else {
  351. // 设置传输模式,DMA传输使能,读多块
  352. rHM1_TRNMOD =(1<<5) | (1<<4) | (1<<2) | (1<<1) | (1<<0);
  353. // 设置命令寄存器,多块读CMD18,R1回复
  354. rHM1_CMDREG =(CMD18<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;
  355. }
  356. ErrorState =Hsmmc_WaitForCommandDone();
  357. if (ErrorState) {
  358. Debug("ReadCommand error\r\n");
  359. returnErrorState;
  360. }
  361. ErrorState =Hsmmc_WaitForTransferDone();
  362. if (ErrorState) {
  363. Debug("Readblock error\r\n");
  364. returnErrorState;
  365. }
  366. // 数据传输成功,拷贝DMA缓存的数据到指定内存
  367. for (i=0;i<ReadBlock*512; i++) {
  368. *pBuffer++ =Hsmmc_Buffer[i];
  369. }
  370. }
  371. return 0; // 所有块读完
  372. }
  373. int Hsmmc_WriteBlock(unsigned char *pBuffer, unsigned intBlockAddr, unsigned int BlockNumber)
  374. {
  375. unsigned int Address =0;
  376. unsigned intWriteBlock;
  377. unsigned int i;
  378. int ErrorState;
  379. if (pBuffer == 0 ||BlockNumber == 0) {
  380. return -1; // 参数错误
  381. }
  382. rHM1_NORINTSIGEN&= ~0xffff; // 清除所有中断使能
  383. // 数据传输完成中断使能
  384. rHM1_NORINTSIGEN |=(1<<0);
  385. while (BlockNumber> 0) {
  386. for (i=0;i<1000; i++) {
  387. if(Hsmmc_GetCardState() == 4) { // 读写数据需在transfer status
  388. break; // 状态正确
  389. }
  390. Delay_us(100);
  391. }
  392. if (i == 1000) {
  393. return -2; // 状态错误或Programming超时
  394. }
  395. if (BlockNumber<= sizeof(Hsmmc_Buffer)/512) {
  396. WriteBlock =BlockNumber;// 写入的块数小于缓存32 Block(16k)
  397. BlockNumber =0; // 剩余写入块数为0
  398. } else {
  399. // 写入的块数大于32 Block,分多次写
  400. WriteBlock =sizeof(Hsmmc_Buffer)/512;
  401. BlockNumber -=WriteBlock;
  402. }
  403. if (WriteBlock> 1) { // 多块写,发送ACMD23先设置预擦除块数
  404. Hsmmc_IssueCommand(CMD55,RCA<<16, 0, Response_R1);
  405. Hsmmc_IssueCommand(CMD23,WriteBlock, 0, Response_R1);
  406. }
  407. for (i=0;i<WriteBlock*512; i++) {
  408. Hsmmc_Buffer[i]= *pBuffer++; // 待写数据从指定内存区拷贝到缓存区
  409. }
  410. // 根据sd主机控制器标准,按顺序写入主机控制器相应的寄存器
  411. // 缓存地址,内存区域为关闭cache,作DMA传输
  412. rHM1_SYSAD =(unsigned int)Hsmmc_Buffer;
  413. rHM1_BLKSIZE =(7<<12) | (512<<0); // 最大DMA缓存大小,block为512字节
  414. rHM1_BLKCNT =WriteBlock; // 写入block数目
  415. if (CardType ==SD_HC) {
  416. Address =BlockAddr; // SDHC卡写入地址为block地址
  417. } else if(CardType == SD_V1 || CardType == SD_V2) {
  418. Address =BlockAddr << 9; // 标准卡写入地址为字节地址
  419. }
  420. BlockAddr +=WriteBlock; // 下一次写地址
  421. rHM1_ARGUMENT =Address; // 写入命令参数
  422. if (WriteBlock ==1) {
  423. // 设置传输模式,DMA传输写单块
  424. rHM1_TRNMOD =(0<<5) | (0<<4) | (0<<2) | (1<<1) | (1<<0);
  425. // 设置命令寄存器,单块写CMD24,R1回复
  426. rHM1_CMDREG =(CMD24<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;
  427. } else {
  428. // 设置传输模式,DMA传输写多块
  429. rHM1_TRNMOD =(1<<5) | (0<<4) | (1<<2) | (1<<1) | (1<<0);
  430. // 设置命令寄存器,多块写CMD25,R1回复
  431. rHM1_CMDREG =(CMD25<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;
  432. }
  433. ErrorState =Hsmmc_WaitForCommandDone();
  434. if (ErrorState) {
  435. Debug("WriteCommand error\r\n");
  436. returnErrorState;
  437. }
  438. ErrorState =Hsmmc_WaitForTransferDone();
  439. if (ErrorState) {
  440. Debug("Writeblock error\r\n");
  441. returnErrorState;
  442. }
  443. }
  444. return 0; // 写完所有数据
  445. }
  446. int Hsmmc_Init()
  447. {
  448. unsigned int i;
  449. unsigned int OCR;
  450. // 设置HSMMC1的接口引脚配置
  451. rGPLCON &=~((0xffff<<0) | (0xf<<16));
  452. rGPLCON |=(0xaaaa<<0) | (0xa<<16);
  453. rGPLUDP &=~((0xffff<<0) | (0xf<<16)); // 上下拉禁止
  454. rHM1_SWRST = 0x7; // 复位HSMMC
  455. Hsmmc_SetClock(0x80);// SDCLK=96M/256=375K
  456. rHM1_TIMEOUTCON = (0xe<< 0); // 最大超时时间
  457. rHM1_HOSTCTL &=~(1<<2); // 正常速度模式
  458. rHM1_NORINTSTS =rHM1_NORINTSTS; // 清除中断状态标志
  459. rHM1_ERRINTSTS =rHM1_ERRINTSTS; // 清除错误中断状态标志
  460. rHM1_NORINTSTSEN =0x7fff; // [14:0]中断使能
  461. rHM1_ERRINTSTSEN =0x3ff; // [9:0]错误中断使能
  462. Hsmmc_IssueCommand(CMD0,0, 0, Response_NONE); // 复位所有卡到空闲状态
  463. CardType =UnusableCard; // 卡类型初始化不可用
  464. if (Hsmmc_IssueCommand(CMD8,0x1aa, 0, Response_R7)) { // 没回复,MMC/ v1.x/
  465. for (i=0;i<1000; i++) {
  466. Hsmmc_IssueCommand(CMD55,0, 0, Response_R1);
  467. // CMD41有回复说明为sd卡
  468. if(!Hsmmc_IssueCommand(CMD41, 0, 0, Response_R3)) {
  469. OCR =rHM1_RSPREG0; // 获得回复的OCR(操作条件寄存器)值
  470. if(OCR & 0x80000000) { // 卡上电是否完成上电流程
  471. CardType= SD_V1; // 正确识别出sd v1.x卡
  472. Debug("SDcard version 1.x is detected\n\r");
  473. break;
  474. }
  475. } else {
  476. // MMC卡识别
  477. }
  478. Delay_us(100);
  479. }
  480. } else { // sd v2.0
  481. // 判断卡是否支持2.7~3.3v电压
  482. if(((rHM1_RSPREG0&0xff) == 0xaa) &&(((rHM1_RSPREG0>>8)&0xf) == 0x1)) {
  483. OCR = 0;
  484. for (i=0;i<1000; i++) {
  485. Hsmmc_IssueCommand(CMD55,0, 0, Response_R1);
  486. Hsmmc_IssueCommand(CMD41,OCR, 0, Response_R3); // reday态
  487. OCR =rHM1_RSPREG0;
  488. if (OCR& 0x80000000) { // 卡上电是否完成上电流程,是否busy
  489. if(OCR & (1<<30)) { // 判断卡为标准卡还是高容量卡
  490. CardType= SD_HC; // 高容量卡
  491. Debug("SDHCcard is detected\n\r");
  492. } else{
  493. CardType= SD_V2; // 标准卡
  494. Debug("SDversion 2.0 standard card is detected\n\r");
  495. }
  496. break;
  497. }
  498. Delay_us(100);
  499. }
  500. }
  501. }
  502. if (CardType == SD_HC|| CardType == SD_V1 || CardType == SD_V2) {
  503. // 请求卡发送CID(卡ID寄存器)号,进入ident
  504. Hsmmc_IssueCommand(CMD2,0, 0, Response_R2);
  505. // 请求卡发布新的RCA(卡相对地址),Stand-by状态
  506. Hsmmc_IssueCommand(CMD3,0, 0, Response_R6);
  507. RCA =(rHM1_RSPREG0 >> 16) & 0xffff; // 从卡回复中得到卡相对地址
  508. // 选择已标记的卡,transfer状态
  509. Hsmmc_IssueCommand(CMD7,RCA<<16, 0, Response_R1);
  510. Debug("Enterto the transfer state\n\r");
  511. Hsmmc_SetClock(0x2);// 设置SDCLK= 96M/4 = 24M
  512. if(!Hsmmc_SetBusWidth(4)) {
  513. Debug("Setbus width error\n\r");
  514. return 1; // 位宽设置出错
  515. }
  516. if(Hsmmc_GetCardState() == 4) { // 此时卡应在transfer态
  517. // 设置块长度为512字节
  518. if(!Hsmmc_IssueCommand(CMD16, 512, 0, Response_R1)) {
  519. rHM1_NORINTSTS= 0xffff; // 清除中断标志
  520. Debug("CardInitialization succeed\n\r");
  521. return 0;// 初始化成功
  522. }
  523. }
  524. }
  525. Debug("CardInitialization failed\n\r");
  526. return 1; // 卡工作异常
  527. }

Sd卡驱动模块头文件Hsmmc.h如下:

  1. #ifndef __HSMMC_H__
  2. #define __HSMMC_H__
  3. #ifdef __cplusplus
  4. extern "C" {
  5. #endif
  6. #define CMD0 0
  7. #define CMD1 1
  8. #define CMD2 2
  9. #define CMD3 3
  10. #define CMD6 6
  11. #define CMD7 7
  12. #define CMD8 8
  13. #define CMD9 9
  14. #define CMD13 13
  15. #define CMD16 16
  16. #define CMD17 17
  17. #define CMD18 18
  18. #define CMD23 23
  19. #define CMD24 24
  20. #define CMD25 25
  21. #define CMD32 32
  22. #define CMD33 33
  23. #define CMD38 38
  24. #define CMD41 41
  25. #define CMD55 55
  26. // 卡类型
  27. #define UnusableCard 0
  28. #define SD_V1 1
  29. #define SD_V2 2
  30. #define SD_HC 3
  31. #define MMC 4
  32. #define Response_NONE 0
  33. #define Response_R1 1
  34. #define Response_R2 2
  35. #define Response_R3 3
  36. #define Response_R4 4
  37. #define Response_R5 5
  38. #define Response_R6 6
  39. #define Response_R7 7
  40. #define Response_R1b 8
  41. int Hsmmc_Init(void);
  42. int Hsmmc_GetCardState(void);
  43. int Hsmmc_GetSdState(unsigned char *pState);
  44. int Hsmmc_Get_CSD(unsigned char *pCSD);
  45. int Hsmmc_EraseBlock(unsigned int StartBlock, unsigned int EndBlock);
  46. int Hsmmc_WriteBlock(unsigned char *pBuffer,
  47. unsigned int BlockAddr,unsigned int BlockNumber);
  48. int Hsmmc_ReadBlock(unsigned char *pBuffer,
  49. unsigned int BlockAddr, unsigned intBlockNumber);
  50. #ifdef __cplusplus
  51. }
  52. #endif
  53. #endif /*__HSMMC_H__*/

3. 附录

Hsmmc.rar,包含sd卡驱动模块实现Hsmmc.c/Hsmmc.h。

http://pan.baidu.com/s/1hqvaQgO