cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F767 USART1 DMA2 TX works, RX does not. But with another UART/DMA it does. Why?

CSeto
Associate

I have the following code to perform a serial transaction. The idea is that RX DMA is configured first to catch bytes as soon as they are transmitted in response from the other device. TX works great, but RX does not. Interestingly enough, if I use a different UART with its corresponding DMA, both RX and TX work great (UART5, DMA1 Stream 7, DMA1 Stream 0). This exact code with these settings does not through. Shouldn't it all be exactly the same? This code bails out in the RX timeout.

bool UartInit(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;
 
	// Enable clocks
	__GPIOB_CLK_ENABLE();
	__USART1_CLK_ENABLE();
  __DMA2_CLK_ENABLE();
	
	// Configure GPIO
	GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull      = GPIO_NOPULL;
	GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_HIGH;
  
  // TX
	GPIO_InitStruct.Pin       = GPIO_PIN_14;
	GPIO_InitStruct.Alternate = GPIO_AF4_USART1;
	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  
  // RX
	GPIO_InitStruct.Pin       = GPIO_PIN_15;
	GPIO_InitStruct.Alternate = GPIO_AF4_USART1;
	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	// Configure the USART peripheral
  // RM0410 Page 249 for DMA mappings
  // USART1
  // DMA2
  // TX: (DMA2: Stream 7, channel 4)
  // RX: (DMA2: Stream 2, channel 4) 
  // DMA TX
  LL_DMA_SetChannelSelection(DMA2, LL_DMA_STREAM_7, LL_DMA_CHANNEL_4);
  LL_DMA_ConfigTransfer(DMA2, LL_DMA_STREAM_7, 
                        LL_DMA_DIRECTION_MEMORY_TO_PERIPH | 
                        LL_DMA_PRIORITY_HIGH              | 
                        LL_DMA_MODE_NORMAL                | 
                        LL_DMA_PERIPH_NOINCREMENT         | 
                        LL_DMA_MEMORY_INCREMENT           | 
                        LL_DMA_PDATAALIGN_BYTE            | 
                        LL_DMA_MDATAALIGN_BYTE);
  
  // DMA RX
  LL_DMA_SetChannelSelection(DMA2, LL_DMA_STREAM_5, LL_DMA_CHANNEL_4);
  LL_DMA_ConfigTransfer(DMA2, LL_DMA_STREAM_5, 
                        LL_DMA_DIRECTION_PERIPH_TO_MEMORY | 
                        LL_DMA_PRIORITY_HIGH              | 
                        LL_DMA_MODE_NORMAL                | 
                        LL_DMA_PERIPH_NOINCREMENT         | 
                        LL_DMA_MEMORY_INCREMENT           | 
                        LL_DMA_PDATAALIGN_BYTE            | 
                        LL_DMA_MDATAALIGN_BYTE);
 
  // Init UART
	uart_handle.Instance          = USART1;
	uart_handle.Init.BaudRate     = 115200;
	uart_handle.Init.WordLength   = UART_WORDLENGTH_8B;
	uart_handle.Init.StopBits     = UART_STOPBITS_1;
	uart_handle.Init.Parity       = UART_PARITY_NONE;
	uart_handle.Init.HwFlowCtl    = UART_HWCONTROL_NONE;
	uart_handle.Init.Mode         = UART_MODE_TX_RX;
  uart_handle.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
 
	// Commit the USART
	HAL_UART_Init(&uart_handle);
  
  // DMA TX interrupts
  LL_DMA_EnableIT_TC(DMA2, LL_DMA_STREAM_7);
  LL_DMA_EnableIT_TE(DMA2, LL_DMA_STREAM_7);
  HAL_NVIC_SetPriority(DMA2_Stream7_IRQn, 6, 1);
  HAL_NVIC_EnableIRQ(DMA2_Stream7_IRQn);
 
  // DMA RX interrupts
  LL_DMA_EnableIT_TC(DMA2, LL_DMA_STREAM_5);
  LL_DMA_EnableIT_TE(DMA2, LL_DMA_STREAM_5);
  HAL_NVIC_SetPriority(DMA2_Stream5_IRQn, 6, 1);
  HAL_NVIC_EnableIRQ(DMA2_Stream5_IRQn);
  
  return true;
}
 
static bool XAction(const uint8_t* tx_buffer, const uint32_t tx_size, const uint8_t* rx_buffer, const uint32_t rx_size, const TickType_t timeout)
{
  uint16_t crc;
  uint32_t dma_tx_ptr = 0;
  uint16_t rx_crc;
  TickType_t start_time;
  
  // We need to have a nonzero TX size and a valid buffer
  if (!tx_size || tx_buffer == NULL)
  {
    return false;
  }
  
  // If we have an rx size, we must have a valid rx buffer
  if ((rx_size && rx_buffer == NULL) || (!rx_size && rx_buffer != NULL))
  {
    return false;
  }
 
  // Assign in sync
  dma_tx_buffer[dma_tx_ptr++] = SERIAL_SYNC;
  
  // Copy in payload
  memcpy(dma_tx_buffer + dma_tx_ptr, tx_buffer, tx_size);
  dma_tx_ptr += tx_size;
  
  // Calculate CRC
  // Omit header byte
  crc = Crc16(dma_tx_buffer + SYNC_SIZE, dma_tx_ptr - SYNC_SIZE);
  
  // Pack CRC
  dma_tx_buffer[dma_tx_ptr++] = crc;
  dma_tx_buffer[dma_tx_ptr++] = (crc >> 8);
  
  // Record start time
  Ticks_Reset(&start_time);
  
  // If we are rxing, configure for rx now
  if (rx_size)
  {
    rxing = true;
    
    LL_DMA_ClearFlag_TC5(DMA2);
    LL_DMA_ClearFlag_TE5(DMA2);
    LL_DMA_ClearFlag_HT5(DMA2);
    LL_DMA_ClearFlag_FE5(DMA2);
    LL_DMA_ClearFlag_DME5(DMA2);
    LL_DMA_ConfigAddresses(DMA2, LL_DMA_STREAM_5,
                       LL_USART_DMA_GetRegAddr(USART1, LL_USART_DMA_REG_DATA_RECEIVE),
                       (uint32_t)&dma_rx_buffer,
                       LL_DMA_GetDataTransferDirection(DMA2, LL_DMA_STREAM_5));
    LL_DMA_SetDataLength(DMA2, LL_DMA_STREAM_5, SYNC_SIZE + rx_size + CRC_SIZE);
    LL_USART_EnableDMAReq_RX(USART1);
    LL_DMA_EnableStream(DMA2, LL_DMA_STREAM_5);
    
    // Wait for rx to be enabled
    while (!LL_DMA_IsEnabledStream(DMA2, LL_DMA_STREAM_5))
    {
      // Check if we have timed out
      if (Ticks_IsExpired(start_time, timeout))
      {
        printf("enabled");
        LL_DMA_DisableStream(DMA2, LL_DMA_STREAM_5);
        return false;
      }
    }
  }
  
  // Start TX
  txing = true;
  LL_DMA_ConfigAddresses(DMA2, LL_DMA_STREAM_7,
                         (uint32_t)&dma_tx_buffer,
                         LL_USART_DMA_GetRegAddr(USART1, LL_USART_DMA_REG_DATA_TRANSMIT),
                         LL_DMA_GetDataTransferDirection(DMA2, LL_DMA_STREAM_7));
  LL_DMA_SetDataLength(DMA2, LL_DMA_STREAM_7, dma_tx_ptr);
  LL_USART_EnableDMAReq_TX(USART1);
  LL_DMA_EnableStream(DMA2, LL_DMA_STREAM_7);
  
  // Wait for TX to complete
  while (txing)
  {
    // Check if we have timed out
    if (Ticks_IsExpired(start_time, timeout))
    {
      LL_DMA_DisableStream(DMA2, LL_DMA_STREAM_7);
      LL_DMA_DisableStream(DMA2, LL_DMA_STREAM_5);
      return false;
    }
  }
  
  // Wait for RX to complete
  if (rx_size)
  {
    // Wait for RX to complete
    while (rxing)
    {
      // Check if we have timed out
      if (Ticks_IsExpired(start_time, timeout))
      {
/*********************************** CODE BAILS HERE!! *****/
        printf("Ticks\r\n");
        LL_DMA_DisableStream(DMA2, LL_DMA_STREAM_7);
        LL_DMA_DisableStream(DMA2, LL_DMA_STREAM_5);
        return false;
      }
    }
 
    // Check for sync byte
    if (dma_rx_buffer[0] != SERIAL_SYNC)
    {
      printf("sync\r\n");
      return false;
    }
    
    // CRC the result
    // Exclude sync but include header
    crc = Crc16(dma_rx_buffer + SYNC_SIZE, rx_size);
    
    rx_crc = 0;
    rx_crc |= (dma_rx_buffer[SYNC_SIZE + rx_size]);
    rx_crc |= (dma_rx_buffer[SYNC_SIZE + rx_size + 1] << 8);
 
    // Verify calculated CRC against the CRC contained in the packet
    if (crc != rx_crc)
    {
      printf("crc\r\n");
      return false;
    }
    
    // Copy the payload out to rx
    // Exclude sync byte
    memcpy((uint8_t*)rx_buffer, dma_rx_buffer + SYNC_SIZE, rx_size);
  }
  
  printf("Ok!\r\n");
  return true;
}
// DMA buffers
#define XBUFFER_SIZE    256
static uint8_t dma_tx_buffer[XBUFFER_SIZE];
static uint8_t dma_rx_buffer[XBUFFER_SIZE];
 
// DMA flags
static volatile bool txing = false;
static volatile bool rxing = false;
 
// DMA TX
void DMA2_Stream7_IRQHandler()
{
  if (LL_DMA_IsActiveFlag_TC7(DMA2))
  {
    LL_DMA_ClearFlag_TC7(DMA2);
    txing = false;
  }
  
  if (LL_DMA_IsActiveFlag_TE7(DMA2))
  {
    LL_DMA_ClearFlag_TE7(DMA2);
  }
}
 
// DMA RX
void DMA2_Stream5_IRQHandler()
{
  if (LL_DMA_IsActiveFlag_TC5(DMA2))
  {
    LL_DMA_ClearFlag_TC5(DMA2);
    rxing = false;
  }
  
  if (LL_DMA_IsActiveFlag_TE5(DMA2))
  {
    LL_DMA_ClearFlag_TE5(DMA2);
  }
}

1 ACCEPTED SOLUTION

Accepted Solutions
CSeto
Associate

Solved! To those who may have this issue, the USART was in an error condition and in addition, there was data in the DR. The solution was to clear the error flags and clear the RXNE flag before known RX began.

  // If we are rxing, configure for rx now
  if (rx_size)
  {
    // Clear possible faults before enabling reception
    __HAL_UART_CLEAR_PEFLAG(&uart_handle);
    __HAL_UART_CLEAR_NEFLAG(&uart_handle);
    __HAL_UART_CLEAR_OREFLAG(&uart_handle);
    uart_handle.Instance->RQR |= USART_RQR_RXFRQ;
    
    // We're now in the process of receiving 
    rxing = true;
    LL_DMA_ConfigAddresses(DMA2, LL_DMA_STREAM_5,
                       LL_USART_DMA_GetRegAddr(USART1, LL_USART_DMA_REG_DATA_RECEIVE),
                       (uint32_t)&dma_rx_buffer,
                       LL_DMA_GetDataTransferDirection(DMA2, LL_DMA_STREAM_5));
    LL_DMA_SetDataLength(DMA2, LL_DMA_STREAM_5, SYNC_SIZE + rx_size + CRC_SIZE);
    LL_USART_EnableDMAReq_RX(USART1);
    LL_DMA_EnableStream(DMA2, LL_DMA_STREAM_5);
    
    // Wait for rx to be enabled
    while (!LL_DMA_IsEnabledStream(DMA2, LL_DMA_STREAM_5))
    {
      // Check if we have timed out
      if (Ticks_IsExpired(start_time, timeout))
      {
        LL_DMA_DisableStream(DMA2, LL_DMA_STREAM_5);
        return false;
      }
    }
  }

View solution in original post

2 REPLIES 2

GPIO set correctly? Try a polled Rx.

Read out the USART and DMA registers and check.

JW

CSeto
Associate

Solved! To those who may have this issue, the USART was in an error condition and in addition, there was data in the DR. The solution was to clear the error flags and clear the RXNE flag before known RX began.

  // If we are rxing, configure for rx now
  if (rx_size)
  {
    // Clear possible faults before enabling reception
    __HAL_UART_CLEAR_PEFLAG(&uart_handle);
    __HAL_UART_CLEAR_NEFLAG(&uart_handle);
    __HAL_UART_CLEAR_OREFLAG(&uart_handle);
    uart_handle.Instance->RQR |= USART_RQR_RXFRQ;
    
    // We're now in the process of receiving 
    rxing = true;
    LL_DMA_ConfigAddresses(DMA2, LL_DMA_STREAM_5,
                       LL_USART_DMA_GetRegAddr(USART1, LL_USART_DMA_REG_DATA_RECEIVE),
                       (uint32_t)&dma_rx_buffer,
                       LL_DMA_GetDataTransferDirection(DMA2, LL_DMA_STREAM_5));
    LL_DMA_SetDataLength(DMA2, LL_DMA_STREAM_5, SYNC_SIZE + rx_size + CRC_SIZE);
    LL_USART_EnableDMAReq_RX(USART1);
    LL_DMA_EnableStream(DMA2, LL_DMA_STREAM_5);
    
    // Wait for rx to be enabled
    while (!LL_DMA_IsEnabledStream(DMA2, LL_DMA_STREAM_5))
    {
      // Check if we have timed out
      if (Ticks_IsExpired(start_time, timeout))
      {
        LL_DMA_DisableStream(DMA2, LL_DMA_STREAM_5);
        return false;
      }
    }
  }