Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[4.4.2] Tri-state USART TX output if load due to powered down peripheral is d… #12782

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/main/drivers/serial_uart.c
Expand Up @@ -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) {
Expand Down
2 changes: 2 additions & 0 deletions src/main/drivers/serial_uart.h
Expand Up @@ -72,6 +72,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);
Expand Down
86 changes: 63 additions & 23 deletions src/main/drivers/serial_uart_hal.c
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -343,41 +385,39 @@ 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
!s->txDMAResource &&
#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)) {
Expand Down
10 changes: 10 additions & 0 deletions src/main/drivers/serial_uart_impl.h
Expand Up @@ -197,6 +197,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;
Expand All @@ -207,6 +213,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[];
Expand All @@ -225,6 +232,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)
Expand Down
2 changes: 2 additions & 0 deletions src/main/drivers/serial_uart_stdperiph.c
Expand Up @@ -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);
Expand Down
61 changes: 60 additions & 1 deletion src/main/drivers/serial_uart_stm32f4xx.c
Expand Up @@ -26,6 +26,7 @@
#include <stdint.h>

#include "platform.h"
#include "build/debug.h"

#ifdef USE_UART

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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]);
Expand Down
13 changes: 12 additions & 1 deletion src/main/drivers/serial_uart_stm32f7xx.c
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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) {
Expand Down
13 changes: 12 additions & 1 deletion src/main/drivers/serial_uart_stm32g4xx.c
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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) {
Expand Down
13 changes: 12 additions & 1 deletion src/main/drivers/serial_uart_stm32h7xx.c
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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) {
Expand Down