/*************************************************************************** * 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 * ***************************************************************************/ #ifndef SERIAL_STM32_H #define SERIAL_STM32_H #include "filesystem/console/console_device.h" #include "kernel/sync.h" #include "kernel/queue.h" #include "interfaces/gpio.h" #include "board_settings.h" #if defined(_ARCH_CORTEXM3_STM32) && defined(__ENABLE_XRAM) //Quirk: concurrent access to the FSMC from both core and DMA is broken in //the stm32f1, so disable DMA mode if XRAM is enabled. #undef SERIAL_1_DMA #undef SERIAL_2_DMA #undef SERIAL_3_DMA #endif #if defined(SERIAL_1_DMA) || defined(SERIAL_2_DMA) || defined(SERIAL_3_DMA) #define SERIAL_DMA #endif #if defined(SERIAL_DMA) && defined(_ARCH_CORTEXM0_STM32) #undef SERIAL_1_DMA #undef SERIAL_2_DMA #undef SERIAL_3_DMA #undef SERIAL_DMA #warning "DMA not yet implemented for STM32F0 family" #endif namespace miosix { /** * Serial port class for stm32 microcontrollers. * Only supports USART1, USART2 and USART3 * Additionally, USARTx can use DMA if SERIAL_x_DMA is defined in * board_settings.h, while the other serial use polling for transmission, * and interrupt for reception. * * Classes of this type are reference counted, must be allocated on the heap * and managed through intrusive_ref_ptr */ class STM32Serial : public Device { public: enum FlowCtrl { NOFLOWCTRL, ///< No hardware flow control RTSCTS ///< RTS/CTS hardware flow control }; /** * Constructor, initializes the serial port using the default pins, which * are: * USART1: tx=PA9 rx=PA10 cts=PA11 rts=PA12 * USART2: tx=PA2 rx=PA3 cts=PA0 rts=PA1 * USART3: tx=PB10 rx=PB11 cts=PB13 rts=PB14 * If you board has a different mapping, use one of the other constructors. * * Calls errorHandler(UNEXPECTED) if id is not in the correct range, or when * attempting to construct multiple objects with the same id. That is, * it is possible to instantiate only one instance of this class for each * hardware USART. * \param id a number 1 to 3 to select which USART * \param baudrate serial port baudrate * \param flowControl to enable hardware flow control on this port */ STM32Serial(int id, int baudrate, FlowCtrl flowControl=NOFLOWCTRL); /** * Constructor, initializes the serial port using remapped pins and disables * flow control. * * NOTE: for stm32f2, f4, f7 and h7 you have to set the correct alternate * function to the pins in order to connect then to the USART peripheral * before passing them to this class. * * Calls errorHandler(UNEXPECTED) if id is not in the correct range, or when * attempting to construct multiple objects with the same id. That is, * it is possible to instantiate only one instance of this class for each * hardware USART. * \param id a number 1 to 3 to select which USART * \param baudrate serial port baudrate * \param tx tx pin * \param rx rx pin */ STM32Serial(int id, int baudrate, miosix::GpioPin tx, miosix::GpioPin rx); /** * Constructor, initializes the serial port using remapped pins and enables * flow control. * * NOTE: for stm32f2, f4, f7 and h7 you have to set the correct alternate * function to the pins in order to connect then to the USART peripheral * before passing them to this class. * * Calls errorHandler(UNEXPECTED) if id is not in the correct range, or when * attempting to construct multiple objects with the same id. That is, * it is possible to instantiate only one instance of this class for each * hardware USART. * \param id a number 1 to 3 to select which USART * \param tx tx pin * \param rx rx pin * \param rts rts pin * \param cts cts pin */ STM32Serial(int id, int baudrate, miosix::GpioPin tx, miosix::GpioPin rx, miosix::GpioPin rts, miosix::GpioPin cts); /** * Read a block of data * \param buffer buffer where read data will be stored * \param size buffer size * \param where where to read from * \return number of bytes read or a negative number on failure. Note that * it is normal for this function to return less character than the amount * asked */ ssize_t readBlock(void *buffer, size_t size, off_t where); /** * Write a block of data * \param buffer buffer where take data to write * \param size buffer size * \param where where to write to * \return number of bytes written or a negative number on failure */ ssize_t writeBlock(const void *buffer, size_t size, off_t where); /** * Write a string. * An extension to the Device interface that adds a new member function, * which is used by the kernel on console devices to write debug information * before the kernel is started or in case of serious errors, right before * rebooting. * Can ONLY be called when the kernel is not yet started, paused or within * an interrupt. This default implementation ignores writes. * \param str the string to write. The string must be NUL terminated. */ void IRQwrite(const char *str); /** * Performs device-specific operations * \param cmd specifies the operation to perform * \param arg optional argument that some operation require * \return the exact return value depends on CMD, -1 is returned on error */ int ioctl(int cmd, void *arg); /** * \internal the serial port interrupts call this member function. * Never call this from user code. */ void IRQhandleInterrupt(); #ifdef SERIAL_DMA /** * \internal the serial port DMA tx interrupts call this member function. * Never call this from user code. */ void IRQhandleDMAtx(); /** * \internal the serial port DMA rx interrupts call this member function. * Never call this from user code. */ void IRQhandleDMArx(); #endif //SERIAL_DMA /** * \return port id, 1 for USART1, 2 for USART2, ... */ int getId() const { return portId; } /** * Destructor */ ~STM32Serial(); private: /** * Code common for all constructors */ void commonInit(int id, int baudrate, miosix::GpioPin tx, miosix::GpioPin rx, miosix::GpioPin rts, miosix::GpioPin cts); #ifdef SERIAL_DMA /** * Wait until a pending DMA TX completes, if any */ void waitDmaTxCompletion(); /** * Write to the serial port using DMA. When the function returns, the DMA * transfer is still in progress. * \param buffer buffer to write * \param size size of buffer to write */ void writeDma(const char *buffer, size_t size); /** * Read from DMA buffer and write data to queue */ void IRQreadDma(); /** * Start DMA read */ void IRQdmaReadStart(); /** * Stop DMA read * \return the number of characters in rxBuffer */ int IRQdmaReadStop(); #endif //SERIAL_DMA /** * Wait until all characters have been written to the serial port. * Needs to be callable from interrupts disabled (it is used in IRQwrite) */ void waitSerialTxFifoEmpty() { #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_TC)==0) ; #else //_ARCH_CORTEXM7_STM32F7/H7 while((port->ISR & USART_ISR_TC)==0) ; #endif //_ARCH_CORTEXM7_STM32F7/H7 } FastMutex txMutex; ///< Mutex locked during transmission FastMutex rxMutex; ///< Mutex locked during reception DynUnsyncQueue rxQueue; ///< Receiving queue static const unsigned int rxQueueMin=16; ///< Minimum queue size Thread *rxWaiting=0; ///< Thread waiting for rx, or 0 USART_TypeDef *port; ///< Pointer to USART peripheral #ifdef SERIAL_DMA #if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \ || defined(_ARCH_CORTEXM4_STM32L4) DMA_Channel_TypeDef *dmaTx; ///< Pointer to DMA TX peripheral DMA_Channel_TypeDef *dmaRx; ///< Pointer to DMA RX peripheral #else //_ARCH_CORTEXM3_STM32 and _ARCH_CORTEXM4_STM32F3 DMA_Stream_TypeDef *dmaTx; ///< Pointer to DMA TX peripheral DMA_Stream_TypeDef *dmaRx; ///< Pointer to DMA RX peripheral #endif //_ARCH_CORTEXM3_STM32 and _ARCH_CORTEXM4_STM32F3 Thread *txWaiting; ///< Thread waiting for tx, or 0 static const unsigned int txBufferSize=16; ///< Size of tx buffer, for tx speedup /// Tx buffer, for tx speedup. This buffer must not end up in the CCM of the /// STM32F4, as it is used to perform DMA operations. This is guaranteed by /// the fact that this class must be allocated on the heap as it derives /// from Device, and the Miosix linker scripts never put the heap in CCM char txBuffer[txBufferSize]; /// This buffer emulates the behaviour of a 16550. It is filled using DMA /// and an interrupt is fired as soon as it is half full char rxBuffer[rxQueueMin]; bool dmaTxInProgress; ///< True if a DMA tx is in progress #endif //SERIAL_DMA bool idle=true; ///< Receiver idle const bool flowControl; ///< True if flow control GPIOs enabled const unsigned char portId; ///< 1 for USART1, 2 for USART2, ... }; } //namespace miosix #endif //SERIAL_STM32_H