基于S3C2440——SD/MMC
来源:互联网 发布:车铣复合加工编程书 编辑:程序博客网 时间:2024/06/05 14:33
下面内容是转载一为网友的内容:http://blog.163.com/zhouhui_1102/blog/static/1504807482011513724255/
之前在http://blog.163.com/zhouhui_1102/blog/static/1504807482011429115933380/,也就是我的SD卡笔记上说了一些需要主要的地方。在这里关于SD和MMC的基础概念我就不提了。一般百科会有相关的知识,可以自行搜索。
这里就笔记上提出来的那两个编程流程图来进行编程。如果还不了解的可以自行查阅我的笔记,或者自己下载SD的datasheet。里面有很详细的说明。
这里说明下我的硬件配置是:2440板子,2G Kingston SD卡。
废话不多说,直接看源码。
###########################################//------------------------SDI.h------------------------#ifndef __SDI_H__#define __SDI_H__#define U32 unsigned int#define U16 unsigned short#define U8 unsigned char#define PCLK 50000000typedef struct SD_STRUCT{U8 sdiWide; // 0:1bit, 1:4bitU8 sdiType; // 0:SD , 1:MMCU16 sdiRCA;U8 cCardCID[16]; // 卡的CID信息U32 lCardCSD[4]; // 卡的CSD信息U32 lSectorSize; /* 一次可擦除的块个数 */ U32 lCardSize; //卡容量(单位:字节)}sdi_set; //卡的信息结构体void test_sdi();U8 sdi_init();U32 SDI_Check_CMD_End(int cmd, int be_resp);void CMD0(void);U8 CMD1(void);U8 CMD2(U8 *cCID_Info);U8 CMD3(U16 iCardType,U16 *iRCA);U8 CMD7(U8 cSorD,U16 iRCA);U8 CMD9(U16 iRCA,U32 *lCSD);U8 CMD12(void);U16 CMD13(U16 iRCA);U8 CMD17(U32 Addr);U8 CMD18(U32 Addr);U8 CMD24(U32 Addr);U8 CMD25(U32 Addr);U8 CMD55(U16 iRCA);U8 ACMD6(U8 BusWidth,U16 iRCA);U8 ACMD41(U16 iRCA);U32 SDI_MMC_OCR(void);U32 SDI_SD_OCR(void);U8 select_or_deselect(U8 cSelDesel,U16 iCardRCA);U8 Set_bus_Width(U8 cCardType,U8 cBusWidth,U16 iRCA);U8 Read_Block(U32 Addr,U32* RxBuffer,U32 block_num);U8 Write_Block(U32 Addr,U32* TxBuffer,U32 block_num);void Delay(U32 i);#endif##############################################
########################################//------------------------SDI.c------------------------#define _SD_DEBUG_#define SDCARD_BUFF_SIZE 512U8 cTxBuffer[SDCARD_BUFF_SIZE];U8 cRxBuffer[SDCARD_BUFF_SIZE];sdi_set SDCard;void test_sdi() //测试函数{U32 i;if(sdi_init()){#ifdef _SD_DEBUG_printf("SDI初始化结束!\r\n");#endif}else{#ifdef _SD_DEBUG_printf("SDI初始化出错,终止!\r\n");#endifreturn;}for(i=0;i<512;i++){cTxBuffer[i]=i+1;cRxBuffer[i]=0;}if(Write_Block(0,(U32 *)cTxBuffer,1)){#ifdef _SD_DEBUG_printf("\r\n写入SD卡0地址数据成功!");#endif}else{#ifdef _SD_DEBUG_printf("\r\n写入SD卡0地址数据出错,终止!");#endifreturn;}if(Read_Block(0,(U32 *)cRxBuffer,1)){#ifdef _SD_DEBUG_printf("\r\n读出SD卡0地址数据成功!");#endifprintf("\r\n读出的数据:\r\n");for(i=0;i<512;i+=4){if(i%32)printf("\r\n");printf("0x%x ",cRxBuffer[i]);printf("0x%x ",cRxBuffer[i+1]);printf("0x%x ",cRxBuffer[i+2]);printf("0x%x ",cRxBuffer[i+3]);}}else{#ifdef _SD_DEBUG_printf("\r\n读出SD卡0地址数据出错,终止!");#endifreturn;}select_or_deselect(0,SDCard.sdiRCA);}U8 sdi_init(){int i;#ifdef _SD_DEBUG_printf("\r\nSDI初始化开始!");#endifGPEUP = 0xf83f; // The pull up 1111 1000 0011 1111 必须上拉 GPECON = 0xaaaaaaaa; // 1010 1010 1010 1010 1010 1010 1010 1010 SDICSTA = 0xffff; //SDI指令状态 SDIDSTA = 0xffff;//SDI数据状态SDIPRE = 124; // 400KHz 波特率设置 频率 PCLK/400K -1 SDICON=(1<<4)|1; // Type B, clk enable SDI控制 SDIFSTA|=1<<16; //FIFO resetSDIBSIZE=0x200; // 512byte(128word) SDI块大小SDIDTIMER=0x7fffff; // Set timeout count 数据传输超时时间//等待74个CLK for(i=0;i<0x1000;i++); CMD0(); //先执行CMD0,复位//判断卡的类型if(SDI_MMC_OCR()) {SDCard.sdiType = 1; //卡为MMC}else{SDCard.sdiType = 0; //卡为SD}//检测SD卡if(SDI_SD_OCR()) //检测SD {#ifdef _SD_DEBUG_printf("SD is ready\r\n");#endif} else{#ifdef _SD_DEBUG_printf("Initialize fail\r\nNo Card assertion\r\n");#endif return 0; } //读CIDif(CMD2(SDCard.cCardCID)){#ifdef _SD_DEBUG_printf("CID\r\n");printf("MID = %d\r\n",SDCard.cCardCID[0]);printf("OLD = %d\r\n",(SDCard.cCardCID[1]*0X100)+SDCard.cCardCID[2]);printf("生产厂家:%s\r\n",(SDCard.cCardCID+3));printf("生产日期:20%d,%d\r\n",((SDCard.cCardCID[13]&0x0f)<<4)+((SDCard.cCardCID[14]&0xf0)>>4),(SDCard.cCardCID[14]&0x0f));#endif}else{#ifdef _SD_DEBUG_printf("Read Card CID is fail!\r\n");#endifreturn 0;}//设置RCA MMC的RCA=1 SD的RCA=0if(SDCard.sdiType==1) //MMC{if(CMD3(1,&SDCard.sdiRCA)){SDCard.sdiRCA = 1;SDIPRE = 2; //16MHZ#ifdef _SD_DEBUG_printf("MMC Card RCA = 0x%x\r\n",SDCard.sdiRCA);printf("MMC Frequency is %dHz\r\n",(PCLK/(SDIPRE+1)));#endif}else{#ifdef _SD_DEBUG_printf("Read MMC RCA is fail!\r\n");#endifreturn 0;}}else //SD{if(CMD3(0,&SDCard.sdiRCA)){SDIPRE = 1; // Normal clock=25MHz#ifdef _SD_DEBUG_printf("SD Card RCA = 0x%x\r\n",SDCard.sdiRCA);printf("SD Frequency is %dHz\r\n",(PCLK/(SDIPRE+1)));#endif}else{#ifdef _SD_DEBUG_printf("Read SD RCA is fail!\r\n");#endifreturn 0;}}//读CSDif(CMD9(SDCard.sdiRCA,SDCard.lCardCSD)){SDCard.lCardSize = (((SDCard.lCardCSD[1]&0x0000003f)<<16)+((SDCard.lCardCSD[2]&0xffff0000)>>16)+1)*512;SDCard.lSectorSize = ((SDCard.lCardCSD[2]>>6)&0x0000007f)+1;#ifdef _SD_DEBUG_printf("Read Card CSD OK!\r\n");printf("0x%08x\r\n",SDCard.lCardCSD[0]);printf("0x%08x\r\n",SDCard.lCardCSD[1]);printf("0x%08x\r\n",SDCard.lCardCSD[2]);printf("0x%08x\r\n",SDCard.lCardCSD[3]);printf("卡容量为:%dKB,%dMB\r\n",SDCard.lCardSize,SDCard.lCardSize/1024);#endif}else{#ifdef _SD_DEBUG_printf("Read Card CSD Fail!\r\n");#endifreturn 0;}//选中卡 CMD7 进入传输状态if(select_or_deselect(1,SDCard.sdiRCA))//1表示选中卡 {#ifdef _SD_DEBUG_printf("Card sel desel OK!\r\n");#endif}else{#ifdef _SD_DEBUG_printf("Card sel desel fail!\r\n");#endifreturn 0;}//CMD13 查询是否为传输状态while((CMD13(SDCard.sdiRCA)&0x1e00) != 0x800);//设置总线带宽 ACMD6if(Set_bus_Width(SDCard.sdiType,1,SDCard.sdiRCA)){SDCard.sdiWide = 1;#ifdef _SD_DEBUG_printf("Bus Width is 4bit\r\n");#endif}else{SDCard.sdiWide = 0;#ifdef _SD_DEBUG_printf("Bus Width is 1bit\r\n");#endif}return 1;}U32 SDI_Check_CMD_End(int cmd, int be_resp) //检查CMD是否结束{ int finish0; if(!be_resp) // No response { finish0=SDICSTA;while((finish0&0x800)!=0x800) // Check cmd end finish0=SDICSTA;SDICSTA=finish0;// Clear cmd end state#ifdef _SD_DEBUG_printf("%x\r\n", finish0);#endifreturn 1; } else // With response { finish0=SDICSTA;while( !( ((finish0&0x200)==0x200) | ((finish0&0x400)==0x400) )) // Check cmd/rsp end finish0=SDICSTA;#ifdef _SD_DEBUG_printf("CMD%d:SDICSTA=0x%x, SDIRSP0=0x%x\r\n",cmd, SDICSTA, SDIRSP0); #endif if(cmd==1 | cmd==9 | cmd==41) // CRC no check{ if( (finish0&0xf00) != 0xa00 ) // Check error {SDICSTA=finish0; // Clear error state if(((finish0&0x400)==0x400)){#ifdef _SD_DEBUG_printf("CMD%d Time out!\r\n", cmd);#endif return 0; // Timeout error } } SDICSTA=finish0; // Clear cmd & rsp end state // printf("%x\r\n", finish0);}else // CRC check{ if( (finish0&0x1f00) != 0xa00 ) // Check error { SDICSTA=finish0; // Clear error stateif(((finish0&0x400)==0x400)){#ifdef _SD_DEBUG_ printf("CMD%d Time out!\r\n", cmd);#endif return 0; // Timeout error}} SDICSTA=finish0;}return 1; }}//复位,使卡进入IDEL状态void CMD0(void){SDICARG = 0x0; SDICCON = (1<<8)|0x40; // No_resp, startSDI_Check_CMD_End(0, 0);SDICSTA = 0x800; // Clear cmd_end(no rsp)}//设置工作电压是根据SD的OCR寄存器来设置U8 CMD1(void){SDICARG = 0xff8000; //(SD OCR:2.7V~3.6V)SDICCON = (0x1<<9)|(0x1<<8)|0x41; //sht_resp, wait_resp, start, if(SDI_Check_CMD_End(1, 1)) //[31]:Card Power up status bit (busy){if((SDIRSP0>>16)==0x80ff){SDICSTA = 0xa00; // Clear cmd_end(with rsp)return 1; // Success}elsereturn 0;}return 0;}//请求设备在CMD上传送CIDU8 CMD2(U8 *cCID_Info) {SDICARG = 0x0;SDICCON = (0x1<<10)|(0x1<<9)|(0x1<<8)|0x42; //lng_resp, wait_resp, startif(!SDI_Check_CMD_End(2, 1)) return 0;*(cCID_Info+0) = SDIRSP0>>24;*(cCID_Info+1) = SDIRSP0>>16;*(cCID_Info+2) = SDIRSP0>>8;*(cCID_Info+3) = SDIRSP0;*(cCID_Info+4) = SDIRSP1>>24;*(cCID_Info+5) = SDIRSP1>>16;*(cCID_Info+6) = SDIRSP1>>8;*(cCID_Info+7) = SDIRSP1;*(cCID_Info+8) = SDIRSP2>>24;*(cCID_Info+9) = SDIRSP2>>16;*(cCID_Info+10) = SDIRSP2>>8;*(cCID_Info+11) = SDIRSP2;*(cCID_Info+12) = SDIRSP3>>24;*(cCID_Info+13) = SDIRSP3>>16;*(cCID_Info+14) = SDIRSP3>>8;*(cCID_Info+15) = SDIRSP3;SDICSTA = 0xa00; // Clear cmd_end(with rsp)return 1;}//给SD卡设定一个相对地址,也就是寻址的地址 = 0:SD卡,=1:MMC卡 =0 失败 =1 成功U8 CMD3(U16 iCardType,U16 *iRCA) {SDICARG = iCardType<<16; // (MMC:Set RCA, SD:Ask RCA-->SBZ)SDICCON = (0x1<<9)|(0x1<<8)|0x43; // sht_resp, wait_resp, startif(!SDI_Check_CMD_End(3, 1)) return 0;SDICSTA=0xa00; // Clear cmd_end(with rsp)if(iCardType){*iRCA = 1;}else {*iRCA =( SDIRSP0 & 0xffff0000 )>>16;}if( SDIRSP0 & 0x1e00!=0x600 ) // CURRENT_STATE checkreturn 0;elsereturn 1;}//选中卡或者解除选中 cSorD=1为选中 为0则解除选中U8 CMD7(U8 cSorD,U16 iRCA) {if(cSorD){SDICARG = iRCA<<16; // (RCA,stuff bit)SDICCON = (0x1<<9)|(0x1<<8)|0x47; // sht_resp, wait_resp, startif(!SDI_Check_CMD_End(7, 1))return 0;SDICSTA = 0xa00; // Clear cmd_end(with rsp)//--State(transfer) checkif( SDIRSP0 & 0x1e00!=0x800 )return 0;elsereturn 1;}else{SDICARG = 0<<16; //(RCA,stuff bit)SDICCON = (0x1<<8)|0x47; //no_resp, startif(!SDI_Check_CMD_End(7, 0))return 0;SDICSTA = 0x800; //Clear cmd_end(no rsp)return 1;}}//获取卡的CSD寄存器的值U8 CMD9(U16 iRCA,U32 *lCSD) {SDICARG = iRCA<<16; // (RCA,stuff bit)SDICCON = (0x1<<10)|(0x1<<9)|(0x1<<8)|0x49; // long_resp, wait_resp, startif(!SDI_Check_CMD_End(9, 1)) return 0;*(lCSD+0) = SDIRSP0;*(lCSD+1) = SDIRSP1;*(lCSD+2) = SDIRSP2;*(lCSD+3) = SDIRSP3;return 1;}//停止数据传输U8 CMD12(void) {SDICARG = 0x0; SDICCON = (0x1<<9)|(0x1<<8)|0x4c; //sht_resp, wait_resp, start,if(!SDI_Check_CMD_End(12, 1)) return 0;elseSDICSTA = 0xa00; //Clear cmd_end(with rsp)return 1;}//获取卡内状态U16 CMD13(U16 iRCA) {SDICARG = iRCA<<16; // (RCA,stuff bit)SDICCON = (0x1<<9)|(0x1<<8)|0x4d; // sht_resp, wait_resp, startif(!SDI_Check_CMD_End(13, 1)) return 0;SDICSTA=0xa00; // Clear cmd_end(with rsp)return SDIRSP0;}//读取一个数据块U8 CMD17(U32 Addr) { //STEP1:发送指令 SDICARG = Addr; //设定指令参数 SDICCON = (1<<9)|(1<<8)|0x51; //发送CMD17指令 if(SDI_Check_CMD_End(17,1)) return 1; else return 0;}//读取多个数据块U8 CMD18(U32 Addr) { //STEP1:发送指令 SDICARG = Addr; //设定指令参数 SDICCON = (1<<9)|(1<<8)|0x52; //发送CMD18指令 if(SDI_Check_CMD_End(18,1)) return 1; else return 0;}//写入一个数据块U8 CMD24(U32 Addr) { //STEP1:发送指令 SDICARG = Addr; //设定指令参数 SDICCON = (1<<9)|(1<<8)|0x58; //发送CMD24指令 if(SDI_Check_CMD_End(24,1)) return 1; else return 0;}//写入多个数据块U8 CMD25(U32 Addr) { //STEP1:发送指令 SDICARG = Addr; //设定指令参数 SDICCON = (1<<9)|(1<<8)|0x59; //发送CMD25指令 if(SDI_Check_CMD_End(25,1)) return 1; else return 0;}//检测是否有卡,执行ACMD必须先执行CMD55U8 CMD55(U16 iRCA) {SDICARG = iRCA<<16;SDICCON = (0x1<<9)|(0x1<<8)|0x77; //sht_resp, wait_resp, startif(!SDI_Check_CMD_End(55, 1)) return 0;SDICSTA = 0xa00; // Clear cmd_end(with rsp)return 1;}// ACMD6命令为设置总线带宽 [1:0] 00为1bit 10为4bitU8 ACMD6(U8 BusWidth,U16 iRCA) {if(!CMD55(iRCA))return 0;SDICARG = BusWidth<<1; //Wide 0: 1bit, 1: 4bitSDICCON = (0x1<<9)|(0x1<<8)|0x46; //sht_resp, wait_resp, startif(!SDI_Check_CMD_End(6, 1)) return 0;SDICSTA=0xa00; // Clear cmd_end(with rsp)return 1;}//检测是否为SD卡及类型 =0应答错误或者卡正忙 =1标准SD卡 =2SDHC V2.0U8 ACMD41(U16 iRCA) {U8 cReturn;if(!CMD55(iRCA)) return 0;SDICARG=0x40ff8000; //ACMD41(SD OCR:2.7V~3.6V)SDICCON=(0x1<<9)|(0x1<<8)|0x69;//sht_resp, wait_resp, start, ACMD41if(SDI_Check_CMD_End(41, 1)) {if(SDIRSP0==0xc0ff8000)cReturn = 2; //SDHCelse if(SDIRSP0==0x80ff8000)cReturn = 1; //标准SDelsecReturn = 0; //应答错误SDICSTA = 0xa00; // Clear cmd_end(with rsp)return cReturn; // Success }SDICSTA = 0xa00; // Clear cmd_end(with rsp)return 0;}//检测MMC卡U32 SDI_MMC_OCR(void){ int i; //-- Negotiate operating condition for MMC, it makes card ready state for(i=0;i<10;i++) {if(CMD1())return 1; } return 0; // Fail}//检测SD卡U32 SDI_SD_OCR(void){ int i; SDCard.sdiRCA = 0; for(i=0;i<50;i++) { if(ACMD41(SDCard.sdiRCA))return 1;Delay(1000); }return 0; //fail}//运用CMD7来选中或解除选中卡,返回1则成功,0失败U8 select_or_deselect(U8 cSelDesel,U16 iCardRCA){if(CMD7(cSelDesel,iCardRCA))return 1;elsereturn 0;}//设置总线带宽U8 Set_bus_Width(U8 cCardType,U8 cBusWidth,U16 iRCA){if(cCardType==1) //MMC,返回0不需要设置 默认为1bit总线带宽return 0;return ACMD6(cBusWidth,iRCA);}/**********************************************************************************功 能:该函数用于从SD卡中读出指定块起始地址的单个或多个数据块参 数: U32 Addr 被读块的起始地址 U32* RxBuffer 用于接收读出数据的缓冲区 U32 block_num 读的块数返回值: 0 读块操作不成功 1 读块操作成功**********************************************************************************/U8 Read_Block(U32 Addr,U32* RxBuffer,U32 block_num){U32 i=0;U32 status=0;SDIDTIMER=0x7fffff; // Set timeout countSDIBSIZE=0x200; // 512byte(128word)SDIFSTA=SDIFSTA|(1<<16); // FIFO resetSDIDCON=(block_num<<0)|(2<<12)|(1<<14)|(SDCard.sdiWide<<16)|(1<<17)|(1<<19)|(2<<22);while(CMD18(Addr)!=1)//发送读多个块指令{SDICSTA=0xF<<9;} while(i<block_num*128){ //开始接收数据到缓冲区if(SDIDSTA&0x60){ //检查是否超时和CRC校验是否出错SDIDSTA=(0x3<<0x5); //清除超时标志和CRC错误标志return 0;}status=SDIFSTA;if((status&0x1000)==0x1000){ //如果接收FIFO中有数据*RxBuffer=SDIDAT;RxBuffer++;i++;}} SDIDCON=SDIDCON&~(7<<12);SDIFSTA = SDIFSTA&0x200;//Clear Rx FIFO Last data Ready SDIDSTA = 0x10;//Clear data Tx/Rx end detect while(CMD12()!=1)//发送结束指令 {SDICSTA=0xF<<9;}return 1;}/**********************************************************************************功 能:该函数用于向SD卡的一个或多个数据块写入数据参 数: U32 Addr 被写块的起始地址 U32* TxBuffer 用于发送数据的缓冲区 U32 block_num 块数返回值: 0 数据写入操作失败 1 数据写入操作成功**********************************************************************************/U8 Write_Block(U32 Addr,U32* TxBuffer,U32 block_num){U16 i=0;U32 status = 0;SDIDTIMER=0x7fffff; // Set timeout countSDIBSIZE=0x200; // 512byte(128word)SDIFSTA = SDIFSTA|(1<<16); // FIFO resetSDIDCON = (block_num<<0)|(3<<12)|(1<<14)|(1<<16)|(1<<17)|(1<<20)|(2<<22);while(CMD25(Addr)!=1)//发送写多个块指令{SDICSTA=0xF<<9;}while(i<block_num*128){ //开始传递数据到缓冲区status=SDIFSTA;if((status&0x2000)==0x2000){ //如果发送FIFO可用,即FIFO未满SDIDAT=*TxBuffer;TxBuffer++;i++;}}SDIDCON = SDIDCON&~(7<<12); while(CMD12()!=1)//发送结束指令 {SDICSTA=0xF<<9;}do{ //等待数据发送结束status=SDIDSTA;}while((status&0x2)==0x2);SDIDSTA = status; SDIDSTA=0xf4; return 1;}void Delay(U32 i){while(i--);}###########################################
- 基于S3C2440——SD/MMC
- 基于S3C2440的嵌入式Linux驱动——MMC/SD子系统解读(一)
- 基于S3C2440的嵌入式Linux驱动——MMC/SD子系统解读(二)
- 基于S3C2440的嵌入式Linux驱动——MMC/SD子系统解读(一)
- 基于S3C2440的嵌入式Linux驱动——MMC/SD子系统解读(二)
- 基于S3C2440的嵌入式Linux驱动——MMC/SD子系统解读(一)
- 基于S3C2440的嵌入式Linux驱动——MMC/SD子系统解读(二)
- linux2.6.20 sd/mmc卡驱动学习日记4(基于s3c2440)
- linux2.6.20 sd/mmc卡驱动学习日记1(基于s3c2440)
- linux2.6.20 sd/mmc卡驱动学习日记2(基于s3c2440)
- linux2.6.20 sd/mmc卡驱动学习日记3(基于s3c2440)
- linux2.6.20 sd/mmc卡驱动学习日记4(基于s3c2440)
- linux2.6.20 sd/mmc卡驱动学习日记4(基于s3c2440)
- linux2.6.20 sd/mmc卡驱动学习日记4(基于s3c2440)
- linux2.6.20 sd/mmc卡驱动学习日记1(基于s3c2440)
- linux2.6.20 sd/mmc卡驱动学习日记2(基于s3c2440)
- linux2.6.20 sd/mmc卡驱动学习日记3(基于s3c2440)
- linux2.6.20 sd/mmc卡驱动学习日记4(基于s3c2440)
- android解析XML文件
- 【线段树+离散化+离线方法】杭电 hdu 3333 Turing Tree
- ZOJ Problem Set - 3607 Lazier Salesgirl
- 单链表实现冒泡排序
- 建立一个带头结点的的单向链表并输出到out53.dat和屏幕上。各节点的值为对应的下表。链表的节点数及输出地文件名作为参数传入
- 基于S3C2440——SD/MMC
- 黑马程序——String类(黑马我来了之九)
- 在ContextMenu中添加/删除ListView的Item
- Android: AlertDialog
- java例程练习(一个BirthDate类 以及简单的测试程序)
- shared pool 深度解析1+
- UIAlertView文字的最简单左对齐方法
- 字符组
- 编程开发必须用到的工具书MSDN中文版