gpt4 book ai didi

c++ - DMA写入SD卡(SSP)不会写入字节

转载 作者:太空狗 更新时间:2023-10-29 23:04:14 27 4
gpt4 key购买 nike

我目前正在使用无阻塞DMA实现替换通过SSP进行的SD卡驱动程序的阻塞忙等待实现。但是,即使一切似乎都按计划进行,也没有实际写入字节(从未发现错误条件)。

首先是一些代码(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();
}
}

关于代码和设置的几点说明:
  • 使用FreeRTOS为lpc4088编写
  • 所有SD_xxx定义都是有条件的定义,用于选择正确的引脚(我需要在开发设置中使用SSP2,最终产品需要使用SSP0)
  • 已知此代码段中未定义的所有外部函数(例如pack_argumentsend_commandsemaphore.take()等)均能正常工作(其中大多数来自工作繁忙的SD实现。我当然不能保证100%它们没有错误,但似乎工作正常。)
  • 由于我正在进行调试,因此有很多printf和硬编码的SSP2变量。这些当然是暂时的。
  • 我主要使用this作为示例代码。

  • 现在,我已经尝试了以下操作:
  • 使用SSP上的繁忙等待进行无DMA写入。如前所述,我从一个可行的实现开始,所以我知道问题必须出在DMA实现中,而不是其他地方。
  • mem->mem而不是mem->sd写入,以消除SSP外设。 mem->mem工作正常,因此问题必须出在DMA设置的SSP部分。
  • 检查是否调用了ISR。它们是:首先为终端计数中断调用DMA IRS,然后调用SSP2 IRS。因此(可能)正确设置了IRS。
  • 对整个sd内容进行二进制转储,以查看其内容是否已被写入错误的位置。结果:通过DMA发送的内容没有出现在SD卡上的任何地方(我对代码进行的任何更改都是这样做的。所有内容都没有在SD卡上获得)。
  • 通过反复从SD卡请求字节以确保不存在超时问题,在SSP IRS中增加了较长的(〜1-2秒)超时(例如,我试图在SD卡具有有机会处理所有内容)。这根本没有改变结果。

  • 不幸的是,由于缺乏硬件工具,我还无法验证字节是否确实通过数据线发送。

    我的代码有什么问题,或者在哪里可以找到导致此问题的原因?在花费了更多的时间之后,我想承认我真的不知道如何使它正常工作,感谢您的帮助!

    更新:我做了很多测试,因此得到了更多结果。下面的结果是通过编写4个512字节的块得到的。每个块包含不断增加的数字模块256。因此,每个块包含2个从0到255的序列。结果:
  • 数据实际上已写入SD卡。但是,似乎第一个写入的块丢失了。我想在write函数中完成了一些设置,需要提前完成。
  • 字节的排列顺序非常奇怪(错误):我基本上是交替使用所有偶数和所有奇数。因此,我首先得到偶数0x00 - 0xFE,然后得到所有奇数0x01 - 0xFF(写入字节的总数似乎是正确的,但缺少第一个块)。但是,此序列中甚至有一个异常(exception):每个块包含其中的2个序列(序列为256个字节,块为512个),但是每个块中的第一个序列具有“交换”的0xfe0xff。也就是说,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);
    }

  • here是原始二进制转储。请注意,这种精确的模式是完全可重现的:到目前为止,每次我尝试使用此模式时,我都会得到这种完全相同的模式。

    Update2:不确定是否相关,但是我将sdram用于内存。

    最佳答案

    当我最终接触到逻辑分析仪时,我获得了很多信息,并且能够解决这些问题。

    我的代码中有一些小错误,但是导致此问题的错误是,我没有在块之前发送“开始块” token (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;

    //...
    }

    附带说明一下,未写入第一个块的原因仅仅是因为我没有等到设备准备好后才发送第一个块。这样做可以解决问题。

    关于c++ - DMA写入SD卡(SSP)不会写入字节,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23325204/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com