From: lottaviano Date: Sat, 9 Apr 2011 13:45:50 +0000 (+0000) Subject: Add serial driver for AVR XMega CPU. X-Git-Tag: 2.7.0~114 X-Git-Url: https://codewiz.org/gitweb?a=commitdiff_plain;h=5f79b6719488747126625b6aae6ef684961f3d52;p=bertos.git Add serial driver for AVR XMega CPU. Signed-off-by: Onno git-svn-id: https://src.develer.com/svnoss/bertos/trunk@4852 38d2e660-2303-0410-9eaa-f027e97ec537 --- diff --git a/bertos/cpu/avr/drv/ser_avr.c b/bertos/cpu/avr/drv/ser_avr.c new file mode 100644 index 00000000..f87f3b0b --- /dev/null +++ b/bertos/cpu/avr/drv/ser_avr.c @@ -0,0 +1,56 @@ +/** + * \file + * + * + * \brief AVR UART and SPI I/O driver (Implementation) + * + * \author Onno + * + * This module is automatically included so no need to include + * in test list. + * notest: avr + */ + +#ifndef WIZ_AUTOGEN + #warning This file is deprecated, you should probably use ser_mega.c + + #include + + #if CPU_AVR_MEGA + #include "ser_mega.c" + #elif CPU_AVR_XMEGA + #include "ser_xmega.c" + /*#elif Add other AVR families here */ + #else + #error Unknown CPU + #endif +#endif /* WIZ_AUTOGEN */ diff --git a/bertos/cpu/avr/drv/ser_avr.h b/bertos/cpu/avr/drv/ser_avr.h new file mode 100644 index 00000000..4909613a --- /dev/null +++ b/bertos/cpu/avr/drv/ser_avr.h @@ -0,0 +1,53 @@ +/** + * \file + * + * + * + * \author Onno + * + * \brief Low-level serial module for AVR (interface). + * + */ +#ifndef SER_AVR_H_ +#define SER_AVR_H_ + +#include + +#if CPU_AVR_MEGA + #include "ser_mega.h" +#elif CPU_AVR_XMEGA + #include "ser_xmega.h" +/*#elif Add other AVR families here */ +#else + #error Unknown CPU +#endif + +#endif /* SER_AVR_H_ */ diff --git a/bertos/cpu/avr/drv/ser_mega.c b/bertos/cpu/avr/drv/ser_mega.c index ce44fd19..5e893255 100644 --- a/bertos/cpu/avr/drv/ser_mega.c +++ b/bertos/cpu/avr/drv/ser_mega.c @@ -31,7 +31,7 @@ * * --> * - * \brief AVR UART and SPI I/O driver (Implementation) + * \brief AVR MEGA UART and SPI I/O driver (Implementation) * * \author Bernie Innocenti * \author Stefano Fedrigo diff --git a/bertos/cpu/avr/drv/ser_mega.h b/bertos/cpu/avr/drv/ser_mega.h index 68d5e508..56683050 100644 --- a/bertos/cpu/avr/drv/ser_mega.h +++ b/bertos/cpu/avr/drv/ser_mega.h @@ -34,12 +34,12 @@ * \author Daniele Basile * \author Luca Ottaviano * - * \brief Low-level serial module for AVR (interface). + * \brief Low-level serial module for AVR MEGA(interface). * */ -#ifndef DRV_SER_AVR_H -#define DRV_SER_AVR_H +#ifndef DRV_SER_MEGA_H +#define DRV_SER_MEGA_H #include /* BV() */ #include /* uint32_t */ @@ -107,4 +107,4 @@ enum }; /*\}*/ -#endif /* DRV_SER_AVR_H */ +#endif /* DRV_SER_MEGA_H */ diff --git a/bertos/cpu/avr/drv/ser_xmega.c b/bertos/cpu/avr/drv/ser_xmega.c new file mode 100644 index 00000000..0e12c398 --- /dev/null +++ b/bertos/cpu/avr/drv/ser_xmega.c @@ -0,0 +1,735 @@ +/** + * \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 + */ + +#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 diff --git a/bertos/cpu/avr/drv/ser_xmega.h b/bertos/cpu/avr/drv/ser_xmega.h new file mode 100644 index 00000000..cad8b1e9 --- /dev/null +++ b/bertos/cpu/avr/drv/ser_xmega.h @@ -0,0 +1,85 @@ +/** + * \file + * + * + * + * \brief Low-level serial module for AVR XMEGA (interface). + * + * 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 + * + */ + +#ifndef DRV_SER_XMEGA_H +#define DRV_SER_XMEGA_H + +#include /* BV() */ +#include /* uint8_t */ + +typedef uint8_t serstatus_t; + +/* Software errors */ +#define SERRF_RXFIFOOVERRUN BV(0) /**< Rx FIFO buffer overrun */ +#define SERRF_RXTIMEOUT BV(5) /**< Receive timeout */ +#define SERRF_TXTIMEOUT BV(6) /**< Transmit timeout */ + +/* +* Hardware errors. +* These flags map directly to the AVR XMEGA UART Status Register. +*/ +#define SERRF_RXSROVERRUN BV(3) /**< Rx shift register overrun */ +#define SERRF_FRAMEERROR BV(4) /**< Stop bit missing */ +#define SERRF_PARITYERROR BV(2) /**< Parity error */ +#define SERRF_NOISEERROR 0 /**< Unsupported */ + +/* + * \name Serial hw numbers + * + * \{ + */ +enum +{ + SER_UART0, + SER_UART1, +#ifdef CPU_AVR_XMEGA_A + //the XMEGA A Family have 5 USART ports + SER_UART2, + SER_UART3, + SER_UART4, +#endif + SER_CNT /**< Number of serial ports */ +}; +/*\}*/ + +#endif /* DRV_SER_XMEGA_H */