📄 mci.c
字号:
/*****************************************************************************
* mci.c: SD/MMC module file for NXP LPC23xx/24xx Family Microprocessors
*
* Copyright(C) 2006, NXP Semiconductor
* All rights reserved.
*
* History
* 2006.07.20 ver 1.00 Prelimnary version, first Release
*
******************************************************************************/
#include "LPC23xx.h" /* LPC23xx/24xx definitions */
#include "type.h"
#include "irq.h"
#include "timer.h"
#include "mci.h"
#include "dma.h"
volatile DWORD CmdCRCErrCount = 0;
volatile DWORD CmdTimeoutErrCount = 0;
volatile DWORD CmdRespEndCount = 0;
volatile DWORD CmdSentCount = 0;
volatile DWORD CmdActiveCount = 0;
volatile DWORD DataCRCErrCount = 0;
volatile DWORD DataTimeoutErrCount = 0;
volatile DWORD DataTxUnderrunErrCount = 0;
volatile DWORD DataRxOverrunErrCount = 0;
volatile DWORD DataStartbitErrCount = 0;
volatile DWORD DataEndCount = 0;
volatile DWORD DataBlockEndCount = 0;
volatile DWORD DataTxActiveCount = 0;
volatile DWORD DataRxActiveCount = 0;
volatile DWORD DataFIFOCount = 0;
volatile DWORD CardRCA;
volatile DWORD CardType;
#if MCI_DMA_ENABLED
extern DWORD *src_addr;
extern DWORD *dest_addr;
#else
extern volatile BYTE WriteBlock[BLOCK_LENGTH], ReadBlock[BLOCK_LENGTH];
extern volatile DWORD TXBlockCounter, RXBlockCounter;
#endif
/******************************************************************************
** Function name: MCI_Interrupt related
**
** Descriptions: MCI interrupt handler and related APIs
**
**
** parameters: None
** Returned value: None
**
******************************************************************************/
void MCI_TXEnable( void )
{
MCI_MASK0 |= DATA_TX_INT_MASK; /* Enable TX interrupts only */
MCI_MASK1 = MCI_MASK0;
}
void MCI_TXDisable( void )
{
MCI_MASK0 &= ~DATA_TX_INT_MASK; /* Disable TX interrupts only */
MCI_MASK1 = MCI_MASK0;
}
/*****************************************************************/
void MCI_RXEnable( void )
{
MCI_MASK0 |= DATA_RX_INT_MASK; /* Enable RX interrupts only */
MCI_MASK1 = MCI_MASK0;
}
void MCI_RXDisable( void )
{
MCI_MASK0 &= ~DATA_RX_INT_MASK; /* Disable RX interrupts only */
MCI_MASK1 = MCI_MASK0;
}
/******************************************************************************
** Function name: MCI_CheckStatus
**
** Descriptions: MCI Check status before and after the block read and
** write. Right after the block read and write, this routine
** is important that, even the FIFO is empty, complete
** block has been sent, but, data is still being written
** to the card, this routine is to ensure that the data
** has been written based on the state of the card, not
** by the length being set.
**
** parameters: None
** Returned value: TRUE or FALSE
**
******************************************************************************/
DWORD MCI_CheckStatus( void )
{
DWORD respValue;
while ( 1 )
{
if ( (respValue = MCI_Send_Status()) == INVALID_RESPONSE )
{
break;
}
else
{
/* The only valid state is TRANS per MMC and SD state diagram.
RCV state may be seen, but, I have found that it happens
only when TX_ACTIVE or RX_ACTIVE occurs before the WRITE_BLOCK and
READ_BLOCK cmds are being sent, which is not a valid sequence. */
if ( (respValue & (0x0F << 8)) == 0x0900 )
{
return ( TRUE );
}
}
}
return ( FALSE );
}
/******************************************************************************
** Function name: MCI_CmdProcess
**
** Descriptions: Called by MCI interrupt handler
** To simplify the process, for card initialization, the
** CMD interrupts are disable.
**
**
** parameters: None
** Returned value: None
**
******************************************************************************/
void MCI_CmdProcess( void )
{
DWORD MCIStatus;
MCIStatus = MCI_STATUS;
if ( MCIStatus & MCI_CMD_CRC_FAIL )
{
CmdCRCErrCount++;
MCI_CLEAR = MCIStatus | MCI_CMD_CRC_FAIL;
}
if ( MCIStatus & MCI_CMD_TIMEOUT )
{
CmdTimeoutErrCount++;
MCI_CLEAR = MCIStatus | MCI_CMD_TIMEOUT;
}
/* Cmd Resp End or Cmd Sent */
if ( MCIStatus & MCI_CMD_RESP_END )
{
CmdRespEndCount++;
MCI_CLEAR = MCIStatus | MCI_CMD_RESP_END;
}
if ( MCIStatus & MCI_CMD_SENT )
{
CmdSentCount++;
MCI_CLEAR = MCIStatus | MCI_CMD_SENT;
}
if ( MCIStatus & MCI_CMD_ACTIVE )
{
CmdActiveCount++;
MCI_CLEAR = MCIStatus | MCI_CMD_ACTIVE;
}
return;
}
/******************************************************************************
** Function name: MCI_DataErrorProcess
**
** Descriptions: Called by MCI interrupt handler
** Process data error.
**
** parameters: None
** Returned value: None
**
******************************************************************************/
void MCI_DataErrorProcess( void )
{
DWORD MCIStatus;
MCIStatus = MCI_STATUS;
if ( MCIStatus & MCI_DATA_CRC_FAIL )
{
DataCRCErrCount++;
MCI_CLEAR = MCIStatus | MCI_DATA_CRC_FAIL;
}
if ( MCIStatus & MCI_DATA_TIMEOUT )
{
DataTimeoutErrCount++;
MCI_CLEAR = MCIStatus | MCI_DATA_TIMEOUT;
}
/* Underrun or overrun */
if ( MCIStatus & MCI_TX_UNDERRUN )
{
DataTxUnderrunErrCount++;
MCI_CLEAR = MCIStatus | MCI_TX_UNDERRUN;
}
if ( MCIStatus & MCI_RX_OVERRUN )
{
DataRxOverrunErrCount++;
MCI_CLEAR = MCIStatus | MCI_RX_OVERRUN;
}
/* Start bit error on data signal */
if ( MCIStatus & MCI_START_BIT_ERR )
{
DataStartbitErrCount++;
MCI_CLEAR = MCIStatus | MCI_START_BIT_ERR;
}
return;
}
/******************************************************************************
** Function name: MCI_DataInterruptProcess
**
** Descriptions: Called by MCI interrupt handler
** This is the key module processing the block write
** read to from the card. The FIFO interrupts are not
** handled, the routine mainly use TX_ACTIVE and RX_ACTIVE
** interrupts to handle a block write and read.
** There is an ugly polling right after the block write
** and read to handle the situation that, the block has
** been sent, but the prograamming to the card is still
** in progress. This needs to be done when TX_ACTIVE or
** RX_ACTIVE is still set. In the real application, during
** the card programming, a semiphone like signaling will be
** needed.
**
** parameters: None
** Returned value: None
**
******************************************************************************/
void MCI_DataInterruptProcess( void )
{
DWORD MCIStatus;
#if !MCI_DMA_ENABLED
DWORD i, blockSize, DataWord;
#endif
MCIStatus = MCI_STATUS;
if ( MCIStatus & MCI_DATA_END ) /* Data end, and Data block end */
{
DataEndCount++;
MCI_CLEAR = MCIStatus | MCI_DATA_END;
}
if ( MCIStatus & MCI_DATA_BLK_END )
{
DataBlockEndCount++;
MCI_CLEAR = MCIStatus | MCI_DATA_BLK_END;
}
/* Tx active, and Rx active */
if ( MCI_STATUS & MCI_TX_ACTIVE )
{
DataTxActiveCount++;
#if MCI_DMA_ENABLED
while ( !(GPDMA_RAW_INT_TCSTAT & 0x01) );
GPDMA_INT_TCCLR |= 0x01;
#else
while ( TXBlockCounter < BLOCK_LENGTH )
{
blockSize = 0;
if ( MCI_STATUS & MCI_TX_FIFO_EMPTY )
{
blockSize = FIFO_SIZE * 4;
}
else if ( MCI_STATUS & MCI_TX_HALF_EMPTY )
{
blockSize = (FIFO_SIZE/2) * 4;
}
else if ( !(MCI_STATUS & MCI_TX_FIFO_FULL) )
{
blockSize = 1 * 4;
}
for ( i = 0; i < blockSize; i += 4 )
{
DataWord = (DWORD)(WriteBlock[TXBlockCounter]) | (DWORD)(WriteBlock[TXBlockCounter+1] << 8)
| (DWORD)(WriteBlock[TXBlockCounter+2] << 16) | (DWORD)(WriteBlock[TXBlockCounter+3] << 24);
MCI_FIFO = DataWord;
TXBlockCounter += 4;
}
}
#endif
/* Data has been written, but has not been programmed into the card yet. */
/* Even it's a dead while loop inside the ISR, but, during the
flash writing, there is not much else you should do. If it's not in the
TRAN state, per MMC/SD state diagram, send STOP to bail out. */
if ( MCI_CheckStatus() != TRUE )
{
MCI_Send_Stop();
}
MCI_DATA_CTRL = 0;
}
if ( MCI_STATUS & MCI_RX_ACTIVE )
{
DataRxActiveCount++;
#if MCI_DMA_ENABLED
while ( !(GPDMA_RAW_INT_TCSTAT & 0x02) );
GPDMA_INT_TCCLR |= 0x02;
#else
while ( RXBlockCounter < BLOCK_LENGTH )
{
if ( !(MCI_STATUS & MCI_RX_FIFO_FULL) )
{
blockSize = FIFO_SIZE * 4;
}
else if ( MCI_STATUS & MCI_RX_HALF_FULL )
{
blockSize = (FIFO_SIZE/2) * 4;
}
else if ( MCI_STATUS & MCI_RX_FIFO_EMPTY )
{
blockSize = 1 * 4;
}
else
{
blockSize = 0;
}
for ( i = 0; i < blockSize; i += 4 )
{
while ( !(MCI_STATUS & MCI_RX_DATA_AVAIL) );
DataWord = MCI_FIFO;
ReadBlock[RXBlockCounter] = DataWord & 0xFF;
ReadBlock[RXBlockCounter+1] = (DataWord >> 8) & 0xFF;
ReadBlock[RXBlockCounter+2] = (DataWord >> 16) & 0xFF;
ReadBlock[RXBlockCounter+3] = (DataWord >> 24) & 0xFF;
RXBlockCounter += 4;
}
}
#endif
if ( MCI_CheckStatus() != TRUE )
{
MCI_Send_Stop();
}
MCI_DATA_CTRL = 0;
}
return;
}
/******************************************************************************
** Function name: MCI_FIFOProcess
**
** Descriptions: Called by MCI interrupt handler
** FIFO interrupts are disabled.
**
** parameters: None
** Returned value: None
**
******************************************************************************/
void MCI_FIFOProcess( void )
{
DWORD MCIStatus;
MCIStatus = MCI_STATUS;
DataFIFOCount++;
return;
}
/******************************************************************************
** Function name: MCI_Handler
**
** Descriptions: MCI interrupt handler
** The handler to handle the block data write and read
** not for the commands.
**
** parameters: None
** Returned value: None
**
******************************************************************************/
void MCI_Handler (void) __irq
{
/* handle MCI_STATUS interrupt */
if ( MCI_STATUS & CMD_INT_MASK )
{
MCI_CmdProcess();
}
else if ( MCI_STATUS & DATA_ERR_INT_MASK )
{
MCI_DataErrorProcess();
}
else if ( MCI_STATUS & DATA_INT_MASK )
{
MCI_DataInterruptProcess();
}
else if ( MCI_STATUS & FIFO_INT_MASK )
{
MCI_FIFOProcess();
}
VICVectAddr = 0; /* Acknowledge Interrupt */
}
/******************************************************************************
** Function name: Set_MCIClock
**
** Descriptions: Set MCI clock rate, during initialization phase < 400K
** during data phase < 20Mhz.
**
** parameters: Clock rate to be set
** Returned value: None
**
******************************************************************************/
void Set_MCIClock( DWORD ClockRate )
{
DWORD i, ClkValue = 0;
if ( ClockRate == SLOW_RATE )
{
ClkValue |= MCLKDIV_SLOW; /* slow clock */
}
else if ( ClockRate == NORMAL_RATE )
{
ClkValue |= MCLKDIV_NORMAL; /* normal clock */
}
MCI_CLOCK |= (1 << 8) | (1 << 9) | ClkValue;
for ( i = 0; i < 0x10; i++ ); /* delay 3MCLK + 2PCLK before next write */
return;
}
/******************************************************************************
** Function name: MCI_Init
**
** Descriptions: Set MCI clock and power registers, setup VIC for
** data interrupt.
**
** parameters: None
** Returned value: true or fase, if VIC table is full, return false
**
******************************************************************************/
DWORD MCI_Init( void )
{
DWORD i;
if ( MCI_CLOCK & (1 << 8) )
{
MCI_CLOCK &= ~(1 << 8);
}
if ( MCI_POWER & 0x02 )
{
MCI_POWER = 0x00;
}
for ( i = 0; i < 0x1000; i++ );
PCONP |= ( 1 << 28 ); /* Enable clock to the MCI block */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -