• STM32+雷龙SD NAND(贴片SD卡)完成FATFS文件系统移植与测试


    一、前言

    STM32项目开发中,经常会用到存储芯片存储数据。 比如:关机时保存机器运行过程中的状态数据,上电再从存储芯片里读取数据恢复;在存储芯片里也会存放很多资源文件。比如,开机音乐,界面上的菜单图标,字库文件,方便设备开机加载。

    为了让单片机更加方便的读写这些资源文件,通常都会加文件系统,如果没有文件系统,直接读取写扇区的方式,对数据不好管理。 这篇文章就手把手教大家,在STM32上完成FATFS文件系统的移植;主控芯片采用STM32F103ZET6, 存储芯片我这里采用(雷龙) CS创世 SD NAND 。 SD NAND 简单来说就是贴片式SD卡,使用起来与普通的SD卡一样,简单的区别就是:比TF卡稳定,比eMMC便宜。 下面章节里会详细介绍下 CS创世 SD NAND。

    下面是CS创世 SD NAND 与STM32开发的板的接线实物图:

    image-20221206191450735

    image-20221206191527228

    这是读写扇区测试的结果:

    image-20221206193549509

    二、SD NAND 介绍

    我当前使用的SD NAND型号是,CSNP32GCR01-AOW,容量是4GB。

    image-20221201164524580

    下面是通过编写STM32代码读取的存储信息:

    Card Type:SDHC V2.0
    Card ManufacturerID:102
    Card RCA:5000
    Card Capacity:3696 MB
    Card BlockSize:512
    
    • 1
    • 2
    • 3
    • 4
    • 5

    芯片的详细参数如下:

    1】不用写驱动程序自带坏块管理
    【2】尺寸小巧,简单易用,兼容性强,稳定可靠,固件可定制,LGA-8封装
    【3】标准SDIO接口,兼容SPI,兼容拔插式TF卡/SD卡,可替代普通TF卡/SD卡
    【4】尺寸6.2x8mm,直接贴片,不占空间
    【5】内置平均读写算法,通过1万次随机掉电测试
    【6】耐高低温,机贴手贴都非常方便
    【7】速度级别Class10(读取速度23.5MB/S写入速度12.3MB/S)
    【8】支持标准的SD 2.0协议,用户可以直接移植标准驱动代码,省去了驱动代码编程环节。支持TF卡启动的SOC都可以用SD NAND
    【9】比TF卡稳定,比eMMC便宜
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    **下面是芯片的实物图: ** 这是官网申请的样品,焊接了转接板,可以直接插在SD卡卡槽上测试。 最终选型之后,设计PCB板时,设计接口,直接贴片上去使用,非常稳定,抖动也不会导致,外置卡TF卡这种容易松动的问题。

    这是雷龙的官网: http://www.longsto.com/product/35.html

    image-20221201170132109

    image-20221201163718601

    image-20221201163735081

    image-20221201163748859

    三、编写SD NAND驱动代码

    SD NAND 的驱动代码与正常的SD卡协议是一样的,支持标准的SD 2.0协议,下面我就直接贴出写好的驱动代码。

    包括了模拟SPI,硬件SPI,SDIO等3种方式,完成对SD NAND 的读写。我当前使用的主控板子是STM32F103ZET6,如果你使用的板子不是这一款,可能还是其他的CPU也没关系;我这里直接贴出了SPI模拟时序的驱动代码,可以直接移植到任何单片机上使用,代码拷贝过去也只需要修改GPIO口即可,非常方便。

    3.1 SPI模拟时序驱动方式

    (1)整体工程代码

    这是当前工程的截图: 代码采用寄存器风格编写,非常简洁。

    当前工程完成SD NAND卡初始化,扇区的读写,测试芯片基本的使用情况。

    image-20221201181308223

    (2) sd.c
    #include "sdcard.h"			   
    static u8  SD_Type=0;  //存放SD卡的类型
    
    /*
    函数功能:SD卡底层接口,通过SPI时序向SD卡读写一个字节
    函数参数:data是要写入的数据
    返 回 值:读到的数据
    */
    u8 SDCardReadWriteOneByte(u8 DataTx)
    {		 
        u8 i;
        u8 data=0;
        for(i=0;i<8;i++)
        {
            SDCARD_SCK=0;
            if(DataTx&0x80)SDCARD_MOSI=1;
            else SDCARD_MOSI=0;
            SDCARD_SCK=1;
            DataTx<<=1;
            
            data<<=1;
            if(SDCARD_MISO)data|=0x01;
        }
        return data;
    }
    
    
    //4种: 边沿两种、电平是两种
    /*
    函数功能:底层SD卡接口初始化
    
    本程序SPI接口如下:
    PC11  片选 SDCardCS
    PC12  时钟 SDCardSCLK
    PD2   输出 SPI_MOSI--主机输出从机输入
    PC8   输入 SPI_MISO--主机输入从机输出
    */
    void SDCardSpiInit(void)
    {
      /*1. 开启时钟*/
     	RCC->APB2ENR|=1<<5;		    //使能PORTD时钟
    	RCC->APB2ENR|=1<<4;		    //使能PORTC时钟
      
      /*2. 配置GPIO口模式*/
      GPIOC->CRH&=0xFFF00FF0;
      GPIOC->CRH|=0x00033008;
      
      GPIOD->CRL&=0xFFFFF0FF;
      GPIOD->CRL|=0x00000300;
      
      /*3. 上拉*/
      GPIOC->ODR|=1<<8;
      GPIOC->ODR|=1<<11;
      GPIOC->ODR|=1<<12;
      GPIOD->ODR|=1<<2;
    }
    
    /*
    函数功能:取消选择,释放SPI总线
    */
    void SDCardCancelCS(void)
    {
    	SDCARD_CS=1;
     	SDCardReadWriteOneByte(0xff);//提供额外的8个时钟
    }
    
    /*
    函数 功 能:选择sd卡,并且等待卡准备OK
    函数返回值:0,成功;1,失败;
    */
    void SDCardSelectCS(void)
    {
    	SDCARD_CS=0;
    	SDCardWaitBusy();//等待成功
    }
    
    
    /*
    函数 功 能:等待卡准备好
    函数返回值:0,准备好了;其他,错误代码
    */
    void SDCardWaitBusy(void)
    {
    	while(SDCardReadWriteOneByte(0XFF)!=0XFF){}
    }
    
    
    /*
    函数功能:等待SD卡回应
    函数参数:
    					Response:要得到的回应值
    返 回 值:
    					0,成功得到了该回应值
    					其他,得到回应值失败
    */
    u8 SDCardGetAck(u8 Response)
    {
    	u16 Count=0xFFFF;//等待次数	   						  
    	while((SDCardReadWriteOneByte(0XFF)!=Response)&&Count)Count--;//等待得到准确的回应  	  
    	if(Count==0)return SDCard_RESPONSE_FAILURE;//得到回应失败   
    	else return SDCard_RESPONSE_NO_ERROR;//正确回应
    }
    
    
    /*
    函数功能:从sd卡读取一个数据包的内容
    函数参数:
    				buf:数据缓存区
    				len:要读取的数据长度.
    返回值:
    			0,成功;其他,失败;	
    */
    u8 SDCardRecvData(u8*buf,u16 len)
    {			  	  
    	if(SDCardGetAck(0xFE))return 1;//等待SD卡发回数据起始令牌0xFE
        while(len--)//开始接收数据
        {
            *buf=SDCardReadWriteOneByte(0xFF);
            buf++;
        }
        //下面是2个伪CRC(dummy CRC)
        SDCardReadWriteOneByte(0xFF);
        SDCardReadWriteOneByte(0xFF);									  					    
        return 0;//读取成功
    }
    
    
    /*
    函数功能:向sd卡写入一个数据包的内容 512字节
    函数参数:
    					buf 数据缓存区
    					cmd 指令
    返 回 值:0表示成功;其他值表示失败;
    */
    u8 SDCardSendData(u8*buf,u8 cmd)
    {	
    	u16 t;		  	  
    	SDCardWaitBusy();  //等待忙状态
    	SDCardReadWriteOneByte(cmd);
    	if(cmd!=0XFD)//不是结束指令
    	{
    		for(t=0;t<512;t++)SDCardReadWriteOneByte(buf[t]);//提高速度,减少函数传参时间
    	    SDCardReadWriteOneByte(0xFF); //忽略crc
    	    SDCardReadWriteOneByte(0xFF);
    		  t=SDCardReadWriteOneByte(0xFF); //接收响应
    		if((t&0x1F)!=0x05)return 2;   //响应错误									  					    
    	}						 									  					    
        return 0;//写入成功
    }
    
    
    
    /*
    函数功能:向SD卡发送一个命令
    函数参数:
    				u8 cmd   命令 
    				u32 arg  命令参数
    				u8 crc   crc校验值	
    返回值:SD卡返回的响应
    */												  
    u8 SendSDCardCmd(u8 cmd, u32 arg, u8 crc)
    {
    	u8 r1;	
    	SDCardCancelCS();               //取消上次片选
    	SDCardSelectCS(); //选中SD卡
    	//发送数据
    	SDCardReadWriteOneByte(cmd | 0x40);//分别写入命令
    	SDCardReadWriteOneByte(arg >> 24);
    	SDCardReadWriteOneByte(arg >> 16);
    	SDCardReadWriteOneByte(arg >> 8);
    	SDCardReadWriteOneByte(arg);	  
    	SDCardReadWriteOneByte(crc); 
    	
    	if(cmd==SDCard_CMD12)SDCardReadWriteOneByte(0xff);//Skip a stuff byte when stop reading
    	do
    	{
    		r1=SDCardReadWriteOneByte(0xFF);
    	}while(r1&0x80);	  //等待响应,或超时退出
       return r1;	//返回状态值
    }
    
    
    /*
    函数功能:获取SD卡的CID信息,包括制造商信息
    函数参数:u8 *cid_data(存放CID的内存,至少16Byte)	  
    返 回 值:
    					0:成功,1:错误				
    */
    u8 GetSDCardCISDCardOutnfo(u8 *cid_data)
    {
        u8 r1;	   
        //发SDCard_CMD10命令,读CID
        r1=SendSDCardCmd(SDCard_CMD10,0,0x01);
        if(r1==0x00)
    	  {
    			r1=SDCardRecvData(cid_data,16);//接收16个字节的数据	 
        }
    	SDCardCancelCS();//取消片选
    	if(r1)return 1;
    	else return 0;
    }	
    
    
    /*
    函数说明:
    					获取SD卡的CSD信息,包括容量和速度信息
    函数参数:
    					u8 *cid_data(存放CID的内存,至少16Byte)	    
    返 回 值:
    					0:成功,1:错误	
    */
    u8 GetSDCardCSSDCardOutnfo(u8 *csd_data)
    {
    	u8 r1;	 
    	r1=SendSDCardCmd(SDCard_CMD9,0,0x01);    //发SDCard_CMD9命令,读CSD
    	if(r1==0)
    	{
    		r1=SDCardRecvData(csd_data, 16);//接收16个字节的数据 
    	}
    	SDCardCancelCS();//取消片选
    	if(r1)return 1;
    	else return 0;
    }  
    
    
    /*
    函数功能:获取SD卡的总扇区数(扇区数)   
    返 回 值:
    				0表示容量检测出错,其他值表示SD卡的容量(扇区数/512字节)
    说   明:
    				每扇区的字节数必为512字节,如果不是512字节,则初始化不能通过.	
    */
    u32 GetSDCardSectorCount(void)
    {
        u8 csd[16];
        u32 Capacity;  
    	  u16 csize;  					    
        if(GetSDCardCSSDCardOutnfo(csd)!=0) return 0;	//取CSD信息,如果期间出错,返回0
        if((csd[0]&0xC0)==0x40)  //SDHC卡,按照下面方式计算
        {	
    			csize = csd[9] + ((u16)csd[8] << 8) + 1;
    			Capacity = (u32)csize << 10;//得到扇区数	 		   
        }
        return Capacity;
    }
    
    
    /*
    函数功能: 初始化SD卡
    返 回 值: 非0表示初始化失败!
    */
    u8 SDCardDeviceInit(void)
    {
      u8 r1;      // 存放SD卡的返回值
      u8 buf[4];  
    	u16 i;
    	SDCardSpiInit();//初始化底层IO口
     	for(i=0;i<10;i++)SDCardReadWriteOneByte(0xFF); //发送最少74个脉冲
    	do
    	{
    		r1=SendSDCardCmd(SDCard_CMD0,0,0x95);//进入IDLE状态 闲置
    	}while(r1!=0X01);
    	
     	SD_Type=0;   //默认无卡
    	if(r1==0X01)
    	{
    		if(SendSDCardCmd(SDCard_CMD8,0x1AA,0x87)==1)  //SD V2.0
    		{
    			for(i=0;i<4;i++)buf[i]=SDCardReadWriteOneByte(0XFF);
    			
    			if(buf[2]==0X01&&buf[3]==0XAA)    //卡是否支持2.7~3.6V
    			{
    				do
    				{
    					SendSDCardCmd(SDCard_CMD55,0,0X01);	    //发送SDCard_CMD55
    					r1=SendSDCardCmd(SDCard_CMD41,0x40000000,0X01);//发送SDCard_CMD41
    				}while(r1);
    				
    				if(SendSDCardCmd(SDCard_CMD58,0,0X01)==0)//鉴别SD2.0卡版本开始
    				{
    					for(i=0;i<4;i++)buf[i]=SDCardReadWriteOneByte(0XFF);//得到OCR值
    					if(buf[0]&0x40)SD_Type=SDCard_TYPE_V2HC;    //检查CCS
    					else SD_Type=SDCard_TYPE_V2;   
    				}
    			}
    		}
    	}
    	printf("SD_Type=0x%X\r\n",SD_Type);
    	SDCardCancelCS();       //取消片选
    	if(SD_Type)return 0;  //初始化成功返回0
    	else if(r1)return r1; //返回值错误值	   
    	return 0xaa;          //其他错误
    }
    
    
    /*
    函数功能:读SD卡
    函数参数:
    				buf:数据缓存区
    				sector:扇区
    				cnt:扇区数
    返回值:
    				0,ok;其他,失败.
    说  明:
    				SD卡一个扇区大小512字节
    */
    u8 SDCardReadData(u8*buf,u32 sector,u32 cnt)
    {
    	u8 r1;
    	if(SD_Type!=SDCard_TYPE_V2HC)sector<<=9;//转换为字节地址
    	if(cnt==1)
    	{
    		r1=SendSDCardCmd(SDCard_CMD17,sector,0X01);//读命令
    		if(r1==0)												  //指令发送成功
    		{
    			r1=SDCardRecvData(buf,512);			//接收512个字节	   
    		}
    	}else
    	{
    		r1=SendSDCardCmd(SDCard_CMD18,sector,0X01);//连续读命令
    		do
    		{
    			r1=SDCardRecvData(buf,512);//接收512个字节	 
    			buf+=512;  
    		}while(--cnt && r1==0); 	
    		SendSDCardCmd(SDCard_CMD12,0,0X01);	//发送停止命令
    	}   
    	SDCardCancelCS();//取消片选
    	return r1;//
    }
    
    /*
    函数功能:向SD卡写数据
    函数参数:
    				buf:数据缓存区
    				sector:起始扇区
    				cnt:扇区数
    返回值:
    				0,ok;其他,失败.
    说  明:
    				SD卡一个扇区大小512字节
    */
    u8 SDCardWriteData(u8*buf,u32 sector,u32 cnt)
    {
    	u8 r1;
    	if(SD_Type!=SDCard_TYPE_V2HC)sector *= 512;//转换为字节地址
    	if(cnt==1)
    	{
    		r1=SendSDCardCmd(SDCard_CMD24,sector,0X01);//读命令
    		if(r1==0)//指令发送成功
    		{
    			r1=SDCardSendData(buf,0xFE);//写512个字节	   
    		}
    	}
    	else
    	{
    		if(SD_Type!=SDCard_TYPE_MMC)
    		{
    			SendSDCardCmd(SDCard_CMD55,0,0X01);	
    			SendSDCardCmd(SDCard_CMD23,cnt,0X01);//发送指令	
    		}
     		r1=SendSDCardCmd(SDCard_CMD25,sector,0X01);//连续读命令
    		if(r1==0)
    		{
    			do
    			{
    				r1=SDCardSendData(buf,0xFC);//接收512个字节	 
    				buf+=512;  
    			}while(--cnt && r1==0);
    			r1=SDCardSendData(0,0xFD);//接收512个字节 
    		}
    	}   
    	SDCardCancelCS();//取消片选
    	return r1;//
    }	
    
    • 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
    (3) sd.h
    #ifndef SD_H
    #define SD_H_	 
    #include "stm32f10x.h"
    #include "led.h"
    #include "usart.h"
    
    /*----------------------------------------------
    本程序SPI接口如下:
    PC11  片选 SDCardCS
    PC12  时钟 SDCardSCLK
    PD2   输出 SPI_MOSI--主机输出从机输入
    PC8   输入 SPI_MISO--主机输入从机输出
    ------------------------------------------------*/
    #define SDCARD_CS PCout(11)
    #define SDCARD_SCK PCout(12)
    #define SDCARD_MOSI PDout(2)
    #define SDCARD_MISO PCin(8)
    
    
    // SD卡类型定义  
    #define SDCard_TYPE_ERR     0X00  //卡类型错误
    #define SDCard_TYPE_MMC     0X01  //MMC卡
    #define SDCard_TYPE_V1      0X02
    #define SDCard_TYPE_V2      0X04
    #define SDCard_TYPE_V2HC    0X06	   
    
    // SD卡指令表  	   
    #define SDCard_CMD0    0       //卡复位
    #define SDCard_CMD1    1
    #define SDCard_CMD8    8       //命令8 ,SEND_IF_COND
    #define SDCard_CMD9    9       //命令9 ,读CSD数据
    #define SDCard_CMD10   10      //命令10,读CID数据
    #define SDCard_CMD12   12      //命令12,停止数据传输
    #define SDCard_CMD13   16      //命令16,设置扇区大小 应返回0x00
    #define SDCard_CMD17   17      //命令17,读扇区
    #define SDCard_CMD18   18      //命令18,读Multi 扇区
    #define SDCard_CMD23   23      //命令23,设置多扇区写入前预先擦除N个block
    #define SDCard_CMD24   24      //命令24,写扇区
    #define SDCard_CMD25   25      //命令25,写多个扇区
    #define SDCard_CMD41   41      //命令41,应返回0x00
    #define SDCard_CMD55   55      //命令55,应返回0x01
    #define SDCard_CMD58   58      //命令58,读OCR信息
    #define SDCard_CMD59   59      //命令59,使能/禁止CRC,应返回0x00、
    
    /*SD卡回应标记字*/
    #define SDCard_RESPONSE_NO_ERROR      0x00   //正确回应
    #define SDCard_SD_IN_IDLE_STATE       0x01   //闲置状态
    #define SDCard_SD_ERASE_RESET         0x02   //擦除复位
    #define SDCard_RESPONSE_FAILURE       0xFF   //响应失败
      
    //函数声明              
    u8 SDCardReadWriteOneByte(u8 data);                 //底层接口,SPI读写字节函数
    void SDCardWaitBusy(void);							           		//等待SD卡准备
    u8 SDCardGetAck(u8 Response);					       				//获得应答
    u8 SDCardDeviceInit(void);							            //初始化
    u8 SDCardReadData(u8*buf,u32 sector,u32 cnt);		    //读块(扇区)
    u8 SDCardWriteData(u8*buf,u32 sector,u32 cnt);		  //写块(扇区)
    u32 GetSDCardSectorCount(void);   					        //读扇区数
    u8 GetSDCardCISDCardOutnfo(u8 *cid_data);           //读SD卡CID
    u8 GetSDCardCSSDCardOutnfo(u8 *csd_data);           //读SD卡CSD
    #endif
    
    • 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
    (4)运行效果

    image-20221201181738815

    3.2 SPI硬件时序方式

    上面的3.1小节是采用SPI模拟时序驱动SD NAND,STM32本身集成有SPI硬件模块,可以直接利用STM32硬件SPI接口读写。

    下面贴出底层的适配代码。 上面贴出的驱动代码里,已经将驱动接口部分和协议逻辑部分区分开了,替换底层的SIP读写代码非常方便。

    (1)主要替换的代码
    /*
    函数功能:SPI初始化(模拟SPI)
    硬件连接:
    MISO--->PB14
    MOSI--->PB15
    SCLK--->PB13
    */
    void SPI_Init(void)
    {
    	/*开启时钟*/
    	RCC->APB1ENR|=1<<14;   //开启SPI2时钟
    	RCC->APB2ENR|=1<<3;    //PB
    	GPIOB->CRH&=0X000FFFFF; //清除寄存器
    	GPIOB->CRH|=0XB8B00000;
    	GPIOB->ODR|=0X7<<13;    	//PB13/14/15上拉--输出高电平
    	/*SPI2基本配置*/
    	SPI2->CR1=0X0; 		//清空寄存器
    	SPI2->CR1|=0<<15; //选择“双线双向”模式
    	SPI2->CR1|=0<<11; //使用8位数据帧格式进行发送/接收;
    	SPI2->CR1|=0<<10; //全双工(发送和接收);
    	SPI2->CR1|=1<<9;  //启用软件从设备管理
    	SPI2->CR1|=1<<8;  //NSS
    	SPI2->CR1|=0<<7;  //帧格式,先发送高位
    	SPI2->CR1|=0x0<<3;//当总线频率为36MHZ时,SPI速度为18MHZ,高速。
    	SPI2->CR1|=1<<2;  //配置为主设备
    	SPI2->CR1|=1<<1;  //空闲状态时, SCK保持高电平。
    	SPI2->CR1|=1<<0;  //数据采样从第二个时钟边沿开始。
    	SPI2->CR1|=1<<6;  //开启SPI设备。
    }
    
    
    /*
    函数功能:SPI读写一个字节
    */
    u8 SPI_ReadWriteOneByte(u8 data_tx)
    {
        u16 cnt=0;				 
        while((SPI2->SR&1<<1)==0)		 //等待发送区空--等待发送缓冲为空	
        {
          cnt++;
          if(cnt>=65530)return 0; 	  //超时退出  u16=2个字节
        }	
        SPI2->DR=data_tx;	 	  		      //发送一个byte 
        cnt=0;
        while((SPI2->SR&1<<0)==0) 		//等待接收完一个byte   
        {
          cnt++;
          if(cnt>=65530)return 0;	   //超时退出
        }	  						    
        return SPI2->DR;          		//返回收到的数据	
    }
    
    函数功能:SD卡底层接口,通过SPI时序向SD卡读写一个字节
    函数参数:data是要写入的数据
    返 回 值:读到的数据
    */
    u8 SDCardReadWriteOneByte(u8 DataTx)
    {		 
        return SPI_ReadWriteOneByte(DataTx);
    }
    
    • 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
    (2)运行效果

    image-20221201182330399

    3.3 SDIO方式

    如果想提高SD NAND的读写速度,可以采用SDIO协议,STM32本身有SDIO的硬件支持,配置好SDIO的寄存器即可完成SD NAND的操作。 SDIO的数据线都比SPI多,读写速度自然没法比的。

    下面贴出STM32F103ZE上面编写的SDIO协议读写SD NAND的驱动代码。

    (1)整体工程代码

    image-20221201185519368

    (2)sdio.c
    #include "sdio_sdcard.h"
    #include "string.h"	 
    #include "sys.h"	 
    #include "usart.h"	 
    
    static u8 CardType=SDIO_STD_CAPACITY_SD_CARD_V1_1;		//SD卡类型(默认为1.x卡)
    static u32 CSD_Tab[4],CID_Tab[4],RCA=0;					      //SD卡CSD,CID以及相对地址(RCA)数据
    static u8 DeviceMode=SD_DMA_MODE;		   				        //工作模式,注意,工作模式必须通过SDIO_SdCardSetDeviceMode,后才算数.这里只是定义一个默认的模式(SD_DMA_MODE)
    static u8 StopCondition=0; 								            //是否发送停止传输标志位,DMA多块读写的时候用到  
    volatile SDIO_SD_ERROR_INFO TransferError=SD_OK;		  //数据传输错误标志,DMA读写时使用	    
    volatile u8 TransferEnd=0;								            //传输结束标志,DMA读写时使用
    SD_CardInfo SDCardInfo;									              //SD卡信息
    
    //SDIO_SdCardReadDiskSector/SDIO_SdCardWriteDiskSector函数专用buf,当这两个函数的数据缓存区地址不是4字节对齐的时候,
    //需要用到该数组,确保数据缓存区地址是4字节对齐的.
    __align(4) u8 SDIO_DATA_BUFFER[512];						  
     
     
    /*
    SD卡与开发板的SDIO方式接线关系如下:
    		DATA0---PC8
    		DATA1---PC9
    		DATA2---PC10
    		DATA3---PC11
    		CLK-----PC1
    		CMD-----PD2
    */
    
    /*
    函数功能:SDIO方式初始化SD卡 
    返回值  :错误代码;(0,无错误)
    */
    SDIO_SD_ERROR_INFO SDIO_SdCardInit(void)
    {
    	u8 clkdiv=0;
    	SDIO_SD_ERROR_INFO errorstatus=SD_OK;	   
    	//SDIO IO口初始化
    	RCC->APB2ENR|=1<<4;    	//使能PORTC时钟	   	 
    	RCC->APB2ENR|=1<<5;    	//使能PORTD时钟
      RCC->AHBENR|=1<<10;    	//使能SDIO时钟	   	 
     	RCC->AHBENR|=1<<1;    	//使能DMA2时钟
    
    	GPIOC->CRH&=0XFFF00000; 
    	GPIOC->CRH|=0X000BBBBB;	//PC.8~12 复用输出
    
    	GPIOD->CRL&=0XFFFFF0FF; 
    	GPIOD->CRL|=0X00000B00;	//PD2复用输出,PD7 上拉输入
     	
      //SDIO外设寄存器设置为默认值 			   
    	SDIO->POWER=0x00000000;
    	SDIO->CLKCR=0x00000000;
    	SDIO->ARG=0x00000000;
    	SDIO->CMD=0x00000000;
    	SDIO->DTIMER=0x00000000;
    	SDIO->DLEN=0x00000000;
    	SDIO->DCTRL=0x00000000;
    	SDIO->ICR=0x00C007FF;
    	SDIO->MASK=0x00000000;	  
     	STM32_NVIC_SetPriority(SDIO_IRQn,0,0);		//SDIO中断配置
      errorstatus=SDIO_SdPowerON();			            //SD卡上电
     	SDIO_SdCardInitializeCards();			        //初始化SD卡														  
      SDIO_SdCardGetInfo(&SDCardInfo);	            //获取卡信息
     	SDIO_SdCardSelectAddr((u32)(SDCardInfo.RCA<<16));//选中SD卡   
      SDIO_SdCardEnableWideBusOperation(1);	            //4位宽度,如果是MMC卡,则不能用4位模式 
      if((errorstatus==SD_OK)||(SDIO_MULTIMEDIA_CARD==CardType))
      {  		    
        if(SDCardInfo.CardType==SDIO_STD_CAPACITY_SD_CARD_V1_1||SDCardInfo.CardType==SDIO_STD_CAPACITY_SD_CARD_V2_0)
        {
          clkdiv=SDIO_TRANSFER_CLK_DIV+6;	  //V1.1/V2.0卡,设置最高72/12=6Mhz
        }else clkdiv=SDIO_TRANSFER_CLK_DIV;	//SDHC等其他卡,设置最高72/6=12Mhz
        SDIO_ClockSet(clkdiv);				      //设置时钟频率,SDIO时钟计算公式:SDIO_CK时钟=SDIOCLK/[clkdiv+2];其中,SDIOCLK固定为48Mhz 
        errorstatus=SDIO_SdCardSetDeviceMode(SD_POLLING_MODE);	//设置为查询模式
      }
    	return errorstatus;		 
    }
    
    /*
    函数功能: SDIO时钟初始化设置
    函数参数:
            clkdiv:时钟分频系数
            CK时钟=SDIOCLK/[clkdiv+2];(SDIOCLK时钟固定为48Mhz)
    */
    void SDIO_ClockSet(u8 clkdiv)
    {
    	u32 tmpreg=SDIO->CLKCR; 
      tmpreg&=0XFFFFFF00; 
     	tmpreg|=clkdiv;   
    	SDIO->CLKCR=tmpreg;
    } 
    
    
    /*
    函数功能: SDIO发送命令函数
    函数参数:
             cmdindex:命令索引,低六位有效
             waitrsp:期待的相应.00/10,无响应;01,短响应;11,长响应
             arg:参数
    */
    void SDIO_SendCmd(u8 cmdindex,u8 waitrsp,u32 arg)
    {			
    	u32 tmpreg;
    	SDIO->ARG=arg;
    	tmpreg=SDIO->CMD; 
    	tmpreg&=0XFFFFF800;		//清除index和waitrsp
    	tmpreg|=cmdindex&0X3F;	//设置新的index			 
    	tmpreg|=waitrsp<<6;		//设置新的wait rsp 
    	tmpreg|=0<<8;			//无等待
      tmpreg|=1<<10;			//命令通道状态机使能
    	SDIO->CMD=tmpreg;
    }
    
    /*
    函数功能: SDIO发送数据配置函数
    函数参数:
            datatimeout:超时时间设置
            datalen:传输数据长度,低25位有效,必须为块大小的整数倍
            blksize:块大小.实际大小为:2^blksize字节
            dir:数据传输方向:0,控制器到卡;1,卡到控制器;      
    */
    void SDIO_SendDataConfig(u32 datatimeout,u32 datalen,u8 blksize,u8 dir)
    {
    	u32 tmpreg;
    	SDIO->DTIMER=datatimeout;
      	SDIO->DLEN=datalen&0X1FFFFFF;	//低25位有效
    	tmpreg=SDIO->DCTRL; 
    	tmpreg&=0xFFFFFF08;		//清除之前的设置.
    	tmpreg|=blksize<<4;		//设置块大小
    	tmpreg|=0<<2;			//块数据传输
    	tmpreg|=(dir&0X01)<<1;	//方向控制
    	tmpreg|=1<<0;			//数据传输使能,DPSM状态机
    	SDIO->DCTRL=tmpreg;		
    }  
    
    
    /*
    函数功能:卡上电
            查询所有SDIO接口上的卡设备,并查询其电压和配置时钟
    返回值:错误代码;(0,无错误)
    */
    SDIO_SD_ERROR_INFO SDIO_SdPowerON(void)
    {
     	u8 i=0;
    	SDIO_SD_ERROR_INFO errorstatus=SD_OK;
    	u32 response=0,count=0,validvoltage=0;
    	u32 SDType=SD_STD_CAPACITY;
    	//配置CLKCR寄存器 
    	SDIO->CLKCR=0;				//清空CLKCR之前的设置
    	SDIO->CLKCR|=0<<9;			//非省电模式
    	SDIO->CLKCR|=0<<10;			//关闭旁路,CK根据分频设置输出
    	SDIO->CLKCR|=0<<11;			//1位数据宽度
    	SDIO->CLKCR|=0<<13;			//SDIOCLK上升沿产生SDIOCK
    	SDIO->CLKCR|=0<<14;			//关闭硬件流控制    
    	SDIO_ClockSet(SDIO_INIT_CLK_DIV);//设置时钟频率(初始化的时候,不能超过400Khz)			 
     	SDIO->POWER=0X03;			//上电状态,开启卡时钟    
      SDIO->CLKCR|=1<<8;			//SDIOCK使能   
      for(i=0;i<74;i++)
    	{
    		SDIO_SendCmd(SD_CMD_GO_IDLE_STATE,0,0);//发送CMD0进入IDLE STAGE模式命令.												  
    		errorstatus=SDIO_CmdErrorCheck();
    		if(errorstatus==SD_OK)break;
     	}
     	if(errorstatus)return errorstatus;//返回错误状态
    	SDIO_SendCmd(SDIO_SEND_IF_COND,1,SD_CHECK_PATTERN);//发送CMD8,短响应,检查SD卡接口特性.
     														//arg[11:8]:01,支持电压范围,2.7~3.6V
    														//arg[7:0]:默认0XAA
    														//返回响应7
      errorstatus=SDIO_CmdResp7Error();					//等待R7响应
     	if(errorstatus==SD_OK) 								//R7响应正常
    	{
    		CardType=SDIO_STD_CAPACITY_SD_CARD_V2_0;		//SD 2.0卡
    		SDType=SD_HIGH_CAPACITY;			   			      //高容量卡
    	}
    	SDIO_SendCmd(SD_CMD_APP_CMD,1,0);					    //发送CMD55,短响应	 
    	errorstatus=SDIO_CmdResp1Error(SD_CMD_APP_CMD); 		//等待R1响应   
    	if(errorstatus==SD_OK)//SD2.0/SD 1.1
    	{																  
    		//SD卡,发送ACMD41 SD_APP_OP_COND,参数为:0x80100000 
    		while((!validvoltage)&&(count<SD_MAX_VOLT_TRIAL))
    		{	   										   
    			SDIO_SendCmd(SD_CMD_APP_CMD,1,0);				      //发送CMD55,短响应	 
    			errorstatus=SDIO_CmdResp1Error(SD_CMD_APP_CMD); 	 	//等待R1响应   
     			if(errorstatus!=SD_OK)return errorstatus;   	//响应错误
    			SDIO_SendCmd(SD_CMD_SD_APP_OP_COND,1,SD_VOLTAGE_WINDOW_SD|SDType);//发送ACMD41,短响应	 
    			errorstatus=SDIO_CmdResp3Error(); 					        //等待R3响应   
     			if(errorstatus!=SD_OK)return errorstatus;   	//响应错误  
    			response=SDIO->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=SDIO_HIGH_CAPACITY_SD_CARD;
    		}
     	}
      return(errorstatus);		
    }
    
    /*
    函数功能: SD卡断电
    返回值:错误代码;(0,无错误)
    */
    SDIO_SD_ERROR_INFO SD_PowerOFF(void)
    {
      SDIO->POWER&=~(3<<0);//SDIO电源关闭,时钟停止	
    	return SD_OK;		  
    } 
    
    
    /*
    函数功能:初始化所有的卡,并让卡进入就绪状态
    返回值:错误代码
    */
    SDIO_SD_ERROR_INFO SDIO_SdCardInitializeCards(void)
    {
     	SDIO_SD_ERROR_INFO errorstatus=SD_OK;
    	u16 rca = 0x01;
     	if((SDIO->POWER&0X03)==0)return SD_REQUEST_NOT_APPLICABLE;//检查电源状态,确保为上电状态
     	if(SDIO_SECURE_DIGITAL_IO_CARD!=CardType)			  //非SECURE_DIGITAL_IO_CARD
    	{
    		SDIO_SendCmd(SD_CMD_ALL_SEND_CID,3,0);			  //发送CMD2,取得CID,长响应	 
    		errorstatus=SDIO_CmdResp2Error(); 					        //等待R2响应   
    		if(errorstatus!=SD_OK)return errorstatus;   	//响应错误		    
     		CID_Tab[0]=SDIO->RESP1;
    		CID_Tab[1]=SDIO->RESP2;
    		CID_Tab[2]=SDIO->RESP3;
    		CID_Tab[3]=SDIO->RESP4;
    	}
    	if((SDIO_STD_CAPACITY_SD_CARD_V1_1==CardType)||(SDIO_STD_CAPACITY_SD_CARD_V2_0==CardType)||(SDIO_SECURE_DIGITAL_IO_COMBO_CARD==CardType)||(SDIO_HIGH_CAPACITY_SD_CARD==CardType))//判断卡类型
    	{
    		SDIO_SendCmd(SD_CMD_SET_REL_ADDR,1,0);			//发送CMD3,短响应 
    		errorstatus=SDIO_CmdResp6Error(SD_CMD_SET_REL_ADDR,&rca);//等待R6响应 
    		if(errorstatus!=SD_OK)return errorstatus;   	//响应错误		    
    	}   
      if(SDIO_MULTIMEDIA_CARD==CardType)
      {
        SDIO_SendCmd(SD_CMD_SET_REL_ADDR,1,(u32)(rca<<16));//发送CMD3,短响应 	   
        errorstatus=SDIO_CmdResp2Error(); 					//等待R2响应   
        if(errorstatus!=SD_OK)return errorstatus;   	//响应错误	 
      }
    	if(SDIO_SECURE_DIGITAL_IO_CARD!=CardType)			//非SECURE_DIGITAL_IO_CARD
    	{
    		RCA = rca;
    		SDIO_SendCmd(SD_CMD_SEND_CSD,3,(u32)(rca<<16));//发送CMD9+卡RCA,取得CSD,长响应 	   
    		errorstatus=SDIO_CmdResp2Error(); 					//等待R2响应   
    		if(errorstatus!=SD_OK)return errorstatus;   	//响应错误		    
      	CSD_Tab[0]=SDIO->RESP1;
    		CSD_Tab[1]=SDIO->RESP2;
    		CSD_Tab[2]=SDIO->RESP3;						
    		CSD_Tab[3]=SDIO->RESP4;					    
    	}
    	return SD_OK;//卡初始化成功
    }
    
    
    /*
    函数功能:得到卡信息
    函数参数:
            cardinfo:卡信息存储区
    返回值:错误状态
    */
    SDIO_SD_ERROR_INFO SDIO_SdCardGetInfo(SD_CardInfo *cardinfo)
    {
     	SDIO_SD_ERROR_INFO 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==SDIO_STD_CAPACITY_SD_CARD_V1_1)||(CardType==SDIO_STD_CAPACITY_SD_CARD_V2_0)||(SDIO_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==SDIO_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;
    }
    
    
    /*
    函数功能: 设置SDIO总线宽度
    函数参数:
            wmode:位宽模式.0,1位数据宽度;1,4位数据宽度;2,8位数据宽度
    返回值:SD卡错误状态
    */
    SDIO_SD_ERROR_INFO SDIO_SdCardEnableWideBusOperation(u32 wmode)
    {
        SDIO_SD_ERROR_INFO errorstatus=SD_OK;
        if((SDIO_STD_CAPACITY_SD_CARD_V1_1==CardType)||(SDIO_STD_CAPACITY_SD_CARD_V2_0==CardType)||(SDIO_HIGH_CAPACITY_SD_CARD==CardType))
        {
          if(wmode>=2)return SD_UNSUPPORTED_FEATURE;//不支持8位模式
          else   
          {
            errorstatus=SDIO_SdCardEnWideBus(wmode);
            if(SD_OK==errorstatus)
            {
              SDIO->CLKCR&=~(3<<11);		//清除之前的位宽设置    
              SDIO->CLKCR|=(u16)wmode<<11;//1位/4位总线宽度 
              SDIO->CLKCR|=0<<14;			//不开启硬件流控制
            }
          }  
        }
        return errorstatus; 
    }
    
    
    /*
    函数功能:设置SD卡工作模式
    返回值:错误状态
    */
    SDIO_SD_ERROR_INFO SDIO_SdCardSetDeviceMode(u32 Mode)
    {
    	SDIO_SD_ERROR_INFO errorstatus = SD_OK;
     	if((Mode==SD_DMA_MODE)||(Mode==SD_POLLING_MODE))DeviceMode=Mode;
    	else errorstatus=SD_INVALID_PARAMETER;
    	return errorstatus;	    
    }
    
    
    /*
    函数功能:选卡,发送CMD7,选择相对地址(rca)为addr的卡,取消其他卡.如果为0,则都不选择.
    函数参数:
            addr:卡的RCA地址
    */
    SDIO_SD_ERROR_INFO SDIO_SdCardSelectAddr(u32 addr)
    {
     	SDIO_SendCmd(SD_CMD_SEL_DESEL_CARD,1,addr);	//发送CMD7,选择卡,短响应	 	   
      return SDIO_CmdResp1Error(SD_CMD_SEL_DESEL_CARD);	  
    }
    
    
    /*
    函数功能: SD卡读取一个块
    函数参数: 
            buf:读数据缓存区(必须4字节对齐!!)
            addr:读取地址
            blksize:块大小
    */
    SDIO_SD_ERROR_INFO SDIO_SdCardReadBlock(u8 *buf,long long addr,u16 blksize)
    {	  
        SDIO_SD_ERROR_INFO errorstatus=SD_OK;
        u8 power;
        u32 count=0,*tempbuff=(u32*)buf;//转换为u32指针 
        u32 timeout=SDIO_DATATIMEOUT;   
        if(NULL==buf)return SD_INVALID_PARAMETER; 
        SDIO->DCTRL=0x0;	//数据控制寄存器清零(关DMA)   
        if(CardType==SDIO_HIGH_CAPACITY_SD_CARD)//大容量卡
        {
          blksize=512;
          addr>>=9;
        }   
        SDIO_SendDataConfig(SD_DATATIMEOUT,0,0,0);	//清除DPSM状态机配置
        if(SDIO->RESP1&SD_CARD_LOCKED)return SD_LOCK_UNLOCK_FAILED;//卡锁了
        if((blksize>0)&&(blksize<=2048)&&((blksize&(blksize-1))==0))
        {
          power=convert_from_bytes_to_power_of_two(blksize);	    	   
          SDIO_SendCmd(SD_CMD_SET_BLOCKLEN,1,blksize);	//发送CMD16+设置数据长度为blksize,短响应 	   
          errorstatus=SDIO_CmdResp1Error(SD_CMD_SET_BLOCKLEN);	//等待R1响应   
          if(errorstatus!=SD_OK)return errorstatus;   	//响应错误	 
        }else return SD_INVALID_PARAMETER;	  	  									    
          SDIO_SendDataConfig(SD_DATATIMEOUT,blksize,power,1);	//blksize,卡到控制器	  
          SDIO_SendCmd(SD_CMD_READ_SINGLE_BLOCK,1,addr);		//发送CMD17+从addr地址出读取数据,短响应 	   
        errorstatus=SDIO_CmdResp1Error(SD_CMD_READ_SINGLE_BLOCK);//等待R1响应   
        if(errorstatus!=SD_OK)return errorstatus;   		//响应错误	 
        if(DeviceMode==SD_POLLING_MODE)						//查询模式,轮询数据	 
        {
           //	INTX_DISABLE();//关闭总中断(POLLING模式,严禁中断打断SDIO读写操作!!!)
          while(!(SDIO->STA&((1<<5)|(1<<1)|(1<<3)|(1<<10)|(1<<9))))//无上溢/CRC/超时/完成(标志)/起始位错误
          {
            if(SDIO->STA&(1<<15))						//接收区半满,表示至少存了8个字
            {
              for(count=0;count<8;count++)			//循环读取数据
              {
                *(tempbuff+count)=SDIO->FIFO;
              }
              tempbuff+=8;	 
              timeout=0X7FFFFF; 	//读数据溢出时间
            }else 	//处理超时
            {
              if(timeout==0)return SD_DATA_TIMEOUT;
              timeout--;
            }
          } 
          if(SDIO->STA&(1<<3))		//数据超时错误
          {										   
            SDIO->ICR|=1<<3; 		//清错误标志
            return SD_DATA_TIMEOUT;
          }else if(SDIO->STA&(1<<1))	//数据块CRC错误
          {
            SDIO->ICR|=1<<1; 		//清错误标志
            return SD_DATA_CRC_FAIL;		   
          }else if(SDIO->STA&(1<<5)) 	//接收fifo上溢错误
          {
            SDIO->ICR|=1<<5; 		//清错误标志
            return SD_RX_OVERRUN;		 
          }else if(SDIO->STA&(1<<9)) 	//接收起始位错误
          {
            SDIO->ICR|=1<<9; 		//清错误标志
            return SD_START_BIT_ERR;		 
          }   
          while(SDIO->STA&(1<<21))	//FIFO里面,还存在可用数据
          {
            *tempbuff=SDIO->FIFO;	//循环读取数据
            tempbuff++;
          }
        //	INTX_ENABLE();//开启总中断
          SDIO->ICR=0X5FF;	 		//清除所有标记
        }else if(DeviceMode==SD_DMA_MODE)
        {
          SDIO_SdCard_DMAConfig((u32*)buf,blksize,0); 
          TransferError=SD_OK;
          StopCondition=0;			//单块读,不需要发送停止传输指令
          TransferEnd=0;				//传输结束标置位,在中断服务置1
          SDIO->MASK|=(1<<1)|(1<<3)|(1<<8)|(1<<5)|(1<<9);	//配置需要的中断 
          SDIO->DCTRL|=1<<3;		 	//SDIO DMA使能 
          while(((DMA2->ISR&0X2000)==RESET)&&(TransferEnd==0)&&(TransferError==SD_OK)&&timeout)timeout--;//等待传输完成 
          if(timeout==0)return SD_DATA_TIMEOUT;//超时
          if(TransferError!=SD_OK)errorstatus=TransferError;  
        }   
        return errorstatus; 
    }
    
    
    /*
    函数功能: SD卡读取多个块
    函数参数: 
            buf:读数据缓存区
            addr:读取地址
            blksize:块大小
            nblks:要读取的块数
    返回值:错误状态
    */
    __align(4) u32 *tempbuff;
    SDIO_SD_ERROR_INFO SDIO_SdCardReadMultiBlocks(u8 *buf,long long addr,u16 blksize,u32 nblks)
    {
      SDIO_SD_ERROR_INFO errorstatus=SD_OK;
    	u8 power;
      u32 count=0;
    	u32 timeout=SDIO_DATATIMEOUT;  
    	tempbuff=(u32*)buf; //转换为u32指针
      SDIO->DCTRL=0x0;		//数据控制寄存器清零(关DMA)   
    	if(CardType==SDIO_HIGH_CAPACITY_SD_CARD)//大容量卡
    	{
    		blksize=512;
    		addr>>=9;
    	}  
      SDIO_SendDataConfig(SD_DATATIMEOUT,0,0,0);	//清除DPSM状态机配置
    	if(SDIO->RESP1&SD_CARD_LOCKED)return SD_LOCK_UNLOCK_FAILED;//卡锁了
    	if((blksize>0)&&(blksize<=2048)&&((blksize&(blksize-1))==0))
    	{
    		power=convert_from_bytes_to_power_of_two(blksize);	    
    		SDIO_SendCmd(SD_CMD_SET_BLOCKLEN,1,blksize);	//发送CMD16+设置数据长度为blksize,短响应 	   
    		errorstatus=SDIO_CmdResp1Error(SD_CMD_SET_BLOCKLEN);	//等待R1响应   
    		if(errorstatus!=SD_OK)return errorstatus;   	//响应错误	 
    	}else return SD_INVALID_PARAMETER;	  
    	if(nblks>1)											//多块读  
    	{									    
     	  if(nblks*blksize>SD_MAX_DATA_LENGTH)return SD_INVALID_PARAMETER;//判断是否超过最大接收长度
    		SDIO_SendDataConfig(SD_DATATIMEOUT,nblks*blksize,power,1);//nblks*blksize,512块大小,卡到控制器	  
    	  SDIO_SendCmd(SD_CMD_READ_MULT_BLOCK,1,addr);	//发送CMD18+从addr地址出读取数据,短响应 	   
    		errorstatus=SDIO_CmdResp1Error(SD_CMD_READ_MULT_BLOCK);//等待R1响应   
    		if(errorstatus!=SD_OK)return errorstatus;   	//响应错误	  
     		if(DeviceMode==SD_POLLING_MODE)
    		{
    //			INTX_DISABLE();//关闭总中断(POLLING模式,严禁中断打断SDIO读写操作!!!)
    			while(!(SDIO->STA&((1<<5)|(1<<1)|(1<<3)|(1<<8)|(1<<9))))//无上溢/CRC/超时/完成(标志)/起始位错误
    			{
    				if(SDIO->STA&(1<<15))						//接收区半满,表示至少存了8个字
    				{
    					for(count=0;count<8;count++)			//循环读取数据
    					{
    						*(tempbuff+count)=SDIO->FIFO;
    					}
    					tempbuff+=8;	 
    					timeout=0X7FFFFF; 	//读数据溢出时间
    				}else 	//处理超时
    				{
    					if(timeout==0)return SD_DATA_TIMEOUT;
    					timeout--;
    				}
    			}  
    			if(SDIO->STA&(1<<3))		//数据超时错误
    			{										   
    		 		SDIO->ICR|=1<<3; 		//清错误标志
    				return SD_DATA_TIMEOUT;
    		 	}else if(SDIO->STA&(1<<1))	//数据块CRC错误
    			{
    		 		SDIO->ICR|=1<<1; 		//清错误标志
    				return SD_DATA_CRC_FAIL;		   
    			}else if(SDIO->STA&(1<<5)) 	//接收fifo上溢错误
    			{
    		 		SDIO->ICR|=1<<5; 		//清错误标志
    				return SD_RX_OVERRUN;		 
    			}else if(SDIO->STA&(1<<9)) 	//接收起始位错误
    			{
    		 		SDIO->ICR|=1<<9; 		//清错误标志
    				return SD_START_BIT_ERR;		 
    			}   
    			while(SDIO->STA&(1<<21))	//FIFO里面,还存在可用数据
    			{
    				*tempbuff=SDIO->FIFO;	//循环读取数据
    				tempbuff++;
    			}
    	 		if(SDIO->STA&(1<<8))		//接收结束
    			{
    				if((SDIO_STD_CAPACITY_SD_CARD_V1_1==CardType)||(SDIO_STD_CAPACITY_SD_CARD_V2_0==CardType)||(SDIO_HIGH_CAPACITY_SD_CARD==CardType))
    				{
    					SDIO_SendCmd(SD_CMD_STOP_TRANSMISSION,1,0);		//发送CMD12+结束传输 	   
    					errorstatus=SDIO_CmdResp1Error(SD_CMD_STOP_TRANSMISSION);//等待R1响应   
    					if(errorstatus!=SD_OK)return errorstatus;	 
    				}
     			}
    		//	INTX_ENABLE();//开启总中断
    	 		SDIO->ICR=0X5FF;	 		//清除所有标记 
     		}
        else if(DeviceMode==SD_DMA_MODE)
    		{
    	 	    SDIO_SdCard_DMAConfig((u32*)buf,nblks*blksize,0); 
    	   		TransferError=SD_OK;
    			StopCondition=1;			//多块读,需要发送停止传输指令 
    			TransferEnd=0;				//传输结束标置位,在中断服务置1
    			SDIO->MASK|=(1<<1)|(1<<3)|(1<<8)|(1<<5)|(1<<9);	//配置需要的中断 
    		 	SDIO->DCTRL|=1<<3;		 						//SDIO DMA使能 
    	 		while(((DMA2->ISR&0X2000)==RESET)&&timeout)timeout--;//等待传输完成 
    			if(timeout==0)return SD_DATA_TIMEOUT;//超时
    			while((TransferEnd==0)&&(TransferError==SD_OK)); 
    			if(TransferError!=SD_OK)errorstatus=TransferError;  	 
    		}		 
      	}
    	return errorstatus;
    }	
    
    
    /*
    函数功能:SD卡写1个块
    函数参数:
            buf:数据缓存区
            addr:写地址
            blksize:块大小
    返回值:错误状态
    */
    SDIO_SD_ERROR_INFO SDIO_SdCardWriteBlock(u8 *buf,long long addr,  u16 blksize)
    {
    	SDIO_SD_ERROR_INFO errorstatus = SD_OK;
    	u8  power=0,cardstate=0;
    	u32 timeout=0,bytestransferred=0;
    	u32 cardstatus=0,count=0,restwords=0;
    	u32	tlen=blksize;						//总长度(字节)
    	u32*tempbuff=(u32*)buf;								 
     	if(buf==NULL)return SD_INVALID_PARAMETER;//参数错误   
      	SDIO->DCTRL=0x0;							//数据控制寄存器清零(关DMA)   
      	SDIO_SendDataConfig(SD_DATATIMEOUT,0,0,0);	//清除DPSM状态机配置
    	if(SDIO->RESP1&SD_CARD_LOCKED)return SD_LOCK_UNLOCK_FAILED;//卡锁了
     	if(CardType==SDIO_HIGH_CAPACITY_SD_CARD)	//大容量卡
    	{
    		blksize=512;
    		addr>>=9;
    	}    
    	if((blksize>0)&&(blksize<=2048)&&((blksize&(blksize-1))==0))
    	{
    		power=convert_from_bytes_to_power_of_two(blksize);	    
    		SDIO_SendCmd(SD_CMD_SET_BLOCKLEN,1,blksize);	//发送CMD16+设置数据长度为blksize,短响应 	   
    		errorstatus=SDIO_CmdResp1Error(SD_CMD_SET_BLOCKLEN);	//等待R1响应   
    		if(errorstatus!=SD_OK)return errorstatus;   	//响应错误	 
    	}else return SD_INVALID_PARAMETER;	 
      SDIO_SendCmd(SD_CMD_SEND_STATUS,1,(u32)RCA<<16);	//发送CMD13,查询卡的状态,短响应 	   
    	errorstatus=SDIO_CmdResp1Error(SD_CMD_SEND_STATUS);		//等待R1响应   		   
    	if(errorstatus!=SD_OK)return errorstatus;
    	cardstatus=SDIO->RESP1;													  
    	timeout=SD_DATATIMEOUT;
      while(((cardstatus&0x00000100)==0)&&(timeout>0)) 	//检查READY_FOR_DATA位是否置位
    	{
    		timeout--;
    	   	SDIO_SendCmd(SD_CMD_SEND_STATUS,1,(u32)RCA<<16);//发送CMD13,查询卡的状态,短响应 	   
    		errorstatus=SDIO_CmdResp1Error(SD_CMD_SEND_STATUS);	//等待R1响应   		   
    		if(errorstatus!=SD_OK)return errorstatus;				    
    		cardstatus=SDIO->RESP1;													  
    	}
    	if(timeout==0)return SD_ERROR;
       	SDIO_SendCmd(SD_CMD_WRITE_SINGLE_BLOCK,1,addr);	//发送CMD24,写单块指令,短响应 	   
    	errorstatus=SDIO_CmdResp1Error(SD_CMD_WRITE_SINGLE_BLOCK);//等待R1响应   		   
    	if(errorstatus!=SD_OK)return errorstatus;   	  
    	StopCondition=0;									//单块写,不需要发送停止传输指令 
     	SDIO_SendDataConfig(SD_DATATIMEOUT,blksize,power,0);	//blksize, 控制器到卡	  
    	timeout=SDIO_DATATIMEOUT;
    	if(DeviceMode == SD_POLLING_MODE)
    	{
    	//	INTX_DISABLE();//关闭总中断(POLLING模式,严禁中断打断SDIO读写操作!!!)
    		while(!(SDIO->STA&((1<<10)|(1<<4)|(1<<1)|(1<<3)|(1<<9))))//数据块发送成功/下溢/CRC/超时/起始位错误
    		{
    			if(SDIO->STA&(1<<14))							//发送区半空,表示至少存了8个字
    			{
    				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)
    					{
    						SDIO->FIFO=*tempbuff;
    					}
    				}else
    				{
    					for(count=0;count<8;count++)
    					{
    						SDIO->FIFO=*(tempbuff+count);
    					}
    					tempbuff+=8;
    					bytestransferred+=32;
    				}
    				timeout=0X3FFFFFFF;	//写数据溢出时间
    			}else
    			{
    				if(timeout==0)return SD_DATA_TIMEOUT;
    				timeout--;
    			}
    		} 
    		if(SDIO->STA&(1<<3))		//数据超时错误
    		{										   
    	 		SDIO->ICR|=1<<3; 		//清错误标志
    			return SD_DATA_TIMEOUT;
    	 	}else if(SDIO->STA&(1<<1))	//数据块CRC错误
    		{
    	 		SDIO->ICR|=1<<1; 		//清错误标志
    			return SD_DATA_CRC_FAIL;		   
    		}else if(SDIO->STA&(1<<4)) 	//接收fifo下溢错误
    		{
    	 		SDIO->ICR|=1<<4; 		//清错误标志
    			return SD_TX_UNDERRUN;		 
    		}else if(SDIO->STA&(1<<9)) 	//接收起始位错误
    		{
    	 		SDIO->ICR|=1<<9; 		//清错误标志
    			return SD_START_BIT_ERR;		 
    		}   
    //		INTX_ENABLE();//开启总中断
    		SDIO->ICR=0X5FF;	 		//清除所有标记	  
    	}else if(DeviceMode==SD_DMA_MODE)
    	{
    		SDIO_SdCard_DMAConfig((u32*)buf,blksize,1);//SDIO DMA配置
       		TransferError=SD_OK;
    		StopCondition=0;			//单块写,不需要发送停止传输指令 
    		TransferEnd=0;				//传输结束标置位,在中断服务置1
    		SDIO->MASK|=(1<<1)|(1<<3)|(1<<8)|(1<<4)|(1<<9);	//配置产生数据接收完成中断
     	 	SDIO->DCTRL|=1<<3;								//SDIO DMA使能.  
     		while(((DMA2->ISR&0X2000)==RESET)&&timeout)timeout--;//等待传输完成 
    		if(timeout==0)
    		{
      			SDIO_SdCardInit();	 					//重新初始化SD卡,可以解决写入死机的问题
    			return SD_DATA_TIMEOUT;			//超时	 
     		}
    		timeout=SDIO_DATATIMEOUT;
    		while((TransferEnd==0)&&(TransferError==SD_OK)&&timeout)timeout--;
     		if(timeout==0)return SD_DATA_TIMEOUT;			//超时	 
      		if(TransferError!=SD_OK)return TransferError;
     	}  
     	SDIO->ICR=0X5FF;	 		//清除所有标记
     	errorstatus=SDIO_SdCardProgrammingState(&cardstate);
     	while((errorstatus==SD_OK)&&((cardstate==SD_CARD_PROGRAMMING)||(cardstate==SD_CARD_RECEIVING)))
    	{
    		errorstatus=SDIO_SdCardProgrammingState(&cardstate);
    	}   
    	return errorstatus;
    }
    
    
    /*
    函数功能:SD卡写多个块 
    函数参数:
            buf:数据缓存区
            addr:写地址
            blksize:块大小
            nblks:要写入的块数
    返回值:错误状态
    */											   
    SDIO_SD_ERROR_INFO SDIO_SdCardWriteMultiBlocks(u8 *buf,long long addr,u16 blksize,u32 nblks)
    {
    	SDIO_SD_ERROR_INFO errorstatus = SD_OK;
    	u8  power = 0, cardstate = 0;
    	u32 timeout=0,bytestransferred=0;
    	u32 count = 0, restwords = 0;
    	u32 tlen=nblks*blksize;				//总长度(字节)
    	u32 *tempbuff = (u32*)buf;  
      if(buf==NULL)return SD_INVALID_PARAMETER; //参数错误  
      SDIO->DCTRL=0x0;							//数据控制寄存器清零(关DMA)   
      SDIO_SendDataConfig(SD_DATATIMEOUT,0,0,0);	//清除DPSM状态机配置
    	if(SDIO->RESP1&SD_CARD_LOCKED)return SD_LOCK_UNLOCK_FAILED;//卡锁了
     	if(CardType==SDIO_HIGH_CAPACITY_SD_CARD)//大容量卡
    	{
    		blksize=512;
    		addr>>=9;
    	}    
    	if((blksize>0)&&(blksize<=2048)&&((blksize&(blksize-1))==0))
    	{
    		power=convert_from_bytes_to_power_of_two(blksize);	    
    		SDIO_SendCmd(SD_CMD_SET_BLOCKLEN,1,blksize);	//发送CMD16+设置数据长度为blksize,短响应 	   
    		errorstatus=SDIO_CmdResp1Error(SD_CMD_SET_BLOCKLEN);	//等待R1响应   
    		if(errorstatus!=SD_OK)return errorstatus;   	//响应错误	 
    	}else return SD_INVALID_PARAMETER;	 
    	if(nblks>1)
    	{					  
    		if(nblks*blksize>SD_MAX_DATA_LENGTH)return SD_INVALID_PARAMETER;   
        if((SDIO_STD_CAPACITY_SD_CARD_V1_1==CardType)||(SDIO_STD_CAPACITY_SD_CARD_V2_0==CardType)||(SDIO_HIGH_CAPACITY_SD_CARD==CardType))
        {
    			//提高性能
    	 	   	SDIO_SendCmd(SD_CMD_APP_CMD,1,(u32)RCA<<16);	//发送ACMD55,短响应 	   
    			errorstatus=SDIO_CmdResp1Error(SD_CMD_APP_CMD);		//等待R1响应   		   
    			if(errorstatus!=SD_OK)return errorstatus;				    
    	 	   	SDIO_SendCmd(SD_CMD_SET_BLOCK_COUNT,1,nblks);	//发送CMD23,设置块数量,短响应 	   
    			errorstatus=SDIO_CmdResp1Error(SD_CMD_SET_BLOCK_COUNT);//等待R1响应   		   
    			if(errorstatus!=SD_OK)return errorstatus;				    
    		} 
    		SDIO_SendCmd(SD_CMD_WRITE_MULT_BLOCK,1,addr);		//发送CMD25,多块写指令,短响应 	   
    		errorstatus=SDIO_CmdResp1Error(SD_CMD_WRITE_MULT_BLOCK);	//等待R1响应   		   
    		if(errorstatus!=SD_OK)return errorstatus;
     	 	SDIO_SendDataConfig(SD_DATATIMEOUT,nblks*blksize,power,0);//blksize, 控制器到卡	
    		if(DeviceMode==SD_POLLING_MODE)
    	  {
    			timeout=SDIO_DATATIMEOUT;
    			while(!(SDIO->STA&((1<<4)|(1<<1)|(1<<8)|(1<<3)|(1<<9))))//下溢/CRC/数据结束/超时/起始位错误
    			{
    				if(SDIO->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)
    						{
    							SDIO->FIFO=*tempbuff;
    						}
    					}else 										//发送区半空,可以发送至少8字(32字节)数据
    					{
    						for(count=0;count<SD_HALFFIFO;count++)
    						{
    							SDIO->FIFO=*(tempbuff+count);
    						}
    						tempbuff+=SD_HALFFIFO;
    						bytestransferred+=SD_HALFFIFOBYTES;
    					}
    					timeout=0X3FFFFFFF;	//写数据溢出时间
    				}else
    				{
    					if(timeout==0)return SD_DATA_TIMEOUT; 
    					timeout--;
    				}
    			} 
    			if(SDIO->STA&(1<<3))		//数据超时错误
    			{										   
    		 		SDIO->ICR|=1<<3; 		//清错误标志
    				return SD_DATA_TIMEOUT;
    		 	}else if(SDIO->STA&(1<<1))	//数据块CRC错误
    			{
    		 		SDIO->ICR|=1<<1; 		//清错误标志
    				return SD_DATA_CRC_FAIL;		   
    			}else if(SDIO->STA&(1<<4)) 	//接收fifo下溢错误
    			{
    		 		SDIO->ICR|=1<<4; 		//清错误标志
    				return SD_TX_UNDERRUN;		 
    			}else if(SDIO->STA&(1<<9)) 	//接收起始位错误
    			{
    		 		SDIO->ICR|=1<<9; 		//清错误标志
    				return SD_START_BIT_ERR;		 
    			}   										   
    			if(SDIO->STA&(1<<8))		//发送结束
    			{															 
    				if((SDIO_STD_CAPACITY_SD_CARD_V1_1==CardType)||(SDIO_STD_CAPACITY_SD_CARD_V2_0==CardType)||(SDIO_HIGH_CAPACITY_SD_CARD==CardType))
    				{
    					SDIO_SendCmd(SD_CMD_STOP_TRANSMISSION,1,0);		//发送CMD12+结束传输 	   
    					errorstatus=SDIO_CmdResp1Error(SD_CMD_STOP_TRANSMISSION);//等待R1响应   
    					if(errorstatus!=SD_OK)return errorstatus;	 
    				}
    			}
    //			INTX_ENABLE();//开启总中断
            SDIO->ICR=0X5FF;	 		//清除所有标记 
    	    }
          else if(DeviceMode==SD_DMA_MODE)
          {
            SDIO_SdCard_DMAConfig((u32*)buf,nblks*blksize,1);//SDIO DMA配置
            TransferError=SD_OK;
            StopCondition=1;			//多块写,需要发送停止传输指令 
            TransferEnd=0;				//传输结束标置位,在中断服务置1
            SDIO->MASK|=(1<<1)|(1<<3)|(1<<8)|(1<<4)|(1<<9);	//配置产生数据接收完成中断
            SDIO->DCTRL|=1<<3;								//SDIO DMA使能. 
            timeout=SDIO_DATATIMEOUT;
            while(((DMA2->ISR&0X2000)==RESET)&&timeout)timeout--;//等待传输完成 
            if(timeout==0)	 								//超时
            {									  
              SDIO_SdCardInit();	 					//重新初始化SD卡,可以解决写入死机的问题
              return SD_DATA_TIMEOUT;			//超时	 
            }
            timeout=SDIO_DATATIMEOUT;
            while((TransferEnd==0)&&(TransferError==SD_OK)&&timeout)timeout--;
            if(timeout==0)return SD_DATA_TIMEOUT;			//超时	 
            if(TransferError!=SD_OK)return TransferError;	 
          }
      	}
     	SDIO->ICR=0X5FF;	 		//清除所有标记
     	errorstatus=SDIO_SdCardProgrammingState(&cardstate);
     	while((errorstatus==SD_OK)&&((cardstate==SD_CARD_PROGRAMMING)||(cardstate==SD_CARD_RECEIVING)))
    	{
    		errorstatus=SDIO_SdCardProgrammingState(&cardstate);
    	}   
    	return errorstatus;	   
    }
    
    
    /*
    函数功能: SDIO中断服务函数
    */	  
    void SDIO_IRQHandler(void) 
    {											
        SDIO_SdCardProcessIRQSrc();//处理所有SDIO相关中断
    }
    
    
    /*
    函数功能: SDIO中断处理函数
    函数参数: 处理SDIO传输过程中的各种中断事务
    返回值:错误代码
    */
    SDIO_SD_ERROR_INFO SDIO_SdCardProcessIRQSrc(void)
    {
    	if(SDIO->STA&(1<<8))//接收完成中断
    	{	 
    		if(StopCondition==1)
    		{
    			SDIO_SendCmd(SD_CMD_STOP_TRANSMISSION,1,0);		//发送CMD12,结束传输 	   
    			TransferError=SDIO_CmdResp1Error(SD_CMD_STOP_TRANSMISSION);
    		}else TransferError = SD_OK;	
     		SDIO->ICR|=1<<8;//清除完成中断标记
    		SDIO->MASK&=~((1<<1)|(1<<3)|(1<<8)|(1<<14)|(1<<15)|(1<<4)|(1<<5)|(1<<9));//关闭相关中断
     		TransferEnd = 1;
    		return(TransferError);
    	}
     	if(SDIO->STA&(1<<1))//数据CRC错误
    	{
    		SDIO->ICR|=1<<1;//清除中断标记
    		SDIO->MASK&=~((1<<1)|(1<<3)|(1<<8)|(1<<14)|(1<<15)|(1<<4)|(1<<5)|(1<<9));//关闭相关中断
    	  TransferError = SD_DATA_CRC_FAIL;
    	  return(SD_DATA_CRC_FAIL);
    	}
     	if(SDIO->STA&(1<<3))//数据超时错误
    	{
    		SDIO->ICR|=1<<3;//清除中断标记
    		SDIO->MASK&=~((1<<1)|(1<<3)|(1<<8)|(1<<14)|(1<<15)|(1<<4)|(1<<5)|(1<<9));//关闭相关中断
    	  TransferError = SD_DATA_TIMEOUT;
    	  return(SD_DATA_TIMEOUT);
    	}
      if(SDIO->STA&(1<<5))//FIFO上溢错误
    	{
    		SDIO->ICR|=1<<5;//清除中断标记
    		SDIO->MASK&=~((1<<1)|(1<<3)|(1<<8)|(1<<14)|(1<<15)|(1<<4)|(1<<5)|(1<<9));//关闭相关中断
    	  TransferError = SD_RX_OVERRUN;
    	  return(SD_RX_OVERRUN);
    	}
      if(SDIO->STA&(1<<4))//FIFO下溢错误
    	{
    		SDIO->ICR|=1<<4;//清除中断标记
    		SDIO->MASK&=~((1<<1)|(1<<3)|(1<<8)|(1<<14)|(1<<15)|(1<<4)|(1<<5)|(1<<9));//关闭相关中断
    	  TransferError = SD_TX_UNDERRUN;
    	  return(SD_TX_UNDERRUN);
    	}
    	if(SDIO->STA&(1<<9))//起始位错误
    	{
    		SDIO->ICR|=1<<9;//清除中断标记
    		SDIO->MASK&=~((1<<1)|(1<<3)|(1<<8)|(1<<14)|(1<<15)|(1<<4)|(1<<5)|(1<<9));//关闭相关中断
    	  TransferError = SD_START_BIT_ERR;
    	  return(SD_START_BIT_ERR);
    	}
    	return(SD_OK);
    }
    
    
    /*
    函数功能: 检查CMD0的执行状态
    返回值:   sd卡错误码
    */
    SDIO_SD_ERROR_INFO SDIO_CmdErrorCheck(void)
    {
        SDIO_SD_ERROR_INFO errorstatus = SD_OK;
        u32 timeout=SDIO_CMD0TIMEOUT;	   
        while(timeout--)
        {
          if(SDIO->STA&(1<<7))break;	//命令已发送(无需响应)	 
        }	    
        if(timeout==0)return SD_CMD_RSP_TIMEOUT;  
        SDIO->ICR=0X5FF;				//清除标记
        return errorstatus;
    }
    
    
    /*
    函数功能: 检查R7响应的错误状态
    函数参数: 返回值:sd卡错误码
    */
    SDIO_SD_ERROR_INFO SDIO_CmdResp7Error(void)
    {
    	SDIO_SD_ERROR_INFO errorstatus=SD_OK;
    	u32 status;
    	u32 timeout=SDIO_CMD0TIMEOUT;
     	while(timeout--)
    	{
    		status=SDIO->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兼容卡,或者不支持设定的电压范围
    		SDIO->ICR|=1<<2;				//清除命令响应超时标志
    		return errorstatus;
    	}	 
    	if(status&1<<6)						//成功接收到响应
    	{								   
    		errorstatus=SD_OK;
    		SDIO->ICR|=1<<6;				//清除响应标志
     	}
    	return errorstatus;
    }
    
    
    /*
    函数功能:检查R1响应的错误状态
    函数参数:
            cmd:当前命令
    返回值:sd卡错误码    
    */
    SDIO_SD_ERROR_INFO SDIO_CmdResp1Error(u8 cmd)
    {	  
        u32 status; 
        while(1)
        {
          status=SDIO->STA;
          if(status&((1<<0)|(1<<2)|(1<<6)))break;//CRC错误/命令响应超时/已经收到响应(CRC校验成功)
        } 
        if(status&(1<<2))					//响应超时
        {																				    
          SDIO->ICR=1<<2;					//清除命令响应超时标志
          return SD_CMD_RSP_TIMEOUT;
        }	
        if(status&(1<<0))					//CRC错误
        {																				    
          SDIO->ICR=1<<0;					//清除标志
          return SD_CMD_CRC_FAIL;
        }		
        if(SDIO->RESPCMD!=cmd)return SD_ILLEGAL_CMD;//命令不匹配 
          SDIO->ICR=0X5FF;	 				//清除标记
        return (SDIO_SD_ERROR_INFO)(SDIO->RESP1&SD_OCR_ERRORBITS);//返回卡响应
    }
    
    
    /*
    函数功能: 检查R3响应的错误状态
    返回值:   错误状态
    */
    SDIO_SD_ERROR_INFO SDIO_CmdResp3Error(void)
    {
    	u32 status;						 
     	while(1)
    	{
    		status=SDIO->STA;
    		if(status&((1<<0)|(1<<2)|(1<<6)))break;//CRC错误/命令响应超时/已经收到响应(CRC校验成功)	
    	}
     	if(status&(1<<2))					//响应超时
    	{											 
    		SDIO->ICR|=1<<2;				//清除命令响应超时标志
    		return SD_CMD_RSP_TIMEOUT;
    	}	 
      SDIO->ICR=0X5FF;	 				//清除标记
     	return SD_OK;								  
    }
    
    
    /*
    函数功能: 检查R2响应的错误状态
    返回值:错误状态
    */
    SDIO_SD_ERROR_INFO SDIO_CmdResp2Error(void)
    {
        SDIO_SD_ERROR_INFO errorstatus=SD_OK;
        u32 status;
        u32 timeout=SDIO_CMD0TIMEOUT;
        while(timeout--)
        {
          status=SDIO->STA;
          if(status&((1<<0)|(1<<2)|(1<<6)))break;//CRC错误/命令响应超时/已经收到响应(CRC校验成功)	
        }
        if((timeout==0)||(status&(1<<2)))	//响应超时
        {																				    
          errorstatus=SD_CMD_RSP_TIMEOUT; 
          SDIO->ICR|=1<<2;				//清除命令响应超时标志
          return errorstatus;
        }	 
        if(status&1<<0)						//CRC错误
        {								   
          errorstatus=SD_CMD_CRC_FAIL;
          SDIO->ICR|=1<<0;				//清除响应标志
        }
        SDIO->ICR=0X5FF;	 				//清除标记
        return errorstatus;								    		 
    }
    
    
    /*
    函数功能: 检查R6响应的错误状态
    函数参数:
            cmd:之前发送的命令
            prca:卡返回的RCA地址
    返回值:错误状态
    */
    SDIO_SD_ERROR_INFO SDIO_CmdResp6Error(u8 cmd,u16*prca)
    {
    	SDIO_SD_ERROR_INFO errorstatus=SD_OK;
    	u32 status;					    
    	u32 rspr1;
     	while(1)
    	{
    		status=SDIO->STA;
    		if(status&((1<<0)|(1<<2)|(1<<6)))break;//CRC错误/命令响应超时/已经收到响应(CRC校验成功)	
    	}
    	if(status&(1<<2))					//响应超时
    	{																				    
     		SDIO->ICR|=1<<2;				//清除命令响应超时标志
    		return SD_CMD_RSP_TIMEOUT;
    	}	 	 
    	if(status&1<<0)						//CRC错误
    	{								   
    		SDIO->ICR|=1<<0;				//清除响应标志
     		return SD_CMD_CRC_FAIL;
    	}
    	if(SDIO->RESPCMD!=cmd)		//判断是否响应cmd命令
    	{
     		return SD_ILLEGAL_CMD; 		
    	}	    
    	SDIO->ICR=0X5FF;	 				//清除所有标记
    	rspr1=SDIO->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;
    }
    
    
    /*
    函数功能:SDIO使能宽总线模式
    函数参数:
             enx:0,不使能;1,使能;
    返回值:错误状态
    */
    SDIO_SD_ERROR_INFO SDIO_SdCardEnWideBus(u8 enx)
    {
    	SDIO_SD_ERROR_INFO errorstatus = SD_OK;
     	u32 scr[2]={0,0};
    	u8 arg=0X00;
    	if(enx)arg=0X02;
    	else arg=0X00;
     	if(SDIO->RESP1&SD_CARD_LOCKED)return SD_LOCK_UNLOCK_FAILED;//SD卡处于LOCKED状态		    
     	errorstatus=SDIO_SdCardFindSCR(RCA,scr);						//得到SCR寄存器数据
     	if(errorstatus!=SD_OK)return errorstatus;
    	if((scr[1]&SD_WIDE_BUS_SUPPORT)!=SD_ALLZERO)		//支持宽总线
    	{
    	 	SDIO_SendCmd(SD_CMD_APP_CMD,1,(u32)RCA<<16);	//发送CMD55+RCA,短响应											  
    	 	errorstatus=SDIO_CmdResp1Error(SD_CMD_APP_CMD);
    	 	if(errorstatus!=SD_OK)return errorstatus; 
    	 	SDIO_SendCmd(SD_CMD_APP_SD_SET_BUSWIDTH,1,arg);//发送ACMD6,短响应,参数:10,4位;00,1位.											  
    		errorstatus=SDIO_CmdResp1Error(SD_CMD_APP_SD_SET_BUSWIDTH);
    		return errorstatus;
    	}else return SD_REQUEST_NOT_APPLICABLE;				//不支持宽总线设置 	 
    }
    
    
    /*
    函数功能: 检查卡是否正在执行写操作
    函数参数: pstatus:当前状态
    返回值:错误代码
    */
    SDIO_SD_ERROR_INFO SDIO_SdCardProgrammingState(u8 *pstatus)
    {
     	vu32 respR1 = 0, status = 0; 
      SDIO_SendCmd(SD_CMD_SEND_STATUS,1,(u32)RCA<<16);		//发送CMD13 	   
      status=SDIO->STA;
    	while(!(status&((1<<0)|(1<<6)|(1<<2))))status=SDIO->STA;//等待操作完成
      if(status&(1<<0))			//CRC检测失败
    	{
    		SDIO->ICR|=1<<0;		//清除错误标记
    		return SD_CMD_CRC_FAIL;
    	}
      if(status&(1<<2))			//命令超时 
    	{
    		SDIO->ICR|=1<<2;		//清除错误标记
    		return SD_CMD_RSP_TIMEOUT;
    	}
     	if(SDIO->RESPCMD!=SD_CMD_SEND_STATUS)return SD_ILLEGAL_CMD;
    	SDIO->ICR=0X5FF;	 		//清除所有标记
    	respR1=SDIO->RESP1;
    	*pstatus=(u8)((respR1>>9)&0x0000000F);
    	return SD_OK;
    }
    
    
    /*
    函数功能: 读取当前卡状态
    函数参数: 
            pcardstatus:卡状态
    返回值 :错误代码
    */
    SDIO_SD_ERROR_INFO SDIO_SdCardSendStatus(uint32_t *pcardstatus)
    {
    	SDIO_SD_ERROR_INFO errorstatus = SD_OK;
    	if(pcardstatus==NULL)
    	{
    		errorstatus=SD_INVALID_PARAMETER;
    		return errorstatus;
    	}
     	SDIO_SendCmd(SD_CMD_SEND_STATUS,1,RCA<<16);	//发送CMD13,短响应		 
    	errorstatus=SDIO_CmdResp1Error(SD_CMD_SEND_STATUS);	//查询响应状态 
    	if(errorstatus!=SD_OK)return errorstatus;
    	*pcardstatus=SDIO->RESP1;//读取响应值
    	return errorstatus;
    }
    
    
    /*
    函数功能: 返回SD卡的状态
    返回值  : SD卡状态
    */
    SDCardState SDIO_SdCardGetState(void)
    {
    	u32 resp1=0;
    	if(SDIO_SdCardSendStatus(&resp1)!=SD_OK)return SD_CARD_ERROR;
    	else return (SDCardState)((resp1>>9) & 0x0F);
    }
    
    
    /*
    函数功能:查找SD卡的SCR寄存器值
    函数参数:
            rca:卡相对地址
            pscr:数据缓存区(存储SCR内容)
    返回值:错误状态	
    */   
    SDIO_SD_ERROR_INFO SDIO_SdCardFindSCR(u16 rca,u32 *pscr)
    { 
    	u32 index = 0; 
    	SDIO_SD_ERROR_INFO errorstatus = SD_OK;
    	u32 tempscr[2]={0,0};  
     	SDIO_SendCmd(SD_CMD_SET_BLOCKLEN,1,8);			//发送CMD16,短响应,设置Block Size为8字节											  
     	errorstatus=SDIO_CmdResp1Error(SD_CMD_SET_BLOCKLEN);
     	if(errorstatus!=SD_OK)return errorstatus;	    
      SDIO_SendCmd(SD_CMD_APP_CMD,1,(u32)rca<<16);	//发送CMD55,短响应 									  
     	errorstatus=SDIO_CmdResp1Error(SD_CMD_APP_CMD);
     	if(errorstatus!=SD_OK)return errorstatus;
    	SDIO_SendDataConfig(SD_DATATIMEOUT,8,3,1);		//8个字节长度,block为8字节,SD卡到SDIO.
      SDIO_SendCmd(SD_CMD_SD_APP_SEND_SCR,1,0);		//发送ACMD51,短响应,参数为0											  
     	errorstatus=SDIO_CmdResp1Error(SD_CMD_SD_APP_SEND_SCR);
     	if(errorstatus!=SD_OK)return errorstatus;							   
     	while(!(SDIO->STA&(SDIO_FLAG_RXOVERR|SDIO_FLAG_DCRCFAIL|SDIO_FLAG_DTIMEOUT|SDIO_FLAG_DBCKEND|SDIO_FLAG_STBITERR)))
    	{ 
    		if(SDIO->STA&(1<<21))//接收FIFO数据可用
    		{
    			*(tempscr+index)=SDIO->FIFO;	//读取FIFO内容
    			index++;
    			if(index>=2)break;
    		}
    	}
     	if(SDIO->STA&(1<<3))		//接收数据超时
    	{										 
     		SDIO->ICR|=1<<3;		//清除标记
    		return SD_DATA_TIMEOUT;
    	}
    	else if(SDIO->STA&(1<<1))	//已发送/接收的数据块CRC校验错误
    	{
     		SDIO->ICR|=1<<1;		//清除标记
    		return SD_DATA_CRC_FAIL;   
    	}
    	else if(SDIO->STA&(1<<5))	//接收FIFO溢出
    	{
     		SDIO->ICR|=1<<5;		//清除标记
    		return SD_RX_OVERRUN;   	   
    	}
    	else if(SDIO->STA&(1<<9))	//起始位检测错误
    	{
     		SDIO->ICR|=1<<9;		//清除标记
    		return SD_START_BIT_ERR;    
    	}
      SDIO->ICR=0X5FF;	 		//清除标记	 
    	//把数据顺序按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;
    }
    
    
    /*
    函数功能: 得到NumberOfBytes以2为底的指数
    函数参数: NumberOfBytes:字节数
    返回值:以2为底的指数值
    */
    u8 convert_from_bytes_to_power_of_two(u16 NumberOfBytes)
    {
    	u8 count=0;
    	while(NumberOfBytes!=1)
    	{
    		NumberOfBytes>>=1;
    		count++;
    	}
    	return count;
    }
    
    
    /*
    函数功能: 配置SDIO DMA  
    函数参数: 
            mbuf:存储器地址
            bufsize:传输数据量
            dir:方向;1,存储器-->SDIO(写数据);0,SDIO-->存储器(读数据);
    */
    void SDIO_SdCard_DMAConfig(u32*mbuf,u32 bufsize,u8 dir)
    {		 
     	DMA2->IFCR|=(0XF<<12);				//清除DMA2通道4的各种标记
     	DMA2_Channel4->CCR&=~(1<<0);		//关闭DMA 通道4
      DMA2_Channel4->CCR&=~(0X7FF<<4);	//清除之前的设置,DIR,CIRC,PINC,MINC,PSIZE,MSIZE,PL,MEM2MEM
     	DMA2_Channel4->CCR|=dir<<4;  		//从存储器读   
    	DMA2_Channel4->CCR|=0<<5;  			//普通模式
    	DMA2_Channel4->CCR|=0<<6; 			//外设地址非增量模式
    	DMA2_Channel4->CCR|=1<<7;  			//存储器增量模式
    	DMA2_Channel4->CCR|=2<<8;  			//外设数据宽度为32位
    	DMA2_Channel4->CCR|=2<<10; 			//存储器数据宽度32位
    	DMA2_Channel4->CCR|=2<<12; 			//高优先级	  
      DMA2_Channel4->CNDTR=bufsize/4;   	//DMA2,传输数据量	  
     	DMA2_Channel4->CPAR=(u32)&SDIO->FIFO;//DMA2 外设地址 
    	DMA2_Channel4->CMAR=(u32)mbuf; 		//DMA2,存储器地址
     	DMA2_Channel4->CCR|=1<<0; 			//开启DMA通道
    }
    
    
    /*
    函数功能: 读SD卡
    函数参数:
            buf:读数据缓存区
            sector:扇区地址
            cnt:扇区个数
    返回值:错误状态;0,正常;其他,错误代码;				
    */ 				 
    u8 SDIO_SdCardReadDiskSector(u8*buf,u32 sector,u8 cnt)
    {
    	u8 sta=SD_OK;
    	long long lsector=sector;
    	u8 n;
    	lsector<<=9;
    	if((u32)buf%4!=0)
    	{
    	 	for(n=0;n<cnt;n++)
    		{
    		 	sta=SDIO_SdCardReadBlock(SDIO_DATA_BUFFER,lsector+512*n,512);//单个sector的读操作
    			memcpy(buf,SDIO_DATA_BUFFER,512);
    			buf+=512;
    		} 
    	}else
    	{
    		if(cnt==1)sta=SDIO_SdCardReadBlock(buf,lsector,512);    	//单个sector的读操作
    		else sta=SDIO_SdCardReadMultiBlocks(buf,lsector,512,cnt);//多个sector  
    	}
    	return sta;
    }
    
    
    /*
    函数功能:写SD卡 
    函数参数:
            buf:写数据缓存区
            sector:扇区地址
            cnt:扇区个数
    返回值:错误状态;0,正常;其他,错误代码;	
    */
    u8 SDIO_SdCardWriteDiskSector(u8*buf,u32 sector,u8 cnt)
    {
    	u8 sta=SD_OK;
    	u8 n;
    	long long lsector=sector;
    	lsector<<=9;
    	if((u32)buf%4!=0)
    	{
    	 	for(n=0;n<cnt;n++)
    		{
    			memcpy(SDIO_DATA_BUFFER,buf,512);
    		 	sta=SDIO_SdCardWriteBlock(SDIO_DATA_BUFFER,lsector+512*n,512);//单个sector的写操作
    			buf+=512;
    		} 
    	}else
    	{
    		if(cnt==1)sta=SDIO_SdCardWriteBlock(buf,lsector,512);    	//单个sector的写操作
    		else sta=SDIO_SdCardWriteMultiBlocks(buf,lsector,512,cnt);	//多个sector  
    	}
    	return sta;
    }
    
    
    • 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
    • 1131
    • 1132
    • 1133
    • 1134
    • 1135
    • 1136
    • 1137
    • 1138
    • 1139
    • 1140
    • 1141
    • 1142
    • 1143
    • 1144
    • 1145
    • 1146
    • 1147
    • 1148
    • 1149
    • 1150
    • 1151
    • 1152
    • 1153
    • 1154
    • 1155
    • 1156
    • 1157
    • 1158
    • 1159
    • 1160
    • 1161
    • 1162
    • 1163
    • 1164
    • 1165
    • 1166
    • 1167
    • 1168
    • 1169
    • 1170
    • 1171
    • 1172
    • 1173
    • 1174
    • 1175
    • 1176
    • 1177
    • 1178
    • 1179
    • 1180
    • 1181
    • 1182
    • 1183
    • 1184
    • 1185
    • 1186
    • 1187
    • 1188
    • 1189
    • 1190
    • 1191
    • 1192
    • 1193
    • 1194
    • 1195
    • 1196
    • 1197
    • 1198
    • 1199
    • 1200
    • 1201
    • 1202
    • 1203
    • 1204
    • 1205
    • 1206
    • 1207
    • 1208
    • 1209
    • 1210
    • 1211
    • 1212
    • 1213
    • 1214
    • 1215
    • 1216
    • 1217
    • 1218
    • 1219
    • 1220
    • 1221
    • 1222
    • 1223
    • 1224
    • 1225
    • 1226
    • 1227
    • 1228
    • 1229
    • 1230
    • 1231
    • 1232
    • 1233
    • 1234
    • 1235
    • 1236
    • 1237
    • 1238
    • 1239
    • 1240
    • 1241
    • 1242
    • 1243
    • 1244
    • 1245
    • 1246
    • 1247
    • 1248
    • 1249
    • 1250
    • 1251
    • 1252
    • 1253
    • 1254
    • 1255
    • 1256
    • 1257
    • 1258
    • 1259
    • 1260
    • 1261
    • 1262
    • 1263
    • 1264
    • 1265
    • 1266
    • 1267
    • 1268
    • 1269
    • 1270
    • 1271
    • 1272
    • 1273
    • 1274
    • 1275
    • 1276
    • 1277
    • 1278
    • 1279
    • 1280
    • 1281
    • 1282
    • 1283
    • 1284
    • 1285
    • 1286
    • 1287
    • 1288
    • 1289
    • 1290
    • 1291
    • 1292
    • 1293
    • 1294
    • 1295
    • 1296
    • 1297
    • 1298
    • 1299
    • 1300
    • 1301
    • 1302
    • 1303
    • 1304
    • 1305
    • 1306
    • 1307
    • 1308
    • 1309
    • 1310
    • 1311
    • 1312
    • 1313
    • 1314
    • 1315
    • 1316
    • 1317
    • 1318
    • 1319
    • 1320
    • 1321
    • 1322
    • 1323
    • 1324
    • 1325
    • 1326
    • 1327
    • 1328
    • 1329
    • 1330
    • 1331
    • 1332
    • 1333
    • 1334
    • 1335
    • 1336
    • 1337
    • 1338
    • 1339
    • 1340
    • 1341
    • 1342
    • 1343
    • 1344
    • 1345
    • 1346
    • 1347
    • 1348
    • 1349
    • 1350
    • 1351
    • 1352
    • 1353
    • 1354
    • 1355
    • 1356
    • 1357
    • 1358
    • 1359
    • 1360
    • 1361
    • 1362
    • 1363
    • 1364
    • 1365
    • 1366
    • 1367
    • 1368
    • 1369
    • 1370
    • 1371
    • 1372
    • 1373
    • 1374
    • 1375
    • 1376
    • 1377
    • 1378
    • 1379
    • 1380
    • 1381
    • 1382
    • 1383
    • 1384
    • 1385
    • 1386
    • 1387
    • 1388
    • 1389
    • 1390
    • 1391
    • 1392
    • 1393
    • 1394
    • 1395
    • 1396
    • 1397
    • 1398
    • 1399
    • 1400
    • 1401
    • 1402
    • 1403
    • 1404
    • 1405
    • 1406
    (3)sdio.h
    #ifndef __SDIO_SDCARD_H
    #define __SDIO_SDCARD_H																			   
    #include "stm32f10x.h" 													   
    
    //SDIO相关标志位
    #define SDIO_FLAG_CCRCFAIL                  ((uint32_t)0x00000001)
    #define SDIO_FLAG_DCRCFAIL                  ((uint32_t)0x00000002)
    #define SDIO_FLAG_CTIMEOUT                  ((uint32_t)0x00000004)
    #define SDIO_FLAG_DTIMEOUT                  ((uint32_t)0x00000008)
    #define SDIO_FLAG_TXUNDERR                  ((uint32_t)0x00000010)
    #define SDIO_FLAG_RXOVERR                   ((uint32_t)0x00000020)
    #define SDIO_FLAG_CMDREND                   ((uint32_t)0x00000040)
    #define SDIO_FLAG_CMDSENT                   ((uint32_t)0x00000080)
    #define SDIO_FLAG_DATAEND                   ((uint32_t)0x00000100)
    #define SDIO_FLAG_STBITERR                  ((uint32_t)0x00000200)
    #define SDIO_FLAG_DBCKEND                   ((uint32_t)0x00000400)
    #define SDIO_FLAG_CMDACT                    ((uint32_t)0x00000800)
    #define SDIO_FLAG_TXACT                     ((uint32_t)0x00001000)
    #define SDIO_FLAG_RXACT                     ((uint32_t)0x00002000)
    #define SDIO_FLAG_TXFIFOHE                  ((uint32_t)0x00004000)
    #define SDIO_FLAG_RXFIFOHF                  ((uint32_t)0x00008000)
    #define SDIO_FLAG_TXFIFOF                   ((uint32_t)0x00010000)
    #define SDIO_FLAG_RXFIFOF                   ((uint32_t)0x00020000)
    #define SDIO_FLAG_TXFIFOE                   ((uint32_t)0x00040000)
    #define SDIO_FLAG_RXFIFOE                   ((uint32_t)0x00080000)
    #define SDIO_FLAG_TXDAVL                    ((uint32_t)0x00100000)
    #define SDIO_FLAG_RXDAVL                    ((uint32_t)0x00200000)
    #define SDIO_FLAG_SDIOIT                    ((uint32_t)0x00400000)
    #define SDIO_FLAG_CEATAEND                  ((uint32_t)0x00800000)
    
    
    //用户配置区			  
    //SDIO时钟计算公式:SDIO_CK时钟=SDIOCLK/[clkdiv+2];其中,SDIOCLK一般为72Mhz
    //使用DMA模式的时候,传输速率可以到24Mhz,不过如果你的卡不是高速卡,可能也会出错
    //出错就请降低时钟,使用查询模式的话,推荐SDIO_TRANSFER_CLK_DIV设置为3或者更大
    #define SDIO_INIT_CLK_DIV        0xB2 		//SDIO初始化频率,最大400Kh  
    #define SDIO_TRANSFER_CLK_DIV    0x04		//SDIO传输频率,该值太小可能会导致读写文件出错 
    										 
    
     
    //SDIO工作模式定义,通过SDIO_SdCardSetDeviceMode函数设置.
    #define SD_POLLING_MODE    	0  	//查询模式,该模式下,如果读写有问题,建议增大SDIO_TRANSFER_CLK_DIV的设置.
    #define SD_DMA_MODE    		1	//DMA模式,该模式下,如果读写有问题,建议增大SDIO_TRANSFER_CLK_DIV的设置.   
    
    //SDIO 各种错误枚举定义
    typedef enum
    {	 
    	//特殊错误定义 
    	SD_CMD_CRC_FAIL                    = (1), /*!< Command response received (but CRC check failed) */
    	SD_DATA_CRC_FAIL                   = (2), /*!< Data bock sent/received (CRC check Failed) */
    	SD_CMD_RSP_TIMEOUT                 = (3), /*!< Command response timeout */
    	SD_DATA_TIMEOUT                    = (4), /*!< Data time out */
    	SD_TX_UNDERRUN                     = (5), /*!< Transmit FIFO under-run */
    	SD_RX_OVERRUN                      = (6), /*!< Receive FIFO over-run */
    	SD_START_BIT_ERR                   = (7), /*!< Start bit not detected on all data signals in widE bus mode */
    	SD_CMD_OUT_OF_RANGE                = (8), /*!< CMD's argument was out of range.*/
    	SD_ADDR_MISALIGNED                 = (9), /*!< Misaligned address */
    	SD_BLOCK_LEN_ERR                   = (10), /*!< Transferred block length is not allowed for the card or the number of transferred bytes does not match the block length */
    	SD_ERASE_SEQ_ERR                   = (11), /*!< An error in the sequence of erase command occurs.*/
    	SD_BAD_ERASE_PARAM                 = (12), /*!< An Invalid selection for erase groups */
    	SD_WRITE_PROT_VIOLATION            = (13), /*!< Attempt to program a write protect block */
    	SD_LOCK_UNLOCK_FAILED              = (14), /*!< Sequence or password error has been detected in unlock command or if there was an attempt to access a locked card */
    	SD_COM_CRC_FAILED                  = (15), /*!< CRC check of the previous command failed */
    	SD_ILLEGAL_CMD                     = (16), /*!< Command is not legal for the card state */
    	SD_CARD_ECC_FAILED                 = (17), /*!< Card internal ECC was applied but failed to correct the data */
    	SD_CC_ERROR                        = (18), /*!< Internal card controller error */
    	SD_GENERAL_UNKNOWN_ERROR           = (19), /*!< General or Unknown error */
    	SD_STREAM_READ_UNDERRUN            = (20), /*!< The card could not sustain data transfer in stream read operation. */
    	SD_STREAM_WRITE_OVERRUN            = (21), /*!< The card could not sustain data programming in stream mode */
    	SD_CID_CSD_OVERWRITE               = (22), /*!< CID/CSD overwrite error */
    	SD_WP_ERASE_SKIP                   = (23), /*!< only partial address space was erased */
    	SD_CARD_ECC_DISABLED               = (24), /*!< Command has been executed without using internal ECC */
    	SD_ERASE_RESET                     = (25), /*!< Erase sequence was cleared before executing because an out of erase sequence command was received */
    	SD_AKE_SEQ_ERROR                   = (26), /*!< Error in sequence of authentication. */
    	SD_INVALID_VOLTRANGE               = (27),
    	SD_ADDR_OUT_OF_RANGE               = (28),
    	SD_SWITCH_ERROR                    = (29),
    	SD_SDIO_DISABLED                   = (30),
    	SD_SDIO_FUNCTION_BUSY              = (31),
    	SD_SDIO_FUNCTION_FAILED            = (32),
    	SD_SDIO_UNKNOWN_FUNCTION           = (33),
    	//标准错误定义
    	SD_INTERNAL_ERROR, 
    	SD_NOT_CONFIGURED,
    	SD_REQUEST_PENDING, 
    	SD_REQUEST_NOT_APPLICABLE, 
    	SD_INVALID_PARAMETER,  
    	SD_UNSUPPORTED_FEATURE,  
    	SD_UNSUPPORTED_HW,  
    	SD_ERROR,  
    	SD_OK = 0 
    } SDIO_SD_ERROR_INFO;		  
    
    //SD卡CSD寄存器数据		  
    typedef struct
    {
    	u8  CSDStruct;            /*!< CSD structure */
    	u8  SysSpecVersion;       /*!< System specification version */
    	u8  Reserved1;            /*!< Reserved */
    	u8  TAAC;                 /*!< Data read access-time 1 */
    	u8  NSAC;                 /*!< Data read access-time 2 in CLK cycles */
    	u8  MaxBusClkFrec;        /*!< Max. bus clock frequency */
    	u16 CardComdClasses;      /*!< Card command classes */
    	u8  RdBlockLen;           /*!< Max. read data block length */
    	u8  PartBlockRead;        /*!< Partial blocks for read allowed */
    	u8  WrBlockMisalign;      /*!< Write block misalignment */
    	u8  RdBlockMisalign;      /*!< Read block misalignment */
    	u8  DSRImpl;              /*!< DSR implemented */
    	u8  Reserved2;            /*!< Reserved */
    	u32 DeviceSize;           /*!< Device Size */
    	u8  MaxRdCurrentVDDMin;   /*!< Max. read current @ VDD min */
    	u8  MaxRdCurrentVDDMax;   /*!< Max. read current @ VDD max */
    	u8  MaxWrCurrentVDDMin;   /*!< Max. write current @ VDD min */
    	u8  MaxWrCurrentVDDMax;   /*!< Max. write current @ VDD max */
    	u8  DeviceSizeMul;        /*!< Device size multiplier */
    	u8  EraseGrSize;          /*!< Erase group size */
    	u8  EraseGrMul;           /*!< Erase group size multiplier */
    	u8  WrProtectGrSize;      /*!< Write protect group size */
    	u8  WrProtectGrEnable;    /*!< Write protect group enable */
    	u8  ManDeflECC;           /*!< Manufacturer default ECC */
    	u8  WrSpeedFact;          /*!< Write speed factor */
    	u8  MaxWrBlockLen;        /*!< Max. write data block length */
    	u8  WriteBlockPaPartial;  /*!< Partial blocks for write allowed */
    	u8  Reserved3;            /*!< Reserded */
    	u8  ContentProtectAppli;  /*!< Content protection application */
    	u8  FileFormatGrouop;     /*!< File format group */
    	u8  CopyFlag;             /*!< Copy flag (OTP) */
    	u8  PermWrProtect;        /*!< Permanent write protection */
    	u8  TempWrProtect;        /*!< Temporary write protection */
    	u8  FileFormat;           /*!< File Format */
    	u8  ECC;                  /*!< ECC code */
    	u8  CSD_CRC;              /*!< CSD CRC */
    	u8  Reserved4;            /*!< always 1*/
    } SD_CSD;   
    
    //SD卡CID寄存器数据
    typedef struct
    {
    	u8  ManufacturerID;       /*!< ManufacturerID */
    	u16 OEM_AppliID;          /*!< OEM/Application ID */
    	u32 ProdName1;            /*!< Product Name part1 */
    	u8  ProdName2;            /*!< Product Name part2*/
    	u8  ProdRev;              /*!< Product Revision */
    	u32 ProdSN;               /*!< Product Serial Number */
    	u8  Reserved1;            /*!< Reserved1 */
    	u16 ManufactDate;         /*!< Manufacturing Date */
    	u8  CID_CRC;              /*!< CID CRC */
    	u8  Reserved2;            /*!< always 1 */
    } SD_CID;	 
    
    //SD卡状态
    typedef enum
    {
    	SD_CARD_READY                  = ((uint32_t)0x00000001),
    	SD_CARD_IDENTIFICATION         = ((uint32_t)0x00000002),
    	SD_CARD_STANDBY                = ((uint32_t)0x00000003),
    	SD_CARD_TRANSFER               = ((uint32_t)0x00000004),
    	SD_CARD_SENDING                = ((uint32_t)0x00000005),
    	SD_CARD_RECEIVING              = ((uint32_t)0x00000006),
    	SD_CARD_PROGRAMMING            = ((uint32_t)0x00000007),
    	SD_CARD_DISCONNECTED           = ((uint32_t)0x00000008),
    	SD_CARD_ERROR                  = ((uint32_t)0x000000FF)
    }SDCardState;
    
    //SD卡信息,包括CSD,CID等数据
    typedef struct
    {
      SD_CSD SD_csd;
      SD_CID SD_cid;
      long long CardCapacity;  	//SD卡容量,单位:字节,最大支持2^64字节大小的卡.
      u32 CardBlockSize; 		//SD卡块大小	
      u16 RCA;					//卡相对地址
      u8 CardType;				//卡类型
    } SD_CardInfo;
    extern SD_CardInfo SDCardInfo;//SD卡信息			 
    
    //SDIO 指令集
    #define SD_CMD_GO_IDLE_STATE                       ((u8)0)
    #define SD_CMD_SEND_OP_COND                        ((u8)1)
    #define SD_CMD_ALL_SEND_CID                        ((u8)2)
    #define SD_CMD_SET_REL_ADDR                        ((u8)3) /*!< SDIO_SEND_REL_ADDR for SD Card */
    #define SD_CMD_SET_DSR                             ((u8)4)
    #define SD_CMD_SDIO_SEN_OP_COND                    ((u8)5)
    #define SD_CMD_HS_SWITCH                           ((u8)6)
    #define SD_CMD_SEL_DESEL_CARD                      ((u8)7)
    #define SD_CMD_HS_SEND_EXT_CSD                     ((u8)8)
    #define SD_CMD_SEND_CSD                            ((u8)9)
    #define SD_CMD_SEND_CID                            ((u8)10)
    #define SD_CMD_READ_DAT_UNTIL_STOP                 ((u8)11) /*!< SD Card doesn't support it */
    #define SD_CMD_STOP_TRANSMISSION                   ((u8)12)
    #define SD_CMD_SEND_STATUS                         ((u8)13)
    #define SD_CMD_HS_BUSTEST_READ                     ((u8)14)
    #define SD_CMD_GO_INACTIVE_STATE                   ((u8)15)
    #define SD_CMD_SET_BLOCKLEN                        ((u8)16)
    #define SD_CMD_READ_SINGLE_BLOCK                   ((u8)17)
    #define SD_CMD_READ_MULT_BLOCK                     ((u8)18)
    #define SD_CMD_HS_BUSTEST_WRITE                    ((u8)19)
    #define SD_CMD_WRITE_DAT_UNTIL_STOP                ((u8)20) 
    #define SD_CMD_SET_BLOCK_COUNT                     ((u8)23) 
    #define SD_CMD_WRITE_SINGLE_BLOCK                  ((u8)24)
    #define SD_CMD_WRITE_MULT_BLOCK                    ((u8)25)
    #define SD_CMD_PROG_CID                            ((u8)26)
    #define SD_CMD_PROG_CSD                            ((u8)27)
    #define SD_CMD_SET_WRITE_PROT                      ((u8)28)
    #define SD_CMD_CLR_WRITE_PROT                      ((u8)29)
    #define SD_CMD_SEND_WRITE_PROT                     ((u8)30)
    #define SD_CMD_SD_ERASE_GRP_START                  ((u8)32) /*!< To set the address of the first write
                                                                      block to be erased. (For SD card only) */
    #define SD_CMD_SD_ERASE_GRP_END                    ((u8)33) /*!< To set the address of the last write block of the
                                                                      continuous range to be erased. (For SD card only) */
    #define SD_CMD_ERASE_GRP_START                     ((u8)35) /*!< To set the address of the first write block to be erased.
                                                                      (For MMC card only spec 3.31) */
    
    #define SD_CMD_ERASE_GRP_END                       ((u8)36) /*!< To set the address of the last write block of the
                                                                      continuous range to be erased. (For MMC card only spec 3.31) */
    
    #define SD_CMD_ERASE                               ((u8)38)
    #define SD_CMD_FAST_IO                             ((u8)39) /*!< SD Card doesn't support it */
    #define SD_CMD_GO_IRQ_STATE                        ((u8)40) /*!< SD Card doesn't support it */
    #define SD_CMD_LOCK_UNLOCK                         ((u8)42)
    #define SD_CMD_APP_CMD                             ((u8)55)
    #define SD_CMD_GEN_CMD                             ((u8)56)
    #define SD_CMD_NO_CMD                              ((u8)64)
    
    /** 
      * @brief Following commands are SD Card Specific commands.
      *        SDIO_APP_CMD :CMD55 should be sent before sending these commands. 
      */
    #define SD_CMD_APP_SD_SET_BUSWIDTH                 ((u8)6)  /*!< For SD Card only */
    #define SD_CMD_SD_APP_STAUS                        ((u8)13) /*!< For SD Card only */
    #define SD_CMD_SD_APP_SEND_NUM_WRITE_BLOCKS        ((u8)22) /*!< For SD Card only */
    #define SD_CMD_SD_APP_OP_COND                      ((u8)41) /*!< For SD Card only */
    #define SD_CMD_SD_APP_SET_CLR_CARD_DETECT          ((u8)42) /*!< For SD Card only */
    #define SD_CMD_SD_APP_SEND_SCR                     ((u8)51) /*!< For SD Card only */
    #define SD_CMD_SDIO_RW_DIRECT                      ((u8)52) /*!< For SD I/O Card only */
    #define SD_CMD_SDIO_RW_EXTENDED                    ((u8)53) /*!< For SD I/O Card only */
    
    /** 
      * @brief Following commands are SD Card Specific security commands.
      *        SDIO_APP_CMD should be sent before sending these commands. 
      */
    #define SD_CMD_SD_APP_GET_MKB                      ((u8)43) /*!< For SD Card only */
    #define SD_CMD_SD_APP_GET_MID                      ((u8)44) /*!< For SD Card only */
    #define SD_CMD_SD_APP_SET_CER_RN1                  ((u8)45) /*!< For SD Card only */
    #define SD_CMD_SD_APP_GET_CER_RN2                  ((u8)46) /*!< For SD Card only */
    #define SD_CMD_SD_APP_SET_CER_RES2                 ((u8)47) /*!< For SD Card only */
    #define SD_CMD_SD_APP_GET_CER_RES1                 ((u8)48) /*!< For SD Card only */
    #define SD_CMD_SD_APP_SECURE_READ_MULTIPLE_BLOCK   ((u8)18) /*!< For SD Card only */
    #define SD_CMD_SD_APP_SECURE_WRITE_MULTIPLE_BLOCK  ((u8)25) /*!< For SD Card only */
    #define SD_CMD_SD_APP_SECURE_ERASE                 ((u8)38) /*!< For SD Card only */
    #define SD_CMD_SD_APP_CHANGE_SECURE_AREA           ((u8)49) /*!< For SD Card only */
    #define SD_CMD_SD_APP_SECURE_WRITE_MKB             ((u8)48) /*!< For SD Card only */
      			   
    //支持的SD卡定义
    #define SDIO_STD_CAPACITY_SD_CARD_V1_1             ((u32)0x00000000)
    #define SDIO_STD_CAPACITY_SD_CARD_V2_0             ((u32)0x00000001)
    #define SDIO_HIGH_CAPACITY_SD_CARD                 ((u32)0x00000002)
    #define SDIO_MULTIMEDIA_CARD                       ((u32)0x00000003)
    #define SDIO_SECURE_DIGITAL_IO_CARD                ((u32)0x00000004)
    #define SDIO_HIGH_SPEED_MULTIMEDIA_CARD            ((u32)0x00000005)
    #define SDIO_SECURE_DIGITAL_IO_COMBO_CARD          ((u32)0x00000006)
    #define SDIO_HIGH_CAPACITY_MMC_CARD                ((u32)0x00000007)
    
    //SDIO相关参数定义
    #define NULL 0
    #define SDIO_STATIC_FLAGS               ((u32)0x000005FF)
    #define SDIO_CMD0TIMEOUT                ((u32)0x00010000)	  
    #define SDIO_DATATIMEOUT                ((u32)0xFFFFFFFF)	  
    #define SDIO_FIFO_Address               ((u32)0x40018080)
    
    //Mask for errors Card Status R1 (OCR Register)  
    #define SD_OCR_ADDR_OUT_OF_RANGE        ((u32)0x80000000)
    #define SD_OCR_ADDR_MISALIGNED          ((u32)0x40000000)
    #define SD_OCR_BLOCK_LEN_ERR            ((u32)0x20000000)
    #define SD_OCR_ERASE_SEQ_ERR            ((u32)0x10000000)
    #define SD_OCR_BAD_ERASE_PARAM          ((u32)0x08000000)
    #define SD_OCR_WRITE_PROT_VIOLATION     ((u32)0x04000000)
    #define SD_OCR_LOCK_UNLOCK_FAILED       ((u32)0x01000000)
    #define SD_OCR_COM_CRC_FAILED           ((u32)0x00800000)
    #define SD_OCR_ILLEGAL_CMD              ((u32)0x00400000)
    #define SD_OCR_CARD_ECC_FAILED          ((u32)0x00200000)
    #define SD_OCR_CC_ERROR                 ((u32)0x00100000)
    #define SD_OCR_GENERAL_UNKNOWN_ERROR    ((u32)0x00080000)
    #define SD_OCR_STREAM_READ_UNDERRUN     ((u32)0x00040000)
    #define SD_OCR_STREAM_WRITE_OVERRUN     ((u32)0x00020000)
    #define SD_OCR_CID_CSD_OVERWRIETE       ((u32)0x00010000)
    #define SD_OCR_WP_ERASE_SKIP            ((u32)0x00008000)
    #define SD_OCR_CARD_ECC_DISABLED        ((u32)0x00004000)
    #define SD_OCR_ERASE_RESET              ((u32)0x00002000)
    #define SD_OCR_AKE_SEQ_ERROR            ((u32)0x00000008)
    #define SD_OCR_ERRORBITS                ((u32)0xFDFFE008)
    
    //Masks for R6 Response 
    #define SD_R6_GENERAL_UNKNOWN_ERROR     ((u32)0x00002000)
    #define SD_R6_ILLEGAL_CMD               ((u32)0x00004000)
    #define SD_R6_COM_CRC_FAILED            ((u32)0x00008000)
    
    #define SD_VOLTAGE_WINDOW_SD            ((u32)0x80100000)
    #define SD_HIGH_CAPACITY                ((u32)0x40000000)
    #define SD_STD_CAPACITY                 ((u32)0x00000000)
    #define SD_CHECK_PATTERN                ((u32)0x000001AA)
    #define SD_VOLTAGE_WINDOW_MMC           ((u32)0x80FF8000)
    
    #define SD_MAX_VOLT_TRIAL               ((u32)0x0000FFFF)
    #define SD_ALLZERO                      ((u32)0x00000000)
    
    #define SD_WIDE_BUS_SUPPORT             ((u32)0x00040000)
    #define SD_SINGLE_BUS_SUPPORT           ((u32)0x00010000)
    #define SD_CARD_LOCKED                  ((u32)0x02000000)
    #define SD_CARD_PROGRAMMING             ((u32)0x00000007)
    #define SD_CARD_RECEIVING               ((u32)0x00000006)
    #define SD_DATATIMEOUT                  ((u32)0xFFFFFFFF)
    #define SD_0TO7BITS                     ((u32)0x000000FF)
    #define SD_8TO15BITS                    ((u32)0x0000FF00)
    #define SD_16TO23BITS                   ((u32)0x00FF0000)
    #define SD_24TO31BITS                   ((u32)0xFF000000)
    #define SD_MAX_DATA_LENGTH              ((u32)0x01FFFFFF)
    
    #define SD_HALFFIFO                     ((u32)0x00000008)
    #define SD_HALFFIFOBYTES                ((u32)0x00000020)
    
    //Command Class Supported  
    #define SD_CCCC_LOCK_UNLOCK             ((u32)0x00000080)
    #define SD_CCCC_WRITE_PROT              ((u32)0x00000040)
    #define SD_CCCC_ERASE                   ((u32)0x00000020)
    																	 
    //CMD8指令
    #define SDIO_SEND_IF_COND               ((u32)0x00000008)
    
    //相关函数定义
    SDIO_SD_ERROR_INFO SDIO_SdCardInit(void);
    void SDIO_ClockSet(u8 clkdiv);
    void SDIO_SendCmd(u8 cmdindex,u8 waitrsp,u32 arg);
    void SDIO_SendDataConfig(u32 datatimeout,u32 datalen,u8 blksize,u8 dir);
    SDIO_SD_ERROR_INFO SDIO_SdPowerON(void);    
    SDIO_SD_ERROR_INFO SD_PowerOFF(void);
    SDIO_SD_ERROR_INFO SDIO_SdCardInitializeCards(void);
    SDIO_SD_ERROR_INFO SDIO_SdCardGetInfo(SD_CardInfo *cardinfo);		  
    SDIO_SD_ERROR_INFO SDIO_SdCardEnableWideBusOperation(u32 wmode);
    SDIO_SD_ERROR_INFO SDIO_SdCardSetDeviceMode(u32 mode);
    SDIO_SD_ERROR_INFO SDIO_SdCardSelectAddr(u32 addr); 
    SDIO_SD_ERROR_INFO SDIO_SdCardSendStatus(uint32_t *pcardstatus);
    SDCardState SDIO_SdCardGetState(void);
    SDIO_SD_ERROR_INFO SDIO_SdCardReadBlock(u8 *buf,long long addr,u16 blksize);  
    SDIO_SD_ERROR_INFO SDIO_SdCardReadMultiBlocks(u8 *buf,long long  addr,u16 blksize,u32 nblks);  
    SDIO_SD_ERROR_INFO SDIO_SdCardWriteBlock(u8 *buf,long long addr,  u16 blksize);	
    SDIO_SD_ERROR_INFO SDIO_SdCardWriteMultiBlocks(u8 *buf,long long addr,u16 blksize,u32 nblks);
    SDIO_SD_ERROR_INFO SDIO_SdCardProcessIRQSrc(void);
    SDIO_SD_ERROR_INFO SDIO_CmdErrorCheck(void);  
    SDIO_SD_ERROR_INFO SDIO_CmdResp7Error(void);
    SDIO_SD_ERROR_INFO SDIO_CmdResp1Error(u8 cmd);
    SDIO_SD_ERROR_INFO SDIO_CmdResp3Error(void);
    SDIO_SD_ERROR_INFO SDIO_CmdResp2Error(void);
    SDIO_SD_ERROR_INFO SDIO_CmdResp6Error(u8 cmd,u16*prca);  
    SDIO_SD_ERROR_INFO SDIO_SdCardEnWideBus(u8 enx);	  
    SDIO_SD_ERROR_INFO SDIO_SdCardProgrammingState(u8 *pstatus); 
    SDIO_SD_ERROR_INFO SDIO_SdCardFindSCR(u16 rca,u32 *pscr);
    u8 convert_from_bytes_to_power_of_two(u16 NumberOfBytes); 
    void SDIO_SdCard_DMAConfig(u32*mbuf,u32 bufsize,u8 dir); 
    u8 SDIO_SdCardReadDiskSector(u8*buf,u32 sector,u8 cnt); 	//读SD卡,fatfs/usb调用
    u8 SDIO_SdCardWriteDiskSector(u8*buf,u32 sector,u8 cnt);	//写SD卡,fatfs/usb调用
    #endif
    
    • 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

    四、移植FATFS文件系统

    前面第3章,完成了SD NAND的驱动代码编写,这一章节实现FATFS文件的移植。

    4.1 FATFS文件系统介绍

    (1)介绍

    FatFs 是一种完全免费开源的 FAT 文件系统模块,专门为小型的嵌入式系统而设计。它完全用标准C 语言编写,所以具有良好的硬件平台独立性,可以移植到 8051、 PIC、 AVR、 SH、 Z80、 H8、 ARM 等系列单片机上而只需做简单的修改。它支持 FATl2、 FATl6 和 FAT32,支持多个存储媒介;有独立的缓冲区,可以对多个文件进行读/写,并特别对 8 位单片机和 16 位单片机做了优化。

    (2)特点

    【1】Windows兼容的FAT文件系统
    【2】不依赖于平台,易于移植
    【3】代码和工作区占用空间非常小
    【4】多种配置选项
    【5】多卷(物理驱动器和分区)
    【6】多ANSI/OEM代码页,包括DBCS
    【7】在ANSI/OEM或Unicode中长文件名的支持
    【8】RTOS的支持
    【9】多扇区大小的支持
    【10】只读,最少API,I/O缓冲区等等

    (3)移植性

    fatfs模块是ANSI C(C89)编写的。 没有平台的依赖, 编译器只要符合ANSI C标准就可以编译。

    fatf模块假设大小的字符/短/长8/16/32位和int是16或32位。 这些数据类型在integer.h文件中定义。这些数据类型在大多数的编译器中定义都符合要求。 如果现有的定义与编译器有任何冲突发生时,需要自己解决。

    4.2 下载源码

    下载地址:http://elm-chan.org/fsw/ff/00index_e.html

    image-20221201191123204

    FATFS有两个版本,一个大版本,一个小版本。小版本主要用于8位机(内存小)使用。

    下载图:

    image-20221201191144911

    4.3 源码结构介绍

    将下载的源码解压后可以得到两个文件夹: doc 和 src。 doc 里面主要是对 FATFS 的介绍(离线文档—英文和日文),而 src 里面才是我们需要的源码。

    其中,与平台无关的是:

    ffconf.h     FATFS配置文件
    ff.h        应用层头文件
    ff.c        应用层源文件
    diskio.h    硬件层头文件
    interger.h  数据类型定义头文件
    option      可选的外部功能(比如支持中文等)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    与平台相关的代码:

    diskio.c     底层接口文件(需要用户提供)
    
    • 1

    FATFS 模块在移植的时候,我们一般只需要修改 2 个文件,即 ffconf.h 和 diskio.c。

    FATFS模块的所有配置项都是存放在 ffconf.h 里面,我们可以通过配置里面的一些选项,来满足自己的需求。

    image-20221201191424011

    最顶层是应用层,使用者无需理会 FATFS 的内部结构和复杂的 FAT 协议,只需要调用FATFS 模块提供给用户的一系列应用接口函数,如 f_open, f_read, f_write 和 f_close 等,就可以像在 PC 上读写文件那样简单。

    中间层 FATFS 模块, 实现了 FAT 文件读/写协议。 FATFS 模块提供的是 ff.c 和 ff.h。除非有必要,使用者一般不用修改,使用时将头文件直接包含进去即可。

    需要我们编写移植代码的是 FATFS 模块提供的底层接口,它包括存储媒介读/写接口 ( disk、I/O) 和供给文件创建修改时间的实时时钟。

    4.4 下载源码并加入到工程

    先准备好一个有SD NAND驱动代码的STM32工程(代码前面第3章已经贴了),接着就完成下面的步骤。

    image-20221201191530152

    打开KEIL工程,添加FATFS文件源码:

    image-20221201191606087

    image-20221201191619365

    加入.h文件主要是方便配。cc936.c 用于支持中文。

    4.5 修改代码进行移植

    (1)修改diskio.c文件

    image-20221201191824668

    注释掉现在不需要的用到的文件,因为我们现在用的是SD卡,与USB,ATA,MMC卡没关系。

    并加入一个新的宏 :

    #define SD 0

    定义SD卡的物理驱动器号为0。

    修改 disk_status函数,该函数主要是用来获取磁盘状态。现在未用到,可以直接函数体内代码删除。

    修改截图:

    image-20221201191925588

    代码示例:

    #include "diskio.h"		  /* fatf底层API */
    #include "sd.h"		      /* SD卡驱动头文件  */
    /* 定义每个驱动器的物理驱动器号*/
    #define SD    0
    
    /*-----------------------------------------------------------------------*/
    /* 获取设备(磁盘)状态                                                     */
    /*-----------------------------------------------------------------------*/
    
    DSTATUS disk_status (
    	BYTE pdrv		/* 物理驱动识别 */
    )
    {
       return 0;  //该函数现在无需用到,直接返回0
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    修改disk_initialize函数,添加SD卡的初始化,其他不用到的代码直接删掉,该函数成功返回0,失败返回1。

    修改截图:

    image-20221201192033155

    代码示例:

    /*-----------------------------------------------------------------------*/
    /* 初始化磁盘驱动                                                        */
    /*-----------------------------------------------------------------------*/
    
    DSTATUS disk_initialize (
    	BYTE pdrv				/* 物理驱动识别 */
    )
    {
    	DSTATUS stat;
    	int result;
    
    	switch (pdrv) {
    	case SD :            //选择SD卡
    		stat=SD_Init();   //初始化SD卡-用户自己提供
    	}
    	if(stat)return STA_NOINIT;  //磁盘未初始化
    	return 0; //初始化成功
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    修改disk_read函数,加入SD卡读任意扇区的函数(需要用户自己提供),其他不用到的选项可以删掉。

    image-20221201192136957

    修改代码如下:

    /*-----------------------------------------------------------------------*/
    /* 读扇区                                                                */
    /*-----------------------------------------------------------------------*/
    DRESULT disk_read (
    	BYTE pdrv,		/* 物理驱动编号 - 范围0-9*/
    	BYTE *buff,		/* 数据缓冲区存储读取数据 */
    	DWORD sector,  	/* 扇区地址*/
    	UINT count		/* 需要读取的扇区数*/
    )
    {
    	DRESULT res;
    	int result;
    	switch (pdrv) {
    		case SD:
    		  res=SD_Read_Data((u8*)buff,sector,count);  //读SD扇区函数--用户提供
    		  return res; //在此处可以判错误
    	}
    	return RES_PARERR;  //无效参数
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    修改disk_write 函数,添加写扇区函数:

    image-20221201192230352

    代码:

    /*-----------------------------------------------------------------------*/
    /* 写扇区                                                                */
    /*-----------------------------------------------------------------------*/
    
    #if _USE_WRITE
    DRESULT disk_write (
    	BYTE pdrv,			  /* 物理驱动号*/
    	const BYTE *buff,	       /* 要写入数据的首地址 */
    	DWORD sector,		   /* 扇区地址 */
    	UINT count			   /* 扇区数量*/
    )
    {
    	DRESULT res;
    	int result;
    
    	switch (pdrv) {
    		case SD:
    			res=SD_Write_Data((u8*)buff,sector,count); //写入扇区
    		  return res;
    	}
    	return RES_PARERR;  //无效参数
    }
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    修改disk_ioctl 函数,填充ioctl命令功能。这些功能是标准的命令,在diskio.h有定义。

    image-20221201192311549

    代码如下:

    /*-----------------------------------------------------------------------*/
    /* 其他函数                                              */
    /*-----------------------------------------------------------------------*/
    
    #if _USE_IOCTL
    DRESULT disk_ioctl (
    	BYTE pdrv,		/* 物理驱动号 */
    	BYTE cmd,		  /* 控制码  */
    	void *buff		/* 发送/接收数据缓冲区地址 */
    )
    {
    	DRESULT res;
    	int result;
    
    	switch (pdrv) {
    		case SD:
    			 switch(cmd)
    			 {
    				 case CTRL_SYNC:      //等待写过程
    					 SD_CS(0);          //选中SD卡
    					 if(SD_Wait_Ready())result = RES_ERROR;/*等待卡准备好*/
    				     else res = RES_OK;     //成功
    					 SD_CS(1);            //释放SD卡
                            break;	 
    				 
    			 case GET_SECTOR_SIZE://获取扇区大小
    			   *(DWORD*)buff = 512; 
    		        res = RES_OK;     //成功
    		        break;	
    				 
    			 case GET_BLOCK_SIZE:    //获取块大小
    				*(WORD*)buff = 8;      //块大小(扇区为单位),一块等于8个扇区
    		         res = RES_OK;
    		         break;
    				 
    			 case GET_SECTOR_COUNT: //获取总扇区数量
    		        *(DWORD*)buff = SD_Get_Sector_Count();
    		        res = RES_OK;
    		        break;
    				 
    			default:  //命令错误
    		        res = RES_PARERR;
    		        break;
    			 }
    		return res;
    	}
    	return RES_PARERR;  //返回状态
    }
    
    
    • 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
    (2)修改ffconf.h文件

    需要注意的一些宏配置:

    #define _CODE_PAGE	936   //采用中文GBK编码       (64行)
    #define	_USE_LFN	3     //动态的堆上工作             (93行)
    #define	_MAX_LFN	255   /*_USE_LFN选项开关LFN(长文件名)特性。
    #define _VOLUMES	1     /* 支持的磁盘数量(逻辑驱动器)。 */   (142)
    #define	_MIN_SS		512                                  (165)
    #define	_MAX_SS		512   /*这些选项配置支持扇区大小的范围。(512,1024, 4096*/ 
    #define _FS_NORTC	    0    /*启用RTC时间功能*/   (202)
    #define _NORTC_MON	    1
    #define _NORTC_MDAY	1
    #define _NORTC_YEAR	2015 //年  
    /*需要实现:get_fattime()函数*/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    ffconf.h 文件源码:

    /*---------------------------------------------------------------------------/
    /  FatFs - FAT文件系统模块配置文件  R0.11a (C)ChaN, 2015
    /---------------------------------------------------------------------------*/
    
    #define _FFCONF 64180	/* 版本识别*/
    
    /*---------------------------------------------------------------------------/
    / 功能配置
    /---------------------------------------------------------------------------*/
    
    #define _FS_READONLY	0
    /* 这个选项开关只读配置。(0:读/写或1:只读)   
    /只读配置删除编写API函数,f_write(),f_sync(),   
    / f_unlink(),f_mkdir(),f_chmod(),f_rename(),f_truncate(),f_getfree()   
    /写和可选的功能. */
    
    
    #define _FS_MINIMIZE	0
    /*此选项定义删除一些基本的API函数极小化水平。  
    /   
    / 0:所有基本功能都是激活的。  
    / 1:f_stat(),f_getfree(),f_unlink(),f_mkdir(),f_chmod(),f_utime(),   
    / f_truncate()和f_rename()函数删除。  
    / 2:f_opendir(),f_readdir()和f_closedir()中除了1。  
    / 3:f_lseek()函数删除除了2。*/
    
    
    #define	_USE_STRFUNC	1
    /*这个选项开关字符串函数,f_gets(),f_putc(),f_puts()和 
    / f_printf()。  
    /   
    / 0:禁用字符串函数。  
    / 1:启用没有LF-CRLF转换。  
    / 2:启用LF-CRLF(回车换行)转换。*/
    
    
    #define _USE_FIND		0
    /*这个选项开关过滤目录读取特性和相关功能,   
    / f_findfirst()和f_findnext()。(0:禁用或1:启用)*/
    
    
    #define	_USE_MKFS		1
    /* 这个选项开关f_mkfs()函数。(0:禁用或1:启用) */
    
    
    #define	_USE_FASTSEEK	1
    /* 这个选项开关快速寻求功能。(0:禁用或1:启用) */
    
    
    #define _USE_LABEL		1
    /*   磁盘卷标这个选项开关功能,f_getlabel()和f_setlabel()。  
    /(0:禁用或1:启用) */
    
    
    #define	_USE_FORWARD	0
    /*  这个选项开关f_forward()函数。(0:禁用或1:启用)   
    /启用它,也_FS_TINY需要设置为1. */
    
    
    /*---------------------------------------------------------------------------/
    / 语言环境和名称空间配置
    /---------------------------------------------------------------------------*/
    
    #define _CODE_PAGE	936  //采用中文GBK编码
    /* 这个选项指定OEM代码页在目标系统上使用。  
    /不正确的代码页的设置会导致文件打开失败.
    /
    /   1   - ASCII (没有扩展字符。Non-LFN cfg。只有)
    /   437 - U.S.
    /   720 - 阿拉伯语
    /   737 - 希腊语;
    /   771 - 阿富汗
    /   775 - 波罗的海
    /   850 - 拉丁1
    /   852 - 拉丁2
    /   855 - 西里尔字母
    /   857 - 土耳其语
    /   860 - 葡萄牙语
    /   861 - 冰岛语
    /   862 - 希伯来人
    /   863 - 加拿大法语
    /   864 - 阿拉伯语
    /   865 - 日耳曼民族的
    /   866 - 俄语
    /   869 - 希腊 2
    /   932 - 日本人 (DBCS)
    /   936 - 简体中文(DBCS)
    /   949 - 韩国人 (DBCS)
    /   950 - 繁体中文(DBCS)
    */
    
    
    #define	_USE_LFN	3 //动态的堆上工作
    #define	_MAX_LFN	255
    /*_USE_LFN选项开关LFN(长文件名)特性。
    /
    / 0:禁用LFN特性。_MAX_LFN没有影响。  
    / 1:启用LFN BSS静态工作缓冲区。总是不是线程安全的。  
    / 2:启用LFN与动态缓冲栈上的工作。  
    / 3:使LFN与动态缓冲区在堆上工作。
    /
    /  当启用LFN(长文件名)特性,Unicode(选项/ unicode.c)必须处理功能  
    /被添加到项目中。LFN工作缓冲区占用(_MAX_LFN + 1)* 2字节。  
    /当使用堆栈缓冲区,照顾堆栈溢出。当使用堆  
    /工作缓冲区内存,内存管理功能,ff_memalloc()和  
    / ff_memfree(),必须添加到项目中。 */
    
    
    #define	_LFN_UNICODE	0 
    /* 这个选项开关字符编码的API。(0:ANSI / OEM或1:Unicode)   
    路径名/使用Unicode字符串,并设置_LFN_UNICODE启用LFN特性  
    /1。这个选项也会影响行为的字符串的I / O功能。
    */
    
    
    #define _STRF_ENCODE	3
    /* 当_LFN(长文件名)_UNICODE是1,这个选项选择文件的字符编码  
    /通过字符串读取/写入I /O功能,f_gets(),f_putc(),f_puts和f_printf().
    /
    /  0: ANSI/OEM
    /  1: UTF-16LE
    /  2: UTF-16BE
    /  3: UTF-8
    /
    / 当_LFN_UNICODE = 0时,该选项没有影响。*/
    
    #define _FS_RPATH	0
    /* 这个选项配置相对路径的功能。  /   
    / 0:禁用相对路径特性和删除相关功能。  
    / 1:启用相对路径特性。f_chdir()和f_chdrive()是可用的。  
    / 2:f_getcwd()函数可用除了1。  /   
    /注意,目录项读通过f_readdir()这个选项。 
    */
    
    /*---------------------------------------------------------------------------/
    / 驱动/卷配置
    /---------------------------------------------------------------------------*/
    
    
    #define _VOLUMES	1
    /* 支持的磁盘数量(逻辑驱动器)。 */
    
    
    #define _STR_VOLUME_ID	0
    #define _VOLUME_STRS	"RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3"
    /* STR_VOLUME_ID选项开关卷ID字符串功能。  
    /当_STR_VOLUME_ID设置为1时,也可以使用预先定义的字符串在路径名称/数量。
    为每个_VOLUME_STRS定义驱动ID字符串  
    /逻辑驱动器。条目的数量必须等于_VOLUMES。有效字符  
    /驱动ID字符串:a - z和0 - 9。*/
    
    
    #define	_MULTI_PARTITION	0
    /*  这个选项开关多分区的特性。在默认情况下(0),每个逻辑驱动器  
    /号绑定到相同的物理驱动器号  
    /物理驱动器将被安装。当启用分区特性(1),   
    /每个逻辑驱动器号是绑定到任意物理驱动器和分区  
    /中列出VolToPart[]。还f_fdisk()函数可用. */
    
    
    #define	_MIN_SS		512
    #define	_MAX_SS		512
    /*  这些选项配置支持扇区大小的范围。(512,1024,   
    / 2048或4096)总是为大多数系统设置两个512,卡和所有类型的内存  
    /硬盘。但是可能需要更大的值为车载闪存和一些  
    /类型的光学媒体。当_MAX_SS大于_MIN_SS,fatf配置  
    /变量扇区大小和GET_SECTOR_SIZE命令必须执行  disk_ioctl()函数. */
    
    
    #define	_USE_TRIM	0
    /* 这个选项开关ATA-TRIM特性。(0:禁用或1:启用)   
    /启用削减特性,也应该实现CTRL_TRIM命令  
    / disk_ioctl()函数。*/
    
    
    #define _FS_NOFSINFO	0
    /*   
    如果你需要知道正确的自由空间体积FAT32,设置一些0   
    /选项,f_getfree()函数在第一次后体积将迫使山  
    /全脂肪扫描。位1控制使用的集群数量分配。  /   
    / bit0 = 0:使用免费的集群计算FSINFO如果可用。  
    / bit0 = 1:不相信自由FSINFO集群计算。  
    / bit1 = 0:最后使用集群可用FSINFO如果数量分配。  
    / bit1 = 1:不相信最后分配FSINFO集群数量.
    */
    
    
    
    /*---------------------------------------------------------------------------/
    / 系统配置列表
    /---------------------------------------------------------------------------*/
    
    #define	_FS_TINY	0
    /* 这个选项开关小缓冲区配置。(0:正常或1:小)   
    /小配置,文件对象的大小(FIL)_MAX_SS减少字节。而不是私人部门从文件对象,缓冲了  
    /公共部门缓冲文件系统中的对象(fatf)是用于该文件  
    /数据传输. */
    
    
    #define _FS_NORTC	0
    #define _NORTC_MON	1
    #define _NORTC_MDAY	1
    #define _NORTC_YEAR	2015 //年
    /* _FS_NORTC选项开关时间戳的特性。如果系统没有/
     RTC函数或不需要有效的时间戳,_FS_NORTC 1设置为禁用/
     时间戳的特性。所有对象修改fatf将有一个固定的时间戳。/
      固定的时间定义为_NORTC_MON _NORTC_MDAY _NORTC_YEAR。  
    
    /当启用时间戳特性(_FS_NORTC = = 0),需要实现get_fattime()函数。  /
     添加到项目RTC读当前时间形式。_NORTC_MON,   /
    _NORTC_MDAY和_NORTC_YEAR没有效果。  
    /这些选项没有影响只读配置(_FS_READONLY = = 1)。 */
    
    
    #define	_FS_LOCK	0
    /*  _FS_LOCK选项开关控制复制的文件打开的文件锁定功能  
    /和非法操作打开对象。这个选项_FS_READONLY时必须是0   
    /是1。  /   
    / 0:禁用文件锁定功能。为了避免体积腐败、应用程序  
    /应该避免非法打开,删除和重命名的开放对象。  
    / > 0:启用文件锁定功能。值定义了多少文件/子目录  
    可以同时打开的/文件锁的控制之下。注意,这个文件独立于re-entrancy /锁功能。 */
    
    
    
    #define _FS_REENTRANT	0
    #define _FS_TIMEOUT		1000
    #define	_SYNC_t			HANDLE
    /*  _FS_REENTRANT选项开关re-entrancy fatf的(线程安全)   
    /模块本身。注意,不管这个选项,文件访问不同  
    /体积始终是凹角和音量控制功能,f_mount(),f_mkfs()   
    /和f_fdisk()函数,总是不凹角。只有文件/目录的访问  
    /相同的体积是这个功能的控制。  
    /   
    / 0:禁用re-entrancy。_FS_TIMEOUT和_SYNC_t没有效果。  
    / 1:启用re-entrancy。还提供用户同步处理程序,   
    / ff_req_grant(),ff_rel_grant(),ff_del_syncobj()和ff_cre_syncobj()   
    /函数,必须添加到项目中。样品中可用  
    /选项
    / syscall.c。
    /
    /  _FS_TIMEOUT定义超时时间单位的滴答声。  
    / _SYNC_t定义了O 
    / S依赖同步对象类型。例如处理、ID、OS_EVENT *   
    / SemaphoreHandle_t等. .O / S的头文件定义需要  
    /包括在ff.c的范围。 */
    
    
    #define _WORD_ACCESS	0
    /* _WORD_ACCESS选项是一个只有依赖于平台的选择。
    它定义了这个词/访问方法是用来体积上的数据。
    /
    / 0:逐字节的访问。总是兼容所有平台。  
    / 1:词的访问。不要选择这个,除非在下列条件。  
    /   
    / *地址对齐内存访问总是允许所有指令。  
    / *字节顺序的记忆是低位优先。  
    /   
    /如果是这样的情况,_WORD_ACCESS也可以减少代码的大小设置为1。  
    /下表显示允许设置某种类型的处理器。
    /
    /  ARM7TDMI   0   *2          ColdFire   0    *1         V850E      0    *2
    /  Cortex-M3  0   *3          Z80        0/1             V850ES     0/1
    /  Cortex-M0  0   *2          x86        0/1             TLCS-870   0/1
    /  AVR        0/1             RX600(LE)  0/1             TLCS-900   0/1
    /  AVR32      0   *1          RL78       0    *2         R32C       0    *2
    /  PIC18      0/1             SH-2       0    *1         M16C       0/1
    /  PIC24      0   *2          H8S        0    *1         MSP430     0    *2
    /  PIC32      0   *1          H8/300H    0    *1         8051       0/1
    /
    /   
    * 1:高位优先。  / 
    * 2:不支持不连续的内存访问。  / 
    * 3:一些编译器生成LDM(逻辑磁盘管理器 ) / STM mem_cpy(内存拷贝)函数。
    */
    
    • 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
    (3)实现动态内存分配函数与时间函数

    ff.h文件有动态内存的释放,动态内存申请,时间获取函数接口。

    image-20221201192603132

    在diskio.c文件实现函数功能:

    image-20221201192626227

    代码实现如下:

    //动态内存分配
    void* ff_memalloc (UINT msize)			    /* 分配内存块 */
    {
    	return (void*)malloc(msize); //分配空间
    }
    
    
    //动态内存释放
    void ff_memfree (void* mblock)			    /* 空闲内存块 */
    {
    	free(mblock);              //释放空间
    }
    
    
    //返回FATFS时间
    //获得时间  
    DWORD get_fattime (void)
    {	
    	//Get_RTC_Timer(); //获取一次RTC时间
    		return (RTC_Timer.year-1980)<<25|   //年
    			  RTC_Timer.month<<21|  //月
    		       RTC_Timer.day<<16|    //日
    		       RTC_Timer.hour<<11|   //时
    		       RTC_Timer.minute<<5|  //分
    		       RTC_Timer.sec;        //秒
    }
    
    
    
    /*
    Return Value
    Currnet local time is returned with packed into a DWORD value. The bit field is as follows:
    bit31:25
    Year origin from the 1980 (0..127)
    bit24:21
    Month (1..12)
    bit20:16
    Day of the month(1..31)
    bit15:11
    Hour (0..23)
    bit10:5
    Minute (0..59)
    bit4:0
    Second / 2 (0..29)
    */
    
    • 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
    (4)修改堆栈空间

    完成了上述的修改,还需要修改堆栈空间,因为长文件支持需要占用堆空间。

    修改STM32启动文件如下:

    image-20221201192757035

    (5)编译工程测试

    修改完毕之后,给开发板插上SD卡,调用API函数在SD卡创建一个文件,并写入数据,测试是否成功:

    #include "ff.h"
    FATFS fs;  // 用户定义的文件系统结构体
    FIL  file;  // 用户定义的文件系统结构体
    u8 buff[]="123 知识!!";
    int main(void)
    {
    	u32 data;                //检测SD卡容量
    	u8 i,res;
        LED_Init();              //LED灯初始化
        Delay_Init();
        KEY_Init();
        USART1_Init(72,115200);
        USART2_Init(36,115200);
         FLASH_Init();
    	  Set_Font_addr(); //字库地址初始化
    	  FSMC_SRAM_Init();
    	  LCD_Init();
    	  RTC_Init();     //RTC时钟初始化
    	  while(SD_Init())    //检测不到SD卡,SD相关硬件初始化
    		{
    			i=!i;
    			LCD_ShowString(60,150,200,16,16,"SD Card Error!  Please Check SD Card!!",0xf800);					
    			Delay_ms(500);
    			LED1(i)//DS0闪烁
    		}
    		
           f_mount(&fs,"0",1);  // 注册工作区,驱动器号 0,初始化后其他函数可使用里面的参数
    		printf("注册工作区!\n");
    		
    		if(f_mkfs("0",0,4096))  //格式化SD卡
    		{
    			printf("格式化失败!!\n");
    		}
    		else
    		{
    			printf("格式化成功!!\n");
    		}
    		res = f_open(&file, "/file.c", FA_OPEN_ALWAYS | FA_READ | FA_WRITE);
    		if(res==0)
    		{
    			printf("文件创建成功!!\n");
    		}
    		else
    		{
    			printf("文件创建失败!!\n");
    		}
    		res =f_write(&file,buff,strlen((const char*)buff),&data);
    		if(res==0)
    		{
    			printf("数据写入成功!!\n");
    		}
    		else
    		{
    			printf("数据写入失败!!\n");
    		}
    		printf("成功写入%d字节数据\n",data);
    		f_close(&file);  //关闭文件
    		//_FS_RPATH
    		
    		while(1)
    		{
    			Delay_ms(1000);
    			LED1(1);
    			Delay_ms(500);
    			LED1(0);
    		}
    }
    
    • 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

    五、案例使用

    5.1 读取GBK字库文件(LCD汉字显示)

    产品开发中,如果设备带有LCD显示屏,一般会显示各种文字提示,或者机器操作说明,显示中文需要字库,为了方便字模的提取,可以将字库文件制作好之后放到SD NAND上,通过文件系统打开字库文件,读取字模进行显示。

    下面贴出文件系统读取字模的核心代码:

    /*
    函数功能: 显示GBK字库数据
              u32 x  范围0~319
              u32 y  范围0~479
              u32 size  数据的宽度(必须是8的倍数)  是正方形
              u8 *p  中文
    说明: 取模横向坐标必须保证是8的倍数
    */
    
    void ILI9341_DisplayGBKData(u32 x,u32 y,u32 size,u8 *p)
    {
    		FIL fp;
    		UINT br;
    		u8 L,H;
    	  u32 Addr;
    	  u16 font_size=size/8*size; //字体占用的点阵码字节大小
    	  u8 *buff=NULL;
    		H=*p;
    		L=*(p+1);
    		if(L<0x7f)L=L-0x40;
    		else L=L-0x41;
    		H=H-0x81;
    		Addr=(190*H+L)*font_size; //中文在字库里的偏移量
    		buff=malloc(font_size);   //使用的堆空间
    		if(buff==NULL)return;
    
    		switch(size)
    		{
    			case 16:
    				if(f_open(&fp,"0:/font/gbk16.DZK",FA_READ)!=FR_OK)
                    {
                          printf("f_open error.\r\n");
                    }
    				f_lseek(&fp,Addr);
    				f_read(&fp,buff,font_size,&br);
    				f_close(&fp);
                   
    				break;
    			case 24:
                    f_open(&fp,"0:/font/gbk24.DZK",FA_READ);
    				f_lseek(&fp,Addr);
    				f_read(&fp,buff,font_size,&br);
    				f_close(&fp);
    				break;
    			case 32:
    				
    				break;
    		}
    		//显示中文
    		ILI9341_DisplayData(x,y,size,size,buff);
    		
    		//释放空间
    		free(buff);
    }
    
    • 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

    这是读取字模,显示的效果:

    image-20221201194230582

    5.2 读取MP3文件播放(开机音乐)

    这个例子是演示文件系统的目录扫描函数使用方式,读取指定目录下的MP3文件进行播放。

    u8 PlayerMP3(const char *path);
    FATFS FatFs;
    int main()
    {
    	LED_Init();
    	BEEP_Init();
    	KeyInit();
      USARTx_Init(USART1,72,115200);
    
      
      SDCardDeviceInit(); //初始化SD卡
      
    //  res=f_mkfs("0:",FM_ANY,0,work,sizeof work);
    //  if(res)printf("格式化失败!\n");
    //  else printf("格式化成功!\n");
      f_mount(&FatFs, "0:", 0);   //注册工作区
      
      PlayerMP3("0:/MP3");
      
    	while(1)
    	{
        DelayMs(100);
        LED0=!LED0;
    	}
    }
    
    
    /*
    函数功能: 扫描目录mp3播放
    0表示成功 1表示失败
    */
    u8 PlayerMP3(const char *path)
    {
        DIR dir;
        FRESULT res; 
        FILINFO fno; //存放读取的文件信息
        char *abs_path=NULL;  
        
        /*1. 打开目录*/    
        res=f_opendir(&dir,path);
        if(res!=FR_OK)return res;
        
        /*2. 循环读取目录*/
         while(1)
         {
            res=f_readdir(&dir,&fno);
            if(fno.fname[0] == 0 || res!=0)break;
            printf("文件名称: %s,文件大小: %ld 字节\r\n",fno.fname,fno.fsize);
    
            /*过滤目录*/
            if(strstr(fno.fname,".mp3"))
            {
                //申请存放文件名称的长度
                abs_path=malloc(strlen(path)+strlen(fno.fname)+1);
                if(abs_path==NULL)break;
                 
                strcpy(abs_path,path);
                strcat(abs_path,"/");
                strcat(abs_path,fno.fname);
              
                printf("abs_path=%s\n",abs_path);
                VS1053_MP3(0,0,abs_path);     
                free(abs_path);
            }
        }
        
        /*3. 关闭目录*/
        f_closedir(&dir);
        return 0;
    }
    
    • 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
  • 相关阅读:
    嵌入式分享合集63
    SSM - Springboot - MyBatis-Plus 全栈体系(十四)
    2023第十四届蓝桥杯国赛 C/C++ 大学 B 组
    Python入门教程47:史上最齐全的第三方模块库
    C++ //练习 9.19 重写上题的程序,用list替代deque。列出程序要做出哪些改变。
    SpringMVC修炼之旅(1)什么是SpringMVC
    字节跳动端智能工程链路 Pitaya 的架构设计
    python项目使用pyinstaller打包
    EventBridge 生态实践:融合 SLS 构建一体化日志服务
    Nacos2.0.3集群搭建
  • 原文地址:https://blog.csdn.net/xiaolong1126626497/article/details/128212291