Merge pull request #11198 from SteveCEvans/sce_rc2
[betaflight.git] / src / main / drivers / serial_uart_stdperiph.c
blobb74ddcc1e097e9b7a7e76a5ce66a30659cdc0c5a
1 /*
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)
8 * any later version.
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
26 * Authors:
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
32 #include <stdbool.h>
33 #include <stdint.h>
35 #include "platform.h"
37 #ifdef USE_UART
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)
53 UNUSED(uartPort);
54 #else
55 bool inverted = uartPort->port.options & SERIAL_INVERTED;
57 #ifdef USE_INVERTER
58 if (inverted) {
59 // Enable hardware inverter if available.
60 enableInverter(uartPort->USARTx, true);
62 #endif
64 #ifdef STM32F303xC
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);
75 #endif
76 #endif
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
89 if (
90 #if defined(STM32F1)
91 false &&
92 #endif
93 (uartPort->port.options & SERIAL_PARITY_EVEN)) {
94 USART_InitStructure.USART_WordLength = USART_WordLength_9b;
95 } else {
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);
115 else
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;
133 #ifdef STM32F4
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;
142 #else
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;
146 #endif
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);
153 } else {
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;
173 #ifdef STM32F4
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;
181 #else
182 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
183 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
184 #endif
186 xDMA_DeInit(uartPort->txDMAResource);
187 xDMA_Init(uartPort->txDMAResource, &DMA_InitStructure);
189 #ifdef STM32F4
190 xDMA_ITConfig(uartPort->txDMAResource, DMA_IT_TC | DMA_IT_FE | DMA_IT_TE | DMA_IT_DME, ENABLE);
191 #else
192 xDMA_ITConfig(uartPort->txDMAResource, DMA_IT_TC, ENABLE);
193 #endif
195 xDMA_SetCurrDataCounter(uartPort->txDMAResource, 0);
196 USART_DMACmd(uartPort->USARTx, USART_DMAReq_Tx, ENABLE);
197 } else {
198 USART_ITConfig(uartPort->USARTx, USART_IT_TXE, ENABLE);
202 USART_Cmd(uartPort->USARTx, ENABLE);
205 #ifdef USE_DMA
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
214 return;
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.
223 goto reenable;
226 if (s->port.txBufferHead == s->port.txBufferTail) {
227 // No more data to transmit.
228 s->txDMAEmpty = true;
229 return;
232 // Start a new transaction.
234 #ifdef STM32F4
235 xDMA_MemoryTargetConfig(s->txDMAResource, (uint32_t)&s->port.txBuffer[s->port.txBufferTail], DMA_Memory_0);
236 #else
237 DMAx_SetMemoryAddress(s->txDMAResource, (uint32_t)&s->port.txBuffer[s->port.txBufferTail]);
238 #endif
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;
243 } else {
244 xDMA_SetCurrDataCounter(s->txDMAResource, s->port.txBufferSize - s->port.txBufferTail);
245 s->port.txBufferTail = 0;
247 s->txDMAEmpty = false;
249 reenable:
250 xDMA_Cmd(s->txDMAResource, ENABLE);
253 #endif
255 #endif // USE_UART