2019-06-20 07:39 PM
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);
}
}
Solved! Go to Solution.
2019-06-21 06:11 PM
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;
}
}
}
2019-06-21 12:37 AM
GPIO set correctly? Try a polled Rx.
Read out the USART and DMA registers and check.
JW
2019-06-21 06:11 PM
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;
}
}
}