您的位置:首页 > 移动开发 > IOS开发

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

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

准备工具及资料

1. WinHex

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

驱动编写及调试

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

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







步骤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打印的信息截图如下。



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



(第0扇区的内容)

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





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

其他问题

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

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







(查看test.bin的属性)

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





拖动文本,直到文本的结尾。 观察



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





源码下载

lcd_at_nios_nii_part.zip

目录

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

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

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

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

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

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

7 [原创][连载].基于SOPC的简易数码相框 - Nios II SBTE部分(软件部分)- ADS7843触摸屏驱动测试
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐