首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >DMA写入SD卡(SSP)不写入字节

DMA写入SD卡(SSP)不写入字节
EN

Stack Overflow用户
提问于 2014-04-27 15:27:47
回答 1查看 916关注 0票数 3

我目前正在用非阻塞DMA实现替换SSP上SD卡驱动程序的阻塞繁忙-等待实现。然而,没有实际写入的字节,尽管所有的事情似乎都是按照计划进行的(从来没有发现错误条件)。

首先,一些代码(C++):

(免责声明:我仍然是嵌入式编程的初学者,所以代码很可能是次等的)

代码语言:javascript
复制
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();
    }
}

关于代码和设置的几点注释:

  • 为lpc4088编写的FreeRTOS
  • 所有SD_xxx定义都是条件定义以选择正确的引脚(我需要在开发设置中使用SSP2,最终产品为SSP0 )
  • 在这个片段中没有定义的所有外部函数(例如pack_argumentsend_commandsemaphore.take()等)是已知的正确工作(其中大多数来自工作繁忙-等待SD实现。当然,我不能百分之百保证他们没有问题,但他们似乎工作得很好。)
  • 因为我正在调试这个过程,所以有很多printf和硬编码的SSP2变量。这些当然是暂时的。
  • 我主要使用作为示例代码。

现在,我已经尝试了以下几点:

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

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

我的代码有什么问题,或者我可以在哪里查找导致这个问题的原因?在花了更多的时间在这上面,然后我想承认,我真的不知道如何使这个工作,任何帮助都是感谢的!

更新:我做了更多的测试,因此我得到了更多的结果。下面的结果是我写了4块512字节的结果。每个块包含不断增加的数量模块256。因此,每个块包含从0到255的2个序列。结果:

  • 数据实际上是写入SD卡。然而,似乎第一个块写的丢失了。我认为在write函数中有一些设置需要更早地完成。
  • 字节按一个非常奇怪(而且错误)的顺序排列:基本上,我得到的是交替的所有偶数和所有奇数。因此,我首先得到偶数0x00 - 0xFE,然后得到所有奇数0x01 - 0xFF (除了丢失的第一个块外,书写字节的总数似乎是正确的)。然而,在这个序列中甚至有一个例外:每个块包含两个这样的序列(序列是256个字节,块是512),但是每个块中的第一个序列具有0xfe0xff“交换”。也就是说,0xFF是偶数的结束,0xFE是奇数级数的结束。我不知道这里发生了什么黑魔法。以防万一我做了一些愚蠢的事情,下面是写入字节的片段: uint8_t block512;for (int i= 0;i< 512;i++) { blocki = (uint8_t)(i % 256);} if (!SD:写(10240,块,1){//此一个不是实际写的警告(“noWrite”,proc);} if (!SD::写(10241,块,1)) {警告(“noWrite”,proc);} if (!SD::写(10242,块,1)) {警告(“noWrite”,proc);} if (!SD::写(10243,块,1){noWrite,proc)};

这里是原始二进制转储。请注意,这个精确的模式是完全可复制的:到目前为止,每次我尝试这个,我都得到了这个完全相同的模式。

Update2:不确定它是否相关,但我使用sdram作为内存。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-05-04 18:04:48

当我终于得到了一个逻辑分析仪,我得到了更多的信息,并能够解决这些问题。

我的代码中有一些小错误,但是导致这种行为的错误是,我没有在块之前发送“开始块”令牌(0xFE),也没有在块之后发送16位(虚拟) crc。当我将这些添加到传输缓冲区时,一切都写得很成功!

因此,这个修正如下:

代码语言:javascript
复制
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;

    //...
}

另外,第一个块没有被写入的原因很简单,因为我没有等到设备准备好之后才发送第一个块。这样做解决了问题。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23325204

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档