Цитата(pitt @ May 8 2015, 02:44)

I don't have issues except sometimes(5%) access time is more than 10 times greater than average. Don't know why but it is. When I'm writing 512 blocks Average speed is the same but lowest is almost 20 times above average.
Это нормально. Многоблочная запись пишет сектора в некоторый буфер (ОЗУ) внутри карты. Когда карта решает, что в нём данных накопилось некоторое кол-во,
она выполняет физическую запись накополенного в буфере. И в конце, при получении Stop Transfer, пишет остаток. В эти моменты и получаете задержки.
Это хорошо видно на осциллограмме: после большинства блоков BUSY короткий, но после некоторых - длинный.
Либо выполняется стирание данных в тех блоках, в которые пойдёт запись. Вы команду ACMD23 перед многоблочной записью выполняете? Я вот не выполнял. Может с ней задержки будут меньше.
Насчёт 512 блоков подряд не очень понял - у меня вроде не было проблем. Хотя надо посмотреть в сторону такого параметра SD-карты как "минимальный стираемый блок" (есть в CSD).
Как я наблюдал на многих картах, он может иметь размер около 64кБ. Может быть с этим связано.
Цитата(pitt @ May 8 2015, 06:00)

Если у Вас есть код/образчик многоблоковой записи через DMA, буду очень признателен за возможность ознакомиться: есть вопросы с очередностью операций и, особенно, с флагом FEIE.
Я писал для SPI-интерфейса. 2-3 месяца назад (LPC1788). Мой драйвер позволяет пользовательскому уровню работать либо напрямую (операции одноблочного чтения и многоблочного продолженного чтения;
однобочной записи и многоблочной продолженной записи), либо через FatFS. При этом если в ПО есть две задачи, одна из которых обращается напрямую, а другая - через FatFS, то они не будут мешать друг другу. FatFS работает через мой драйвер (он выполняет роль низкоуровневого доступа к SD).
Естественно - никаких ожиданий в глухих лупах нет - используется uCOS и её объекты синхронизации. Более того - есть поддержка работы службы снижения потребления питания
(есть нотификации в службу о периодах работы/простоя драйвера) для возможности ухода в сон устройства.
В моём драйвере передача блоков данных/команд идёт через DMA, ожидание готовности - периодическая программная посылка 0xFF в SPI с прерыванием по приёму с таймаутом.
Вряд-ли Вам чем-то поможет кусок моего исходника, но всё-таки выложу:
CODE
//Переходит сюда после успешного обнаружения и идентификации SD-карты.
//Здесь обрабатываются запросы операций от пользователей SD-сервиса
//прикладного уровня.
static FFaza FazaIdle()
{
SDact act;
u32 i, j, n;
u8 typ;
do {
ILivePing(ILIVE_SD);
act.op = act.OP_NONE;
if (!sdReq) {
IdleBegin(IDLE_READY_SD);
MboxPend(sdMbox, ms2tkt(300));
if (!Pdat(PIN_SD_INSERT)) return (FFaza)FazaIdle;
LogsCR(COL_BLACK "Card removed");
return (FFaza)FazaRestart;
}
} while (!(n = Prepare(&act)));
IdleEnd(IDLE_READY_SD);
MboxAccept(sdMbox);
while (1) {
if (act.op == act.OP_WAIT_READY) StartAct();
else if (act.op == act.OP_TRIM) {
if (cardType <= CARD_MMC) {
sdCompletes[act.user](NULL);
return (FFaza)FazaIdle;
}
#ifdef SD_ARG_CHK
SDassert(act.trim.start <= act.trim.end, REGION_SDREQ_TRIM);
#endif
StartAct();
if ((j = act.trim.start) < sdCapacity) {
j -= j % sdEraseSize;
if (cardType != CARD_SD2_BLOCK) j <<= SD_SECTOR_LEN;
if (SendCmd(CM_SET_ERASE_START, j)) FazaIdleRestart(&act);
if ((j = act.trim.end) >= sdCapacity) j = sdCapacity - 1;
j = j - j % sdEraseSize + sdEraseSize - 1;
if (cardType != CARD_SD2_BLOCK) j <<= SD_SECTOR_LEN;
if (SendCmd(CM_SET_ERASE_END, j)) FazaIdleRestart(&act);
if (SendCmd(CM_ERASE)) FazaIdleRestart(&act);
}
} else if (act.op != act.OP_NONE) break;
sdCompletes[act.user](&act.rw.buf);
SDsselOff();
OffSpi();
return (FFaza)FazaIdle;
}
#ifdef SD_ARG_CHK
SDassert(act.rw.buf && act.rw.sector < sdCapacity &&
act.rw.secOfs < SD_SECTOR_LEN, REGION_SDREQ_RW);
#endif
for (typ = 0; ; ) {
ILivePing(ILIVE_SD);
if ((uint)(act.op - act.OP_READ_1) <= act.OP_READ_N - act.OP_READ_1) {
if (sector != act.rw.sector || !sectorValid) {
while (1) {
if (!typ) if (StartAct()) break;
i = CM_READ_MULTIPLE_BLOCK;
if (n == 1 << SD_SECTOR_LEN && act.op == act.OP_READ_1)
i = CM_READ_SINGLE_BLOCK;
typ = i - CM_READ_MULTIPLE_BLOCK;
u32 jj = act.rw.sector;
if (cardType != CARD_SD2_BLOCK) jj <<= SD_SECTOR_LEN;
if (SendCmd(i, jj)) FazaIdleRestart(&act);
break;
}
if (RcvDataPacket(sh.sectorBuf, 1 << SD_SECTOR_LEN,
ms2tkt(100)) >= 0) break;
sector = act.rw.sector;
sectorValid = 1;
}
i = act.rw.secOfs;
if ((j = (1 << SD_SECTOR_LEN) - i) > n) j = n;
memcpy(act.rw.buf, &sh.sectorBuf[i], j);
if ((i = act.rw.secOfs + j) >> SD_SECTOR_LEN) {
act.rw.sector++;
i = 0;
}
act.rw.secOfs = i;
} else {
i = DataCRC(act.rw.buf, j = 1 << SD_SECTOR_LEN);
while (1) {
if (typ) WaitR1b();
else if (StartAct()) {
WaitR1b();
break;
}
typ = sectorValid = 0;
int ii = CM_WRITE_MULTIPLE_BLOCK;
if (n == 1 << SD_SECTOR_LEN && act.op == act.OP_WRITE_1) {
typ = TOKEN_DATA - TOKEN_DATA_WMB;
ii = CM_WRITE_BLOCK;
}
u32 jj = act.rw.sector;
if (cardType != CARD_SD2_BLOCK) jj <<= SD_SECTOR_LEN;
if (SendCmd(ii, jj)) FazaIdleRestart(&act);
break;
}
if (SndDataPacket(act.rw.buf, j, i, typ + TOKEN_DATA_WMB) !=
SND_PACKET_OK) break;
act.rw.sector++;
}
act.rw.buf += j;
if (n -= j) continue;
while (!(n = sdCompletes[act.user](&act.rw.buf))) {
if (!act.rw.buf) {
if (!PeripheralPowerState(concatAB(PERIPH_PCSSP, nSSP_sd)))
return (FFaza)FazaIdle;
if (!typ) {
if ((uint)(act.op - act.OP_READ_1) > 1) {
WaitR1b();
SndStopTran();
} else if (SendCmd(CM_STOP_TRANSMISSION)) FazaIdleRestart(&act);
}
#ifdef SD_SAFE_WRITE
if ((uint)(act.op - act.OP_WRITE_1) <=
act.OP_WRITE_N - act.OP_WRITE_1) {
WaitR1b();
if (SendCmd(CM_SEND_STATUS)) FazaIdleRestart(&act);
}
#endif //SD_SAFE_WRITE
SDsselOff();
OffSpi();
return (FFaza)FazaIdle;
}
if (!(AtomicSwap(&sdReq, 0) & 1 << SDREQ_START)) do {
MboxPend(sdMbox, ms2tkt(300));
ILivePing(ILIVE_SD);
} while (!sdReq);
sdReq = 1 << SDREQ_BUSY;
MboxAccept(sdMbox);
}
#ifdef SD_ARG_CHK
SDassert(act.rw.buf, REGION_SDREQ_RW);
#endif
}
FazaIdleRestart(&act);
}
union SDact {
enum {
OP_NONE, //нет операции
OP_READ_1, //выполнить чтение цепочки секторов без возможности продолжения
OP_READ_N, //выполнить чтение цепочки секторов с возможностью продолжения
OP_WRITE_1, //выполнить запись цепочки секторов без возможности продолжени
OP_WRITE_N, //выполнить запись цепочки секторов с возможностью продолжения
OP_TRIM, //выполнить стирание цепочки секторов
OP_WAIT_READY, //ожидание освобождения SD-карты (снятия сигнала BUSY)
OP_n};
enum {SD_USER_TERMINAL, SD_USER_FF, SD_USER_n};
struct { //для OP_READ_.., OP_WRITE_..
u8 *buf;
u32 sector;
u16 secOfs;
} rw;
struct { //для OP_TRIM
u32 start, end;
} trim;
struct { //для всех OP_..
u32 unuse0[2];
u16 unuse1;
u8 op, user;
};
};
Данный кусок выполняет приём запросов на операции с SD от прикладных задач, их выполнение, нотификацию источников запросов о завершении
выполнения (или о возможности продолжения выполнения).
Собственно функция SndDataPacket() и выполняет отправку блока записываемых данных через SPI+DMA.
WaitR1b() - ожидание освобождения интерфейса (снятие BUSY картой).
SendCmd() - отсылка команды карте.
Функция Prepare() принимает запрос на операцю с SD от пользовательского уровня.
Собственно со строки
for (typ = 0; ; ) { начинается обработка запросов чтения и записи (многоблочных и одноблочных).