/*This file has been prepared for Doxygen automatic documentation generation.*/ /*! \file ********************************************************************* * * \brief FreeRTOS serial port for AVR32 UC3. * * - Compiler: IAR EWAVR32 and GNU GCC for AVR32 * - Supported devices: All AVR32 devices 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. */ /* BASIC INTERRUPT DRIVEN SERIAL PORT DRIVER FOR USART0. */ /* Scheduler includes. */ #include "FreeRTOS.h" #include "queue.h" #include "task.h" /* Demo application includes. */ #include "serial.h" #include #include "board.h" #include "gpio.h" /*-----------------------------------------------------------*/ /* Constants to setup and access the USART. */ #define serINVALID_COMPORT_HANDLER ( ( xComPortHandle ) 0 ) #define serINVALID_QUEUE ( ( QueueHandle_t ) 0 ) #define serHANDLE ( ( xComPortHandle ) 1 ) #define serNO_BLOCK ( ( TickType_t ) 0 ) /*-----------------------------------------------------------*/ /* Queues used to hold received characters, and characters waiting to be transmitted. */ static QueueHandle_t xRxedChars; static QueueHandle_t xCharsForTx; /*-----------------------------------------------------------*/ /* Forward declaration. */ static void vprvSerialCreateQueues( unsigned portBASE_TYPE uxQueueLength, QueueHandle_t *pxRxedChars, QueueHandle_t *pxCharsForTx ); /*-----------------------------------------------------------*/ #if __GNUC__ __attribute__((__noinline__)) #elif __ICCAVR32__ #pragma optimize = no_inline #endif static portBASE_TYPE prvUSART0_ISR_NonNakedBehaviour( void ) { /* Now we can declare the local variables. */ signed char cChar; portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; unsigned long ulStatus; volatile avr32_usart_t *usart0 = &AVR32_USART0; portBASE_TYPE retstatus; /* What caused the interrupt? */ ulStatus = usart0->csr & usart0->imr; if (ulStatus & AVR32_USART_CSR_TXRDY_MASK) { /* The interrupt was caused by the THR becoming empty. Are there any more characters to transmit? */ /* Because FreeRTOS is not supposed to run with nested interrupts, put all OS calls in a critical section . */ portENTER_CRITICAL(); retstatus = xQueueReceiveFromISR(xCharsForTx, &cChar, &xHigherPriorityTaskWoken); portEXIT_CRITICAL(); if (retstatus == pdTRUE) { /* A character was retrieved from the queue so can be sent to the THR now. */ usart0->thr = cChar; } else { /* Queue empty, nothing to send so turn off the Tx interrupt. */ usart0->idr = AVR32_USART_IDR_TXRDY_MASK; } } if (ulStatus & AVR32_USART_CSR_RXRDY_MASK) { /* The interrupt was caused by the receiver getting data. */ cChar = usart0->rhr; //TODO /* Because FreeRTOS is not supposed to run with nested interrupts, put all OS calls in a critical section . */ portENTER_CRITICAL(); xQueueSendFromISR(xRxedChars, &cChar, &xHigherPriorityTaskWoken); portEXIT_CRITICAL(); } /* The return value will be used by portEXIT_SWITCHING_ISR() to know if it should perform a vTaskSwitchContext(). */ return ( xHigherPriorityTaskWoken ); } /* * USART0 interrupt service routine. */ #if __GNUC__ __attribute__((__naked__)) #elif __ICCAVR32__ #pragma shadow_registers = full // Naked. #endif static void vUSART0_ISR( void ) { /* This ISR can cause a context switch, so the first statement must be a call to the portENTER_SWITCHING_ISR() macro. This must be BEFORE any variable declarations. */ portENTER_SWITCHING_ISR(); prvUSART0_ISR_NonNakedBehaviour(); /* Exit the ISR. If a task was woken by either a character being received or transmitted then a context switch will occur. */ portEXIT_SWITCHING_ISR(); } /*-----------------------------------------------------------*/ /* * Init the serial port for the Minimal implementation. */ xComPortHandle xSerialPortInitMinimal( unsigned long ulWantedBaud, unsigned portBASE_TYPE uxQueueLength ) { static const gpio_map_t USART0_GPIO_MAP = { { AVR32_USART0_RXD_0_PIN, AVR32_USART0_RXD_0_FUNCTION }, { AVR32_USART0_TXD_0_PIN, AVR32_USART0_TXD_0_FUNCTION } }; xComPortHandle xReturn = serHANDLE; volatile avr32_usart_t *usart0 = &AVR32_USART0; int cd; /* USART0 Clock Divider. */ /* Create the rx and tx queues. */ vprvSerialCreateQueues( uxQueueLength, &xRxedChars, &xCharsForTx ); /* Configure USART0. */ if( ( xRxedChars != serINVALID_QUEUE ) && ( xCharsForTx != serINVALID_QUEUE ) && ( ulWantedBaud != ( unsigned long ) 0 ) ) { portENTER_CRITICAL(); { /** ** Reset USART0. **/ /* Disable all USART0 interrupt sources to begin... */ usart0->idr = 0xFFFFFFFF; /* Reset mode and other registers that could cause unpredictable behaviour after reset */ usart0->mr = 0; /* Reset Mode register. */ usart0->rtor = 0; /* Reset Receiver Time-out register. */ usart0->ttgr = 0; /* Reset Transmitter Timeguard register. */ /* Shutdown RX and TX, reset status bits, reset iterations in CSR, reset NACK and turn off DTR and RTS */ usart0->cr = AVR32_USART_CR_RSTRX_MASK | AVR32_USART_CR_RSTTX_MASK | AVR32_USART_CR_RXDIS_MASK | AVR32_USART_CR_TXDIS_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; /** ** Configure USART0. **/ /* Enable USART0 RXD & TXD pins. */ gpio_enable_module( USART0_GPIO_MAP, sizeof( USART0_GPIO_MAP ) / sizeof( USART0_GPIO_MAP[0] ) ); /* Set the USART0 baudrate to be as close as possible to the wanted baudrate. */ /* * ** BAUDRATE CALCULATION ** * * Selected Clock Selected Clock * baudrate = ---------------- or baudrate = ---------------- * 16 x CD 8 x CD * * (with 16x oversampling) (with 8x oversampling) */ if ( ulWantedBaud < (configCPU_CLOCK_HZ/16) ){ /* Use 8x oversampling */ usart0->mr |= (1<brgr = (cd << AVR32_USART_BRGR_CD_OFFSET); } else { /* Use 16x oversampling */ usart0->mr &= ~(1< 65535) { /* Baudrate is too low */ return serINVALID_COMPORT_HANDLER; } } usart0->brgr = (cd << AVR32_USART_BRGR_CD_OFFSET); /* Set the USART0 Mode register: Mode=Normal(0), Clk selection=MCK(0), CHRL=8, SYNC=0(asynchronous), PAR=None, NBSTOP=1, CHMODE=0, MSBF=0, MODE9=0, CKLO=0, OVER(previously done when setting the baudrate), other fields not used in this mode. */ usart0->mr |= ((8-5) << AVR32_USART_MR_CHRL_OFFSET ) | ( 4 << AVR32_USART_MR_PAR_OFFSET ) | ( 1 << AVR32_USART_MR_NBSTOP_OFFSET); /* Write the Transmit Timeguard Register */ usart0->ttgr = 0; // Register the USART0 interrupt handler to the interrupt controller and // enable the USART0 interrupt. INTC_register_interrupt((__int_handler)&vUSART0_ISR, AVR32_USART0_IRQ, INT1); /* Enable USART0 interrupt sources (but not Tx for now)... */ usart0->ier = AVR32_USART_IER_RXRDY_MASK; /* Enable receiver and transmitter... */ usart0->cr |= AVR32_USART_CR_TXEN_MASK | AVR32_USART_CR_RXEN_MASK; } portEXIT_CRITICAL(); } else { xReturn = serINVALID_COMPORT_HANDLER; } return xReturn; } /*-----------------------------------------------------------*/ signed portBASE_TYPE xSerialGetChar( xComPortHandle pxPort, signed char *pcRxedChar, TickType_t xBlockTime ) { /* The port handle is not required as this driver only supports UART0. */ ( void ) pxPort; /* Get the next character from the buffer. Return false if no characters are available, or arrive before xBlockTime expires. */ if( xQueueReceive( xRxedChars, pcRxedChar, xBlockTime ) ) { return pdTRUE; } else { return pdFALSE; } } /*-----------------------------------------------------------*/ void vSerialPutString( xComPortHandle pxPort, const signed char * const pcString, unsigned short usStringLength ) { signed char *pxNext; /* NOTE: This implementation does not handle the queue being full as no block time is used! */ /* The port handle is not required as this driver only supports UART0. */ ( void ) pxPort; /* Send each character in the string, one at a time. */ pxNext = ( signed char * ) pcString; while( *pxNext ) { xSerialPutChar( pxPort, *pxNext, serNO_BLOCK ); pxNext++; } } /*-----------------------------------------------------------*/ signed portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, signed char cOutChar, TickType_t xBlockTime ) { volatile avr32_usart_t *usart0 = &AVR32_USART0; /* Place the character in the queue of characters to be transmitted. */ if( xQueueSend( xCharsForTx, &cOutChar, xBlockTime ) != pdPASS ) { return pdFAIL; } /* Turn on the Tx interrupt so the ISR will remove the character from the queue and send it. This does not need to be in a critical section as if the interrupt has already removed the character the next interrupt will simply turn off the Tx interrupt again. */ usart0->ier = (1 << AVR32_USART_IER_TXRDY_OFFSET); return pdPASS; } /*-----------------------------------------------------------*/ void vSerialClose( xComPortHandle xPort ) { /* Not supported as not required by the demo application. */ } /*-----------------------------------------------------------*/ /*###########################################################*/ /* * Create the rx and tx queues. */ static void vprvSerialCreateQueues( unsigned portBASE_TYPE uxQueueLength, QueueHandle_t *pxRxedChars, QueueHandle_t *pxCharsForTx ) { /* Create the queues used to hold Rx and Tx characters. */ xRxedChars = xQueueCreate( uxQueueLength, ( unsigned portBASE_TYPE ) sizeof( signed char ) ); xCharsForTx = xQueueCreate( uxQueueLength + 1, ( unsigned portBASE_TYPE ) sizeof( signed char ) ); /* Pass back a reference to the queues so the serial API file can post/receive characters. */ *pxRxedChars = xRxedChars; *pxCharsForTx = xCharsForTx; } /*-----------------------------------------------------------*/