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。