fix(radio): half-duplex UART (#6120)

This commit is contained in:
Raphael Coeffic
2025-04-23 12:08:21 +10:00
committed by GitHub
parent 8344f4ce28
commit 8fe9f685af
3 changed files with 67 additions and 40 deletions
@@ -293,9 +293,15 @@ static const stm32_usart_t sportUSART = {
.txDMA = TELEMETRY_DMA,
.txDMA_Stream = TELEMETRY_DMA_Stream_TX,
.txDMA_Channel = TELEMETRY_DMA_Channel_TX,
#if defined(TELEMETRY_DMA_Stream_RX)
.rxDMA = TELEMETRY_DMA,
.rxDMA_Stream = TELEMETRY_DMA_Stream_RX,
.rxDMA_Channel = TELEMETRY_DMA_Channel_RX,
#else
.rxDMA = nullptr,
.rxDMA_Stream = 0,
.rxDMA_Channel = 0,
#endif
#if defined(STM32H7) || (STM32H7RS)
.set_input = nullptr,
#else
@@ -337,7 +337,22 @@ static void stm32_serial_send_byte(void* ctx, uint8_t c)
}
}
#define IS_CCM_RAM(addr) (((uint32_t)(addr) & (uint32_t)0xFFF00000) == 0x10000000)
#if defined(STM32F4)
extern uint32_t _sram;
extern uint32_t _eram;
#define _IS_DMA_BUFFER(addr) \
((intptr_t)(addr) >= (intptr_t)&_sram && (intptr_t)(addr) <= (intptr_t)&_eram)
#elif defined(STM32H7) || defined(STM32H7RS)
extern uint32_t _s_dram;
extern uint32_t _e_dram;
#define _IS_DMA_BUFFER(addr) \
((intptr_t)(addr) >= (intptr_t)&_s_dram && \
(intptr_t)(addr) <= (intptr_t)&_e_dram)
#else
#define _IS_DMA_BUFFER(addr) (true)
#endif
#define _IS_ALIGNED(addr) (((intptr_t)(addr) & 3U) == 0U)
static void stm32_serial_send_buffer(void* ctx, const uint8_t* data, uint32_t size)
{
@@ -347,10 +362,7 @@ static void stm32_serial_send_buffer(void* ctx, const uint8_t* data, uint32_t si
// try TX DMA first
auto sp = st->sp;
auto usart = sp->usart;
if (usart->txDMA && !IS_CCM_RAM(data)) {
#if defined(STM32H7) || defined(STM32H7RS)
SCB_CleanDCache_by_Addr((void*)data, size);
#endif
if (usart->txDMA && _IS_DMA_BUFFER(data) && _IS_ALIGNED(data)) {
stm32_usart_send_buffer(usart, data, size);
return;
}
@@ -28,6 +28,12 @@
#include <string.h>
#if defined(STM32H7) || defined(STM32H7RS)
#define IS_HALF_DUPLEX(usart) ((usart)->set_input || (usart)->rxGPIO == GPIO_UNDEF)
#else
#define IS_HALF_DUPLEX(usart) ((usart)->set_input)
#endif
// WARNING:
//
// NVIC_GetEnableIRQ is stolen from "${THIRDPARTY_DIR}/CMSIS/Include/core_cm4.h".
@@ -66,19 +72,19 @@ static void _enable_tx_dma_irq(const stm32_usart_t* usart)
static inline void _half_duplex_input(const stm32_usart_t* usart)
{
if (usart->set_input) {
usart->set_input(true);
if (usart->set_input) usart->set_input(true);
if (IS_HALF_DUPLEX(usart)) {
LL_USART_EnableDirectionRx(usart->USARTx);
LL_USART_DisableDirectionTx(usart->USARTx);
}
}
static inline void _half_duplex_output(const stm32_usart_t* usart)
{
if (usart->set_input) {
usart->set_input(false);
if (usart->set_input) usart->set_input(false);
if (IS_HALF_DUPLEX(usart)) {
LL_USART_DisableDirectionRx(usart->USARTx);
LL_USART_EnableDirectionTx(usart->USARTx);
}
}
@@ -370,27 +376,25 @@ void stm32_usart_deinit_rx_dma(const stm32_usart_t* usart)
void stm32_usart_rx_inversion(const stm32_usart_t* usart, bool on)
{
bool enableUart = false;
if(LL_USART_IsEnabled(usart->USARTx))
{
if (LL_USART_IsEnabled(usart->USARTx)) {
LL_USART_Disable(usart->USARTx);
enableUart = true;
}
LL_USART_SetRXPinLevel(usart->USARTx, on?LL_USART_RXPIN_LEVEL_INVERTED:LL_USART_RXPIN_LEVEL_STANDARD);
if(enableUart)
LL_USART_Enable(usart->USARTx);
LL_USART_SetRXPinLevel(usart->USARTx, on ? LL_USART_RXPIN_LEVEL_INVERTED
: LL_USART_RXPIN_LEVEL_STANDARD);
if (enableUart) LL_USART_Enable(usart->USARTx);
}
void stm32_usart_tx_inversion(const stm32_usart_t* usart, bool on)
{
bool enableUart = false;
if(LL_USART_IsEnabled(usart->USARTx))
{
if (LL_USART_IsEnabled(usart->USARTx)) {
LL_USART_Disable(usart->USARTx);
enableUart = true;
}
LL_USART_SetTXPinLevel(usart->USARTx, on?LL_USART_TXPIN_LEVEL_INVERTED:LL_USART_TXPIN_LEVEL_STANDARD);
if(enableUart)
LL_USART_Enable(usart->USARTx);
LL_USART_SetTXPinLevel(usart->USARTx, on ? LL_USART_TXPIN_LEVEL_INVERTED
: LL_USART_TXPIN_LEVEL_STANDARD);
if (enableUart) LL_USART_Enable(usart->USARTx);
}
#endif
@@ -424,6 +428,24 @@ bool stm32_usart_init(const stm32_usart_t* usart, const etx_serial_init* params)
gpio_init_af(usart->txGPIO, af, _get_pin_speed(params->baudrate));
}
bool half_duplex = usart->set_input;
#if defined(STM32H7) || defined(STM32H7RS)
bool one_wire_half_duplex =
usart->rxGPIO == GPIO_UNDEF && (params->direction & ETX_Dir_RX);
if (one_wire_half_duplex) {
if (params->direction & ETX_Dir_TX) {
LL_GPIO_SetPinPull(
gpio_get_port(usart->txGPIO), 1 << gpio_get_pin(usart->txGPIO),
params->polarity ? LL_GPIO_PULL_UP : LL_GPIO_PULL_DOWN);
}
LL_USART_ConfigHalfDuplexMode(usart->USARTx);
}
half_duplex = half_duplex || one_wire_half_duplex;
#endif
LL_USART_InitTypeDef usartInit;
LL_USART_StructInit(&usartInit);
@@ -459,22 +481,15 @@ bool stm32_usart_init(const stm32_usart_t* usart, const etx_serial_init* params)
usartInit.TransferDirection |= LL_USART_DIRECTION_TX;
LL_USART_Init(usart->USARTx, &usartInit);
#if defined(STM32H7) || defined (STM32H7RS)
if(usart->rxGPIO == GPIO_UNDEF)
{
LL_USART_EnableHalfDuplex(usart->USARTx);
}
#endif
LL_USART_Enable(usart->USARTx);
if (params->direction & ETX_Dir_TX) {
// Enable TX DMA request
if (usart->txDMA) {
LL_USART_EnableDMAReq_TX(usart->USARTx);
// 2-wire half-duplex: setup TX DMA IRQ
if (usart->set_input && (int32_t)(usart->txDMA_IRQn) >= 0) {
if (half_duplex && (int32_t)(usart->txDMA_IRQn) >= 0) {
_enable_tx_dma_irq(usart);
}
}
@@ -487,8 +502,9 @@ bool stm32_usart_init(const stm32_usart_t* usart, const etx_serial_init* params)
}
// half-duplex: start in input mode
if (usart->set_input)
if (half_duplex) {
_half_duplex_input(usart);
}
}
if (((params->direction & ETX_Dir_TX) && !usart->txDMA) ||
@@ -528,7 +544,7 @@ void stm32_usart_send_byte(const stm32_usart_t* usart, uint8_t byte)
// TODO: split into 2 steps to avoid blocking on send
while (!LL_USART_IsActiveFlag_TXE(usart->USARTx));
if (usart->set_input) {
if (IS_HALF_DUPLEX(usart)) {
_half_duplex_output(usart);
// switch back to input after TC
@@ -573,8 +589,9 @@ void stm32_usart_send_buffer(const stm32_usart_t* usart, const uint8_t * data, u
LL_DMA_Init(usart->txDMA, usart->txDMA_Stream, &dmaInit);
if (usart->set_input && (int32_t)(usart->txDMA_IRQn) >= 0) {
if (IS_HALF_DUPLEX(usart) && (int32_t)(usart->txDMA_IRQn) >= 0) {
LL_DMA_EnableIT_TC(usart->txDMA, usart->txDMA_Stream);
LL_USART_ClearFlag_TC(usart->USARTx);
}
LL_DMA_EnableStream(usart->txDMA, usart->txDMA_Stream);
#endif // !STM32H7RS
@@ -779,13 +796,5 @@ void stm32_usart_tx_dma_isr(const stm32_usart_t* usart)
return;
auto USARTx = usart->USARTx;
// clear TC flag before enabling USART TC interrupt:
// -> TC flag will be re-triggered once the last byte which has
// just been transfered from DMA to USART will be transmitted,
// thus triggering TELEMETRY_USART_IRQHandler(), which will
// switch from output to input mode.
//
LL_USART_ClearFlag_TC(USARTx);
LL_USART_EnableIT_TC(USARTx);
}