• 卡函数or1200基于simplespi的SD卡驱动


    每日一贴,天今的内容关键字为卡函数

            这篇blog来说说基于simple-spi这个ipcore编写spi模式的SD Card裸机的驱动程序,植移依附分不清什么SD卡啊,micro SD啊,miniSD,MMC,SDIO啊,SDHC啊等等一大堆的观点,天今抽了点时光百科和wiki扫盲去了,顺便把结总的贴出来,留自己后以回想~

            MMC:Multimedia Card(多媒体记忆卡),基于NAND-Flash技巧,衍生版有出RS-MMC(小尺寸的多媒体卡)、双电压小尺寸多媒体卡(DV-RS-MMC)。4.x准标引入升级版MMC plus,MMC4卡和RS-MMC4(移动式MMC,老式RS-MMC的盗窟),并且引入secureMMC准标

            SD:Secure Digital Memory Card,基于MMC卡格式展发而来,同样是NAND-Flash技巧,SD设备兼容MMC卡存取,而MMC设备不能存取SD卡(因为卡槽不一致,不能互插)。

            miniSD:个人解理是小型的SD卡,兼容SD卡,电器性属略微有点区分。

            microSD:原名Trans-flash Card(TF卡),04年名更microSD Card,比miniSD尺寸更小的SD卡。

            SDHC:Secure Digital High Capacity(高容量SD卡4G~32G),判定终了(本人机手用的也只microSD咧,SDHC用在什么方面?求释解),划定用使FAT32文件系统。

            SDIO:Secure Digital Input and Output Card(安全字数输入输出卡),在SD准标上定义的一种外设接口。(接口?我能不能解理成似类USB一样,不过就是把外设的接口做成SDIO,那有USB了为毛还要用这个接口,求大神释解)

        

            现在SD卡差不多代取完MMC卡了,不过SD的卡槽可以卡得进MMC,所以是兼容MMC的,也因为这样MMC卡,因为用MMC卡可以不交SD议协的税版,所以还在用,至于miniSD和microSD套进去插到SD卡槽就能够用了,SDHC没见过,释解不了了,SDIO的设备也没用过,可以去宝淘YY一下,SDIO接口的wifi,蓝牙等等的,但是比起USB接口的来得贵,大神,求释解啊,应用合场是什么?

            这界世貌似多好西东我不能够解理啊~除了坑爹还是坑爹~

        

            再说说那个作操接口和时序准标的问题

            (转一下wiki面下关于各种技巧比对的图,很然了目一)

        卡和函数

        

            SD卡比起MMC多了2pins,都是用来做数据线的;

            miniSD比起SD又多了2pins,当初只做预留,没什么用途,其他和SD卡一样

            microSD比SD少了1pin?貌似比对我手上的那些原理图是少了个VSS?

            因为历史因原,SPI时序基本在这面里都市支撑,还有SD总线的1bit mode是支撑的,但是SD卡用于高速情况是一般我们择选是4bit mode,我自己是没用过SD总线的1 bit mode,平日我会择选跑高速数据择选SD总线的4 bit mode,低速跑SPI。

            有这个SD 1 bit mode为毛还要SPI mode咧?所以这个界世除了坑爹还是坑爹,但是像我们这些读过一点点书的人都道知:“存在就是理合”这个堡碉了的名言。

            1.你见过大部分SOC(特别是低端)成集SPI controller还是支撑SD Bus 和controller多

                    2.写过SD卡驱动的友朋们处置CRC校验你耗资源多不多(件硬无CRC校验)

            3.SD的CMD线与DATA线之间有可能同时生产数据,对没有SD件硬模块的机主支撑起来难度较高(引用别人blog的原话)

            挑点重点来讲讲,也因为我只道知这么多了,见笑见笑!!!

        

            面下就是讲讲SD/MMC准标SPI时序的SD卡写读进程了,至于SD 4bit mode就先不细说了,后以有时光编写程序再发个blog~

            至于官方的SD specs我没怎么看过,太长了~心真看不动~google一下有没有别人结总过的验经了,就算把官方的specs翻译成中文也懒得看了~

            SD展发了这么多头年,官方结总的资料我认为比起看官方的spces来得际实很多,至于关于SD卡的资料推荐《ALIENTEK战舰STM32开发板》面里的盘光,这里绝对我不是托,只是友朋再用这款开发板,而面里整理好关于SD卡的资料我心真认为不错。

        

            到此,根据写好的基于simple-spi这个core的SD驱动,结总最简略的流程

        

            一、Initialize:

            1.开发板上电

            2.延时>74clocks

            3.写CMD0,复位SD卡

            4.写CMD8,检查版本

            5.写CMD58,查询OCR,获得卡供电情况

            6.写CMD1,激活卡

            7.8 clocks后,止禁SD的CS

        

             二,read block:

            1.写CMD17 

            2.等到R1格式的令命应答

            3.取读始起令牌0xFE

            4.读512 bytes

            5.弃丢2 bytes的CRC校验bytes

            6.8 clocks后,止禁SD的CS

        

            三、write block:

            1.写CMD24

            2.等到R1格式的令命应答

            3.插入干若clocks

            4.写始起令牌0xFE

            5.写512 bytes

            6.写2 bytes CRC校验bytes(dummy bytes)

            7.收接响应数据0x05

            8.8 clocks后,止禁SD的CS

        

            四、write blocks:

            1.写CMD25

            2.等到R1格式的令命应答

            3.取读始起令牌0xFC

            4.写512 bytes

            5.写2 bytes CRC校验bytes(dummy bytes)

            6.收接响应数据0x05

            7.等待SD card闲暇

            8.重复第2步

            至于关于SD卡的令命集google一下一大堆,解了一下经常使用的令命就OK啦,或者参看官方的SD specs 2.x的第7 chapter关于SPI Mode的述描,面里有关于令命集合时序图。

            空话不多了,关于SD的基础知识和SPI的作操释解到这里,至于友朋们还须要更多的SD的知识可以去wiki关于SD的详解。

            http://en.wikipedia.org/wiki/Secure_Digital

            面下就根据SD卡SPI的时序图和simple-spi这个core的spces来敲SD card的driver

            还是来讲讲编写好的simple-spi的几个封装函数先了,这些函数咧,是opencore区社的牛牛写好的,我就不另外自己去编了~省点力精~

            首先在高速写读SD卡的时候咧,用轮询方法比用断中方法去写读SD卡效率要高(真的吗?我自己没究研过),但是我轻信了这句话,所以没用断中去写SD的driver,所以这里仅仅绍介用到的API啦~

            这个驱动文件在u-boot/driver/spi中可以找到oc_simple_spi.c~

            至于对应的simple-spi的代码我用的是orpsocv2面里的,orpsocv2修改了simple-spi支撑slave的择选功能,这份驱动就是按照修过改的core编写的,上代码了~

        

            只贴出来用到的函数,体具参考simple-spi的specs把寄存器置配都略微看懂。

        

    void 
    spi_core_enable(int core)
    {
      REG8((SPI_BASE_ADR[core] + SIMPLESPI_SPCR)) |= SIMPLESPI_SPCR_SPE;
    }
    
    void 
    spi_core_disable(int core)
    {
      REG8((SPI_BASE_ADR[core] + SIMPLESPI_SPCR)) &= ~SIMPLESPI_SPCR_SPE;
    }
    
    void 
    spi_core_clock_setup(int core, char polarity, char phase, char rate,
    			  char ext_rate)
    {
      char spcr = REG8((SPI_BASE_ADR[core] + SIMPLESPI_SPCR));
    
      if (polarity)
        spcr |= SIMPLESPI_SPCR_CPOL;
      else
        spcr &= ~SIMPLESPI_SPCR_CPOL;
    
      if (phase)
        spcr |= SIMPLESPI_SPCR_CPHA;
      else
        spcr &= ~SIMPLESPI_SPCR_CPHA;
    
      spcr = (spcr & ~SIMPLESPI_SPCR_SPR) | (rate & SIMPLESPI_SPCR_SPR);
    
      REG8((SPI_BASE_ADR[core] + SIMPLESPI_SPCR)) = spcr;
    
      char sper = REG8((SPI_BASE_ADR[core] + SIMPLESPI_SPER));
      
      sper = (sper & ~SIMPLESPI_SPER_ESPR) | (ext_rate & SIMPLESPI_SPER_ESPR);
    
      REG8((SPI_BASE_ADR[core] + SIMPLESPI_SPER)) = sper;
    
    }
    
    // No decode on slave select lines, so assert correct bit to select slave
    void 
    spi_core_slave_select(int core, char slave_sel_dec)
    {  
      REG8((SPI_BASE_ADR[core] + SIMPLESPI_SSPU)) = slave_sel_dec;
    }
    
    int 
    spi_core_data_avail(int core)
    {
      return !!!(REG8((SPI_BASE_ADR[core]+SIMPLESPI_SPSR))&SIMPLESPI_SPSR_RFEMPTY);
    }
    
    int 
    spi_core_write_avail(int core)
    {
      return !!!(REG8((SPI_BASE_ADR[core]+SIMPLESPI_SPSR))&SIMPLESPI_SPSR_WFFULL);
    }
    
    // Should call spi_core_write_avail() before calling this, we don't check
    void 
    spi_core_write_data(int core, char data)
    {
      REG8((SPI_BASE_ADR[core] + SIMPLESPI_SPDR)) = data;
    }
    
    char 
    spi_core_read_data(int core)
    {
      return REG8((SPI_BASE_ADR[core] + SIMPLESPI_SPDR));
    }

            然后略微解理用到的最平日的写读函数,就能够转到SD卡driver的编写了~驱动参考振南兄的znFAT里SD驱动流程编写

            好,第一个,reset进程函数SDReset(),对着时序图来谈话

        卡和函数

            按照面下述描过的初始化流程,送74clocks,cs拉低,送令命CMD0(0x4000000095),距离8*clock*n后,等待从设备的0x1应答,cs再次拉高

            必须注意的是,reset和初始化时时钟率速必须下降,至于率速降到多少貌似还没有定论,我初始化的时候时降到了300K阁下,貌似400K一下都可以作操。

    unsigned char SDReset(void){		
    	unsigned char times, temp, i;
    	unsigned char pcmd[] = {0x40,0x00,0x00,0x00,0x00,0x95};	// CMD0
    
    	// send SDCard 74 clocks
    	spi_core_slave_select(SD, SDDisable);
    	SetSDClockInitRate(SD);		// slow down sdcard clock speed
    	for(i=0; i<0x0f; i++){		
    		spi_write_ignore_read(SD, 0xff);	// 8*15 = 120 clocks
    	}
    
    	// send CMD0
    	spi_core_slave_select(SD, SDEnable);
    		
    	times = 0;
    	do{
    		temp = SDWriteCmd(pcmd);
    		times++;
    		if(times == TRY_TIME){
    			return INIT_CMD0_ERROR;
    		}
    	}while(temp!=0x01);
    
    	spi_core_slave_select(SD, SDDisable);
    	spi_write_ignore_read(SD, 0xff);
    	
    	return 0;
    }

            初始化SD卡时序:cs拉低,送令命CMD0(0x41000000ff),距离8*clock*n后,等待从设备的0x0应答,cs再次拉高

        卡和函数

        
            初始化代码:

    unsigned char SDInit(void){
    	unsigned char times, temp;
    	unsigned char pcmd[] = {0x41,0x40,0x00,0x00,0x00,0xff};
    
    	spi_core_slave_select(SD, SDEnable);
    	times = 0;
    	do{
    		temp = SDWriteCmd(pcmd);
    		times++;
    		if(times==TRY_TIME){
    			return INIT_CMD1_ERROR;
    		}
    	}while(temp!=0x00);
    
    	SetSDClockTransferRate(SD);		//set sdcrad clock as transfre clock
    	spi_core_slave_select(SD, SDDisable);
    	spi_write_ignore_read(SD, 0xff);
    	return 0;
    }

            至于CID和CSD的取读,程序里没有实现,有兴致的友朋可以根据CMD0和CMD1的时序自己敲个代码上去弥补完全

        卡和函数

        每日一道理
    人生是洁白的画纸,我们每个人就是手握各色笔的画师;人生也是一条看不到尽头的长路,我们每个人则是人生道路的远足者;人生还像是一块神奇的土地,我们每个人则是手握农具的耕耘者;但人生更像一本难懂的书,我们每个人则是孜孜不倦的读书郎。

        卡和函数

        

            在认默情况下,SD卡的写读block巨细都是512bytes,所以就按照最平日的block巨细行进写读作操。

            block读作操:cs拉低,送令命CMD17(0x51000000ff),距离8*clock*n后,等待从设备的0x0应答,再次距离距离8*clock*n,取读开始字节(0xfe),取读512bytes,弃丢2bytes的CRC,cs再次拉高

        卡和函数

        

            写读一个section的代码:

    unsigned char SDReadSector(unsigned long addr,unsigned char *buffer){
    	unsigned char temp,times;
    	unsigned char i;
    	unsigned char pcmd[]={0x51,0x00,0x00,0x00,0x00,0xff};	// CMD17
    
    	addr<<=9;
    	pcmd[1]=addr>>24;
    	pcmd[2]=addr>>16;
    	pcmd[3]=addr>>8;
    	pcmd[4]=addr;
    
    	spi_core_slave_select(SD, SDEnable);
    	times = 0;
    	do{
    		temp = SDWriteCmd(pcmd);
    		times++;
    		if(times==TRY_TIME){
    			return READ_BLOCK_ERROR;
    		}
    	}while(temp!=0x00);
    	
    	// check if datas ready ,then recevie datas and two bytes CRC(ignored)
    	do{
    		temp = spi_read_ignore_write(SD);
    	}while(temp!=0xfe);
    	
    	for(i=0; i<64; i++){
    		*(buffer++) = spi_read_ignore_write(SD);
    		*(buffer++) = spi_read_ignore_write(SD);
    		*(buffer++) = spi_read_ignore_write(SD);
    		*(buffer++) = spi_read_ignore_write(SD);
    		*(buffer++) = spi_read_ignore_write(SD);
    		*(buffer++) = spi_read_ignore_write(SD);
    		*(buffer++) = spi_read_ignore_write(SD);
    		*(buffer++) = spi_read_ignore_write(SD);
    	}
    	spi_read_ignore_write(SD);
    	spi_read_ignore_write(SD);
    
    	spi_core_slave_select(SD, SDDisable);
    	spi_write_ignore_read(SD, 0xff);
    	return 0;	
    }

        卡和函数

            block写作操:cs拉低,送令命CMD24(0x58000000ff),距离8*clock*n后,等待从设备的0x0应答,再次距离距离8*clock*n,入写开始字节(0xfe),入写512bytes,写2bytes的dummy CRC,取读应答字节0x05,等待SD忙状态束结,cs再次拉高。

        

            写作操代码:

    unsigned char SDWriteSector(unsigned long addr,unsigned char *buffer){
    	unsigned char temp,times;
    	unsigned char i;
    	unsigned char pcmd[] = {0x58,0x00,0x00,0x00,0x00,0xff};	// CMD24
    
    	addr<<=9;
    	// *((unsigned long *)(pcmd+1))=addr;
    	pcmd[1]=addr>>24;
    	pcmd[2]=addr>>16;
    	pcmd[3]=addr>>8;
    	pcmd[4]=addr;
    
    	spi_core_slave_select(SD, SDEnable);
    	times = 0;
    	do{
    		temp = SDWriteCmd(pcmd);
    		times++;
    		if(times==TRY_TIME){
    			return temp;
    		}
    	}while(temp!=0x00);
    
    	// insert some clocks
    	for(i=0; i<10; i++){
    		spi_read_ignore_write(SD);
    	}
    
    	// write 512 bytes to SD Card ,and two bytes CRC(ignored)
    	spi_write_ignore_read(SD, 0xfe);
    	for(i=0; i<64; i++){
    		spi_write_ignore_read(SD, *buffer++);
    		spi_write_ignore_read(SD, *buffer++);
    		spi_write_ignore_read(SD, *buffer++);
    		spi_write_ignore_read(SD, *buffer++);
    		spi_write_ignore_read(SD, *buffer++);
    		spi_write_ignore_read(SD, *buffer++);
    		spi_write_ignore_read(SD, *buffer++);
    		spi_write_ignore_read(SD, *buffer++);
    	}
    	spi_write_ignore_read(SD, 0xff);
    	spi_write_ignore_read(SD, 0xff);
    
    	// check if datas have been written to SD Card
    	temp = spi_read_ignore_write(SD);
    	if((temp & 0x1F)!=0x05){
    		spi_core_slave_select(SD, SDDisable);
    		return WRITE_BLOCK_ERROR;
    	}
    	
    	do{
    		temp = spi_read_ignore_write(SD);
    	}while(temp!=0xff);	
    	
    	spi_core_slave_select(SD, SDDisable);
    	spi_write_ignore_read(SD, 0xff);
    	return 0;
    }

            对于多个blocks的连续写作操,直接参考代码吧,只是用连续写作操令命CMD18,令命的数参段位入写datas时的address,好吧,有了写作操进程连续写的进程也不难,上代码咯~

            连续写n个sections代码:

    unsigned char SDWritenSector(unsigned long nsec,unsigned long addr,unsigned char *buffer){
    	unsigned char temp,times;
    	unsigned long i, j;
    	unsigned char pcmd[] = {0x59,0x00,0x00,0x00,0x00,0xff};
    
    	unsigned char *temp_buf = buffer;
    
    	if(sd_ver==0x05 || !addr_mode) addr<<=9;
    	pcmd[1]=addr>>24;
    	pcmd[2]=addr>>16;
    	pcmd[3]=addr>>8;
    	pcmd[4]=addr;
    
    	spi_core_slave_select(SD, SDEnable);
    
    	times = 0;
    	do{
    		temp = SDWriteCmd(pcmd);
    		times++;
    		if(times==TRY_TIME){
    			return temp;
    		}
    	}while(temp!=0x00);
    
    	// insert some clocks
    	for(i=0; i<10; i++){
    		spi_read_ignore_write(SD);
    	}
    
    	// write datas to sections
    	for(j=0; j<nsec; j++){	
    		// write 512 bytes to SD Card ,and two bytes CRC(ignored)
    		spi_write_ignore_read(SD, 0xfc);	
    		for(i=0; i<64; i++){
    			spi_write_ignore_read(SD, *buffer++);
    			spi_write_ignore_read(SD, *buffer++);
    			spi_write_ignore_read(SD, *buffer++);
    			spi_write_ignore_read(SD, *buffer++);
    			spi_write_ignore_read(SD, *buffer++);
    			spi_write_ignore_read(SD, *buffer++);
    			spi_write_ignore_read(SD, *buffer++);
    			spi_write_ignore_read(SD, *buffer++);
    		}
    		spi_write_ignore_read(SD, 0xff);
    		spi_write_ignore_read(SD, 0xff);
    		
    		// check if datas have been write to SD Card
    		temp = spi_read_ignore_write(SD);
    		if((temp & 0x1F)!=0x05){
    			spi_core_slave_select(SD, SDDisable);
    			return WRITE_BLOCK_ERROR;
    		}
    		while(spi_read_ignore_write(SD)!=0xff);
    		buffer=temp_buf;	
    	}
    
    	spi_write_ignore_read(SD, 0xfd);
    	while(spi_read_ignore_write(SD)!=0xff);
    	spi_core_slave_select(SD, SDDisable);
    	spi_write_ignore_read(SD, 0xff);
    	return 0;	
    }

            OK,到这里SD卡的驱动就基本可以用了,部全的码源的话可以有兴致的友朋邮件我,接下来就是还有一些用到的函数都讲讲吧。

            SD驱动的写读函数

    void spi_write_ignore_read(int core, char dat){
    	spi_core_write_data(core, dat);
      	while (!(spi_core_data_avail(core))); // Wait for the transaction (should generate a byte)
      	spi_core_read_data(core);
    }
    
    char spi_read_ignore_write(int core){
    	spi_core_write_data(core, 0xff);
    	while (!(spi_core_data_avail(core))); // Wait for the transaction (should generate a byte)
    	return spi_core_read_data(core);
    }

            Oc-simple-spi的初始化函数

    void SpiCoreInit(int core){
    	// disable spi core, and deselect sdcard
    	spi_core_enable(core);
    	spi_core_slave_select(core, SDDisable);
    
    	// clear read buffer
    	while (spi_core_data_avail(core)){
    		spi_core_read_data(core);
    	}
    
    	// setup default clock = sysclk/128
    	spi_core_clock_setup(core, 0, 0, 0x01, 0x02);
    
    	// enable spi core
    	spi_core_slave_select(core, SDEnable);
    	spi_core_enable(core);
    }

            Oc-simple-spi的时钟率速切换函数

    void SetSDClockInitRate(int core){
    	spi_core_disable(core);
    
    	// set sdcard clock = sysclk/128
    	spi_core_clock_setup(core, 0, 0, 0x01, 0x02);
    
    	spi_core_enable(core);		
    }
    
    void SetSDClockTransferRate(int core){
    	spi_core_disable(core);
    
    	// set sdcard clock = sysclk/2
    	spi_core_clock_setup(core, 0, 0, 0x00, 0x00);
    
    	spi_core_enable(core);
    }

        

            以上那些函数都和体具的oc-simple-spi core相干,可以参考这个core的specs来看

            SD驱动的初始化函数,这个函数用于下一节提到的在植移znFat时须要驳接的SD卡复位函数

    unsigned char SDReady(void){	
    	SDReset();
    	SDCheckVersion();
    	SDGetAddrMode();
    	return SDInit();
    }

            好,没了,驱动写到这里就OK了,下节根据znFat的植移教程植移这个文件系统

    文章结束给大家分享下程序员的一些笑话语录: 开发时间
      项目经理: 如果我再给你一个人,那可以什么时候可以完工?程序员: 3个月吧!项目经理: 那给两个呢?程序员: 1个月吧!
    项目经理: 那100呢?程序员: 1年吧!
    项目经理: 那10000呢?程序员: 那我将永远无法完成任务.

  • 相关阅读:
    Perfect ScrollBar插件使用方法
    分享WEBAPP利用纯HTML5实现拨打电话,打开相册,打开摄像头源码
    一周心得
    19赵亮龙07杨康
    李娜跟姜山
    一周心得
    对结对编程的个人理解
    周心得和总结
    关于IT行业抄袭和借鉴
    第三周总结
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3060072.html
Copyright © 2020-2023  润新知