2025-01-05 11:56 AM
Greetings
I am trying to use 2 UART interfaces on the STM32L4xx series and echo what's received on 1 interface. I am receiving strings of unknown length on UART1 using the DMA and echo-ing it with the virtual com port (UART2).
I intend on using receiving one byte at a time and using the HAL_UARTEx_ReceiveToIdle_DMA() function, and be notified via the ISR HAL_UARTEx_RxEventCallback() function, when the UART1-reception is completed because UART1 will be idle, and I can form the array of bytes received and transmit it via UART2. However, HAL_UARTEx_RxEventCallback() only fires for each byte received. The brief of the function says it will fire when the uart line is idle.
I have also tried to use the HAL_UART_RxCpltCallback() function to notify me that reception is complete, but it never fires.
What am I doing wrong? My code is as follows
#define UART_RX_BUFFER_SIZE 1
uint8_t UART1_RxBuffer[UART_RX_BUFFER_SIZE] = {0};
uint16_t RxDataLen = 0;
uint16_t cnt = 0;
uint8_t final_buffer[1024];
bool busy_flag = false;
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART2_UART_Init();
MX_USART3_UART_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/*
* Init of the reception using DMA
*
* */
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, UART1_RxBuffer, UART_RX_BUFFER_SIZE);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/*
* Looking to use this flag to transmit the constructed string from each byte/character received
*
* */
if(busy_flag == 1)
{
busy_flag = 0;
HAL_UART_Transmit(&huart2, final_buffer, strlen(final_buffer), 100);
}
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/* ISR which runs when a byte is received but does not notify when the uart line is idle*/
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
final_buffer[cnt++] = UART1_RxBuffer[0];
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, UART1_RxBuffer, UART_RX_BUFFER_SIZE);
}
/*
* This ISR does not fire at all. One would think this ISR fires when the reception is complete
*
*
* */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
busy_flag = 1;
}
2025-01-05 12:46 PM
1-byte DMA seems a pointless exercise, you might as well do in IRQ's, it will be less complicated. Perhaps use SysTick to manage gap detection, or something with even better resolution.
Variables changed outside normal execution, ie in DMA, IRQ or callback should be volatile.
Is there potential for no apparent delay between strings? Or any reason not to forward immediately? You really don't want to be playing catch-up, and the input/output rates might be slightly divergent.
Use int, uint32_t, or size_t for counters, uint16_t may take less space, but aren't more efficient
2025-01-05 04:54 PM
Call HAL_UARTEx_ReceiveToIdle_DMA to start the transfer, as you've done.
In HAL_UARTEx_RxEventCallback, you can see why it was called by calling HAL_UARTEx_GetRxEventType. That will tell you if the line is idle, or the buffer is half complete, or complete. The HAL_UART_RxCpltCallback function is never called here; it is called on non receive-to-idle function starts.
Documentation of these functions spells it out:
/**
* @brief Provide Rx Event type that has lead to RxEvent callback execution.
* @note When HAL_UARTEx_ReceiveToIdle_IT() or HAL_UARTEx_ReceiveToIdle_DMA() API are called, progress
* of reception process is provided to application through calls of Rx Event callback (either default one
* HAL_UARTEx_RxEventCallback() or user registered one). As several types of events could occur (IDLE event,
* Half Transfer, or Transfer Complete), this function allows to retrieve the Rx Event type that has lead
* to Rx Event callback execution.
* @note This function is expected to be called within the user implementation of Rx Event Callback,
* in order to provide the accurate value :
* In Interrupt Mode :
* - HAL_UART_RXEVENT_TC : when Reception has been completed (expected nb of data has been received)
* - HAL_UART_RXEVENT_IDLE : when Idle event occurred prior reception has been completed (nb of
* received data is lower than expected one)
* In DMA Mode :
* - HAL_UART_RXEVENT_TC : when Reception has been completed (expected nb of data has been received)
* - HAL_UART_RXEVENT_HT : when half of expected nb of data has been received
* - HAL_UART_RXEVENT_IDLE : when Idle event occurred prior reception has been completed (nb of
* received data is lower than expected one).
* In DMA mode, RxEvent callback could be called several times;
* When DMA is configured in Normal Mode, HT event does not stop Reception process;
* When DMA is configured in Circular Mode, HT, TC or IDLE events don't stop Reception process;
* @PAram huart UART handle.
* @retval Rx Event Type (return vale will be a value of @ref UART_RxEvent_Type_Values)
*/
HAL_UART_RxEventTypeTypeDef HAL_UARTEx_GetRxEventType(const UART_HandleTypeDef *huart)
This is better done with a buffer of greater than one byte in size. If you want to handle every character coming in, it would be more efficient to use interrupts. Using a buffer size largest than the size of the message you expect to receive is typically best.
2025-01-06 12:33 PM
When using HAL_UARTEx_ReceiveToIdle_DMA, the callback that HAL driver uses is HAL_UARTEx_RxEventCallback, not HAL_UART_RxCpltCallback
If you are intending to interrupt on each byte, then you might as well use HAL_UART_Receive_IT along with HAL_UART_RxCpltCallback.
To take advantage of using the DMA, see this project that demonstrates the use of the DMA in Circular Mode which interrupts on Transfer Complete, Half Transfer and Idle.
https://github.com/karlyamashita/Nucleo-G071RB_UART_DMA_Idle_Circular/wiki