44.3 软件设计 打开上一章的工程,首先在HARDWARE文件夹下新建一个SD的文件夹。然后新建一个MMC_SD.C和MMC_SD.H的文件保存在SD文件夹下,并将这个文件夹加入头文件包含路径。 打开MMC_SD.C文件,在该文件里面,我们输入与SD卡相关的操作代码,这里由于篇幅限制,我们不贴出所有代码,仅介绍两个最重要的函数,第一个是SD_Initialize函数,该函数源码如下: //初始化SD卡 u8 SD_Initialize(void) { u8 r1; // 存放SD卡的返回值 u16 retry; // 用来进行超时计数 u8 buf[4]; u16 i; SD_SPI_Init(); //初始化IO SD_SPI_SpeedLow(); //设置到低速模式 for(i=0;i<10;i++)SD_SPI_ReadWriteByte(0XFF);//发送最少74个脉冲 retry=20; do { r1=SD_SendCmd(CMD0,0,0x95);//进入IDLE状态 }while((r1!=0X01) && retry--); SD_Type=0;//默认无卡 if(r1==0X01) { if(SD_SendCmd(CMD8,0x1AA,0x87)==1)//SD V2.0 { for(i=0;i<4;i++)buf=SD_SPI_ReadWriteByte(0XFF);//得到R7相应值 if(buf[2]==0X01&&buf[3]==0XAA)//卡是否支持2.7~3.6V { retry=0XFFFE; do { SD_SendCmd(CMD55,0,0X01); //发送CMD55 r1=SD_SendCmd(CMD41,0x40000000,0X01);//发送CMD41 }while(r1&&retry--); if(retry&&SD_SendCmd(CMD58,0,0X01)==0)//鉴别SD2.0卡版本开始 { for(i=0;i<4;i++)buf=SD_SPI_ReadWriteByte(0XFF);//得到OCR值 if(buf[0]&0x40)SD_Type=SD_TYPE_V2HC; //检查CCS else SD_Type=SD_TYPE_V2; } } }else//SD V1.x/ MMC V3 { SD_SendCmd(CMD55,0,0X01); //发送CMD55 r1=SD_SendCmd(CMD41,0,0X01); //发送CMD41 if(r1<=1) { SD_Type=SD_TYPE_V1; retry=0XFFFE; do //等待退出IDLE模式 { SD_SendCmd(CMD55,0,0X01); //发送CMD55 r1=SD_SendCmd(CMD41,0,0X01);//发送CMD41 }while(r1&&retry--); }else//MMC卡不支持CMD55+CMD41识别 { SD_Type=SD_TYPE_MMC;//MMC V3 retry=0XFFFE; do //等待退出IDLE模式 { r1=SD_SendCmd(CMD1,0,0X01);//发送CMD1 }while(r1&&retry--); } if(retry==0||SD_SendCmd(CMD16,512,0X01)!=0)SD_Type=SD_TYPE_ERR; //错误的卡 } } SD_DisSelect();//取消片选 SD_SPI_SpeedHigh();//高速 if(SD_Type)return 0; else if(r1)return r1; return 0xaa;//其他错误 } 该函数先设置与SD相关的IO口及SPI初始化,然后发送CMD0,进入IDLE状态,并设置SD卡为SPI模式通信,然后判断SD卡类型,完成SD卡的初始化,注意该函数调用的SD_SPI_Init等函数,实际是对SPI2的相关函数进行了一层封装,方便移植。另外一个要介绍的函数是SD_ReadDisk,该函数用于从SD卡读取一个扇区的数据(这里一般为512字节),该函数代码如下: //读SD卡 //buf:数据缓存区 //sector:扇区 //cnt:扇区数 //返回值:0,ok;其他,失败. u8 SD_ReadDisk(u8*buf,u32 sector,u8 cnt) { u8 r1; if(SD_Type!=SD_TYPE_V2HC)sector <<= 9;//转换为字节地址 if(cnt==1) { r1=SD_SendCmd(CMD17,sector,0X01); //读命令 if(r1==0) r1=SD_RecvData(buf,512); //命令发送成功,接收512个字节 }else { r1=SD_SendCmd(CMD18,sector,0X01);//连续读命令 do { r1=SD_RecvData(buf,512);//接收512个字节 buf+=512; }while(--cnt && r1==0); SD_SendCmd(CMD12,0,0X01); //发送停止命令 } SD_DisSelect();//取消片选 return r1;// } 此函数先发送CMD17命令,然后读取一个扇区的数据,详细见代码,这里我们就不多介绍了。保存MMC_SD.C文件,并加入到HARDWARE组下,然后打开MMC_SD.H,在该文件里面输入如下代码: #ifndef _MMC_SD_H_ #define _MMC_SD_H_ #include "sys.h" #include <stm32f10x_map.h> // SD卡类型定义 #define SD_TYPE_ERR 0X00 #define SD_TYPE_MMC 0X01 #define SD_TYPE_V1 0X02 #define SD_TYPE_V2 0X04 #define SD_TYPE_V2HC 0X06 // SD卡指令表 #define CMD0 0 //卡复位 #define CMD1 1 #define CMD8 8 //命令8 ,SEND_IF_COND #define CMD9 9 //命令9 ,读CSD数据 #define CMD10 10 //命令10,读CID数据 #define CMD12 12 //命令12,停止数据传输 #define CMD16 16 //命令16,设置SectorSize 应返回0x00 #define CMD17 17 //命令17,读sector #define CMD18 18 //命令18,读Multi sector #define CMD23 23 //命令23,设置多sector写入前预先擦除N个block #define CMD24 24 //命令24,写sector #define CMD25 25 //命令25,写Multi sector #define CMD41 41 //命令41,应返回0x00 #define CMD55 55 //命令55,应返回0x01 #define CMD58 58 //命令58,读OCR信息 #define CMD59 59 //命令59,使能/禁止CRC,应返回0x00 //数据写入回应字意义 #define MSD_DATA_OK 0x05 #define MSD_DATA_CRC_ERROR 0x0B #define MSD_DATA_WRITE_ERROR 0x0D #define MSD_DATA_OTHER_ERROR 0xFF //SD卡回应标记字 #define MSD_RESPONSE_NO_ERROR 0x00 #define MSD_IN_IDLE_STATE 0x01 #define MSD_ERASE_RESET 0x02 #define MSD_ILLEGAL_COMMAND 0x04 #define MSD_COM_CRC_ERROR 0x08 #define MSD_ERASE_SEQUENCE_ERROR 0x10 #define MSD_ADDRESS_ERROR 0x20 #define MSD_PARAMETER_ERROR 0x40 #define MSD_RESPONSE_FAILURE 0xFF //这部分应根据具体的连线来修改! //战舰STM32开发板使用的是PD2作为SD卡的CS脚. #define SD_CS PDout(2) //SD卡片选引脚 extern u8 SD_Type;//SD卡的类型 //函数申明区 u8 SD_SPI_ReadWriteByte(u8 data); void SD_SPI_SpeedLow(void); void SD_SPI_SpeedHigh(void); u8 SD_WaitReady(void); //等待SD卡准备 u8 SD_GetResponse(u8 Response); //获得相应 u8 SD_Initialize(void); //初始化 u8 SD_ReadDisk(u8*buf,u32 sector,u8 cnt); //读块 u8 SD_WriteDisk(u8*buf,u32 sector,u8 cnt); //写块 u32 SD_GetSectorCount(void); //读扇区数 u8 SD_GetCID(u8 *cid_data); //读SD卡CID u8 SD_GetCSD(u8 *csd_data); //读SD卡CSD #endif 该部分代码主要是一些命令的宏定义以及函数声明,在这里我们设定了SD卡的CS管脚为PD2。保存MMC_SD.H,就可以在主函数里面编写我们的应用代码了,打开test.c文件,在该文件中修改main函数如下: int main(void) { u8 key; u8 t=0; u8 *buf; u32 sd_size; Stm32_Clock_Init(9); //系统时钟设置 uart_init(72,9600); //串口初始化为9600 delay_init(72); //延时初始化 LED_Init(); //初始化与LED连接的硬件接口 LCD_Init(); //初始化LCD usmart_dev.init(72); //初始化USMART KEY_Init(); //按键初始化 FSMC_SRAM_Init(); //初始化外部SRAM mem_init(SRAMIN); //初始化内部内存池 POINT_COLOR=RED;//设置字体为红色 LCD_ShowString(60,50,200,16,16,"WarShip STM32"); LCD_ShowString(60,70,200,16,16,"SD CARD TEST"); LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK"); LCD_ShowString(60,110,200,16,16,"2012/9/17"); LCD_ShowString(60,130,200,16,16,"KEY0:Read Sector 0"); while(SD_Initialize())//检测不到SD卡 { LCD_ShowString(60,150,200,16,16,"SD Card Error!"); delay_ms(500); LCD_ShowString(60,150,200,16,16,"Please Check! "); delay_ms(500); LED0=!LED0;//DS0闪烁 } POINT_COLOR=BLUE;//设置字体为蓝色 //检测SD卡成功 LCD_ShowString(60,150,200,16,16,"SD Card OK "); LCD_ShowString(60,170,200,16,16,"SD Card Size: MB"); sd_size=SD_GetSectorCount();//得到扇区数 LCD_ShowNum(164,170,sd_size>>11,5,16);//显示SD卡容量(MB) while(1) { key=KEY_Scan(0); if(key==KEY_RIGHT)//KEY0按下了 { buf=mymalloc(0,512); //在内部内存池,申请512字节内存 if(SD_ReadDisk(buf,0,1)==0) //读取0扇区的内容 { LCD_ShowString(60,190,200,16,16,"USART1 Sending Data..."); printf("SECTOR 0 DATA:\r\n"); for(sd_size=0;sd_size<512;sd_size++)printf("%x ",buf[sd_size]); //打印0扇区数据 printf("\r\nDATA ENDED\r\n"); LCD_ShowString(60,190,200,16,16,"USART1 Send Data Over!"); } myfree(0,buf); //释放内存 } t++; delay_ms(10); if(t==20) { LED0=!LED0; t=0; } } } 这里我们通过SD_GetSectorCount函数来得到SD卡的扇区数,间接得到SD卡容量,然后在液晶上显示出来,接着我们通过按键KEY0控制读取SD卡的扇区0,然后把读到的数据通过串口打印出来。这里,我们对上一章学过的内存管理小试牛刀,稍微用了下,以后我们会尽量使用内存管理来设计。 44.4 下载验证 在代码编译成功之后,我们通过下载代码到ALIENTEK战舰STM32开发板上,可以看到LCD显示如图44.4.1所示的内容(默认SD卡已经接上了):
图44.4.1 程序运行效果图
打开串口调试助手,按下KEY0就可以看到从开发板发回来的数据了,如图44.4.2所示:
图44.4.2 串口收到的SD卡扇区0内容
这里请大家注意,不同的SD卡,读出来的扇区0是不尽相同的,所以不要因为你读出来的数据和图44.4.2不同而感到惊讶。 |