📄 sdspi.c
字号:
//------------------------------------------------------------------------------
/// Get response after send data.
/// Return 0 if no error, otherwise indicate an error.
/// \param pSdSpi Pointer to the SD SPI instance.
/// \param pCommand Pointer to the SD command
//------------------------------------------------------------------------------
unsigned char SDSPI_GetDataResp(SdSpi *pSdSpi, SdSpiCmd *pCommand)
{
unsigned char resp = 0; // response
unsigned char error;
unsigned int respRetry = 18; //NCR max 8, refer to card datasheet
// Wait for response start bit.
do {
error = SDSPI_Read(pSdSpi, &resp, 1);
if (error) {
return error;
}
if (((resp & 0x11) == 0x1) || ((resp & 0xf0) == 0))
break;
respRetry--;
} while(respRetry > 0);
//TRACE_DEBUG("SDSPI_GetDataResp 0x%X\n\r",resp);
return resp;
}
//------------------------------------------------------------------------------
/// Starts a SPI master transfer. This is a non blocking function. It will
/// return as soon as the transfer is started.
/// Returns 0 if the transfer has been started successfully; otherwise returns
/// error.
/// \param pSdSpi Pointer to a SdSpi instance.
/// \param pCommand Pointer to the SPI command to execute.
//------------------------------------------------------------------------------
unsigned char SDSPI_SendCommand(SdSpi *pSdSpi, SdSpiCmd *pCommand)
{
AT91S_SPI *pSpiHw = pSdSpi->pSpiHw;
unsigned char CmdToken[6];
unsigned char *pData;
unsigned int blockSize;
unsigned int i;
unsigned char error;
unsigned char dataHeader;
unsigned int dataRetry1 = 100;
unsigned int dataRetry2 = 100;
unsigned char crc[2];
unsigned char crcPrev = 0;
unsigned char crcPrev2 = 0;
SANITY_CHECK(pSdSpi);
SANITY_CHECK(pSpiHw);
SANITY_CHECK(pCommand);
CmdToken[0] = pCommand->cmd & 0x3F;
pData = pCommand->pData;
blockSize = pCommand->blockSize;
SDSPI_MakeCmd((unsigned char *)&CmdToken, pCommand->arg);
// Command is now being executed
pSdSpi->pCommand = pCommand;
pCommand->status = SDSPI_STATUS_PENDING;
// Send the command
if((pCommand->conTrans == SPI_NEW_TRANSFER) || (blockSize == 0)) {
for(i = 0; i < 6; i++) {
error = SDSPI_Write(pSdSpi, &CmdToken[i], 1);
if (error) {
TRACE_DEBUG("Error: %d\n\r", error);
return error;
}
}
// Specific for Cmd12()
if ((pCommand->cmd & 0x3F) == 12) {
if( 1 == SDSPI_Wait(pSdSpi, 2) ) {
TRACE_DEBUG("Pb Send command 12\n\r");
}
}
if (pCommand->pResp) {
error = SDSPI_GetCmdResp(pSdSpi, pCommand);
if (error) {
TRACE_DEBUG("Error: %d\n\r", error);
return error;
}
}
}
if( (blockSize > 0) && (pCommand->nbBlock == 0) ) {
pCommand->nbBlock = 1;
}
// For data block operations
while (pCommand->nbBlock > 0) {
// If data block size is invalid, return error
if (blockSize == 0) {
TRACE_DEBUG("Block Size = 0\n\r");
return 1;
}
// DATA transfer from card to host
if (pCommand->isRead) {
do {
SDSPI_Read(pSdSpi, &dataHeader, 1);
dataRetry1 --;
if (dataHeader == SDSPI_START_BLOCK_1) {
break;
}
else if((dataHeader & 0xf0) == 0x00) {
pCommand->status = SDSPI_STATUS_ERROR;
TRACE_DEBUG("Data Error 0x%X!\n\r", dataHeader);
return 1;
}
} while(dataRetry1 > 0);
if (dataRetry1 == 0) {
TRACE_DEBUG("Timeout dataretry1\n\r");
return 1;
}
SDSPI_Read(pSdSpi, pData, blockSize);
// Specific for Cmd9()
if ((pCommand->cmd & 0x3f) != 0x9) {
SDSPI_Read(pSdSpi, crc, 2);
#ifdef SDSPI_CRC_ON
// Check data CRC
TRACE_DEBUG("Check Data CRC\n\r");
crcPrev = 0;
crcPrev2 = 0;
if (crc[0] != ((crc_itu_t(crcPrev, pData, blockSize) & 0xff00) >> 8 )
|| crc[1] != (crc_itu_t(crcPrev2, pData, blockSize) & 0xff)) {
TRACE_ERROR("CRC error 0x%X 0x%X 0x%X\n\r", \
crc[0], crc[1], crc_itu_t(pData, blockSize));
return 1;
}
#endif
}
}
// DATA transfer from host to card
else {
SDSPI_NCS(pSdSpi);
if ((pCommand->conTrans == SPI_CONTINUE_TRANSFER) || ((pCommand->cmd & 0x3f) == 25)) {
dataHeader = SDSPI_START_BLOCK_2;
}
else {
dataHeader = SDSPI_START_BLOCK_1;
}
crcPrev = 0;
crc[0] = (crc_itu_t(crcPrev, pData, blockSize) & 0xff00) >> 8;
crcPrev2 = 0;
crc[1] = (crc_itu_t(crcPrev2, pData, blockSize) & 0xff);
SDSPI_Write(pSdSpi, &dataHeader, 1);
SDSPI_Write(pSdSpi, pData, blockSize);
SDSPI_Write(pSdSpi, crc, 2);
// If status bits in data response is not "data accepted", return error
if ((SDSPI_GetDataResp(pSdSpi, pCommand) & 0xe) != 0x4) {
TRACE_ERROR("Write resp error!\n\r");
return 1;
}
do {
if (SDSPI_WaitDataBusy(pSdSpi) == 0) {
break;
}
dataRetry2--;
} while(dataRetry2 > 0);
}
pData += blockSize;
pCommand->nbBlock--;
}
if (pCommand->status == SDSPI_STATUS_PENDING) {
pCommand->status = 0;
}
//TRACE_DEBUG("end SDSPI_SendCommand\n\r");
return 0;
}
//!
//------------------------------------------------------------------------------
/// The SPI_Handler must be called by the SPI Interrupt Service Routine with the
/// corresponding Spi instance.
/// The SPI_Handler will unlock the Spi semaphore and invoke the upper application
/// callback.
/// \param pSdSpi Pointer to a SdSpi instance.
//------------------------------------------------------------------------------
void SDSPI_Handler(SdSpi *pSdSpi)
{
SdSpiCmd *pCommand = pSdSpi->pCommand;
AT91S_SPI *pSpiHw = pSdSpi->pSpiHw;
volatile unsigned int spiSr;
// Read the status register
spiSr = pSpiHw->SPI_SR;
if(spiSr & AT91C_SPI_RXBUFF) {
if (pCommand->status == SDSPI_STATUS_PENDING) {
pCommand->status = 0;
}
// Disable transmitter and receiver
pSpiHw->SPI_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS;
// Disable the SPI clock
AT91C_BASE_PMC->PMC_PCDR = (1 << pSdSpi->spiId);
// Disable buffer complete interrupt
pSpiHw->SPI_IDR = AT91C_SPI_RXBUFF | AT91C_SPI_ENDTX;
// Release the SPI semaphore
pSdSpi->semaphore++;
}
// Invoke the callback associated with the current command
if (pCommand && pCommand->callback) {
pCommand->callback(0, pCommand);
}
}
//------------------------------------------------------------------------------
/// Returns 1 if the given SPI transfer is complete; otherwise returns 0.
/// \param pCommand Pointer to a SdSpiCmd instance.
//------------------------------------------------------------------------------
unsigned char SDSPI_IsTxComplete(SdSpiCmd *pCommand)
{
if (pCommand->status != SDSPI_STATUS_PENDING) {
if (pCommand->status != 0){
TRACE_DEBUG("SPI_IsTxComplete %d\n\r", pCommand->status);
}
return 1;
}
else {
return 0;
}
}
//------------------------------------------------------------------------------
/// Close a SPI driver instance and the underlying peripheral.
/// \param pSdSpi Pointer to a SD SPI driver instance.
//------------------------------------------------------------------------------
void SDSPI_Close(SdSpi *pSdSpi)
{
AT91S_SPI *pSpiHw = pSdSpi->pSpiHw;
SANITY_CHECK(pSdSpi);
SANITY_CHECK(pSpiHw);
// Enable the SPI clock
AT91C_BASE_PMC->PMC_PCER = (1 << pSdSpi->spiId);
// Disable the PDC transfer
pSpiHw->SPI_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS;
// Enable the SPI
pSpiHw->SPI_CR = AT91C_SPI_SPIDIS;
// Disable the SPI clock
AT91C_BASE_PMC->PMC_PCDR = (1 << pSdSpi->spiId);
// Disable all the interrupts
pSpiHw->SPI_IDR = 0xFFFFFFFF;
}
//------------------------------------------------------------------------------
/// Returns 1 if the SPI driver is currently busy programming;
/// otherwise returns 0.
/// \param pSdSpi Pointer to a SD SPI driver instance.
//------------------------------------------------------------------------------
unsigned char SDSPI_IsBusy(SdSpi *pSdSpi)
{
if (pSdSpi->semaphore == 0) {
return 1;
}
else {
return 0;
}
}
//------------------------------------------------------------------------------
/// Wait several cycles on SPI bus;
/// Returns 0 to indicates no error, otherwise return 1.
/// \param pSdSpi Pointer to a SD SPI driver instance.
/// \param cycles Wait data cycles.
//------------------------------------------------------------------------------
unsigned char SDSPI_Wait(SdSpi *pSdSpi, unsigned int cycles)
{
unsigned int i = cycles;
unsigned char data = 0xff;
for (; i > 0; i--) {
if (SDSPI_Read(pSdSpi, &data, 1)) {
return 1;
}
}
return 0;
}
//------------------------------------------------------------------------------
/// Send stop transfer data token;
/// Returns 0 to indicates no error, otherwise return 1.
/// \param pSdSpi Pointer to a SD SPI driver instance.
//------------------------------------------------------------------------------
unsigned char SDSPI_StopTranToken(SdSpi *pSdSpi)
{
unsigned char stopToken = SDSPI_STOP_TRAN;
TRACE_DEBUG("SDSPI_StopTranToken\n\r");
return SDSPI_Write(pSdSpi, &stopToken, 1);
}
//------------------------------------------------------------------------------
/// Wait, SD card Ncs cycles;
/// Returns 0 to indicates no error, otherwise return 1.
/// \param pSdSpi Pointer to a SD SPI driver instance.
//------------------------------------------------------------------------------
unsigned char SDSPI_NCS(SdSpi *pSdSpi)
{
unsigned int i;
unsigned char ncs;
for(i = 0; i < 15; i++) {
ncs = 0xff;
if (SDSPI_Write(pSdSpi, &ncs, 1)) {
return 1;
}
}
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -