В настоящее время я работаю над заменой блокирующей реализации драйвера занятости SD-карты на SSP неблокирующей реализацией DMA. Тем не менее, на самом деле нет записанных байтов, хотя кажется, что все идет по плану (условия ошибок никогда не обнаруживаются).
Сначала немного кода (C ++):
(Отказ от ответственности: я все еще новичок во встроенном программировании, поэтому код, вероятно, не на должном уровне)
namespace SD {
bool initialize() {
//Setup SSP and detect SD card
//... (removed since not relevant for question)
//Setup DMA
LPC_SC->PCONP |= (1UL << 29);
LPC_GPDMA->Config = 0x01;
//Enable DMA interrupts
NVIC_EnableIRQ(DMA_IRQn);
NVIC_SetPriority(DMA_IRQn, 4);
//enable SSP interrupts
NVIC_EnableIRQ(SSP2_IRQn);
NVIC_SetPriority(SSP2_IRQn, 4);
}
bool write (size_t block, uint8_t const * data, size_t blocks) {
//TODO: support more than one block
ASSERT(blocks == 1);
printf("Request sd semaphore (write)\n");
sd_semaphore.take();
printf("Writing to block " ANSI_BLUE "%d" ANSI_RESET "\n", block);
memcpy(SD::write_buffer, data, BLOCKSIZE);//Start the write
uint8_t argument[4];
reset_argument(argument);
pack_argument(argument, block);
if (!send_command(CMD::WRITE_BLOCK, CMD_RESPONSE_SIZE::WRITE_BLOCK, response, argument)){
return fail();
}
assert_cs();
//needs 8 clock cycles
delay8(1);
//reset pending interrupts
LPC_GPDMA->IntTCClear = 0x01 << SD_DMACH_NR;
LPC_GPDMA->IntErrClr = 0x01 << SD_DMACH_NR;
LPC_GPDMA->SoftSReq = SD_DMA_REQUEST_LINES;
//Prepare channel
SD_DMACH->CSrcAddr = (uint32_t)SD::write_buffer;
SD_DMACH->CDestAddr = (uint32_t)&SD_SSP->DR;
SD_DMACH->CLLI = 0;
SD_DMACH->CControl = (uint32_t)BLOCKSIZE
| 0x01 << 26 //source increment
| 0x01 << 31; //Terminal count interrupt
SD_SSP->DMACR = 0x02; //Enable ssp write dma
SD_DMACH->CConfig = 0x1 //enable
| SD_DMA_DEST_PERIPHERAL << 6
| 0x1 << 11 //mem to peripheral
| 0x1 << 14 //enable error interrupt
| 0x1 << 15; //enable terminal count interrupt
return true;
}
}
extern "C" __attribute__ ((interrupt)) void DMA_IRQHandler(void) {
printf("dma irq\n");
uint8_t channelBit = 1 << SD_DMACH_NR;
if (LPC_GPDMA->IntStat & channelBit) {
if (LPC_GPDMA->IntTCStat & channelBit) {
printf(ANSI_GREEN "terminal count interrupt\n" ANSI_RESET);
LPC_GPDMA->IntTCClear = channelBit;
}
if (LPC_GPDMA->IntErrStat & channelBit) {
printf(ANSI_RED "error interrupt\n" ANSI_RESET);
LPC_GPDMA->IntErrClr = channelBit;
}
SD_DMACH->CConfig = 0;
SD_SSP->IMSC = (1 << 3);
}
}
extern "C" __attribute__ ((interrupt)) void SSP2_IRQHandler(void) {
if (SD_SSP->MIS & (1 << 3)) {
SD_SSP->IMSC &= ~(1 << 3);
printf("waiting until idle\n");
while(SD_SSP->SR & (1UL << 4));
//Stop transfer token
//I'm not sure if the part below up until deassert_cs is necessary.
//Adding or removing it made no difference.
SPI::send(0xFD);
{
uint8_t response;
unsigned int timeout = 4096;
do {
response = SPI::receive();
} while(response != 0x00 && --timeout);
if (timeout == 0){
deassert_cs();
printf("fail");
return;
}
}
//Now wait until the device isn't busy anymore
{
uint8_t response;
unsigned int timeout = 4096;
do {
response = SPI::receive();
} while(response != 0xFF && --timeout);
if (timeout == 0){
deassert_cs();
printf("fail");
return;
}
}
deassert_cs();
printf("idle\n");
SD::sd_semaphore.give_from_isr();
}
}
Несколько замечаний о коде и настройке:
SD_xxx
определяет условно определяет, чтобы выбрать правильные выводы (мне нужно использовать SSP2 в моей настройке dev, SSP0 для конечного продукта)pack_argument
, send_command
, semaphore.take()
и т.д.), как известно, работают правильно (большинство из них происходит от работающей реализации SD с занятым ожиданием. Конечно, я не могу на 100% гарантировать, что они без ошибок, но, похоже, они работают правильно.). printf
с и жестко закодированы SSP2
переменные. Это конечно временно.Сейчас я уже попробовал следующие вещи:
mem->mem
вместо mem->sd
устранить периферию SSP. mem->mem
работал нормально, поэтому проблема должна быть в части SSP установки DMA.К сожалению, из-за отсутствия аппаратных средств я пока не смог проверить, действительно ли байты передаются по линиям данных.
Что не так с моим кодом, или где я могу найти причину этой проблемы? Потратив гораздо больше времени на это, я хотел бы признать, что я действительно не знаю, как заставить это работать, и любая помощь приветствуется!
ОБНОВЛЕНИЕ: я сделал намного больше тестирования, и таким образом я получил еще некоторые результаты. Результаты ниже я получил написав 4 блока по 512 байт. Каждый блок содержит модуль 256 постоянно растущих чисел. Таким образом, каждый блок содержит 2 последовательности, идущие от 0 до 255. Результаты:
write
функция, которая должна быть сделана ранее. Байты расположены в очень странном (и неправильном) порядке: в основном я чередую все четные числа, за которыми следуют все нечетные числа. Таким образом я сначала получаю четные числа 0x00 - 0xFE
а потом все нечетные числа 0x01 - 0xFF
(общее количество записанных байтов представляется правильным, за исключением отсутствующего первого блока). Однако в этой последовательности есть даже одно исключение: каждый блок содержит 2 из этих последовательностей (последовательность — 256 байтов, блок — 512), но первая последовательность в каждом блоке имеет 0xfe
а также 0xff
«Поменялись местами». То есть, 0xFF
конец четных чисел и 0xFE
это конец нечетного ряда. Я понятия не имею, что за черная магия здесь происходит. На всякий случай я сделал что-то глупое, вот фрагмент кода, который записывает байты:
uint8_t block[512];
for (int i = 0; i < 512; i++) {
block[i] = (uint8_t)(i % 256);
}
if (!SD::write(10240, block, 1)) { //this one isn't actually written
WARN("noWrite", proc);
}
if (!SD::write(10241, block, 1)) {
WARN("noWrite", proc);
}
if (!SD::write(10242, block, 1)) {
WARN("noWrite", proc);
}
if (!SD::write(10243, block, 1)) {
WARN("noWrite", proc);
}
А также Вот это сырой двоичный дамп Обратите внимание, что этот точный шаблон полностью воспроизводим: до сих пор каждый раз, когда я пробовал это, я получал точно такой же шаблон.
Update2: Не уверен, что это актуально, но я использую SDRAM для памяти.
Когда я наконец получил в свои руки логический анализатор, я получил гораздо больше информации и смог решить эти проблемы.
В моем коде было несколько небольших ошибок, но ошибка, вызвавшая такое поведение, заключалась в том, что я не отправлял токен «start block» (0xFE
) до блока, и я не отправил 16-битный (фиктивный) crc после блока. Когда я добавил их в буфер передачи, все было написано успешно!
Итак, это исправление было следующим:
bool write (size_t block, uint8_t const * data, size_t blocks) {
//TODO: support more than one block
ASSERT(blocks == 1);
printf("Request sd semaphore (write)\n");
sd_semaphore.take();
printf("Writing to block " ANSI_BLUE "%d" ANSI_RESET "\n", block);
SD::write_buffer[0] = 0xFE; //start block
memcpy(&SD::write_buffer[1], data, BLOCKSIZE);
SD::write_buffer[BLOCKSIZE + 1] = 0; //dummy crc
SD::write_buffer[BLOCKSIZE + 2] = 0;
//...
}
В качестве примечания, причина, по которой первый блок не был записан, заключалась в том, что я просто не дождался готовности устройства перед отправкой первого блока. Это решило проблему.