/*************************************************************************** * 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 * ***************************************************************************/ #include #include #include #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 u1tx; typedef Gpio u1rx; typedef Gpio u1cts; typedef Gpio u1rts; typedef Gpio u2tx; typedef Gpio u2rx; typedef Gpio u2cts; typedef Gpio u2rts; typedef Gpio u3tx; typedef Gpio u3rx; typedef Gpio u3cts; typedef Gpio 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(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 l(rxMutex); char *buf=reinterpret_cast(buffer); size_t result=0; FastInterruptDisableLock dLock; for(;;) { //Try to get data from the queue for(;result0) 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 l(txMutex); const char *buf=reinterpret_cast(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(remaining-txBufferSize,65535); waitDmaTxCompletion(); writeDma(buf,transferSize); buf+=transferSize; remaining-=transferSize; } } while(remaining>0) { size_t transferSize=min(remaining,static_cast(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;iSR & 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(arg) & 0b11) return -EFAULT; //Unaligned termios *t=reinterpret_cast(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(&port->DR); dmaTx->CMAR=reinterpret_cast(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(&port->TDR); dmaTx->CMAR=reinterpret_cast(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(&port->DR); #else //_ARCH_CORTEXM7_STM32F7/H7 dmaTx->PAR=reinterpret_cast(&port->TDR); #endif //_ARCH_CORTEXM7_STM32F7/H7 dmaTx->M0AR=reinterpret_cast(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;iCPAR=reinterpret_cast(&port->DR); dmaRx->CMAR=reinterpret_cast(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(&port->RDR); dmaRx->CMAR=reinterpret_cast(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(&port->DR); #else //_ARCH_CORTEXM7_STM32F7/H7 dmaRx->PAR=reinterpret_cast(&port->RDR); #endif //_ARCH_CORTEXM7_STM32F7/H7 dmaRx->M0AR=reinterpret_cast(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