STM32F0 DMA "переполнение ввода"
У меня проблема с получением STM32F0 DMA данных от UART. Я использую 2 канала DMA (для rx и tx) как в некруглом режиме, так и канал rx имеет более низкий приоритет. Данные из UART обрабатывают прерывание Idle Line, где я считываю количество полученных байтов DMA и обрабатываю их. Все работает нормально, пока количество байтов в пакете не станет меньше или равно размеру буфера DMA. В противном случае DMA странным образом отключается, и после прерываний Idle Line я получаю 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] = '\0';
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);
}
}
После первого "переполнения" и включения DMA следует "размер буфера + 1", который был в регистре rx, а затем число принятых байтов стабильно равно нулю. Что я делаю не так?
1 ответ
Вот что происходит, если вы пытаетесь получить пакет с размером больше S_M_INPUT_CMD_SIZE, используя ваш код:
- DMA завершает прием блока S_M_INPUT_CMD_SIZE и отключает его (некруговой режим)
- USART1 получает один байт
- USART1 получает еще один байт из пакета и отбрасывает их, потому что никто не обрабатывает UART
- пакет завершен, вы получаете прерывание IDLE и перезапускаете DMA
- DMA читает ранее полученный байт
- Нет больше пакетов - вы получаете 0, 0, ... количество принятых байтов DMA
Я должен сказать, что это действительно странный способ обработки передач DMA. Передачи DMA обычно должны обрабатываться обработчиками прерываний DMA, а не периферийными устройствами.
Для обработки длинных пакетов вы должны реализовать обработчик прерываний DMA. Таким образом, можно повторно начать прием DMA раньше, чем прерывание IDLE. Таким образом, DMA будет готов получить больше данных от UART.
Еще кое-что. В вашем коде есть гонка. между прочтением bytes_number
и отключить DMA, один или несколько байтов могут быть переданы DMA.
Но более правильным способом обработки UART является запуск DMA в циклическом режиме и не повторная его инициализация. Потому что каждый раз, когда вы отключаете DMA, вы можете пропустить ввод UART.