Configure STM32 U585 UART in single-wire half duplex mode
I am trying to configure the LPUART1 on STM32U585 in single-wire half duplex mode. As I understand, in half-duplex mode, only one pin is configured which is used to send and receive data. I need the half-duplex mode for self-test in my code letting me know that the hardware pin itself is operational. I do not want to use DMA or interrupts for transmit or receive. I am configuring the GPIO Pin as follows -
I am only configuring PC1 as both the TX and RX pin.
GPIO_HI (UART_PORT, IPC_UART_TX_PIN); //UART PORT = PORT C, IPC_UART_TX_PIN = Pin 1
GPIO_SET_MODE (UART_PORT, IPC_UART_TX_PIN, GPIO_MODE_ALT_FUNC);
GPIO_SET_OUT_TYPE (UART_PORT, IPC_UART_TX_PIN, GPIO_OTYPE_OPENDRAIN);
GPIO_SET_SPEED (UART_PORT, IPC_UART_TX_PIN, GPIO_SPEED_LO__10MHZ);
GPIO_SET_PULL_UPDN(UART_PORT, IPC_UART_TX_PIN, GPIO_PUPD_PULLUP);
GPIO_SET_ALT_FUNC (UART_PORT, IPC_UART_TX_PIN, GPIO_AFN_LPUART1);
I enable the half-duplex mode when doing self-test by doing the following -
void enable_halfduplex(USART_TypeDef * p_uart_handle)
{
// Single-wire Half-duplex mode is selected by setting
// the HDSEL bit in the LPUART_CR3 register.
// disable usart controller.
// this is needed for changes
// to usart setting.
p_uart_handle->CR1 &= ~(USART_CR1_UE);
p_uart_handle->CR1 &= ~(USART_CR1_TE);
p_uart_handle->CR1 &= ~(USART_CR1_RE);
p_uart_handle->CR3 |= USART_CR3_HDSEL;
// enable usart controller.
p_uart_handle->CR1 |= (USART_CR1_UE);
p_uart_handle->CR1 |= (USART_CR1_TE);
p_uart_handle->CR1 |= (USART_CR1_RE);
}
As I understand, HAL_HalfDuplex_EnableTrasmitter() and HAL_HalfDuplex_EnableReceiver() needs to be called everytime we transmit or receive data. I created my versions of these two functions since I am not using the HAL library -
void uart_half_duplex_enable_transmitter(USART_TypeDef * p_uart_handle)
{
p_uart_handle->CR1 &= ~(USART_CR1_UE);
/* Clear TE and RE bits */
p_uart_handle->CR1 &= ~((uint32_t)(USART_CR1_TE | USART_CR1_RE));
p_uart_handle->CR1 |= (USART_CR1_UE);
/* Enable the USART's transmit interface by setting the TE bit in the USART CR1 register */
SET_BIT(p_uart_handle->CR1, USART_CR1_TE) ;
}
void uart_half_duplex_enable_receiver(USART_TypeDef * p_uart_handle)
{
p_uart_handle->CR1 &= ~(USART_CR1_UE);
/* Clear TE and RE bits */
p_uart_handle->CR1 &= ~((uint32_t)(USART_CR1_TE | USART_CR1_RE));
p_uart_handle->CR1 |= (USART_CR1_UE);
/* Enable the USART's transmit interface by setting the TE bit in the USART CR1 register */
SET_BIT(p_uart_handle->CR1, USART_CR1_RE) ;
}
I transmit and receive as follows-
int uart_tx(const uint8_t c, USART_TypeDef * p_uart_handle)
{
uint32_t reg;
do
{
reg = p_uart_handle->ISR;
} while ((reg & USART_ISR_TXE) == 0);
p_uart_handle->TDR = c;
return 1;
}
int uart_rx(uint8_t *c, USART_TypeDef * p_uart_handle)
{
volatile uint32_t reg = p_uart_handle->ISR;
if ((reg & USART_ISR_RXNE) != 0)
{
reg = p_uart_handle->RDR;
*c = (uint8_t)(reg & 0xFF);
return 1;
}
return 0;
}
void test_half_duplex(USART_TypeDef * p_uart_handle)
{
uint8_t tbuffer[] = USART_TEST_STRING;
for (chars = 0; chars < USART_TEST_STRING_SIZE; chars++)
{
uart_half_duplex_enable_transmitter(p_session->p_uart_handle);
(void)uart_tx((uint8_t)tbuffer[chars], p_session->p_uart_handle);
uart_half_duplex_enable_receiver(p_session->p_uart_handle);
uart_rx(&rbuffer[chars], p_session->p_uart_handle);
}
}
When I do this, I see the data being transmitted and see it in the TDR register but I never receive it in the RDR register. I am configuring the PC1 GPIO as in open drain and pull up mode? Is this pull-up sufficient for me to be able to receive the sent data or do I need to add an external pull-up? I am really confused as to why I cannot receive the sent data on the same pin. Any help in the right direction is really appreciated. Thank you!
