1247 lines
45 KiB
C++
1247 lines
45 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2010-2018 by Terraneo Federico *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program 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. *
|
|
* *
|
|
* As a special exception, if other files instantiate templates or use *
|
|
* macros or inline functions from this file, or you compile this file *
|
|
* and link it with other works to produce a work based on this file, *
|
|
* this file does not by itself cause the resulting work to be covered *
|
|
* by the GNU General Public License. However the source code for this *
|
|
* file must still be made available in accordance with the GNU General *
|
|
* Public License. This exception does not invalidate any other reasons *
|
|
* why a work based on this file might be covered by the GNU General *
|
|
* Public License. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
|
***************************************************************************/
|
|
|
|
#include <cstring>
|
|
#include <errno.h>
|
|
#include <termios.h>
|
|
#include "serial_stm32.h"
|
|
#include "kernel/sync.h"
|
|
#include "kernel/scheduler/scheduler.h"
|
|
#include "interfaces/portability.h"
|
|
#include "filesystem/ioctl.h"
|
|
#include "core/cache_cortexMx.h"
|
|
|
|
using namespace std;
|
|
using namespace miosix;
|
|
|
|
static const int numPorts=3; //Supporting only USART1, USART2, USART3
|
|
|
|
//A nice feature of the stm32 is that the USART are connected to the same
|
|
//GPIOS in all families, stm32f1, f2, f4 and l1. Additionally, USART1 is
|
|
//always connected to the APB2, while USART2 and USART3 are always on APB1
|
|
//Unfortunately, this does not hold with DMA.
|
|
typedef Gpio<GPIOA_BASE,9> u1tx;
|
|
typedef Gpio<GPIOA_BASE,10> u1rx;
|
|
typedef Gpio<GPIOA_BASE,11> u1cts;
|
|
typedef Gpio<GPIOA_BASE,12> u1rts;
|
|
|
|
typedef Gpio<GPIOA_BASE,2> u2tx;
|
|
typedef Gpio<GPIOA_BASE,3> u2rx;
|
|
typedef Gpio<GPIOA_BASE,0> u2cts;
|
|
typedef Gpio<GPIOA_BASE,1> u2rts;
|
|
|
|
typedef Gpio<GPIOB_BASE,10> u3tx;
|
|
typedef Gpio<GPIOB_BASE,11> u3rx;
|
|
typedef Gpio<GPIOB_BASE,13> u3cts;
|
|
typedef Gpio<GPIOB_BASE,14> u3rts;
|
|
|
|
/// Pointer to serial port classes to let interrupts access the classes
|
|
static STM32Serial *ports[numPorts]={0};
|
|
|
|
/**
|
|
* \internal interrupt routine for usart1 actual implementation
|
|
*/
|
|
void __attribute__((noinline)) usart1irqImpl()
|
|
{
|
|
if(ports[0]) ports[0]->IRQhandleInterrupt();
|
|
}
|
|
|
|
/**
|
|
* \internal interrupt routine for usart1
|
|
*/
|
|
void __attribute__((naked)) USART1_IRQHandler()
|
|
{
|
|
saveContext();
|
|
asm volatile("bl _Z13usart1irqImplv");
|
|
restoreContext();
|
|
}
|
|
|
|
#if !defined(STM32_NO_SERIAL_2_3)
|
|
|
|
/**
|
|
* \internal interrupt routine for usart2 actual implementation
|
|
*/
|
|
void __attribute__((noinline)) usart2irqImpl()
|
|
{
|
|
if(ports[1]) ports[1]->IRQhandleInterrupt();
|
|
}
|
|
|
|
/**
|
|
* \internal interrupt routine for usart2
|
|
*/
|
|
void __attribute__((naked)) USART2_IRQHandler()
|
|
{
|
|
saveContext();
|
|
asm volatile("bl _Z13usart2irqImplv");
|
|
restoreContext();
|
|
}
|
|
|
|
#if !defined(STM32F411xE) && !defined(STM32F401xE) && !defined(STM32F401xC)
|
|
/**
|
|
* \internal interrupt routine for usart3 actual implementation
|
|
*/
|
|
void __attribute__((noinline)) usart3irqImpl()
|
|
{
|
|
if(ports[2]) ports[2]->IRQhandleInterrupt();
|
|
}
|
|
|
|
/**
|
|
* \internal interrupt routine for usart3
|
|
*/
|
|
#if !defined(STM32F072xB)
|
|
void __attribute__((naked)) USART3_IRQHandler()
|
|
{
|
|
saveContext();
|
|
asm volatile("bl _Z13usart3irqImplv");
|
|
restoreContext();
|
|
}
|
|
#else //!defined(STM32F072xB)
|
|
void __attribute__((naked)) USART3_4_IRQHandler()
|
|
{
|
|
saveContext();
|
|
asm volatile("bl _Z13usart3irqImplv");
|
|
restoreContext();
|
|
}
|
|
#endif //!defined(STM32F072xB)
|
|
#endif //!defined(STM32F411xE) && !defined(STM32F401xE) && !defined(STM32F401xC)
|
|
#endif //!defined(STM32_NO_SERIAL_2_3)
|
|
|
|
#ifdef SERIAL_1_DMA
|
|
|
|
/**
|
|
* \internal USART1 DMA tx actual implementation
|
|
*/
|
|
void __attribute__((noinline)) usart1txDmaImpl()
|
|
{
|
|
#if defined(_ARCH_CORTEXM3_STM32) || defined (_ARCH_CORTEXM4_STM32F3) \
|
|
|| defined(_ARCH_CORTEXM4_STM32L4)
|
|
DMA1->IFCR=DMA_IFCR_CGIF4;
|
|
DMA1_Channel4->CCR=0; //Disable DMA
|
|
#else //stm32f2 and f4
|
|
DMA2->HIFCR=DMA_HIFCR_CTCIF7
|
|
| DMA_HIFCR_CTEIF7
|
|
| DMA_HIFCR_CDMEIF7
|
|
| DMA_HIFCR_CFEIF7;
|
|
#endif
|
|
if(ports[0]) ports[0]->IRQhandleDMAtx();
|
|
}
|
|
|
|
/**
|
|
* \internal USART1 DMA rx actual implementation
|
|
*/
|
|
void __attribute__((noinline)) usart1rxDmaImpl()
|
|
{
|
|
if(ports[0]) ports[0]->IRQhandleDMArx();
|
|
}
|
|
|
|
#if defined(_ARCH_CORTEXM3_STM32) || defined (_ARCH_CORTEXM4_STM32F3) \
|
|
|| defined(_ARCH_CORTEXM4_STM32L4)
|
|
/**
|
|
* \internal DMA1 Channel 4 IRQ (configured as USART1 TX)
|
|
*/
|
|
void __attribute__((naked)) DMA1_Channel4_IRQHandler()
|
|
{
|
|
saveContext();
|
|
asm volatile("bl _Z15usart1txDmaImplv");
|
|
restoreContext();
|
|
}
|
|
|
|
/**
|
|
* \internal DMA1 Channel 5 IRQ (configured as USART1 RX)
|
|
*/
|
|
void __attribute__((naked)) DMA1_Channel5_IRQHandler()
|
|
{
|
|
saveContext();
|
|
asm volatile("bl _Z15usart1rxDmaImplv");
|
|
restoreContext();
|
|
}
|
|
|
|
#else //stm32f2 and stm32f4
|
|
|
|
/**
|
|
* \internal DMA2 stream 7 IRQ (configured as USART1 TX)
|
|
*/
|
|
void __attribute__((naked)) DMA2_Stream7_IRQHandler()
|
|
{
|
|
saveContext();
|
|
asm volatile("bl _Z15usart1txDmaImplv");
|
|
restoreContext();
|
|
}
|
|
|
|
/**
|
|
* \internal DMA2 stream 5 IRQ (configured as USART1 RX)
|
|
*/
|
|
void __attribute__((naked)) DMA2_Stream5_IRQHandler()
|
|
{
|
|
saveContext();
|
|
asm volatile("bl _Z15usart1rxDmaImplv");
|
|
restoreContext();
|
|
}
|
|
#endif
|
|
#endif //SERIAL_1_DMA
|
|
|
|
#if defined(SERIAL_2_DMA) && !defined(STM32_NO_SERIAL_2_3)
|
|
|
|
/**
|
|
* \internal USART2 DMA tx actual implementation
|
|
*/
|
|
void __attribute__((noinline)) usart2txDmaImpl()
|
|
{
|
|
#if defined(_ARCH_CORTEXM3_STM32) || defined (_ARCH_CORTEXM4_STM32F3) \
|
|
|| defined(_ARCH_CORTEXM4_STM32L4)
|
|
DMA1->IFCR=DMA_IFCR_CGIF7;
|
|
DMA1_Channel7->CCR=0; //Disable DMA
|
|
#else //stm32f2 and f4
|
|
DMA1->HIFCR=DMA_HIFCR_CTCIF6
|
|
| DMA_HIFCR_CTEIF6
|
|
| DMA_HIFCR_CDMEIF6
|
|
| DMA_HIFCR_CFEIF6;
|
|
#endif
|
|
if(ports[1]) ports[1]->IRQhandleDMAtx();
|
|
}
|
|
|
|
/**
|
|
* \internal USART2 DMA rx actual implementation
|
|
*/
|
|
void __attribute__((noinline)) usart2rxDmaImpl()
|
|
{
|
|
if(ports[1]) ports[1]->IRQhandleDMArx();
|
|
}
|
|
|
|
#if defined(_ARCH_CORTEXM3_STM32) || defined (_ARCH_CORTEXM4_STM32F3) \
|
|
|| defined(_ARCH_CORTEXM4_STM32L4)
|
|
/**
|
|
* \internal DMA1 Channel 7 IRQ (configured as USART2 TX)
|
|
*/
|
|
void __attribute__((naked)) DMA1_Channel7_IRQHandler()
|
|
{
|
|
saveContext();
|
|
asm volatile("bl _Z15usart2txDmaImplv");
|
|
restoreContext();
|
|
}
|
|
|
|
/**
|
|
* \internal DMA1 Channel 6 IRQ (configured as USART2 RX)
|
|
*/
|
|
void __attribute__((naked)) DMA1_Channel6_IRQHandler()
|
|
{
|
|
saveContext();
|
|
asm volatile("bl _Z15usart2rxDmaImplv");
|
|
restoreContext();
|
|
}
|
|
|
|
#else //stm32f2 and stm32f4
|
|
|
|
/**
|
|
* \internal DMA1 stream 6 IRQ (configured as USART2 TX)
|
|
*/
|
|
void __attribute__((naked)) DMA1_Stream6_IRQHandler()
|
|
{
|
|
saveContext();
|
|
asm volatile("bl _Z15usart2txDmaImplv");
|
|
restoreContext();
|
|
}
|
|
|
|
/**
|
|
* \internal DMA1 stream 5 IRQ (configured as USART2 RX)
|
|
*/
|
|
void __attribute__((naked)) DMA1_Stream5_IRQHandler()
|
|
{
|
|
saveContext();
|
|
asm volatile("bl _Z15usart2rxDmaImplv");
|
|
restoreContext();
|
|
}
|
|
#endif
|
|
#endif //SERIAL_2_DMA
|
|
|
|
#if defined(SERIAL_3_DMA) && !defined(STM32_NO_SERIAL_2_3)
|
|
|
|
/**
|
|
* \internal USART3 DMA tx actual implementation
|
|
*/
|
|
void __attribute__((noinline)) usart3txDmaImpl()
|
|
{
|
|
#if defined(_ARCH_CORTEXM3_STM32) || defined (_ARCH_CORTEXM4_STM32F3) \
|
|
|| defined(_ARCH_CORTEXM4_STM32L4)
|
|
DMA1->IFCR=DMA_IFCR_CGIF2;
|
|
DMA1_Channel2->CCR=0; //Disable DMA
|
|
#else //stm32f2 and f4
|
|
DMA1->LIFCR=DMA_LIFCR_CTCIF3
|
|
| DMA_LIFCR_CTEIF3
|
|
| DMA_LIFCR_CDMEIF3
|
|
| DMA_LIFCR_CFEIF3;
|
|
#endif
|
|
if(ports[2]) ports[2]->IRQhandleDMAtx();
|
|
}
|
|
|
|
/**
|
|
* \internal USART3 DMA rx actual implementation
|
|
*/
|
|
void __attribute__((noinline)) usart3rxDmaImpl()
|
|
{
|
|
if(ports[2]) ports[2]->IRQhandleDMArx();
|
|
}
|
|
|
|
#if defined(_ARCH_CORTEXM3_STM32) || defined (_ARCH_CORTEXM4_STM32F3) \
|
|
|| defined(_ARCH_CORTEXM4_STM32L4)
|
|
/**
|
|
* \internal DMA1 Channel 2 IRQ (configured as USART3 TX)
|
|
*/
|
|
void __attribute__((naked)) DMA1_Channel2_IRQHandler()
|
|
{
|
|
saveContext();
|
|
asm volatile("bl _Z15usart3txDmaImplv");
|
|
restoreContext();
|
|
}
|
|
|
|
/**
|
|
* \internal DMA1 Channel 3 IRQ (configured as USART3 RX)
|
|
*/
|
|
void __attribute__((naked)) DMA1_Channel3_IRQHandler()
|
|
{
|
|
saveContext();
|
|
asm volatile("bl _Z15usart3rxDmaImplv");
|
|
restoreContext();
|
|
}
|
|
|
|
#else //stm32f2 and stm32f4
|
|
|
|
/**
|
|
* \internal DMA1 stream 3 IRQ (configured as USART3 TX)
|
|
*/
|
|
void __attribute__((naked)) DMA1_Stream3_IRQHandler()
|
|
{
|
|
saveContext();
|
|
asm volatile("bl _Z15usart3txDmaImplv");
|
|
restoreContext();
|
|
}
|
|
|
|
/**
|
|
* \internal DMA1 stream 1 IRQ (configured as USART3 RX)
|
|
*/
|
|
void __attribute__((naked)) DMA1_Stream1_IRQHandler()
|
|
{
|
|
saveContext();
|
|
asm volatile("bl _Z15usart3rxDmaImplv");
|
|
restoreContext();
|
|
}
|
|
#endif
|
|
#endif //SERIAL_3_DMA
|
|
|
|
namespace miosix {
|
|
|
|
#ifdef SERIAL_DMA
|
|
#if defined(_ARCH_CORTEXM4_STM32F4) || defined(_ARCH_CORTEXM4_STM32F3) \
|
|
|| defined(_ARCH_CORTEXM4_STM32L4)
|
|
/**
|
|
* The STM3F3, STM32F4 and STM32L4 have an ugly quirk of having 64KB RAM area
|
|
* called CCM that can only be accessed by the processor and not be the DMA.
|
|
* \param x pointer to check
|
|
* \return true if the pointer is inside the CCM, and thus it isn't possible
|
|
* to use it for DMA transfers
|
|
*/
|
|
static bool isInCCMarea(const void *x)
|
|
{
|
|
unsigned int ptr=reinterpret_cast<const unsigned int>(x);
|
|
return (ptr>=0x10000000) && (ptr<(0x10000000+64*1024));
|
|
}
|
|
#else //_ARCH_CORTEXM4_STM32F4 and _ARCH_CORTEXM4_STM32F3
|
|
static inline bool isInCCMarea(const void *x) { return false; }
|
|
#endif // _ARCH_CORTEXM4_STM32F4 and _ARCH_CORTEXM4_STM32F3
|
|
#endif //SERIAL_DMA
|
|
|
|
//
|
|
// class STM32Serial
|
|
//
|
|
|
|
// A note on the baudrate/500: the buffer is selected so as to withstand
|
|
// 20ms of full data rate. In the 8N1 format one char is made of 10 bits.
|
|
// So (baudrate/10)*0.02=baudrate/500
|
|
STM32Serial::STM32Serial(int id, int baudrate, FlowCtrl flowControl)
|
|
: Device(Device::TTY), rxQueue(rxQueueMin+baudrate/500),
|
|
flowControl(flowControl==RTSCTS), portId(id)
|
|
{
|
|
#if !defined(_ARCH_CORTEXM3_STM32)
|
|
//stm32f2, f4, l4, l1, f7, h7 require alternate function mapping
|
|
//stm32f0 family has different alternate function mapping
|
|
//with respect to the other families
|
|
switch(id)
|
|
{
|
|
case 1:
|
|
#if !defined(_ARCH_CORTEXM0_STM32)
|
|
u1tx::alternateFunction(7);
|
|
u1rx::alternateFunction(7);
|
|
if(flowControl)
|
|
{
|
|
u1rts::alternateFunction(7);
|
|
u1cts::alternateFunction(7);
|
|
}
|
|
#else //!defined(_ARCH_CORTEXM0_STM32)
|
|
u1tx::alternateFunction(1);
|
|
u1rx::alternateFunction(1);
|
|
if(flowControl)
|
|
{
|
|
u1rts::alternateFunction(1);
|
|
u1cts::alternateFunction(1);
|
|
}
|
|
#endif //!defined(_ARCH_CORTEXM0_STM32)
|
|
break;
|
|
case 2:
|
|
#if !defined(_ARCH_CORTEXM0_STM32)
|
|
u2tx::alternateFunction(7);
|
|
u2rx::alternateFunction(7);
|
|
if(flowControl)
|
|
{
|
|
u2rts::alternateFunction(7);
|
|
u2cts::alternateFunction(7);
|
|
}
|
|
#else //!defined(_ARCH_CORTEXM0_STM32)
|
|
u2tx::alternateFunction(1);
|
|
u2rx::alternateFunction(1);
|
|
if(flowControl)
|
|
{
|
|
u2rts::alternateFunction(1);
|
|
u2cts::alternateFunction(1);
|
|
}
|
|
#endif //!defined(_ARCH_CORTEXM0_STM32)
|
|
break;
|
|
case 3:
|
|
#if !defined(_ARCH_CORTEXM0_STM32)
|
|
u3tx::alternateFunction(7);
|
|
u3rx::alternateFunction(7);
|
|
if(flowControl)
|
|
{
|
|
u3rts::alternateFunction(7);
|
|
u3cts::alternateFunction(7);
|
|
}
|
|
#else //!defined(_ARCH_CORTEXM0_STM32)
|
|
u3tx::alternateFunction(4);
|
|
u3rx::alternateFunction(4);
|
|
if(flowControl)
|
|
{
|
|
u3rts::alternateFunction(4);
|
|
u3cts::alternateFunction(4);
|
|
}
|
|
#endif //!defined(_ARCH_CORTEXM0_STM32)
|
|
break;
|
|
}
|
|
#endif //_ARCH_CORTEXM3_STM32
|
|
|
|
switch(id)
|
|
{
|
|
case 1:
|
|
commonInit(id,baudrate,u1tx::getPin(),u1rx::getPin(),
|
|
u1rts::getPin(),u1cts::getPin());
|
|
break;
|
|
case 2:
|
|
commonInit(id,baudrate,u2tx::getPin(),u2rx::getPin(),
|
|
u2rts::getPin(),u2cts::getPin());
|
|
break;
|
|
case 3:
|
|
commonInit(id,baudrate,u3tx::getPin(),u3rx::getPin(),
|
|
u3rts::getPin(),u3cts::getPin());
|
|
break;
|
|
}
|
|
}
|
|
|
|
STM32Serial::STM32Serial(int id, int baudrate, GpioPin tx, GpioPin rx)
|
|
: Device(Device::TTY), rxQueue(rxQueueMin+baudrate/500),
|
|
flowControl(false), portId(id)
|
|
{
|
|
commonInit(id,baudrate,tx,rx,tx,rx); //The last two args will be ignored
|
|
}
|
|
|
|
STM32Serial::STM32Serial(int id, int baudrate, GpioPin tx, GpioPin rx,
|
|
miosix::GpioPin rts, miosix::GpioPin cts)
|
|
: Device(Device::TTY), rxQueue(rxQueueMin+baudrate/500),
|
|
flowControl(true), portId(id)
|
|
{
|
|
commonInit(id,baudrate,tx,rx,rts,cts);
|
|
}
|
|
|
|
void STM32Serial::commonInit(int id, int baudrate, GpioPin tx, GpioPin rx,
|
|
GpioPin rts, GpioPin cts)
|
|
{
|
|
#ifdef SERIAL_DMA
|
|
dmaTx=0;
|
|
dmaRx=0;
|
|
txWaiting=0;
|
|
dmaTxInProgress=false;
|
|
#endif //SERIAL_DMA
|
|
InterruptDisableLock dLock;
|
|
if(id<1|| id>numPorts || ports[id-1]!=0) errorHandler(UNEXPECTED);
|
|
ports[id-1]=this;
|
|
unsigned int freq=SystemCoreClock;
|
|
//Quirk the position of the PPRE1 and PPRE2 bitfields in RCC->CFGR changes
|
|
//STM32F0 does not have ppre1 and ppre2, in this case the variables are not
|
|
//defined in order to avoid "unused variable" warning
|
|
#if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM3_STM32L1) \
|
|
|| defined(_ARCH_CORTEXM4_STM32F3) || defined(_ARCH_CORTEXM4_STM32L4)
|
|
const unsigned int ppre1=8;
|
|
const unsigned int ppre2=11;
|
|
#elif !defined(_ARCH_CORTEXM7_STM32H7) && !defined(_ARCH_CORTEXM0_STM32)
|
|
const unsigned int ppre1=10;
|
|
const unsigned int ppre2=13;
|
|
#endif
|
|
switch(id)
|
|
{
|
|
case 1:
|
|
port=USART1;
|
|
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
|
|
RCC_SYNC();
|
|
#ifdef SERIAL_1_DMA
|
|
#if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \
|
|
|| defined(_ARCH_CORTEXM4_STM32L4)
|
|
#ifdef _ARCH_CORTEXM4_STM32L4
|
|
RCC->AHB1ENR |= RCC_AHBENR_DMA1EN;
|
|
DMA1_CSELR->CSELR |= (2 << DMA_CSELR_C4S_Pos) // Assign DMA1_CH4 to USART1_TX
|
|
| (2 << DMA_CSELR_C5S_Pos);// Assign DMA1_CH5 to USART1_RX
|
|
#else
|
|
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
|
|
#endif
|
|
RCC_SYNC();
|
|
NVIC_SetPriority(DMA1_Channel4_IRQn,15);//Lowest priority for serial
|
|
NVIC_EnableIRQ(DMA1_Channel4_IRQn);
|
|
dmaTx=DMA1_Channel4;
|
|
//Higher priority to ensure IRQhandleDMArx() is called before
|
|
//IRQhandleInterrupt(), so that idle is set correctly
|
|
NVIC_SetPriority(DMA1_Channel5_IRQn,14);
|
|
NVIC_EnableIRQ(DMA1_Channel5_IRQn);
|
|
dmaRx=DMA1_Channel5;
|
|
#else //stm32f2, stm32f4
|
|
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
|
|
RCC_SYNC();
|
|
NVIC_SetPriority(DMA2_Stream7_IRQn,15);//Lowest priority for serial
|
|
NVIC_EnableIRQ(DMA2_Stream7_IRQn);
|
|
dmaTx=DMA2_Stream7;
|
|
//Higher priority to ensure IRQhandleDMArx() is called before
|
|
//IRQhandleInterrupt(), so that idle is set correctly
|
|
NVIC_SetPriority(DMA2_Stream5_IRQn,14);
|
|
NVIC_EnableIRQ(DMA2_Stream5_IRQn);
|
|
dmaRx=DMA2_Stream5;
|
|
#endif
|
|
port->CR3=USART_CR3_DMAT | USART_CR3_DMAR;
|
|
#endif //SERIAL_1_DMA
|
|
NVIC_SetPriority(USART1_IRQn,15);//Lowest priority for serial
|
|
NVIC_EnableIRQ(USART1_IRQn);
|
|
#if !defined(_ARCH_CORTEXM7_STM32H7) && !defined(_ARCH_CORTEXM0_STM32)
|
|
if(RCC->CFGR & RCC_CFGR_PPRE2_2) freq/=1<<(((RCC->CFGR>>ppre2) & 0x3)+1);
|
|
#elif defined(_ARCH_CORTEXM0_STM32)
|
|
// STM32F0 family has only PPRE2 register
|
|
if(RCC->CFGR & RCC_CFGR_PPRE_2) freq/=1<<(((RCC->CFGR>>8) & 0x3)+1);
|
|
#else
|
|
//rcc_hclk3 = SystemCoreClock / HPRE
|
|
//rcc_pclk2 = rcc_hclk1 / D2PPRE2
|
|
//NOTE: are rcc_hclk3 and rcc_hclk1 the same signal?
|
|
//usart1 clock is rcc_pclk2
|
|
if(RCC->D1CFGR & RCC_D1CFGR_HPRE_3)
|
|
freq/=1<<(((RCC->D1CFGR>>RCC_D1CFGR_HPRE_Pos) & 0x7)+1);
|
|
if(RCC->D2CFGR & RCC_D2CFGR_D2PPRE2_2)
|
|
freq/=1<<(((RCC->D2CFGR>>RCC_D2CFGR_D2PPRE2_Pos) & 0x3)+1);
|
|
#endif //_ARCH_CORTEXM7_STM32H7
|
|
break;
|
|
|
|
#if !defined(STM32_NO_SERIAL_2_3)
|
|
case 2:
|
|
port=USART2;
|
|
#ifndef _ARCH_CORTEXM7_STM32H7
|
|
#ifndef _ARCH_CORTEXM4_STM32L4
|
|
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
|
|
#else //_ARCH_CORTEXM4_STM32L4
|
|
RCC->APB1ENR1 |= RCC_APB1ENR1_USART2EN;
|
|
#endif //_ARCH_CORTEXM4_STM32L4
|
|
#else //_ARCH_CORTEXM7_STM32H7
|
|
RCC->APB1LENR |= RCC_APB1LENR_USART2EN;
|
|
#endif //_ARCH_CORTEXM7_STM32H7
|
|
RCC_SYNC();
|
|
#ifdef SERIAL_2_DMA
|
|
#if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \
|
|
|| defined(_ARCH_CORTEXM4_STM32L4)
|
|
#ifdef _ARCH_CORTEXM4_STM32L4
|
|
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
|
|
DMA1_CSELR->CSELR |= (2 << DMA_CSELR_C7S_Pos) // Assign DMA1_CH7 to USART2_TX
|
|
| (2 << DMA_CSELR_C6S_Pos);// Assign DMA1_CH6 to USART2_RX
|
|
#else
|
|
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
|
|
#endif
|
|
RCC_SYNC();
|
|
NVIC_SetPriority(DMA1_Channel7_IRQn,15);//Lowest priority for serial
|
|
NVIC_EnableIRQ(DMA1_Channel7_IRQn);
|
|
dmaTx=DMA1_Channel7;
|
|
//Higher priority to ensure IRQhandleDMArx() is called before
|
|
//IRQhandleInterrupt(), so that idle is set correctly
|
|
NVIC_SetPriority(DMA1_Channel6_IRQn,14);
|
|
NVIC_EnableIRQ(DMA1_Channel6_IRQn);
|
|
dmaRx=DMA1_Channel6;
|
|
#else //stm32f2, stm32f4
|
|
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
|
|
RCC_SYNC();
|
|
NVIC_SetPriority(DMA1_Stream6_IRQn,15);//Lowest priority for serial
|
|
NVIC_EnableIRQ(DMA1_Stream6_IRQn);
|
|
dmaTx=DMA1_Stream6;
|
|
//Higher priority to ensure IRQhandleDMArx() is called before
|
|
//IRQhandleInterrupt(), so that idle is set correctly
|
|
NVIC_SetPriority(DMA1_Stream5_IRQn,14);
|
|
NVIC_EnableIRQ(DMA1_Stream5_IRQn);
|
|
dmaRx=DMA1_Stream5;
|
|
#endif
|
|
port->CR3=USART_CR3_DMAT | USART_CR3_DMAR;
|
|
#endif //SERIAL_2_DMA
|
|
NVIC_SetPriority(USART2_IRQn,15);//Lowest priority for serial
|
|
NVIC_EnableIRQ(USART2_IRQn);
|
|
#if !defined(_ARCH_CORTEXM7_STM32H7) && !defined(_ARCH_CORTEXM0_STM32)
|
|
if(RCC->CFGR & RCC_CFGR_PPRE1_2) freq/=1<<(((RCC->CFGR>>ppre1) & 0x3)+1);
|
|
#elif defined(_ARCH_CORTEXM0_STM32)
|
|
// STM32F0 family has only PPRE2 register
|
|
if(RCC->CFGR & RCC_CFGR_PPRE_2) freq/=1<<(((RCC->CFGR>>8) & 0x3)+1);
|
|
#else //_ARCH_CORTEXM7_STM32H7
|
|
//rcc_hclk3 = SystemCoreClock / HPRE
|
|
//rcc_pclk1 = rcc_hclk1 / D2PPRE1
|
|
//NOTE: are rcc_hclk3 and rcc_hclk1 the same signal?
|
|
//usart2 clock is rcc_pclk1
|
|
if(RCC->D1CFGR & RCC_D1CFGR_HPRE_3)
|
|
freq/=1<<(((RCC->D1CFGR>>RCC_D1CFGR_HPRE_Pos) & 0x7)+1);
|
|
if(RCC->D2CFGR & RCC_D2CFGR_D2PPRE1_2)
|
|
freq/=1<<(((RCC->D2CFGR>>RCC_D2CFGR_D2PPRE1_Pos) & 0x3)+1);
|
|
#endif //_ARCH_CORTEXM7_STM32H7
|
|
break;
|
|
#if !defined(STM32F411xE) && !defined(STM32F401xE) && !defined(STM32F401xC)
|
|
case 3:
|
|
port=USART3;
|
|
#ifndef _ARCH_CORTEXM7_STM32H7
|
|
#ifndef _ARCH_CORTEXM4_STM32L4
|
|
RCC->APB1ENR |= RCC_APB1ENR_USART3EN;
|
|
#else //_ARCH_CORTEXM4_STM32L4
|
|
RCC->APB1ENR1 |= RCC_APB1ENR1_USART3EN;
|
|
#endif //_ARCH_CORTEXM4_STM32L4
|
|
#else
|
|
RCC->APB1LENR |= RCC_APB1LENR_USART3EN;
|
|
#endif //_ARCH_CORTEXM7_STM32H7
|
|
RCC_SYNC();
|
|
#ifdef SERIAL_3_DMA
|
|
#if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \
|
|
|| defined(_ARCH_CORTEXM4_STM32L4)
|
|
#ifdef _ARCH_CORTEXM4_STM32L4
|
|
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
|
|
DMA1_CSELR->CSELR |= (2 << DMA_CSELR_C2S_Pos) // Assign DMA1_CH2 to USART2_TX
|
|
| (2 << DMA_CSELR_C3S_Pos);// Assign DMA1_CH3 to USART2_RX
|
|
#else
|
|
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
|
|
#endif
|
|
RCC_SYNC();
|
|
NVIC_SetPriority(DMA1_Channel2_IRQn,15);//Lowest priority for serial
|
|
NVIC_EnableIRQ(DMA1_Channel2_IRQn);
|
|
dmaTx=DMA1_Channel2;
|
|
//Higher priority to ensure IRQhandleDMArx() is called before
|
|
//IRQhandleInterrupt(), so that idle is set correctly
|
|
NVIC_SetPriority(DMA1_Channel3_IRQn,14);
|
|
NVIC_EnableIRQ(DMA1_Channel3_IRQn);
|
|
dmaRx=DMA1_Channel3;
|
|
#else //stm32f2, stm32f4
|
|
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
|
|
RCC_SYNC();
|
|
NVIC_SetPriority(DMA1_Stream3_IRQn,15);//Lowest priority for serial
|
|
NVIC_EnableIRQ(DMA1_Stream3_IRQn);
|
|
dmaTx=DMA1_Stream3;
|
|
//Higher priority to ensure IRQhandleDMArx() is called before
|
|
//IRQhandleInterrupt(), so that idle is set correctly
|
|
NVIC_SetPriority(DMA1_Stream1_IRQn,14);
|
|
NVIC_EnableIRQ(DMA1_Stream1_IRQn);
|
|
dmaRx=DMA1_Stream1;
|
|
#endif
|
|
port->CR3=USART_CR3_DMAT | USART_CR3_DMAR;
|
|
#endif //SERIAL_3_DMA
|
|
#if !defined(STM32F072xB)
|
|
NVIC_SetPriority(USART3_IRQn,15);//Lowest priority for serial
|
|
NVIC_EnableIRQ(USART3_IRQn);
|
|
#else //STM32F072xB
|
|
NVIC_SetPriority(USART3_4_IRQn,15);
|
|
NVIC_EnableIRQ(USART3_4_IRQn);
|
|
#endif //STM32F072xB
|
|
#if !defined(_ARCH_CORTEXM7_STM32H7) && !defined(_ARCH_CORTEXM0_STM32)
|
|
if(RCC->CFGR & RCC_CFGR_PPRE1_2) freq/=1<<(((RCC->CFGR>>ppre1) & 0x3)+1);
|
|
#elif defined(_ARCH_CORTEXM0_STM32)
|
|
// STM32F0 family has only PPRE2 register
|
|
if(RCC->CFGR & RCC_CFGR_PPRE_2) freq/=1<<(((RCC->CFGR>>8) & 0x3)+1);
|
|
#else //_ARCH_CORTEXM7_STM32H7
|
|
//rcc_hclk3 = SystemCoreClock / HPRE
|
|
//rcc_pclk1 = rcc_hclk1 / D2PPRE1
|
|
//NOTE: are rcc_hclk3 and rcc_hclk1 the same signal?
|
|
//usart2 clock is rcc_pclk1
|
|
if(RCC->D1CFGR & RCC_D1CFGR_HPRE_3)
|
|
freq/=1<<(((RCC->D1CFGR>>RCC_D1CFGR_HPRE_Pos) & 0x7)+1);
|
|
if(RCC->D2CFGR & RCC_D2CFGR_D2PPRE1_2)
|
|
freq/=1<<(((RCC->D2CFGR>>RCC_D2CFGR_D2PPRE1_Pos) & 0x3)+1);
|
|
#endif //_ARCH_CORTEXM7_STM32H7
|
|
break;
|
|
#endif //!defined(STM32F411xE) && !defined(STM32F401xE) && !defined(STM32F401xC)
|
|
#endif //!defined(STM32_NO_SERIAL_2_3)
|
|
}
|
|
//Quirk: stm32f1 rx pin has to be in input mode, while stm32f2 and up want
|
|
//it in ALTERNATE mode. Go figure...
|
|
#ifdef _ARCH_CORTEXM3_STM32
|
|
Mode::Mode_ rxPinMode=Mode::INPUT;
|
|
#else //_ARCH_CORTEXM3_STM32
|
|
Mode::Mode_ rxPinMode=Mode::ALTERNATE;
|
|
#endif //_ARCH_CORTEXM3_STM32
|
|
tx.mode(Mode::ALTERNATE);
|
|
rx.mode(rxPinMode);
|
|
if(flowControl)
|
|
{
|
|
rts.mode(Mode::ALTERNATE);
|
|
cts.mode(rxPinMode);
|
|
}
|
|
const unsigned int quot=2*freq/baudrate; //2*freq for round to nearest
|
|
port->BRR=quot/2 + (quot & 1); //Round to nearest
|
|
if(flowControl==false) port->CR3 |= USART_CR3_ONEBIT;
|
|
else port->CR3 |= USART_CR3_ONEBIT | USART_CR3_RTSE | USART_CR3_CTSE;
|
|
//Enabled, 8 data bit, no parity, interrupt on character rx
|
|
#ifdef SERIAL_DMA
|
|
if(dmaTx)
|
|
{
|
|
port->CR1 = USART_CR1_UE //Enable port
|
|
| USART_CR1_IDLEIE //Interrupt on idle line
|
|
| USART_CR1_TE //Transmission enbled
|
|
| USART_CR1_RE; //Reception enabled
|
|
IRQdmaReadStart();
|
|
return;
|
|
}
|
|
#endif //SERIAL_DMA
|
|
port->CR1 = USART_CR1_UE //Enable port
|
|
| USART_CR1_RXNEIE //Interrupt on data received
|
|
| USART_CR1_IDLEIE //Interrupt on idle line
|
|
| USART_CR1_TE //Transmission enbled
|
|
| USART_CR1_RE; //Reception enabled
|
|
}
|
|
|
|
ssize_t STM32Serial::readBlock(void *buffer, size_t size, off_t where)
|
|
{
|
|
Lock<FastMutex> l(rxMutex);
|
|
char *buf=reinterpret_cast<char*>(buffer);
|
|
size_t result=0;
|
|
FastInterruptDisableLock dLock;
|
|
for(;;)
|
|
{
|
|
//Try to get data from the queue
|
|
for(;result<size;result++)
|
|
{
|
|
if(rxQueue.tryGet(buf[result])==false) break;
|
|
//This is here just not to keep IRQ disabled for the whole loop
|
|
FastInterruptEnableLock eLock(dLock);
|
|
}
|
|
if(idle && result>0) break;
|
|
if(result==size) break;
|
|
//Wait for data in the queue
|
|
do {
|
|
rxWaiting=Thread::IRQgetCurrentThread();
|
|
Thread::IRQwait();
|
|
{
|
|
FastInterruptEnableLock eLock(dLock);
|
|
Thread::yield();
|
|
}
|
|
} while(rxWaiting);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ssize_t STM32Serial::writeBlock(const void *buffer, size_t size, off_t where)
|
|
{
|
|
Lock<FastMutex> l(txMutex);
|
|
const char *buf=reinterpret_cast<const char*>(buffer);
|
|
#ifdef SERIAL_DMA
|
|
if(dmaTx)
|
|
{
|
|
size_t remaining=size;
|
|
if(isInCCMarea(buf)==false)
|
|
{
|
|
//Use zero copy for all but the last txBufferSize bytes, if possible
|
|
while(remaining>txBufferSize)
|
|
{
|
|
//DMA is limited to 64K
|
|
size_t transferSize=min<size_t>(remaining-txBufferSize,65535);
|
|
waitDmaTxCompletion();
|
|
writeDma(buf,transferSize);
|
|
buf+=transferSize;
|
|
remaining-=transferSize;
|
|
}
|
|
}
|
|
while(remaining>0)
|
|
{
|
|
size_t transferSize=min(remaining,static_cast<size_t>(txBufferSize));
|
|
waitDmaTxCompletion();
|
|
//Copy to txBuffer only after DMA xfer completed, as the previous
|
|
//xfer may be using the same buffer
|
|
memcpy(txBuffer,buf,transferSize);
|
|
writeDma(txBuffer,transferSize);
|
|
buf+=transferSize;
|
|
remaining-=transferSize;
|
|
}
|
|
return size;
|
|
}
|
|
#endif //SERIAL_DMA
|
|
for(size_t i=0;i<size;i++)
|
|
{
|
|
#if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
|
|
&& !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM4_STM32F3) \
|
|
&& !defined(_ARCH_CORTEXM4_STM32L4)
|
|
while((port->SR & USART_SR_TXE)==0) ;
|
|
port->DR=*buf++;
|
|
#else //_ARCH_CORTEXM7_STM32F7/H7
|
|
while((port->ISR & USART_ISR_TXE)==0) ;
|
|
port->TDR=*buf++;
|
|
#endif //_ARCH_CORTEXM7_STM32F7/H7
|
|
}
|
|
return size;
|
|
}
|
|
|
|
void STM32Serial::IRQwrite(const char *str)
|
|
{
|
|
// We can reach here also with only kernel paused, so make sure
|
|
// interrupts are disabled. This is important for the DMA case
|
|
bool interrupts=areInterruptsEnabled();
|
|
if(interrupts) fastDisableInterrupts();
|
|
#ifdef SERIAL_DMA
|
|
if(dmaTx)
|
|
{
|
|
#if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \
|
|
|| defined(_ARCH_CORTEXM4_STM32L4)
|
|
//If no DMA transfer is in progress bit EN is zero. Otherwise wait until
|
|
//DMA xfer ends, by waiting for the TC (or TE) interrupt flag
|
|
static const unsigned int irqMask[]=
|
|
{
|
|
(DMA_ISR_TCIF4 | DMA_ISR_TEIF4),
|
|
(DMA_ISR_TCIF7 | DMA_ISR_TEIF7),
|
|
(DMA_ISR_TCIF2 | DMA_ISR_TEIF2)
|
|
};
|
|
#if defined(_ARCH_CORTEXM4_STM32F3) || defined(_ARCH_CORTEXM4_STM32L4)
|
|
// Workaround for ST messing up with flag definitions...
|
|
constexpr unsigned int DMA_CCR4_EN = DMA_CCR_EN;
|
|
#endif
|
|
while((dmaTx->CCR & DMA_CCR4_EN) && !(DMA1->ISR & irqMask[getId()-1])) ;
|
|
#else //_ARCH_CORTEXM3_STM32
|
|
//Wait until DMA xfer ends. EN bit is cleared by hardware on transfer end
|
|
while(dmaTx->CR & DMA_SxCR_EN) ;
|
|
#endif //_ARCH_CORTEXM3_STM32
|
|
}
|
|
#endif //SERIAL_DMA
|
|
while(*str)
|
|
{
|
|
#if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
|
|
&& !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM4_STM32F3) \
|
|
&& !defined(_ARCH_CORTEXM4_STM32L4)
|
|
while((port->SR & USART_SR_TXE)==0) ;
|
|
port->DR=*str++;
|
|
#else //_ARCH_CORTEXM7_STM32F7/H7
|
|
while((port->ISR & USART_ISR_TXE)==0) ;
|
|
port->TDR=*str++;
|
|
#endif //_ARCH_CORTEXM7_STM32F7/H7
|
|
}
|
|
waitSerialTxFifoEmpty();
|
|
if(interrupts) fastEnableInterrupts();
|
|
}
|
|
|
|
int STM32Serial::ioctl(int cmd, void* arg)
|
|
{
|
|
if(reinterpret_cast<unsigned>(arg) & 0b11) return -EFAULT; //Unaligned
|
|
termios *t=reinterpret_cast<termios*>(arg);
|
|
switch(cmd)
|
|
{
|
|
case IOCTL_SYNC:
|
|
waitSerialTxFifoEmpty();
|
|
return 0;
|
|
case IOCTL_TCGETATTR:
|
|
t->c_iflag=IGNBRK | IGNPAR;
|
|
t->c_oflag=0;
|
|
t->c_cflag=CS8 | (flowControl ? CRTSCTS : 0);
|
|
t->c_lflag=0;
|
|
return 0;
|
|
case IOCTL_TCSETATTR_NOW:
|
|
case IOCTL_TCSETATTR_DRAIN:
|
|
case IOCTL_TCSETATTR_FLUSH:
|
|
//Changing things at runtime unsupported, so do nothing, but don't
|
|
//return error as console_device.h implements some attribute changes
|
|
return 0;
|
|
default:
|
|
return -ENOTTY; //Means the operation does not apply to this descriptor
|
|
}
|
|
}
|
|
|
|
void STM32Serial::IRQhandleInterrupt()
|
|
{
|
|
#if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
|
|
&& !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM4_STM32F3) \
|
|
&& !defined(_ARCH_CORTEXM4_STM32L4)
|
|
unsigned int status=port->SR;
|
|
#else //_ARCH_CORTEXM7_STM32F7/H7
|
|
unsigned int status=port->ISR;
|
|
constexpr unsigned int USART_SR_RXNE=USART_ISR_RXNE;
|
|
constexpr unsigned int USART_SR_IDLE=USART_ISR_IDLE;
|
|
constexpr unsigned int USART_SR_FE =USART_ISR_FE;
|
|
#endif //_ARCH_CORTEXM7_STM32F7/H7
|
|
char c;
|
|
#ifdef SERIAL_DMA
|
|
if(dmaRx==0 && (status & USART_SR_RXNE))
|
|
#else //SERIAL_DMA
|
|
if(status & USART_SR_RXNE)
|
|
#endif //SERIAL_DMA
|
|
{
|
|
//Always read data, since this clears interrupt flags
|
|
#if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
|
|
&& !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM4_STM32F3) \
|
|
&& !defined(_ARCH_CORTEXM4_STM32L4)
|
|
c=port->DR;
|
|
#else //_ARCH_CORTEXM7_STM32F7/H7
|
|
c=port->RDR;
|
|
#endif //_ARCH_CORTEXM7_STM32F7/H7
|
|
//If no error put data in buffer
|
|
if((status & USART_SR_FE)==0)
|
|
if(rxQueue.tryPut(c)==false) /*fifo overflow*/;
|
|
idle=false;
|
|
}
|
|
if(status & USART_SR_IDLE)
|
|
{
|
|
#if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
|
|
&& !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM4_STM32F3) \
|
|
&& !defined(_ARCH_CORTEXM4_STM32L4)
|
|
c=port->DR; //clears interrupt flags
|
|
#else //_ARCH_CORTEXM7_STM32F7/H7
|
|
port->ICR=USART_ICR_IDLECF; //clears interrupt flags
|
|
#endif //_ARCH_CORTEXM7_STM32F7/H7
|
|
#ifdef SERIAL_DMA
|
|
if(dmaRx) IRQreadDma();
|
|
#endif //SERIAL_DMA
|
|
idle=true;
|
|
}
|
|
if((status & USART_SR_IDLE) || rxQueue.size()>=rxQueueMin)
|
|
{
|
|
//Enough data in buffer or idle line, awake thread
|
|
if(rxWaiting)
|
|
{
|
|
rxWaiting->IRQwakeup();
|
|
if(rxWaiting->IRQgetPriority()>
|
|
Thread::IRQgetCurrentThread()->IRQgetPriority())
|
|
Scheduler::IRQfindNextThread();
|
|
rxWaiting=0;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef SERIAL_DMA
|
|
void STM32Serial::IRQhandleDMAtx()
|
|
{
|
|
dmaTxInProgress=false;
|
|
if(txWaiting==0) return;
|
|
txWaiting->IRQwakeup();
|
|
if(txWaiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
|
|
Scheduler::IRQfindNextThread();
|
|
txWaiting=0;
|
|
}
|
|
|
|
void STM32Serial::IRQhandleDMArx()
|
|
{
|
|
IRQreadDma();
|
|
idle=false;
|
|
if(rxWaiting==0) return;
|
|
rxWaiting->IRQwakeup();
|
|
if(rxWaiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
|
|
Scheduler::IRQfindNextThread();
|
|
rxWaiting=0;
|
|
}
|
|
#endif //SERIAL_DMA
|
|
|
|
STM32Serial::~STM32Serial()
|
|
{
|
|
waitSerialTxFifoEmpty();
|
|
{
|
|
InterruptDisableLock dLock;
|
|
port->CR1=0;
|
|
int id=getId();
|
|
ports[id-1]=0;
|
|
switch(id)
|
|
{
|
|
case 1:
|
|
#ifdef SERIAL_1_DMA
|
|
IRQdmaReadStop();
|
|
#if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \
|
|
|| defined(_ARCH_CORTEXM4_STM32L4)
|
|
NVIC_DisableIRQ(DMA1_Channel4_IRQn);
|
|
NVIC_ClearPendingIRQ(DMA1_Channel4_IRQn);
|
|
NVIC_DisableIRQ(DMA1_Channel5_IRQn);
|
|
NVIC_ClearPendingIRQ(DMA1_Channel5_IRQn);
|
|
#else //stm32f2, stm32f4
|
|
NVIC_DisableIRQ(DMA2_Stream7_IRQn);
|
|
NVIC_ClearPendingIRQ(DMA2_Stream7_IRQn);
|
|
NVIC_DisableIRQ(DMA2_Stream5_IRQn);
|
|
NVIC_ClearPendingIRQ(DMA2_Stream5_IRQn);
|
|
#endif
|
|
#endif //SERIAL_1_DMA
|
|
NVIC_DisableIRQ(USART1_IRQn);
|
|
NVIC_ClearPendingIRQ(USART1_IRQn);
|
|
RCC->APB2ENR &= ~RCC_APB2ENR_USART1EN;
|
|
break;
|
|
|
|
#if !defined(STM32_NO_SERIAL_2_3)
|
|
case 2:
|
|
#ifdef SERIAL_2_DMA
|
|
IRQdmaReadStop();
|
|
#if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \
|
|
|| defined(_ARCH_CORTEXM4_STM32L4)
|
|
NVIC_DisableIRQ(DMA1_Channel7_IRQn);
|
|
NVIC_ClearPendingIRQ(DMA1_Channel7_IRQn);
|
|
NVIC_DisableIRQ(DMA1_Channel6_IRQn);
|
|
NVIC_ClearPendingIRQ(DMA1_Channel6_IRQn);
|
|
#else //stm32f2, stm32f4
|
|
NVIC_DisableIRQ(DMA1_Stream6_IRQn);
|
|
NVIC_ClearPendingIRQ(DMA1_Stream6_IRQn);
|
|
NVIC_DisableIRQ(DMA1_Stream5_IRQn);
|
|
NVIC_ClearPendingIRQ(DMA1_Stream5_IRQn);
|
|
#endif
|
|
#endif //SERIAL_2_DMA
|
|
NVIC_DisableIRQ(USART2_IRQn);
|
|
NVIC_ClearPendingIRQ(USART2_IRQn);
|
|
#ifndef _ARCH_CORTEXM7_STM32H7
|
|
#ifdef _ARCH_CORTEXM4_STM32L4
|
|
RCC->APB1ENR1 &= ~RCC_APB1ENR1_USART2EN;
|
|
#else
|
|
RCC->APB1ENR &= ~RCC_APB1ENR_USART2EN;
|
|
#endif
|
|
#else //_ARCH_CORTEXM7_STM32H7
|
|
RCC->APB1LENR &= ~RCC_APB1LENR_USART2EN;
|
|
#endif //_ARCH_CORTEXM7_STM32H7
|
|
break;
|
|
#if !defined(STM32F411xE) && !defined(STM32F401xE) && !defined(STM32F401xC)
|
|
case 3:
|
|
#ifdef SERIAL_3_DMA
|
|
IRQdmaReadStop();
|
|
#if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \
|
|
|| defined(_ARCH_CORTEXM4_STM32L4)
|
|
NVIC_DisableIRQ(DMA1_Channel2_IRQn);
|
|
NVIC_ClearPendingIRQ(DMA1_Channel2_IRQn);
|
|
NVIC_DisableIRQ(DMA1_Channel3_IRQn);
|
|
NVIC_ClearPendingIRQ(DMA1_Channel3_IRQn);
|
|
#else //stm32f2, stm32f4
|
|
NVIC_DisableIRQ(DMA1_Stream3_IRQn);
|
|
NVIC_ClearPendingIRQ(DMA1_Stream3_IRQn);
|
|
NVIC_DisableIRQ(DMA1_Stream1_IRQn);
|
|
NVIC_ClearPendingIRQ(DMA1_Stream1_IRQn);
|
|
#endif
|
|
#endif //SERIAL_3_DMA
|
|
#if !defined(STM32F072xB)
|
|
NVIC_SetPriority(USART3_IRQn,15);//Lowest priority for serial
|
|
NVIC_EnableIRQ(USART3_IRQn);
|
|
#else //STM32F072xB
|
|
NVIC_SetPriority(USART3_4_IRQn,15);
|
|
NVIC_EnableIRQ(USART3_4_IRQn);
|
|
#endif //STM32F072xB
|
|
#ifndef _ARCH_CORTEXM7_STM32H7
|
|
#ifdef _ARCH_CORTEXM4_STM32L4
|
|
RCC->APB1ENR1 &= ~RCC_APB1ENR1_USART3EN;
|
|
#else
|
|
RCC->APB1ENR &= ~RCC_APB1ENR_USART3EN;
|
|
#endif
|
|
#else //_ARCH_CORTEXM7_STM32H7
|
|
RCC->APB1LENR &= ~RCC_APB1LENR_USART3EN;
|
|
#endif //_ARCH_CORTEXM7_STM32H7
|
|
break;
|
|
#endif //!defined(STM32F411xE) && !defined(STM32F401xE) && !defined(STM32F401xC)
|
|
#endif //!defined(STM32_NO_SERIAL_2_3)
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef SERIAL_DMA
|
|
void STM32Serial::waitDmaTxCompletion()
|
|
{
|
|
FastInterruptDisableLock dLock;
|
|
// If a previous DMA xfer is in progress, wait
|
|
if(dmaTxInProgress)
|
|
{
|
|
txWaiting=Thread::IRQgetCurrentThread();
|
|
do {
|
|
Thread::IRQwait();
|
|
{
|
|
FastInterruptEnableLock eLock(dLock);
|
|
Thread::yield();
|
|
}
|
|
} while(txWaiting);
|
|
}
|
|
}
|
|
|
|
void STM32Serial::writeDma(const char *buffer, size_t size)
|
|
{
|
|
markBufferBeforeDmaWrite(buffer,size);
|
|
//Quirk: DMA messes up the TC bit, and causes waitSerialTxFifoEmpty() to
|
|
//return prematurely, causing characters to be missed when rebooting
|
|
//immediatley a write. You can just clear the bit manually, but doing that
|
|
//is dangerous, as if you clear the bit but for any reason the serial
|
|
//write doesn't start (think an invalid buffer, or another thread crashing),
|
|
//then TC will never be set and waitSerialTxFifoEmpty() deadlocks!
|
|
//The only way to clear it safely is to first read SR and then write to
|
|
//DR (thus the bit is cleared at the same time a transmission is started,
|
|
//and the race condition is eliminated). This is the purpose of this
|
|
//instruction, it reads SR. When we start the DMA, the DMA controller
|
|
//writes to DR and completes the TC clear sequence.
|
|
#if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
|
|
&& !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM4_STM32F3) \
|
|
&& !defined(_ARCH_CORTEXM4_STM32L4)
|
|
while((port->SR & USART_SR_TXE)==0) ;
|
|
#else //_ARCH_CORTEXM7_STM32F7/H7
|
|
while((port->ISR & USART_ISR_TXE)==0) ;
|
|
#endif //_ARCH_CORTEXM7_STM32F7/H7
|
|
|
|
dmaTxInProgress=true;
|
|
#if defined(_ARCH_CORTEXM3_STM32)
|
|
dmaTx->CPAR=reinterpret_cast<unsigned int>(&port->DR);
|
|
dmaTx->CMAR=reinterpret_cast<unsigned int>(buffer);
|
|
dmaTx->CNDTR=size;
|
|
dmaTx->CCR=DMA_CCR4_MINC //Increment RAM pointer
|
|
| DMA_CCR4_DIR //Memory to peripheral
|
|
| DMA_CCR4_TEIE //Interrupt on transfer error
|
|
| DMA_CCR4_TCIE //Interrupt on transfer complete
|
|
| DMA_CCR4_EN; //Start DMA
|
|
#else
|
|
#if defined(_ARCH_CORTEXM4_STM32F3) || defined(_ARCH_CORTEXM4_STM32L4)
|
|
dmaTx->CPAR=reinterpret_cast<unsigned int>(&port->TDR);
|
|
dmaTx->CMAR=reinterpret_cast<unsigned int>(buffer);
|
|
dmaTx->CNDTR=size;
|
|
dmaTx->CCR=DMA_CCR_MINC //Increment RAM pointer
|
|
| DMA_CCR_DIR //Memory to peripheral
|
|
| DMA_CCR_TEIE //Interrupt on transfer error
|
|
| DMA_CCR_TCIE //Interrupt on transfer complete
|
|
| DMA_CCR_EN; //Start DMA
|
|
#else //_ARCH_CORTEXM4_STM32F3
|
|
#if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
|
|
&& !defined(_ARCH_CORTEXM0_STM32)
|
|
dmaTx->PAR=reinterpret_cast<unsigned int>(&port->DR);
|
|
#else //_ARCH_CORTEXM7_STM32F7/H7
|
|
dmaTx->PAR=reinterpret_cast<unsigned int>(&port->TDR);
|
|
#endif //_ARCH_CORTEXM7_STM32F7/H7
|
|
dmaTx->M0AR=reinterpret_cast<unsigned int>(buffer);
|
|
dmaTx->NDTR=size;
|
|
//Quirk: not enabling DMA_SxFCR_FEIE because the USART seems to
|
|
//generate a spurious fifo error. The code was tested and the
|
|
//transfer completes successfully even in the presence of this fifo
|
|
//error
|
|
dmaTx->FCR=DMA_SxFCR_DMDIS;//Enable fifo
|
|
dmaTx->CR=DMA_SxCR_CHSEL_2 //Select channel 4 (USART_TX)
|
|
| DMA_SxCR_MINC //Increment RAM pointer
|
|
| DMA_SxCR_DIR_0 //Memory to peripheral
|
|
| DMA_SxCR_TCIE //Interrupt on completion
|
|
| DMA_SxCR_TEIE //Interrupt on transfer error
|
|
| DMA_SxCR_DMEIE //Interrupt on direct mode error
|
|
| DMA_SxCR_EN; //Start the DMA
|
|
#endif //_ARCH_CORTEXM4_STM32F3
|
|
#endif //_ARCH_CORTEXM3_STM32
|
|
}
|
|
|
|
void STM32Serial::IRQreadDma()
|
|
{
|
|
int elem=IRQdmaReadStop();
|
|
markBufferAfterDmaRead(rxBuffer,rxQueueMin);
|
|
for(int i=0;i<elem;i++)
|
|
if(rxQueue.tryPut(rxBuffer[i])==false) /*fifo overflow*/;
|
|
IRQdmaReadStart();
|
|
}
|
|
|
|
void STM32Serial::IRQdmaReadStart()
|
|
{
|
|
#if defined(_ARCH_CORTEXM3_STM32)
|
|
dmaRx->CPAR=reinterpret_cast<unsigned int>(&port->DR);
|
|
dmaRx->CMAR=reinterpret_cast<unsigned int>(rxBuffer);
|
|
dmaRx->CNDTR=rxQueueMin;
|
|
dmaRx->CCR=DMA_CCR4_MINC //Increment RAM pointer
|
|
| 0 //Peripheral to memory
|
|
| DMA_CCR4_TEIE //Interrupt on transfer error
|
|
| DMA_CCR4_TCIE //Interrupt on transfer complete
|
|
| DMA_CCR4_EN; //Start DMA
|
|
#else
|
|
#if defined(_ARCH_CORTEXM4_STM32F3) || defined(_ARCH_CORTEXM4_STM32L4)
|
|
dmaRx->CPAR=reinterpret_cast<unsigned int>(&port->RDR);
|
|
dmaRx->CMAR=reinterpret_cast<unsigned int>(rxBuffer);
|
|
dmaRx->CNDTR=rxQueueMin;
|
|
dmaRx->CCR=DMA_CCR_MINC //Increment RAM pointer
|
|
| 0 //Peripheral to memory
|
|
| DMA_CCR_TEIE //Interrupt on transfer error
|
|
| DMA_CCR_TCIE //Interrupt on transfer complete
|
|
| DMA_CCR_EN; //Start DMA
|
|
#else //_ARCH_CORTEXM4_STM32F3
|
|
#if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
|
|
&& !defined(_ARCH_CORTEXM0_STM32)
|
|
dmaRx->PAR=reinterpret_cast<unsigned int>(&port->DR);
|
|
#else //_ARCH_CORTEXM7_STM32F7/H7
|
|
dmaRx->PAR=reinterpret_cast<unsigned int>(&port->RDR);
|
|
#endif //_ARCH_CORTEXM7_STM32F7/H7
|
|
dmaRx->M0AR=reinterpret_cast<unsigned int>(rxBuffer);
|
|
dmaRx->NDTR=rxQueueMin;
|
|
dmaRx->CR=DMA_SxCR_CHSEL_2 //Select channel 4 (USART_RX)
|
|
| DMA_SxCR_MINC //Increment RAM pointer
|
|
| 0 //Peripheral to memory
|
|
| DMA_SxCR_HTIE //Interrupt on half transfer
|
|
| DMA_SxCR_TEIE //Interrupt on transfer error
|
|
| DMA_SxCR_DMEIE //Interrupt on direct mode error
|
|
| DMA_SxCR_EN; //Start the DMA
|
|
#endif //_ARCH_CORTEXM4_STM32F3
|
|
#endif //_ARCH_CORTEXM3_STM32
|
|
}
|
|
|
|
int STM32Serial::IRQdmaReadStop()
|
|
{
|
|
#if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \
|
|
|| defined(_ARCH_CORTEXM4_STM32L4)
|
|
dmaRx->CCR=0;
|
|
static const unsigned int irqMask[]=
|
|
{
|
|
DMA_IFCR_CGIF5,
|
|
DMA_IFCR_CGIF6,
|
|
DMA_IFCR_CGIF3
|
|
};
|
|
DMA1->IFCR=irqMask[getId()-1];
|
|
return rxQueueMin-dmaRx->CNDTR;
|
|
#else //_ARCH_CORTEXM3_STM32
|
|
//Stop DMA and wait for it to actually stop
|
|
dmaRx->CR &= ~DMA_SxCR_EN;
|
|
while(dmaRx->CR & DMA_SxCR_EN) ;
|
|
static const unsigned int irqMask[]=
|
|
{
|
|
(DMA_HIFCR_CTCIF5 | DMA_HIFCR_CHTIF5 | DMA_HIFCR_CTEIF5 | DMA_HIFCR_CDMEIF5 | DMA_HIFCR_CFEIF5),
|
|
(DMA_HIFCR_CTCIF5 | DMA_HIFCR_CHTIF5 | DMA_HIFCR_CTEIF5 | DMA_HIFCR_CDMEIF5 | DMA_HIFCR_CFEIF5),
|
|
(DMA_LIFCR_CTCIF1 | DMA_LIFCR_CHTIF1 | DMA_LIFCR_CTEIF1 | DMA_LIFCR_CDMEIF1 | DMA_LIFCR_CFEIF1)
|
|
};
|
|
static volatile unsigned long * const irqRegs[]=
|
|
{
|
|
&DMA2->HIFCR,
|
|
&DMA1->HIFCR,
|
|
&DMA1->LIFCR
|
|
};
|
|
*irqRegs[getId()-1]=irqMask[getId()-1];
|
|
return rxQueueMin-dmaRx->NDTR;
|
|
#endif //_ARCH_CORTEXM3_STM32
|
|
}
|
|
#endif //SERIAL_DMA
|
|
|
|
} //namespace miosix
|