Skip to main content
CSeto
Associate
June 21, 2019
Solved

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

  • June 21, 2019
  • 2 replies
  • 1086 views

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);
 }
}

This topic has been closed for replies.
Best answer by CSeto

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;
 }
 }
 }

2 replies

waclawek.jan
Super User
June 21, 2019

GPIO set correctly? Try a polled Rx.

Read out the USART and DMA registers and check.

JW

CSeto
CSetoAuthorBest answer
Associate
June 22, 2019

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;
 }
 }
 }