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, а затем количество полученных байтов стабильно равно нулю. Что я делаю не так?


person Yuriy    schedule 13.01.2017    source источник


Ответы (1)


Вот что произойдет, если вы попытаетесь получить пакет размером больше S_M_INPUT_CMD_SIZE, используя свой код:

  1. DMA завершает прием блока S_M_INPUT_CMD_SIZE и отключает (некруговой режим)
  2. USART1 получает один байт
  3. USART1 получает еще один байт из пакета и отбрасывает их, потому что никто не обрабатывает UART
  4. пакет завершен, вы получаете прерывание IDLE и повторно запускаете DMA
  5. DMA прочитал ранее полученный байт
  6. Больше никаких пакетов - вы получите 0, 0, ... количество полученных байтов DMA

Я должен сказать, что это действительно странный способ обработки DMA-передач. Передачи DMA обычно должны обрабатываться обработчиками прерываний DMA, а не периферийными устройствами.

Чтобы обрабатывать длинные пакеты, вы должны реализовать обработчик прерывания DMA. Таким образом, можно повторно запустить прием DMA до прерывания IDLE. Таким образом, DMA будет готов получать больше данных от UART.

Еще кое-что. В вашем коде есть гонка. между чтением bytes_number и отключением DMA один или несколько байтов могут быть переданы по DMA.

Но более правильный способ обработки UART - запустить DMA в циклическом режиме и не инициализировать его повторно. Потому что каждый раз, когда вы отключаете DMA, вы можете пропустить какой-то вход UART.

person alexander    schedule 13.01.2017
comment
Спасибо за ответ @alexander. Может быть, я вас не понимаю, но полученные байты с номерами 0, 0, ... - это реакция на следующие пакеты в новых бездействующих прерываниях, похоже, что DMA просто устал работать. Это странный способ. Я согласен, но это единственный способ, который я придумал, чтобы получать пакеты переменной длины с минимальной стоимостью процессора. И спасибо за гонку, я исправлю. - person Yuriy; 13.01.2017
comment
@Yuriy, а после длинного пакета вы отправляете еще один пакет? А вы попадаете 0 в следующие пакеты? Как вы проверяете bytes_number? Надеюсь, вы не остановите программу, использующую JTAG внутри ISR, поскольку JTAG может испортить реальную картинку. Используйте глобальную переменную для записи событий. Также возможно, что UART и DMA рассинхронизировали друг друга (поэтому вам нужно сбросить UART или DMA). Также возможно, что UART установит флаг переполнения и остановит прием до тех пор, пока его не сбросят. - person alexander; 13.01.2017
comment
Проверка нулевого числа_байт пропускается, если (число_байт> 0). Я попытался повторно подключить USART и DMA, и попытался отключить / включить DMA RCC. Я попробую выполнить полную переинициализацию, но это выглядит не очень хорошо. - person Yuriy; 13.01.2017
comment
Решение было простым. Установка DMA канала rx в круговой режим решила проблему. Теперь каждый слишком длинный пакет перехватывается флагом DMA Transmission Complete, и все последующие пакеты хорошо обрабатываются. Еще раз спасибо @alexander. Специально для гонки это решило мою другую проблему) - person Yuriy; 20.01.2017