打印

ATmega128驱动SD卡的读写程序

[复制链接]
3902|2
手机看帖
扫描二维码
随时随地手机跟帖
沙发
哇咔咔1|  楼主 | 2016-11-29 14:08 | 只看该作者
#include <avr/io.h>
#include<util/delay.h>
#define        SPI_DDR                        DDRB
#define SPI_PORT                PORTB
#define SPI_PIN                        PINB
#define SPI_SS                        PB0
#define SPI_MOSI                PB2
#define SPI_MISO                PB3
#define SPI_SCK                        PB1
#define SET_SD_CS                  PORTG |= (1<<PG3)
#define CLR_SD_CS                  PORTG &= ~(1<<PG3)
#define        SDCSDPORT()       PORTG |= (1 << PG3)    //SD卡的CS端口为高
#define        SDCSDDDR()        DDRG |= (1 << PG3)     //SD卡的CD端口为输出
#define SET_SPI_MOSI          PORTB |= (1 << PB2)                //没数据时SD_MI应保持为高电平

///-------------------------------以下是子程序-----------------------------------//
void SPI_Low(void)               
{//SPI低速模式
        SPCR = 0;
        SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0) | (1 << SPR1);
        //使能SPI,主机方式,MSB在前,模式0,128分频
}

void SPI_High(void)
{//SPI高速模式
        SPCR = 0;
        SPCR = (1 << SPE) | (1 << MSTR);
                SPSR |= (1 << SPI2X);
                //使能SPI,主机方式,MSB在前,模式0,4分频,2倍频
}

void SPI_Init(void)
{//SPI初始化
        SPI_PORT = (1 << SPI_SS) | (1 << SPI_MISO);                //将SS置位输出拉高,MISO输入带上拉
        SPI_DDR = (1 << SPI_SS) | (1 << SPI_MOSI) | (1 << SPI_SCK);
        //将SS SCK MOSI置为输出       
}

uint8_t SPI_RW(uint8_t dat)
{//SPI读写1Byte(欲想读之必先与之)
        SPDR = dat;
                while(!(SPSR & (1 << SPIF)))
                ;
        return (SPDR);
}

uint8_t SD_Write_Com(uint8_t *cmd)
{
        uint8_t i,temp,retry;

        SET_SD_CS;                                        //关片选
        SPI_RW(0XFF);                                 //提高兼容性,如果没有这里,有些SD卡可能不支持   
        CLR_SD_CS;                                        //开片选(后面的读写扇区可以省去开片选)
        asm("nop");
       
        for(i=0;i<6;i++)
        {
                SPI_RW(*cmd);
                cmd++;
        }

        SPI_RW(0XFF);                                //写入指令后附加8个时钟脉冲       

        retry = 0;
        do                                                        //不断地读
        {
                temp = SPI_RW(0XFF);
                retry++;               
        }while((temp == 0XFF) && (retry < 254));
       
        return (temp);
}

uint8_t SD_Init(void)
{
        uint8_t i,temp;
        uint16_t retry;                                                        //重试次数16位数据(适应大容量SD卡)       
        uint8_t CMD[] = {0x40,0x00,0x00,0x00,0x00,0x95};       
    SDCSDPORT();                            //G口设为输出
    SDCSDDDR();

        SPI_Init();                                                               
        SPI_Low();                                                                //SPI初始化低速模式

        for(i=0;i<200;i++)                                                 //Wait MMC/SD ready...
        {
                asm("nop");
        }
       
        retry=0;
        do
        {
                SET_SD_CS;
                for (i=0;i<0x0f;i++)
                {
                        SPI_RW(0xff);                                                 //至少74个信号脉冲
                }
                       
                temp = SD_Write_Com(CMD);                      //写入CMD0号命令,最大重试次数254
                retry++;
                if(retry == 600)
                {
                        SET_SD_CS;
                        return(1);                                                //写入CMD0号命令错误
                }
        }while(temp!=1);

       
        CMD[0] = 0x41;                                                         //CMD1号命令
        CMD[5] = 0xFF;  

        retry = 0;
        do
        {
                temp = SD_Write_Com(CMD);
                retry++;
                if(retry > 60000)                                         //写入CMD1号命令,最大重试次数60000
                {
                        SET_SD_CS;                                                //关闭片选
                        return(1);                                                //写CMD1命令失败
                }
        }while(temp != 0);
       

        SET_SD_CS;                                                          //关闭片选

        SPI_High();                                        //初始化成功,SPI高速模式
        SET_SPI_MOSI;                                //没数据时SD_MI应保持为高电平       
        return(0);                                         //成功初始化       
}

uint8_t  SD_Write_Sector(uint32_t addr , uint8_t *buffer)
{//向SD卡中的指定地址的扇区写入512个字节,使用CMD24(24号命令,单块写命令)
        uint8_t temp,retry;
        uint16_t i;
        uint8_t cmd24[] = {0x58,0x00,0x00,0x00,0x00,0xff};

        addr <<= 9;
        //addr = addr * 512        将块地址(扇区地址)转为字节地址(所以SD卡的最大容量为4G)

        cmd24[1] = ((addr & 0xFF000000) >> 24);
        cmd24[2] = ((addr & 0x00FF0000) >> 16);
        cmd24[3] = ((addr & 0x0000FF00) >> 8);
        //cmd24[4] = ((addr & 0x000000FF) >> 0);        //可以省去

        CLR_SD_CS;//打开SD卡片选

        retry = 0;
        do
        {  
                temp = SD_Write_Com(cmd24);
                retry++;
                if(retry > 200)
                {
                        return(1);                         //CMD24命令写入失败
                }
        }while(temp != 0);       


        SPI_RW(0XFF);
        SPI_RW(0XFF);       
        SPI_RW(0XFF);                                //按照时序要求插入若干时序信号

        SPI_RW(0XFE);                                //写入开始字节 0xfe,后面就是要写入的512个字节的数据         

        for (i=0;i<512;i++)                 //将缓冲区中要写入的512个字节写入SD卡
        {
                SPI_RW(buffer[i]);               
        }

        SPI_RW(0xFF);
        SPI_RW(0xFF);                                 //两个字节的CRC校验码,不用关心


        temp = SPI_RW(0XFF);                   //读取返回值
        if((temp & 0x1F) != 0x05)         //如果返回值是 XXX0 0101b 说明数据已经被SD卡接受了
        {
                SET_SD_CS;
                return(1);                                 //写块数据失败
        }

        while(SPI_RW(0XFF) != 0xff)
                ;
        //等待SD卡不忙
        //等到SD卡不忙(数据被接受以后,SD卡要将这些数据写入到自身的FLASH中,需要一个时间)
        //忙时,读回来的值为0x00,不忙时,为0xff

        SET_SD_CS;                                         //关闭片选

        SPI_RW(0XFF);                                //按照SD卡的操作时序在这里补8个时钟

        SET_SPI_MOSI;                                //没数据时SD_MI应保持为高电平
        return(0);                                         //返回0,说明写扇区操作成功
}


uint8_t  SD_Read_Sector(uint32_t addr , uint8_t *buffer)
{//从SD卡的指定扇区中读出512个字节,使用CMD17(17号命令)
        uint16_t i;
        uint8_t retry,temp;
        uint8_t cmd17[]={0x51,0x00,0x00,0x00,0x00,0xFF}; //CMD17的字节序列

        addr <<= 9;                 //sector = sector * 512           将块地址(扇区地址)转为字节地址

        cmd17[1] = ((addr & 0xFF000000) >> 24);
        cmd17[2] = ((addr & 0x00FF0000) >> 16);
        cmd17[3] = ((addr & 0x0000FF00) >> 8);
        //cmd17[4] = ((addr & 0x000000FF) >> 0);//可以省去
        CLR_SD_CS;                                                        //打开片选

        retry = 0;
        do
        {  
                temp = SD_Write_Com(cmd17);                 //写入CMD17
                retry++;
                if(retry > 250)                                         //最大重试次数250
                {
                        SET_SD_CS;
                        return(1);                                                 //读块失败
                }
        }while(temp != 0);


        while (SPI_RW(0XFF) != 0xfe);         //一直读,当读到0xfe时,说明后面的是512字节的数据了

        for(i=0;i<512;i++)                                 //将数据写入到数据缓冲区中
        {       
                buffer[i] = SPI_RW(0XFF);
        }

        SPI_RW(0XFE);
        SPI_RW(0XFE);                                         //读取两个字节的CRC校验码,不用关心它们

        SET_SD_CS;                                          //SD卡关闭片选

        SPI_RW(0xFF);                                        //按照SD卡的操作时序在这里补8个时钟

        SET_SPI_MOSI;                                //没数据时SD_MI应保持为高电平
        return 0;                                                //返回0,说明读扇区成功
}
int main()
{
        int i=0;
        unsigned int *buffer;
        SD_Init();
        for(i=0;i<512;i++)
        {
                buffer[i]=i;    //向缓冲区中写入数据
        }
        SD_Write_Sector(80,buffer);
        for(i=0;i<512;i++)
        {
                buffer[i]=0;    //向缓冲区中写入数据
        }
        SD_Read_Sector(80,buffer);
        _delay_us(1000);
        while(1);
               
}

使用特权

评论回复
板凳
哇咔咔1|  楼主 | 2016-11-29 14:09 | 只看该作者
程序烧录完,电脑上显示不出来,大神看看哪有问题

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1

主题

3

帖子

0

粉丝