Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Debugging 为什么DMA I2C传输会锁定执行?_Debugging_I2c_Dma_Stm32f4discovery - Fatal编程技术网

Debugging 为什么DMA I2C传输会锁定执行?

Debugging 为什么DMA I2C传输会锁定执行?,debugging,i2c,dma,stm32f4discovery,Debugging,I2c,Dma,Stm32f4discovery,我正在尝试更改STM32F407的a,以便在使用I2C时包括DMA传输。我用它来驱动OLED屏幕。在其原始形式中,它是在不存在问题的情况下工作的。在评论中,有人添加了DMA,但也将其移植到STM32F10,我正在尝试将其移植回F407 我的问题是,在启用DMA传输后,调试器停止工作(恰好在该行)-调试器活动指示灯停止/关闭,调试器停留在下一个语句。 经过更多的测试(在某些事件中闪烁led以查看是否发生),我发现代码实际上会持续到某一点(特别是,下次需要DMA传输时-在第二次调用更新屏幕中)。在此

我正在尝试更改STM32F407的a,以便在使用I2C时包括DMA传输。我用它来驱动OLED屏幕。在其原始形式中,它是在不存在问题的情况下工作的。在评论中,有人添加了DMA,但也将其移植到STM32F10,我正在尝试将其移植回F407

我的问题是,在启用DMA传输后,调试器停止工作(恰好在该行)-调试器活动指示灯停止/关闭,调试器停留在下一个语句。 经过更多的测试(在某些事件中闪烁led以查看是否发生),我发现代码实际上会持续到某一点(特别是,下次需要DMA传输时-在第二次调用更新屏幕中)。在此之后,程序不会继续(如果在该语句之后设置为打开,则LED不会打开)

奇怪的是,我知道传输工作正常,因为屏幕上写了几个字符。只有在我不一步一步地调试的情况下才会发生这种情况,因为CPU同时将新数据写入屏幕缓冲区,并在DMA将其完全发送到屏幕之前更改其内容(我将在稍后找出如何修复该问题-可能是双缓冲区,但无论如何它都不应干扰DMA传输)。然而,若我一步一步地调试,DMA会在CPU将新内容写入屏幕缓冲区之前完成,屏幕是黑色的(因为缓冲区首先被清除)。为了进行测试,我删除了对DMA的第一个调用(在清除缓冲区之后),并让程序将预期的文本写入缓冲区。它显示时没有任何异常,因此这意味着DMA必须已经完成,但之后发生了一些事情。我无法解释,如果DMA完成传输,调试器为什么会停止工作。 我尝试在DMA的传输完成中断处理程序中闪烁led,但它从不闪烁,这意味着它从未被触发。我将感谢任何帮助,因为我不知所措(已经调试了几天了)。 谢谢大家!

下面是代码的相关部分(我省略了代码的其余部分,因为代码太多了,但如果需要,我可以发布)。该代码在没有DMA(普通I2C传输)的情况下工作,但在DMA的情况下才中断

//TM_STM32F4_I2C.h

typedef struct DMA_Data
{
    DMA_Stream_TypeDef* DMAy_Streamx;
    uint32_t feif;
    uint32_t dmeif;
    uint32_t teif;
    uint32_t htif;
    uint32_t tcif;
} DMA_Data;

//...
//TM_STM32F4_I2C.c

void TM_I2C_Init(I2C_TypeDef* I2Cx, uint32_t clockSpeed) {
    I2C_InitTypeDef I2C_InitStruct;

    /* Enable clock */
    RCC->APB1ENR |= RCC_APB1ENR_I2C3EN;

    /* Enable pins */
    TM_GPIO_InitAlternate(GPIOA, GPIO_PIN_8, TM_GPIO_OType_OD, TM_GPIO_PuPd_UP, TM_GPIO_Speed_Medium, GPIO_AF_I2C3);
    TM_GPIO_InitAlternate(GPIOC, GPIO_PIN_9, TM_GPIO_OType_OD, TM_GPIO_PuPd_UP, TM_GPIO_Speed_Medium, GPIO_AF_I2C3);

    /* Check clock, set the lowest clock your devices support on the same I2C bus */
    if (clockSpeed < TM_I2C_INT_Clocks[2]) {
        TM_I2C_INT_Clocks[2] = clockSpeed;
    }

    /* Set values */
    I2C_InitStruct.I2C_ClockSpeed = TM_I2C_INT_Clocks[2];
    I2C_InitStruct.I2C_AcknowledgedAddress = TM_I2C3_ACKNOWLEDGED_ADDRESS;
    I2C_InitStruct.I2C_Mode = TM_I2C3_MODE;
    I2C_InitStruct.I2C_OwnAddress1 = TM_I2C3_OWN_ADDRESS;
    I2C_InitStruct.I2C_Ack = TM_I2C3_ACK;
    I2C_InitStruct.I2C_DutyCycle = TM_I2C3_DUTY_CYCLE;

    /* Disable I2C first */
    I2Cx->CR1 &= ~I2C_CR1_PE;

    /* Initialize I2C */
    I2C_Init(I2Cx, &I2C_InitStruct);

    /* Enable I2C */
    I2Cx->CR1 |= I2C_CR1_PE;
}

int16_t TM_I2C_WriteMultiDMA(DMA_Data* dmaData, I2C_TypeDef* I2Cx, uint8_t address, uint8_t reg, uint16_t len)
{
    int16_t ok = 0;
    // If DMA is already enabled, wait for it to complete first.
    // Interrupt will disable this after transmission is complete.
    TM_I2C_Timeout = 10000000;
    // TODO: Is this I2C check ok?
    while (I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY) && !I2C_GetFlagStatus(I2Cx, I2C_FLAG_TXE) && DMA_GetCmdStatus(dmaData->DMAy_Streamx) && TM_I2C_Timeout)
    {
        if (--TM_I2C_Timeout == 0)
        {
            return -1;
        }
    }
    //Set amount of bytes to transfer
    DMA_Cmd(dmaData->DMAy_Streamx, DISABLE); //should already be disabled at this point
    DMA_SetCurrDataCounter(dmaData->DMAy_Streamx, len);
    DMA_ClearFlag(dmaData->DMAy_Streamx, dmaData->feif | dmaData->dmeif | dmaData->teif | dmaData->htif | dmaData->tcif);   // Clear dma flags
    DMA_Cmd(dmaData->DMAy_Streamx, ENABLE); // enable DMA
    //Send I2C start
    ok = TM_I2C_Start(I2Cx, address, I2C_TRANSMITTER_MODE, I2C_ACK_DISABLE);
    //Send register to write to
    TM_I2C_WriteData(I2Cx, reg);
    //Start DMA transmission, interrupt will handle transmit complete.
    I2C_DMACmd(I2Cx, ENABLE);
    return ok;
}

//...
//TM_STM32F4_SSD1306.c

void TM_SSD1306_initDMA(void)
{
    DMA_InitTypeDef DMA_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
    DMA_DeInit(DMA1_Stream4);
    DMA_Cmd(DMA1_Stream4, DISABLE);

    //Configure DMA controller channel 3, I2C TX channel.
    DMA_StructInit(&DMA_InitStructure);                                 // Load defaults
    DMA_InitStructure.DMA_Channel = DMA_Channel_3;
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(I2C3->DR)); // I2C3 data register address
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)SSD1306_Buffer;   // Display buffer address
    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;             // DMA from mem to periph
    DMA_InitStructure.DMA_BufferSize = 1024;                            // Is set later in transmit function
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;    // Do not increment peripheral address
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;             // Do increment memory address
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                       // DMA one shot, no circular.
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;               // Tweak if interfering with other dma actions
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    DMA_Init(DMA1_Stream4, &DMA_InitStructure);
    DMA_ITConfig(DMA1_Stream4, DMA_IT_TC, ENABLE);                      // Enable transmit complete interrupt
    DMA_ClearITPendingBit(DMA1_Stream4, DMA_IT_TC);

    // Set interrupt controller for DMA
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream4_IRQn;             // I2C3 TX connect to stream 4 of DMA1
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x05;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x05;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    // Set interrupt controller for I2C
    NVIC_InitStructure.NVIC_IRQChannel = I2C3_EV_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    I2C_ITConfig(I2C3, I2C_IT_BTF, ENABLE);
}

extern void DMA1_Channel3_IRQHandler(void)
{
    //I2C3 DMA transmit completed
    if (DMA_GetITStatus(DMA1_Stream4, DMA_IT_TC) != RESET)
    {
        // Stop DMA, clear interrupt
        DMA_Cmd(DMA1_Stream4, DISABLE);
        DMA_ClearITPendingBit(DMA1_Stream4, DMA_IT_TC);
        I2C_DMACmd(SSD1306_I2C, DISABLE);
    }
}
// Sending stop condition to I2C in separate handler necessary
// because DMA can finish before I2C finishes
// transmitting and last byte is not sent
extern void I2C3_EV_IRQHandler(void)
{
    if (I2C_GetITStatus(I2C3, I2C_IT_BTF) != RESET)
    {
        TM_I2C_Stop(SSD1306_I2C); // send i2c stop
        I2C_ClearITPendingBit(I2C3, I2C_IT_BTF);
    }
}

// ...

void TM_SSD1306_UpdateScreen(void) {
    TM_I2C_WriteMultiDMA(&ssd1306_dma_data, SSD1306_I2C, SSD1306_I2C_ADDR, 0x40, 1024); // Use DMA
}
编辑:我注意到初始化新传输时的错误条件检查,但修复它并不能解决主要问题

while ((I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY) || !I2C_GetFlagStatus(I2Cx, I2C_FLAG_TXE) || DMA_GetCmdStatus(dmaData->DMAy_Streamx)) && TM_I2C_Timeout)

作为一个实验,尝试调整DMA每一块传输的字节数。DMA可能阻塞了数据总线,因此调试器无法获取数据(在传输期间)。您可能希望在较小的块中传输,以便CPU和其他实体共享数据总线。BTW,您的问题可能与C++语言无关。我很确定这个问题可以用其他语言来解决。@ThomasMatthews我很抱歉我缺乏知识,但你指的是哪种设置?我认为DMA必须等到I2C可以吞下下下一个字节,而I2C只能在400kHz下工作。此外,我在这里读到“DMA并没有占用100%的总线时间。DMA在内存之间进行单字传输需要5个AHB总线周期–其中三个仍然留给CPU访问。这意味着DMA最多只占用40%的总线时间。因此,即使DMA进行密集的数据传输,CPU也可以随时访问任何内存区域、外围设备。”在一些DMA芯片上,您可以控制“块”中传输的字节数。例如,如果设置为64,DMA将在释放数据总线之前传输64字节。它将释放总线,然后再传输64个字节。由于总线控制问题,LCD驱动程序的DMA导致系统延迟。您解决了这个问题吗?我正试图让一个STM32F427使用以前为STM32F407使用I2C1的例程执行I2C3 DMA传输。。。我的I2C可以手动工作,但在DMA中挂起。作为一个实验,请尝试调整DMA在每个块上传输的字节数。DMA可能阻塞了数据总线,因此调试器无法获取数据(在传输期间)。您可能希望在较小的块中传输,以便CPU和其他实体共享数据总线。BTW,您的问题可能与C++语言无关。我很确定这个问题可以用其他语言来解决。@ThomasMatthews我很抱歉我缺乏知识,但你指的是哪种设置?我认为DMA必须等到I2C可以吞下下下一个字节,而I2C只能在400kHz下工作。此外,我在这里读到“DMA并没有占用100%的总线时间。DMA在内存之间进行单字传输需要5个AHB总线周期–其中三个仍然留给CPU访问。这意味着DMA最多只占用40%的总线时间。因此,即使DMA进行密集的数据传输,CPU也可以随时访问任何内存区域、外围设备。”在一些DMA芯片上,您可以控制“块”中传输的字节数。例如,如果设置为64,DMA将在释放数据总线之前传输64字节。它将释放总线,然后再传输64个字节。由于总线控制问题,LCD驱动程序的DMA导致系统延迟。您解决了这个问题吗?我正试图让一个STM32F427使用以前为STM32F407使用I2C1的例程执行I2C3 DMA传输。。。我的I2C可以手动工作,但使用DMA挂起。
while ((I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY) || !I2C_GetFlagStatus(I2Cx, I2C_FLAG_TXE) || DMA_GetCmdStatus(dmaData->DMAy_Streamx)) && TM_I2C_Timeout)