2 * This file is part of Betaflight.
4 * Betaflight is free software. You can redistribute this software
5 * and/or modify this software under the terms of the GNU General
6 * Public License as published by the Free Software Foundation,
7 * either version 3 of the License, or (at your option) any later
10 * Betaflight is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this software.
19 * If not, see <http://www.gnu.org/licenses/>.
23 * Common UART hardware functions
31 #if defined(USE_UART) && !defined(SIMULATOR_BUILD)
33 #include "build/build_config.h"
35 #include "drivers/nvic.h"
36 #include "drivers/rcc.h"
37 #include "drivers/inverter.h"
38 #include "drivers/serial.h"
39 #include "drivers/serial_impl.h"
40 #include "drivers/serial_uart.h"
41 #include "drivers/serial_uart_impl.h"
43 // TODO: split this function into mcu-specific UART files ?
44 static void enableRxIrq(const uartHardware_t
*hardware
)
46 #if defined(USE_HAL_DRIVER)
47 HAL_NVIC_SetPriority(hardware
->irqn
, NVIC_PRIORITY_BASE(hardware
->rxPriority
), NVIC_PRIORITY_SUB(hardware
->rxPriority
));
48 HAL_NVIC_EnableIRQ(hardware
->irqn
);
49 #elif defined(STM32F4)
50 NVIC_InitTypeDef NVIC_InitStructure
;
51 NVIC_InitStructure
.NVIC_IRQChannel
= hardware
->irqn
;
52 NVIC_InitStructure
.NVIC_IRQChannelPreemptionPriority
= NVIC_PRIORITY_BASE(hardware
->rxPriority
);
53 NVIC_InitStructure
.NVIC_IRQChannelSubPriority
= NVIC_PRIORITY_SUB(hardware
->rxPriority
);
54 NVIC_InitStructure
.NVIC_IRQChannelCmd
= ENABLE
;
55 NVIC_Init(&NVIC_InitStructure
);
57 nvic_irq_enable(hardware
->irqn
, NVIC_PRIORITY_BASE(hardware
->rxPriority
), NVIC_PRIORITY_SUB(hardware
->rxPriority
));
58 #elif defined(APM32F4)
59 DAL_NVIC_SetPriority(hardware
->irqn
, NVIC_PRIORITY_BASE(hardware
->rxPriority
), NVIC_PRIORITY_SUB(hardware
->rxPriority
));
60 DAL_NVIC_EnableIRQ(hardware
->irqn
);
62 # error "Unhandled MCU type"
66 uartPort_t
*serialUART(uartDevice_t
*uartdev
, uint32_t baudRate
, portMode_e mode
, portOptions_e options
)
68 uartPort_t
*s
= &uartdev
->port
;
70 const uartHardware_t
*hardware
= uartdev
->hardware
;
72 s
->port
.vTable
= uartVTable
;
74 s
->port
.baudRate
= baudRate
;
76 s
->port
.rxBuffer
= hardware
->rxBuffer
;
77 s
->port
.txBuffer
= hardware
->txBuffer
;
78 s
->port
.rxBufferSize
= hardware
->rxBufferSize
;
79 s
->port
.txBufferSize
= hardware
->txBufferSize
;
81 s
->USARTx
= hardware
->reg
;
84 s
->Handle
.Instance
= hardware
->reg
;
87 s
->checkUsartTxOutput
= checkUsartTxOutput
;
90 RCC_ClockCmd(hardware
->rcc
, ENABLE
);
94 uartConfigureDma(uartdev
);
98 IO_t txIO
= IOGetByTag(uartdev
->tx
.pin
);
99 IO_t rxIO
= IOGetByTag(uartdev
->rx
.pin
);
101 uartdev
->txPinState
= TX_PIN_IGNORE
;
103 const serialPortIdentifier_e identifier
= s
->port
.identifier
;
105 const int ownerIndex
= serialOwnerIndex(identifier
);
106 const resourceOwner_e ownerTxRx
= serialOwnerTxRx(identifier
); // rx is always +1
109 #if UART_TRAIT_AF_PORT
110 uint8_t rxAf
= hardware
->af
;
111 uint8_t txAf
= hardware
->af
;
112 #elif UART_TRAIT_AF_PIN
113 uint8_t rxAf
= uartdev
->rx
.af
;
114 uint8_t txAf
= uartdev
->tx
.af
;
117 // Note: F4 did not check txIO before refactoring
118 if ((options
& SERIAL_BIDIR
) && txIO
) {
119 // pushPull / openDrain
120 const bool pushPull
= serialOptions_pushPull(options
);
122 const serialPullMode_t pull
= serialOptions_pull(options
);
123 #if defined(STM32F7) || defined(STM32H7) || defined(STM32G4) || defined(APM32F4)
124 // Note: APM32F4 is different from STM32F4 here
125 const ioConfig_t ioCfg
= IO_CONFIG(
126 pushPull
? GPIO_MODE_AF_PP
: GPIO_MODE_AF_OD
,
127 GPIO_SPEED_FREQ_HIGH
,
128 ((const unsigned[]){GPIO_NOPULL
, GPIO_PULLDOWN
, GPIO_PULLUP
})[pull
]
130 #elif defined(AT32F4)
131 const ioConfig_t ioCfg
= IO_CONFIG(
133 GPIO_DRIVE_STRENGTH_STRONGER
,
134 pushPull
? GPIO_OUTPUT_PUSH_PULL
: GPIO_OUTPUT_OPEN_DRAIN
,
135 ((const gpio_pull_type
[]){GPIO_PULL_NONE
, GPIO_PULL_DOWN
, GPIO_PULL_UP
})[pull
]
137 #elif defined(STM32F4)
138 // UART inverter is not supproted on F4, but keep it in line with other CPUs
139 // External inverter in bidir mode would be quite problematic anyway
140 const ioConfig_t ioCfg
= IO_CONFIG(
142 GPIO_Low_Speed
, // TODO: should use stronger drive
143 pushPull
? GPIO_OType_PP
: GPIO_OType_OD
,
144 ((const unsigned[]){GPIO_PuPd_NOPULL
, GPIO_PuPd_DOWN
, GPIO_PuPd_UP
})[pull
]
147 IOInit(txIO
, ownerTxRx
, ownerIndex
);
148 IOConfigGPIOAF(txIO
, ioCfg
, txAf
);
150 if ((mode
& MODE_TX
) && txIO
) {
151 IOInit(txIO
, ownerTxRx
, ownerIndex
);
153 if (options
& SERIAL_CHECK_TX
) {
155 // see https://github.com/betaflight/betaflight/pull/13021
156 // Allow for F7 UART idle preamble to be sent on startup
157 uartdev
->txPinState
= TX_PIN_MONITOR
;
158 // Switch TX to UART output whilst UART sends idle preamble
159 checkUsartTxOutput(s
);
161 uartdev
->txPinState
= TX_PIN_ACTIVE
;
162 // Switch TX to an input with pullup so it's state can be monitored
166 #if defined(STM32F4) || defined(APM32F4)
167 // TODO: no need for pullup on TX only pin
168 const ioConfig_t ioCfg
= IOCFG_AF_PP_UP
;
170 const ioConfig_t ioCfg
= IOCFG_AF_PP
;
172 IOConfigGPIOAF(txIO
, ioCfg
, txAf
);
176 if ((mode
& MODE_RX
) && rxIO
) {
177 #if defined(STM32F4) || defined(APM32F4)
178 // no inversion possible on F4, always use pullup
179 const ioConfig_t ioCfg
= IOCFG_AF_PP_UP
;
181 // TODO: pullup/pulldown should be enabled for RX (based on inversion)
182 const ioConfig_t ioCfg
= IOCFG_AF_PP
;
184 IOInit(rxIO
, ownerTxRx
+ 1, ownerIndex
);
185 IOConfigGPIOAF(rxIO
, ioCfg
, rxAf
);
191 && !s
->rxDMAResource
// do not enable IRW if using rxDMA
194 enableRxIrq(hardware
);
199 // called from platform-specific uartReconfigure
200 void uartConfigureExternalPinInversion(uartPort_t
*uartPort
)
202 #if !defined(USE_INVERTER)
205 const bool inverted
= uartPort
->port
.options
& SERIAL_INVERTED
;
206 enableInverter(uartPort
->port
.identifier
, inverted
);
210 #endif /* USE_UART */