mirror of
https://github.com/betaflight/betaflight.git
synced 2025-07-13 03:20:00 +03:00
The STM32F3DISCOVERY board would crash when setting GPIOA Pin_13 or Pin_14 to analog mode so they are excluded in the gpio initialisation. The USART GPIO configuration did not work when using the F10x code. The USART status and DMA registers are different too.
389 lines
12 KiB
C
389 lines
12 KiB
C
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "platform.h"
|
|
|
|
#include "system_common.h"
|
|
#include "gpio_common.h"
|
|
|
|
#include "serial_common.h"
|
|
#include "serial_uart.h"
|
|
|
|
/*
|
|
DMA UART routines idea lifted from AutoQuad
|
|
Copyright © 2011 Bill Nesbitt
|
|
*/
|
|
|
|
static uartPort_t uartPort1;
|
|
static uartPort_t uartPort2;
|
|
|
|
// USART1 - Telemetry (RX/TX by DMA)
|
|
uartPort_t *serialUSART1(uint32_t baudRate, portMode_t mode)
|
|
{
|
|
uartPort_t *s;
|
|
static volatile uint8_t rx1Buffer[UART1_RX_BUFFER_SIZE];
|
|
static volatile uint8_t tx1Buffer[UART1_TX_BUFFER_SIZE];
|
|
NVIC_InitTypeDef NVIC_InitStructure;
|
|
|
|
s = &uartPort1;
|
|
s->port.vTable = uartVTable;
|
|
|
|
s->port.baudRate = baudRate;
|
|
|
|
s->port.rxBuffer = rx1Buffer;
|
|
s->port.txBuffer = tx1Buffer;
|
|
s->port.rxBufferSize = UART1_RX_BUFFER_SIZE;
|
|
s->port.txBufferSize = UART1_TX_BUFFER_SIZE;
|
|
|
|
s->rxDMAChannel = DMA1_Channel5;
|
|
s->txDMAChannel = DMA1_Channel4;
|
|
|
|
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
|
|
|
|
// USART1_TX PA9
|
|
// USART1_RX PA10
|
|
|
|
#ifdef STM32F303xC
|
|
|
|
#define UART1_TX_PIN GPIO_Pin_9
|
|
#define UART1_RX_PIN GPIO_Pin_10
|
|
#define UART1_GPIO GPIOA
|
|
#define UART1_TX_PINSOURCE GPIO_PinSource9
|
|
#define UART1_RX_PINSOURCE GPIO_PinSource10
|
|
|
|
GPIO_InitTypeDef GPIO_InitStructure;
|
|
|
|
GPIO_InitStructure.GPIO_Pin = UART1_TX_PIN | UART1_RX_PIN;
|
|
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
|
|
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
|
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
|
|
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
|
|
|
|
GPIO_PinAFConfig(UART1_GPIO, UART1_TX_PINSOURCE, GPIO_AF_7);
|
|
GPIO_PinAFConfig(UART1_GPIO, UART1_RX_PINSOURCE, GPIO_AF_7);
|
|
|
|
GPIO_Init(UART1_GPIO, &GPIO_InitStructure);
|
|
#endif
|
|
|
|
#ifdef STM32F10X_MD
|
|
gpio_config_t gpio;
|
|
gpio.speed = Speed_2MHz;
|
|
gpio.pin = Pin_9;
|
|
gpio.mode = Mode_AF_PP;
|
|
if (mode & MODE_TX)
|
|
gpioInit(GPIOA, &gpio);
|
|
gpio.pin = Pin_10;
|
|
gpio.mode = Mode_IPU;
|
|
if (mode & MODE_RX)
|
|
gpioInit(GPIOA, &gpio);
|
|
#endif
|
|
|
|
// DMA TX Interrupt
|
|
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
|
|
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
|
|
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
|
|
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
|
|
NVIC_Init(&NVIC_InitStructure);
|
|
|
|
return s;
|
|
}
|
|
|
|
// USART2 - GPS or Spektrum or ?? (RX + TX by IRQ)
|
|
uartPort_t *serialUSART2(uint32_t baudRate, portMode_t mode)
|
|
{
|
|
uartPort_t *s;
|
|
static volatile uint8_t rx2Buffer[UART2_RX_BUFFER_SIZE];
|
|
static volatile uint8_t tx2Buffer[UART2_TX_BUFFER_SIZE];
|
|
gpio_config_t gpio;
|
|
NVIC_InitTypeDef NVIC_InitStructure;
|
|
|
|
s = &uartPort2;
|
|
s->port.vTable = uartVTable;
|
|
|
|
s->port.baudRate = baudRate;
|
|
|
|
s->port.rxBufferSize = UART2_RX_BUFFER_SIZE;
|
|
s->port.txBufferSize = UART2_TX_BUFFER_SIZE;
|
|
s->port.rxBuffer = rx2Buffer;
|
|
s->port.txBuffer = tx2Buffer;
|
|
|
|
s->USARTx = USART2;
|
|
|
|
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
|
|
// USART2_TX PA2
|
|
// USART2_RX PA3
|
|
gpio.speed = Speed_2MHz;
|
|
gpio.pin = Pin_2;
|
|
gpio.mode = Mode_AF_PP;
|
|
if (mode & MODE_TX)
|
|
gpioInit(GPIOA, &gpio);
|
|
gpio.pin = Pin_3;
|
|
gpio.mode = Mode_IPU;
|
|
if (mode & MODE_RX)
|
|
gpioInit(GPIOA, &gpio);
|
|
|
|
// RX/TX Interrupt
|
|
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
|
|
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
|
|
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
|
|
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
|
|
NVIC_Init(&NVIC_InitStructure);
|
|
|
|
return s;
|
|
}
|
|
|
|
serialPort_t *uartOpen(USART_TypeDef *USARTx, serialReceiveCallbackPtr callback, uint32_t baudRate, portMode_t mode)
|
|
{
|
|
DMA_InitTypeDef DMA_InitStructure;
|
|
USART_InitTypeDef USART_InitStructure;
|
|
|
|
uartPort_t *s = NULL;
|
|
|
|
if (USARTx == USART1)
|
|
s = serialUSART1(baudRate, mode);
|
|
if (USARTx == USART2)
|
|
s = serialUSART2(baudRate, mode);
|
|
|
|
s->USARTx = USARTx;
|
|
|
|
// common serial initialisation code should move to serialPort::init()
|
|
s->port.rxBufferHead = s->port.rxBufferTail = 0;
|
|
s->port.txBufferHead = s->port.txBufferTail = 0;
|
|
// callback for IRQ-based RX ONLY
|
|
s->port.callback = callback;
|
|
s->port.mode = mode;
|
|
s->port.baudRate = baudRate;
|
|
|
|
USART_InitStructure.USART_BaudRate = baudRate;
|
|
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
|
|
if (mode & MODE_SBUS) {
|
|
USART_InitStructure.USART_StopBits = USART_StopBits_2;
|
|
USART_InitStructure.USART_Parity = USART_Parity_Even;
|
|
} else {
|
|
USART_InitStructure.USART_StopBits = USART_StopBits_1;
|
|
USART_InitStructure.USART_Parity = USART_Parity_No;
|
|
}
|
|
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
|
|
USART_InitStructure.USART_Mode = 0;
|
|
if (mode & MODE_RX)
|
|
USART_InitStructure.USART_Mode |= USART_Mode_Rx;
|
|
if (mode & MODE_TX)
|
|
USART_InitStructure.USART_Mode |= USART_Mode_Tx;
|
|
USART_Init(USARTx, &USART_InitStructure);
|
|
|
|
DMA_StructInit(&DMA_InitStructure);
|
|
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USARTx->RDR;
|
|
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
|
|
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
|
|
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
|
|
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
|
|
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
|
|
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
|
|
|
|
// Receive DMA or IRQ
|
|
if (mode & MODE_RX) {
|
|
if (s->rxDMAChannel) {
|
|
DMA_InitStructure.DMA_BufferSize = s->port.rxBufferSize;
|
|
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
|
|
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
|
|
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)s->port.rxBuffer;
|
|
DMA_DeInit(s->rxDMAChannel);
|
|
DMA_Init(s->rxDMAChannel, &DMA_InitStructure);
|
|
DMA_Cmd(s->rxDMAChannel, ENABLE);
|
|
USART_DMACmd(USARTx, USART_DMAReq_Rx, ENABLE);
|
|
s->rxDMAPos = DMA_GetCurrDataCounter(s->rxDMAChannel);
|
|
} else {
|
|
USART_ITConfig(USARTx, USART_IT_RXNE, ENABLE);
|
|
}
|
|
}
|
|
|
|
DMA_StructInit(&DMA_InitStructure);
|
|
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USARTx->TDR;
|
|
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
|
|
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
|
|
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
|
|
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
|
|
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
|
|
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
|
|
|
|
// Transmit DMA or IRQ
|
|
if (mode & MODE_TX) {
|
|
if (s->txDMAChannel) {
|
|
DMA_InitStructure.DMA_BufferSize = s->port.txBufferSize;
|
|
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
|
|
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
|
|
DMA_DeInit(s->txDMAChannel);
|
|
DMA_Init(s->txDMAChannel, &DMA_InitStructure);
|
|
DMA_ITConfig(s->txDMAChannel, DMA_IT_TC, ENABLE);
|
|
DMA_SetCurrDataCounter(s->txDMAChannel, 0);
|
|
s->txDMAChannel->CNDTR = 0;
|
|
USART_DMACmd(USARTx, USART_DMAReq_Tx, ENABLE);
|
|
} else {
|
|
USART_ITConfig(USARTx, USART_IT_TXE, ENABLE);
|
|
}
|
|
}
|
|
|
|
USART_Cmd(USARTx, ENABLE);
|
|
|
|
return (serialPort_t *)s;
|
|
}
|
|
|
|
void uartSetBaudRate(serialPort_t *instance, uint32_t baudRate)
|
|
{
|
|
USART_InitTypeDef USART_InitStructure;
|
|
uartPort_t *s = (uartPort_t *)instance;
|
|
|
|
USART_InitStructure.USART_BaudRate = baudRate;
|
|
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
|
|
USART_InitStructure.USART_StopBits = USART_StopBits_1;
|
|
USART_InitStructure.USART_Parity = USART_Parity_No;
|
|
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
|
|
USART_InitStructure.USART_Mode = 0;
|
|
if (s->port.mode & MODE_RX)
|
|
USART_InitStructure.USART_Mode |= USART_Mode_Rx;
|
|
if (s->port.mode & MODE_TX)
|
|
USART_InitStructure.USART_Mode |= USART_Mode_Tx;
|
|
USART_Init(s->USARTx, &USART_InitStructure);
|
|
|
|
s->port.baudRate = baudRate;
|
|
}
|
|
|
|
void uartSetMode(serialPort_t *s, portMode_t mode)
|
|
{
|
|
// not implemented.
|
|
}
|
|
|
|
|
|
static void uartStartTxDMA(uartPort_t *s)
|
|
{
|
|
s->txDMAChannel->CMAR = (uint32_t)&s->port.txBuffer[s->port.txBufferTail];
|
|
if (s->port.txBufferHead > s->port.txBufferTail) {
|
|
s->txDMAChannel->CNDTR = s->port.txBufferHead - s->port.txBufferTail;
|
|
s->port.txBufferTail = s->port.txBufferHead;
|
|
} else {
|
|
s->txDMAChannel->CNDTR = s->port.txBufferSize - s->port.txBufferTail;
|
|
s->port.txBufferTail = 0;
|
|
}
|
|
s->txDMAEmpty = false;
|
|
DMA_Cmd(s->txDMAChannel, ENABLE);
|
|
}
|
|
|
|
uint8_t uartTotalBytesWaiting(serialPort_t *instance)
|
|
{
|
|
uartPort_t *s = (uartPort_t*)instance;
|
|
// FIXME always returns 1 or 0, not the amount of bytes waiting
|
|
if (s->rxDMAChannel)
|
|
return s->rxDMAChannel->CNDTR != s->rxDMAPos;
|
|
else
|
|
return s->port.rxBufferTail != s->port.rxBufferHead;
|
|
}
|
|
|
|
// BUGBUG TODO TODO FIXME - What is the bug?
|
|
bool isUartTransmitBufferEmpty(serialPort_t *instance)
|
|
{
|
|
uartPort_t *s = (uartPort_t *)instance;
|
|
if (s->txDMAChannel)
|
|
return s->txDMAEmpty;
|
|
else
|
|
return s->port.txBufferTail == s->port.txBufferHead;
|
|
}
|
|
|
|
uint8_t uartRead(serialPort_t *instance)
|
|
{
|
|
uint8_t ch;
|
|
uartPort_t *s = (uartPort_t *)instance;
|
|
|
|
if (s->rxDMAChannel) {
|
|
ch = s->port.rxBuffer[s->port.rxBufferSize - s->rxDMAPos];
|
|
if (--s->rxDMAPos == 0)
|
|
s->rxDMAPos = s->port.rxBufferSize;
|
|
} else {
|
|
ch = s->port.rxBuffer[s->port.rxBufferTail];
|
|
s->port.rxBufferTail = (s->port.rxBufferTail + 1) % s->port.rxBufferSize;
|
|
}
|
|
|
|
return ch;
|
|
}
|
|
|
|
void uartWrite(serialPort_t *instance, uint8_t ch)
|
|
{
|
|
uartPort_t *s = (uartPort_t *)instance;
|
|
s->port.txBuffer[s->port.txBufferHead] = ch;
|
|
s->port.txBufferHead = (s->port.txBufferHead + 1) % s->port.txBufferSize;
|
|
|
|
if (s->txDMAChannel) {
|
|
if (!(s->txDMAChannel->CCR & 1))
|
|
uartStartTxDMA(s);
|
|
} else {
|
|
USART_ITConfig(s->USARTx, USART_IT_TXE, ENABLE);
|
|
}
|
|
}
|
|
|
|
const struct serialPortVTable uartVTable[] = {
|
|
{
|
|
uartWrite,
|
|
uartTotalBytesWaiting,
|
|
uartRead,
|
|
uartSetBaudRate,
|
|
isUartTransmitBufferEmpty,
|
|
uartSetMode,
|
|
}
|
|
};
|
|
|
|
// Handlers
|
|
|
|
// USART1 Tx DMA Handler
|
|
void DMA1_Channel4_IRQHandler(void)
|
|
{
|
|
uartPort_t *s = &uartPort1;
|
|
DMA_ClearITPendingBit(DMA1_IT_TC4);
|
|
DMA_Cmd(s->txDMAChannel, DISABLE);
|
|
|
|
if (s->port.txBufferHead != s->port.txBufferTail)
|
|
uartStartTxDMA(s);
|
|
else
|
|
s->txDMAEmpty = true;
|
|
}
|
|
|
|
// USART1 Tx IRQ Handler
|
|
void USART1_IRQHandler(void)
|
|
{
|
|
uartPort_t *s = &uartPort1;
|
|
uint16_t SR = s->USARTx->ISR;
|
|
|
|
if (SR & USART_FLAG_TXE) {
|
|
if (s->port.txBufferTail != s->port.txBufferHead) {
|
|
s->USARTx->TDR = s->port.txBuffer[s->port.txBufferTail];
|
|
s->port.txBufferTail = (s->port.txBufferTail + 1) % s->port.txBufferSize;
|
|
} else {
|
|
USART_ITConfig(s->USARTx, USART_IT_TXE, DISABLE);
|
|
}
|
|
}
|
|
}
|
|
|
|
// USART2 Rx/Tx IRQ Handler
|
|
void USART2_IRQHandler(void)
|
|
{
|
|
uartPort_t *s = &uartPort2;
|
|
uint16_t SR = s->USARTx->ISR;
|
|
|
|
if (SR & USART_FLAG_RXNE) {
|
|
// If we registered a callback, pass crap there
|
|
if (s->port.callback) {
|
|
s->port.callback(s->USARTx->RDR);
|
|
} else {
|
|
s->port.rxBuffer[s->port.rxBufferHead] = s->USARTx->RDR;
|
|
s->port.rxBufferHead = (s->port.rxBufferHead + 1) % s->port.rxBufferSize;
|
|
}
|
|
}
|
|
if (SR & USART_FLAG_TXE) {
|
|
if (s->port.txBufferTail != s->port.txBufferHead) {
|
|
s->USARTx->TDR = s->port.txBuffer[s->port.txBufferTail];
|
|
s->port.txBufferTail = (s->port.txBufferTail + 1) % s->port.txBufferSize;
|
|
} else {
|
|
USART_ITConfig(s->USARTx, USART_IT_TXE, DISABLE);
|
|
}
|
|
}
|
|
}
|