1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-19 22:35:23 +03:00

Tri-state USART TX output if load due to powered down peripheral is detected (#12760)

Tri-state USART TX output if loaded due to powered down peripheral being detected
This commit is contained in:
Steve Evans 2023-05-10 02:53:35 +01:00 committed by GitHub
parent cbaf9b0265
commit 4dc04d6a33
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 239 additions and 28 deletions

View file

@ -177,11 +177,44 @@ void uartReconfigure(uartPort_t *uartPort)
} else {
usart_interrupt_enable(uartPort->USARTx, USART_TDBE_INT, TRUE);
}
usart_interrupt_enable(uartPort->USARTx, USART_TDC_INT, TRUE);
}
usart_enable(uartPort->USARTx,TRUE);
}
bool checkUsartTxOutput(uartPort_t *s)
{
uartDevice_t *uart = container_of(s, uartDevice_t, port);
IO_t txIO = IOGetByTag(uart->tx.pin);
if ((uart->txPinState == TX_PIN_MONITOR) && txIO) {
if (IORead(txIO)) {
// TX is high so we're good to transmit
// Enable USART TX output
uart->txPinState = TX_PIN_ACTIVE;
IOConfigGPIOAF(txIO, IOCFG_AF_PP, uart->hardware->af);
return true;
} else {
// TX line is pulled low so don't enable USART TX
return false;
}
}
return true;
}
void uartTxMonitor(uartPort_t *s)
{
uartDevice_t *uart = container_of(s, uartDevice_t, port);
IO_t txIO = IOGetByTag(uart->tx.pin);
// Switch TX to an input with pullup so it's state can be monitored
uart->txPinState = TX_PIN_MONITOR;
IOConfigGPIO(txIO, IOCFG_IPU);
}
#ifdef USE_DMA
void uartTryStartTxDMA(uartPort_t *s)
{
@ -228,7 +261,14 @@ void uartTryStartTxDMA(uartPort_t *s)
static void handleUsartTxDma(uartPort_t *s)
{
uartDevice_t *uart = container_of(s, uartDevice_t, port);
uartTryStartTxDMA(s);
if (s->txDMAEmpty && (uart->txPinState != TX_PIN_IGNORE)) {
// Switch TX to an input with pullup so it's state can be monitored
uartTxMonitor(s);
}
}
void uartDmaIrqHandler(dmaChannelDescriptor_t* descriptor)
@ -258,6 +298,14 @@ void uartIrqHandler(uartPort_t *s)
}
}
// UART transmission completed
if ((usart_flag_get(s->USARTx, USART_TDC_FLAG) != RESET)) {
usart_flag_clear(s->USARTx, USART_TDC_FLAG);
// Switch TX to an input with pull-up so it's state can be monitored
uartTxMonitor(s);
}
if (!s->txDMAResource && (usart_flag_get(s->USARTx, USART_TDBE_FLAG) == SET)) {
if (s->port.txBufferTail != s->port.txBufferHead) {
usart_data_transmit(s->USARTx, s->port.txBuffer[s->port.txBufferTail]);

View file

@ -286,6 +286,8 @@ uartPort_t *serialUART(UARTDevice_e device, uint32_t baudRate, portMode_e mode,
s->port.rxBufferSize = hardware->rxBufferSize;
s->port.txBufferSize = hardware->txBufferSize;
s->checkUsartTxOutput = checkUsartTxOutput;
#ifdef USE_DMA
uartConfigureDma(uartdev);
#endif
@ -297,6 +299,8 @@ uartPort_t *serialUART(UARTDevice_e device, uint32_t baudRate, portMode_e mode,
IO_t txIO = IOGetByTag(uartdev->tx.pin);
IO_t rxIO = IOGetByTag(uartdev->rx.pin);
uartdev->txPinState = TX_PIN_IGNORE;
if ((options & SERIAL_BIDIR) && txIO) {
//mode,speed,otype,pupd
ioConfig_t ioCfg = IO_CONFIG(
@ -310,7 +314,14 @@ uartPort_t *serialUART(UARTDevice_e device, uint32_t baudRate, portMode_e mode,
} else {
if ((mode & MODE_TX) && txIO) {
IOInit(txIO, OWNER_SERIAL_TX, RESOURCE_INDEX(device));
IOConfigGPIOAF(txIO, IOCFG_AF_PP, uartdev->tx.af);
if ((options & SERIAL_INVERTED) == SERIAL_NOT_INVERTED) {
uartdev->txPinState = TX_PIN_ACTIVE;
// Switch TX to an input with pullup so it's state can be monitored
uartTxMonitor(s);
} else {
IOConfigGPIOAF(txIO, IOCFG_AF_PP, uartdev->tx.af);
}
}
if ((mode & MODE_RX) && rxIO) {

View file

@ -267,6 +267,12 @@ static void uartWrite(serialPort_t *instance, uint8_t ch)
{
uartPort_t *uartPort = (uartPort_t *)instance;
// Check if the TX line is being pulled low by an unpowered peripheral
if (uartPort->checkUsartTxOutput && !uartPort->checkUsartTxOutput(uartPort)) {
// TX line is being pulled low, so don't transmit
return;
}
uartPort->port.txBuffer[uartPort->port.txBufferHead] = ch;
if (uartPort->port.txBufferHead + 1 >= uartPort->port.txBufferSize) {

View file

@ -76,6 +76,8 @@ typedef struct uartPort_s {
#endif
USART_TypeDef *USARTx;
bool txDMAEmpty;
bool (* checkUsartTxOutput)(struct uartPort_s *s);
} uartPort_t;
void uartPinConfigure(const serialPinConfig_t *pSerialPinConfig);

View file

@ -217,6 +217,12 @@ extern const uartHardware_t uartHardware[];
// uartDevice_t is an actual device instance.
// XXX Instances are allocated for uarts defined by USE_UARTx atm.
typedef enum {
TX_PIN_ACTIVE,
TX_PIN_MONITOR,
TX_PIN_IGNORE
} txPinState_t;
typedef struct uartDevice_s {
uartPort_t port;
const uartHardware_t *hardware;
@ -227,6 +233,7 @@ typedef struct uartDevice_s {
#if !defined(STM32F4) // Don't support pin swap.
bool pinSwap;
#endif
txPinState_t txPinState;
} uartDevice_t;
extern uartDevice_t *uartDevmap[];
@ -245,6 +252,9 @@ void uartConfigureDma(uartDevice_t *uartdev);
void uartDmaIrqHandler(dmaChannelDescriptor_t* descriptor);
bool checkUsartTxOutput(uartPort_t *s);
void uartTxMonitor(uartPort_t *s);
#if defined(STM32F7) || defined(STM32H7) || defined(STM32G4)
#define UART_REG_RXD(base) ((base)->RDR)
#define UART_REG_TXD(base) ((base)->TDR)

View file

@ -231,11 +231,46 @@ void uartReconfigure(uartPort_t *uartPort)
/* Enable the UART Transmit Data Register Empty Interrupt */
SET_BIT(uartPort->USARTx->CR1, USART_CR1_TXEIE);
SET_BIT(uartPort->USARTx->CR1, USART_CR1_TCIE);
}
}
return;
}
bool checkUsartTxOutput(uartPort_t *s)
{
uartDevice_t *uart = container_of(s, uartDevice_t, port);
IO_t txIO = IOGetByTag(uart->tx.pin);
if ((uart->txPinState == TX_PIN_MONITOR) && txIO) {
if (IORead(txIO)) {
// TX is high so we're good to transmit
// Enable USART TX output
uart->txPinState = TX_PIN_ACTIVE;
IOConfigGPIOAF(txIO, IOCFG_AF_PP, uart->tx.af);
return true;
} else {
// TX line is pulled low so don't enable USART TX
return false;
}
}
return true;
}
void uartTxMonitor(uartPort_t *s)
{
uartDevice_t *uart = container_of(s, uartDevice_t, port);
IO_t txIO = IOGetByTag(uart->tx.pin);
if (uart->txPinState == TX_PIN_ACTIVE) {
// Switch TX to an input with pullup so it's state can be monitored
uart->txPinState = TX_PIN_MONITOR;
IOConfigGPIO(txIO, IOCFG_IPU);
}
}
#ifdef USE_DMA
void uartTryStartTxDMA(uartPort_t *s)
{
@ -275,7 +310,14 @@ void uartTryStartTxDMA(uartPort_t *s)
static void handleUsartTxDma(uartPort_t *s)
{
uartDevice_t *uart = container_of(s, uartDevice_t, port);
uartTryStartTxDMA(s);
if (s->txDMAEmpty && (uart->txPinState != TX_PIN_IGNORE)) {
// Switch TX to an input with pullup so it's state can be monitored
uartTxMonitor(s);
}
}
void uartDmaIrqHandler(dmaChannelDescriptor_t* descriptor)
@ -343,7 +385,19 @@ FAST_IRQ_HANDLER void uartIrqHandler(uartPort_t *s)
__HAL_UART_CLEAR_IT(huart, UART_CLEAR_OREF);
}
// UART transmitter in interrupting mode, tx buffer empty
// UART transmission completed
if ((__HAL_UART_GET_IT(huart, UART_IT_TC) != RESET)) {
__HAL_UART_CLEAR_IT(huart, UART_CLEAR_TCF);
// Switch TX to an input with pull-up so it's state can be monitored
uartTxMonitor(s);
#ifdef USE_DMA
if (s->txDMAResource) {
handleUsartTxDma(s);
}
#endif
}
if (
#ifdef USE_DMA
@ -351,33 +405,19 @@ FAST_IRQ_HANDLER void uartIrqHandler(uartPort_t *s)
#endif
(__HAL_UART_GET_IT(huart, UART_IT_TXE) != RESET)) {
/* Check that a Tx process is ongoing */
if (huart->gState != HAL_UART_STATE_BUSY_TX) {
if (s->port.txBufferTail == s->port.txBufferHead) {
huart->TxXferCount = 0;
/* Disable the UART Transmit Data Register Empty Interrupt */
CLEAR_BIT(huart->Instance->CR1, USART_CR1_TXEIE);
if (s->port.txBufferTail == s->port.txBufferHead) {
/* Disable the UART Transmit Data Register Empty Interrupt */
CLEAR_BIT(huart->Instance->CR1, USART_CR1_TXEIE);
} else {
if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE)) {
huart->Instance->TDR = (((uint16_t) s->port.txBuffer[s->port.txBufferTail]) & (uint16_t) 0x01FFU);
} else {
if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE)) {
huart->Instance->TDR = (((uint16_t) s->port.txBuffer[s->port.txBufferTail]) & (uint16_t) 0x01FFU);
} else {
huart->Instance->TDR = (uint8_t)(s->port.txBuffer[s->port.txBufferTail]);
}
s->port.txBufferTail = (s->port.txBufferTail + 1) % s->port.txBufferSize;
huart->Instance->TDR = (uint8_t)(s->port.txBuffer[s->port.txBufferTail]);
}
s->port.txBufferTail = (s->port.txBufferTail + 1) % s->port.txBufferSize;
}
}
// UART transmitter in DMA mode, transmission completed
if ((__HAL_UART_GET_IT(huart, UART_IT_TC) != RESET)) {
HAL_UART_IRQHandler(huart);
#ifdef USE_DMA
if (s->txDMAResource) {
handleUsartTxDma(s);
}
#endif
}
// UART reception idle detected
if (__HAL_UART_GET_IT(huart, UART_IT_IDLE)) {

View file

@ -180,6 +180,8 @@ void uartReconfigure(uartPort_t *uartPort)
} else {
USART_ITConfig(uartPort->USARTx, USART_IT_TXE, ENABLE);
}
// Enable the interrupt so completion of the transmission will be signalled
USART_ITConfig(uartPort->USARTx, USART_IT_TC, ENABLE);
}
USART_Cmd(uartPort->USARTx, ENABLE);

View file

@ -26,6 +26,7 @@
#include <stdint.h>
#include "platform.h"
#include "build/debug.h"
#ifdef USE_UART
@ -217,9 +218,48 @@ const uartHardware_t uartHardware[UARTDEV_COUNT] = {
#endif
};
bool checkUsartTxOutput(uartPort_t *s)
{
uartDevice_t *uart = container_of(s, uartDevice_t, port);
IO_t txIO = IOGetByTag(uart->tx.pin);
if ((uart->txPinState == TX_PIN_MONITOR) && txIO) {
if (IORead(txIO)) {
// TX is high so we're good to transmit
// Enable USART TX output
uart->txPinState = TX_PIN_ACTIVE;
IOConfigGPIOAF(txIO, IOCFG_AF_PP, uart->hardware->af);
return true;
} else {
// TX line is pulled low so don't enable USART TX
return false;
}
}
return true;
}
void uartTxMonitor(uartPort_t *s)
{
uartDevice_t *uart = container_of(s, uartDevice_t, port);
IO_t txIO = IOGetByTag(uart->tx.pin);
// Switch TX to an input with pullup so it's state can be monitored
uart->txPinState = TX_PIN_MONITOR;
IOConfigGPIO(txIO, IOCFG_IPU);
}
static void handleUsartTxDma(uartPort_t *s)
{
uartDevice_t *uart = container_of(s, uartDevice_t, port);
uartTryStartTxDMA(s);
if (s->txDMAEmpty && (uart->txPinState != TX_PIN_IGNORE)) {
// Switch TX to an input with pullup so it's state can be monitored
uartTxMonitor(s);
}
}
void uartDmaIrqHandler(dmaChannelDescriptor_t* descriptor)
@ -268,6 +308,8 @@ uartPort_t *serialUART(UARTDevice_e device, uint32_t baudRate, portMode_e mode,
s->USARTx = hardware->reg;
s->checkUsartTxOutput = checkUsartTxOutput;
#ifdef USE_DMA
uartConfigureDma(uart);
#endif
@ -279,13 +321,22 @@ uartPort_t *serialUART(UARTDevice_e device, uint32_t baudRate, portMode_e mode,
RCC_ClockCmd(hardware->rcc, ENABLE);
}
uart->txPinState = TX_PIN_IGNORE;
if (options & SERIAL_BIDIR) {
IOInit(txIO, OWNER_SERIAL_TX, RESOURCE_INDEX(device));
IOConfigGPIOAF(txIO, ((options & SERIAL_BIDIR_PP) || (options & SERIAL_BIDIR_PP_PD)) ? IOCFG_AF_PP : IOCFG_AF_OD, hardware->af);
} else {
if ((mode & MODE_TX) && txIO) {
IOInit(txIO, OWNER_SERIAL_TX, RESOURCE_INDEX(device));
IOConfigGPIOAF(txIO, IOCFG_AF_PP_UP, hardware->af);
if ((options & SERIAL_INVERTED) == SERIAL_NOT_INVERTED) {
uart->txPinState = TX_PIN_ACTIVE;
// Switch TX to an input with pullup so it's state can be monitored
uartTxMonitor(s);
} else {
IOConfigGPIOAF(txIO, IOCFG_AF_PP, hardware->af);
}
}
if ((mode & MODE_RX) && rxIO) {
@ -320,6 +371,14 @@ void uartIrqHandler(uartPort_t *s)
}
}
// Detect completion of transmission
if (USART_GetITStatus(s->USARTx, USART_IT_TC) == SET) {
// Switch TX to an input with pullup so it's state can be monitored
uartTxMonitor(s);
USART_ClearITPendingBit(s->USARTx, USART_IT_TC);
}
if (!s->txDMAResource && (USART_GetITStatus(s->USARTx, USART_IT_TXE) == SET)) {
if (s->port.txBufferTail != s->port.txBufferHead) {
USART_SendData(s->USARTx, s->port.txBuffer[s->port.txBufferTail]);

View file

@ -345,6 +345,8 @@ uartPort_t *serialUART(UARTDevice_e device, uint32_t baudRate, portMode_e mode,
s->port.rxBufferSize = hardware->rxBufferSize;
s->port.txBufferSize = hardware->txBufferSize;
s->checkUsartTxOutput = checkUsartTxOutput;
#ifdef USE_DMA
uartConfigureDma(uartdev);
#endif
@ -358,6 +360,8 @@ uartPort_t *serialUART(UARTDevice_e device, uint32_t baudRate, portMode_e mode,
IO_t txIO = IOGetByTag(uartdev->tx.pin);
IO_t rxIO = IOGetByTag(uartdev->rx.pin);
uartdev->txPinState = TX_PIN_IGNORE;
if ((options & SERIAL_BIDIR) && txIO) {
ioConfig_t ioCfg = IO_CONFIG(
((options & SERIAL_INVERTED) || (options & SERIAL_BIDIR_PP) || (options & SERIAL_BIDIR_PP_PD)) ? GPIO_MODE_AF_PP : GPIO_MODE_AF_OD,
@ -371,7 +375,14 @@ uartPort_t *serialUART(UARTDevice_e device, uint32_t baudRate, portMode_e mode,
else {
if ((mode & MODE_TX) && txIO) {
IOInit(txIO, OWNER_SERIAL_TX, RESOURCE_INDEX(device));
IOConfigGPIOAF(txIO, IOCFG_AF_PP, uartdev->tx.af);
if ((options & SERIAL_INVERTED) == SERIAL_NOT_INVERTED) {
uartdev->txPinState = TX_PIN_ACTIVE;
// Switch TX to an input with pullup so it's state can be monitored
uartTxMonitor(s);
} else {
IOConfigGPIOAF(txIO, IOCFG_AF_PP, uartdev->tx.af);
}
}
if ((mode & MODE_RX) && rxIO) {

View file

@ -279,6 +279,8 @@ uartPort_t *serialUART(UARTDevice_e device, uint32_t baudRate, portMode_e mode,
s->port.rxBufferSize = hardware->rxBufferSize;
s->port.txBufferSize = hardware->txBufferSize;
s->checkUsartTxOutput = checkUsartTxOutput;
#ifdef USE_DMA
uartConfigureDma(uartdev);
#endif
@ -292,6 +294,8 @@ uartPort_t *serialUART(UARTDevice_e device, uint32_t baudRate, portMode_e mode,
IO_t txIO = IOGetByTag(uartdev->tx.pin);
IO_t rxIO = IOGetByTag(uartdev->rx.pin);
uartdev->txPinState = TX_PIN_IGNORE;
if ((options & SERIAL_BIDIR) && txIO) {
ioConfig_t ioCfg = IO_CONFIG(
((options & SERIAL_INVERTED) || (options & SERIAL_BIDIR_PP) || (options & SERIAL_BIDIR_PP_PD)) ? GPIO_MODE_AF_PP : GPIO_MODE_AF_OD,
@ -304,7 +308,14 @@ uartPort_t *serialUART(UARTDevice_e device, uint32_t baudRate, portMode_e mode,
} else {
if ((mode & MODE_TX) && txIO) {
IOInit(txIO, OWNER_SERIAL_TX, RESOURCE_INDEX(device));
IOConfigGPIOAF(txIO, IOCFG_AF_PP, uartdev->tx.af);
if ((options & SERIAL_INVERTED) == SERIAL_NOT_INVERTED) {
uartdev->txPinState = TX_PIN_ACTIVE;
// Switch TX to an input with pullup so it's state can be monitored
uartTxMonitor(s);
} else {
IOConfigGPIOAF(txIO, IOCFG_AF_PP, uartdev->tx.af);
}
}
if ((mode & MODE_RX) && rxIO) {

View file

@ -456,6 +456,8 @@ uartPort_t *serialUART(UARTDevice_e device, uint32_t baudRate, portMode_e mode,
s->port.rxBufferSize = hardware->rxBufferSize;
s->port.txBufferSize = hardware->txBufferSize;
s->checkUsartTxOutput = checkUsartTxOutput;
#ifdef USE_DMA
uartConfigureDma(uartdev);
#endif
@ -469,6 +471,8 @@ uartPort_t *serialUART(UARTDevice_e device, uint32_t baudRate, portMode_e mode,
IO_t txIO = IOGetByTag(uartdev->tx.pin);
IO_t rxIO = IOGetByTag(uartdev->rx.pin);
uartdev->txPinState = TX_PIN_IGNORE;
if ((options & SERIAL_BIDIR) && txIO) {
ioConfig_t ioCfg = IO_CONFIG(
((options & SERIAL_INVERTED) || (options & SERIAL_BIDIR_PP) || (options & SERIAL_BIDIR_PP_PD)) ? GPIO_MODE_AF_PP : GPIO_MODE_AF_OD,
@ -481,7 +485,14 @@ uartPort_t *serialUART(UARTDevice_e device, uint32_t baudRate, portMode_e mode,
} else {
if ((mode & MODE_TX) && txIO) {
IOInit(txIO, OWNER_SERIAL_TX, RESOURCE_INDEX(device));
IOConfigGPIOAF(txIO, IOCFG_AF_PP, uartdev->tx.af);
if ((options & SERIAL_INVERTED) == SERIAL_NOT_INVERTED) {
uartdev->txPinState = TX_PIN_ACTIVE;
// Switch TX to an input with pullup so it's state can be monitored
uartTxMonitor(s);
} else {
IOConfigGPIOAF(txIO, IOCFG_AF_PP, uartdev->tx.af);
}
}
if ((mode & MODE_RX) && rxIO) {