Best way to wait for data read from SD card

dibosco wrote on Thursday, February 14, 2013:

I suspect my question is largely a question of naivety so please be gentle! :slight_smile:

I have a set up where I am successfully reading files from an SD card, but I need to make it more efficient. I believe that I should be reading a sector of my SD card in 48us, so with files that are 2048 bytes long should, best case scenario take around 192us. They are not and I am pretty sure I know why, it’s just a case of how to fix this elegantly in FreeRTOS.

So, my sector read routine, sends the applicable command out to the SD card and just waits for the 512 bytes to be read from the SD card. I figured that although this wasn’t efficient at all in a run-to-complete program, with FreeRTOS, the OS would switch out of the thread that was calling the sector read routine and then come back at the next time slice by which time that sector would be read. This does work and it works very nicely; the problem I have is that now we are upping the rate this read data is sent out of the serial port, I am waiting for the next file to be read from the SD card. It’s actually taking many milliseconds to read s small file. I guess this is understandable because the fat file reading thread will be doing the following in pseudo-code:

Open file
while sector to read
Start DMA to read sector
Wait for sector <-- presumably task switching here
File is read and is available to send out to our screen.

Now, with all this  context switching, I am sure that is why it’s taking so long to read  file (and the files are about to go up to 3k, not 2k, to further compound this).

So, I am wondering what is the way to do this to make this as elegant and fast as possible. Once I have requested a sector read and the thread is just sat waiting for the DMA completion there is no point in it sitting there eating up processor time I would think. So, is it wise/possible to force a context switch at this point? Or will it happen automatically? In some ways  a wait of 192us seems nothing when a time slice is 1ms and therefore much longer. So, I am actually wondering whether FreeRTOS is smart enough to switch anyway when it’s waiting like that (for a DMA transfer completion).

I have a feeling that I should be setting a DMA handler and doing something that wakes the appropriate thread ASAP but I must confess my understand of this is hazy. I am not sure enough of this hunch to plough on down this route without asking though. Also, with the USART routine interrupt examples it only has one byte to pull out of the UART receive, with 512 bytes coming out I’m not clear on how the interrupt routine knows where the DMA controller would have stored that 512 bytes (although this is probably a hardware issue more than a FreeRTOS issue as it’s possible the DMA controller value that was passed to tell the controller the first address to start storing data is kept there and the interrupt routine would know from that register value.

If this interrupt method is the best way, should I simply pass the 512 bytes into a queue and then would the calling FAT routine be woken up straight away the queue was filled? As an aside, would copying 512 bytes into a queue not actually be a pretty hefty waste of time in itself? As far as I understand writing to a queue actually copies data across to the queue and doesn’t just give a pointer to this data I’ve read from the SD card.

I would add that, the routine that is doing the read of the sector on the SD card is a subroutine of a subroutine of a subroutine of…well you get the idea. I assume that doesn’t matter in  terms of context switching, but again am not sure about this.

I’m also wondering whether I need to give higher priority to the FAT thread, although whenever I’ve tried something like this I always find the OS gets stuck in the highest priority thread and never comes out!

So, would someone be able to advise on the way to go about something like this please? I hope I have explained this clearly enough, but please ask for clarification if not.

Many thanks in advance.

richard_damon wrote on Thursday, February 14, 2013:

When you say you “wait for sector”, what are you doing, if polling a status bit, that will not cause FreeRTOS to task switch, if you execute a delay, that will delay to the next tick, which is likely a lot longer than you need (which would cause your reads to be very slow).

Typically your statement “wait for sector” would be some FreeRTOS action which would block your task until an interrupt occurs, who’s handler will do the appropriate signal to resume the task.

This COULD be waiting on a fifo for the data block, but this is somewhat inefficient with all the data copying.

IF the hardware supports it, it would make a lot of sense to set up a DMA transfer to where the task wants the data, and have the transfer complete interrupt signal the task with a semaphore when done.

dibosco wrote on Thursday, February 14, 2013:

I am, indeed, polling a status bit. I am not invoking a task delay. So that means that the task switch will only occur once the time slice for that task has expired. thanks for that.

I will give the semaphore option a try, having done some reading about it, it looks like it might be the right option.

The hardware certainly supports the DMA putting the data wherever it is wanted. The issue (I think) is that when you read a file from the card, you read it a sector (512 bytes) at a time which means that once you have set that sector read in motion all you can do is wait until it is done. Obviously it’s better if you can do other things while the HCMI (memory card interface) is churning away in the background. However, I am still not quite convinced in the case of these small files that I’d be better simply waiting for all sectors to be read as even with six sectors you need to read for a 3k file this should only be 300us which is obviously well inside the 1ms tick time.

It seems counter intuitive to just have the processor sit waiting, but task switching six times also seems a little daft when it could be all done within a third of tick time. Maybe it would make more sense to *stop* any possible task switching while this read is done? Would that be possible and sensible? If so, how would I do that please?

Thanks for the reply, Richard. :slight_smile:

richard_damon wrote on Friday, February 15, 2013:

One way to think about this is that the task switch should be able to happen a lot faster than 300us, so doing a task switch will let you get more done. The main purpose of using a RTOS is to allow you to use this type of spare time to get other operations done.

If the operations to get all the chunks is simple enough, it could make sense to do the sequencing in the ISR, and then return the done semaphore only when the full operation is done. This would cut down the over head of the task switching.

As to how to do it without task switching, that would be what you were doing, just poll loop on the ready bit. Then FreeRTOS won’t task switch unless a higher priority task gets ready (in which case you SHOULD task switch), or the timer tick happens and their is another task has the same priority, in which case again you should switch or this task won’t be sharing well.

dibosco wrote on Friday, February 15, 2013:

OK, fair enough! I’ll try the semaphore, get some debug pins toggling and see what difference it makes, then report back. Thanks again for the advice, much appreciated.

dibosco wrote on Friday, February 15, 2013:

I’ve spent all day on this and got absolutely nowhere. I am not really sure whether I am implementing the semaphore incorrectly, whether the tasks switch while waiting for the semaphore or whether I have not correctly implemented the interrupt routine for the HSCMI interface.

So, what I would like to do, to prevent me having to knock p a run-to-complete program, in the part of the software that reads all the relevant sectors of a file, to temporarily stop FreeRTOS exiting the thread. That way I can tell, at least whether the reading of the file is taking way longer than it should because of context switching or because I have the HSCMI interface incorrectly implemented.

Is it possible to do this and if so, how would I do it please? I’m hoping to do something along the lines of:


Do All the SD card reading


Thanks again. 

davedoors wrote on Friday, February 15, 2013:

/* your code here */

dibosco wrote on Friday, February 15, 2013:

I don’t seem to have those, but I do have vTaskSuspendAll() and xTaskResumeAll(). Using these doesn’t help in the slightest though.