stm32f4 DMA挂起后并不总是启动
stm32f4 DMA does not always start after suspending
所以这个问题是这个问题的 "sequel":Stm32f4: DMA + ADC Transfer pausing。
同样,我正在尝试实现这样的算法:
- 在一个通道上以三重交错模式使用 ADC 初始化 DMA
- 等待外部中断
- 暂停 DMA 传输和 ADC
- 在中断中通过USART从内存发送缓冲数据
- 恢复 DMA 和 ADC
- 退出中断,转到2。
DMA 和 ADC 挂起和恢复,但有时(在大约 16% 的中断调用中)恢复失败 - DMA 只是从 ADC 写入第一个测量值并停止直到下一个中断,在该中断中 DMA 和 ADC 重新启动(因为它们被暂停并再次恢复)并且 - 好吧,一切 returns 恢复正常,直到下一个这样的错误。
我试过像参考手册所说的那样暂停 DMA:
In order to restart from the point where the transfer was stopped, the
software has to read the DMA_SxNDTR register after disabling the
stream by writing the EN bit in DMA_SxCR register (and then checking
that it is at ‘0’) to know the number of data items already collected.
Then:
– The peripheral and/or memory addresses have to be updated in order to adjust the address pointers
– The SxNDTR register has to be updated with the remaining number of data items to be transferred (the value read when the stream was disabled)
– The stream may then be re-enabled to restart the transfer from the point it was stopped
唯一的实际区别在于恢复 DMA 工作时写入的 NDTR 值。在我的例子中它是 buffer_size,在 RefMan 的例子中 - 它是暂停 DMA 时读取的值。在 RefMan 的情况下,DMA 在暂停后不会再次启动。就我而言,正如我上面所说,它开始了,但并非总是如此。
如何防止这种情况发生?
当前的中断代码如下所示:
void EXTI4_IRQHandler(void) {
uint16_t temp = DMA_GetFlagStatus(DMA2_Stream0, DMA_FLAG_TEIF0);
if(EXTI_GetITStatus(EXTI_Line4) != RESET) {
uint16_t fPoint1 = 0;
uint16_t fPoint2 = 0;
//Some delay using the TIM2
TIM_SetCounter(TIM2, 0);
TIM_Cmd(TIM2, ENABLE);
//Measure the first point NDTR
fPoint1 = DMA2_Stream0->NDTR;
while(TIM_GetITStatus(TIM2, TIM_IT_Update) != SET) {};
//Measure the second point here.
fPoint2 = DMA2_Stream0->NDTR;
if(fPoint1 == fPoint2) {
//The NDTR does not change!
//If it does not change, it is stuck at buffer_size - 1
}
//Disable the timer
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
TIM_Cmd(TIM2, DISABLE);
DMA_Cmd(DMA2_Stream0, DISABLE);
//Wait until the DMA will turn off
while((DMA2_Stream0->CR & (uint32_t)DMA_SxCR_EN) != 0x00) {};
//Turn off all ADCs
ADC_Cmd(ADC1, DISABLE);
ADC_Cmd(ADC2, DISABLE);
ADC_Cmd(ADC3, DISABLE);
//Send all the data here
//Turn everything back on
//Turn the DMA ON again
DMA_SetCurrDataCounter(DMA2_Stream0, BUFFERSIZE);
DMA_Cmd(DMA2_Stream0, ENABLE);
while((DMA2_Stream0->CR & (uint32_t)DMA_SxCR_EN) == 0x00) {};
//See note @ RefMan (Rev. 12), p. 410
ADC->CCR &= ~((uint32_t)(0x000000FF));
ADC->CCR |= ADC_TripleMode_Interl;
ADC_Cmd(ADC1, ENABLE);
ADC_Cmd(ADC2, ENABLE);
ADC_Cmd(ADC3, ENABLE);
while((ADC1->CR2 & (uint32_t)ADC_CR2_ADON) == 0) {};
while((ADC2->CR2 & (uint32_t)ADC_CR2_ADON) == 0) {};
while((ADC3->CR2 & (uint32_t)ADC_CR2_ADON) == 0) {};
ADC_SoftwareStartConv(ADC1);
}
EXTI_ClearITPendingBit(EXTI_Line4);
}
我自己找到了解决方案。我以为是 DMA 问题;然而,这不是ADC的问题。
ADCx->CR 寄存器中的 OVR 标志总是在传输 "stuck" 时设置。因此,我在 ADC 溢出情况下添加了一个中断,并在其中重新启动了 DMA 和 ADC。现在问题已经解决了。
所以这个问题是这个问题的 "sequel":Stm32f4: DMA + ADC Transfer pausing。
同样,我正在尝试实现这样的算法:
- 在一个通道上以三重交错模式使用 ADC 初始化 DMA
- 等待外部中断
- 暂停 DMA 传输和 ADC
- 在中断中通过USART从内存发送缓冲数据
- 恢复 DMA 和 ADC
- 退出中断,转到2。
DMA 和 ADC 挂起和恢复,但有时(在大约 16% 的中断调用中)恢复失败 - DMA 只是从 ADC 写入第一个测量值并停止直到下一个中断,在该中断中 DMA 和 ADC 重新启动(因为它们被暂停并再次恢复)并且 - 好吧,一切 returns 恢复正常,直到下一个这样的错误。
我试过像参考手册所说的那样暂停 DMA:
In order to restart from the point where the transfer was stopped, the software has to read the DMA_SxNDTR register after disabling the stream by writing the EN bit in DMA_SxCR register (and then checking that it is at ‘0’) to know the number of data items already collected. Then:
– The peripheral and/or memory addresses have to be updated in order to adjust the address pointers
– The SxNDTR register has to be updated with the remaining number of data items to be transferred (the value read when the stream was disabled)
– The stream may then be re-enabled to restart the transfer from the point it was stopped
唯一的实际区别在于恢复 DMA 工作时写入的 NDTR 值。在我的例子中它是 buffer_size,在 RefMan 的例子中 - 它是暂停 DMA 时读取的值。在 RefMan 的情况下,DMA 在暂停后不会再次启动。就我而言,正如我上面所说,它开始了,但并非总是如此。
如何防止这种情况发生?
当前的中断代码如下所示:
void EXTI4_IRQHandler(void) {
uint16_t temp = DMA_GetFlagStatus(DMA2_Stream0, DMA_FLAG_TEIF0);
if(EXTI_GetITStatus(EXTI_Line4) != RESET) {
uint16_t fPoint1 = 0;
uint16_t fPoint2 = 0;
//Some delay using the TIM2
TIM_SetCounter(TIM2, 0);
TIM_Cmd(TIM2, ENABLE);
//Measure the first point NDTR
fPoint1 = DMA2_Stream0->NDTR;
while(TIM_GetITStatus(TIM2, TIM_IT_Update) != SET) {};
//Measure the second point here.
fPoint2 = DMA2_Stream0->NDTR;
if(fPoint1 == fPoint2) {
//The NDTR does not change!
//If it does not change, it is stuck at buffer_size - 1
}
//Disable the timer
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
TIM_Cmd(TIM2, DISABLE);
DMA_Cmd(DMA2_Stream0, DISABLE);
//Wait until the DMA will turn off
while((DMA2_Stream0->CR & (uint32_t)DMA_SxCR_EN) != 0x00) {};
//Turn off all ADCs
ADC_Cmd(ADC1, DISABLE);
ADC_Cmd(ADC2, DISABLE);
ADC_Cmd(ADC3, DISABLE);
//Send all the data here
//Turn everything back on
//Turn the DMA ON again
DMA_SetCurrDataCounter(DMA2_Stream0, BUFFERSIZE);
DMA_Cmd(DMA2_Stream0, ENABLE);
while((DMA2_Stream0->CR & (uint32_t)DMA_SxCR_EN) == 0x00) {};
//See note @ RefMan (Rev. 12), p. 410
ADC->CCR &= ~((uint32_t)(0x000000FF));
ADC->CCR |= ADC_TripleMode_Interl;
ADC_Cmd(ADC1, ENABLE);
ADC_Cmd(ADC2, ENABLE);
ADC_Cmd(ADC3, ENABLE);
while((ADC1->CR2 & (uint32_t)ADC_CR2_ADON) == 0) {};
while((ADC2->CR2 & (uint32_t)ADC_CR2_ADON) == 0) {};
while((ADC3->CR2 & (uint32_t)ADC_CR2_ADON) == 0) {};
ADC_SoftwareStartConv(ADC1);
}
EXTI_ClearITPendingBit(EXTI_Line4);
}
我自己找到了解决方案。我以为是 DMA 问题;然而,这不是ADC的问题。 ADCx->CR 寄存器中的 OVR 标志总是在传输 "stuck" 时设置。因此,我在 ADC 溢出情况下添加了一个中断,并在其中重新启动了 DMA 和 ADC。现在问题已经解决了。