博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
嵌入式成长轨迹36 【Zigbee项目】【单片机基础】【单片机SD卡】
阅读量:4709 次
发布时间:2019-06-10

本文共 10800 字,大约阅读时间需要 36 分钟。

 

SD卡的驱动有两种模式,SD模式与SPI模式,用单片机驱动时常使用SPI模式,一方面容易实现,另一方面操作数据量并不是很大,速度要求不高。

SD卡工作电压时3.3V,在SPI模式时只需要4根信号线,即CS片选、DIN数据输入、CLK时钟、DOUT数据输出。

问题:

代码运行时出现:main.c(1): warning C318: can't open file 'REGX51.H'

分析:

在sd.h里边定义了管脚
//定义SD卡需要的4根信号线
sbit SD_CLK = P1^0;
sbit SD_DI  = P1^2;
sbit SD_DO  = P1^1;
sbit SD_CS  = P1^3;

//main.c

#include <REGX51.H>
#include "sd.H"
#define F_OSC  11059200//晶振频率Hz
#define F_BAUD 9600
#define RELOAD 256-F_OSC/12/32/F_BAUD
#define CR 0x0D        //回车
unsigned char xdata DATA[512];

main代码如下:

1 void main()2 {3     UART();4     while(!SdInit());5     SdWriteBlock("ABCDEFG",0x000000,7);//写入abcdefg6     SdReadBlock(DATA,0x000000,7);7     Sen_String(DATA);8     while(1);9 }

 

【分析】
main做了这些事情:
串口初始化
SD卡初始化
写入字符串abcdefg
读取字符串
向串口发送字符串
死循环

其中UART函数如下:

1 /******************************************* 2          串口初始化 3 *******************************************/ 4 void UART() 5 { 6     SCON=0x40;//工作方式1,不允许接受串口数据 7     TMOD=0x20;//定时器1工作于方式2自动重装模式 8     TH1=RELOAD; 9     TR1=1;10     TI=0;   11 }

 

【问题与分析】
TMOD TH1分别是指什么?
答:P10
TMOD用了控制和设定定时器的工作方式和4种工作模式。低四位用于T0,高四位用于T1。
TH1用于保存定时器T1的初值,TL1用于计数,TL1溢出时,若在模式2则会自动重装TH1中的初值。
这里TMOD为0x20,也即0010 0000,每个定时器各四位,分别指GATE C/~T M1 M0.
对定时器T1,GATE=0,定时器启动与中断无关;C/~T为0,工作在定时方式,以单片机机器周期为计数脉冲;M1=1,M0=0,工作在模式2,常数自动装入.
对定时器T2,GATE=0,定时器启动与中断无关;C/~T为0,工作在定时方式,以单片机机器周期为计数脉冲;M1=0,M0=0,工作在模式0,13位定时/计数器.

TI是指什么?

答:TI是串口中断发送标志 (P377上方)

SCON是指什么?P376

答:SCON是串口控制寄存器。 SCON的八位分别是SM0 SM1 SM2 REN TB8 RB8 TI RI.
这里SCON八位被设为0x40,也即0100 0000.SM0 SM1为01,表示采用模式1,也即10位异步收发模式,数据传输率由定时器控制;模式SM2为多机通信控制位,在模式0 1下不应使用,应置为0;REN是允许接收位,为0禁止串口接收;TB8 RB8分别代表数据发送 接收第九位,主要用于模式2、3。在模式1中,若SM2为0,则RB8用于存放接收到的停止位。TI、RI分别是发送、接收中断标志位,用于指示一帧数据是否发送、接收完毕,都由软件复位、硬件置位。

TR1是指什么?

答:P11 T1定时器的运行控制位,为1时开始计时

TH1怎么计算的(跟P379公式,数据传输率=2^SMOD*f_osc/32/12/(2^k-初值)有关吗)?

SdInit函数如下:

1 //sd.h 2 //初始化SD卡 3 unsigned char SdInit(void) 4 { 5     int delay=0, trials=0; 6     unsigned char i; 7     unsigned char response=0x01; 8      9     SD_CS=1;10     for(i=0;i<=9;i++)11     SdWrite(0xff);12     SD_CS=0;13     14     //Send Command 0 to put MMC in SPI mode15     SdCommand(0x00,0,0x95);16     17     18     response=SdResponse();19     20     if(response!=0x01)21     {22         return 0;23     } 24 25     while(response==0x01)26     {27         SD_CS=1;28         SdWrite(0xff);29         SD_CS=0;30         SdCommand(0x01,0x00ffc000,0xff);31         response=SdResponse();32     } 33 34     SD_CS=1;35     SdWrite(0xff);36     return 1; 37 }

 

【问题】
1、振南电子说初始化响应信号时0x00,但是这里边的响应信号却写0x01,为什么?
答:这里的响应信号是指复位的响应,复位响应信号就是0x01。如果复位失败,那初始化必然失败。

2、最后命令执行完毕后,为什么还要再写入一次校验码?而且为什么不检查初始化响应信号0x00?

再写入一次校验码是为了稳定起见,再输入8个时钟信号,这个信号内容没有什么特殊含义,只是为了输入8个时钟附带的。

【分析】

SdInit做了这些事情:
SdWrite(0xff);先写入命令,0xff是表示不使用校验码
SdCommand(0x00,0,0x95);执行0x95复位命令
response=SdResponse(); if(response!=0x01) return 0;如果复位失败,则返回0
SdCommand(0x01,0x00ffc000,0xff);接下来执行初始化命令
SdWrite(0xff);写入表示不使用的校验码

其中,SdWrite函数如下:

1 //写一字节到SD卡,模拟SPI总线方式 2 void SdWrite(unsigned char n) 3 { 4  5     unsigned char i; 6      7     for(i=8;i;i--) 8     { 9         SD_CLK=0;10         SD_DI=(n&0x80);11         n<<=1;12         SD_CLK=1;13         }14         SD_DI=1; 15     } 16 }

 

【问题】
为什么SD_DI要和0x80做&操作,来标记后面七位呢?又为什么要左移一位?

SdCommand函数如下:

1 void SdCommand(unsigned char command, unsigned long argument, unsigned char CRC) 2 { 3  4     SdWrite(command|0x40); 5     SdWrite(((unsigned char *)&argument)[0]); 6     SdWrite(((unsigned char *)&argument)[1]); 7     SdWrite(((unsigned char *)&argument)[2]); 8     SdWrite(((unsigned char *)&argument)[3]); 9     SdWrite(CRC);10 }

 

【分析】

命令格式:命令|参数|CRC校验码
所有命令是从0x40开始的,使用或操作的话,只要输入命令0、1即可,不用记忆那些个奇怪的0x40、0x41...
中间四个字节是参数,不使用参数的时候也可记0.
最后是CRC校验码。

SdResponse函数如下:

1 //检测SD卡的响应 2 unsigned char SdResponse() 3 { 4     unsigned char i=0,response; 5      6     while(i<=8) 7     { 8         response = SdRead(); 9         if(response==0x00)10         break;11         if(response==0x01)12         break;13         i++;14     }15     return response;16 }

 

【分析】
这里只return两种,一是0x00,二是0x01,除了这些都是不正常的返回信号。

 

SdRead函数如下:

1 //从SD卡读一字节,模拟SPI总线方式 2 unsigned char SdRead() 3 { 4     unsigned char n,i; 5     for(i=8;i;i--) 6     { 7         SD_CLK=0; 8         SD_CLK=1; 9         n<<=1;10         if(SD_DO) n|=1;11     12     }13     return n;14 }

 

【问题】
为什么要左移?

【分析】

读取的时候,如果SD_DO没有被拉低,那么n就是全1

SdWriteBlock函数代码如下:

1 unsigned char SdWriteBlock(unsigned char *Block, unsigned long address,int len) 2 { 3     unsigned int count; 4     unsigned char dataResp; 5     //Block size is 512 bytes exactly 6     //First Lower SS 7      8     SD_CS=0; 9     //Then send write command10     SdCommand(0x18,address,0xff);11     12     if(SdResponse()==00)13     {14         SdWrite(0xff);15         SdWrite(0xff);16         SdWrite(0xff);17         //command was a success - now send data18         //start with DATA TOKEN = 0xFE19         SdWrite(0xfe);20         //now send data21         for(count=0;count

 

【问题】
振南电子中说写扇区用命令24,但为什么这里用命令18?
答:这里还是用命令24,命令24是0x58,第1个命令是0x40,0x58-0x40=0x18

【分析】

SdWriteBlock做了这些事情:
SdCommand(0x18,address,0xff);执行写命令0x58
if(SdResponse()==00) 命令被写入成功
SdWrite(0xff); 给入若干时钟周期(100个可以了)
SdWrite(0xfe); 写入开始字节
SdWrite(*Block++); 写入字符
SdWrite(0xff); SdWrite(0xff);两字节CRC校验
dataResp=SdRead();while(SdRead()==0); 读字节,如果字节为0,则是忙状态。
dataResp=dataResp&0x0f;标记高四位
SdWrite(0xff);再给一个周期
if(dataResp==0x0b) return 0;读得CRC为0x0b则发生错误
if(dataResp==0x05) return 1;正确

SdReadBlock函数如下:

1 //从SD卡指定地址读取数据,一次最多512字节 2 unsigned char SdReadBlock(unsigned char *Block, unsigned long address,int len) 3 { 4     unsigned int count; 5     //Block size is 512 bytes exactly 6     //First Lower SS 7      8      //printf("MMC_read_block\n"); 9     10     SD_CS=0;11     //Then send write command12     SdCommand(0x11,address,0xff);13 14     if(SdResponse()==00)15     {16         //command was a success - now send data17         //start with DATA TOKEN = 0xFE18         while(SdRead()!=0xfe);19         20         for(count=0;count

 

【分析】

SdCommand(0x11,address,0xff);执行读命令0x51
if(SdResponse()==00) 命令被成功写入
while(SdRead()!=0xfe);不停读取,直到开始字节
*Block++=SdRead();读取数据
SdRead();SdRead();两个CRC校验码的读取,不用作处理
SdRead();补充8个时钟周期

Sen_String函数如下:

1 /******************************************* 2          发送字符串 3 *******************************************/ 4 void Sen_String(unsigned char *string) 5 { 6     while(*string!='\0') 7     { 8         if(*string=='\n') 9         {10             SBUF=CR;11         }12         else13         {14             SBUF=*string;15         }16         while(TI==0);17         TI=0;18         string++;19     }20 }

 

【问题】
SBUF、TI、CR是什么?
答:
SBUF是在数据收发过程中,串口收发数据暂存的地方 P376
TI是串口中断发送标志

为什么一定要把TI改为0?

答:表示不再中断

【分析】

CR为0x0D,也即回车

【完整代码】

1 //sd.h  2 #include 
3 //定义SD卡需要的4根信号线 4 sbit SD_CLK = P1^0; 5 sbit SD_DI = P1^2; 6 sbit SD_DO = P1^1; 7 sbit SD_CS = P1^3; 8 //定义512字节缓冲区,注意需要使用 xdata关键字 9 10 11 //=========================================================== 12 //写一字节到SD卡,模拟SPI总线方式 13 void SdWrite(unsigned char n) 14 { 15 16 unsigned char i; 17 18 for(i=8;i;i--) 19 { 20 SD_CLK=0; 21 SD_DI=(n&0x80); 22 n<<=1; 23 SD_CLK=1; 24 } 25 SD_DI=1; 26 } 27 28 //=========================================================== 29 //从SD卡读一字节,模拟SPI总线方式 30 unsigned char SdRead() 31 { 32 unsigned char n,i; 33 for(i=8;i;i--) 34 { 35 SD_CLK=0; 36 SD_CLK=1; 37 n<<=1; 38 if(SD_DO) n|=1; 39 40 } 41 return n; 42 } 43 //============================================================ 44 //检测SD卡的响应 45 unsigned char SdResponse() 46 { 47 unsigned char i=0,response; 48 49 while(i<=8) 50 { 51 response = SdRead(); 52 if(response==0x00) 53 break; 54 if(response==0x01) 55 break; 56 i++; 57 } 58 return response; 59 } 60 //================================================================ 61 //发命令到SD卡 62 void SdCommand(unsigned char command, unsigned long argument, unsigned char CRC) 63 { 64 65 SdWrite(command|0x40); 66 SdWrite(((unsigned char *)&argument)[0]); 67 SdWrite(((unsigned char *)&argument)[1]); 68 SdWrite(((unsigned char *)&argument)[2]); 69 SdWrite(((unsigned char *)&argument)[3]); 70 SdWrite(CRC); 71 } 72 //================================================================ 73 //初始化SD卡 74 unsigned char SdInit(void) 75 { 76 int delay=0, trials=0; 77 unsigned char i; 78 unsigned char response=0x01; 79 80 SD_CS=1; 81 for(i=0;i<=9;i++) 82 SdWrite(0xff); 83 SD_CS=0; 84 85 //Send Command 0 to put MMC in SPI mode 86 SdCommand(0x00,0,0x95); 87 88 89 response=SdResponse(); 90 91 if(response!=0x01) 92 { 93 return 0; 94 } 95 96 while(response==0x01) 97 { 98 SD_CS=1; 99 SdWrite(0xff);100 SD_CS=0;101 SdCommand(0x01,0x00ffc000,0xff);102 response=SdResponse();103 } 104 105 SD_CS=1;106 SdWrite(0xff);107 return 1; 108 }109 //================================================================110 //往SD卡指定地址写数据,一次最多512字节111 unsigned char SdWriteBlock(unsigned char *Block, unsigned long address,int len)112 {113 unsigned int count;114 unsigned char dataResp;115 //Block size is 512 bytes exactly116 //First Lower SS117 118 SD_CS=0;119 //Then send write command120 SdCommand(0x18,address,0xff);121 122 if(SdResponse()==00)123 {124 SdWrite(0xff);125 SdWrite(0xff);126 SdWrite(0xff);127 //command was a success - now send data128 //start with DATA TOKEN = 0xFE129 SdWrite(0xfe);130 //now send data131 for(count=0;count

 

1 //main.c 2 #include "sd.H" 3 #include 
4 5 #define F_OSC 11059200//晶振平率Hz 6 #define F_BAUD 9600 7 #define RELOAD 256-F_OSC/12/32/F_BAUD 8 #define CR 0x0D //回车 9 unsigned char xdata DATA[512];10 /*******************************************11 串口初始化12 *******************************************/13 void UART()14 {15 SCON=0x40;//工作与方式1不允许接受16 TMOD=0x20;//定时器1工作与方式2自动重装模式17 TH1=RELOAD;18 TR1=1;19 TI=0; 20 }21 /*******************************************22 发送字符串23 *******************************************/24 void Sen_String(unsigned char *string)25 {26 while(*string!='\0')27 {28 if(*string=='\n')29 {30 SBUF=CR;31 }32 else33 {34 SBUF=*string;35 }36 while(TI==0);37 TI=0;38 string++;39 }40 }41 void main()42 {43 UART();44 while(!SdInit());45 SdWriteBlock("ABCDEFG",0x000000,7);//写入abcdefg46 SdReadBlock(DATA,0x000000,7);47 Sen_String(DATA);48 while(1);49 }

 

 

转载于:https://www.cnblogs.com/zeedmood/archive/2012/09/01/2666882.html

你可能感兴趣的文章
机器学习 - pycharm, pyspark, spark集成篇
查看>>
mysql explain 中key_len的计算
查看>>
实验一
查看>>
Linux内核--网络栈实现分析(九)--传输层之UDP协议(下)
查看>>
Lua -- 简洁、轻量、可扩展的脚本语言
查看>>
Python 2.7_Second_try_爬取阳光电影网_获取电影下载地址并写入文件 20161207
查看>>
[Fiddler] 开启Fiddler抓包的时候产品报“证书错误”
查看>>
打包苦逼活
查看>>
Oracle Certified Java Programmer 经典题目分析(二)
查看>>
第二十五章补充内容 17位字段
查看>>
灰色预测
查看>>
css随笔
查看>>
基于自己封装的select下拉选择的省市区三级联动效果,兼容IE
查看>>
初识Python
查看>>
nodejs+mysql入门实例(改)
查看>>
表达式语言
查看>>
jQuery EasyUI实现关闭全部tabs
查看>>
iOS项目之WKWebView替换UIWebView相关
查看>>
Lambda表达式效率问题
查看>>
【转载】iOS 设置Launch Image 启动图片(适用iOS9)
查看>>