STM32F0 DMA "input overflow"
STM32F0 DMA "input overflow"
STM32F0 DMA 从 UART 接收数据时遇到问题。我在非循环模式下使用 2 个 DMA 通道(用于 rx 和 tx),rx 通道具有较低的优先级。来自 UART 的数据在空闲线中断中处理,我在其中读取 DMA 接收到的字节数并对其进行处理。一切正常,直到包中的字节数小于或等于 DMA 缓冲区大小。否则 DMA 会奇怪地关闭,随后的空闲线中断会给我 1、0、0 ... DMA 接收到的字节数。这是代码的一部分,我在其中检查 DMA 缓冲区是否已满并尝试将 DMA 计数器重置为缓冲区大小:
#define S_M_INPUT_CMD_SIZE 20
static char s_m_uart_dma_in_buff[S_M_INPUT_CMD_SIZE + 1];
void USART1_IRQHandler(void)
{
ITStatus reception_status = USART_GetITStatus(USART1, USART_IT_IDLE);
if(reception_status != RESET)
{
int32_t bytes_number = S_M_INPUT_CMD_SIZE - DMA_GetCurrDataCounter(DMA1_Channel3);
if (DMA_GetFlagStatus(DMA1_FLAG_TC3) != RESET)
{
USART_ITConfig(UART_, USART_IT_IDLE, DISABLE);
DMA_Cmd(DMA1_Channel3, DISABLE);
while (DMA1_Channel3->CCR & DMA_CCR_EN);
for (int i = 0; i < S_M_INPUT_CMD_SIZE; i++)
s_m_uart_dma_in_buff[i] = '[=11=]';
DMA_SetCurrDataCounter(DMA1_Channel3, S_M_INPUT_CMD_SIZE);
DMA_Cmd(DMA1_Channel3, ENABLE);
DMA_ClearFlag(DMA1_FLAG_GL3);
}
USART_ClearITPendingBit(UART_, USART_IT_IDLE);
}
}
在第一个 "overflow" 和 DMA 启用之后,"buffer size + 1" 字节出现在 rx 寄存器中,之后接收到的字节数稳定为零。我做错了什么?
如果您尝试使用您的代码接收大小大于 S_M_INPUT_CMD_SIZE 的数据包,会发生什么情况:
- DMA 完成接收块 S_M_INPUT_CMD_SIZE 并禁用(non-circular 模式)
- USART1 接收一个字节
- USART1 从数据包中再接收一个字节并丢弃它们,因为没有人处理 UART
- 数据包完成,您获得 IDLE 中断并重新初始化 DMA
- DMA 读取先前接收到的字节
- 没有更多数据包 - 您得到 0、0、... DMA 接收字节数
我不得不说,这种处理 DMA 传输的方式真的很奇怪。 DMA 传输通常必须在 DMA 中断处理程序中处理,而不是外设。
为了处理长数据包,您必须实现 DMA 中断处理程序。因此可以比 IDLE 中断更早地重新初始化 DMA 接收。因此 DMA 将准备好从 UART 接收更多数据。
还有一件事。在您的代码中有一场比赛。在您阅读 bytes_number
和禁用 DMA 之间,一个或多个字节可以通过 DMA 传输。
但更正确的处理 UART 的方法是 运行 DMA 循环模式并且不要重新初始化它。因为每次禁用 DMA 时都会错过一些 UART 输入。
STM32F0 DMA 从 UART 接收数据时遇到问题。我在非循环模式下使用 2 个 DMA 通道(用于 rx 和 tx),rx 通道具有较低的优先级。来自 UART 的数据在空闲线中断中处理,我在其中读取 DMA 接收到的字节数并对其进行处理。一切正常,直到包中的字节数小于或等于 DMA 缓冲区大小。否则 DMA 会奇怪地关闭,随后的空闲线中断会给我 1、0、0 ... DMA 接收到的字节数。这是代码的一部分,我在其中检查 DMA 缓冲区是否已满并尝试将 DMA 计数器重置为缓冲区大小:
#define S_M_INPUT_CMD_SIZE 20
static char s_m_uart_dma_in_buff[S_M_INPUT_CMD_SIZE + 1];
void USART1_IRQHandler(void)
{
ITStatus reception_status = USART_GetITStatus(USART1, USART_IT_IDLE);
if(reception_status != RESET)
{
int32_t bytes_number = S_M_INPUT_CMD_SIZE - DMA_GetCurrDataCounter(DMA1_Channel3);
if (DMA_GetFlagStatus(DMA1_FLAG_TC3) != RESET)
{
USART_ITConfig(UART_, USART_IT_IDLE, DISABLE);
DMA_Cmd(DMA1_Channel3, DISABLE);
while (DMA1_Channel3->CCR & DMA_CCR_EN);
for (int i = 0; i < S_M_INPUT_CMD_SIZE; i++)
s_m_uart_dma_in_buff[i] = '[=11=]';
DMA_SetCurrDataCounter(DMA1_Channel3, S_M_INPUT_CMD_SIZE);
DMA_Cmd(DMA1_Channel3, ENABLE);
DMA_ClearFlag(DMA1_FLAG_GL3);
}
USART_ClearITPendingBit(UART_, USART_IT_IDLE);
}
}
在第一个 "overflow" 和 DMA 启用之后,"buffer size + 1" 字节出现在 rx 寄存器中,之后接收到的字节数稳定为零。我做错了什么?
如果您尝试使用您的代码接收大小大于 S_M_INPUT_CMD_SIZE 的数据包,会发生什么情况:
- DMA 完成接收块 S_M_INPUT_CMD_SIZE 并禁用(non-circular 模式)
- USART1 接收一个字节
- USART1 从数据包中再接收一个字节并丢弃它们,因为没有人处理 UART
- 数据包完成,您获得 IDLE 中断并重新初始化 DMA
- DMA 读取先前接收到的字节
- 没有更多数据包 - 您得到 0、0、... DMA 接收字节数
我不得不说,这种处理 DMA 传输的方式真的很奇怪。 DMA 传输通常必须在 DMA 中断处理程序中处理,而不是外设。
为了处理长数据包,您必须实现 DMA 中断处理程序。因此可以比 IDLE 中断更早地重新初始化 DMA 接收。因此 DMA 将准备好从 UART 接收更多数据。
还有一件事。在您的代码中有一场比赛。在您阅读 bytes_number
和禁用 DMA 之间,一个或多个字节可以通过 DMA 传输。
但更正确的处理 UART 的方法是 运行 DMA 循环模式并且不要重新初始化它。因为每次禁用 DMA 时都会错过一些 UART 输入。