Xmega Application Note


sw_usart.c File Reference


Detailed Description

This file shows how to implement a half duplex software driven uart.

This single file shows how to implement a software uart using Timer0 and external interrupt 0.

Note that the RX_PIN must be the external interrupt 0 pin on your AVR of choice. The TX_PIN can be chosen to be any suitable pin. Note that this code is intended to run from an internal 8 MHz clock source.

Application note:
AVR1907: Xplain Evaluation Board
Documentation
For comprehensive code documentation, supported compilers, compiler settings and supported devices see readme.html
Author:
Atmel Corporation: http://www.atmel.com
Support email: avr@atmel.com
Revision
3137
Date
2010-01-18 09:21:33 +0100 (ma, 18 jan 2010)

Copyright (c) 2010, 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.

Definition in file sw_usart.c.

#include "sw_usart.h"

Include dependency graph for sw_usart.c:

Go to the source code of this file.

Functions

 ISR (SW_USART_TX_COMP_VECT)
 Timer2 interrupt service routine.
 ISR (SW_USART_RX_COMP_VECT)
 Timer0 interrupt service routine.
 ISR (INT0_vect)
 External interrupt service routine.
void SW_USART_init (void)
 Function to initialize the software UART.
void SW_USART_PutChar (const uint8_t c)
 Send a unsigned char.
uint8_t SW_USART_RX_Buffer_GetByte (void)
 Get byte from the RX buffer.
void SW_USART_RX_Buffer_PutByte (uint8_t rx_byte)
 Function to put byte in RX buffer.
void SW_USART_Set_Baudrate (uint32_t new_baud)
 Set new baudrate.
uint8_t SW_USART_Test_Hit (void)
 Function to check if data is pending in the buffer.
uint8_t SW_USART_Tx_Ready (void)
 Function to check if transmitter is busy.


Function Documentation

ISR ( SW_USART_TX_COMP_VECT   ) 

Timer2 interrupt service routine.

Timer2 will ensure that bits are written at the correct instants in time.

If state should be something else, the variable is set to IDLE. IDLE is regarded as a safe state/mode.

Note:
SW_USART_init(void) must be called in advance.

Definition at line 150 of file sw_usart.c.

References IDLE, SW_USART_CLR_TX_PIN, SW_USART_SET_TX_PIN, SW_USART_TX_DISABLE_TIMER_INTERRUPT, SwUartTXBitCount, SwUartTXData, SwUsartTXState, TRANSMIT, and TRANSMIT_STOP_BIT.

00151 {
00152         /* Start bit has already been sent, process the byte and stop bit. */
00153         switch (SwUsartTXState) {
00154 
00155             /* Transmit byte in TX buffer. */
00156             case TRANSMIT:
00157                 
00158                 /* If the complete byte is not transmitted, send next bit.*/
00159                 if( SwUartTXBitCount < 8 ) {
00160                         if( SwUartTXData & 0x01 ){
00161                                 SW_USART_SET_TX_PIN();
00162                         }else{
00163                                 SW_USART_CLR_TX_PIN();
00164                         }
00165                         
00166                         /* Rightshift the tx byte to be ready for the next bit transmit. */
00167                         SwUartTXData = SwUartTXData >> 1;
00168                         SwUartTXBitCount++;
00169                 }
00170                 
00171                 /* Send stop bit if complete byte have been sent. */
00172                 else {
00173                         SW_USART_SET_TX_PIN();
00174                         SwUsartTXState = TRANSMIT_STOP_BIT;
00175                 }
00176                 break;
00177                 
00178             /* Stop bit has been sent, go to idle and disable this interrupt. */
00179             case TRANSMIT_STOP_BIT:
00180                 SW_USART_TX_DISABLE_TIMER_INTERRUPT();
00181                 SwUsartTXState = IDLE;
00182                 break;
00183                 
00184             /* Unknown state. This indicates an error, but this should never occur.
00185              * Set state to IDLE to be able to continue operation.
00186              */
00187             default:
00188                 SW_USART_TX_DISABLE_TIMER_INTERRUPT();
00189                 SwUsartTXState = IDLE;
00190         }
00191 }

ISR ( SW_USART_RX_COMP_VECT   ) 

Timer0 interrupt service routine.

Timer0 will ensure that bits are read at the correct instants in time.

If state should be something else, the variable is set to IDLE. IDLE is regarded as a safe state/mode.

Note:
SW_USART_init(void) must be called in advance.

Definition at line 98 of file sw_usart.c.

References IDLE, SW_USART_EXT_IFR, SW_USART_GET_RX_PIN, SW_USART_RX_Buffer_PutByte(), SW_USART_RX_DISABLE_PRESCALER, SW_USART_RX_DISABLE_TIMER_INTERRUPT, SW_USART_RX_ENABLE_EXTERNAL0_INTERRUPT, SwUartRXBitCount, SwUartRXData, and SwUsartRXState.

00099 {
00100         /* Get state of RX pin. */
00101         uint8_t rx_pinstate = SW_USART_GET_RX_PIN();
00102         
00103         /* If 8 bit have not been received, capture and add bit to input byte.*/
00104         if( SwUartRXBitCount < 8 ) {
00105 
00106                 /* Rightshift due to receiving LSB first.*/
00107                 SwUartRXData = (SwUartRXData>>1);
00108 
00109                 /* If a logical 1 is read on the pin, add this to the MSB
00110                  * of the input byte.
00111                  */
00112                 if(rx_pinstate != 0 ){
00113                         SwUartRXData |= 0x80;
00114                 }
00115                 
00116                 SwUartRXBitCount++;
00117         }
00118         
00119         /* Complete byte received, send stop byte.
00120          * Store the received byte in the buffer.
00121          * Disable this interrupt for bit receiving and enable
00122          * pin change interrupt for detecting start bit.
00123          */
00124         else{
00125                 /* If STOP bit is detected, add byte to buffer. If STOP was not
00126                  * detected, discard the byte and reset receiver.
00127                  */
00128                 if(rx_pinstate != 0 ){
00129                         SW_USART_RX_Buffer_PutByte(SwUartRXData);
00130                 }
00131                 SwUsartRXState = IDLE;
00132                 SW_USART_RX_DISABLE_TIMER_INTERRUPT();
00133                 SW_USART_RX_DISABLE_PRESCALER();
00134                 SW_USART_EXT_IFR |= (1 << INTF0);
00135                 SW_USART_RX_ENABLE_EXTERNAL0_INTERRUPT();
00136         }
00137 }

Here is the call graph for this function:

ISR ( INT0_vect   ) 

External interrupt service routine.

The falling edge in the beginning of the start bit will trig this interrupt. The state will be changed to RECEIVE, and the timer interrupt will be set to trig one and a half bit period from the falling edge. At that instant the code should sample the first data bit.

Note:
SW_USART_init(void) must be called in advance.

Definition at line 68 of file sw_usart.c.

References INTERRUPT_EXEC_CYCL, RECEIVE, SW_USART_RX_CLEAR_TIMER_INTERRUPT, SW_USART_RX_COMPARE, SW_USART_RX_DISABLE_EXTERNAL0_INTERRUPT, SW_USART_RX_DISABLE_PRESCALER, SW_USART_RX_DISABLE_TIMER_INTERRUPT, SW_USART_RX_ENABLE_PRESCALER, SW_USART_RX_ENABLE_TIMER_INTERRUPT, SW_USART_RX_TIMER, SwUartRXBitCount, SwUartTICKS2WAITONE, and SwUsartRXState.

00069 {
00070         SwUsartRXState = RECEIVE;                     /* Change state */
00071         SW_USART_RX_DISABLE_EXTERNAL0_INTERRUPT();    /* Disable interrupt during the data bits. */
00072         
00073         SW_USART_RX_DISABLE_TIMER_INTERRUPT();        /* Disable timer to change its registers. */
00074         SW_USART_RX_DISABLE_PRESCALER();              /* Reset prescaler counter. */
00075         
00076         SW_USART_RX_TIMER = INTERRUPT_EXEC_CYCL;      /* Clear counter register. Include time to run interrupt rutine. */
00077         SW_USART_RX_COMPARE = SwUartTICKS2WAITONE;    /* Count one and a half period into the future. */
00078         SW_USART_RX_ENABLE_PRESCALER();               /* Start prescaler clock. */
00079 
00080         SwUartRXBitCount = 0;                         /* Clear received bit counter. */
00081         SW_USART_RX_CLEAR_TIMER_INTERRUPT();          /* Clear interrupt bits */
00082         SW_USART_RX_ENABLE_TIMER_INTERRUPT();         /* Enable timer0 interrupt on again */
00083         
00084 }

void SW_USART_init ( void   ) 

Function to initialize the software UART.

This function will set up pins to transmit and receive on. Control of Timer0 and External interrupt 0.

Definition at line 200 of file sw_usart.c.

References IDLE, SW_USART_EXT_ICR, SW_USART_EXT_IFR, SW_USART_RX_COMPARE, SW_USART_RX_CTRL, SW_USART_RX_DISABLE_TIMER_INTERRUPT, SW_USART_RX_ENABLE_EXTERNAL0_INTERRUPT, SW_USART_RX_PIN, SW_USART_RX_PRESCALE, SW_USART_SET_TX_PIN, SW_USART_TRX_DDR, SW_USART_TRX_PORT, SW_USART_TX_COMPARE, SW_USART_TX_CTRL, SW_USART_TX_DISABLE_TIMER_INTERRUPT, SW_USART_TX_PIN, SW_USART_TX_PRESCALE, SwUartTICKS2WAITONE, SwUsartRXState, and SwUsartTXState.

Referenced by cdc_task_init().

00201 {
00202         /* Setup the TRX PORT for the Soft UART.
00203          * RX_PIN is input, tri-stated.
00204          * TX_PIN is output, set high to IDLE state.
00205          */
00206         SW_USART_TRX_PORT |= ( 1 << SW_USART_RX_PIN );
00207         SW_USART_TRX_DDR |= ( 1 << SW_USART_TX_PIN );
00208         SW_USART_SET_TX_PIN();
00209         
00210         /* Disable all TX and RX interrupts. */
00211         SW_USART_RX_DISABLE_TIMER_INTERRUPT();
00212         SW_USART_TX_DISABLE_TIMER_INTERRUPT();
00213         
00214         /* Initialize the RX Timer to CTC mode.
00215          * This will clear the timer on compare match, and the counter start
00216          * at bottom again. The compare value is equal to on bit length.
00217          */
00218         SW_USART_RX_PRESCALE = 0x00;
00219         SW_USART_RX_CTRL = 0x02;
00220         SW_USART_RX_COMPARE = SwUartTICKS2WAITONE;
00221 
00222         /* Initialize the TX Timer to CTC mode.
00223          * This will clear the timer on compare match, and the counter start
00224          * at bottom again. The compare value is equal to on bit length.
00225          */
00226         SW_USART_TX_PRESCALE = 0x00;
00227         SW_USART_TX_CTRL = 0x02;
00228         SW_USART_TX_COMPARE = SwUartTICKS2WAITONE;
00229         
00230         /* Initialize the RX external interrupt for sensing the start bit.
00231          * This will trigger on the falling edge of the IO pin.
00232          * Clear any pending interrupts and enable the interrupt.
00233          */
00234         SW_USART_EXT_ICR |= ( 1 << ISC01 );
00235         SW_USART_EXT_IFR |= (1 << INTF0 );
00236         SW_USART_RX_ENABLE_EXTERNAL0_INTERRUPT();
00237         
00238         /* Set TRX states initially to IDLE. */
00239         SwUsartRXState = IDLE;
00240         SwUsartTXState = IDLE;
00241 }

void SW_USART_PutChar ( const uint8_t  c  ) 

Send a unsigned char.

This function sends a unsigned char on the TX_PIN using the timer2 isr.

Note:
SW_USART_init(void) must be called in advance.
Parameters:
c char to transmit.
Return values:
void 

Definition at line 255 of file sw_usart.c.

References IDLE, SW_USART_CLR_TX_PIN, SW_USART_TX_CLEAR_TIMER_INTERRUPT, SW_USART_TX_COMPARE, SW_USART_TX_DISABLE_PRESCALER, SW_USART_TX_ENABLE_PRESCALER, SW_USART_TX_ENABLE_TIMER_INTERRUPT, SW_USART_TX_TIMER, SwUartTICKS2WAITONE, SwUartTXBitCount, SwUartTXData, SwUsartTXState, and TRANSMIT.

Referenced by cdc_task().

00256 {
00257         /* Wait until the TX is not busy or the communication will fail */
00258         while( SwUsartTXState != IDLE ){
00259         }
00260         
00261         /* Disable the clock to the timer and set the compare value to one bit length. */
00262         SW_USART_TX_DISABLE_PRESCALER();
00263         SW_USART_TX_COMPARE = SwUartTICKS2WAITONE;
00264 
00265         /* Change state to indicate that the TX is busy. */
00266         SwUsartTXState = TRANSMIT;
00267 
00268         /* Set intial bitcount value and copy byte into the TX buffer. */
00269         SwUartTXData = c;
00270         SwUartTXBitCount = 0;
00271         
00272         /* Clear the timer counter value and clear any pending interrupts. */
00273         SW_USART_TX_TIMER = 0;
00274         SW_USART_TX_CLEAR_TIMER_INTERRUPT();
00275         
00276         /* Start the timer, set pin value to transmit start bit and enable interrupt. */
00277         SW_USART_TX_ENABLE_PRESCALER();
00278         SW_USART_CLR_TX_PIN();
00279         SW_USART_TX_ENABLE_TIMER_INTERRUPT();
00280 }

uint8_t SW_USART_RX_Buffer_GetByte ( void   ) 

Get byte from the RX buffer.

Note:
The pointers might be changed in interrupts, and it's important to disable the global interrupt while manipulating the values that might be changed in the interrupt.
Return values:
Byte from buffer

Definition at line 336 of file sw_usart.c.

References AVR_ENTER_CRITICAL_REGION, AVR_LEAVE_CRITICAL_REGION, SW_USART_BUFFER_SIZE, SW_USART_RX_Buffer, SW_USART_RX_Num, and SW_USART_RX_Tail.

Referenced by cdc_task().

00337 {
00338         uint8_t ret_byte = 0;
00339         
00340         /* Check if there is any bytes in the buffer. */
00341         if(SW_USART_RX_Num > 0){
00342                 ret_byte = SW_USART_RX_Buffer[SW_USART_RX_Tail];
00343         
00344                 /* Enter a critical region as the value might be changed in an interrupt. */
00345                 AVR_ENTER_CRITICAL_REGION();
00346                 
00347                 /* Decrease number of bytes left in buffer. */
00348                 SW_USART_RX_Num--;
00349                 
00350                 /* Leave the critical region and restore SREG register. */
00351                 AVR_LEAVE_CRITICAL_REGION();
00352                 
00353                 /* Increase the tail pointer and wrap around if necessary. */
00354                 SW_USART_RX_Tail++;
00355                 if(SW_USART_RX_Tail >= SW_USART_BUFFER_SIZE){
00356                         SW_USART_RX_Tail = 0;
00357                 }
00358         }
00359         return ret_byte;
00360 }

void SW_USART_RX_Buffer_PutByte ( uint8_t  rx_byte  ) 

Function to put byte in RX buffer.

Note:
This function is called from an interrupt and might change the pointers while the SW_USART_RX_Buffer_GetByte(void) function access the same values.
Parameters:
rx_byte to put into buffer.

Definition at line 310 of file sw_usart.c.

References SW_USART_BUFFER_SIZE, SW_USART_RX_Buffer, SW_USART_RX_Head, and SW_USART_RX_Num.

Referenced by ISR().

00311 {
00312         /* Check if buffer is full. The byte will be lost if the buffer is full.*/
00313         if(SW_USART_RX_Num < SW_USART_BUFFER_SIZE ){
00314                 SW_USART_RX_Buffer[SW_USART_RX_Head] = rx_byte;
00315                 
00316                 /* Increase the number of bytes in buffer and the pointer. */
00317                 SW_USART_RX_Num++;
00318                 SW_USART_RX_Head++;
00319                 
00320                 /* Check if we should wrap around. */
00321                 if(SW_USART_RX_Head >= SW_USART_BUFFER_SIZE){
00322                         SW_USART_RX_Head = 0;
00323                 }
00324         }       
00325 }

void SW_USART_Set_Baudrate ( uint32_t  new_baud  ) 

Set new baudrate.

Note:
Change do not work yet, do not use.
Parameters:
new_baud The new baudrate to set.

Definition at line 369 of file sw_usart.c.

Referenced by cdc_set_line_coding().

00370 {
00371 //      SwUartTICKS2WAITONE = (uint8_t) ((1000000/new_baud)-1);
00372 }

uint8_t SW_USART_Test_Hit ( void   ) 

Function to check if data is pending in the buffer.

Return values:
true if data is pending, false otherwise

Definition at line 287 of file sw_usart.c.

References SW_USART_RX_Num.

Referenced by cdc_task().

00288 {
00289         return (SW_USART_RX_Num > 0);
00290 }

uint8_t SW_USART_Tx_Ready ( void   ) 

Function to check if transmitter is busy.

Return values:
true if ready, false otherwise.

Definition at line 297 of file sw_usart.c.

References IDLE, and SwUsartTXState.

00298 {
00299         return ( SwUsartTXState == IDLE );
00300 }

@DOC_TITLE@
Generated on Mon Jan 18 09:26:10 2010 for AVR1907 Xplain USB Gateway by doxygen 1.5.5