Hi All
Has anyone had a problem that the SDHC gets blocked?
When doing tests I sometimes get the following while loop waiting forever:
while (!(SDHC_PRSSTAT & SDHC_PRSSTAT_BREN)) {}
This is performed in the read of a sector (not using DMA) of 512 bytes (actually 128 long words at the SDHC interface). The BREN flag indicates that there are bytes to be read and the problem always occurs when 127 long word reads have been made and there is never a flag to say that the final one is ready.
When this happens there are no data error bits set in the SDHC_IRQSTAT register.
The value of SDHC_PRSSTAT is 0xff88020a
This is the routine:
// Read a sector from SD card into the specified data buffer//extern int fnGetSector(unsigned char *ptrBuf){ unsigned long *ptrData = (unsigned long *)ptrBuf; // the Kinetis driver ensures that the buffer is long word aligned int i = (512/sizeof(unsigned long)); while (i--) { if (SDHC_IRQSTAT & (SDHC_IRQSTAT_DEBE | SDHC_IRQSTAT_DCE | SDHC_IRQSTAT_DTOE)) { // check for read errors SDHC_IRQSTAT = (SDHC_IRQSTAT_DEBE | SDHC_IRQSTAT_DCE | SDHC_IRQSTAT_DTOE | SDHC_IRQSTAT_BRR); return UTFAT_DISK_READ_ERROR; // return error } while (!(SDHC_PRSSTAT & SDHC_PRSSTAT_BREN)) { <---------- gets stuck here } *ptrData++ = SDHC_DATPORT; } while (!(SDHC_IRQSTAT & SDHC_IRQSTAT_TC)) { // wait for transfer to complete } SDHC_IRQSTAT = (SDHC_IRQSTAT_TC | SDHC_IRQSTAT_BRR | SDHC_IRQSTAT_AC12E); // reset flags return UTFAT_SUCCESS; // read successfully terminated}
This seems quite a standard/typical realisation. The following subtle effects have also been noticed:
1) When built with GCC it hangs very often when the SDHC_IRQSTAT check before it is removed. With the check in position it seems to be stable.
2) When built with CW10.1 it gets stuck quite often, even with the SDHC_IRQSTAT check before the line.
Therefore the stability is compiler/timing dependent (all registers are declared as volatile).
Has anyone else encountered such a behaviour?
Regards
Mark
P.S. During the test the SD card is used by various sources (to display images to the TFT display (file system reads), for USB-MSD (PC reads) and for serving pages at web server (web server file reads). It is easier to reproduce when all are working together but still happens when only one read source is actually active (although less frequently). Accesses are however protected so only one source actually physically reads at a time.
Hi All
I may have been able to solve this by adding the following before starting the read:
while (SDHC_PRSSTAT & SDHC_PRSSTAT_DLA) {}
This ensures that the previous command has completely terminated before checking for the read data. There is obviously a short time where the command hasn't completely terminated but the FIFO can flag that there is something there (or something similar). This would explain why the compiler (with slightly different optimisation and delays) could be more or less affected. In fact, using IAR (faster code) it was even worst before and then stable afterward.
Regards
Mark
Hi Mark,
I'm having the same issue.
Are you sure that line solves your problem?
I mean, I think you are checking the wrong value. From the datasheet:
"In the case of read transactions:
This bit will be set in either of the following cases:
• After the end bit of the read command.
..."
Therefore, I think you should check:
while ((SDHC_PRSSTAT & SDHC_PRSSTAT_DLA_MASK)==0){};
But anyway, sometimes I still have the same problem.
Someone has any idea where should be the problem?
Thanks,
Marc Domingo
Hi Marc
I have understood that the DLA indicates that the data line(s) is/are active. It is set when a read or write is in operation and cleared when the operation has been completed.
By adding
while (SDHC_PRSSTAT & SDHC_PRSSTAT_DLA) {}
'before' starting the read it is verifying that any 'previous' operation has completely terminated. If the tail end of any previous activity happens to still not be completely termianted the start of the new read will be delayed until it is (the DLA returns back to '0').
Since adding this I haven't had a hang take place so it looks to be doing something positive but I never did any measurements with an analyser connected to the bus and some indication of where the code was; whether the wait took place and how long, etc. (which I would do in case of any further difficulties).
My feeling is that the difficulty occurs when a read quickly follows another operation, whereby the bus has not fully completed what it was doing before the new read is started. I also expect that by simply adding a short delay (say a couple fo us) at the start of the read function would also give the bus time to complete and so would stop the hanging situation arise, which is presumably because new commands are issued a bit too soon.
I could however be completely wrong, so if you can confirm something else it would also be useful :smileywink:
Regards
Mark
Hi,
I think you are right. Seems that now my driver is working better. Thanks!!
Do you also check DLA before starting writing to the sd?
Thanks,
Marc Domingo
Hi Marc
I didn't need to change anything when writing (yet...)
Regards
Mark
Hi Mark (and others)
I know that this tread is old and solved, but I have a concern about the code
I just copy the inner loop
{ if (SDHC_IRQSTAT & (SDHC_IRQSTAT_DEBE | SDHC_IRQSTAT_DCE | SDHC_IRQSTAT_DTOE)) { SDHC_IRQSTAT = (SDHC_IRQSTAT_DEBE | SDHC_IRQSTAT_DCE | SDHC_IRQSTAT_DTOE | SDHC_IRQSTAT_BRR ); return UTFAT_DISK_READ_ERROR; // return error } while (!(SDHC_PRSSTAT & SDHC_PRSSTAT_BREN)) { } *ptrData++ = SDHC_DATPORT; } while (!(SDHC_IRQSTAT & SDHC_IRQSTAT_TC)) { } // wait for transfer to complete
I do not underestand why you did not check errors inside while loop as follows
{
while (!(SDHC_PRSSTAT & SDHC_PRSSTAT_BREN)) {
if (SDHC_IRQSTAT & (SDHC_IRQSTAT_DEBE | SDHC_IRQSTAT_DCE | SDHC_IRQSTAT_DTOE)) {
SDHC_IRQSTAT = (SDHC_IRQSTAT_DEBE | SDHC_IRQSTAT_DCE |
SDHC_IRQSTAT_DTOE | SDHC_IRQSTAT_BRR );
return UTFAT_DISK_READ_ERROR; // return error
}
}
*ptrData++ = SDHC_DATPORT;
}
while (!(SDHC_IRQSTAT & SDHC_IRQSTAT_TC)) { } // wait for transfer to complete
Hi JOliverasC
The reason why the check for errors is not performed in the while loop is because the while loop is entered very rarely - normally the BREN flag is set and so the error check would not be made at all.
The question as to whether the error check should be before AND in the while loop is another one. This may be the best but the wait loop by itself hasn't represented a problem in any cases to date.
In fact a do {error_check} while (BREN not set) may be the best solution. In practice I never actually had an error and there may be other waits which could potentially be improved by checking for errors while waiting. The exact internal workings of the SDHC is not documented in such detail that one can see in what order things take place and what can happen while other things are in progress and so there is a certain amount of trial and error involved - sometimes with a code hang during initial testing (which does however help to get some insights into what can take place).
Regards
Mark
Where can I get this SDHC driver code?
Thanks!
Richard.
Hi Richard
The particular code referenced here is from the uTasker project for Kinetis: http://www.utasker.com/forum/index.php?topic=1252.0
Regards
Mark
Hi Mark!
Have you used the multiple_block read?
When I use it ,I fond it often could 't stop,and BCEN unnormal.It often read wrong
Hi
I am sorry but I never used the multi-block command; just the single block command.
Regards
Mark