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