SDCard HSMCI or SPI

Discussion around product based on ARM Cortex M4 core.

Moderators: nferre, ncollot

Sam++
Posts: 16
Joined: Tue Jul 01, 2014 9:05 pm

SDCard HSMCI or SPI

Wed Aug 06, 2014 10:36 am

Hi All,

I'm trying to implement a driver for 800 x 480 TFT & Touchscreen. I encounter a problem when loading bitmap data from a SD card :

Code: Select all

void SPg_SSD1963::DrawBitmap(SPg_Bitmap* pBitmap, const SPg_Point& p)
{
	if(		'B' != pBitmap->m_pBmp->HeaderField[0]
		||	'M' != pBitmap->m_pBmp->HeaderField[1]
		||	  1 != pBitmap->m_pBmp->ColorPlanes
		||	 24 != pBitmap->m_pBmp->BitsPerPixel
		||	  0 != pBitmap->m_pBmp->Compression
	  )
	{
		return;
	}
	
	*m_pCmd = SSD1963_CMD_SET_COLUMN_ADDRESS;
	*m_pData = 0;
	*m_pData =	0;
	*m_pData = 3;
	*m_pData = 31;
	
	*m_pCmd = SSD1963_CMD_SET_PAGE_ADDRESS;
	*m_pData = 0;
	*m_pData = 0;
	*m_pData = 1;
	*m_pData = 223;
	
	*m_pCmd = SSD1963_CMD_WRITE_MEMORY_START;
	 
	uint8_t*	pPixels = new uint8_t[pBitmap->m_pBmp->WidthInPixels * pBitmap->m_pBmp->BitsPerPixel/8];
	uint32_t	PixelsPerLine = pBitmap->m_pBmp->WidthInPixels * pBitmap->m_pBmp->BitsPerPixel/8 / 3;
	 
	for(uint32_t line = pBitmap->m_pBmp->HeightInPixels - 1; line > 0;  line--)
	{
		pBitmap->GetLinePixels(pPixels, line);
		uint8_t* pPixel = pPixels;
		uint32_t c = PixelsPerLine;
		while(c--)
		{
			*m_pData = pPixel[2];
			*m_pData = pPixel[1];
			*m_pData = *pPixel;
			pPixel += 3;
		}
	}

	delete [] pPixels;
}

Code: Select all

void SPg_fileBitmap::GetLinePixels(uint8_t* buffer, uint32_t line)
{
	UINT	NbrOfBytesRead;
	
	FRESULT fRslt = f_lseek(&m_File, m_pBmp->OffsetToPixelArray + (line * m_pBmp->WidthInPixels * 3));
	fRslt = f_read(&m_File, buffer, m_pBmp->WidthInPixels * 3, &NbrOfBytesRead);
}
The problem manifests itselves when reading pixels from an HSMCI implementation :

Code: Select all

void SPg_HSMCard::ReadSingleBlock(void* pBuffer, uint32_t sector)
{
LA_SD_Trigger.Off();
LA_SD_Trigger.On();

	Set_BlockLength();
	
	HSMCI->HSMCI_MR |= HSMCI_MR_PDCMODE;
	HSMCI->HSMCI_BLKR = HSMCI_BLKR_BLKLEN(512);
	HSMCI->HSMCI_RPR = (uint32_t)pBuffer;
	HSMCI->HSMCI_RCR = ((512) / 4);
	HSMCI->HSMCI_PTCR = HSMCI_PTCR_RXTEN;
	if(m_bIsHCXC)	//	Byte address or Block Address ?
		HSMCI->HSMCI_ARGR = sector;
	else
		HSMCI->HSMCI_ARGR = sector * 512;
	
	HSMCI->HSMCI_CMDR	=	HSMCI_CMDR_CMDNB(17) 
						|	HSMCI_CMDR_MAXLAT_64 
						|	HSMCI_CMDR_RSPTYP_48_BIT 
						|	HSMCI_CMDR_TRTYP_BYTE
						|	HSMCI_CMDR_TRDIR_READ 
						|	HSMCI_CMDR_TRCMD_START_DATA;
	
	while(HSMCI_SR_ENDRX != (HSMCI->HSMCI_SR  & HSMCI_SR_ENDRX));

LA_SD_Trigger.Off();
}

void SPg_HSMCard::ReadMultipleBlocks(void* pBuffer, uint32_t sector, uint32_t nbrOfBlocks)
{
	uint32_t CurrentSector = sector;
	uint32_t EndAddress = (uint32_t)pBuffer + (m_BlockSize * nbrOfBlocks);
	
	for(	uint32_t CurrentAddress = (uint32_t)pBuffer; 
			CurrentAddress < EndAddress; 
			CurrentAddress += m_BlockSize, CurrentSector++)
	{
		ReadSingleBlock((void*)CurrentAddress, CurrentSector);
	}
}
while the SPI-implementation works as expected:

Code: Select all

void SPg_microSD::ReadSingleBlock(void* pBuffer, uint32_t sector)
{
	static const uint16_t	crc = 0xFFFF;
	uint8_t cmd[7] = {0xFF, 0x51, 0x00, 0x00, 0x00, 0x00, 0x01};
	uint8_t startMarker;

	uint32_t address = sector;
	if(m_bIsSCard)
		address *= 512;
	cmd[2] = ((uint8_t*)&address)[3];
	cmd[3] = ((uint8_t*)&address)[2];
	cmd[4] = ((uint8_t*)&address)[1];
	cmd[5] = ((uint8_t*)&address)[0];
	
	m_pSPIChannel->Transmit(cmd, 7);
	do 
	{
		m_pSPIChannel->Receive(&startMarker, 1);
	} while (0xFE != startMarker);
	
	m_pSPIChannel->Receive(pBuffer, m_BlockSize);
	
	m_pSPIChannel->Transmit(&crc, 2);
}

void SPg_microSD::ReadMultipleBlocks(void* pBuffer, uint32_t firstSector, uint32_t sectorCount)
{
	uint32_t CurrentSector = firstSector;
	uint32_t EndAddress = (uint32_t)pBuffer + (m_BlockSize * sectorCount);
	
	for(	
			uint32_t CurrentAddress = (uint32_t)pBuffer; 
			CurrentAddress < EndAddress; 
			CurrentAddress += m_BlockSize, CurrentSector++
		)
	{
		ReadSingleBlock((void*)CurrentAddress, CurrentSector);
	}
}

Code: Select all

void SPg_SPI_Channel::Transmit(const void* buffer, const size_t count)
{
	m_pSpi->SPI_CR = SPI_CR_SPIEN;
	while(SPI_SR_TXEMPTY  != (m_pSpi->SPI_SR & SPI_SR_TXEMPTY));
	m_pSpi->SPI_MR &= ~(SPI_MR_DLYBCS_Msk | SPI_MR_PCS_Msk);
	m_pSpi->SPI_MR |=(SPI_MR_MSTR | SPI_MR_DLYBCS(0) | SPI_MR_PCS(~(1 << m_NPCS)));

	if((uint32_t)buffer < (uint32_t)IRAM_ADDR)    // PDC cannot reach below IRAM start address.
	{
		for(size_t index = 0; index < count; index++)
		{
			while(SPI_SR_TXEMPTY  != (m_pSpi->SPI_SR & SPI_SR_TXEMPTY));
			m_pSpi->SPI_TDR = ((uint8_t*)buffer)[index];
		}
		while(SPI_SR_TXEMPTY  != (m_pSpi->SPI_SR & SPI_SR_TXEMPTY));
	}
	else
	{
		m_pSpi->SPI_PTCR = SPI_PTCR_TXTDIS | SPI_PTCR_RXTDIS;
		m_pSpi->SPI_TPR = (uint32_t)buffer;        //  first byte to transfer
		m_pSpi->SPI_TCR = count;                   //  nbr of bytes to transfer
		m_pSpi->SPI_PTCR = SPI_PTCR_TXTEN;         //  Start the transfer
	}
}

void SPg_SPI_Channel::Receive(void* buffer, const size_t count)
{
	m_pSpi->SPI_CR = SPI_CR_SPIEN;
	while(SPI_SR_TXEMPTY  != (m_pSpi->SPI_SR & SPI_SR_TXEMPTY));
	m_pSpi->SPI_MR &= ~(SPI_MR_DLYBCS_Msk | SPI_MR_PCS_Msk);
	m_pSpi->SPI_MR |= (SPI_MR_MSTR | SPI_MR_DLYBCS(0) | SPI_MR_PCS(~(1 << m_NPCS)));

	memset(buffer, 0xFF, count);

	m_pSpi->SPI_PTCR = SPI_PTCR_TXTDIS | SPI_PTCR_RXTDIS;

	if (count > 1)
	{
		if ((m_pSpi->SPI_RCR == 0) && (m_pSpi->SPI_TCR == 0))
		{
			m_pSpi->SPI_TPR = (uint32_t)buffer;
			m_pSpi->SPI_TCR = count - 1;
			
			m_pSpi->SPI_RPR = (uint32_t)buffer;
			m_pSpi->SPI_RCR = count;
		}
		else
		{
			//	Error handling/recovery here
		}
	
		m_pSpi->SPI_PTCR = SPI_PTCR_TXTEN | SPI_PTCR_RXTEN;         //  Start the transfer
	
		while((m_pSpi->SPI_RCR > 1) || (m_pSpi->SPI_TCR > 0));
		while(SPI_SR_TXEMPTY  != (m_pSpi->SPI_SR & SPI_SR_TXEMPTY));
	}
	m_pSpi->SPI_TDR = 0xFF;
	while(SPI_SR_TXEMPTY  != (m_pSpi->SPI_SR & SPI_SR_TXEMPTY));
	((uint8_t*)buffer)[count - 1] = m_pSpi->SPI_RDR;
}
It seems like the HSMCI inserts 2 bytes at the end of each block of data (512 bytes), maybe a crc or somthing...

Anyone an idee how to solve this?

Thanks in advance.

Paul

PS: The above code is far from complete and works only for 800 x 480 pixel 24bit/pixel bmp files.
Owais
Posts: 57
Joined: Wed Jul 23, 2014 3:25 pm

Re: SDCard HSMCI or SPI

Thu Aug 07, 2014 10:35 am

Hello, sorry not a reply to the above post. However, my question is also related to a similar topic. I am looking for interfacing an eMMC (Embedded MutiMedia Card) with SAM4S16C over SPI, has anyone tried this technique. If so please share your experience and if possible provide some related links. Thanks in advance.

Regards
Owais.
Sam++
Posts: 16
Joined: Tue Jul 01, 2014 9:05 pm

Re: SDCard HSMCI or SPI

Thu Aug 07, 2014 12:09 pm

Hi, I *think* I found the problem : It seems that the buffer for storing the bytes from the SD-card has to be alligned so that the last two bits are zero (address % 4 must be 0) so that the PDC can do his work...

I changed the code to this :

Code: Select all

void SPg_HSMCard::ReadSingleBlock(void* pBuffer, uint32_t sector)
{
LA_SD_Trigger.Off();
LA_SD_Trigger.On();

	//Set_BlockLength();
	
	uint8_t*	pInternalBuffer = new uint8_t[4 * 129];
	uint8_t*	pAddress = (uint8_t*)((uint32_t)pInternalBuffer + (4 - (uint32_t)pInternalBuffer % 4));
	
	HSMCI->HSMCI_MR |= HSMCI_MR_PDCMODE;
	HSMCI->HSMCI_BLKR = HSMCI_BLKR_BLKLEN(512);
	HSMCI->HSMCI_RPR = (uint32_t)pAddress;
	HSMCI->HSMCI_RCR = ((512) / 4);
	HSMCI->HSMCI_PTCR = HSMCI_PTCR_RXTEN;
	if(m_bIsHCXC)	//	Byte address or Block Address ?
		HSMCI->HSMCI_ARGR = sector;
	else
		HSMCI->HSMCI_ARGR = sector * 512;
	
	HSMCI->HSMCI_CMDR	=	HSMCI_CMDR_CMDNB(17) 
						|	HSMCI_CMDR_MAXLAT_64 
						|	HSMCI_CMDR_RSPTYP_48_BIT 
						|	HSMCI_CMDR_TRTYP_BYTE
						|	HSMCI_CMDR_TRDIR_READ 
						|	HSMCI_CMDR_TRCMD_START_DATA;
	
	while(HSMCI_SR_ENDRX != (HSMCI->HSMCI_SR  & HSMCI_SR_ENDRX));
	
	memcpy(pBuffer, pAddress, 512);
	
	delete [] pInternalBuffer;

LA_SD_Trigger.Off();
}

void SPg_HSMCard::ReadMultipleBlocks(void* pBuffer, uint32_t sector, uint32_t nbrOfBlocks)
{
	uint32_t CurrentSector = sector;
	uint32_t EndAddress = (uint32_t)pBuffer + (m_BlockSize * nbrOfBlocks);
	
	for(	uint32_t CurrentAddress = (uint32_t)pBuffer; 
			CurrentAddress < EndAddress; 
			CurrentAddress += m_BlockSize, CurrentSector++)
	{
		ReadSingleBlock((void*)CurrentAddress, CurrentSector);
	}
}
This creates overhead. The original destination buffer is assigned inside of FatFs, I think it is bad practice to change the internals of that module, so I create another buffer and copy the received data into it afterwards.

Anybody a sugestions to reduce the overhead, so as to speed up the operation?

Thanks

Return to “SAM4 Cortex-M4 MCU”

Who is online

Users browsing this forum: No registered users and 1 guest