STM32: круговой буфер DMA UART без выравнивания - PullRequest
0 голосов
/ 06 мая 2019

для моего приложения микроконтроллера STM32L053 мне нужен стабильный буфер UART RX и для этого используется реализация DMA от github, основанная на ST HAL: https://github.com/akospasztor/stm32-dma-uart.

Эта реализация работает достаточно стабильно, если RXвходные данные выровнены согласно соответствующему размеру буфера.Например, если размер буфера составляет 24 байта, а все входящие запросы данных имеют кратный размер этой длины буфера, скажем, например, 8 байтов на запросы, переполнение буфера работает без проблем.

MyПриложение использует сообщения различной длины, поэтому я обнаружил, что в этой реализации есть недостаток переполнения буфера без выравнивания.Например, если длина буфера установлена ​​в 23 байта, первые оба 8-байтовых сообщения передаются корректно, но следующие 8-байтовые сообщения передаются некорректно.

Для этого я расширил процедуру HAL_UART_RxCpltCallback для обработкипереполнение буфера без выравнивания с использованием регистра CNDTR DMA и запоминание его последнего значения в переменной dma_uart_rx.prevCNDTR:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *uartHandle)
{
  uint16_t i, pos, start, length;
  uint32_t currCNDTR = huart.hdmarx->Instance->CNDTR;

  /* Ignore IDLE Timeout when the received characters exactly filled up the DMA buffer and DMA Rx Complete IT is generated, but there is no new character during timeout */
  if((dma_uart_rx.flag && currCNDTR == DMA_BUF_SIZE) || error_flag)
  {
    error_flag = RESET;
    dma_uart_rx.flag = 0;
    return;
  }

  /* Determine start position in DMA buffer based on previous CNDTR value */
  start = (dma_uart_rx.prevCNDTR < DMA_BUF_SIZE) ? (DMA_BUF_SIZE - dma_uart_rx.prevCNDTR) : 0;

  if (dma_uart_rx.flag)    /* Timeout event */
  {
    /* Determine new data length based on previous DMA_CNDTR value:
     *  If previous CNDTR is less than DMA buffer size: there is old data in DMA buffer (from previous timeout) that has to be ignored.
     *  If CNDTR == DMA buffer size: entire buffer content is new and has to be processed.
    */
    length = (dma_uart_rx.prevCNDTR < DMA_BUF_SIZE) ? (dma_uart_rx.prevCNDTR - currCNDTR) : (DMA_BUF_SIZE - currCNDTR);
    dma_uart_rx.prevCNDTR = currCNDTR;
    dma_uart_rx.flag = 0;
  }
  else                /* DMA Rx Complete event */
  {
    // My buffer overrun handling
    if (currCNDTR > dma_uart_rx.prevCNDTR) 
    {
      length = dma_uart_rx.prevCNDTR + DMA_BUF_SIZE - currCNDTR;

      // 1st rx data part
      for (i=0, pos=DMA_BUF_SIZE - dma_uart_rx.prevCNDTR; pos < DMA_BUF_SIZE; ++i,++pos)
      {
        data[i] = dma_rx_buf[pos];
      }

      // 2nd rx data part
      for (pos=0; pos < DMA_BUF_SIZE - currCNDTR; ++i,++pos)
      {
        data[i] = dma_rx_buf[pos];
      }

      receivedBytes = length;
      dma_uart_rx.prevCNDTR = currCNDTR;
      return;
    }

    length = DMA_BUF_SIZE - start;
    dma_uart_rx.prevCNDTR = DMA_BUF_SIZE;
  }

  /* Copy and Process new data */
  for (i=0,pos=start; i<length; ++i,++pos)
  {
    data[i] = dma_rx_buf[pos];
  }

  receivedBytes = length;
}

До этого момента все работало без проблем, но затем я выяснил странное поведениерегистр CNDTR при переполнении буфера: если я остановился на точке останова после присвоения значения регистра CNDTR переменной currCNDTR, а затем сравнил текущее значение регистра регистра CNDTR с упомянутой переменной в отладчикепеременная всегда на 1 байт выше значения регистра CNDTR, хотя других назначений переменной нет!!

enter image description here

Можеткто-нибудь, пожалуйста, помогите мне разобраться, что я здесь не так делаю?

1 Ответ

0 голосов
/ 07 мая 2019

нет ничего общего с выравниванием.Вам нужно обработать два события - конец передачи DMA - это происходит, когда CNDR достигает нуля и IDLE от USART, чтобы обнаружить конец передачи usart.Вполне логично, что NTDR будет ниже, чем при передаче в фоновом режиме, и запуск точки останова занимает некоторое время.

...