/** * \file * * * \brief AVR XMEGA USART driver (Implementation) * * This file is heavily inspired by the AVR implementation for BeRTOS, * but uses a different approach for implementing the different debug * ports, by using the USART_t structs. * * \author Onno * notest:all */ #include "hw/hw_ser.h" /* Required for bus macros overrides */ #include /* CPU_FREQ */ #include "cfg/cfg_ser.h" /* Serialport configuration settings */ #include /* DIV_ROUND */ #include /* debug configuration */ #include #include #include #include #include /* AVR IO ports and structures */ #include /* AVR Interrupt methods */ /* * Scalefactor to use for computing the baudrate * this scalefactor should be an integer value between -7 * and 7 */ #ifndef USART_SCALE_FACTOR #define USART_SCALE_FACTOR (-7) #else #if USART_SCALE_FACTOR > 7 || USART_SCALE_FACTOR < -7 #error USART_SCALE_FACTOR should be an integer between -7 and 7 #endif #endif /* Helper macros, mostly taken from the Atmel Examples * Slightly alterd to match the BeRTOS naming convention */ /* \brief Set USART baud rate. * * Sets the USART's baud rate register. * * UBRR_Value : Value written to UBRR * ScaleFactor : Time Base Generator Scale Factor * * Equation for calculation of BSEL value in asynchronous normal speed mode: * If ScaleFactor >= 0 * BSEL = ((I/O clock frequency)/(2^(ScaleFactor)*16*Baudrate))-1 * If ScaleFactor < 0 * BSEL = (1/(2^(ScaleFactor)*16))*(((I/O clock frequency)/Baudrate)-1) * * \note See XMEGA manual for equations for calculation of BSEL value in other * modes. * * \param _usart Pointer to the USART module. * \param _bselValue Value to write to BSEL part of Baud control register. * Use uint16_t type. * \param _bScaleFactor USART baud rate scale factor. * Use uint8_t type */ #define USART_SET_BAUDRATE(_usart, _bselValue, _bScaleFactor) \ (_usart)->BAUDCTRLA =(uint8_t)_bselValue; \ (_usart)->BAUDCTRLB =(_bScaleFactor << USART_BSCALE0_bp)|(_bselValue >> 8) /* \brief Enable USART receiver. * * \param _usart Pointer to the USART module */ #define USART_RX_ENABLE(_usart) ((_usart)->CTRLB |= USART_RXEN_bm) /* \brief Disable USART receiver. * * \param _usart Pointer to the USART module. */ #define USART_RX_DISABLE(_usart) ((_usart)->CTRLB &= ~USART_RXEN_bm) /* \brief Enable USART transmitter. * * \param _usart Pointer to the USART module. */ #define USART_TX_ENABLE(_usart) ((_usart)->CTRLB |= USART_TXEN_bm) /* \brief Disable USART transmitter. * * \param _usart Pointer to the USART module. */ #define USART_TX_DISABLE(_usart) ((_usart)->CTRLB &= ~USART_TXEN_bm) /* \brief Set USART RXD interrupt level. * * Sets the interrupt level on RX Complete interrupt. * * \param _usart Pointer to the USART module. * \param _rxdIntLevel Interrupt level of the RXD interrupt. * Use USART_RXCINTLVL_t type. */ #define USART_SET_RX_INTERRUPT_LEVEL(_usart, _rxdIntLevel) \ ((_usart)->CTRLA = ((_usart)->CTRLA & ~USART_RXCINTLVL_gm) | _rxdIntLevel) /* \brief Set USART TXD interrupt level. * * Sets the interrupt level on TX Complete interrupt. * * \param _usart Pointer to the USART module. * \param _txdIntLevel Interrupt level of the TXD interrupt. * Use USART_TXCINTLVL_t type. */ #define USART_SET_TX_INTERRUPT_LEVEL(_usart, _txdIntLevel) \ (_usart)->CTRLA = ((_usart)->CTRLA & ~USART_TXCINTLVL_gm) | _txdIntLevel /* \brief Set USART DRE interrupt level. * * Sets the interrupt level on Data Register interrupt. * * \param _usart Pointer to the USART module. * \param _dreIntLevel Interrupt level of the DRE interrupt. * Use USART_DREINTLVL_t type. */ #define USART_SET_DRE_INTERRUPT_LEVEL(_usart, _dreIntLevel) \ (_usart)->CTRLA = ((_usart)->CTRLA & ~USART_DREINTLVL_gm) | _dreIntLevel /* \brief Set the mode the USART run in. * * Set the mode the USART run in. The default mode is asynchronous mode. * * \param _usart Pointer to the USART module register section. * \param _usartMode Selects the USART mode. Use USART_CMODE_t type. * * USART modes: * - 0x0 : Asynchronous mode. * - 0x1 : Synchronous mode. * - 0x2 : IrDA mode. * - 0x3 : Master SPI mode. */ #define USART_SET_MODE(_usart, _usartMode) \ ((_usart)->CTRLC = ((_usart)->CTRLC & (~USART_CMODE_gm)) | _usartMode) /* \brief Check if data register empty flag is set. * * \param _usart The USART module. */ #define USART_IS_TX_DATA_REGISTER_EMPTY(_usart) (((_usart)->STATUS & USART_DREIF_bm) != 0) /* \brief Put data (5-8 bit character). * * Use the macro USART_IsTXDataRegisterEmpty before using this function to * put data to the TX register. * * \param _usart The USART module. * \param _data The data to send. */ #define USART_PUT_CHAR(_usart, _data) ((_usart)->DATA = _data) /* \brief Checks if the RX complete interrupt flag is set. * * Checks if the RX complete interrupt flag is set. * * \param _usart The USART module. */ #define USART_IS_RX_COMPLETE(_usart) (((_usart)->STATUS & USART_RXCIF_bm) != 0) /* \brief Get received data (5-8 bit character). * * This macro reads out the RX register. * Use the macro USART_RX_Complete to check if anything is received. * * \param _usart The USART module. * * \retval Received data. */ #define USART_GET_CHAR(_usart) ((_usart)->DATA) /* configurable macros */ #if !CONFIG_SER_HWHANDSHAKE /** * \name Hardware handshake (RTS/CTS). * \{ */ #define RTS_ON do {} while (0) #define RTS_OFF do {} while (0) #define IS_CTS_ON true #define EIMSKF_CTS 0 /**< Dummy value, must be overridden */ /*\}*/ #endif /* * \name Overridable serial bus hooks * * These can be redefined in hw.h to implement * special bus policies such as half-duplex, 485, etc. * * * \code * TXBEGIN TXCHAR TXEND TXOFF * | __________|__________ | | * | | | | | | | | | * v v v v v v v v v * ______ __ __ __ __ __ __ ________________ * \/ \/ \/ \/ \/ \/ \/ * ______/\__/\__/\__/\__/\__/\__/ * * \endcode * * \{ */ #ifndef SER_UART_BUS_TXINIT /* * Default TXINIT macro - invoked in uart_init() * * - Enable both the receiver and the transmitter * - Enable only the RX complete interrupt */ #define SER_UART_BUS_TXINIT(_usart) do { \ USART_RX_ENABLE(_usart); \ USART_TX_ENABLE(_usart); \ USART_SET_RX_INTERRUPT_LEVEL(_usart, USART_RXCINTLVL_MED_gc); \ } while (0) #endif #ifndef SER_UART_BUS_TXBEGIN /* * Invoked before starting a transmission * * - Enable both the receiver and the transmitter * - Enable both the RX complete and UDR empty interrupts */ #define SER_UART_BUS_TXBEGIN(_usart) do { \ USART_SET_RX_INTERRUPT_LEVEL(_usart, USART_RXCINTLVL_MED_gc); \ USART_SET_DRE_INTERRUPT_LEVEL(_usart, USART_DREINTLVL_MED_gc);\ } while (0) #endif #ifndef SER_UART_BUS_TXCHAR /* * Invoked to send one character. */ #define SER_UART_BUS_TXCHAR(_usart, c) do { \ USART_PUT_CHAR(_usart, c); \ } while (0) #endif #ifndef SER_UART_BUS_TXEND /* * Invoked as soon as the txfifo becomes empty * * - Keep both the receiver and the transmitter enabled * - Keep the RX complete interrupt enabled * - Disable the UDR empty interrupt */ #define SER_UART_BUS_TXEND(_usart) do { \ USART_SET_DRE_INTERRUPT_LEVEL(_usart, USART_DREINTLVL_OFF_gc); \ } while (0) #endif #ifndef SER_UART_BUS_TXOFF /* * \def SER_UART_BUS_TXOFF * * Invoked after the last character has been transmitted * * The default is no action. */ #ifdef __doxygen__ #define SER_UART_BUS_TXOFF(_usart) #endif #endif /*\}*/ /* From the high-level serial driver */ extern struct Serial *ser_handles[SER_CNT]; /* TX and RX buffers */ static unsigned char uart0_txbuffer[CONFIG_UART0_TXBUFSIZE]; static unsigned char uart0_rxbuffer[CONFIG_UART0_RXBUFSIZE]; static unsigned char uart1_txbuffer[CONFIG_UART1_TXBUFSIZE]; static unsigned char uart1_rxbuffer[CONFIG_UART1_RXBUFSIZE]; #ifdef CPU_AVR_XMEGA_A static unsigned char uart2_txbuffer[CONFIG_UART2_TXBUFSIZE]; static unsigned char uart2_rxbuffer[CONFIG_UART2_RXBUFSIZE]; static unsigned char uart3_txbuffer[CONFIG_UART3_TXBUFSIZE]; static unsigned char uart3_rxbuffer[CONFIG_UART3_RXBUFSIZE]; static unsigned char uart4_txbuffer[CONFIG_UART4_TXBUFSIZE]; static unsigned char uart4_rxbuffer[CONFIG_UART4_RXBUFSIZE]; #endif /* * Internal hardware state structure * * The \a sending variable is true while the transmission * interrupt is retriggering itself. * * the \a usart variable will point to the USART_t structure * that should be used. * * the \a port variable will point to the PORT_t structure * that should be modified to set the tx pin as an output and the * rx pin as an input * * the \a txpin variable will hold the pinnumber of the pin to use * as the tx output * * the \a rxpin variable will hold the pinnumber of the pin to use * as the rx input * * For the USARTs the \a sending flag is useful for taking specific * actions before sending a burst of data, at the start of a trasmission * but not before every char sent. * * For the SPI, this flag is necessary because the SPI sends and receives * bytes at the same time and the SPI IRQ is unique for send/receive. * The only way to start transmission is to write data in SPDR (this * is done by spi_starttx()). We do this *only* if a transfer is * not already started. */ struct AvrxmegaSerial { struct SerialHardware hw; volatile bool sending; volatile USART_t* usart; volatile PORT_t* port; uint8_t txpin; uint8_t rxpin; }; /* * Callbacks * The same callbacks are used for all USARTS. * By casting the SerialHardware structure to the AvrxmegaSerial * structure a pointer to the USART_t structure can be obtained, * to perform the callback for the specific USART. * This methode might cost some more cpu time, but saves on * code duplication and code size. */ /* * \brief Initializes the uart * * The TX pin of the uart will be set as an outputpin * The RX pin of the uart will be set as an inputpin * The usart will be initialized * \see SER_UART_BUS_TXINIT * * \param _hw struct AvrxmegaSerial * \param ser Unused */ static void uart_init(struct SerialHardware * _hw, UNUSED_ARG(struct Serial *, ser)) { struct AvrxmegaSerial *hw = (struct AvrxmegaSerial *)_hw; //set transmit pin as output hw->port->DIRSET = BV(hw->txpin); hw->port->OUTCLR = BV(hw->txpin); //set receive pin as input hw->port->DIRCLR = BV(hw->rxpin); //initialize the USART SER_UART_BUS_TXINIT(hw->usart); RTS_ON; SER_STROBE_INIT; } /* * \brief Cleans up / Disables the uart * * \param _hw struct AvrxmegaSerial */ static void uart_cleanup(struct SerialHardware * _hw) { struct AvrxmegaSerial *hw = (struct AvrxmegaSerial *)_hw; hw->usart->CTRLA = 0; hw->usart->CTRLB = 0; } /* * \brief Enableds the TX interrupt * * \param _hw struct AvrxmegaSerial */ static void uart_enabletxirq(struct SerialHardware *_hw) { struct AvrxmegaSerial *hw = (struct AvrxmegaSerial *)_hw; /* * WARNING: racy code here! The tx interrupt sets hw->sending to false * when it runs with an empty fifo. The order of statements in the * if-block matters. */ if (!hw->sending) { hw->sending = true; SER_UART_BUS_TXBEGIN(hw->usart); } } /* * \brief sets the uart to the provided baudrate * * For setting the baudrate an scale factor (bscale) and a period * setting (BSEL) is required. * * The scale factor should be privided by defining USART_SCALE_FACTOR * * Atmel specifies BSEL for normal speed mode and bscale >= 0 as: * BSEL = (cpu_freq / ((2^bscale) * 16 * rate)) - 1 * To allow BSEL to be calculated with an power function this can be * rewriten to: * BSEL = BSEL = (cpu_freq / ((1 << bscale) * 16 * rate)) - 1 * * Atmel specifies BSEL for normal speed mode and bscale < 0 as: * BSEL = (1 / (2^bscale)) * ( (cpu_freq / (16 * rate)) - 1) * To calculte this float atheritmic is required as the second product will be smaller * than zero in a lot of cases. * To allow BSEL to be calculated with interger devision and no power function * this can be rewriten by folowing simple math rules to: * BSEL = ((1 << -bscale) * (cpu_freq - (16 * rate)) / (16 * rate) * * \param _hw struct AvrxmegaSerial * \param _rate the required baudrate * */ static void uart_setbaudrate(struct SerialHardware * _hw, unsigned long _rate) { struct AvrxmegaSerial *hw = (struct AvrxmegaSerial *)_hw; /* Compute baud-rate period, this requires a valid USART_SCALE_FACTOR */ #if USART_SCALE_FACTOR < 0 uint16_t bsel = DIV_ROUND((1 << (-(USART_SCALE_FACTOR))) * (CPU_FREQ - (16 * _rate)), 16 * _rate); #else uint16_t bsel = DIV_ROUND(CPU_FREQ, (1 << (USART_SCALE_FACTOR)) * 16 * _rate) - 1; #endif USART_SET_BAUDRATE(hw->usart, bsel, USART_SCALE_FACTOR); } /* * \brief Sets the parity of the uart * * \param _hw struct AvrxmegaSerial * \param _parity the parity to set */ static void uart_setparity(struct SerialHardware * _hw, int _parity) { struct AvrxmegaSerial *hw = (struct AvrxmegaSerial *)_hw; USART_SET_MODE(hw->usart, _parity); } /* * \brief Returns true if Transmitter is sending * * \param _hw struct AvrxmegaSerial * \return true if transmitter is sending */ static bool tx_sending(struct SerialHardware* _hw) { struct AvrxmegaSerial *hw = (struct AvrxmegaSerial *)_hw; return hw->sending; } // FIXME: move into compiler.h? Ditch? #if COMPILER_C99 #define C99INIT(name,val) .name = val #elif defined(__GNUC__) #define C99INIT(name,val) name: val #else #warning No designated initializers, double check your code #define C99INIT(name,val) (val) #endif /* * High-level interface data structures */ static const struct SerialHardwareVT UART_VT = { C99INIT(init, uart_init), C99INIT(cleanup, uart_cleanup), C99INIT(setBaudrate, uart_setbaudrate), C99INIT(setParity, uart_setparity), C99INIT(txStart, uart_enabletxirq), C99INIT(txSending, tx_sending) }; static struct AvrxmegaSerial UARTDescs[SER_CNT] = { { C99INIT(hw, /**/) { C99INIT(table, &UART_VT), C99INIT(txbuffer, uart0_txbuffer), C99INIT(rxbuffer, uart0_rxbuffer), C99INIT(txbuffer_size, sizeof(uart0_txbuffer)), C99INIT(rxbuffer_size, sizeof(uart0_rxbuffer)), }, C99INIT(sending, false), C99INIT(usart, &USARTC0), C99INIT(port, &PORTC), C99INIT(txpin, PIN3_bp), C99INIT(rxpin, PIN2_bp), }, { C99INIT(hw, /**/) { C99INIT(table, &UART_VT), C99INIT(txbuffer, uart1_txbuffer), C99INIT(rxbuffer, uart1_rxbuffer), C99INIT(txbuffer_size, sizeof(uart1_txbuffer)), C99INIT(rxbuffer_size, sizeof(uart1_rxbuffer)), }, C99INIT(sending, false), C99INIT(usart, &USARTD0), C99INIT(port, &PORTD), C99INIT(txpin, PIN3_bp), C99INIT(rxpin, PIN2_bp), }, #ifdef CPU_AVR_XMEGA_A { C99INIT(hw, /**/) { C99INIT(table, &UART_VT), C99INIT(txbuffer, uart2_txbuffer), C99INIT(rxbuffer, uart2_rxbuffer), C99INIT(txbuffer_size, sizeof(uart2_txbuffer)), C99INIT(rxbuffer_size, sizeof(uart2_rxbuffer)), }, C99INIT(sending, false), C99INIT(usart, &USARTC1), C99INIT(port, &PORTC), C99INIT(txpin, PIN7_bp), C99INIT(rxpin, PIN6_bp), }, { C99INIT(hw, /**/) { C99INIT(table, &UART_VT), C99INIT(txbuffer, uart3_txbuffer), C99INIT(rxbuffer, uart3_rxbuffer), C99INIT(txbuffer_size, sizeof(uart3_txbuffer)), C99INIT(rxbuffer_size, sizeof(uart3_rxbuffer)), }, C99INIT(sending, false), C99INIT(usart, &USARTD1), C99INIT(port, &PORTD), C99INIT(txpin, PIN7_bp), C99INIT(rxpin, PIN6_bp), }, { C99INIT(hw, /**/) { C99INIT(table, &UART_VT), C99INIT(txbuffer, uart4_txbuffer), C99INIT(rxbuffer, uart4_rxbuffer), C99INIT(txbuffer_size, sizeof(uart4_txbuffer)), C99INIT(rxbuffer_size, sizeof(uart4_rxbuffer)), }, C99INIT(sending, false), C99INIT(usart, &USARTE0), C99INIT(port, &PORTE), C99INIT(txpin, PIN3_bp), C99INIT(rxpin, PIN2_bp), }, #endif //CPU_AVR_XMEGA_A }; struct SerialHardware *ser_hw_getdesc(int unit) { ASSERT(unit < SER_CNT); return &UARTDescs[unit].hw; } /* * Interrupt handlers */ static inline void usart_handleDreInterrupt(uint8_t usartNumber) { SER_STROBE_ON; struct FIFOBuffer * const txfifo = &ser_handles[usartNumber]->txfifo; if (fifo_isempty(txfifo)) { SER_UART_BUS_TXEND(UARTDescs[usartNumber].usart); #ifndef SER_UART_BUS_TXOFF UARTDescs[usartNumber].sending = false; #endif } else { char c = fifo_pop(txfifo); SER_UART_BUS_TXCHAR(UARTDescs[usartNumber].usart, c); } SER_STROBE_OFF; } #define USART_DRE_INTERRUPT_VECTOR(_vector, _usart) \ DECLARE_ISR(_vector) \ { \ usart_handleDreInterrupt( _usart ); \ } USART_DRE_INTERRUPT_VECTOR(USARTC0_DRE_vect, SER_UART0) USART_DRE_INTERRUPT_VECTOR(USARTD0_DRE_vect, SER_UART1) #ifdef CPU_AVR_XMEGA_A USART_DRE_INTERRUPT_VECTOR(USARTC1_DRE_vect, SER_UART2) USART_DRE_INTERRUPT_VECTOR(USARTD1_DRE_VECT, SER_UART3) USART_DRE_INTERRUPT_VECTOR(USARTE0_DRE_vect, SER_UART4) #endif #ifdef SER_UART_BUS_TXOFF static inline void USART_handleTXCInterrupt(uint8_t usartNumber) { SER_STROBE_ON; struct FIFOBuffer * const txfifo = &ser_handles[usartNumber]->txfifo; if (fifo_isempty(txfifo)) { SER_UART_BUS_TXOFF(UARTDescs[usartNumber].usart); UARTDescs[usartNumber].sending = false; } else { SER_UART_BUS_TXBEGIN(UARTDescs[usartNumber].usart); } SER_STROBE_OFF; } /* * Serial port 0 TX complete interrupt handler. * * This IRQ is usually disabled. The UDR-empty interrupt * enables it when there's no more data to transmit. * We need to wait until the last character has been * transmitted before switching the 485 transceiver to * receive mode. * * The txfifo might have been refilled by putchar() while * we were waiting for the transmission complete interrupt. * In this case, we must restart the UDR empty interrupt, * otherwise we'd stop the serial port with some data * still pending in the buffer. */ #define USART_TXC_INTERRUPT_VECTOR(_vector, _usart) \ DECLARE_ISR(_vector) \ { \ USART_handleTXCInterrupt( _usart ); \ } USART_TXC_INTERRUPT_VECTOR(USARTC0_TXC_vect, SER_UART0) USART_TXC_INTERRUPT_VECTOR(USARTD0_TXC_vect, SER_UART1) #ifdef CPU_AVR_XMEGA_A USART_TXC_INTERRUPT_VECTOR(USARTC1_TXC_vect, SER_UART2) USART_TXC_INTERRUPT_VECTOR(USARTD1_TXC_vect, SER_UART3) USART_TXC_INTERRUPT_VECTOR(USARTE0_TXC_vect, SER_UART4) #endif /* CPU_AVR_XMEGA_A */ #endif /* SER_UART_BUS_TXOFF */ /* * Serial RX complete interrupt handler. * * This handler is interruptible. * Interrupt are reenabled as soon as recv complete interrupt is * disabled. Using INTERRUPT() is troublesome when the serial * is heavily loaded, because an interrupt could be retriggered * when executing the handler prologue before RXCIE is disabled. */ static inline void USART_handleRXCInterrupt(uint8_t usartNumber) { SER_STROBE_ON; /* read status */ ser_handles[usartNumber]->status |= (UARTDescs[usartNumber].usart)->STATUS & (SERRF_RXSROVERRUN | SERRF_FRAMEERROR); /* To clear the RXC flag we must _always_ read the UDR even when we're * not going to accept the incoming data, otherwise a new interrupt * will occur once the handler terminates. */ char c = (UARTDescs[usartNumber].usart)->DATA; struct FIFOBuffer * const rxfifo = &ser_handles[usartNumber]->rxfifo; if (fifo_isfull(rxfifo)) { ser_handles[usartNumber]->status |= SERRF_RXFIFOOVERRUN; } else { fifo_push(rxfifo, c); #if CONFIG_SER_HWHANDSHAKE if (fifo_isfull(rxfifo)) { RTS_OFF(UARTDescs[usartNumber].usart); } #endif } SER_STROBE_OFF; } #define USART_RXC_INTERRUPT_VECTOR(_vector, _usart) \ DECLARE_ISR(_vector) \ { \ USART_handleRXCInterrupt( _usart ); \ } USART_RXC_INTERRUPT_VECTOR(USARTC0_RXC_vect, SER_UART0) USART_RXC_INTERRUPT_VECTOR(USARTD0_RXC_vect, SER_UART1) #ifdef CPU_AVR_XMEGA_A USART_RXC_INTERRUPT_VECTOR(USARTC1_RXC_vect, SER_UART2) USART_RXC_INTERRUPT_VECTOR(USARTD1_RXC_vect, SER_UART3) USART_RXC_INTERRUPT_VECTOR(USARTE0_RXC_vect, SER_UART4) #endif