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卡引脚功能详述:
注: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读写操作系统的,但是查看芯片资料发现程序存储空间有点小只能作罢。 经过一些简单的测试,就我个人而言这款芯片做一些手持低功耗设备还是非常不错的选择,如果想要使用一些控制系统的话 程序存储空间就有一些不过用了,如果改进希望能将这部分做大一些,另外引脚建议增加。
|