• 嵌入式成长轨迹36 【Zigbee项目】【单片机基础】【单片机SD卡】


    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);//写入abcdefg
    6     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 mode
    15     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 command
    10     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 data
    18         //start with DATA TOKEN = 0xFE
    19         SdWrite(0xfe);
    20         //now send data
    21         for(count=0;count<len;count++) SdWrite(*Block++);
    22         
    23         for(;count<512;count++) SdWrite(0);
    24         //data block sent - now send checksum
    25         SdWrite(0xff); //两字节CRC校验, 为0XFFFF 表示不考虑CRC
    26         SdWrite(0xff);
    27         //Now read in the DATA RESPONSE token
    28         dataResp=SdRead();
    29         //Following the DATA RESPONSE token
    30         //are a number of BUSY bytes
    31         //a zero byte indicates the MMC is busy
    32         
    33         while(SdRead()==0);
    34         
    35         dataResp=dataResp&0x0f; //mask the high byte of the DATA RESPONSE token
    36         SD_CS=1;
    37         SdWrite(0xff);
    38         if(dataResp==0x0b)
    39         {
    40         //printf("DATA WAS NOT ACCEPTED BY CARD -- CRC ERROR\n");
    41         return 0;
    42         }
    43         if(dataResp==0x05)
    44         return 1;
    45         
    46         //printf("Invalid data Response token.\n");
    47         return 0;
    48     }
    49     //printf("Command 0x18 (Write) was not received by the MMC.\n");
    50     return 0;
    51 }


    【问题】
    振南电子中说写扇区用命令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 command
    12     SdCommand(0x11,address,0xff);
    13 
    14     if(SdResponse()==00)
    15     {
    16         //command was a success - now send data
    17         //start with DATA TOKEN = 0xFE
    18         while(SdRead()!=0xfe);
    19         
    20         for(count=0;count<len;count++) *Block++=SdRead(); 
    21         
    22         for(;count<512;count++) SdRead();
    23         
    24         //data block sent - now send checksum
    25         SdRead();
    26         SdRead();
    27         //Now read in the DATA RESPONSE token
    28         SD_CS=1;
    29         SdRead();
    30         return 1;
    31     }
    32  //printf("Command 0x11 (Read) was not received by the MMC.\n");
    33     return 0;
    34 }

    【分析】
    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         else
    13         {
    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 <REGX51.H>
      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 exactly
    116     //First Lower SS
    117     
    118     SD_CS=0;
    119     //Then send write command
    120     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 data
    128         //start with DATA TOKEN = 0xFE
    129         SdWrite(0xfe);
    130         //now send data
    131         for(count=0;count<len;count++) SdWrite(*Block++);
    132         
    133         for(;count<512;count++) SdWrite(0);
    134         //data block sent - now send checksum
    135         SdWrite(0xff); //两字节CRC校验, 为0XFFFF 表示不考虑CRC
    136         SdWrite(0xff);
    137         //Now read in the DATA RESPONSE token
    138         dataResp=SdRead();
    139         //Following the DATA RESPONSE token
    140         //are a number of BUSY bytes
    141         //a zero byte indicates the MMC is busy
    142         
    143         while(SdRead()==0);
    144         dataResp=dataResp&0x0f; //mask the high byte of the DATA RESPONSE token
    145         SD_CS=1;
    146         SdWrite(0xff);
    147         if(dataResp==0x0b)
    148         {
    149         //printf("DATA WAS NOT ACCEPTED BY CARD -- CRC ERROR\n");
    150         return 0;
    151         }
    152         if(dataResp==0x05)
    153         return 1;
    154         
    155         //printf("Invalid data Response token.\n");
    156         return 0;
    157     }
    158     //printf("Command 0x18 (Write) was not received by the MMC.\n");
    159     return 0;
    160 }
    161 
    162 //=======================================================================
    163 //从SD卡指定地址读取数据,一次最多512字节
    164 unsigned char SdReadBlock(unsigned char *Block, unsigned long address,int len)
    165 {
    166     unsigned int count;
    167     //Block size is 512 bytes exactly
    168     //First Lower SS
    169     
    170      //printf("MMC_read_block\n");
    171     
    172     SD_CS=0;
    173     //Then send write command
    174     SdCommand(0x11,address,0xff);
    175 
    176     if(SdResponse()==00)
    177     {
    178         //command was a success - now send data
    179         //start with DATA TOKEN = 0xFE
    180         while(SdRead()!=0xfe);
    181         
    182         for(count=0;count<len;count++) *Block++=SdRead(); 
    183         
    184         for(;count<512;count++) SdRead();
    185         
    186         //data block sent - now send checksum
    187         SdRead();
    188         SdRead();
    189         //Now read in the DATA RESPONSE token
    190         SD_CS=1;
    191         SdRead();
    192         return 1;
    193     }
    194  //printf("Command 0x11 (Read) was not received by the MMC.\n");
    195     return 0;
    196 }
     1 //main.c
     2 #include "sd.H"
     3 #include <REGX51.H>
     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         else
    33         {
    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);//写入abcdefg
    46     SdReadBlock(DATA,0x000000,7);
    47     Sen_String(DATA);
    48     while(1);
    49 }
  • 相关阅读:
    CF1462E2 Solution
    CF1450D Solution
    CF1451D Solution
    CF1442B Solution
    CF1453C Solution
    CF1455D Solution
    linux服务器部署node项目
    原生javascript实现 hasClass addClass removeClass
    图片加载完执行函数
    MySQL
  • 原文地址:https://www.cnblogs.com/zeedmood/p/2666882.html
Copyright © 2020-2023  润新知