Is it possible to use STM32 HAL for UART full-duplex in interrupt mode?
Hi,
I want to use the UART HAL for full-duplex communication in interrupt mode. I couldn't find any examples for that.
For the first tests, my hardware setup is as follows:
- Nucleo-32 board connected via USB to PC, running terminal software with the COM port provided by the Nucleo-32 board
- enabled two UARTs on the STM32 device
- the first UART is the one connected to the on-board STlink
- the second UART has Rx & Tx pin connected together
My application will support different modes, one of them is simple pass-through for the two UARTs (that's the reason why Rx/Tx are shorted on the 2nd UART for testing).
Now, regarding the current test firmware, as far as I understand the HAL, the IRQ handler will call the HAL_UART_RxCpltCallback() function if the required amount of bytes indicated by calling HAL_UART_Receive_IT() function has been received.
So, the main() function is as follows:
int main(void) {
MX_USART1_UART_Init();
MX_USART2_UART_Init();
HAL_UART_Receive_IT(&huart1, &Uart1_Rx, 1);
HAL_UART_Receive_IT(&huart2, &Uart2_Rx, 1);
while(1) {
};
}Note: for readability I omitted initialization for clock, other peripherals etc, but the while(1) loop is really empty.
Uart1_Rx and Uart2_Rx are single byte variables, declared as volatile. The same exists for Tx.
In the receive complete handler, I forward the received byte to the Tx buffer of the other UART and start transmission. Also, receiption is re-enabled:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if(AppMode == LEGIC_DKS) {
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
if(huart == &huart1) {
Uart2_Tx = Uart1_Rx;
// while(huart1.RxState != HAL_UART_STATE_READY);
// HAL_UART_Receive_IT(&huart1, &Uart1_Rx, 1);
while(HAL_UART_Receive_IT(&huart1, &Uart1_Rx, 1) != HAL_OK);
// while(huart2.gState != HAL_UART_STATE_READY);
// HAL_UART_Transmit_IT(&huart2, &Uart2_Tx, 1);
while(HAL_UART_Transmit(&huart2, &Uart2_Tx, 1, HAL_MAX_DELAY) != HAL_OK);
}
else if(huart == &huart2) {
Uart1_Tx = Uart2_Rx;
// while(huart2.RxState != HAL_UART_STATE_READY);
// HAL_UART_Receive_IT(&huart2, &Uart2_Rx, 1);
while(HAL_UART_Receive_IT(&huart2, &Uart2_Rx, 1) != HAL_OK);
// while(huart1.gState != HAL_UART_STATE_READY);
// HAL_UART_Transmit_IT(&huart1, &Uart1_Tx, 1);
while(HAL_UART_Transmit(&huart1, &Uart1_Tx, 1, HAL_MAX_DELAY) != HAL_OK);
}
}
}This is the current code for the callback. The functions which are commented out are different approaches to achieve the pass-through function. As you may assume, none of them is working... ;)
The current setup works like an echo function due to the Rx/Tx pins of the 2nd UART connected together. In the terminal software, typing in single characters works as expected. But transmitting "short" strings (about 20 characters) will lead to missing characters, and bigger strings (500+ characters) will cause the firmware to hang up. Investigating via debugging, it looks like it's caused by how the HAL IRQ handler works.
As long as the receive completed callback is active, it prevents the IRQ handler to update the Tx status, therefore checking the Rx/Tx status in the callback will end up in endless loop.
Note: I know that there are other ways to realize the pass-through function, e.g. DMA, or modifying the HAL source code, using LL instead of HAL, etc. However, I want to know if it's possible with unmodified HAL in interrupt mode and by using only single byte buffers.
I know that this will lead to a worst-case scenario (e.g. interrupt overhead, etc.), but I'm curious if it's possible and of course, I want to know if I'm using the HAL functions the right way.
Regards
