上一篇文章已经介绍了Micro SD卡SPI模式的实现方法,这里给出自己写的基于nios ii的Micro SD卡读写程序(IO口模拟spi)。
硬件设计就不多说了,主要是添加4的1为PIO口来模拟SPI的CS、SCLK、MISO和MOSI。
以下是代码:
头文件SD_spi_solution.h
#ifndef SD_SPI_SOLUTION_H_
#define SD_SPI_SOLUTION_H_
#include
#include
#include
#define CMD0 0 /* GO_IDLE_STATE */
#define CMD55 55 /* APP_CMD */
#define ACMD41 41 /* SEND_OP_COND (ACMD) */
#define CMD1 1 /* SEND_OP_COND */
#define CMD17 17 /* READ_SINGLE_BLOCK */
#define CMD8 8 /* SEND_IF_COND */
#define CMD18 18 /* READ_MULTIPLE_BLOCK */
#define CMD12 12 /* STOP_TRANSMISSION */
#define CMD24 24 /* WRITE_BLOCK */
#define CMD25 25 /* WRITE_MULTIPLE_BLOCK */
#define CMD13 13 /* SEND_STATUS */
#define CMD9 9 /* SEND_CSD */
#define CMD10 10 /* SEND_CID */
#define CSD 9
#define CID 10
//delay 1us(actually not,it maybe is several us,I don't test it)
void usleep(alt_u8 i);
//set CS low
void CS_Enable();
//set CS high and send 8 clocks
void CS_Disable();
//write a byte
void SDWriteByte(alt_u8 data);
//read a byte
alt_u8 SDReadByte();
//send a command and send back the response
alt_u8 SDSendCmd(alt_u8 cmd,alt_u32 arg,alt_u8 crc);
//reset SD card
alt_u8 SDReset();
//initial SD card
alt_u8 SDInit();
//read a single sector
alt_u8 SDReadSector(alt_u32 addr,alt_u8 * buffer);
//read multiple sectors
alt_u8 SDReadMultiSector(alt_u32 addr,alt_u8 sector_num,alt_u8 * buffer);
//write a single sector
alt_u8 SDWriteSector(alt_u32 addr,alt_u8 * buffer);
//write multiple sectors
alt_u8 SDWriteMultiSector(alt_u32 addr,alt_u8 sector_num,alt_u8 * buffer);
//get CID or CSD
alt_u8 SDGetCIDCSD(alt_u8 cid_csd,alt_u8 * buffer);
//spi speed(0-255),0 is fastest
alt_u8 spi_speed;
#endif /* SD_SPI_SOLUTION_H_ */
源文件SD_spi_Solution.c
#include"SD_spi_Solution.h"
alt_u8 spi_speed = 10;//the spi speed(0-255),0 is fastest
//delay 1us(actually not,it maybe is several us,I don't test it)
void usleep(alt_u8 i)
{
while(i --);
}
//set CS low
void CS_Enable()
{
//set CS low
IOWR_ALTERA_AVALON_PIO_DATA(CS_BASE, 0x00);
}
//set CS high and send 8 clocks
void CS_Disable()
{
//set CS high
IOWR_ALTERA_AVALON_PIO_DATA(CS_BASE, 0x01);
//send 8 clocks
SDWriteByte(0xff);
}
//write a byte
void SDWriteByte(alt_u8 data)
{
alt_u8 i;
//write 8 bits(MSB)
for(i = 0;i < 8;i ++)
{
IOWR_ALTERA_AVALON_PIO_DATA(SCLK_BASE, 0x00);
usleep(spi_speed);
if(data & 0x80) IOWR_ALTERA_AVALON_PIO_DATA(DI_BASE, 0x01);
else IOWR_ALTERA_AVALON_PIO_DATA(DI_BASE, 0x00);
data <<= 1;
IOWR_ALTERA_AVALON_PIO_DATA(SCLK_BASE, 0x01);
usleep(spi_speed);
}
//when DI is free,it should be set high
IOWR_ALTERA_AVALON_PIO_DATA(DI_BASE, 0x01);
}
//read a byte
alt_u8 SDReadByte()
{
alt_u8 data = 0x00,i;
//read 8 bit(MSB)
for(i = 0;i < 8;i ++)
{
IOWR_ALTERA_AVALON_PIO_DATA(SCLK_BASE, 0x00);
usleep(spi_speed);
IOWR_ALTERA_AVALON_PIO_DATA(SCLK_BASE, 0x01);
data <<= 1;
if(IORD_ALTERA_AVALON_PIO_DATA(DO_BASE)) data |= 0x01;
usleep(spi_speed);
}
return data;
}
//send a command and send back the response
alt_u8 SDSendCmd(alt_u8 cmd,alt_u32 arg,alt_u8 crc)
{
alt_u8 r1,time = 0;
//send the command,arguments and CRC
SDWriteByte((cmd & 0x3f) | 0x40);
SDWriteByte(arg >> 24);
SDWriteByte(arg >> 16);
SDWriteByte(arg >> 8);
SDWriteByte(arg);
SDWriteByte(crc);
//read the respond until responds is not '0xff' or timeout
do{
r1 = SDReadByte();
time ++;
//if time out,return
if(time > 254) break;
}while(r1 == 0xff);
return r1;
}
//reset SD card
alt_u8 SDReset()
{
alt_u8 i,r1,time = 0;
//set CS high
CS_Disable();
//send 128 clocks
for(i = 0;i < 16;i ++)
{
SDWriteByte(0xff);
}
//set CS low
CS_Enable();
//send CMD0 till the response is 0x01
do{
r1 = SDSendCmd(CMD0,0,0x95);
time ++;
//if time out,set CS high and return r1
if(time > 254)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
}while(r1 != 0x01);
//set CS high and send 8 clocks
CS_Disable();
return 0;
}
//initial SD card(send CMD55+ACMD41 or CMD1)
alt_u8 SDInit()
{
alt_u8 r1,time = 0;
//set CS low
CS_Enable();
//check interface operating condition
r1 = SDSendCmd(CMD8,0x000001aa,0x87);
//if support Ver1.x,but do not support Ver2.0,set CS high and return r1
if(r1 == 0x05)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
//read the other 4 bytes of response(the response of CMD8 is 5 bytes)
r1=SDReadByte();
r1=SDReadByte();
r1=SDReadByte();
r1=SDReadByte();
do{
//send CMD55+ACMD41 to initial SD card
do{
r1 = SDSendCmd(CMD55,0,0xff);
time ++;
//if time out,set CS high and return r1
if(time > 254)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
}while(r1 != 0x01);
r1 = SDSendCmd(ACMD41,0x40000000,0xff);
//send CMD1 to initial SD card
//r1 = SDSendCmd(CMD1,0x00ffc000,0xff);
time ++;
//if time out,set CS high and return r1
if(time > 254)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
}while(r1 != 0x00);
//set CS high and send 8 clocks
CS_Disable();
return 0;
}
//read a single sector
alt_u8 SDReadSector(alt_u32 addr,alt_u8 * buffer)
{
alt_u8 r1;
alt_u16 i,time = 0;
//set CS low
CS_Enable();
//send CMD17 for single block read
r1 = SDSendCmd(CMD17,addr << 9,0x55);
//if CMD17 fail,return
if(r1 != 0x00)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
//continually read till get the start byte 0xfe
do{
r1 = SDReadByte();
time ++;
//if time out,set CS high and return r1
if(time > 30000)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
}while(r1 != 0xfe);
//read 512 Bits of data
for(i = 0;i < 512;i ++)
{
buffer[i] = SDReadByte();
}
//read two bits of CRC
SDReadByte();
SDReadByte();
//set CS high and send 8 clocks
CS_Disable();
return 0;
}
//read multiple sectors
alt_u8 SDReadMultiSector(alt_u32 addr,alt_u8 sector_num,alt_u8 * buffer)
{
alt_u16 i,time = 0;
alt_u8 r1;
//set CS low
CS_Enable();
//send CMD18 for multiple blocks read
r1 = SDSendCmd(CMD18,addr << 9,0xff);
//if CMD18 fail,return
if(r1 != 0x00)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
//read sector_num sector
do{
//continually read till get start byte
do{
r1 = SDReadByte();
time ++;
//if time out,set CS high and return r1
if(time > 30000 || ((r1 & 0xf0) == 0x00 && (r1 & 0x0f)))
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
}while(r1 != 0xfe);
time = 0;
//read 512 Bits of data
for(i = 0;i < 512;i ++)
{
*buffer ++ = SDReadByte();
}
//read two bits of CRC
SDReadByte();
SDReadByte();
}while( -- sector_num);
time = 0;
//stop multiple reading
r1 = SDSendCmd(CMD12,0,0xff);
//set CS high and send 8 clocks
CS_Disable();
return 0;
}
//write a single sector
alt_u8 SDWriteSector(alt_u32 addr,alt_u8 * buffer)
{
alt_u16 i,time = 0;
alt_u8 r1;
//set CS low
CS_Enable();
do{
do{
//send CMD24 for single block write
r1 = SDSendCmd(CMD24,addr << 9,0xff);
time ++;
//if time out,set CS high and return r1
if(time > 254)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
}while(r1 != 0x00);
time = 0;
//send some dummy clocks
for(i = 0;i < 5;i ++)
{
SDWriteByte(0xff);
}
//write start byte
SDWriteByte(0xfe);
//write 512 bytes of data
for(i = 0;i < 512;i ++)
{
SDWriteByte(buffer[i]);
}
//write 2 bytes of CRC
SDWriteByte(0xff);
SDWriteByte(0xff);
//read response
r1 = SDReadByte();
time ++;
//if time out,set CS high and return r1
if(time > 254)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
}while((r1 & 0x1f)!= 0x05);
time = 0;
//check busy
do{
r1 = SDReadByte();
time ++;
//if time out,set CS high and return r1
if(time > 60000)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
}while(r1 != 0xff);
//set CS high and send 8 clocks
CS_Disable();
return 0;
}
//write several blocks
alt_u8 SDWriteMultiSector(alt_u32 addr,alt_u8 sector_num,alt_u8 * buffer)
{
alt_u16 i,time = 0;
alt_u8 r1;
//set CS low
CS_Enable();
//send CMD25 for multiple block read
r1 = SDSendCmd(CMD25,addr << 9,0xff);
//if CMD25 fail,return
if(r1 != 0x00)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
do{
do{
//send several dummy clocks
for(i = 0;i < 5;i ++)
{
SDWriteByte(0xff);
}
//write start byte
SDWriteByte(0xfc);
//write 512 byte of data
for(i = 0;i < 512;i ++)
{
SDWriteByte(*buffer ++);
}
//write 2 byte of CRC
SDWriteByte(0xff);
SDWriteByte(0xff);
//read response
r1 = SDReadByte();
time ++;
//if time out,set CS high and return r1
if(time > 254)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
}while((r1 & 0x1f)!= 0x05);
time = 0;
//check busy
do{
r1 = SDReadByte();printf("n%d",r1);
time ++;
//if time out,set CS high and return r1
if(time > 30000)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
}while(r1 != 0xff);
time = 0;
}while(-- sector_num);
//send stop byte
SDWriteByte(0xfd);
//check busy
do{
r1 = SDReadByte();
time ++;
//if time out,set CS high and return r1
if(time > 30000)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
}while(r1 != 0xff);
//set CS high and send 8 clocks
CS_Disable();
return 0;
}
//get CID or CSD
alt_u8 SDGetCIDCSD(alt_u8 cid_csd,alt_u8 * buffer)
{
alt_u8 r1;
alt_u16 i,time = 0;
//set CS low
CS_Enable();
//send CMD10 for CID read or CMD9 for CSD
do{
if(cid_csd == CID)
r1 = SDSendCmd(CMD10,0,0xff);
else
r1 = SDSendCmd(CMD9,0,0xff);
time ++;
//if time out,set CS high and return r1
if(time > 254)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
}while(r1 != 0x00);
time = 0;
//continually read till get 0xfe
do{
r1 = SDReadByte();
time ++;
//if time out,set CS high and return r1
if(time > 30000)
{
//set CS high and send 8 clocks
CS_Disable();
return r1;
}
}while(r1 != 0xfe);
//read 512 Bits of data
for(i = 0;i < 16;i ++)
{
*buffer ++ = SDReadByte();
}
//read two bits of CRC
SDReadByte();
SDReadByte();
//set CS high and send 8 clocks
CS_Disable();
return 0;
}