zoukankan      html  css  js  c++  java
  • [原创][连载].基于SOPC的简易数码相框 Nios II SBTE部分(软件部分) SD卡(SPI模式)驱动

    上一讲,我们完成了Nios II SBTE的配置工作。下面讲解如何根据已有参考资料(手册及代码)编写SD卡驱动。

    准备工具及资料

    1. WinHex

    2. Efronc的博文SD/MMC 接口及上电时序SD/MMC 内部寄存器SD/MMC SPI模式下命令集

    驱动编写及调试

    步骤1 添加sd_card文件夹到APP工程路径

    如何添加,请参考[原创][连载].基于SOPC的简易数码相框 – Nios II SBTE部分(软件部分) - 配置工作

    image image

    步骤2 编写代码

    SD卡有很多标准,此处选用最简单的SD 1-线模式,即SPI模式。

    代码2.1 sd_card.h

    #ifndef SD_CARD_H_
    #define SD_CARD_H_
    
    
    #include "my_types.h"
    #include "my_regs.h"
    
    
    #define ENABLE_SD_CARD_DEBUG // turn on debug message
    
    
    void SD_CARD_Port_Init();
    void SD_CARD_Write_Byte(u8 byte);
    u8 SD_CARD_Read_Byte();
    u8 SD_CARD_Write_CMD(u8 *CMD);
    //
    u8 SD_CARD_Init();
    u8 SD_CARD_Write_Sector(u32 addr,u8 *buf);
    u8 SD_CARD_Read_Sector(u8 *CMD,u8 *buf,u16 n_bytes);
    u8 SD_CARD_Read_Sector_Start(u32 sector);
    void SD_CARD_Read_Data(u16 n_bytes,u8 *buf);
    void SD_CARD_Read_Data_LBA(u32 LBA,u16 n_bytes,u8 *buf);
    void SD_CARD_Read_Sector_End();
    u8 SD_CARD_Read_CSD(u8 *buf);
    u8 SD_CARD_Read_CID(u8 *buf);
    void SD_CARD_Get_Info(void);
    void SD_CARD_DEMO(void);
    
    
    #endif /* SD_CARD_H_ */
    

    第5~6行,加入自定义的宏,统一代码风格。第9行,打开调试信息显示开关。调试正确后,可用添加注释的方式的关闭开关。

    第12行void SD_CARD_Port_Init(),为SPI接口的初始函数。

    第13~14行void SD_CARD_Write_Byte(u8 byte)和u8 SD_CARD_Read_Byte(),为SPI写字节和读字节函数。

    第15行u8 SD_CARD_Write_CMD(u8 *CMD),为SD卡写命令函数。

    第17行u8 SD_CARD_Init(),为SD卡的初始化函数。这个函数需要特别注意,因为SPI模式的模式的SD卡需要低速率收发数据来初始化SD卡。

    第18~19行u8 SD_CARD_Write_Sector(u32 addr,u8 *buf)和u8 SD_CARD_Read_Sector(u8 *CMD,u8 *buf,u16 n_bytes)为SD卡写块和读块函数;需要注意的是,一般的SD卡的块有512字节,而通过WinHex 查看的SD卡的每个扇区也是512字节。为了统一风格,此处一律写作Sector。

    第21行void SD_CARD_Read_Data_LBA(u32 LBA,u16 n_bytes,u8 *buf),比较好用,其参数LBA为Winhex中可查看的扇区地址,及逻辑块地址;有了这个函数,我们后续的工作就方便了需要。

    第24~25行u8 SD_CARD_Read_CSD(u8 *buf)和u8 SD_CARD_Read_CID(u8 *buf),为读取SD卡的CSD和CID寄存器函数。

    其他的函数请参考源代码自行解析。

    代码2.2 sd_card.c

    #include <unistd.h>
    #include "sd_card.h"
    
    
    // debug switch
    #ifdef ENABLE_SD_CARD_DEBUG
      #include "debug.h"
      #define SD_CARD_DEBUG(x)  DEBUG(x)
    #else
      #define SD_CARD_DEBUG(x)
    #endif
    
    
    // error macro
    #define INIT_CMD0_ERROR   0x01
    #define INIT_CMD1_ERROR   0x02
    #define WRITE_BLOCK_ERROR 0x03
    #define READ_BLOCK_ERROR  0x04
    
    
    // SD-CARD(SPI mode) initial with low speed
    // insert a certain delay
    #define SD_CARD_INIT_DELAY usleep(10)
    
    
    // CID info structure
    typedef union
    {
      u8 data[16];
      struct
      {
        u8 MID;   // Manufacture ID; Binary
        u8 OLD[2];// OEM/Application ID; ASCII
        u8 PNM[5];// Product Name; ASCII
        u8 PRV;   // Product Revision; BCD
        u8 PSN[4];// Serial Number; Binary
        u8 MDT[2];// Manufacture Data Code; BCD; upper 4 bits of first byte are reserved
        u8 CRC;   // CRC7_checksum; Binary; LSB are reserved
      };
    }CID_Info_STR;
    
    
    // CSD info structure
    typedef struct
    {
      u8 data[16];
      u32 capacity_MB;
      u8 READ_BL_LEN;
      u16 C_SIZE;
      u8 C_SIZE_MULT;
    }CSD_Info_STR;
    
    
    // flags
    u16 gByteOffset=0;       // byte offset in one sector
    u16 gSectorOffset=0;     // sector offset in SD-CARD
    bool gSectorOpened=FALSE;// set to 1 when a sector is opened.
    bool gSD_CARDInit=FALSE; // set it to 1 when SD-CARD is initialized
    
    
    // SD-CARD port init
    void SD_CARD_Port_Init()
    {
      sd_CLK=1;
      sd_DOUT=1;
      sd_nCS=1;
    }
    
    
    // write a byte to SD-CARD
    void SD_CARD_Write_Byte(u8 byte)
    {
      u8 i;
      for(i=0;i<8;i++)
      { // MSB First
        sd_DIN=(byte >> (7-i)) & 0x1;
        sd_CLK=0;if(gSD_CARDInit) SD_CARD_INIT_DELAY;
        sd_CLK=1;if(gSD_CARDInit) SD_CARD_INIT_DELAY;
      }
    }
    
    
    // read a byte to SD-CARD
    u8 SD_CARD_Read_Byte()
    {
      u8 i,byte;
      byte=0;
      for(i=0;i<8;i++)
      { // MSB First
        sd_CLK=0;if(gSD_CARDInit) SD_CARD_INIT_DELAY;
        byte<<=1;if(sd_DOUT) byte++;
        sd_CLK=1;if(gSD_CARDInit) SD_CARD_INIT_DELAY;
      }
      return byte;
    }
    
    
    // write a command to SD-CARD
    // return: the second byte of response register of SD-CARD
    u8 SD_CARD_Write_CMD(u8 *CMD)
    {
      u8 temp,retry;
      u8 i;
    
      sd_nCS=1; // set chipselect (disable SD-CARD)
      SD_CARD_Write_Byte(0xFF); // send 8 clock impulse
      sd_nCS=0; // clear chipselect (enable SD-CARD)
    
      // write 6 bytes command to SD-CARD
      for(i=0;i<6;i++) SD_CARD_Write_Byte(*CMD++);
    
      // get 16 bits response
      SD_CARD_Read_Byte(); // read the first byte, ignore it.
      retry=0;
      do
      { // only last 8 bits is valid
        temp=SD_CARD_Read_Byte();
        retry++;
      }
      while((temp==0xff) && (retry<100));
      return temp;
    }
    
    
    // SD-CARD initialization(SPI mode)
    u8 SD_CARD_Init()
    {
      u8 retry,temp;
      u8 i;
      u8 CMD[]={0x40,0x00,0x00,0x00,0x00,0x95};
    
      SD_CARD_Port_Init();  
      usleep(1000);
    
      SD_CARD_DEBUG(("SD-CARD Init!\n"));
      gSD_CARDInit=TRUE; // Set init flag of SD-CARD
      
      for(i=0;i<10;i++) SD_CARD_Write_Byte(0xff);// send 74 clock at least!!!
    
      // write CMD0 to SD-CARD
      retry=0;
      do
      { // retry 200 times to write CMD0
        temp=SD_CARD_Write_CMD(CMD);
        retry++;
        if(retry==200) return INIT_CMD0_ERROR;// CMD0 error!
      }
      while(temp!=1);
    
      //write CMD1 to SD-CARD
      CMD[0]=0x41;// Command 1
      CMD[5]=0xFF;
      retry=0;
      do
      { // retry 100 times to write CMD1
        temp=SD_CARD_Write_CMD(CMD);
        retry++;
        if(retry==100)  return INIT_CMD1_ERROR;// CMD1 error!
      }
      while(temp!=0);
    
      gSD_CARDInit=FALSE; // clear init flag of SD-CARD
    
      sd_nCS=1; // disable SD-CARD
      SD_CARD_DEBUG(("SD-CARD Init Suc!\n"));
      return 0x55;// All commands have been taken.
    }
    
    
    // writing a Block(512Byte, 1 sector) to SD-CARD
    // return 0 if sector writing is completed.
    u8 SD_CARD_Write_Sector(u32 addr,u8 *buf)
    {
      u8 temp,retry;
      u16 i;
    
      // CMD24 for writing blocks
      u8 CMD[]={0x58,0x00,0x00,0x00,0x00,0xFF};
      SD_CARD_DEBUG(("Write A Sector Starts!!\n"));
    
      addr=addr << 9;// addr=addr * 512
    
      CMD[1]=((addr & 0xFF000000) >>24 );
      CMD[2]=((addr & 0x00FF0000) >>16 );
      CMD[3]=((addr & 0x0000FF00) >>8 );
    
      // write CMD24 to SD-CARD(write 1 block/512 bytes, 1 sector)
      retry=0;
      do
      { // retry 100 times to write CMD24
        temp=SD_CARD_Write_CMD(CMD);
        retry++;
        if(retry==100) return(temp);//CMD24 error!
      }
      while(temp!=0);
    
      // before writing, send 100 clock to SD-CARD
      for(i=0;i<100;i++) SD_CARD_Read_Byte();
    
      // write start byte to SD-CARD
      SD_CARD_Write_Byte(0xFE);
    
      SD_CARD_DEBUG(("\n"));
      // now write real bolck data(512 bytes) to SD-CARD
      for(i=0;i<512;i++) SD_CARD_Write_Byte(*buf++);
    
      SD_CARD_DEBUG(("CRC-Byte\n"));
      SD_CARD_Write_Byte(0xFF);// dummy CRC
      SD_CARD_Write_Byte(0xFF);// dummy CRC
    
      // read response
      temp=SD_CARD_Read_Byte();
      if( (temp & 0x1F)!=0x05 ) // data block accepted ?
      {
        sd_nCS=1; // disable SD-CARD
        return WRITE_BLOCK_ERROR;// error!
      }
    
      // wait till SD-CARD is not busy
      while(SD_CARD_Read_Byte()!=0xff){};
    
      sd_nCS=1; // disable SD-CARD
    
      SD_CARD_DEBUG(("Write Sector suc!!\n"));
      return 0;
    }
    
    
    // read bytes in a block(normally 512KB, 1 sector) from SD-CARD
    // return 0 if no error.
    u8 SD_CARD_Read_Sector(u8 *CMD,u8 *buf,u16 n_bytes)
    {
      u16 i;
      u8 retry,temp;
    
      // write CMD to SD-CARD
      retry=0;
      do
      { // Retry 100 times to write CMD
        temp=SD_CARD_Write_CMD(CMD);
        retry++;
        if(retry==100) return READ_BLOCK_ERROR;// block read error!
      }
      while(temp!=0);
    
      // read start byte form SD-CARD (0xFE/Start Byte)
      while(SD_CARD_Read_Byte()!=0xfe);
    
      // read bytes in a block(normally 512KB, 1 sector) from SD-CARD
      for(i=0;i<n_bytes;i++)  *buf++=SD_CARD_Read_Byte();
    
      SD_CARD_Read_Byte();// dummy CRC
      SD_CARD_Read_Byte();// dummy CRC
    
      sd_nCS=1; // disable SD-CARD
      return 0;
    }
    
    
    // return: [0]-success or something error!
    u8 SD_CARD_Read_Sector_Start(u32 sector)
    {
      u8 retry;
      // CMD16 for reading Blocks
      u8 CMD[]={0x51,0x00,0x00,0x00,0x00,0xFF};
      u8 temp;
    
      // address conversation(logic block address-->byte address)
      sector=sector << 9;// sector=sector * 512
      CMD[1]=((sector & 0xFF000000) >>24 );
      CMD[2]=((sector & 0x00FF0000) >>16 );
      CMD[3]=((sector & 0x0000FF00) >>8 );
    
      // write CMD16 to SD-CARD
      retry=0;
      do
      {
        temp=SD_CARD_Write_CMD(CMD);
        retry++;
        if(retry==100) return READ_BLOCK_ERROR;// READ_BLOCK_ERROR
      }
      while( temp!=0 );
      
      // read start byte form SD-CARD (feh/start byte)
      while (SD_CARD_Read_Byte() != 0xfe);
    
      SD_CARD_DEBUG(("Open a Sector Succ!\n"));
      gSectorOpened=TRUE;
      return 0;
    }
    
    
    void SD_CARD_Read_Data(u16 n_bytes,u8 *buf)
    {
      u16 i;
      for(i=0;((i<n_bytes) && (gByteOffset<512));i++)
      {
        *buf++=SD_CARD_Read_Byte();
        gByteOffset++;// increase byte offset in a sector
      }
      if(gByteOffset==512)
      { 
        SD_CARD_Read_Byte(); // Dummy CRC
        SD_CARD_Read_Byte(); // Dummy CRC
        gByteOffset=0;       // clear byte offset in a sector
        gSectorOffset++;     // one sector is read completely
        gSectorOpened=FALSE; // set to 1 when a sector is opened
        sd_nCS=1;            // disable SD-CARD
      }
    }
    
    
    // read block date by logic block address(sector offset)
    void SD_CARD_Read_Data_LBA(u32 LBA,u16 n_bytes,u8 *buf)
    { // if one sector is read completely; open the next sector
      if(gByteOffset==0) SD_CARD_Read_Sector_Start(LBA);
      SD_CARD_Read_Data(n_bytes,buf);
    }
    
    
    // dummy read out the rest bytes in a sector
    void SD_CARD_Read_Sector_End()
    {
      u8 temp[1];
      while((gByteOffset!=0x00) | (gSectorOpened==TRUE))
        SD_CARD_Read_Data(1,temp); // dummy read  
    }
    
    
    // read CSD registers of SD-CARD
    // return 0 if no error.
    u8 SD_CARD_Read_CSD(u8 *buf)
    { // command for reading CSD registers
      u8 CMD[]={0x49,0x00,0x00,0x00,0x00,0xFF};
      return SD_CARD_Read_Sector(CMD,buf,16);// read 16 bytes
    }
    
    
    // read CID register of SD-CARD
    // return 0 if no error.
    u8 SD_CARD_Read_CID(u8 *buf)
    { // command for reading CID registers
      u8 CMD[]={0x4A,0x00,0x00,0x00,0x00,0xFF};
      return SD_CARD_Read_Sector(CMD,buf,16);//read 16 bytes
    }
    
    
    void SD_CARD_Get_Info(void)
    {
      CID_Info_STR CID;
      CSD_Info_STR CSD;
    
      SD_CARD_Read_CID(CID.data);
      SD_CARD_DEBUG(("SD-CARD CID:\n"));
      SD_CARD_DEBUG(("  Manufacturer ID(MID): 0x%.2X\n", CID.MID));
      SD_CARD_DEBUG(("  OEM/Application ID(OLD): %c%c\n", CID.OLD[0], CID.OLD[1]));
      SD_CARD_DEBUG(("  Product Name(PNM): %c%c%c%c%c\n", CID.PNM[0], CID.PNM[1], CID.PNM[2], CID.PNM[3], CID.PNM[4]));
      SD_CARD_DEBUG(("  Product Revision: 0x%.2X\n", CID.PRV));
      SD_CARD_DEBUG(("  Serial Number(PSN): 0x%.2X%.2X%.2X%.2X\n", CID.PSN[0], CID.PSN[1], CID.PSN[2], CID.PSN[3]));
      SD_CARD_DEBUG(("  Manufacture Date Code(MDT): 0x%.1X%.2X\n", CID.MDT[0] & 0x0F, CID.MDT[1]));
      SD_CARD_DEBUG(("  CRC-7 Checksum(CRC7):0x%.2X\n", CID.CRC >> 1));
    
      SD_CARD_Read_CSD(CSD.data);
      CSD.C_SIZE = ((CSD.data[6]&0x03) << 10) | (CSD.data[7] << 2) | ((CSD.data[8]&0xC0) >>6);
      CSD.C_SIZE_MULT = ((CSD.data[9]&0x03) << 1) | ((CSD.data[10]&0x80) >> 7);
      CSD.READ_BL_LEN = (CSD.data[5]&0x0F);
      CSD.capacity_MB = (((CSD.C_SIZE)+1) << (((CSD.C_SIZE_MULT) +2) + (CSD.READ_BL_LEN))) >> 20;
      SD_CARD_DEBUG(("SD-CARD CSD:\n"));
      SD_CARD_DEBUG(("  max.read data block length: %d\n", 1<<CSD.READ_BL_LEN));
      SD_CARD_DEBUG(("  device size: %d\n", CSD.C_SIZE));
      SD_CARD_DEBUG(("  device size multiplier: %d\n", CSD.C_SIZE_MULT));
      SD_CARD_DEBUG(("  device capacity: %d MB\n", CSD.capacity_MB));
    }
    
    
    void SD_CARD_DEMO(void)
    {
      u16 i;
      u8 buf[512];
    
      // init SD-CARD
      while(SD_CARD_Init() != 0x55);
      // Get CID & CSD
      SD_CARD_Get_Info();
      // read the 1st block(sector) of SD-Card
      SD_CARD_Read_Data_LBA(0,512,buf);
      for(i=0; i<512; i++)
      {
        SD_CARD_DEBUG(("%.2X ", buf[i]));
        if((i+1) % 16 == 0) SD_CARD_DEBUG(("\n"));
      }
    }

    源码很长,我简单说明其中比较重要的几点。

    第58行,申明一个bool型的全局变量bool gSD_CARDInit=FALSE;我们在u8 SD_CARD_Init()函数中将此变量置一或清零,然后在函数void SD_CARD_Write_Byte(u8 byte)和u8 SD_CARD_Read_Byte()检测此变量,以实现慢速率SPI初始化SD卡。

    我们拿void SD_CARD_Write_Byte(u8 byte)做说明。

    // read a byte to SD-CARD
    u8 SD_CARD_Read_Byte()
    {
      u8 i,byte;
      byte=0;
      for(i=0;i<8;i++)
      { // MSB First
        sd_CLK=0;if(gSD_CARDInit) SD_CARD_INIT_DELAY;
        byte<<=1;if(sd_DOUT) byte++;
        sd_CLK=1;if(gSD_CARDInit) SD_CARD_INIT_DELAY;
      }
      return byte;
    }
    

    由于是采用GPIO模拟SPI总线,而Nios II(100MHz nios/f)的GPIO比较慢,因此无需延时即可实现25MHz的速率。但是初始化SD卡的时候必须采用

    低于400KHz的时钟,需要插入适当延时。我以前也说过Nios II的延时不准,故此延时需要多次调试。我在第23行,使用一个宏来设定需要插入的延时。

    由于CID寄存器的信息与字节比较对齐,因此第27~40行,使用了联合体来储存CID寄存器。而CSD寄存器内容比较零散,就没有采用联合体,而是使用了结构体(第44~51行)来存储信息。这样做的目的主要是为了理解方便,但是对存储器是比较浪费的。

    第326行void SD_CARD_DEMO(void)函数中,先初始化SD卡,然后读取其第0个块(扇区)的内容。

    关于SD(SPI)的寄存器结构、存储结构和指令体系等,请自行认真阅读相关资料,此处不解析。

    步骤3 调用SD卡驱动函数

    代码3.1 main.c

    #include <stdio.h>                    // printf()
    #include <unistd.h>                   // usleep()
    #include "my_types.h"                 // 数据类型
    #include "debug.h"                    // debug
    #include "sd_card.h"
    
    
    #define ENABLE_APP_DEBUG // turn on debug message
    #ifdef ENABLE_APP_DEBUG
        #define APP_DEBUG(x)    DEBUG(x)
    #else
        #define APP_DEBUG(x)
    #endif
    
    
    int main(void)
    {
      SD_CARD_DEMO();
      while(1)
      {
      }
      return 0;
    }
    

    jtag-uart打印的信息截图如下。

    image (黄色为SD卡初始化调试信息;绿色为CID寄存器信息;青色为CSD寄存器信息)

    image (第0扇区的内容)

    下面我们通过WinHex读取SD卡的第一扇区的内容,注意与上图对比。

    image 

    对比数据显示,SD_CARD_Read_Data_LBA函数可实现SD卡块读取动作。

    其他问题

    下面讲下如何在SD卡内读取二进制文件。我先使用Notspad++(或记事本)新建一个文件,保存为SD卡的某个位置,命名为test.bin。

    简单起见,我直接把sd_card.h另存到我的SD卡内(FAT32格式),命名为test.bin。

    image 

    image  (查看test.bin的属性)

    在FAT16/32内,文件的数据总是从某个扇区的0字节开始连续存储的,若文件较大则需要连续存储n个扇区;需要注意的是最后的一个扇区如果没有存满,则补0。上面我们通过查看属性,得知test.bin的文件大小为718字节,即需要占用718/512=1.4,取2,即2个扇区。下面使用WinHex来查看文件的数据如何存储。Crtl+F7,打开目录查看器,选择test.bin文件。注意到test.bin的标识id和左下角显示的扇区地址移植。拖动

    image

    拖动文本,直到文本的结尾。 观察imageimage ,即占用了第81336和81337两个扇区。知道了扇区地址和扇区内的字节偏移,即可使用void SD_CARD_Read_Data_LBA(u32 LBA,u16 n_bytes,u8 *buf)函数读取到想要的数据。

    image 

    源码下载

     lcd_at_nios_nii_part.zip 

    目录

    [原创][连载].基于SOPC的简易数码相框 -  Quartus II部分(硬件部分)

    [原创][连载].基于SOPC的简易数码相框 -  Nios II SBTE部分(软件部分)-  配置工作

    [原创][连载].基于SOPC的简易数码相框 -  Nios II SBTE部分(软件部分)-  SD卡(SPI模式)驱动

    [原创][连载].基于SOPC的简易数码相框 -  Nios II SBTE部分(软件部分)-  TFT-LCD(控制器为ILI9325)驱动

    [原创][连载].基于SOPC的简易数码相框 -  Nios II SBTE部分(软件部分)-  从SD卡内读取图片文件,然后显示在TFT-LCD上

    [原创][连载].基于SOPC的简易数码相框 -  Nios II SBTE部分(软件部分)-  优化工作

    [原创][连载].基于SOPC的简易数码相框 -  Nios II SBTE部分(软件部分)-  ADS7843触摸屏驱动测试

  • 相关阅读:
    Spring-boot内置的程序管理监控工具-Actuator
    分表工具类(根据唯一字符串)
    ES设计及规范
    测试单元Junit一直进不去@test方法解决方案
    MAC OS怎样将普通成员升级为管理员
    elasticSearch小结
    Mysql 查询条件中字符串尾部有空格也能匹配上的问题
    gitlab进行meger代码回滚
    ES与关系型数据库的通俗比较
    kafka的生产者配置以及发送信息的三种方式
  • 原文地址:https://www.cnblogs.com/yuphone/p/1917947.html
Copyright © 2011-2022 走看看