fix(radio): half-duplex UART (#6120)
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user