SendCommand函数发送一个命令到SD卡:
//------------------------------------------------------------------------------
/// Sends the current SD card driver command to the card.
/// Returns 0 if successful; Otherwise, returns the transfer status code or
/// SD_ERROR_DRIVER if there was a problem with the SD transfer.
/// \param pSd Pointer to a SdCard driver instance.
//------------------------------------------------------------------------------
static unsigned char SendCommand(SdCard *pSd)
{
SdCmd *pCommand = &(pSd->command);
SdDriver *pSdDriver = pSd->pSdDriver;
unsigned char error;
unsigned int i;
// Send command
error = MCI_SendCommand((Mci *)pSdDriver, (MciCmd *)pCommand);
if (error) {
TRACE_ERROR("MCI SendCommand: Failed to send command (%d)\n\r", error);
return SD_ERROR_DRIVER;
}
// Wait for command to complete
while (!MCI_IsTxComplete((MciCmd *)pCommand));//等待命令发送完成并接收到回应
if(pCommand->cmd == AT91C_STOP_TRANSMISSION_CMD) {
while (MCI_CheckBusy((Mci *)pSdDriver) != 0);
}
// Delay between sending commands, only for MMC card test.
if((pSd->cardType == CARD_MMC)
||(pSd->cardType == UNKNOWN_CARD)
||(pSd->cardType == CARD_SD)) {
for(i=0; i < MMC_DELAY; i++);
}
return pCommand->status;
}
调用MCI_SendCommand,然后等待命令发送完成并接收到回应。在MCI_SendCommand中会设置pCommand->status 的值为MCI_STATUS_PENDING,在中断处理中,如果成功,则设置pCommand->status 的值为0,如果超时,则设置为MCI_STATUS_NORESPONSE,如果发生错误,则设置值为MCI_STATUS_ERROR。在MCI_IsTxComplete中,则会根据pCommand->status的值是否为MCI_STATUS_PENDING来判断一次传输是否完成。
MCI_SendCommand:
//------------------------------------------------------------------------------
/// Starts a MCI transfer. This is a non blocking function. It will return
/// as soon as the transfer is started.
/// Return 0 if successful; otherwise returns MCI_ERROR_LOCK if the driver is
/// already in use.
/// \param pMci Pointer to an MCI driver instance.
/// \param pCommand Pointer to the command to execute.
//------------------------------------------------------------------------------
unsigned char MCI_SendCommand(Mci *pMci, MciCmd *pCommand)
{
AT91PS_MCI pMciHw = pMci->pMciHw;
unsigned int mciIer, mciMr;
SANITY_CHECK(pMci);
SANITY_CHECK(pMciHw);
SANITY_CHECK(pCommand);
// Try to acquire the MCI semaphore
//判断信号量
if (pMci->semaphore == 0) {
return MCI_ERROR_LOCK;
}
pMci->semaphore--;
// TRACE_DEBUG("MCI_SendCommand %x %d\n\r", READ_MCI(pMciHw, MCI_SR), pCommand->cmd & 0x3f);
// Command is now being executed
pMci->pCommand = pCommand;
pCommand->status = MCI_STATUS_PENDING;
// Enable the MCI clock
WRITE_PMC(AT91C_BASE_PMC, PMC_PCER, (1 << pMci->mciId));
//Disable MCI clock, for multi-block data transfer
MCI_Enable(pMci, DISABLE); //设置MCI_CR的第0位(Multi-Media Interface Enable)
// Set PDC data transfer direction
//根据是读或写来设置PDC(DMA),启动读或写操作
if(pCommand->blockSize > 0) {
if(pCommand->isRead) {
WRITE_MCI(pMciHw, MCI_PTCR, AT91C_PDC_RXTEN);
}
else {
WRITE_MCI(pMciHw, MCI_PTCR, AT91C_PDC_TXTEN);
}
}
// Disable transmitter and receiver
WRITE_MCI(pMciHw, MCI_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS);
mciMr = READ_MCI(pMciHw, MCI_MR) & (~(AT91C_MCI_WRPROOF|AT91C_MCI_RDPROOF|AT91C_MCI_BLKLEN | AT91C_MCI_PDCMODE));
// Command with DATA stage
if (pCommand->blockSize > 0) { //blockSize的大小不为0,说明有数据传输
// Enable PDC mode and set block size
if(pCommand->conTrans != MCI_CONTINUE_TRANSFER) {
//设置block的大小
WRITE_MCI(pMciHw, MCI_MR, mciMr | AT91C_MCI_PDCMODE |AT91C_MCI_RDPROOF|AT91C_MCI_WRPROOF|(pCommand->blockSize << 16));
}
// DATA transfer from card to host
if (pCommand->isRead) {
WRITE_MCI(pMciHw, MCI_RPR, (int) pCommand->pData);//设置接收数据的地址
// Sanity check
if (pCommand->nbBlock == 0)
pCommand->nbBlock = 1;//保证最少有一个block传输。
if ((pCommand->blockSize & 0x3) != 0) {
WRITE_MCI(pMciHw, MCI_RCR, (pCommand->nbBlock * pCommand->blockSize) / 4 + 1);
}
else {
WRITE_MCI(pMciHw, MCI_RCR, (pCommand->nbBlock * pCommand->blockSize) / 4);
}
WRITE_MCI(pMciHw, MCI_PTCR, AT91C_PDC_RXTEN);
mciIer = AT91C_MCI_ENDRX | STATUS_ERRORS; //设置需要打开中断
// mciIer = AT91C_MCI_RXBUFF | STATUS_ERRORS;
}
// DATA transfer from host to card
else {
// Sanity check
if (pCommand->nbBlock == 0)
pCommand->nbBlock = 1;
WRITE_MCI(pMciHw, MCI_TPR, (int) pCommand->pData);
// Update the PDC counter
if ((pCommand->blockSize & 0x3) != 0) {
WRITE_MCI(pMciHw, MCI_TCR, (pCommand->nbBlock * pCommand->blockSize) / 4 + 1);
}
else {
WRITE_MCI(pMciHw, MCI_TCR, (pCommand->nbBlock * pCommand->blockSize) / 4);
}
// MCI_BLKE notifies the end of Multiblock command
mciIer = AT91C_MCI_BLKE | STATUS_ERRORS; //设置需要打开中断
}
}
// No data transfer: stop at the end of the command
else { //没有数据需要传输的情况
WRITE_MCI(pMciHw, MCI_MR, mciMr);
mciIer = AT91C_MCI_CMDRDY | STATUS_ERRORS; //设置需要打开中断
}
// Enable MCI clock
MCI_Enable(pMci, ENABLE);
// Send the command
if((pCommand->conTrans != MCI_CONTINUE_TRANSFER)
|| (pCommand->blockSize == 0)) {
WRITE_MCI(pMciHw, MCI_ARGR, pCommand->arg);
WRITE_MCI(pMciHw, MCI_CMDR, pCommand->cmd);
}
// In case of transmit, the PDC shall be enabled after sending the command
if ((pCommand->blockSize > 0) && !(pCommand->isRead)) {
WRITE_MCI(pMciHw, MCI_PTCR, AT91C_PDC_TXTEN);
}
// Ignore data error
mciIer &= ~(AT91C_MCI_UNRE | AT91C_MCI_OVRE \
| AT91C_MCI_DTOE | AT91C_MCI_DCRCE);
// Interrupt enable shall be done after PDC TXTEN and RXTEN
WRITE_MCI(pMciHw, MCI_IER, mciIer);
return 0;
}
此函数设置
(1)设置PDC(DMA),启动读或写操作;
(2)设置block的大小;如果设置了PDCFBYTE位,需要保证设置的block的大小是4的整数倍;
(3)设置PDC;
(4)设置中断屏蔽;
MCI_IsTxComplete:
//------------------------------------------------------------------------------
/// Returns 1 if the given MCI transfer is complete; otherwise returns 0.
/// \param pCommand Pointer to a MciCmd instance.
//------------------------------------------------------------------------------
unsigned char MCI_IsTxComplete(MciCmd *pCommand)
{
if (pCommand->status != MCI_STATUS_PENDING) {
if (pCommand->status != 0) {
TRACE_DEBUG("MCI_IsTxComplete %d\n\r", pCommand->status);
}
return 1;
}
else {
return 0;
}
}
在发送前会设置pCommand->status= MCI_STATUS_PENDING,发送完成后会,如果成功,则设置pCommand->status 的值为0,如果超时,则设置为MCI_STATUS_NORESPONSE,如果发生错误,则设置值为MCI_STATUS_ERROR。