I have a simple function for testing writing log data to a SD card.
Exact after 1368 time writing (append) a row to the SD card the software locks
and after a hard reset everyting works fine.
I have try this 4 time's and everytime program stops after 1368 time append a string to the SD card.
someone has a suggestion where it goes wrong?
I use controller LPC1549 on a own designed pcb.
Log function locks after 1368 writes (appends) to log file
SDFileSystem sd(SD_MOSI, SD_MISO, SD_SCK, SD_CS, "sd", P1_16, SDFileSystem::SWITCH_NEG_NC, 25000000);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void SendLog(void)
{
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
u08 displayed = false;
time_t seconds = time(NULL);
// create an empty file pointer.
FILE *myLogFile = NULL;
if (!sd.card_present()) { // Make sure a card is present
Display_String(" No card present! ",50,10); // Write string on row 50 and column 10
displayed = false;
for (u08 teller = 52; teller<56; teller++) { // Deze regels clearen
Display_String(" ",teller,10);
}
sd.unmount(); // Unmount the SD card
return;
} else {
Display_String(" Mounting SD card...",50,10); // Write string on row 50 and column 10
if (sd.mount() != 0) { // Mount the filesystem
Display_String("failed to mount the filesystem! ",50,10);
} else {
if(!(displayed)) {
displayed = true;
Display_String(" Mounting OK ",50,10);
//Display the card type
SDFileSystem::CardType cardType = sd.card_type();
if (cardType == SDFileSystem::CARD_NONE)
Display_String("Card type: None",52,10);
else if (cardType == SDFileSystem::CARD_MMC)
Display_String("Card type: MMC ",52,10);
else if (cardType == SDFileSystem::CARD_SD)
Display_String("Card type: SD ",52,10);
else if (cardType == SDFileSystem::CARD_SDHC)
Display_String("Card type: SDHC",52,10);
else
Display_String("Card type: Unknown",52,10);
//Display the card capacity
Display_Value_u32("Sectors: ", sd.disk_sectors(),53,10);
PrintFF("Capacity (MB): ",((float)(sd.disk_sectors())/2048.0),54,10);
}
myLogFile = fopen("/sd/log.txt", "a");
if (myLogFile == NULL) { // check the file is open.
Display_String("failed to open file!",55,10);
myLogFile = fopen("/sd/log.txt", "w");
return;
}
char day[25];
strftime(day, 25,"%Y/%m/%d %a %H:%M:%S",localtime(&seconds)); // Put Year, month, day, weekday, Hour, Minuts, Seconds in string
fprintf(myLogFile,"\r\n%s %010lu %d %d %d ",day,test.ErrorStatus,200,300,400); // write string, counter (test.Errorstatus) and 3 numbers on the SD card
fclose( myLogFile); // Close logfile
}
}
sd.unmount(); // Unmount the SD card
}
Here's a crude example of how to modify my library for DMA support:
SDFileSystem.cpp
//NOTE: Add this between the includes and the constructor #include "rtos.h" namespace { enum ThreadSignals { SIG_DMA_DONE = (1 << 2) }; unsigned int m_DmaChannelTable[18 * 4] __attribute__((aligned(512))) = {}; osThreadId threadId; void onDmaIrq() { //Clear the interrupt flags LPC_DMA->INTA0 = (1 << 6) | (1 << 7); //Signal the thread that the DMA transfer has finished osSignalSet(threadId, SIG_DMA_DONE); } void dmaInit() { //Enable the AHB clock to the DMA controller and reset it LPC_SYSCON->SYSAHBCLKCTRL0 |= (1 << 20); LPC_SYSCON->PRESETCTRL0 |= (1 << 20); LPC_SYSCON->PRESETCTRL0 &= ~(1 << 20); //Configure the DMA controller and enable it LPC_DMA->SRAMBASE = (unsigned int)m_DmaChannelTable & 0xFFFFFE00; LPC_DMA->CTRL = 0x00000001; //Configure channel 6 (SPI0_RX_DMA) LPC_DMA->CFG6 = 0x00010001; LPC_DMA->INTENSET0 = (1 << 6); LPC_DMA->ENABLESET0 |= (1 << 6); //Configure channel 7 (SPI0_TX_DMA) LPC_DMA->CFG7 = 0x00020001; LPC_DMA->INTENSET0 = (1 << 7); LPC_DMA->ENABLESET0 |= (1 << 7); //Set the DMA IRQ vector and enable it NVIC_SetVector(DMA_IRQn, (uint32_t)onDmaIrq); NVIC_EnableIRQ(DMA_IRQn); } void dmaRead(char* buffer, int length) { //Adjust the length length -= 1; //Configure the SPI controller for 8-bit frames LPC_SPI0->TXCTL = 0x07000000; //Configure channel 6 and trigger it m_DmaChannelTable[25] = 0x40048014; m_DmaChannelTable[26] = (unsigned int)buffer + length; LPC_DMA->XFERCFG6 = (length << 16) | 0x0000401D; //Configure channel 7 and trigger it char txByte = 0xFF; m_DmaChannelTable[29] = (unsigned int)&txByte; m_DmaChannelTable[30] = 0x4004801C; LPC_DMA->XFERCFG7 = (length << 16) | 0x0000000D; //Wait for the transfer to complete threadId = Thread::gettid(); osSignalWait(SIG_DMA_DONE, osWaitForever); } void dmaWrite(const char* buffer, int length) { //Adjust the length length -= 1; //Configure the SPI controller for 8-bit frames, TX only LPC_SPI0->TXCTL = 0x07400000; //Configure channel 7 and trigger it m_DmaChannelTable[29] = (unsigned int)buffer + length; m_DmaChannelTable[30] = 0x4004801C; LPC_DMA->XFERCFG7 = (length << 16) | 0x0000101D; //Wait for the transfer to complete threadId = Thread::gettid(); osSignalWait(SIG_DMA_DONE, osWaitForever); //Set the SPI controller back to normal LPC_SPI0->TXCTL = 0x07000000; } } //NOTE: Modify the constructor as indicated SDFileSystem::SDFileSystem(PinName mosi, PinName miso, PinName sclk, PinName cs, const char* name, PinName cd, SwitchType cdtype, int hz) : FATFileSystem(name), m_Spi(mosi, miso, sclk), m_Cs(cs, 1), m_Cd(cd), m_FREQ(hz) { //NOTE: Add this to the bottom of the constructor code dmaInit(); } //NOTE: Replace readData() with this version bool SDFileSystem::readData(char* buffer, int length) { char token; unsigned short crc; //Wait for up to 500ms for a token to arrive m_Timer.start(); do { token = m_Spi.write(0xFF); } while (token == 0xFF && m_Timer.read_ms() < 500); m_Timer.stop(); m_Timer.reset(); //Check if a valid start block token was received if (token != 0xFE) return false; //Read the data block using DMA dmaRead(buffer, length); //Read the CRC16 checksum for the data block crc = (m_Spi.write(0xFF) << 8); crc |= m_Spi.write(0xFF); //Return the validity of the CRC16 checksum (if enabled) return (!m_Crc || crc == CRC16(buffer, length)); } //NOTE: Replace writeData() with this version char SDFileSystem::writeData(const char* buffer, char token) { //Calculate the CRC16 checksum for the data block (if enabled) unsigned short crc = (m_Crc) ? CRC16(buffer, 512) : 0xFFFF; //Wait for up to 500ms for the card to become ready if (!waitReady(500)) return false; //Send the start block token m_Spi.write(token); //Write the data block using DMA dmaWrite(buffer, 512); //Write the CRC16 checksum for the data block m_Spi.write(crc >> 8); m_Spi.write(crc); //Return the data response token return (m_Spi.write(0xFF) & 0x1F); }And there you have it! This code is written for the LPC1549, but it should be fairly easy to modify for other targets. It uses the official rtos library to sleep the calling thread while data transfer takes place (so accessing the SD card from ISR context is not allowed).
At this point, I don't have any plans to bake DMA support into the library since I don't feel like building my own lightweight HAL layer and keeping up with new targets as they're added. The best way to do this would be if the mbed library provided a non-blocking API for reading/writing from the SPI bus, and hid the actually implementation from the user. Something like the following would work (with a bit of additional template magic of course):
Targets with a DMA controller would use it to perform the transfer, while targets without a DMA controller could use interrupts. The important point here is that the transfer takes place in the background, using a microcontroller-independent non-blocking API. This could be extended to other busses as well, such as I2C or Serial. If there's enough support for this kind of API, I may be persuaded to create a separate thread to work on getting it added.