The other is a rare error(every 5 minutes for me while playing .wav from SD) occuring when reading a block (sdc_lld_read_aligned) and an interrupt happens while in sdc_lld_send_cmd_short (and its variants).
Code: Select all
SDIO->CMD = (uint32_t)cmd | SDIO_CMD_WAITRESP_0 | SDIO_CMD_CPSMEN;
while (((sta = SDIO->STA) & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT |
SDIO_STA_CCRCFAIL)) == 0)
;
SDIO->ICR = sta;If a interrupt happens between CMD=SDIO_CMD_CPSMEN and the reading of SDIO->STA the whole transfer might already be complete and ICR=sta will clear the SDIO_STA_DATAEND bit which is needed by sdc_lld_wait_transaction_end.
The fix is to not clear everything, only clear the bits we were waiting for:
Code: Select all
SDIO->ICR = sta & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | SDIO_STA_CCRCFAIL);Affected code paths are sdc_lld_send_cmd_short_crc, sdc_lld_send_cmd_short_crc and sdc_lld_send_cmd_long_crc.
While I'm at it, the behaviour of chThdSleepUntil seems very error prone. If used like I did "chThdSleepUntil(lastSleepTime+=MS2ST(1));" it might attempt to sleep until slightly in the past. At first glance the code already checks this:
Code: Select all
void chThdSleepUntil(systime_t time) {
chSysLock();
if ((time -= chTimeNow()) > 0)
chThdSleepS(time);
chSysUnlock();
}But time is unsigned so the check is almost never false and schedules a extremely long sleep duration instead. My local fix is to check if the given sleep duration is more than a day and if so assume that it has overflowed and treat it as no sleep.
Code: Select all
void chThdSleepUntil(systime_t time) {
chSysLock();
time -= chTimeNow();
if (time > 0 && time < S2ST(24UL*60UL*60UL))
chThdSleepS(time);
chSysUnlock();
}The duration might need to be tweaked if the size of systime_t varies.