/*This file is prepared for Doxygen automatic documentation generation.*/ /*! \file ********************************************************************* * * \brief USART driver for AVR32 UC3. * * This file contains basic functions for the AVR32 USART, with support for all * modes, settings and clock speeds. * * - Compiler: IAR EWAVR32 and GNU GCC for AVR32 * - Supported devices: All AVR32 devices with a USART module can be used. * - AppNote: * * \author Atmel Corporation: http://www.atmel.com \n * Support and FAQ: http://support.atmel.no/ * ******************************************************************************/ /* Copyright (c) 2007, Atmel Corporation All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The name of ATMEL may not be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY ATMEL ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY AND * SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "usart.h" //------------------------------------------------------------------------------ /*! \name Private Functions */ //! @{ /*! \brief Checks if the USART is in multidrop mode. * * \param usart Base address of the USART instance. * * \return \c 1 if the USART is in multidrop mode, otherwise \c 0. */ #if __GNUC__ __attribute__((__always_inline__)) #endif static __inline__ int usart_mode_is_multidrop(volatile avr32_usart_t *usart) { return ((usart->mr >> AVR32_USART_MR_PAR_OFFSET) & AVR32_USART_MR_PAR_MULTI) == AVR32_USART_MR_PAR_MULTI; } /*! \brief Calculates a clock divider (\e CD) that gets the USART as close to a * wanted baudrate as possible. * * \todo manage the FP fractal part to avoid big errors * * Baudrate calculation: * \f$ baudrate = \frac{Selected Clock}{16 \times CD} \f$ with 16x oversampling or * \f$ baudrate = \frac{Selected Clock}{8 \times CD} \f$ with 8x oversampling or * \f$ baudrate = \frac{Selected Clock}{CD} \f$ with SYNC bit set to allow high speed. * * \param usart Base address of the USART instance. * \param baudrate Wanted baudrate. * \param pba_hz USART module input clock frequency (PBA clock, Hz). * * \retval USART_SUCCESS Baudrate successfully initialized. * \retval USART_INVALID_INPUT Wanted baudrate is impossible with given clock speed. */ static int usart_set_baudrate(volatile avr32_usart_t *usart, unsigned int baudrate, long pba_hz) { // Clock divider. int cd; // Baudrate calculation. if (baudrate < pba_hz / 16) { // Use 16x oversampling, clear SYNC bit. usart->mr &=~ (AVR32_USART_MR_OVER_MASK | AVR32_USART_MR_SYNC_MASK); cd = (pba_hz + 8 * baudrate) / (16 * baudrate); if ((cd >65535)) return USART_INVALID_INPUT; } else if (baudrate < pba_hz / 8) { // Use 8x oversampling. usart->mr |= AVR32_USART_MR_OVER_MASK; // clear SYNC bit usart->mr &=~ AVR32_USART_MR_SYNC_MASK; cd = (pba_hz + 4 * baudrate) / (8 * baudrate); if ((cd < 1)||(cd >65535)) return USART_INVALID_INPUT; } else { // set SYNC to 1 usart->mr |= AVR32_USART_MR_SYNC_MASK; // use PBA/BaudRate cd = (pba_hz / baudrate); } usart->brgr = cd << AVR32_USART_BRGR_CD_OFFSET; return USART_SUCCESS; } //! @} //------------------------------------------------------------------------------ /*! \name Initialization Functions */ //! @{ void usart_reset(volatile avr32_usart_t *usart) { // Disable all USART interrupts. // Interrupts needed should be set explicitly on every reset. usart->idr = 0xFFFFFFFF; // Reset mode and other registers that could cause unpredictable behavior after reset. usart->mr = 0; usart->rtor = 0; usart->ttgr = 0; // Shutdown TX and RX (will be re-enabled when setup has successfully completed), // reset status bits and turn off DTR and RTS. usart->cr = AVR32_USART_CR_RSTRX_MASK | AVR32_USART_CR_RSTTX_MASK | AVR32_USART_CR_RSTSTA_MASK | AVR32_USART_CR_RSTIT_MASK | AVR32_USART_CR_RSTNACK_MASK | AVR32_USART_CR_DTRDIS_MASK | AVR32_USART_CR_RTSDIS_MASK; } int usart_init_rs232(volatile avr32_usart_t *usart, const usart_options_t *opt, long pba_hz) { // Reset the USART and shutdown TX and RX. usart_reset(usart); // Check input values. if (!opt) // Null pointer. return USART_INVALID_INPUT; if (opt->charlength < 5 || opt->charlength > 9 || opt->paritytype > 7 || opt->stopbits > 2 + 255 || opt->channelmode > 3) return USART_INVALID_INPUT; if (usart_set_baudrate(usart, opt->baudrate, pba_hz) == USART_INVALID_INPUT) return USART_INVALID_INPUT; if (opt->charlength == 9) { // Character length set to 9 bits. MODE9 dominates CHRL. usart->mr |= AVR32_USART_MR_MODE9_MASK; } else { // CHRL gives the character length (- 5) when MODE9 = 0. usart->mr |= (opt->charlength - 5) << AVR32_USART_MR_CHRL_OFFSET; } usart->mr |= (opt->channelmode << AVR32_USART_MR_CHMODE_OFFSET) | (opt->paritytype << AVR32_USART_MR_PAR_OFFSET); if (opt->stopbits > USART_2_STOPBITS) { // Set two stop bits usart->mr |= AVR32_USART_MR_NBSTOP_2 << AVR32_USART_MR_NBSTOP_OFFSET; // and a timeguard period gives the rest. usart->ttgr = opt->stopbits - USART_2_STOPBITS; } else // Insert 1, 1.5 or 2 stop bits. usart->mr |= opt->stopbits << AVR32_USART_MR_NBSTOP_OFFSET; // Setup complete; enable communication. // Enable input and output. usart->cr |= AVR32_USART_CR_TXEN_MASK | AVR32_USART_CR_RXEN_MASK; return USART_SUCCESS; } int usart_init_hw_handshaking(volatile avr32_usart_t *usart, const usart_options_t *opt, long pba_hz) { // First: Setup standard RS232. if (usart_init_rs232(usart, opt, pba_hz) == USART_INVALID_INPUT) return USART_INVALID_INPUT; // Clear previous mode. usart->mr &= ~AVR32_USART_MR_MODE_MASK; // Hardware handshaking. usart->mr |= USART_MODE_HW_HSH << AVR32_USART_MR_MODE_OFFSET; return USART_SUCCESS; } int usart_init_IrDA(volatile avr32_usart_t *usart, const usart_options_t *opt, long pba_hz, unsigned char irda_filter) { // First: Setup standard RS232. if (usart_init_rs232(usart, opt, pba_hz) == USART_INVALID_INPUT) return USART_INVALID_INPUT; // Set IrDA counter. usart->ifr = irda_filter; // Activate "low-pass filtering" of input. usart->mr |= AVR32_USART_MR_FILTER_MASK; return USART_SUCCESS; } int usart_init_modem(volatile avr32_usart_t *usart, const usart_options_t *opt, long pba_hz) { // First: Setup standard RS232. if (usart_init_rs232(usart, opt, pba_hz) == USART_INVALID_INPUT) return USART_INVALID_INPUT; // Clear previous mode. usart->mr &= ~AVR32_USART_MR_MODE_MASK; // Set modem mode. usart->mr |= USART_MODE_MODEM << AVR32_USART_MR_MODE_OFFSET; return USART_SUCCESS; } int usart_init_rs485(volatile avr32_usart_t *usart, const usart_options_t *opt, long pba_hz) { // First: Setup standard RS232. if (usart_init_rs232(usart, opt, pba_hz) == USART_INVALID_INPUT) return USART_INVALID_INPUT; // Clear previous mode. usart->mr &= ~AVR32_USART_MR_MODE_MASK; // Set RS485 mode. usart->mr |= USART_MODE_RS485 << AVR32_USART_MR_MODE_OFFSET; return USART_SUCCESS; } int usart_init_iso7816(volatile avr32_usart_t *usart, const iso7816_options_t *opt, int t, long pba_hz) { // Reset the USART and shutdown TX and RX. usart_reset(usart); // Check input values. if (!opt) // Null pointer. return USART_INVALID_INPUT; if (t == 0) { // Set USART mode to ISO7816, T=0. // The T=0 protocol always uses 2 stop bits. usart->mr = (USART_MODE_ISO7816_T0 << AVR32_USART_MR_MODE_OFFSET) | (AVR32_USART_MR_NBSTOP_2 << AVR32_USART_MR_NBSTOP_OFFSET) | (opt->bit_order << AVR32_USART_MR_MSBF_OFFSET); // Allow MSBF in T=0. } else if (t == 1) { // Only LSB first in the T=1 protocol. // max_iterations field is only used in T=0 mode. if (opt->bit_order != 0 || opt->max_iterations != 0) return USART_INVALID_INPUT; // Set USART mode to ISO7816, T=1. // The T=1 protocol always uses 1 stop bit. usart->mr = (USART_MODE_ISO7816_T1 << AVR32_USART_MR_MODE_OFFSET) | (AVR32_USART_MR_NBSTOP_1 << AVR32_USART_MR_NBSTOP_OFFSET); } else return USART_INVALID_INPUT; if (usart_set_baudrate(usart, opt->iso7816_hz, pba_hz) == USART_INVALID_INPUT) return USART_INVALID_INPUT; // Set FIDI register: bit rate = selected clock/FI_DI_ratio/16. usart->fidi = opt->fidi_ratio; // Set ISO7816 spesific options in the MODE register. usart->mr |= (opt->inhibit_nack << AVR32_USART_MR_INACK_OFFSET) | (opt->dis_suc_nack << AVR32_USART_MR_DSNACK_OFFSET) | (opt->max_iterations << AVR32_USART_MR_MAX_ITERATION_OFFSET) | AVR32_USART_MR_CLKO_MASK; // Enable clock output. // Setup complete; enable input. // Leave TX disabled for now. usart->cr |= AVR32_USART_CR_RXEN_MASK; return USART_SUCCESS; } //! @} //------------------------------------------------------------------------------ /*! \name Transmit/Receive Functions */ //! @{ int usart_send_address(volatile avr32_usart_t *usart, int address) { // Check if USART is in multidrop / RS485 mode. if (!usart_mode_is_multidrop(usart)) return USART_MODE_FAULT; // Prepare to send an address. usart->cr |= AVR32_USART_CR_SENDA_MASK; // Write the address to TX. usart_bw_write_char(usart, address); return USART_SUCCESS; } int usart_write_char(volatile avr32_usart_t *usart, int c) { if (usart->csr & AVR32_USART_CSR_TXRDY_MASK) { usart->thr = c; return USART_SUCCESS; } else return USART_TX_BUSY; } int usart_putchar(volatile avr32_usart_t *usart, int c) { int timeout = USART_DEFAULT_TIMEOUT; if (c == '\n') { do { if (!timeout--) return USART_FAILURE; } while (usart_write_char(usart, '\r') != USART_SUCCESS); timeout = USART_DEFAULT_TIMEOUT; } do { if (!timeout--) return USART_FAILURE; } while (usart_write_char(usart, c) != USART_SUCCESS); return USART_SUCCESS; } int usart_read_char(volatile avr32_usart_t *usart, int *c) { // Check for errors: frame, parity and overrun. In RS485 mode, a parity error // would mean that an address char has been received. if (usart->csr & (AVR32_USART_CSR_OVRE_MASK | AVR32_USART_CSR_FRAME_MASK | AVR32_USART_CSR_PARE_MASK)) return USART_RX_ERROR; // No error; if we really did receive a char, read it and return SUCCESS. if (usart->csr & AVR32_USART_CSR_RXRDY_MASK) { *c = (unsigned short)usart->rhr; return USART_SUCCESS; } else return USART_RX_EMPTY; } int usart_getchar(volatile avr32_usart_t *usart) { int c, ret; while ((ret = usart_read_char(usart, &c)) == USART_RX_EMPTY); if (ret == USART_RX_ERROR) return USART_FAILURE; return c; } void usart_write_line(volatile avr32_usart_t *usart, const char *string) { while (*string != '\0') usart_putchar(usart, *string++); } int usart_get_echo_line(volatile avr32_usart_t *usart) { int rx_char; int retval = USART_SUCCESS; while (1) { rx_char = usart_getchar(usart); if (rx_char == USART_FAILURE) { usart_write_line(usart, "Error!!!\n"); break; } if (rx_char == '\x03') { retval = USART_FAILURE; break; } usart_putchar(usart, rx_char); if (rx_char == '\r') { usart_putchar(usart, '\n'); break; } } return retval; } //! @}