📄 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 "LPC2468.h" /* LPC23xx/24xx definitions */
#include "type.h"
#include "irq.h"
#include "mci.h"
#include "dma.h"
volatile DWORD MCI_DataErrorProcess_count = 0;
volatile DWORD MCI_DATA_END_InterruptService_count = 0;
volatile DWORD MCI_FIFOInterruptService_count = 0;
volatile DWORD MCI_CmdProcess_count = 0;
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 MCI_Block_End_Flag = 0;
volatile DWORD DataTxActiveCount = 0;
volatile DWORD DataRxActiveCount = 0;
volatile DWORD DataFIFOCount = 0;
volatile DWORD DataRxFIFOCount = 0;
volatile DWORD DataTxFIFOCount = 0;
volatile DWORD CardRCA;
volatile DWORD MCI_CardType;
/* Assembly modules using the ARM block transfer instruction in readfifo.s. */
extern DWORD MCI_ReadFifo(DWORD * dest);
extern DWORD MCI_WriteFifo(DWORD * src);
volatile BYTE *WriteBlock = (BYTE *)(DMA_SRC); /* treat WriteBlock as a constant address */
volatile BYTE *ReadBlock = (BYTE *)(DMA_DST); /* treat ReadBlock as a constant address */
volatile DWORD TXBlockCounter=0, RXBlockCounter=0;
/******************************************************************************
** Function name: MCI_Interrupt related
**
** Descriptions: MCI interrupt handler and related APIs
**
**
** parameters: None
** Returned value: None
**
******************************************************************************/
void MCI_TXEnable( void )
{
#if MCI_DMA_ENABLED
MCI_MASK0 |= ((DATA_END_INT_MASK)|(ERR_TX_INT_MASK)); /* Enable TX interrupts only */
MCI_MASK1 |= ((DATA_END_INT_MASK)|(ERR_TX_INT_MASK)); /* Enable TX interrupts only */
#else
MCI_MASK0 |= ((FIFO_TX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_TX_INT_MASK)); /* FIFO TX interrupts only */
MCI_MASK1 |= ((FIFO_TX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_TX_INT_MASK)); /* FIFO TX interrupts only */
#endif
return;
}
/*****************************************************************/
void MCI_TXDisable( void )
{
#if MCI_DMA_ENABLED
MCI_MASK0 &= ~((DATA_END_INT_MASK)|(ERR_TX_INT_MASK)); /* Enable TX interrupts only */
MCI_MASK1 &= ~((DATA_END_INT_MASK)|(ERR_TX_INT_MASK)); /* Enable TX interrupts only */
#else
MCI_MASK0 &= ~((FIFO_TX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_TX_INT_MASK)); /* FIFO TX interrupts only */
MCI_MASK1 &= ~((FIFO_TX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_TX_INT_MASK)); /* FIFO TX interrupts only */
#endif
return;
}
/*****************************************************************/
void MCI_RXEnable( void )
{
#if MCI_DMA_ENABLED
MCI_MASK0 |= ((DATA_END_INT_MASK)|(ERR_RX_INT_MASK)); /* Enable RX interrupts only */
MCI_MASK1 |= ((DATA_END_INT_MASK)|(ERR_RX_INT_MASK)); /* Enable RX interrupts only */
#else
MCI_MASK0 |= ((FIFO_RX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_RX_INT_MASK)); /* FIFO RX interrupts only */
MCI_MASK1 |= ((FIFO_RX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_RX_INT_MASK)); /* FIFO RX interrupts only */
#endif
return;
}
/*****************************************************************/
void MCI_RXDisable( void )
{
#if MCI_DMA_ENABLED
MCI_MASK0 &= ~((DATA_END_INT_MASK)|(ERR_RX_INT_MASK)); /* Enable TX interrupts only */
MCI_MASK1 &= ~((DATA_END_INT_MASK)|(ERR_RX_INT_MASK)); /* Enable TX interrupts only */
#else
MCI_MASK0 &= ~((FIFO_RX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_RX_INT_MASK)); /* FIFO TX interrupts only */
MCI_MASK1 &= ~((FIFO_RX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_RX_INT_MASK)); /* FIFO TX interrupts only */
#endif
return;
}
/******************************************************************************
** 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 disabled.
**
**
** parameters: None
** Returned value: None
**
******************************************************************************/
void MCI_CmdProcess( void )
{
DWORD MCIStatus;
MCIStatus = MCI_STATUS;
if ( MCIStatus & MCI_CMD_CRC_FAIL )
{
CmdCRCErrCount++;
MCI_CLEAR = MCI_CMD_CRC_FAIL;
}
if ( MCIStatus & MCI_CMD_TIMEOUT )
{
CmdTimeoutErrCount++;
MCI_CLEAR = MCI_CMD_TIMEOUT;
}
/* Cmd Resp End or Cmd Sent */
if ( MCIStatus & MCI_CMD_RESP_END )
{
CmdRespEndCount++;
MCI_CLEAR = MCI_CMD_RESP_END;
}
if ( MCIStatus & MCI_CMD_SENT )
{
CmdSentCount++;
MCI_CLEAR = MCI_CMD_SENT;
}
if ( MCIStatus & MCI_CMD_ACTIVE )
{
CmdActiveCount++;
MCI_CLEAR = 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 = MCI_DATA_CRC_FAIL;
}
if ( MCIStatus & MCI_DATA_TIMEOUT )
{
DataTimeoutErrCount++;
MCI_CLEAR = MCI_DATA_TIMEOUT;
}
/* Underrun or overrun */
if ( MCIStatus & MCI_TX_UNDERRUN )
{
DataTxUnderrunErrCount++;
MCI_CLEAR = MCI_TX_UNDERRUN;
}
if ( MCIStatus & MCI_RX_OVERRUN )
{
DataRxOverrunErrCount++;
MCI_CLEAR = MCI_RX_OVERRUN;
}
/* Start bit error on data signal */
if ( MCIStatus & MCI_START_BIT_ERR )
{
DataStartbitErrCount++;
MCI_CLEAR = MCI_START_BIT_ERR;
}
return;
}
/******************************************************************************
** Function name: MCI_DATA_END_InterruptService
**
** Descriptions: Called by MCI interrupt handler
** This is the last interrupt module processing
** the block write and read to and from the MM card.
**
** FIFO interrupts are also used when DMA is disabled
** This routine simply clears the
** MCI_Block_End_Flag, and increments counters for debug
**
** parameters: None
** Returned value: None
**
******************************************************************************/
void MCI_DATA_END_InterruptService( void )
{
DWORD MCIStatus;
MCIStatus = MCI_STATUS;
if ( MCIStatus & MCI_DATA_END ) /* Data end, and Data block end */
{
DataEndCount++;
MCI_CLEAR = MCI_DATA_END;
return;
}
if ( MCIStatus & MCI_DATA_BLK_END )
{
DataBlockEndCount++;
MCI_CLEAR = MCI_DATA_BLK_END;
MCI_TXDisable();
MCI_Block_End_Flag = 0;
return;
}
/* Tx active */
if ( MCIStatus & MCI_TX_ACTIVE )
{
DataTxActiveCount++;
}
/* Rx active */
if ( MCIStatus & MCI_RX_ACTIVE )
{
DataRxActiveCount++;
}
return;
}
/******************************************************************************
** Function name: MCI_FIFOInterruptService
**
** Descriptions: Called by MCI interrupt handler when using FIFO
** interrupts and DMA is disabled
**
**
** parameters: None
** Returned value: None
**
******************************************************************************/
void MCI_FIFOInterruptService( void )
{
#if !MCI_DMA_ENABLED
DWORD MCIStatus;
MCIStatus = MCI_STATUS;
if ( MCIStatus & (FIFO_TX_INT_MASK ) )
{
DataTxFIFOCount++; /* if using TX_HALF_EMPTY remove one WriteFifo below */
if ( MCIStatus & MCI_TX_HALF_EMPTY ) /* empty is multiple of 512 block size */
{
MCI_WriteFifo((DWORD *)&WriteBlock[TXBlockCounter]); /* write 8 words to fifo */
TXBlockCounter += 32;
}
if (TXBlockCounter == BLOCK_LENGTH) /* block complete */
{
TXBlockCounter = 0;
MCI_MASK0 &= ~(FIFO_TX_INT_MASK); /* disable FIFO int until next block write */
MCI_MASK1 &= ~(FIFO_TX_INT_MASK);
/* wait for SD card to complete sending data i.e MCI_DATA_BLK_END interrupt */
}
}
else if ( MCIStatus & (FIFO_RX_INT_MASK) )
{
DataRxFIFOCount++;
if ( MCIStatus & MCI_RX_HALF_FULL ) /* if using RX_HALF_FULL remove one ReadFIFO below */
{
MCI_ReadFifo((DWORD *)&ReadBlock[RXBlockCounter]); /* read 8 words from fifo */
RXBlockCounter += 32;
}
if (RXBlockCounter == BLOCK_LENGTH) /* block complete */
{
RXBlockCounter = 0;
}
}
#endif
DataFIFOCount++;
return;
}
/******************************************************************************
** Function name: MCI_IRQHandler
**
** Descriptions: MCI interrupt handler
** The handler to handle the block data write and read
** not for the commands.
**
** parameters: None
** Returned value: None
**
******************************************************************************/
__irq __arm void MCI_IRQHandler (void)
{
DWORD MCI_Status;
MCI_Status = MCI_STATUS;
/* handle MCI_STATUS interrupt */
if ( MCI_Status & DATA_ERR_INT_MASK )
{
MCI_DataErrorProcess();
MCI_DataErrorProcess_count++;
VICVectAddr = 0; /* Acknowledge Interrupt */
return;
}
if ( MCI_Status & DATA_END_INT_MASK )
{
MCI_DATA_END_InterruptService();
MCI_DATA_END_InterruptService_count++;
VICVectAddr = 0; /* Acknowledge Interrupt */
return;
}
else if ( MCI_Status & FIFO_INT_MASK )
{
MCI_FIFOInterruptService();
MCI_FIFOInterruptService_count++;
VICVectAddr = 0; /* Acknowledge Interrupt */
return;
}
else if ( MCI_Status & CMD_INT_MASK )
{
MCI_CmdProcess();
MCI_CmdProcess_count++;
VICVectAddr = 0; /* Acknowledge Interrupt */
return;
}
VICVectAddr = 0; /* Acknowledge Interrupt */
}
/******************************************************************************
** Function name: MCI_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 MCI_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 &= ~(0xFF); /* clear clock divider */
MCI_CLOCK |= (1 << 8) |ClkValue;
for ( i = 0; i < 0x10; i++ ); /* delay 3MCLK + 2PCLK before next write */
return;
}
/******************************************************************************
** Function name: SD_Set_BusWidth
**
** Descriptions: 1-bit bus or 4-bit bus.
**
** parameters: bus width
** Returned value: TRUE or FALSE
**
******************************************************************************/
DWORD SD_Set_BusWidth( DWORD width )
{
DWORD i;
for ( i = 0; i < 0x10; i++ ); /* delay 3MCLK + 2PCLK */
if ( width == SD_1_BIT )
{
MCI_CLOCK &= ~(1 << 11); /* 1 bit bus */
}
else if ( width == SD_4_BIT )
{
MCI_CLOCK |= (1 << 11);/* 4 bit bus */
}
if ( MCI_Send_ACMD_Bus_Width( BUS_WIDTH_4BITS ) == FALSE )
{
return( FALSE );
}
return TRUE;
}
/******************************************************************************
** 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
**
******************************************************************************/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -