基于S3C2440——SD/MMC
2012-04-20 20:40
357 查看
下面内容是转载一为网友的内容: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卡。
废话不多说,直接看源码。
之前在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 50000000 typedef struct SD_STRUCT { U8 sdiWide; // 0:1bit, 1:4bit U8 sdiType; // 0:SD , 1:MMC U16 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 512 U8 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"); #endif return; } 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地址数据出错,终止!"); #endif return; } if(Read_Block(0,(U32 *)cRxBuffer,1)) { #ifdef _SD_DEBUG_ printf("\r\n读出SD卡0地址数据成功!"); #endif printf("\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地址数据出错,终止!"); #endif return; } select_or_deselect(0,SDCard.sdiRCA); } U8 sdi_init() { int i; #ifdef _SD_DEBUG_ printf("\r\nSDI初始化开始!"); #endif GPEUP = 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 reset SDIBSIZE=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; } //读CID if(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"); #endif return 0; } //设置RCA MMC的RCA=1 SD的RCA=0 if(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"); #endif return 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"); #endif return 0; } } //读CSD if(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( 4000 "卡容量为:%dKB,%dMB\r\n",SDCard.lCardSize,SDCard.lCardSize/1024); #endif } else { #ifdef _SD_DEBUG_ printf("Read Card CSD Fail!\r\n"); #endif return 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"); #endif return 0; } //CMD13 查询是否为传输状态 while((CMD13(SDCard.sdiRCA)&0x1e00) != 0x800); //设置总线带宽 ACMD6 if(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); #endif return 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 state if(((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, start SDI_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 } else return 0; } return 0; } //请求设备在CMD上传送CID U8 CMD2(U8 *cCID_Info) { SDICARG = 0x0; SDICCON = (0x1<<10)|(0x1<<9)|(0x1<<8)|0x42; //lng_resp, wait_resp, start if(!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, start if(!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 check return 0; else return 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, start if(!SDI_Check_CMD_End(7, 1)) return 0; SDICSTA = 0xa00; // Clear cmd_end(with rsp) //--State(transfer) check if( SDIRSP0 & 0x1e00!=0x800 ) return 0; else return 1; } else { SDICARG = 0<<16; //(RCA,stuff bit) SDICCON = (0x1<<8)|0x47; //no_resp, start if(!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, start if(!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; else SDICSTA = 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, start if(!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必须先执行CMD55 U8 CMD55(U16 iRCA) { SDICARG = iRCA<<16; SDICCON = (0x1<<9)|(0x1<<8)|0x77; //sht_resp, wait_resp, start if(!SDI_Check_CMD_End(55, 1)) return 0; SDICSTA = 0xa00; // Clear cmd_end(with rsp) return 1; } // ACMD6命令为设置总线带宽 [1:0] 00为1bit 10为4bit U8 ACMD6(U8 BusWidth,U16 iRCA) { if(!CMD55(iRCA)) return 0; SDICARG = BusWidth<<1; //Wide 0: 1bit, 1: 4bit SDICCON = (0x1<<9)|(0x1<<8)|0x46; //sht_resp, wait_resp, start if(!SDI_Check_CMD_End(6, 1)) return 0; SDICSTA=0xa00; // Clear cmd_end(with rsp) return 1; } //检测是否为SD卡及类型 =0应答错误或者卡正忙 =1标准SD卡 =2SDHC V2.0 U8 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, ACMD41 if(SDI_Check_CMD_End(41, 1)) { if(SDIRSP0==0xc0ff8000) cReturn = 2; //SDHC else if(SDIRSP0==0x80ff8000) cReturn = 1; //标准SD else cReturn = 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; else return 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 count SDIBSIZE=0x200; // 512byte(128word) SDIFSTA=SDIFSTA|(1<<16); // FIFO reset SDIDCON=(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 count SDIBSIZE=0x200; // 512byte(128word) SDIFSTA = SDIFSTA|(1<<16); // FIFO reset SDIDCON = (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--); } ###########################################
相关文章推荐
- u-boot移植总结(三)(转)S3C2440对Nand Flash操作和电路原理(基于K9F2G08U0A)
- 基于 mini2440 电阻式触摸屏(二):S3C2440 电阻式触摸屏接口、内部ADC结构
- 菜鸟要飞向ARM城堡——MDK中对基于S3C2440工程的配置
- mmc/sd 在 2.6.27 2440
- [Funkunux] 自己写MMU实验 基于S3C2440
- 基于 mini2440 电阻式触摸屏(二):S3C2440 电阻式触摸屏接口、内部ADC结构
- 基于 mini2440 电阻式触摸屏(二):S3C2440 电阻式触摸屏接口、内部ADC结构
- S3C2440+Linux2.6mmc/sd驱动程序
- 自己写bootloader1 - start.S,基于s3c2440
- 基于 mini2440 电阻式触摸屏(二):S3C2440 电阻式触摸屏接口、内部ADC结构
- 自己写bootloader1 - start.S,基于s3c2440
- 基于 mini2440 电阻式触摸屏(二):S3C2440 电阻式触摸屏接口、内部ADC结构
- 自己写bootloader2 -跳转执行,基于s3c2440
- u-boot移植总结(三)(转)S3C2440对Nand Flash操作和电路原理(基于K9F2G08U0A)
- SD/MMC/SDIO 概念区分概要
- linux2.6.20 sd/mmc卡驱动学习日记4(基于s3c2440)
- S3C2440 Linux驱动移植——LCD
- SD与MMC的区别
- 基于2410/2440让程序在Windows CE系统启动时自动运行(含platform.reg .dat .bib介绍)
- linux驱动之S3C2440 Uart