2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
22 * Initialization part of serial_uart.c
27 * jflyper - Refactoring, cleanup and made pin-configurable
28 * Dominic Clifton - Serial port abstraction, Separation of common STM32 code for cleanflight, various cleanups.
29 * Hamasaki/Timecop - Initial baseflight code
39 #include "build/build_config.h"
40 #include "build/atomic.h"
42 #include "common/utils.h"
43 #include "drivers/inverter.h"
44 #include "drivers/nvic.h"
45 #include "drivers/rcc.h"
47 #include "drivers/serial.h"
48 #include "drivers/serial_uart.h"
49 #include "drivers/serial_uart_impl.h"
51 static void usartConfigurePinInversion(uartPort_t
*uartPort
) {
52 #if !defined(USE_INVERTER) && !defined(STM32F303xC)
55 bool inverted
= uartPort
->port
.options
& SERIAL_INVERTED
;
59 // Enable hardware inverter if available.
60 enableInverter(uartPort
->USARTx
, true);
65 uint32_t inversionPins
= 0;
67 if (uartPort
->port
.mode
& MODE_TX
) {
68 inversionPins
|= USART_InvPin_Tx
;
70 if (uartPort
->port
.mode
& MODE_RX
) {
71 inversionPins
|= USART_InvPin_Rx
;
74 USART_InvPinCmd(uartPort
->USARTx
, inversionPins
, inverted
? ENABLE
: DISABLE
);
79 void uartReconfigure(uartPort_t
*uartPort
)
81 USART_InitTypeDef USART_InitStructure
;
82 USART_Cmd(uartPort
->USARTx
, DISABLE
);
84 USART_InitStructure
.USART_BaudRate
= uartPort
->port
.baudRate
;
86 // according to the stm32 documentation wordlen has to be 9 for parity bits
87 // this does not seem to matter for rx but will give bad data on tx!
88 // This seems to cause RX to break on STM32F1, see https://github.com/betaflight/betaflight/pull/1654
93 (uartPort
->port
.options
& SERIAL_PARITY_EVEN
)) {
94 USART_InitStructure
.USART_WordLength
= USART_WordLength_9b
;
96 USART_InitStructure
.USART_WordLength
= USART_WordLength_8b
;
99 USART_InitStructure
.USART_StopBits
= (uartPort
->port
.options
& SERIAL_STOPBITS_2
) ? USART_StopBits_2
: USART_StopBits_1
;
100 USART_InitStructure
.USART_Parity
= (uartPort
->port
.options
& SERIAL_PARITY_EVEN
) ? USART_Parity_Even
: USART_Parity_No
;
102 USART_InitStructure
.USART_HardwareFlowControl
= USART_HardwareFlowControl_None
;
103 USART_InitStructure
.USART_Mode
= 0;
104 if (uartPort
->port
.mode
& MODE_RX
)
105 USART_InitStructure
.USART_Mode
|= USART_Mode_Rx
;
106 if (uartPort
->port
.mode
& MODE_TX
)
107 USART_InitStructure
.USART_Mode
|= USART_Mode_Tx
;
109 USART_Init(uartPort
->USARTx
, &USART_InitStructure
);
111 usartConfigurePinInversion(uartPort
);
113 if (uartPort
->port
.options
& SERIAL_BIDIR
)
114 USART_HalfDuplexCmd(uartPort
->USARTx
, ENABLE
);
116 USART_HalfDuplexCmd(uartPort
->USARTx
, DISABLE
);
118 USART_Cmd(uartPort
->USARTx
, ENABLE
);
120 // Receive DMA or IRQ
121 DMA_InitTypeDef DMA_InitStructure
;
122 if (uartPort
->port
.mode
& MODE_RX
) {
123 if (uartPort
->rxDMAResource
) {
124 DMA_StructInit(&DMA_InitStructure
);
125 DMA_InitStructure
.DMA_Mode
= DMA_Mode_Circular
;
126 DMA_InitStructure
.DMA_PeripheralBaseAddr
= uartPort
->rxDMAPeripheralBaseAddr
;
127 DMA_InitStructure
.DMA_Priority
= DMA_Priority_Medium
;
128 DMA_InitStructure
.DMA_PeripheralInc
= DMA_PeripheralInc_Disable
;
129 DMA_InitStructure
.DMA_PeripheralDataSize
= DMA_PeripheralDataSize_Byte
;
130 DMA_InitStructure
.DMA_MemoryInc
= DMA_MemoryInc_Enable
;
131 DMA_InitStructure
.DMA_MemoryDataSize
= DMA_MemoryDataSize_Byte
;
132 DMA_InitStructure
.DMA_BufferSize
= uartPort
->port
.rxBufferSize
;
134 DMA_InitStructure
.DMA_FIFOMode
= DMA_FIFOMode_Disable
;
135 DMA_InitStructure
.DMA_FIFOThreshold
= DMA_FIFOThreshold_1QuarterFull
;
136 DMA_InitStructure
.DMA_MemoryBurst
= DMA_MemoryBurst_Single
;
137 DMA_InitStructure
.DMA_PeripheralBurst
= DMA_PeripheralBurst_Single
;
139 DMA_InitStructure
.DMA_Channel
= uartPort
->rxDMAChannel
;
140 DMA_InitStructure
.DMA_DIR
= DMA_DIR_PeripheralToMemory
;
141 DMA_InitStructure
.DMA_Memory0BaseAddr
= (uint32_t)uartPort
->port
.rxBuffer
;
143 DMA_InitStructure
.DMA_M2M
= DMA_M2M_Disable
;
144 DMA_InitStructure
.DMA_DIR
= DMA_DIR_PeripheralSRC
;
145 DMA_InitStructure
.DMA_MemoryBaseAddr
= (uint32_t)uartPort
->port
.rxBuffer
;
148 xDMA_DeInit(uartPort
->rxDMAResource
);
149 xDMA_Init(uartPort
->rxDMAResource
, &DMA_InitStructure
);
150 xDMA_Cmd(uartPort
->rxDMAResource
, ENABLE
);
151 USART_DMACmd(uartPort
->USARTx
, USART_DMAReq_Rx
, ENABLE
);
152 uartPort
->rxDMAPos
= xDMA_GetCurrDataCounter(uartPort
->rxDMAResource
);
154 USART_ClearITPendingBit(uartPort
->USARTx
, USART_IT_RXNE
);
155 USART_ITConfig(uartPort
->USARTx
, USART_IT_RXNE
, ENABLE
);
156 USART_ITConfig(uartPort
->USARTx
, USART_IT_IDLE
, ENABLE
);
160 // Transmit DMA or IRQ
161 if (uartPort
->port
.mode
& MODE_TX
) {
162 if (uartPort
->txDMAResource
) {
163 DMA_StructInit(&DMA_InitStructure
);
164 DMA_InitStructure
.DMA_Mode
= DMA_Mode_Normal
;
165 DMA_InitStructure
.DMA_PeripheralBaseAddr
= uartPort
->txDMAPeripheralBaseAddr
;
166 DMA_InitStructure
.DMA_Priority
= DMA_Priority_Medium
;
167 DMA_InitStructure
.DMA_PeripheralInc
= DMA_PeripheralInc_Disable
;
168 DMA_InitStructure
.DMA_PeripheralDataSize
= DMA_PeripheralDataSize_Byte
;
169 DMA_InitStructure
.DMA_MemoryInc
= DMA_MemoryInc_Enable
;
170 DMA_InitStructure
.DMA_MemoryDataSize
= DMA_MemoryDataSize_Byte
;
171 DMA_InitStructure
.DMA_BufferSize
= uartPort
->port
.txBufferSize
;
174 DMA_InitStructure
.DMA_FIFOMode
= DMA_FIFOMode_Disable
;
175 DMA_InitStructure
.DMA_FIFOThreshold
= DMA_FIFOThreshold_1QuarterFull
;
176 DMA_InitStructure
.DMA_MemoryBurst
= DMA_MemoryBurst_Single
;
177 DMA_InitStructure
.DMA_PeripheralBurst
= DMA_PeripheralBurst_Single
;
179 DMA_InitStructure
.DMA_Channel
= uartPort
->txDMAChannel
;
180 DMA_InitStructure
.DMA_DIR
= DMA_DIR_MemoryToPeripheral
;
182 DMA_InitStructure
.DMA_M2M
= DMA_M2M_Disable
;
183 DMA_InitStructure
.DMA_DIR
= DMA_DIR_PeripheralDST
;
186 xDMA_DeInit(uartPort
->txDMAResource
);
187 xDMA_Init(uartPort
->txDMAResource
, &DMA_InitStructure
);
190 xDMA_ITConfig(uartPort
->txDMAResource
, DMA_IT_TC
| DMA_IT_FE
| DMA_IT_TE
| DMA_IT_DME
, ENABLE
);
192 xDMA_ITConfig(uartPort
->txDMAResource
, DMA_IT_TC
, ENABLE
);
195 xDMA_SetCurrDataCounter(uartPort
->txDMAResource
, 0);
196 USART_DMACmd(uartPort
->USARTx
, USART_DMAReq_Tx
, ENABLE
);
198 USART_ITConfig(uartPort
->USARTx
, USART_IT_TXE
, ENABLE
);
202 USART_Cmd(uartPort
->USARTx
, ENABLE
);
206 void uartTryStartTxDMA(uartPort_t
*s
)
208 // uartTryStartTxDMA must be protected, since it is called from
209 // uartWrite and handleUsartTxDma (an ISR).
211 ATOMIC_BLOCK(NVIC_PRIO_SERIALUART_TXDMA
) {
212 if (IS_DMA_ENABLED(s
->txDMAResource
)) {
213 // DMA is already in progress
217 // For F4 (and F1), there are cases that NDTR (CNDTR for F1) is non-zero upon TC interrupt.
218 // We couldn't find out the root cause, so mask the case here.
219 // F3 is not confirmed to be vulnerable, but not excluded as a safety.
221 if (xDMA_GetCurrDataCounter(s
->txDMAResource
)) {
222 // Possible premature TC case.
226 if (s
->port
.txBufferHead
== s
->port
.txBufferTail
) {
227 // No more data to transmit.
228 s
->txDMAEmpty
= true;
232 // Start a new transaction.
235 xDMA_MemoryTargetConfig(s
->txDMAResource
, (uint32_t)&s
->port
.txBuffer
[s
->port
.txBufferTail
], DMA_Memory_0
);
237 DMAx_SetMemoryAddress(s
->txDMAResource
, (uint32_t)&s
->port
.txBuffer
[s
->port
.txBufferTail
]);
240 if (s
->port
.txBufferHead
> s
->port
.txBufferTail
) {
241 xDMA_SetCurrDataCounter(s
->txDMAResource
, s
->port
.txBufferHead
- s
->port
.txBufferTail
);
242 s
->port
.txBufferTail
= s
->port
.txBufferHead
;
244 xDMA_SetCurrDataCounter(s
->txDMAResource
, s
->port
.txBufferSize
- s
->port
.txBufferTail
);
245 s
->port
.txBufferTail
= 0;
247 s
->txDMAEmpty
= false;
250 xDMA_Cmd(s
->txDMAResource
, ENABLE
);