打印
[活动专区]

【AT-START-F425测评】SD卡读写

[复制链接]
807|4
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
zmlopq|  楼主 | 2022-3-6 00:05 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
file:///C:/Users/Administrator/Desktop/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20220305232548.jpg

内核:ARM®32位的Cortex®-M4 CPU
−最高96 MHz工作频率,带存储器保护单元(MPU),内建单周期乘法和硬件除法
−具有DSP指令集
n 存储器
−32 K到64 K字节的内部闪存程序/数据存储器
−4 K字节的启动程序存储器作启动加载程序(Bootloader)用,可一次性配置成一般用户程序和数据区
−sLib:将指定之主存储区设为执行代码安全库区,此区代码仅能调用无法读取
−20 K字节的SRAM  
SD卡在现在的日常生活与工作中使用非常广泛,时下已经成为最为通用的数据存储卡。使用AT32F425通过软件模拟SPI通信成功实现了对SD卡的扇区读写, 并对其读写速度进行了评估。下面先来讲解SD卡。
SD卡的引脚定义:
file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps_clip_image-21123.png
SD卡引脚功能详述:
SD引脚
SD模式
SPI模式
1
DAT3
CS
2
CMD
DI
3
VSS
VSS
4
VDD
VDD
5
CLK
SCLK
6
VSS
VSS
7
DAT0
DO
8
DAT1
Resvered
9
DAT2
Resvered
SD引脚
SD模式
SPI模式

注:S:电源供给 I:输入 O:采用推拉驱动的输出
PP:采用推拉驱动的输入输出
SD卡支持两种总线方式:SD方式与SPI方式。其中SD方式采用6线制,使用CLK、CMD、DAT0~DAT3进行数据通信。而SPI方式采用4线 制,使用CS、CLK、DataIn、DataOut进行数据通信。SD方式时的数据传输速度与SPI方式要快,采用单片机对SD卡进行读写时一般都采用 SPI模式。采用不同的初始化方式可以使SD卡工作于SD方式或SPI方式。这里只对其SPI方式进行介绍。

引脚定义
#define SD_IOSPI_SO _Clr()  GPIOA->clr = GPIO_PINS_6
#define SD_IOSPI_SO_Set()   GPIOA->scr = GPIO_PINS_6
#define SD_IOSPI_SCL _Clr()  GPIOA->clr = GPIO_PINS_5/
#define SD_IOSPI_SCL_Set()   GPIOA->scr = GPIO_PINS_5
#define SD_IOSPI_CS _Clr()  GPIOA->clr = GPIO_PINS_4
#define SD_IOSPI_CS _Set()   GPIOA->scr = GPIO_PINS_4
IO模拟SPI字节读写
 
 u8 SD1_IOSPI_RWByte(u8 Data)
 {
         u8 i=0;
         u8 Temp_DI;
         for(i=0;i<8;i++)
         {               
                 Temp_DI <<= 1;
                 if(SET == gpio_input_data_bit_read(GPIOA, GPIO_PINS_7))
                         Temp_DI |= 0X01;
                 else
                         Temp_DI &=~ 0X01;
                
                
                 if((Data & 0x80)!=0)           
                         SD_IOSPI_SO_Set();
                 else
                         SD_IOSPI_SO _Clr();
                 Data <<= 1;
                
                 SD_IOSPI_SCL_Set();       
                 if(Low_or_High1) delay(DELAY_TIME);
                 SD_IOSPI_SCL _Clr();
         }  
   return Temp_DI;
 }
 向SD卡写命令 SD卡的命令是6个字节,pcmd是指向命令字节序列的指针  命令写入不成功,将返回0xff
 
 u8 SD1_Write_Cmd(u8 *pcmd)
 {
  u8 r=0,time=0;
 
  SD_IOSPI_CS _Set();
  SD1_IOSPI_RWByte(0xFF); //发送8个时钟,提高兼容性,如果没有这里,有些SD卡可能不支持   
        
  SD_IOSPI_CS  _Clr();
  while(0XFF!=SD1_IOSPI_RWByte(0XFF)); //等待SD卡准备好 
  //将6字节的命令序列写入SD卡
  SD1_IOSPI_RWByte(pcmd[0]);
  SD1_IOSPI_RWByte(pcmd[1]);
  SD1_IOSPI_RWByte(pcmd[2]);
  SD1_IOSPI_RWByte(pcmd[3]);
  SD1_IOSPI_RWByte(pcmd[4]);
  SD1_IOSPI_RWByte(pcmd[5]);
        
  if(pcmd[0]==0X1C) SD1_IOSPI_RWByte(0XFF); //如果是停止命令,跳过多余的字节
 
  do
  {  
   r=SD1_IOSPI_RWByte(0XFF);
   time++;
  }while((r&0X80)&&(time<TRY_TIME)); //如果重试次数超过TRY_TIME则返回错误
 
  return r;
 }
SD卡初始化,针对于不同的SD卡,如MMC、SD或SDHC,初始化方法是不同的 。调用成功,返回0x00,否则返回错误码
u8 SD1_Init(void)
{
u8 time=0,r=0,i=0;
       
u8 rbuf[4]={0};
       
u8 pCMD0[6] ={0x40,0x00,0x00,0x00,0x00,0x95}; //将SD卡从默认上电后的SD模式切换到SPI模式
u8 pCMD1[6] ={0x41,0x00,0x00,0x00,0x00,0x01}; //CMD1命令进行初始化
u8 pCMD8[6] ={0x48,0x00,0x00,0x01,0xAA,0x87}; //用于鉴别SD卡的版本
u8 pCMD16[6]={0x50,0x00,0x00,0x02,0x00,0x01}; //设置扇区大小为512字节
u8 pCMD55[6]={0x77,0x00,0x00,0x00,0x00,0x01}SD卡初始化
u8 pACMD41H[6]={0x69,0x40,0x00,0x00,0x00,0x01}; //
u8 pACMD41S[6]={0x69,0x00,0x00,0x00,0x00,0x01}; //
u8 pCMD58[6]={0x7A,0x00,0x00,0x00,0x00,0x01}; //
       
SD_IOSPI_CS _Set() ;
       
for(i=0;i<0x0f;i++) //激活SD卡
{
  SD1_IOSPI_RWByte(0xff); //120个时钟
}
time=0;
do
{
  r=SD1_Write_Cmd(pCMD0);//写入CMD0
  time++;
  if(time>=TRY_TIME)
  {
   return(INIT_CMD0_ERROR);//CMD0写入失败
  }
}while(r!=0x01);
if(1==SD1_Write_Cmd(pCMD8))//返回值为1,则SD卡版本为2.0
{
        rbuf[0]=SD1_IOSPI_RWByte(0XFF);
    rbuf[1]=SD1_IOSPI_RWByte(0XFF); //读取4个字节R7
        rbuf[2]=SD1_IOSPI_RWByte(0XFF);
rbuf[3]=SD1_IOSPI_RWByte(0XFF);
         
        if(rbuf[2]==0X01 && rbuf[3]==0XAA)//
        {               
         time=0;
         do
         {
                SD1_Write_Cmd(pCMD55);//
                r=SD1_Write_Cmd(pACMD41H);//
                time++;
    if(time>=TRY_TIME)
    {
     return(INIT_SDV2_ACMD41_ERROR);//    }
   }while(r!=0);       
   if(0==SD1_Write_Cmd(pCMD58)) //鉴别SD2.0
   {
          rbuf[0]=SD1_IOSPI_RWByte(0XFF); rbuf[1]=SD1_IOSPI_RWByte(0XFF); //读取4个字节的OCR
          rbuf[2]=SD1_IOSPI_RWByte(0XFF); rbuf[3]=SD1_IOSPI_RWByte(0XFF);                                       
   }
  }
}
SD_IOSPI_CS = 1;
SD1_IOSPI_RWByte(0xFF); //按照SD卡的操作时序在这里补8个时钟
  
return 0;//返回0,复位操作成功
}
SD卡初始化成功后,读写扇区时,尽量将SPI速度提上来,提高效率将buffer指向的512个字节的数据写入到SD卡的addr扇区中调用成功,返回0x00,否则返回错误码
u8 SD1_Write_Sector(u32 addr,u8 *buffer)        //向SD卡中的指定地址的扇区写入512个字节
{  
u8 r,time;
u8 i=0,ii=0;
u8 pCMD24[]={0x58,0x00,0x00,0x00,0x00,0xff}; //向SD卡中单个块(512字节,一个扇区)写入数据
addr<<=9; //addr = addr * 512        将块地址(扇区地址)转为字节地址
pCMD24[1]=addr>>24; //将字节地址写入
pCMD24[2]=addr>>16;
pCMD24[3]=addr>>8;
pCMD24[4]=addr;       
time=0;
do
{  
  r=SD1_Write_Cmd(pCMD24);
  time++;
  if(time==TRY_TIME)
  {
   return(r); //命令写入失败
  }
}while(r!=0);
while(0XFF!=SD1_IOSPI_RWByte(0XFF)); //等待SD卡准备好,再向其发送命令及后续的数据
       
SD1_IOSPI_RWByte(0xFE);//写入开始字节 0xfe,后面就是要写入的512个字节的数据       
       
for(i=0;i<4;i++) //将缓冲区中要写入的512个字节写入SD1卡,减少循环次数,提高数据写入速度
{
         for(ii=0;ii<128;ii++)
         {
           SD1_IOSPI_RWByte(*(buffer++));
         }
  
}
  
SD1_IOSPI_RWByte(0xFF);
SD1_IOSPI_RWByte(0xFF); //CRC校验码
      
r=SD1_IOSPI_RWByte(0XFF);   //读取返回值
if((r & 0x1F)!=0x05) //如果返回值是 XXX00101 说明数据已经被SD卡接受了
{
  return(WRITE_BLOCK_ERROR); //写块数据失败
}
while(0xFF!=SD1_IOSPI_RWByte(0XFF));//SD卡忙检测
SD_IOSPI_CS_Set();
SD1_IOSPI_RWByte(0xFF); //按照SD卡的操作时序在这里补8个时钟
return(0);                 //返回0,说明写扇区操作成功
}
读取addr扇区的512个字节到buffer指向的数据缓冲区,调用成功,返回0x00,否则返回错误码
- 注:SD卡初始化成功后,读写扇区时,尽量将SPI速度提上来,提高效率
u8 SD1_Read_Sector(u32 addr,u8 *buffer)//从SD卡的指定扇区中读出512个字节
{
u8 i=0,ii=0;
u8 time,r;
       
u8 pCMD17[]={0x51,0x00,0x00,0x00,0x00,0x01}; //CMD17的字节序列
   
addr<<=9; //sector = sector * 512           将块地址(扇区地址)转为字节地址
pCMD17[1]=addr>>24; //将字节地址写入到CMD17字节序列中
pCMD17[2]=addr>>16;
pCMD17[3]=addr>>8;
pCMD17[4]=addr;       
time=0;
do
{  
  r=SD1_Write_Cmd(pCMD17); //写入CMD17
  time++;
  if(time==TRY_TIME)
  {
   return(READ_BLOCK_ERROR); //读块失败
  }
}while(r!=0);
                          
while(SD1_IOSPI_RWByte(0XFF)!= 0xFE); //一直读,当读到0xfe时,说明后面的是512字节的数据了
for(i=0;i<4;i++)         //将数据写入到数据缓冲区中
{       
         
         for(ii=0;ii<128;ii++)
         {
           *(buffer++)=SD1_IOSPI_RWByte(0XFF);
         }
  
}
SD1_IOSPI_RWByte(0XFF);
SD1_IOSPI_RWByte(0XFF);//CRC校验码
SD_IOSPI_CS _Set();
SD1_IOSPI_RWByte(0xFF); //按照SD1卡的操作时序在这里补8个时钟
return 0;
}
小结:
本来是想着完整的移植过来一个FAT读写操作系统的,但是查看芯片资料发现程序存储空间有点小只能作罢。
经过一些简单的测试,就我个人而言这款芯片做一些手持低功耗设备还是非常不错的选择,如果想要使用一些控制系统的话
程序存储空间就有一些不过用了,如果改进希望能将这部分做大一些,另外引脚建议增加。


使用特权

评论回复
沙发
zmlopq|  楼主 | 2022-3-6 00:12 | 只看该作者

使用特权

评论回复
板凳
不错

使用特权

评论回复
地板
两只袜子| | 2022-3-10 09:53 | 只看该作者
非常棒,赞一个

使用特权

评论回复
5
七毛钱| | 2022-3-11 10:08 | 只看该作者
不错哦

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

5

主题

64

帖子

2

粉丝