mirror of
https://github.com/betaflight/betaflight.git
synced 2025-07-16 12:55:19 +03:00
310 lines
10 KiB
C
310 lines
10 KiB
C
/*
|
|
* This file is part of Betaflight.
|
|
*
|
|
* Betaflight is free software. You can redistribute this software
|
|
* and/or modify this software under the terms of the GNU General
|
|
* Public License as published by the Free Software Foundation,
|
|
* either version 3 of the License, or (at your option) any later
|
|
* version.
|
|
*
|
|
* Betaflight is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
*
|
|
* See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public
|
|
* License along with this software.
|
|
*
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* Initialization part of serial_uart.c using at32 bsp driver
|
|
*
|
|
* Authors:
|
|
* emsr ports the code to at32f435/7
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
|
|
#include "platform.h"
|
|
|
|
#include "build/debug.h"
|
|
|
|
#ifdef USE_UART
|
|
|
|
#include "build/build_config.h"
|
|
#include "build/atomic.h"
|
|
|
|
#include "common/utils.h"
|
|
#include "drivers/inverter.h"
|
|
#include "drivers/nvic.h"
|
|
#include "drivers/rcc.h"
|
|
|
|
#include "drivers/dma.h"
|
|
|
|
#include "drivers/serial.h"
|
|
#include "drivers/serial_uart.h"
|
|
#include "drivers/serial_uart_impl.h"
|
|
|
|
static void uartConfigurePinSwap(uartPort_t *uartPort)
|
|
{
|
|
uartDevice_t *uartDevice = container_of(uartPort, uartDevice_t, port);
|
|
if (uartDevice->pinSwap) {
|
|
usart_transmit_receive_pin_swap(uartDevice->port.USARTx, TRUE);
|
|
}
|
|
}
|
|
|
|
void uartReconfigure(uartPort_t *uartPort)
|
|
{
|
|
usart_enable(uartPort->USARTx, FALSE);
|
|
//init
|
|
usart_init(uartPort->USARTx,
|
|
uartPort->port.baudRate,
|
|
USART_DATA_8BITS,
|
|
(uartPort->port.options & SERIAL_STOPBITS_2) ? USART_STOP_2_BIT : USART_STOP_1_BIT);
|
|
|
|
//set parity
|
|
usart_parity_selection_config(uartPort->USARTx,
|
|
(uartPort->port.options & SERIAL_PARITY_EVEN) ? USART_PARITY_EVEN : USART_PARITY_NONE);
|
|
|
|
//set hardware control
|
|
usart_hardware_flow_control_set(uartPort->USARTx, USART_HARDWARE_FLOW_NONE);
|
|
|
|
//set mode rx or tx
|
|
if (uartPort->port.mode & MODE_RX) {
|
|
usart_receiver_enable(uartPort->USARTx, TRUE);
|
|
}
|
|
|
|
if (uartPort->port.mode & MODE_TX) {
|
|
usart_transmitter_enable(uartPort->USARTx, TRUE);
|
|
}
|
|
|
|
// config external pin inverter (no internal pin inversion available)
|
|
uartConfigureExternalPinInversion(uartPort);
|
|
|
|
// config pin swap
|
|
uartConfigurePinSwap(uartPort);
|
|
|
|
if (uartPort->port.options & SERIAL_BIDIR) {
|
|
usart_single_line_halfduplex_select(uartPort->USARTx, TRUE);
|
|
} else {
|
|
usart_single_line_halfduplex_select(uartPort->USARTx, FALSE);
|
|
}
|
|
//enable usart
|
|
usart_enable(uartPort->USARTx, TRUE);
|
|
|
|
// Receive DMA or IRQ
|
|
dma_init_type DMA_InitStructure;
|
|
if (uartPort->port.mode & MODE_RX) {
|
|
if (uartPort->rxDMAResource) {
|
|
|
|
dma_default_para_init(&DMA_InitStructure);
|
|
DMA_InitStructure.loop_mode_enable = TRUE;
|
|
DMA_InitStructure.peripheral_base_addr = uartPort->rxDMAPeripheralBaseAddr;
|
|
DMA_InitStructure.priority = DMA_PRIORITY_MEDIUM;
|
|
DMA_InitStructure.peripheral_inc_enable = FALSE;
|
|
DMA_InitStructure.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
|
|
DMA_InitStructure.memory_inc_enable = TRUE;
|
|
DMA_InitStructure.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;
|
|
DMA_InitStructure.memory_base_addr = (uint32_t)uartPort->port.rxBuffer;
|
|
DMA_InitStructure.buffer_size = uartPort->port.rxBufferSize;
|
|
DMA_InitStructure.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;
|
|
|
|
xDMA_DeInit(uartPort->rxDMAResource);
|
|
xDMA_Init(uartPort->rxDMAResource, &DMA_InitStructure);
|
|
xDMA_Cmd(uartPort->rxDMAResource, TRUE);
|
|
usart_dma_receiver_enable(uartPort->USARTx,TRUE);
|
|
uartPort->rxDMAPos = xDMA_GetCurrDataCounter(uartPort->rxDMAResource);
|
|
} else {
|
|
usart_flag_clear(uartPort->USARTx, USART_RDBF_FLAG);
|
|
usart_interrupt_enable(uartPort->USARTx, USART_RDBF_INT, TRUE);
|
|
usart_interrupt_enable(uartPort->USARTx, USART_IDLE_INT, TRUE);
|
|
}
|
|
}
|
|
|
|
// Transmit DMA or IRQ
|
|
if (uartPort->port.mode & MODE_TX) {
|
|
if (uartPort->txDMAResource) {
|
|
dma_default_para_init(&DMA_InitStructure);
|
|
DMA_InitStructure.loop_mode_enable = FALSE;
|
|
DMA_InitStructure.peripheral_base_addr = uartPort->txDMAPeripheralBaseAddr;
|
|
DMA_InitStructure.priority = DMA_PRIORITY_MEDIUM;
|
|
DMA_InitStructure.peripheral_inc_enable = FALSE;
|
|
DMA_InitStructure.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
|
|
DMA_InitStructure.memory_inc_enable = TRUE;
|
|
DMA_InitStructure.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;
|
|
DMA_InitStructure.memory_base_addr = (uint32_t)uartPort->port.txBuffer;
|
|
DMA_InitStructure.buffer_size = uartPort->port.txBufferSize;
|
|
DMA_InitStructure.direction = DMA_DIR_MEMORY_TO_PERIPHERAL;
|
|
|
|
xDMA_DeInit(uartPort->txDMAResource);
|
|
xDMA_Init(uartPort->txDMAResource, &DMA_InitStructure);
|
|
xDMA_ITConfig(uartPort->txDMAResource, DMA_IT_TCIF, TRUE);
|
|
xDMA_SetCurrDataCounter(uartPort->txDMAResource, 0);
|
|
usart_dma_transmitter_enable(uartPort->USARTx, TRUE);
|
|
|
|
} else {
|
|
usart_interrupt_enable(uartPort->USARTx, USART_TDBE_INT, TRUE);
|
|
}
|
|
usart_interrupt_enable(uartPort->USARTx, USART_TDC_INT, TRUE);
|
|
}
|
|
// TODO: usart_enable is called twice
|
|
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->tx.af);
|
|
|
|
// Enable the UART transmitter
|
|
usart_transmitter_enable(s->USARTx, true);
|
|
|
|
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);
|
|
|
|
if (uart->txPinState == TX_PIN_ACTIVE) {
|
|
IO_t txIO = IOGetByTag(uart->tx.pin);
|
|
|
|
// Disable the UART transmitter
|
|
usart_transmitter_enable(s->USARTx, false);
|
|
|
|
// 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)
|
|
{
|
|
// uartTryStartTxDMA must be protected, since it is called from
|
|
// uartWrite and handleUsartTxDma (an ISR).
|
|
|
|
ATOMIC_BLOCK(NVIC_PRIO_SERIALUART_TXDMA) {
|
|
if (IS_DMA_ENABLED(s->txDMAResource)) {
|
|
// DMA is already in progress
|
|
return;
|
|
}
|
|
|
|
// For F4, there are cases that NDTR is non-zero upon TC interrupt.
|
|
// We couldn't find out the root cause, so mask the case here.
|
|
if (xDMA_GetCurrDataCounter(s->txDMAResource)) {
|
|
// Possible premature TC case.
|
|
goto reenable;
|
|
}
|
|
|
|
if (s->port.txBufferHead == s->port.txBufferTail) {
|
|
// No more data to transmit.
|
|
s->txDMAEmpty = true;
|
|
return;
|
|
}
|
|
|
|
// Start a new transaction.
|
|
((DMA_ARCH_TYPE*)s->txDMAResource) -> maddr =(uint32_t)&s->port.txBuffer[s->port.txBufferTail];
|
|
|
|
if (s->port.txBufferHead > s->port.txBufferTail) {
|
|
xDMA_SetCurrDataCounter(s->txDMAResource, s->port.txBufferHead - s->port.txBufferTail);
|
|
s->port.txBufferTail = s->port.txBufferHead;
|
|
} else {
|
|
xDMA_SetCurrDataCounter(s->txDMAResource, s->port.txBufferSize - s->port.txBufferTail);
|
|
s->port.txBufferTail = 0;
|
|
}
|
|
s->txDMAEmpty = false;
|
|
|
|
reenable:
|
|
xDMA_Cmd(s->txDMAResource, TRUE);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void handleUsartTxDma(uartPort_t *s)
|
|
{
|
|
uartTryStartTxDMA(s);
|
|
|
|
if (s->txDMAEmpty) {
|
|
// Switch TX to an input with pullup so it's state can be monitored
|
|
uartTxMonitor(s);
|
|
}
|
|
}
|
|
|
|
void uartDmaIrqHandler(dmaChannelDescriptor_t* descriptor)
|
|
{
|
|
uartPort_t *s = &(((uartDevice_t*)(descriptor->userParam))->port);
|
|
if (DMA_GET_FLAG_STATUS(descriptor, DMA_IT_TCIF))
|
|
{
|
|
DMA_CLEAR_FLAG(descriptor, DMA_IT_TCIF);
|
|
DMA_CLEAR_FLAG(descriptor, DMA_IT_HTIF);
|
|
handleUsartTxDma(s);
|
|
}
|
|
|
|
if (DMA_GET_FLAG_STATUS(descriptor, DMA_IT_TEIF))
|
|
{
|
|
DMA_CLEAR_FLAG(descriptor, DMA_IT_TEIF);
|
|
}
|
|
}
|
|
|
|
void uartIrqHandler(uartPort_t *s)
|
|
{
|
|
if (!s->rxDMAResource && (usart_flag_get(s->USARTx, USART_RDBF_FLAG) == SET)) {
|
|
if (s->port.rxCallback) {
|
|
s->port.rxCallback(s->USARTx->dt, s->port.rxCallbackData);
|
|
} else {
|
|
s->port.rxBuffer[s->port.rxBufferHead] = s->USARTx->dt;
|
|
s->port.rxBufferHead = (s->port.rxBufferHead + 1) % s->port.rxBufferSize;
|
|
}
|
|
}
|
|
|
|
// 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]);
|
|
s->port.txBufferTail = (s->port.txBufferTail + 1) % s->port.txBufferSize;
|
|
} else {
|
|
usart_interrupt_enable(s->USARTx, USART_TDBE_INT, FALSE);
|
|
}
|
|
}
|
|
|
|
if (usart_flag_get(s->USARTx, USART_ROERR_FLAG) == SET) {
|
|
usart_flag_clear(s->USARTx, USART_ROERR_FLAG);
|
|
}
|
|
|
|
if (usart_flag_get(s->USARTx, USART_IDLEF_FLAG) == SET) {
|
|
if (s->port.idleCallback) {
|
|
s->port.idleCallback();
|
|
}
|
|
|
|
(void) s->USARTx->sts;
|
|
(void) s->USARTx->dt;
|
|
}
|
|
}
|
|
#endif // USE_UART
|