C - 在 SPI 模式下初始化 SD 卡,始终读回 0xFF
我正在尝试使用 Cypress PSoC 5 初始化 2GB SanDisk SD 卡SPI接口。我正在使用 FatFs - 通用 FAT 文件系统模块中的示例位敲击代码。我已经在逻辑分析仪上分析了接口(请参阅 )。问题是 SD 卡的数据输出线始终为高电平,我应该从卡初始化命令中返回 0x01。然而,SD 卡在 PC 上读取良好。我正在使用的代码如下,问题可能是什么?
我正在使用 http://ucontroller.com/documentation/SDCardDoc.html。
/*------------------------------------------------------------------------/
/ Bitbanging MMCv3/SDv1/SDv2 (in SPI mode) control module
/-------------------------------------------------------------------------/
/
/ Copyright (C) 2010, ChaN, all right reserved.
/
/ * This software is a free software and there is NO WARRANTY.
/ * No restriction on use. You can use, modify and redistribute it for
/ personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
/ * Redistributions of source code must retain the above copyright notice.
/
/--------------------------------------------------------------------------/
Features and Limitations:
* Very Easy to Port
It uses only 4-6 bit of GPIO port. No interrupt, no SPI port is used.
* Platform Independent
You need to modify only a few macros to control GPIO ports.
* Low Speed
The data transfer rate will be several times slower than hardware SPI.
* No Media Change Detection
Application program must re-mount the volume after media change or it
results a hard error.
/-------------------------------------------------------------------------*/
#include <device.h>
#include "diskio.h" /* Common include file for FatFs and disk I/O layer */
/*-------------------------------------------------------------------------*/
/* Platform dependent macros and functions needed to be modified */
/*-------------------------------------------------------------------------*/
#include <device.h> /* Include device specific declareation file here */
#define INIT_PORT() /* Initialize MMC control port (CS/CLK/DI:output, DO/WP/INS:input) */
#define DLY_US(n) CyDelayUs(n) /* Delay n microseconds */
#define CLK_DELAY() CyDelayUs(1)
#define CS_H() CyPins_SetPin(ss_0) /* Set MMC CS "high" */
#define CS_L() CyPins_ClearPin(ss_0) /* Set MMC CS "low" */
#define CK_H() CyPins_SetPin(clk_0) /* Set MMC SCLK "high" */
#define CK_L() CyPins_ClearPin(clk_0) /* Set MMC SCLK "low" */
#define DI_H() CyPins_SetPin(spiout_0) /* Set MMC DI "high" */
#define DI_L() CyPins_ClearPin(spiout_0) /* Set MMC DI "low" */
#define DO CyPins_ReadPin(spiin_0) /* Get MMC DO value (high:true, low:false) */
#define INS (1) /* Card is inserted (yes:true, no:false, default:true) */
#define WP (0) /* Card is write protected (yes:true, no:false, default:false) */
/*--------------------------------------------------------------------------
Module Private Functions
---------------------------------------------------------------------------*/
/* MMC/SD command (SPI mode) */
#define CMD0 (0) /* GO_IDLE_STATE */
#define CMD1 (1) /* SEND_OP_COND */
#define ACMD41 (0x80+41) /* SEND_OP_COND (SDC) */
#define CMD8 (8) /* SEND_IF_COND */
#define CMD9 (9) /* SEND_CSD */
#define CMD10 (10) /* SEND_CID */
#define CMD12 (12) /* STOP_TRANSMISSION */
#define ACMD13 (0x80+13) /* SD_STATUS (SDC) */
#define CMD16 (16) /* SET_BLOCKLEN */
#define CMD17 (17) /* READ_SINGLE_BLOCK */
#define CMD18 (18) /* READ_MULTIPLE_BLOCK */
#define CMD23 (23) /* SET_BLOCK_COUNT */
#define ACMD23 (0x80+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */
#define CMD24 (24) /* WRITE_BLOCK */
#define CMD25 (25) /* WRITE_MULTIPLE_BLOCK */
#define CMD41 (41) /* SEND_OP_COND (ACMD) */
#define CMD55 (55) /* APP_CMD */
#define CMD58 (58) /* READ_OCR */
/* Card type flags (CardType) */
#define CT_MMC 0x01 /* MMC ver 3 */
#define CT_SD1 0x02 /* SD ver 1 */
#define CT_SD2 0x04 /* SD ver 2 */
#define CT_SDC (CT_SD1|CT_SD2) /* SD */
#define CT_BLOCK 0x08 /* Block addressing */
static
DSTATUS Stat = STA_NOINIT; /* Disk status */
static
BYTE CardType; /* b0:MMC, b1:SDv1, b2:SDv2, b3:Block addressing */
static char buf[80];
/*-----------------------------------------------------------------------*/
/* Transmit bytes to the MMC (bitbanging) */
/*-----------------------------------------------------------------------*/
static
void xmit_mmc (
const BYTE* buff, /* Data to be sent */
UINT bc /* Number of bytes to send */
)
{
BYTE d;
do {
d = *buff++; /* Get a byte to be sent */
if (d & 0x80) DI_H(); else DI_L(); /* bit7 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
if (d & 0x40) DI_H(); else DI_L(); /* bit6 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
if (d & 0x20) DI_H(); else DI_L(); /* bit5 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
if (d & 0x10) DI_H(); else DI_L(); /* bit4 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
if (d & 0x08) DI_H(); else DI_L(); /* bit3 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
if (d & 0x04) DI_H(); else DI_L(); /* bit2 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
if (d & 0x02) DI_H(); else DI_L(); /* bit1 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
if (d & 0x01) DI_H(); else DI_L(); /* bit0 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
DLY_US(10);
//snprintf(buf, sizeof buf, "sent 0x%02x\r\n", d);
//UART_1_PutString(buf);
} while (--bc);
}
/*-----------------------------------------------------------------------*/
/* Receive bytes from the MMC (bitbanging) */
/*-----------------------------------------------------------------------*/
static
void rcvr_mmc (
BYTE *buff, /* Pointer to read buffer */
UINT bc /* Number of bytes to receive */
)
{
BYTE r;
DI_H(); /* Send 0xFF */
do {
r = 0; if (DO) r++; /* bit7 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
r <<= 1; if (DO) r++; /* bit6 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
r <<= 1; if (DO) r++; /* bit5 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
r <<= 1; if (DO) r++; /* bit4 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
r <<= 1; if (DO) r++; /* bit3 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
r <<= 1; if (DO) r++; /* bit2 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
r <<= 1; if (DO) r++; /* bit1 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
r <<= 1; if (DO) r++; /* bit0 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
*buff++ = r; /* Store a received byte */
DLY_US(10);
//snprintf(buf, sizeof buf, "received 0x%02x\r\n", r);
//UART_1_PutString(buf);
} while (--bc);
}
/*-----------------------------------------------------------------------*/
/* Wait for card ready */
/*-----------------------------------------------------------------------*/
static
int wait_ready (void) /* 1:OK, 0:Timeout */
{
BYTE d;
UINT tmr;
for (tmr = 5000; tmr; tmr--) { /* Wait for ready in timeout of 500ms */
rcvr_mmc(&d, 1);
if (d == 0xFF) return 1;
DLY_US(100);
}
return 0;
}
/*-----------------------------------------------------------------------*/
/* Deselect the card and release SPI bus */
/*-----------------------------------------------------------------------*/
static
void deselect (void)
{
BYTE d;
CS_H();
rcvr_mmc(&d, 1);
}
/*-----------------------------------------------------------------------*/
/* Select the card and wait for ready */
/*-----------------------------------------------------------------------*/
static
int select (void) /* 1:OK, 0:Timeout */
{
CS_L();
if (!wait_ready()) {
deselect();
return 0;
}
return 1;
}
/*-----------------------------------------------------------------------*/
/* Receive a data packet from MMC */
/*-----------------------------------------------------------------------*/
static
int rcvr_datablock ( /* 1:OK, 0:Failed */
BYTE *buff, /* Data buffer to store received data */
UINT btr /* Byte count */
)
{
BYTE d[2];
UINT tmr;
for (tmr = 1000; tmr; tmr--) { /* Wait for data packet in timeout of 100ms */
rcvr_mmc(d, 1);
if (d[0] != 0xFF) break;
DLY_US(100);
}
if (d[0] != 0xFE)
return 0; /* If not valid data token, retutn with error */
rcvr_mmc(buff, btr); /* Receive the data block into buffer */
rcvr_mmc(d, 2); /* Discard CRC */
return 1; /* Return with success */
}
/*-----------------------------------------------------------------------*/
/* Send a data packet to MMC */
/*-----------------------------------------------------------------------*/
static
int xmit_datablock ( /* 1:OK, 0:Failed */
const BYTE *buff, /* 512 byte data block to be transmitted */
BYTE token /* Data/Stop token */
)
{
BYTE d[2];
if (!wait_ready())
return 0;
d[0] = token;
xmit_mmc(d, 1); /* Transmit a token */
if (token != 0xFD) { /* Is it data token? */
xmit_mmc(buff, 512); /* Transmit the 512 byte data block to MMC */
rcvr_mmc(d, 2); /* Dummy CRC (FF,FF) */
rcvr_mmc(d, 1); /* Receive data response */
if ((d[0] & 0x1F) != 0x05) /* If not accepted, return with error */
return 0;
}
return 1;
}
/*-----------------------------------------------------------------------*/
/* Send a command packet to MMC */
/*-----------------------------------------------------------------------*/
static
BYTE send_cmd ( /* Returns command response (bit7==1:Send failed)*/
BYTE cmd, /* Command byte */
DWORD arg /* Argument */
)
{
BYTE n, d, buf[6];
if (cmd & 0x80) { /* ACMD<n> is the command sequense of CMD55-CMD<n> */
cmd &= 0x7F;
n = send_cmd(CMD55, 0);
if (n > 1) return n;
}
/* Select the card and wait for ready */
deselect();
if (!select()) {
return 0xFF;
}
/* Send a command packet */
buf[0] = 0x40 | cmd; /* Start + Command index */
buf[1] = (BYTE)(arg >> 24); /* Argument[31..24] */
buf[2] = (BYTE)(arg >> 16); /* Argument[23..16] */
buf[3] = (BYTE)(arg >> 8); /* Argument[15..8] */
buf[4] = (BYTE)arg; /* Argument[7..0] */
n = 0x01; /* Dummy CRC + Stop */
if (cmd == CMD0)
n = 0x95; /* (valid CRC for CMD0(0)) */
if (cmd == CMD8)
n = 0x87; /* (valid CRC for CMD8(0x1AA)) */
buf[5] = n;
xmit_mmc(buf, 6);
/* Receive command response */
if (cmd == CMD12)
rcvr_mmc(&d, 1); /* Skip a stuff byte when stop reading */
n = 10; /* Wait for a valid response in timeout of 10 attempts */
do
rcvr_mmc(&d, 1);
while ((d & 0x80) && --n);
return d; /* Return with the response value */
}
/*--------------------------------------------------------------------------
Public Functions
---------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------*/
/* Get Disk Status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
BYTE drv /* Drive number (0) */
)
{
DSTATUS s = Stat;
if (drv || !INS) {
s = STA_NODISK | STA_NOINIT;
} else {
s &= ~STA_NODISK;
if (WP)
s |= STA_PROTECT;
else
s &= ~STA_PROTECT;
}
Stat = s;
return s;
}
/*-----------------------------------------------------------------------*/
/* Initialize Disk Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE drv /* Physical drive nmuber (0) */
)
{
BYTE n, ty, cmd, buf[4];
UINT tmr;
DSTATUS s;
INIT_PORT(); /* Initialize control port */
s = disk_status(drv); /* Check if card is in the socket */
if (s & STA_NODISK) return s;
CS_H();
for (n = 10; n; n--) rcvr_mmc(buf, 1); /* 80 dummy clocks */
ty = 0;
if (send_cmd(CMD0, 0) == 1) { /* Enter Idle state */
if (send_cmd(CMD8, 0x1AA) == 1) { /* SDv2? */
rcvr_mmc(buf, 4); /* Get trailing return value of R7 resp */
if (buf[2] == 0x01 && buf[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */
for (tmr = 1000; tmr; tmr--) { /* Wait for leaving idle state (ACMD41 with HCS bit) */
if (send_cmd(ACMD41, 1UL << 30) == 0) break;
DLY_US(1000);
}
if (tmr && send_cmd(CMD58, 0) == 0) { /* Check CCS bit in the OCR */
rcvr_mmc(buf, 4);
ty = (buf[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* SDv2 */
}
}
} else { /* SDv1 or MMCv3 */
if (send_cmd(ACMD41, 0) <= 1) {
ty = CT_SD1; cmd = ACMD41; /* SDv1 */
} else {
ty = CT_MMC; cmd = CMD1; /* MMCv3 */
}
for (tmr = 1000; tmr; tmr--) { /* Wait for leaving idle state */
if (send_cmd(ACMD41, 0) == 0) break;
DLY_US(1000);
}
if (!tmr || send_cmd(CMD16, 512) != 0) /* Set R/W block length to 512 */
ty = 0;
}
}
else {
UART_1_PutString("send CMD 0 failed");
}
CardType = ty;
if (ty) /* Initialization succeded */
s &= ~STA_NOINIT;
else /* Initialization failed */
s |= STA_NOINIT;
Stat = s;
deselect();
return s;
}
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
BYTE drv, /* Physical drive nmuber (0) */
BYTE *buff, /* Pointer to the data buffer to store read data */
DWORD sector, /* Start sector number (LBA) */
BYTE count /* Sector count (1..128) */
)
{
DSTATUS s;
s = disk_status(drv);
if (s & STA_NOINIT) return RES_NOTRDY;
if (!count) return RES_PARERR;
if (!(CardType & CT_BLOCK)) sector *= 512; /* Convert LBA to byte address if needed */
if (count == 1) { /* Single block read */
if ((send_cmd(CMD17, sector) == 0) /* READ_SINGLE_BLOCK */
&& rcvr_datablock(buff, 512))
count = 0;
}
else { /* Multiple block read */
if (send_cmd(CMD18, sector) == 0) { /* READ_MULTIPLE_BLOCK */
do {
if (!rcvr_datablock(buff, 512)) break;
buff += 512;
} while (--count);
send_cmd(CMD12, 0); /* STOP_TRANSMISSION */
}
}
deselect();
return count ? RES_ERROR : RES_OK;
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_write (
BYTE drv, /* Physical drive nmuber (0) */
const BYTE *buff, /* Pointer to the data to be written */
DWORD sector, /* Start sector number (LBA) */
BYTE count /* Sector count (1..128) */
)
{
DSTATUS s;
s = disk_status(drv);
if (s & STA_NOINIT) return RES_NOTRDY;
if (s & STA_PROTECT) return RES_WRPRT;
if (!count) return RES_PARERR;
if (!(CardType & CT_BLOCK)) sector *= 512; /* Convert LBA to byte address if needed */
if (count == 1) { /* Single block write */
if ((send_cmd(CMD24, sector) == 0) /* WRITE_BLOCK */
&& xmit_datablock(buff, 0xFE))
count = 0;
}
else { /* Multiple block write */
if (CardType & CT_SDC) send_cmd(ACMD23, count);
if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */
do {
if (!xmit_datablock(buff, 0xFC)) break;
buff += 512;
} while (--count);
if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */
count = 1;
}
}
deselect();
return count ? RES_ERROR : RES_OK;
}
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl (
BYTE drv, /* Physical drive nmuber (0) */
BYTE ctrl, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
DRESULT res;
BYTE n, csd[16];
WORD cs;
if (disk_status(drv) & STA_NOINIT) /* Check if card is in the socket */
return RES_NOTRDY;
res = RES_ERROR;
switch (ctrl) {
case CTRL_SYNC : /* Make sure that no pending write process */
if (select()) {
deselect();
res = RES_OK;
}
break;
case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */
if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) {
if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */
cs= csd[9] + ((WORD)csd[8] << 8) + 1;
*(DWORD*)buff = (DWORD)cs << 10;
} else { /* SDC ver 1.XX or MMC */
n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
cs = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;
*(DWORD*)buff = (DWORD)cs << (n - 9);
}
res = RES_OK;
}
break;
case GET_BLOCK_SIZE : /* Get erase block size in unit of sector (DWORD) */
*(DWORD*)buff = 128;
res = RES_OK;
break;
default:
res = RES_PARERR;
}
deselect();
return res;
}
I'm trying to initialize a 2 GB SanDisk SD card on a Cypress PSoC 5 using the SPI interface. I'm using the sample bit banging code from FatFs - Generic FAT File System Module. I've analyzed the interface on a logic analyzer (see ). The problem is that the data out line from the SD card is always high, I should be getting back a 0x01 from the card init command. The SD card reads fine on the PC however. The code I am using is below, what could the problem be?
I'm using an SD socket from http://ucontroller.com/documentation/SDCardDoc.html.
/*------------------------------------------------------------------------/
/ Bitbanging MMCv3/SDv1/SDv2 (in SPI mode) control module
/-------------------------------------------------------------------------/
/
/ Copyright (C) 2010, ChaN, all right reserved.
/
/ * This software is a free software and there is NO WARRANTY.
/ * No restriction on use. You can use, modify and redistribute it for
/ personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
/ * Redistributions of source code must retain the above copyright notice.
/
/--------------------------------------------------------------------------/
Features and Limitations:
* Very Easy to Port
It uses only 4-6 bit of GPIO port. No interrupt, no SPI port is used.
* Platform Independent
You need to modify only a few macros to control GPIO ports.
* Low Speed
The data transfer rate will be several times slower than hardware SPI.
* No Media Change Detection
Application program must re-mount the volume after media change or it
results a hard error.
/-------------------------------------------------------------------------*/
#include <device.h>
#include "diskio.h" /* Common include file for FatFs and disk I/O layer */
/*-------------------------------------------------------------------------*/
/* Platform dependent macros and functions needed to be modified */
/*-------------------------------------------------------------------------*/
#include <device.h> /* Include device specific declareation file here */
#define INIT_PORT() /* Initialize MMC control port (CS/CLK/DI:output, DO/WP/INS:input) */
#define DLY_US(n) CyDelayUs(n) /* Delay n microseconds */
#define CLK_DELAY() CyDelayUs(1)
#define CS_H() CyPins_SetPin(ss_0) /* Set MMC CS "high" */
#define CS_L() CyPins_ClearPin(ss_0) /* Set MMC CS "low" */
#define CK_H() CyPins_SetPin(clk_0) /* Set MMC SCLK "high" */
#define CK_L() CyPins_ClearPin(clk_0) /* Set MMC SCLK "low" */
#define DI_H() CyPins_SetPin(spiout_0) /* Set MMC DI "high" */
#define DI_L() CyPins_ClearPin(spiout_0) /* Set MMC DI "low" */
#define DO CyPins_ReadPin(spiin_0) /* Get MMC DO value (high:true, low:false) */
#define INS (1) /* Card is inserted (yes:true, no:false, default:true) */
#define WP (0) /* Card is write protected (yes:true, no:false, default:false) */
/*--------------------------------------------------------------------------
Module Private Functions
---------------------------------------------------------------------------*/
/* MMC/SD command (SPI mode) */
#define CMD0 (0) /* GO_IDLE_STATE */
#define CMD1 (1) /* SEND_OP_COND */
#define ACMD41 (0x80+41) /* SEND_OP_COND (SDC) */
#define CMD8 (8) /* SEND_IF_COND */
#define CMD9 (9) /* SEND_CSD */
#define CMD10 (10) /* SEND_CID */
#define CMD12 (12) /* STOP_TRANSMISSION */
#define ACMD13 (0x80+13) /* SD_STATUS (SDC) */
#define CMD16 (16) /* SET_BLOCKLEN */
#define CMD17 (17) /* READ_SINGLE_BLOCK */
#define CMD18 (18) /* READ_MULTIPLE_BLOCK */
#define CMD23 (23) /* SET_BLOCK_COUNT */
#define ACMD23 (0x80+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */
#define CMD24 (24) /* WRITE_BLOCK */
#define CMD25 (25) /* WRITE_MULTIPLE_BLOCK */
#define CMD41 (41) /* SEND_OP_COND (ACMD) */
#define CMD55 (55) /* APP_CMD */
#define CMD58 (58) /* READ_OCR */
/* Card type flags (CardType) */
#define CT_MMC 0x01 /* MMC ver 3 */
#define CT_SD1 0x02 /* SD ver 1 */
#define CT_SD2 0x04 /* SD ver 2 */
#define CT_SDC (CT_SD1|CT_SD2) /* SD */
#define CT_BLOCK 0x08 /* Block addressing */
static
DSTATUS Stat = STA_NOINIT; /* Disk status */
static
BYTE CardType; /* b0:MMC, b1:SDv1, b2:SDv2, b3:Block addressing */
static char buf[80];
/*-----------------------------------------------------------------------*/
/* Transmit bytes to the MMC (bitbanging) */
/*-----------------------------------------------------------------------*/
static
void xmit_mmc (
const BYTE* buff, /* Data to be sent */
UINT bc /* Number of bytes to send */
)
{
BYTE d;
do {
d = *buff++; /* Get a byte to be sent */
if (d & 0x80) DI_H(); else DI_L(); /* bit7 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
if (d & 0x40) DI_H(); else DI_L(); /* bit6 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
if (d & 0x20) DI_H(); else DI_L(); /* bit5 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
if (d & 0x10) DI_H(); else DI_L(); /* bit4 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
if (d & 0x08) DI_H(); else DI_L(); /* bit3 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
if (d & 0x04) DI_H(); else DI_L(); /* bit2 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
if (d & 0x02) DI_H(); else DI_L(); /* bit1 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
if (d & 0x01) DI_H(); else DI_L(); /* bit0 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
DLY_US(10);
//snprintf(buf, sizeof buf, "sent 0x%02x\r\n", d);
//UART_1_PutString(buf);
} while (--bc);
}
/*-----------------------------------------------------------------------*/
/* Receive bytes from the MMC (bitbanging) */
/*-----------------------------------------------------------------------*/
static
void rcvr_mmc (
BYTE *buff, /* Pointer to read buffer */
UINT bc /* Number of bytes to receive */
)
{
BYTE r;
DI_H(); /* Send 0xFF */
do {
r = 0; if (DO) r++; /* bit7 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
r <<= 1; if (DO) r++; /* bit6 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
r <<= 1; if (DO) r++; /* bit5 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
r <<= 1; if (DO) r++; /* bit4 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
r <<= 1; if (DO) r++; /* bit3 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
r <<= 1; if (DO) r++; /* bit2 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
r <<= 1; if (DO) r++; /* bit1 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
r <<= 1; if (DO) r++; /* bit0 */
CK_H(); CLK_DELAY(); CK_L(); CLK_DELAY();
*buff++ = r; /* Store a received byte */
DLY_US(10);
//snprintf(buf, sizeof buf, "received 0x%02x\r\n", r);
//UART_1_PutString(buf);
} while (--bc);
}
/*-----------------------------------------------------------------------*/
/* Wait for card ready */
/*-----------------------------------------------------------------------*/
static
int wait_ready (void) /* 1:OK, 0:Timeout */
{
BYTE d;
UINT tmr;
for (tmr = 5000; tmr; tmr--) { /* Wait for ready in timeout of 500ms */
rcvr_mmc(&d, 1);
if (d == 0xFF) return 1;
DLY_US(100);
}
return 0;
}
/*-----------------------------------------------------------------------*/
/* Deselect the card and release SPI bus */
/*-----------------------------------------------------------------------*/
static
void deselect (void)
{
BYTE d;
CS_H();
rcvr_mmc(&d, 1);
}
/*-----------------------------------------------------------------------*/
/* Select the card and wait for ready */
/*-----------------------------------------------------------------------*/
static
int select (void) /* 1:OK, 0:Timeout */
{
CS_L();
if (!wait_ready()) {
deselect();
return 0;
}
return 1;
}
/*-----------------------------------------------------------------------*/
/* Receive a data packet from MMC */
/*-----------------------------------------------------------------------*/
static
int rcvr_datablock ( /* 1:OK, 0:Failed */
BYTE *buff, /* Data buffer to store received data */
UINT btr /* Byte count */
)
{
BYTE d[2];
UINT tmr;
for (tmr = 1000; tmr; tmr--) { /* Wait for data packet in timeout of 100ms */
rcvr_mmc(d, 1);
if (d[0] != 0xFF) break;
DLY_US(100);
}
if (d[0] != 0xFE)
return 0; /* If not valid data token, retutn with error */
rcvr_mmc(buff, btr); /* Receive the data block into buffer */
rcvr_mmc(d, 2); /* Discard CRC */
return 1; /* Return with success */
}
/*-----------------------------------------------------------------------*/
/* Send a data packet to MMC */
/*-----------------------------------------------------------------------*/
static
int xmit_datablock ( /* 1:OK, 0:Failed */
const BYTE *buff, /* 512 byte data block to be transmitted */
BYTE token /* Data/Stop token */
)
{
BYTE d[2];
if (!wait_ready())
return 0;
d[0] = token;
xmit_mmc(d, 1); /* Transmit a token */
if (token != 0xFD) { /* Is it data token? */
xmit_mmc(buff, 512); /* Transmit the 512 byte data block to MMC */
rcvr_mmc(d, 2); /* Dummy CRC (FF,FF) */
rcvr_mmc(d, 1); /* Receive data response */
if ((d[0] & 0x1F) != 0x05) /* If not accepted, return with error */
return 0;
}
return 1;
}
/*-----------------------------------------------------------------------*/
/* Send a command packet to MMC */
/*-----------------------------------------------------------------------*/
static
BYTE send_cmd ( /* Returns command response (bit7==1:Send failed)*/
BYTE cmd, /* Command byte */
DWORD arg /* Argument */
)
{
BYTE n, d, buf[6];
if (cmd & 0x80) { /* ACMD<n> is the command sequense of CMD55-CMD<n> */
cmd &= 0x7F;
n = send_cmd(CMD55, 0);
if (n > 1) return n;
}
/* Select the card and wait for ready */
deselect();
if (!select()) {
return 0xFF;
}
/* Send a command packet */
buf[0] = 0x40 | cmd; /* Start + Command index */
buf[1] = (BYTE)(arg >> 24); /* Argument[31..24] */
buf[2] = (BYTE)(arg >> 16); /* Argument[23..16] */
buf[3] = (BYTE)(arg >> 8); /* Argument[15..8] */
buf[4] = (BYTE)arg; /* Argument[7..0] */
n = 0x01; /* Dummy CRC + Stop */
if (cmd == CMD0)
n = 0x95; /* (valid CRC for CMD0(0)) */
if (cmd == CMD8)
n = 0x87; /* (valid CRC for CMD8(0x1AA)) */
buf[5] = n;
xmit_mmc(buf, 6);
/* Receive command response */
if (cmd == CMD12)
rcvr_mmc(&d, 1); /* Skip a stuff byte when stop reading */
n = 10; /* Wait for a valid response in timeout of 10 attempts */
do
rcvr_mmc(&d, 1);
while ((d & 0x80) && --n);
return d; /* Return with the response value */
}
/*--------------------------------------------------------------------------
Public Functions
---------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------*/
/* Get Disk Status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
BYTE drv /* Drive number (0) */
)
{
DSTATUS s = Stat;
if (drv || !INS) {
s = STA_NODISK | STA_NOINIT;
} else {
s &= ~STA_NODISK;
if (WP)
s |= STA_PROTECT;
else
s &= ~STA_PROTECT;
}
Stat = s;
return s;
}
/*-----------------------------------------------------------------------*/
/* Initialize Disk Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE drv /* Physical drive nmuber (0) */
)
{
BYTE n, ty, cmd, buf[4];
UINT tmr;
DSTATUS s;
INIT_PORT(); /* Initialize control port */
s = disk_status(drv); /* Check if card is in the socket */
if (s & STA_NODISK) return s;
CS_H();
for (n = 10; n; n--) rcvr_mmc(buf, 1); /* 80 dummy clocks */
ty = 0;
if (send_cmd(CMD0, 0) == 1) { /* Enter Idle state */
if (send_cmd(CMD8, 0x1AA) == 1) { /* SDv2? */
rcvr_mmc(buf, 4); /* Get trailing return value of R7 resp */
if (buf[2] == 0x01 && buf[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */
for (tmr = 1000; tmr; tmr--) { /* Wait for leaving idle state (ACMD41 with HCS bit) */
if (send_cmd(ACMD41, 1UL << 30) == 0) break;
DLY_US(1000);
}
if (tmr && send_cmd(CMD58, 0) == 0) { /* Check CCS bit in the OCR */
rcvr_mmc(buf, 4);
ty = (buf[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* SDv2 */
}
}
} else { /* SDv1 or MMCv3 */
if (send_cmd(ACMD41, 0) <= 1) {
ty = CT_SD1; cmd = ACMD41; /* SDv1 */
} else {
ty = CT_MMC; cmd = CMD1; /* MMCv3 */
}
for (tmr = 1000; tmr; tmr--) { /* Wait for leaving idle state */
if (send_cmd(ACMD41, 0) == 0) break;
DLY_US(1000);
}
if (!tmr || send_cmd(CMD16, 512) != 0) /* Set R/W block length to 512 */
ty = 0;
}
}
else {
UART_1_PutString("send CMD 0 failed");
}
CardType = ty;
if (ty) /* Initialization succeded */
s &= ~STA_NOINIT;
else /* Initialization failed */
s |= STA_NOINIT;
Stat = s;
deselect();
return s;
}
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
BYTE drv, /* Physical drive nmuber (0) */
BYTE *buff, /* Pointer to the data buffer to store read data */
DWORD sector, /* Start sector number (LBA) */
BYTE count /* Sector count (1..128) */
)
{
DSTATUS s;
s = disk_status(drv);
if (s & STA_NOINIT) return RES_NOTRDY;
if (!count) return RES_PARERR;
if (!(CardType & CT_BLOCK)) sector *= 512; /* Convert LBA to byte address if needed */
if (count == 1) { /* Single block read */
if ((send_cmd(CMD17, sector) == 0) /* READ_SINGLE_BLOCK */
&& rcvr_datablock(buff, 512))
count = 0;
}
else { /* Multiple block read */
if (send_cmd(CMD18, sector) == 0) { /* READ_MULTIPLE_BLOCK */
do {
if (!rcvr_datablock(buff, 512)) break;
buff += 512;
} while (--count);
send_cmd(CMD12, 0); /* STOP_TRANSMISSION */
}
}
deselect();
return count ? RES_ERROR : RES_OK;
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_write (
BYTE drv, /* Physical drive nmuber (0) */
const BYTE *buff, /* Pointer to the data to be written */
DWORD sector, /* Start sector number (LBA) */
BYTE count /* Sector count (1..128) */
)
{
DSTATUS s;
s = disk_status(drv);
if (s & STA_NOINIT) return RES_NOTRDY;
if (s & STA_PROTECT) return RES_WRPRT;
if (!count) return RES_PARERR;
if (!(CardType & CT_BLOCK)) sector *= 512; /* Convert LBA to byte address if needed */
if (count == 1) { /* Single block write */
if ((send_cmd(CMD24, sector) == 0) /* WRITE_BLOCK */
&& xmit_datablock(buff, 0xFE))
count = 0;
}
else { /* Multiple block write */
if (CardType & CT_SDC) send_cmd(ACMD23, count);
if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */
do {
if (!xmit_datablock(buff, 0xFC)) break;
buff += 512;
} while (--count);
if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */
count = 1;
}
}
deselect();
return count ? RES_ERROR : RES_OK;
}
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl (
BYTE drv, /* Physical drive nmuber (0) */
BYTE ctrl, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
DRESULT res;
BYTE n, csd[16];
WORD cs;
if (disk_status(drv) & STA_NOINIT) /* Check if card is in the socket */
return RES_NOTRDY;
res = RES_ERROR;
switch (ctrl) {
case CTRL_SYNC : /* Make sure that no pending write process */
if (select()) {
deselect();
res = RES_OK;
}
break;
case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */
if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) {
if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */
cs= csd[9] + ((WORD)csd[8] << 8) + 1;
*(DWORD*)buff = (DWORD)cs << 10;
} else { /* SDC ver 1.XX or MMC */
n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
cs = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;
*(DWORD*)buff = (DWORD)cs << (n - 9);
}
res = RES_OK;
}
break;
case GET_BLOCK_SIZE : /* Get erase block size in unit of sector (DWORD) */
*(DWORD*)buff = 128;
res = RES_OK;
break;
default:
res = RES_PARERR;
}
deselect();
return res;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
一般情况下,同一接口线上连接多张SD卡时,可以放置上拉电阻。特别是,当您取消选择并选择另一张卡时,信号 DI、DO 和 CLK 会变为浮动。
否则,我可以想象它也会对主机和从机(在本例中为 microSD 卡)之间的轨道长度产生影响。
我在互联网上找到的许多示例电路中都看到了上拉电阻。
Normally, you can put pull-up resistors when more than one SD card is connected to the same interface lines. In particular, the signals DI, DO, and CLK becomes floating when you deselect and select another card.
Otherwise, I can imagine that it also has an impact on the length of the track between host and slave, in this case the microSD card.
I see the pullup resistors in many example circuits that you find on the Internet.
根据三星microSD卡的数据表,DO信号(SPI模式)被定义为推挽输出。这意味着该信号不需要外部上拉电阻。
According to the datasheet of the microSD card from Samsung, the DO signal (in SPI mode) is defined as a push-pull-output. This means that a external pull-up resistor isn't need on this signal.