您的位置:首页 > 其它

at91_sdcard源码分析(4)-命令的发送

2014-03-23 22:46 357 查看
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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: