读写SD卡的设计及其程序硬件
SD卡读写程序

SD卡读写程序SD卡读写程序//实验目的:学习SD卡的操作//软件设计// 1、SD卡采用SPI通信// 2、先往SD里顺序写入0-255共256个数据,然后再读回送LCD1602显示//硬件要求:// 拨码开关S11置ON// 跳线J18全部接通#include //dsPIC30F6014标准头文件_FOSC(CSW_FSCM_OFF & XT_PLL4); //4倍频晶振,Failsafe 时钟关闭_FWDT(WDT_OFF); //关闭看门狗定时器_FBORPOR(PBOR_OFF & MCLR_EN); //掉电复位禁止,MCLR 复位使能。
_FGS(CODE_PROT_OFF); //代码保护禁止#define cs PORTGbits.RG9 //定义SD卡片选脚#define rs /doc/e62205906.html,TB4 //定义LCD控制位(注意这里只能用LATB寄存器,不能直接用PORTB 寄存器)#define rw /doc/e62205906.html,TB5 #define e /doc/e62205906.html,TB6unsigned char __attribute__((address(0x900))) lcd[3]={0,0,0};void spi_init(); //申明系统初始函数void spi_low(); //申明产生低波特率函数(SD卡初始化使用)void spi_high(); //申明产生高波特率函数(SD卡初始化后使用)unsigned char sd_reset(); //申明SD卡初始化函数unsigned char SD_SendCommand(unsigned char cmd,unsigned long arg); //申明写SD卡命令函数unsigned char SPI_WriteByte(unsigned char val); //申明写一字节函数unsigned char SPI_ReadByte(void); //申明接收一字节函数unsigned char SD_WriteSingleBlock(unsigned long sector); //申明写SD卡单BLOCK数据函数unsigned char SD_ReadSingleBlock(unsigned long sector); //申明读SD卡单BLOCK数据函数void lcd_display(); //申明结果显示函数void delay(); //申明延时函数(显示时用)//系统初始化函数void spi_init(){TRISG="0x00d0"; //设置SDI为输出,其他C口为输出TRISB="0X0000"; //设置B口为输出TRISD="0X0000"; //设置D口为输出SPI2CON=0x0278; //空闲时总线为高电平,fosc/64SPI2STAT=0x8000; // 输出数据的末尾采样输入数据,上升沿发送数据}//*************************写LCD程序****************************************//写一个字节数据函数//在电平发生改变后需要插入一段延时时间,否则LCD反应不过来。
单片机读写SD卡

单片机读写SD卡最简单最基本的程序处理器:s3c44b0 (arm7)SD卡与处理器的引脚连接:MISO -->SIORxD MOSI -->SIOTxD CLK -->SCLK CS -->PE5包括四个文件:sd_drive.c :用户API函数,移植时不需修改sd_cmd.c:中间层函数,移植时不需修改sd_hard.c:硬件层函数,移植时需修改sd_config.h:一些功能的宏定义,移植时需修改第一次读写SD卡时,需调用SD_Init(void),然后就可以条用Read_Single_Block或者Write_Single_Block进行读写操作注意:进行写操作时,最好不要写前700个扇区,应为这些扇区都是FAT文件系统的重要扇区,一旦误写则可能会导致SD无法被电脑识别,需格式化。
/*******************************************************文件名:sd_drive.c作用:用户API函数,包括四个函数,读取一块扇区(512字节)U8 Read_Single_Block(U32 blk_addr, U8 *rx_buf)写一个扇区(512字节)U8 Write_Single_Block(U32 blk_addr, U8 *tx_buf)获取SD卡基本信息,即读CSD寄存器信息(16字节):void SD_info()SD卡初始化:U8 SD_Init(void)********************************************************//********************************************功能:读取一个block输入:blk_addr为第几个block,rx_buf为数据缓存区首地址输出:返回NO_ERR则成功,其它则读取失败********************************************/U8 Read_Single_Block(U32 blk_addr, U8 *rx_buf){U16 rsp = 1;U8 i = 0;SD_sel(); //使能SD卡while(rsp && (i < 100)){write_cmd(CMD17, blk_addr << 9); //写命令CMD17rsp = Get_rsp(R1); //获取答应send_clk();}if(i > 99) //如果命令超时,则执行超时处理{SD_desel();Uart_Printf("fail in writing CMD17\n");return WR_SGL_BLK_ERR;}spi_ro_mode();send_clk(); //发送8个clkread_data(rx_buf); //读取512字节SD_desel();Uart_Printf("succeed in reading the %dst block!!!\n", blk_addr); return NO_ERR;}/********************************************功能:写一个block输入:blk_addr为要写第几个block,tx_buf为数据区输出:返回NO_ERR则成功,其它则读取失败********************************************/U8 Write_Single_Block(U32 blk_addr, U8 *tx_buf){U16 rsp = 1;U8 i = 0;SD_sel(); //使能SD卡while(rsp && (i < 100)){write_cmd(CMD24, blk_addr << 9); //写命令CMD24rsp = Get_rsp(R1); //获取答应send_clk();}if(i > 99) //如果命令超时,则执行超时处理{SD_desel();Uart_Printf("fail in writing CMD17\n");return WR_SGL_BLK_ERR;}spi_ro_mode();send_clk(); //发送8个clkwrite_data(tx_buf); //读取512字节SD_desel();Uart_Printf("succeed in writing a block!!!\n");return NO_ERR;}/********************************************功能:SD卡初始化输入:无输出:返回NO_ERR则成功,其它则读取失败********************************************/U8 SD_Init(void){U16 rsp = 1;U8 i = 0;spi_port_init(); //初始化spi端口spi_low_speed(); //初始化时SPI的速度必须低于400khzspi_ro_mode(); //只读模式SD_sel(); //选择SD卡for (i = 0;i < 10; i++) //发送至少75个clksend_clk();while(rsp && (i++ < 100)){write_cmd(CMD0, 0); //写命令CMD0rsp = Get_rsp(R1); //获取答应if (rsp == 1) //rsp为0则初始化成功,为1则继续写CMD0 break;send_clk();}SD_desel();if (i > 99) //初始化超时处理{Uart_Printf("fail in writing CMD0!!!\n");return INIT_FAIL;}i=0;SD_sel();while(rsp && (i++ < 100)){write_cmd(CMD1, 0); //写CMD1rsp = Get_rsp(R1); //获取答应send_clk();}SD_desel();if (i > 99){Uart_Printf("fail in writing CMD1!!!\n");return INIT_FAIL;}Uart_Printf("SD card init OK!!!\n");spi_high_speed(); //初始化工作全部完毕,SPI进入模式模式spi_rt_mode();return NO_ERR;}/********************************************功能:获取SD卡信息输入:输出:********************************************/void SD_info(){U8 rsp=0;U8 csd[16];SD_sel();write_cmd(CMD9, 0);rsp = Get_rsp(R1);if (rsp != 0){SD_desel();Uart_Printf("error in getting SD info!!!\n");return ;//GET_INFO_ERR;}if (read_register(16, csd) != NO_ERR){SD_desel();return ;}SD_desel();Uart_Printf("SD information :\n");if (csd[0] & 0x40 ==0x40){Uart_Printf("version 2.0\n");Uart_Printf("size is : %d\n",1024 * (csd[8]<<8 + csd[9]));}else{Uart_Printf("version 1.x \n");Uart_Printf("size is : %d MByte\n", ((((csd[6]&0x03)<<10) | (csd[7]<<2) |((csd[8]&0xC0)>>6) + 1) * (1 << ((((csd[9]&0x03)<<1) | ((csd[10]&0x80)>>7)) + 2)))>>11); }Uart_Printf("max block lenght is : %d\n",1<<(csd[5]&0x0f));}/****************************************************************************文件名:sd_cmd.c作用:中间层函数****************************************************************************//********************************************功能:向SD写入一个命令输入:cmd为命令,addr为SD卡片内地址输出:无********************************************/void write_cmd(U8 cmd, U32 addr){U8 i = 0;U8 temp[4];spi_rt_mode(); //spi发送与接收模式if (cmd <= 13) //前13个命令与地址无关{spi_write_byte((cmd & 0x3F) | 0x40); //命令最高两位必须是01for(i = 0; i < 4; i++) //发送4个0,协议规定的spi_write_byte(0);if (cmd == 0)spi_write_byte(0x95); //如果是CMD0,则要发送CRC校正else spi_write_byte(0xff); //非CMD0,则无需CRC校正,默认为0xFF}else{for(i = 0; i < 4; i++) //将32位的地址分割成4个字节,准备发送temp[i]=(char)(addr >> (24 - 8 * i));spi_write_byte((cmd & 0x3F) | 0x40); //命令最高两位必须是01for(i =0; i < 4; i++)spi_write_byte(temp[i]); //发送地址,共4个字节spi_write_byte(0xff); //非CMD0,则无需CRC校正,默认为0xFF}}/********************************************功能:获取SD卡的答应字节,可能是一个或者两个字节输入:type为答应类型输出:答应字节,个数有答应类型而定********************************************/U16 Get_rsp(U8 type){U16 rsp, temp;spi_ro_mode(); //spi只读模式send_clk(); //先发送8个clkrsp = spi_read_byte(); //用spi读取答应字节if (rsp & 0x8)rsp = spi_read_byte();if (type == R2) //如果是R2类型,则答应为两个字节,须再次读取{temp = rsp << 8;rsp = spi_read_byte();rsp = temp | rsp;}return rsp;}/********************************************功能:读取SD的一个block的内容,一般为512字节输入:buffer为数据缓存区头地址输出:无********************************************/void read_data(U8 *buffer){U32 i;U8 rsp = 0;while(!(rsp == 0xfe)) //答应字节的最低为0则代表起始位rsp = spi_read_byte();for(i = 0;i < BLOCK_LEN; i++) //读一个block的内容,一般为512字节buffer[i] = spi_read_byte();for(i = 0; i < 2; i++) //读两个CRC校正码send_clk();send_clk(); //读结束字节}/********************************************功能:写入SD的一个block的内容,一般为512字节输入:buffer为数据缓存区头地址输出:********************************************/U8 write_data(U8 *buffer){U16 rsp = 0, tmp = 0, busy = 0, i = 6;spi_rt_mode();spi_write_byte(0xfe); //起始位for(i = 0; i < 512; i++) //发送512个字节spi_write_byte(buffer[i]);for(i = 0; i < 2; i++) //发送16位的CRC校正spi_write_byte(0xff);spi_ro_mode(); //等待答应while(!(rsp == 0x1)){rsp =(U16)spi_read_byte();tmp = rsp;rsp &= 0x11;}while(!(busy == 0xff)) //判忙{busy = spi_read_byte();}tmp &= 0xe;if (tmp == 4)return NO_ERR;else{Uart_Printf("writing error!!!\n");return WR_SGL_BLK_ERR;}}/********************************************功能:输入:输出:********************************************/U8 read_register(U8 len, U8 *buffer){U8 rsp = 0xff, i = 0;spi_ro_mode();while((rsp == 0xff) && (i < 100)){rsp=spi_read_byte();}if (i > 99){Uart_Printf("ERR in readding register!!!\n");return rsp;}if (rsp != 0xfe){buffer[0] = rsp;i = 1;}elsei = 0;for( ; i < len; i++)buffer[i] = spi_read_byte();for(i = 0; i < 2; i++ )send_clk();send_clk();return NO_ERR;}/*******************************************************************文件名:sd_hard.c作用:硬件层函数,移植时需根据处理器或者硬件结构的不同,对该文件的函数进行修改********************************************************************//********************************************功能:使能SPI,发送CLK输入:无输出:无********************************************/void send_clk(){rSIOCON |= (1 << 3); //使能SPIwhile (!(rINTPND & BIT_SIO)); //等待发送完毕rI_ISPC|=BIT_SIO; //清除中断标志}/********************************************功能:用SPI发送一个字节输入:dat为要发送的字节输出:无********************************************/void spi_write_byte(U8 dat){rSIODAT = dat;send_clk(); //SPI发送}/********************************************功能:用SPI读取外设一个字节输入:无输出:读到的一个字节********************************************/U8 spi_read_byte(void){send_clk(); //SPI发送return rSIODAT;}/********************************************功能:初始化SPI的端口输入:无输出:无********************************************/void spi_port_init(){rIVTCNT = 0;rPCONF = (rPCONF & 0xe3ff) | 0x1B0C00; //除了CLK,MISO,MOSI外,不改变其他位rPUPF |= 0x160; //使能MISO的上拉电阻}/***************************************************************文件名:sd_config.h作用:相关功能的宏定义,以便被以上三个文件调用,便于移植移植时需修改***************************************************************/#ifndef _SD_CONG#define _SD_CONG#define BLOCK_LEN (512) //一个block的长度#define CMD0 0#define CMD1 1 // 读OCR寄存器#define CMD9 9 // 读CSD寄存器#define CMD10 10 // 读CID寄存器#define CMD12 12 // 停止读多块时的数据传输#define CMD13 13 // 读Card_Status 寄存器#define CMD16 16 // 设置块的长度#define CMD17 17 // 读单块#define CMD18 18 // 读多块,直至主机发送CMD12#define CMD24 24 // 写单块#define CMD25 25 // 写多块#define CMD27 27 // 写CSD寄存器#define CMD28 28 // Set the write protection bit of the addressed group#define CMD29 29 // Clear the write protection bit of the addressed group#define CMD30 30 // Ask the card for the status of the write protection bits#define CMD32 32 // 设置擦除块的起始地址#define CMD33 33 // 设置擦除块的终止地址#define CMD38 38 //擦除所选择的块#define CMD42 42 // 设置/复位密码或上锁/解锁卡#define CMD55 55 // 禁止下一个命令为应用命令#define CMD56 56 // 应用命令的通用I/O#define CMD58 58 // 读OCR寄存器#define CMD59 59 // 使能或禁止//错误返回#define INIT_FAIL 0#define NO_ERR 1#define WR_SGL_BLK_ERR 2#define GET_INFO_ERR 3#define R1 1 //SD卡答应类型,表示一个字节#define R2 2 //SD卡答应类型,表示两个字节//一下是移植时需修改的内容#define SD_desel() rPDATE=0x20; //使能SD卡#define SD_sel() rPDATE=0x00; //放开SD卡#define spi_high_speed() rSBRDR = 5; //spi高速模式#define spi_low_speed() rSBRDR = 99; //spi低速模式#define spi_ro_mode() rSIOCON = (0x0 << 7) | (0x0 << 6) | (0x0 << 5) | (0x0 << 4) | (0x0 << 3) | (0x0 << 2) | 0x1 //只读模式#define spi_rt_mode() rSIOCON = (0x0 << 7) | (0x0 << 6) | (0x1 << 5) | (0x0 << 4) | (0x0 << 3) | (0x0 << 2) | 0x1 //读写模式#endif。
SD卡读写器制作

摘要随着嵌入式处理器计算机能力的不断提高,32 位MPU 已成为嵌入式系统应用的主流。
在ARM9 嵌入式处理器和mC/OS-II操作系统基础上采用SD 卡设计开发了一种嵌入式文件系统,该系统具有支持多操作系统、易于移植和存储速度快的特点。
本文在三星ARM9-S3C2440上实现了MMC /SD卡的硬件扩展, 设计了此硬件平台上的MMC /SD卡在L inux下的驱动, 并给出了相应的关键代码。
关键词: 嵌入式Linux; MMC /SD卡; Linux设备驱动; S3C2440目录1. 概述 (3)1.1 SD卡简介 (3)1.2 系统研究背景 (3)2. 方案论述 (4)2.1 SD卡工作原理 (4)2.1.1 SD 卡内部结构及引脚如下图所示 (4)2.1.2 SD卡指令数据包 (4)2.1.3 SD卡接口电路 (5)3. 芯片选择 (6)3.1 S3C2440的简介 (6)3.2 硬件资源 (6)3.3物理特性 (6)3.4 核心板展示 (7)4. 总体框图 (7)6.1 源程序代码 (9)6.2 读SD卡流程图 (10)6.3 写SD卡流程图 (11)总结 (12)参考文献 (13)附录1:SD卡命令表 (14)附录2:源代码清单 (16)附录3:原理图 (28)附录4:PCB图 (31)1.概述1.1 SD卡简介●SD卡是基于flash的存储卡。
SD卡和MMC卡的区别在于初始化过程不同。
●SD卡的通信协议包括SD和SPI两类。
●SD卡使用卡内智能控制模块进行FLASH操作控制,包括协议、安全算法、数据存取、ECC●算法、缺陷处理和分析、电源管理、时钟管理●SD卡(Secure Digital Memory Card)是一种为满足安全性、容量、性能和使用环境等各方面的需求而设计的一种新型存储器件,SD卡允许在两种模式下工作,即SD模式和SPI模式。
1.2 系统研究背景本设计旨在深入对ARM的学习,巩固大学四年所学专业知识,提升动手能力和思考问题解决问题的能力。
SD卡读写程序设计(一)

SD卡读写程序设计(一)一、概述在智林STM32开发板上有采用SPI方式的SD卡接口,我手上有一块512M的SD卡,所以接下来就开始SD卡的读写程序设计,学习完了,就可以开始文件系统的移植学习了#####。
二、程序框架设计1、在shell里增加两个命令:sdwr,和sdrd。
2、sdwr在SD卡设定的扇区写入输入数据。
Sdrd是将刚刚写入的数据读出来。
3、sdwr命令输入后,内部执行uartcmdsdwrite()函数。
调用SD_WriteBlock ()函数,将缓冲字符数组写入。
而sdrd命令执行UartCmdSDRead()函数,调用SD_ReadBlock()函数,读入一个扇区,并用十六进制的方式在串口中断上显示。
4、SD的驱动分为三层:应用层、命令层、和驱动层。
分别用sddrive.c,sdcmd.c,和spi.c三个文件来实现。
三、SPI接口的SD卡1、SPI接口SPI是一种全双工、同步串行通信方式接口,这里用到了四个IO口:分别是时钟线SCK、输出口MOSI、输入口MISO、模式从机选择线NSS。
2、SPI接口与SD卡STM32与采用SPI接口的SD卡,就像两个CPU通信一样,STM32处理器通过SPI接口发出命令,SD卡执行命令后返回相应的状态。
命令有读写命令、也有参数设置命令。
3、SD卡的内部结构:几个重要寄存器(1)OCR寄存器:保存着卡的供电允许范围,位31表示卡上电后的状态,1表示空闲。
(2)CSD寄存器:总共128位,表示了卡的大部分配置信息。
(3)状态寄存器:命令响应的状态。
4、常用的SPI模式命令(1)命令由六个字节组成:01-六位命令号-四个字节的命令参数-7位校验码-结束位1。
(2)命令分为10个类:SPI支持:类0基本控制的(0复位、1激活初始化、9读CSD寄存器、10读CID寄存器、12多块过程中停止传输、13读状态寄存器),类2块读的(16设置块长度、17读一个数据块、18读多个数据块,直到发命令12);类4块写的(24写块、25写多个块、27写CSD的可编程为);类5擦除的(32设置擦除块的起始地址、33设置终止块地址、38擦除先前选择的所有块);块6写保护的(可选28设置写保护、29清除写保护、30读写保护状态);类7的锁卡命令(可选42上锁或者解锁);类8的指定应用(55通知SD卡下个是特殊应用命令、56获取或写入一个数据块)。
[整理]SD卡程序设计、流程及操作系统.
![[整理]SD卡程序设计、流程及操作系统.](https://img.taocdn.com/s3/m/529ac35ea45177232f60a2ec.png)
对SD卡进行操作首先要对SD卡进行初始化,初始化的过程中设置SD卡工作在SPI模式,其流程图如图3所示。
在复位成功之后可以通过CMD55和ACMD41判断当前电压是否在工作范围内。
主机还可以继续通过CMD10读取SD卡的CID寄存器,通过CMD16设置数据Block长度,通过CMD9读取卡的CSD寄存器。
从CSD寄存器中,主机可获知卡容量,支持的命令集等重要参数。
SD卡初始化的C语言程序如下:unsigned char SD_Init(void){ unsigned char retry,temp;unsigned char i;for (i=0;i<0x0f;i++){ SPI_TransferByte(0xff); //延迟74个以上的时钟}SD_Enable(); //开片选SPI_TransferByte(SD_RESET); //发送复位命令SPI_TransferByte(0x00);SPI_TransferByte(0x00);SPI_TransferByte(0x00);SPI_TransferByte(0x00);SPI_TransferByte(0x95);SPI_TransferByte(0xff);SPI_TransferByte(0xff);retry=0;do{ temp="Write"_Command_SD(SD_INIT,0);//发送初始化命令retry++;if(retry==100) //重试100次{SD_Disable(); //关片选return(INIT_CMD1_ERROR);//如果重试100次失败返回错误号}}while(temp!=0);SD_Disable(); //关片选return(TRUE); //返回成功}数据块的读写完成SD卡的初始化之后即可进行它的读写操作。
SD卡的读写操作都是通过发送SD 卡命令完成的。
SD卡读写

SD卡读写(1)系统硬件文件系统只是数据的组织格式的统称,不涉及到硬件,所以系统的硬件与上篇日志中相同,不作修改。
(2)文件系统结构和读写原理带有文件系统的SD卡的内部结构一般如下表:256MSD卡的文件系统结构文件系统结构说明扇区起始号占用扇区数 Partiton Boot Sector 分区记录扇区 0 1 Reserved Sectors 保留扇区 0 4 FAT1 文件分配表1 4 242 FAT2 文件分配表2 246 242 DIR(FDT) 文件根目录区 488 32 User Data 数据区 520 493560 SD卡的保留扇区中一般不要写入数据,否则可能破坏其文件系统结构,导致操作系统不能识别。
在FAT文件系统中,BPB(Bios ParameterBlock)是一个很重要的参数表,该表通常位于0扇区(保留扇区中的第一个扇区)中的12-36字节,它记录了分区中的一些重要数据如总扇区数、每扇区的字节数、每簇的扇区数、保留扇区数、FAT表占用扇区数等,我这里的256M的SD卡中的BPB表如下:名称占用字节数内容说明BPB_BytesPerSec 2 0x0200 扇区大小为512字节 BPB_SecPerChus 1 0x08 每簇有8个扇区 BPB_RsvdSecCnt 2 0x0004 有4个保留扇区 BPB_NumFATs 1 0x02 有2个FAT表BPB_RootEntCnt 2 0x0200 根目录中可有512个登记项 BPB_TotSec16 20x0000 为0表示总扇区数大于65536BPB_MediaType 1 0xF8 磁盘介质为硬盘 BPB_FATSize16 2 0x00F2 每个FAT表占242个扇区 BPB_SecPerTrk 2 0x3F 每个磁道有63个扇区 BPB_NumHeads 2 0x00FF 磁头数为255BPB_HiddSec 4 0x00000000 有0个隐藏扇区 BPB_TotSec32 4 0x00078A00 共有494080个扇区保留扇区之后是文件分配表,FAT16文件系统有两份文件分配表(FAT),FAT的大小可以在BPB中查到。
对SD卡的读写程序

SD卡(Secure Digital Memory Card)中文翻译为安全数码卡,是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用,例如数码相机、个人数码助理(PDA)和多媒体播放器等。
SD卡由日本松下、东芝及美国SanDisk公司于1999年8月共同开发研制。
大小犹如一张邮票的SD记忆卡,重量只有2克,但却拥有高记忆容量、快速数据传输率、极大的移动灵活性以及很好的安全性。
贴程序:/*-------------------------------------1.本程序主要为了实现以下功能:1)通过对SPI方式对SD卡进行读与写2)简单的FAT操作3)对写入的数据进行打印2.编写日期:09.10.133.版本号:V1.04.作者:andyluo----------------------------------------*/#include<reg52.h> //添加头文件#define uchar unsigned char //宏定义数据类型#define uint unsigned int/*-------------------------------------与指示灯有关的IO--------------------------------------*/sbit power=P0^4;/*-------------------------------------与SD卡通信有关的IO---------------------------------------*/sbit SCL=P1^5;//时钟信号sbit CS=P1^6;//片选信号sbit SI=P1^7;//数据输入sbit SO=P3^3;//数据输出//错误码定义#define INIT_CMD0_ERROR 0X01#define INIT_CMD1_ERROR 0X02#define WRITE_BLOCK_ERROR 0X03#define READ_BLOCK_ERROR 0X04/*--------------------------------------与通信和定时器相关的变量---------------------------------------- */uchar cn=0;/*--------------------------------------变量定义-----------------------------------------*/uchar is_init;//用于控制SPI的速度,通过为1与0进行选择uchar xdata pbuf[512];//定义512个数据缓冲区/*--------------------------------------函数名称:delay()函数作用:用于某些程序中的延时函数特点:无返回值,带入口参数-----------------------------------------*/void delay(uint k){uint m,n;for(m=0;m<5;m++)for(n=0;n<k;n++);}/*-------------------------------SPI写一个字节----------------------------------*/void spi_write(uchar x)//SPI写一个字节,其中is_init为1 {uchar i;for(i=0;i<8;i++){SI=((x<<i)&0x80);SCL=0;if(is_init){delay(8);}SCL=1;if(is_init){delay(8);}}}/*--------------------------------SPI读一个字节----------------------------------*/uchar spi_read(){uchar temp=0,i;SO=1;for(i=0;i<8;i++){SCL=0;if(is_init){delay(8);}// if(SO)// {// temp+=(0x80>>i);// }temp=(temp<<1)+(uchar)SO;SCL=1;if(is_init)//放慢速度{delay(8);}}return(temp);}/*--------------------------------向SD卡写命令---------------------------------*/uchar write_cmd(uchar *pcmd)//pcmd为命令字{uchar temp,i,time=0;CS=1;spi_write(0XFF);CS=0;for(i=0;i<6;i++) //发送6个字节的命令字节序列{spi_write(*pcmd++);}spi_read();do{temp=spi_read();//一直读,直到读到的不是0XFF或者超时time++;}while((temp==0xff)&&(time<100));return temp;}/*------------------------------------SD卡复位,进入SPI模式,使用CMD0命令-------------------------------------*/uchar SD_Reset()//SD卡复位{uchar time,temp,i;uchar pcmd[]={0x40,0x00,0x00,0x00,0x00,0x95};//0号命令对应的6个字节is_init=1;//set is_init flagCS=1;for(i=0;i<0x0f;i++)//初始化时,首先要发送至少74个时钟信号,这是必须的{spi_write(0xff);//实质发了120个时钟(15*8)}CS=0;time=0;do{temp=write_cmd(pcmd);time++;if(time==200){return(INIT_CMD0_ERROR );//cmd0写入失败}}while(temp!=0x01);CS=1;spi_write(0xff);//按照SD的操作时序在这里需要补8个时钟return 0;//返回0,说明SD卡复位操作成功}/*-------------------------------------SD卡初始化,使用CMD1号命令---------------------------------------*/uchar SD_Init()//SD卡初始化,使用CMD1号命令{uchar time,temp;uchar pcmd[]={0x41,0x00,0x00,0x00,0x00,0xff};CS=0;time=0;do{temp=write_cmd(pcmd);time++;if(time==100){return(INIT_CMD1_ERROR );}}while(temp!=0x00);is_init=0;//初始化完成,将is_init设置为0,以提高后面数据的传输速度CS=1;spi_write(0xff);return 0;//说明初始化成功}/*-----------------------------向SD卡扇区中写数据,每一个扇区中有512个字节---------------------------------*/uchar SD_write_sector(unsigned long addr,uchar *Buffer){uchar temp,time;uint i;uchar pcmd[]={0x58,0x00,0x00,0x00,0x00,0xff};//向SD卡中写入24号命令addr=addr<<9;//addr=addr+512,将块地址(扇区地址)转化为字节地址pcmd[1]=((addr&0xff000000)>>24);//将字节地址写入到24号命令的时序中pcmd[2]=((addr&0x00ff0000)>>16);pcmd[3]=((addr&0x0000ff00)>>8);//SD卡最大容量4G// pcmd[4]=(addr&0x000000FF);//此行为增加项CS=0;time=0;do{temp=write_cmd(pcmd);time++;if(time==100){return(temp);//命令写入失败}}while(temp!=0);for(i=0;i<100;i++)//这里要插入若干个时钟信号{spi_read();}spi_write(0xfe);//写入开始字节0XFE,后面就要写入512个字节for(i=0;i<512;i++)//将缓冲区中要写入的512个字节写入到SD卡中{spi_write(*Buffer++);}spi_write(0xff);spi_write(0xff);//两个字节CRC校验码temp=spi_read();//读取返回值if((temp&0x1f)!=0x05)//如果返回值为xxx00101,说明数据已经被接收{CS=1;return(WRITE_BLOCK_ERROR );//写块数据失败}while(spi_read()!=0xff);//等待SD卡不忙(数据被接收以后,SD卡要将这些数据写入自身的FLASH// 中,需要一定时间,忙时为0x00,不忙是为0xff, )CS=1;spi_write(0xff);//补8个时钟return 0;}/*--------------------------------读SD卡的一个扇区---------------------------------*/uchar SD_read_sector(unsigned long addr,uchar *Buffer){uint i;uchar time,temp;uchar pcmd[]={0x51,0x00,0x00,0x00,0x00,0xff};//CMD17号命令addr=addr<<9;pcmd[1]=((addr&0xff000000)>>24);pcmd[2]=((addr&0x00ff0000)>>16);pcmd[3]=((addr&0x0000ff00)>>8);CS=0;time=0;do{temp=write_cmd(pcmd);time++;if(time==100){return(READ_BLOCK_ERROR);//读块失败}}while(temp!=0);while(spi_read()!=0xfe);//一直读,当读到0xfe时,说明后面是512个数据字节for(i=0;i<512;i++){Buffer[i]=spi_read();}spi_read();spi_read();CS=1;spi_write(0xff);//补8个时钟return 0;}/*---------------------------------串口初始化-----------------------------------*/void serial_init(void) //{TMOD = 0x21; //使用定时器1工作在方式2,做波率发生器,定时器0方式1 TH0 = 0X3c; //设置定时初值,定时20ms,1s采用20*50算法TL0 = 0Xb0;ET0 = 1; //开定时器0中断标志TR0 = 1; //启动定时器0TH1 = 0xfd; //32M,9600TL1 = 0xfd;TR1 = 1; //启动定时器1SCON=0X40; //串口工作在方式1,不允许接收REN = 1; //允许串口接收ES = 1; //允许串口中断EA = 1; //开总中断}/*-------------------------函数名称:main()函数作用:主函数---------------------------*/void main(){int i=0;SD_Reset();SD_Init();serial_init();for(i=0;i<512;i++){pbuf[i]=i;//向缓冲区中写入数据}SD_write_sector(80,pbuf);//将缓冲区中512个字节的数据写入80扇区for(i=0;i<512;i++){pbuf[i]=0;//清空数据缓冲区}SD_read_sector(80,pbuf);//从SD卡的第80个扇区中读取512个字节的数据for(i=0;i<512;i++){P2=~pbuf[i];//将缓冲区中的数据输出在P2口,delay(1000);}//P2=0X00;while(1);}void timer0_int(void) interrupt 1 //中断程序,注意中断类型号{TH0=0X3c; //重新赋初值,并且必须要这样做,不然的话定时时间会不准确的TL0=0Xb0;cn++; //每当进入中断程序,count++,当有关参数设置正确后,程序会自动进入中断程序if(cn==20){cn=0;power = !power; //本机运行指示灯闪烁}}2009-10-17,09:26:21资料邮件回复引用回复编辑删除【1楼】andyluo324积分:123派别:程序还不够完善,等会改好后上完整版.2009-10-17,09:资料邮件编辑删除---------------------------------------- */uchar cn=0;/*--------------------------------------变量定义-----------------------------------------*/uchar is_init;//用于控制SPI的速度,通过为1与0进行选择uchar xdata pbuf[512];//定义512个数据缓冲区/*--------------------------------------函数名称:delay()函数作用:用于某些程序中的延时函数特点:无返回值,带入口参数-----------------------------------------*/void delay(uint k){uint m,n;for(m=0;m<5;m++)for(n=0;n<k;n++);}/*----------------------------------------函数名称:IO_init()函数作用:对相关IO进行初始化函数特点:无返回值,无入口参数------------------------------------------*/void IO_init(){SCL=1;CS=1;SO=1;power=1;}/*----------------------------------------函数名称:send_byte(uchar i)函数作用:对相关数据进行打印函数特点:无返回值,带入口参数--------------------------------------------*/void send_byte(uchar i){TI=0;SBUF=i;while(!TI);TI=0;}/*-------------------------------------------------------函数名:send_s()功能:用户函数,发送一个字符----------------------------------------------------------*/void send_s(char *s){int len=strlen(s);int i;for(i=0;i<len;i++)send_byte(s[i]);send_byte(0x0d);send_byte(0x0a);}/*-------------------------------SPI写一个字节----------------------------------*/void spi_write(uchar x)//SPI写一个字节,其中is_init为1{uchar i;for(i=0;i<8;i++){SI=((x<<i)&0x80);SCL=0;if(is_init){delay(8);}SCL=1;if(is_init){delay(8);}}}/*--------------------------------SPI读一个字节----------------------------------*/uchar spi_read(){uchar temp=0,i;SO=1;for(i=0;i<8;i++){SCL=0;if(is_init){delay(8);}// if(SO)// {// temp+=(0x80>>i);// }temp=(temp<<1)+(uchar)SO;SCL=1;if(is_init)//放慢速度{delay(8);}}return(temp);}/*--------------------------------向SD卡写命令---------------------------------*/uchar write_cmd(uchar *pcmd)//pcmd为命令字{uchar temp,i,time=0;CS=1;spi_write(0XFF);CS=0;for(i=0;i<6;i++) //发送6个字节的命令字节序列{spi_write(*pcmd++);}spi_read();do{temp=spi_read();//一直读,直到读到的不是0XFF或者超时time++;}while((temp==0xff)&&(time<100));return temp;}/*------------------------------------SD卡复位,进入SPI模式,使用CMD0命令-------------------------------------*/uchar SD_Reset()//SD卡复位{uchar time,temp,i;uchar pcmd[]={0x40,0x00,0x00,0x00,0x00,0x95};//0号命令对应的6个字节send_s("SD_Reset start\n");is_init=1;//set is_init flagCS=1;for(i=0;i<0x0f;i++)//初始化时,首先要发送至少74个时钟信号,这是必须的{spi_write(0xff);//实质发了120个时钟(15*8)}send_s("send 120 clk succeed\n");CS=0;time=0;do{temp=write_cmd(pcmd);time++;if(time==200){return(INIT_CMD0_ERROR );//cmd0写入失败send_s("send CMD0 ERROR\n");}}while(temp!=0x01);CS=1;spi_write(0xff);//按照SD的操作时序在这里需要补8个时钟send_s("send CMD0 succeed\n");return 0;//返回0,说明SD卡复位操作成功}/*-------------------------------------SD卡初始化,使用CMD1号命令---------------------------------------*/uchar SD_Init()//SD卡初始化,使用CMD1号命令{uchar time,temp;uchar pcmd[]={0x41,0x00,0x00,0x00,0x00,0xff};send_s("SD_Init start\n");CS=0;time=0;do{temp=write_cmd(pcmd);time++;if(time==100){return(INIT_CMD1_ERROR );send_s("CMD1 ERROR\n");}}while(temp!=0x00);is_init=0;//初始化完成,将is_init设置为0,以提高后面数据的传输速度send_s("send CMD1 succeed\n");CS=1;spi_write(0xff);send_s("SD_Init succeed\n");return 0;//说明初始化成功}/*-----------------------------向SD卡扇区中写数据,每一个扇区中有512个字节---------------------------------*/uchar SD_write_sector(unsigned long addr,uchar *Buffer){uchar temp,time;uint i;uchar pcmd[]={0x58,0x00,0x00,0x00,0x00,0xff};//向SD卡中写入24号命令addr=addr<<9;//addr=addr+512,将块地址(扇区地址)转化为字节地址pcmd[1]=((addr&0xff000000)>>24);//将字节地址写入到24号命令的时序中pcmd[2]=((addr&0x00ff0000)>>16);pcmd[3]=((addr&0x0000ff00)>>8);//SD卡最大容量4G// pcmd[4]=(addr&0x000000FF);//此行为增加项send_s("SD_write_sector start\n");CS=0;time=0;do{temp=write_cmd(pcmd);time++;if(time==100){return(temp);//命令写入失败}}while(temp!=0);for(i=0;i<100;i++)//这里要插入若干个时钟信号{spi_read();}spi_write(0xfe);//写入开始字节0XFE,后面就要写入512个字节for(i=0;i<512;i++)//将缓冲区中要写入的512个字节写入到SD卡中{spi_write(*Buffer++);}spi_write(0xff);spi_write(0xff);//两个字节CRC校验码temp=spi_read();//读取返回值if((temp&0x1f)!=0x05)//如果返回值为xxx00101,说明数据已经被接收{CS=1;return(WRITE_BLOCK_ERROR );//写块数据失败}while(spi_read()!=0xff);//等待SD卡不忙(数据被接收以后,SD卡要将这些数据写入自身的FLASH// 中,需要一定时间,忙时为0x00,不忙是为0xff, )send_s("SD_write_sector succeed\n");CS=1;spi_write(0xff);//补8个时钟return 0;}/*--------------------------------读SD卡的一个扇区---------------------------------*/uchar SD_read_sector(unsigned long addr,uchar *Buffer){uint i;uchar time,temp;uchar pcmd[]={0x51,0x00,0x00,0x00,0x00,0xff};//CMD17号命令addr=addr<<9;pcmd[1]=((addr&0xff000000)>>24);pcmd[2]=((addr&0x00ff0000)>>16);pcmd[3]=((addr&0x0000ff00)>>8);send_s("SD_read_sector start\n");CS=0;time=0;do{temp=write_cmd(pcmd);time++;if(time==100){return(READ_BLOCK_ERROR);//读块失败}}while(temp!=0);while(spi_read()!=0xfe);//一直读,当读到0xfe时,说明后面是512个数据字节for(i=0;i<512;i++){Buffer[i]=spi_read();}spi_read();spi_read();CS=1;send_s("SD_read_sector succeed\n");spi_write(0xff);//补8个时钟return 0;}/*---------------------------------串口初始化-----------------------------------*/void serial_init(void) //{TMOD = 0x21; //使用定时器1工作在方式2,做波率发生器,定时器0方式1 TH0 = 0X3c; //设置定时初值,定时20ms,1s采用20*50算法TL0 = 0Xb0;ET0 = 1; //开定时器0中断标志TR0 = 1; //启动定时器0TH1 = 0xfd; //11.0592M,9600TL1 = 0xfd;TR1 = 1; //启动定时器1SCON=0X40; //串口工作在方式1,不允许接收REN = 1; //允许串口接收ES = 1; //允许串口中断EA = 1; //开总中断}/*-------------------------函数名称:main()函数作用:主函数---------------------------*/void main(){int i=0;serial_init();send_s("serial_init!\n");IO_init();send_s("Port Init!\n");SD_Reset();SD_Init();for(i=0;i<512;i++){pbuf[i]=i;//向缓冲区中写入数据}SD_write_sector(80,pbuf);//将缓冲区中512个字节的数据写入80扇区for(i=0;i<512;i++){pbuf[i]=0;//清空数据缓冲区}SD_read_sector(80,pbuf);//从SD卡的第80个扇区中读取512个字节的数据for(i=0;i<512;i++){P2=~pbuf[i];//将缓冲区中的数据输出在P2口,send_byte(pbuf[i]);delay(1000);}//P2=0X00;while(1);}。
读写SD卡的设计及其程序硬件

前言长期以来,以Flash Memory为存储体的SD卡因具备体积小、功耗低、可擦写以及非易失性等特点而被广泛应用于消费类电子产品中。
特别是近年来,随着价格不断下降且存储容量不断提高,它的应用范围日益增广。
当数据采集系统需要长时间地采集、记录海量数据时,选择SD卡作为存储媒质是开发者们一个很好的选择。
在电能监测以及无功补偿系统中,要连续记录大量的电压、电流、有功功率、无功功率以及时间等参数,当单片机采集到这些数据时可以利用SD作为存储媒质。
本文主要介绍了SD卡在电能监测及无功补偿数据采集系统中的应用方案。
设计方案应用AT89C52读写SD卡有两点需要注意。
首先,需要寻找一个实现AT89C52单片机与SD卡通讯的解决方案;其次,SD卡所能接受的逻辑电平与AT89C52提供的逻辑电平不匹配,需要解决电平匹配问题。
通讯模式SD卡有两个可选的通讯协议:SD模式和SPI模式。
SD模式是SD卡标准的读写方式,但是在选用SD模式时,往往需要选择带有SD卡控制器接口的MCU,或者必须加入额外的SD 卡控制单元以支持SD卡的读写。
然而,AT89C52单片机没有集成SD卡控制器接口,若选用SD模式通讯就无形中增加了产品的硬件成本。
在SD卡数据读写时间要求不是很严格的情况下,选用SPI模式可以说是一种最佳的解决方案。
因为在SPI模式下,通过四条线就可以完成所有的数据交换,并且目前市场上很多MCU都集成有现成的SPI接口电路,采用SPI模式对SD卡进行读写操作可大大简化硬件电路的设计。
虽然AT89C52不带SD卡硬件控制器,也没有现成的SPI接口模块,但是可以用软件模拟出SPI总线时序。
本文用SPI总线模式读写SD卡。
电平匹配SD卡的逻辑电平相当于3.3V TTL电平标准,而控制芯片AT89C52的逻辑电平为5V CMOS 电平标准。
因此,它们之间不能直接相连,否则会有烧毁SD卡的可能。
出于对安全工作的考虑,有必要解决电平匹配问题。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
前言长期以来,以Flash Memory为存储体的SD卡因具备体积小、功耗低、可擦写以及非易失性等特点而被广泛应用于消费类电子产品中。
特别是近年来,随着价格不断下降且存储容量不断提高,它的应用范围日益增广。
当数据采集系统需要长时间地采集、记录海量数据时,选择SD卡作为存储媒质是开发者们一个很好的选择。
在电能监测以及无功补偿系统中,要连续记录大量的电压、电流、有功功率、无功功率以及时间等参数,当单片机采集到这些数据时可以利用SD作为存储媒质。
本文主要介绍了SD卡在电能监测及无功补偿数据采集系统中的应用方案。
设计方案应用AT89C52读写SD卡有两点需要注意。
首先,需要寻找一个实现AT89C52单片机与SD卡通讯的解决方案;其次,SD卡所能接受的逻辑电平与AT89C52提供的逻辑电平不匹配,需要解决电平匹配问题。
通讯模式SD卡有两个可选的通讯协议:SD模式和SPI模式。
SD模式是SD卡标准的读写方式,但是在选用SD模式时,往往需要选择带有SD卡控制器接口的MCU,或者必须加入额外的SD 卡控制单元以支持SD卡的读写。
然而,AT89C52单片机没有集成SD卡控制器接口,若选用SD模式通讯就无形中增加了产品的硬件成本。
在SD卡数据读写时间要求不是很严格的情况下,选用SPI模式可以说是一种最佳的解决方案。
因为在SPI模式下,通过四条线就可以完成所有的数据交换,并且目前市场上很多MCU都集成有现成的SPI接口电路,采用SPI模式对SD卡进行读写操作可大大简化硬件电路的设计。
虽然AT89C52不带SD卡硬件控制器,也没有现成的SPI接口模块,但是可以用软件模拟出SPI总线时序。
本文用SPI总线模式读写SD卡。
电平匹配SD卡的逻辑电平相当于3.3V TTL电平标准,而控制芯片AT89C52的逻辑电平为5V CMOS 电平标准。
因此,它们之间不能直接相连,否则会有烧毁SD卡的可能。
出于对安全工作的考虑,有必要解决电平匹配问题。
要解决这一问题,最根本的就是解决逻辑器件接口的电平兼容问题,原则主要有两条:一为输出电平器件输出高电平的最小电压值,应该大于接收电平器件识别为高电平的最低电压值;另一条为输出电平器件输出低电平的最大电压值,应该小于接收电平器件识别为低电平的最高电压值。
一般来说,通用的电平转换方案是采用类似SN74ALVC4245的专用电平转换芯片,这类芯片不仅可以用作升压和降压,而且允许两边电源不同步。
但是,这个方案代价相对昂贵,而且一般的专用电平转换芯片都是同时转换8路、16路或者更多路数的电平,相对本系统仅仅需要转换3路来说是一种资源的浪费。
考虑到SD卡在SPI协议的工作模式下,通讯都是单向的,于是在单片机向SD卡传输数据时采用晶体管加上拉电阻法的方案,基本电路如图1所示。
而在SD卡向单片机传输数据时可以直接连接,因为它们之间的电平刚好满足上述的电平兼容原则,既经济又实用。
这个方案需要双电源供电(一个5V电源、一个3.3V电源供电),3.3V电源可以用AMS1117稳压管从5V电源稳压获取。
硬件接口设计SD卡提供9Pin的引脚接口便于外围电路对其进行操作,9Pin的引脚随工作模式的不同有所差异。
在SPI模式下,引脚1(DAT3)作为SPI片选线CS用,引脚2(CMD)用作SPI 总线的数据输出线MOSI,而引脚7(DAT0)为数据输入线MISO,引脚5用作时钟线(CLK)。
除电源和地,保留引脚可悬空。
本文中控制SD卡的MCU是ATMEL公司生产的低电压、高性能CMOS 8位单片机AT89C52,内含8K字节的可反复擦写的只读程序存储器和256字节的随机存储数据存储器。
由于AT89C52只有256字节的数据存储器,而SD卡的数据写入是以块为单位,每块为512字节,所以需要在单片机最小系统上增加一片RAM。
本系统中RAM选用存储器芯片HM62256,容量为32K。
对RAM进行读写时,锁存器把低8位地址锁存,与P2口的8位地址数据构成16位地址空间,从而可使SD卡一次读写512字节的块操作。
系统硬件图如图2所示。
软件设计SPI工作模式SD卡在上电初期自动进入SD总线模式,在此模式下向SD卡发送复位命令CMD0。
如果SD卡在接收复位命令过程中CS低电平有效,则进入SPI模式,否则工作在SD总线模式。
对于不带SPI串行总线接口的AT89C52单片机来说,用软件来模拟SPI总线操作的具体做法是:将P1.5口(模拟CLK线)的初始状态设置为1,而在允许接收后再置P1.5为0。
这样,MCU在输出1位SCK时钟的同时,将使接口芯片串行左移,从而输出1位数据至AT89C52单片机的P1.7(模拟MISO线),此后再置P1.5为1,使单片机从P1.6(模拟MOSI线)输出1位数据(先为高位)至串行接口芯片。
至此,模拟1位数据输入输出便完成。
此后再置P1.5为0,模拟下1位数据的输入输出,依此循环8次,即可完成1次通过SPI总线传输8位数据的操作。
本文的实现程序把SPI总线读写功能集成在一起,传递的val变量既是向SPI写的数据,也是从SPI读取的数据。
具体程序如下:(程序是在Keil uVision2的编译环境下编写)sbit CS=P3^5;sbit CLK= P1^5;sbit DataI=P1^7;sbit DataO=P1^6;#define SD_Disable() CS=1 //片选关#define SD_Enable() CS=0 //片选开unsigned char SPI_TransferByte(unsigned char val){unsigned char BitCounter;for(BitCounter=8; BiCounter!=0; BitCounter--){ CLK=0;DataI=0; // writeif(val&0x80) DataI=1;val<<=1;CLK=1;if(DataO)val|=1; // read}CLK=0;return val;}SD卡的初始化对SD卡进行操作首先要对SD卡进行初始化,初始化的过程中设置SD卡工作在SPI模式,其流程图如图3所示。
在复位成功之后可以通过CMD55和ACMD41判断当前电压是否在工作范围内。
主机还可以继续通过CMD10读取SD卡的CID寄存器,通过CMD16设置数据Block长度,通过CMD9读取卡的CSD寄存器。
从CSD寄存器中,主机可获知卡容量,支持的命令集等重要参数。
SD 卡初始化的C语言程序如下:unsigned char SD_Init(void){ unsigned char retry,temp;unsigned char i;for (i=0;i<0x0f;i++){ SPI_TransferByte(0xff); //延迟74个以上的时钟}SD_Enable(); //开片选SPI_TransferByte(SD_RESET); //发送复位命令SPI_TransferByte(0x00);SPI_TransferByte(0x00);SPI_TransferByte(0x00);SPI_TransferByte(0x00);SPI_TransferByte(0x95);SPI_TransferByte(0xff);SPI_TransferByte(0xff);retry=0;do{ temp=Write_Command_SD(SD_INIT,0);//发送初始化命令retry++;if(retry==100) //重试100次{SD_Disable(); //关片选return(INIT_CMD1_ERROR);//如果重试100次失败返回错误号}}while(temp!=0);SD_Disable(); //关片选return(TRUE); //返回成功}数据块的读写完成SD卡的初始化之后即可进行它的读写操作。
SD卡的读写操作都是通过发送SD卡命令完成的。
SPI总线模式支持单块(CMD24)和多块(CMD25)写操作,多块操作是指从指定位置开始写下去,直到SD卡收到一个停止命令CMD12才停止。
单块写操作的数据块长度只能是512字节。
单块写入时,命令为CMD24,当应答为0时说明可以写入数据,大小为512字节。
SD卡对每个发送给自己的数据块都通过一个应答命令确认,它为1个字节长,当低5位为00101时,表明数据块被正确写入SD卡。
在需要读取SD卡中数据的时候,读SD卡的命令字为CMD17,接收正确的第一个响应命令字节为0xFE,随后是512个字节的用户数据块,最后为2个字节的CRC验证码。
可见,读写SD卡的操作都是在初始化后基于SD卡命令和响应完成操作的,写、读SD 卡的程序流程图如图4和图5所示。
结束语实验结果表明单片机使用12MHz的晶体振荡器时,读写速度和功耗都基本令人满意,可以应用于对读写速度要求不高的情况下。
本文详细阐述了用AT89C52单片机对SD卡进行操作的过程,提出了一种不带SD卡控制器,MCU读写SD卡的方法,实现了SD卡在电能监测及无功补偿数据采集系统中的用途。
了解了指令的形式和具体的控制内容,下面主要解决的就是MCU与SD卡的通信问题,通信主要需要注意下面的问题:(1)供电电压:必须是3.3V(2) 通信模式的切换:SD卡有两种通信模式:SPI模式和SD模式,默认情况下的通信模式是SD模式,但是我们常用的模式是SPI模式,这就需要一个切换模式的方法,具体的实现方法在其他地方也都有介绍,其关键的地方就是先上电延时大于74个时钟周期后发送复位命令,复位成功(接收到0x01的响应)后,连续发送CMD55和ACMD41,直到响应0X00为止,此时SD卡已经进入SPI模式。
(3)上面所说的发送复位命令(CMD0)以及CMD55和ACMD41要有具体的实现方法,需要解决的就是时序问题,下图就为复位的时序图,只要能够按照下图的时序进行操作,肯定能够复位成功.本复位分为(1)上电,(2)延时74个周期以上,(3)发送命令CMD0,(4)发送命令参数0X0000,(5)发送CRC校验0X95,(6)等待响应(7)响应0X01此时得到正确响应复位成功,否则重复以上操作直到成功为止。
介绍复位的同时其他的命令也和复位命令类似,只是根据命令的作用不同有着不同的响应类型和不同的后续操作,下面给出集中常用命令的时序图。
(1)读CID寄存器时序图说明:当发送命令并得到响应0X00后就开始准备接收CID寄存器中的内容,此时只要接收到起始标志0XFE后,之后的16个字节的内容即为CID寄存器的内容。
(2)读CSD寄存器内容时序和读CID的类似,只是此时发送的命令为CMD9SD卡数据的读写是以块为单位:默认情况下一块的大小为512字节(3) 读SD卡一个块(512字节)时序(4) 写一个块(512字节)时序图1.SD卡的命令格式:SD卡的指令由6字节(Byte)组成,如下:Byte1:0 1 x x x x x x(命令号,由指令标志定义,如CMD39为100111即16进制0x27,那么完整的CMD39第一字节为01100111,即0x27+0x40)Byte2-5:Command Arguments,命令参数,有些命令没有参数Byte6:前7位为CRC(Cyclic Redundacy Check,循环冗余校验)校验位,最后一位为停止位0 2.SD卡的命令SD卡命令共分为12类,分别为class0到class11,不同的SDd卡,主控根据其功能,支持不同的命令集如下:Class0 :(卡的识别、初始化等基本命令集)CMD0:复位SD 卡.CMD1:读OCR寄存器.CMD9:读CSD寄存器.CMD10:读CID寄存器.CMD12:停止读多块时的数据传输CMD13:读Card_Status 寄存器Class2 (读卡命令集):CMD16:设置块的长度CMD17:读单块.CMD18:读多块,直至主机发送CMD12为止.Class4(写卡命令集) :CMD24:写单块.CMD25:写多块.CMD27:写CSD寄存器.Class5 (擦除卡命令集):CMD32:设置擦除块的起始地址.CMD33:设置擦除块的终止地址.CMD38: 擦除所选择的块.Class6(写保护命令集):CMD28:设置写保护块的地址.CMD29:擦除写保护块的地址.CMD30: Ask the card for the status of the write protection bitsclass7:卡的锁定,解锁功能命令集class8:申请特定命令集。