Main Page | Modules | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

# uartsw.c

Go to the documentation of this file.

    00001 /*! \file uartsw.c \brief Software Interrupt-driven UART Driver. */
    00002 //*****************************************************************************
    00003 //
    00004 // File Name    : 'uartsw.c'
    00005 // Title        : Software Interrupt-driven UART Driver
    00006 // Author       : Pascal Stang - Copyright (C) 2002-2004
    00007 // Created      : 7/20/2002
    00008 // Revised      : 4/27/2004
    00009 // Version      : 0.1
    00010 // Target MCU   : Atmel AVR Series (intended for the ATmega16 and ATmega32)
    00011 // Editor Tabs  : 4
    00012 //
    00013 // This code is distributed under the GNU Public License
    00014 //      which can be found at http://www.gnu.org/licenses/gpl.txt
    00015 //
    00016 //*****************************************************************************
    00017
    00018 #include <avr/io.h>
    00019 #include <avr/interrupt.h>
    00020
    00021 #include "global.h"
    00022 #include "timer.h"
    00023 #include "uartsw.h"
    00024
    00025 // Program ROM constants
    00026
    00027 // Global variables
    00028
    00029 // uartsw transmit status and data variables
    00030 static volatile u08 UartswTxBusy;
    00031 static volatile u08 UartswTxData;
    00032 static volatile u08 UartswTxBitNum;
    00033
    00034 // baud rate common to transmit and receive
    00035 static volatile u16 UartswBaudRateDiv;
    00036
    00037 // uartsw receive status and data variables
    00038 static volatile u08 UartswRxBusy;
    00039 static volatile u08 UartswRxData;
    00040 static volatile u08 UartswRxBitNum;
    00041 // receive buffer
    00042 static cBuffer uartswRxBuffer;               ///< uartsw receive buffer
    00043 // automatically allocate space in ram for each buffer
    00044 static char uartswRxData[UARTSW_RX_BUFFER_SIZE];
    00045
    00046 // functions
    00047
    00048 //! enable and initialize the software uart
    00049 void uartswInit(void)
    00050 {
    00051     // initialize the buffers
    00052     uartswInitBuffers();
    00053     // initialize the ports
    00054     sbi(UARTSW_TX_DDR, UARTSW_TX_PIN);
    00055     cbi(UARTSW_RX_DDR, UARTSW_RX_PIN);
    00056     cbi(UARTSW_RX_PORT, UARTSW_RX_PIN);
    00057     // initialize baud rate
    00058     uartswSetBaudRate(9600);
    00059
    00060     // setup the transmitter
    00061     UartswTxBusy = FALSE;
    00062     // disable OC1A interrupt
    00063     cbi(TIMSK, OCIE1A);
    00064     // attach TxBit service routine to OC1A
    00065     timerAttach(TIMER1OUTCOMPAREA_INT, uartswTxBitService);
    00066
    00067     // setup the receiver
    00068     UartswRxBusy = FALSE;
    00069     // disable OC1B interrupt
    00070     cbi(TIMSK, OCIE1B);
    00071     // attach RxBit service routine to OC1B
    00072     timerAttach(TIMER1OUTCOMPAREB_INT, uartswRxBitService);
    00073     // attach RxBit service routine to ICP
    00074     timerAttach(TIMER1INPUTCAPTURE_INT, uartswRxBitService);
    00075     #ifdef UARTSW_INVERT
    00076     // trigger on rising edge
    00077     sbi(TCCR1B, ICES1);
    00078     #else
    00079     // trigger on falling edge
    00080     cbi(TCCR1B, ICES1);
    00081     #endif
    00082     // enable ICP interrupt
    00083     sbi(TIMSK, TICIE1);
    00084
    00085     // turn on interrupts
    00086     sei();
    00087 }
    00088
    00089 //! create and initialize the uart buffers
    00090 void uartswInitBuffers(void)
    00091 {
    00092     // initialize the UART receive buffer
    00093     bufferInit(&uartswRxBuffer, uartswRxData, UARTSW_RX_BUFFER_SIZE);
    00094 }
    00095
    00096 //! turns off software UART
    00097 void uartswOff(void)
    00098 {
    00099     // disable interrupts
    00100     cbi(TIMSK, OCIE1A);
    00101     cbi(TIMSK, OCIE1B);
    00102     cbi(TIMSK, TICIE1);
    00103     // detach the service routines
    00104     timerDetach(TIMER1OUTCOMPAREA_INT);
    00105     timerDetach(TIMER1OUTCOMPAREB_INT);
    00106     timerDetach(TIMER1INPUTCAPTURE_INT);
    00107 }
    00108
    00109 void uartswSetBaudRate(u32 baudrate)
    00110 {
    00111     // set timer prescaler
    00112     timer1SetPrescaler(TIMER_CLK_DIV1);
    00113     // calculate division factor for requested baud rate, and set it
    00114     UartswBaudRateDiv = (u16)((F_CPU+(baudrate/2L))/(baudrate*1L));
    00115 }
    00116
    00117 //! returns the receive buffer structure
    00118 cBuffer* uartswGetRxBuffer(void)
    00119 {
    00120     // return rx buffer pointer
    00121     return &uartswRxBuffer;
    00122 }
    00123
    00124 void uartswSendByte(u08 data)
    00125 {
    00126     // wait until uart is ready
    00127     while(UartswTxBusy);
    00128     // set busy flag
    00129     UartswTxBusy = TRUE;
    00130     // save data
    00131     UartswTxData = data;
    00132     // set number of bits (+1 for stop bit)
    00133     UartswTxBitNum = 9;
    00134
    00135     // set the start bit
    00136     #ifdef UARTSW_INVERT
    00137     sbi(UARTSW_TX_PORT, UARTSW_TX_PIN);
    00138     #else
    00139     cbi(UARTSW_TX_PORT, UARTSW_TX_PIN);
    00140     #endif
    00141
    00142     // schedule the next bit
    00143     outw(OCR1A, inw(TCNT1) + UartswBaudRateDiv);
    00144     // enable OC1A interrupt
    00145     sbi(TIMSK, OCIE1A);
    00146 }
    00147
    00148 //! gets a byte (if available) from the uart receive buffer
    00149 u08 uartswReceiveByte(u08* rxData)
    00150 {
    00151     // make sure we have a receive buffer
    00152     if(uartswRxBuffer.size)
    00153     {
    00154         // make sure we have data
    00155         if(uartswRxBuffer.datalength)
    00156         {
    00157             // get byte from beginning of buffer
    00158             *rxData = bufferGetFromFront(&uartswRxBuffer);
    00159             return TRUE;
    00160         }
    00161         else
    00162         {
    00163             // no data
    00164             return FALSE;
    00165         }
    00166     }
    00167     else
    00168     {
    00169         // no buffer
    00170         return FALSE;
    00171     }
    00172 }
    00173
    00174 void uartswTxBitService(void)
    00175 {
    00176     if(UartswTxBitNum)
    00177     {
    00178         // there are bits still waiting to be transmitted
    00179         if(UartswTxBitNum > 1)
    00180         {
    00181             // transmit data bits (inverted, LSB first)
    00182             #ifdef UARTSW_INVERT
    00183             if( !(UartswTxData & 0x01) )
    00184             #else
    00185             if( (UartswTxData & 0x01) )
    00186             #endif
    00187                 sbi(UARTSW_TX_PORT, UARTSW_TX_PIN);
    00188             else
    00189                 cbi(UARTSW_TX_PORT, UARTSW_TX_PIN);
    00190             // shift bits down
    00191             UartswTxData = UartswTxData>>1;
    00192         }
    00193         else
    00194         {
    00195             // transmit stop bit
    00196             #ifdef UARTSW_INVERT
    00197             cbi(UARTSW_TX_PORT, UARTSW_TX_PIN);
    00198             #else
    00199             sbi(UARTSW_TX_PORT, UARTSW_TX_PIN);
    00200             #endif
    00201         }
    00202         // schedule the next bit
    00203         outw(OCR1A, inw(OCR1A) + UartswBaudRateDiv);
    00204         // count down
    00205         UartswTxBitNum--;
    00206     }
    00207     else
    00208     {
    00209         // transmission is done
    00210         // clear busy flag
    00211         UartswTxBusy = FALSE;
    00212     }
    00213 }
    00214
    00215 void uartswRxBitService(void)
    00216 {
    00217     // this function runs on either:
    00218     // - a rising edge interrupt
    00219     // - OC1B
    00220     if(!UartswRxBusy)
    00221     {
    00222         // this is a start bit
    00223         // disable ICP interrupt
    00224         cbi(TIMSK, TICIE1);
    00225         // schedule data bit sampling 1.5 bit periods from now
    00226         outw(OCR1B, inw(TCNT1) + UartswBaudRateDiv + UartswBaudRateDiv/2);
    00227         // clear OC1B interrupt flag
    00228         sbi(TIFR, OCF1B);
    00229         // enable OC1B interrupt
    00230         sbi(TIMSK, OCIE1B);
    00231         // set start bit flag
    00232         UartswRxBusy = TRUE;
    00233         // reset bit counter
    00234         UartswRxBitNum = 0;
    00235         // reset data
    00236         UartswRxData = 0;
    00237     }
    00238     else
    00239     {
    00240         // start bit has already been received
    00241         // we're in the data bits
    00242
    00243         // shift data byte to make room for new bit
    00244         UartswRxData = UartswRxData>>1;
    00245
    00246         // sample the data line
    00247         #ifdef UARTSW_INVERT
    00248         if( !(inb(UARTSW_RX_PORTIN) & (1<<UARTSW_RX_PIN)) )
    00249         #else
    00250         if( (inb(UARTSW_RX_PORTIN) & (1<<UARTSW_RX_PIN)) )
    00251         #endif
    00252         {
    00253             // serial line is marking
    00254             // record '1' bit
    00255             UartswRxData |= 0x80;
    00256         }
    00257
    00258         // increment bit counter
    00259         UartswRxBitNum++;
    00260         // schedule next bit sample
    00261         outw(OCR1B, inw(OCR1B) + UartswBaudRateDiv);
    00262
    00263         // check if we have a full byte
    00264         if(UartswRxBitNum >= 8)
    00265         {
    00266             // save data in receive buffer
    00267             bufferAddToEnd(&uartswRxBuffer, UartswRxData);
    00268             // disable OC1B interrupt
    00269             cbi(TIMSK, OCIE1B);
    00270             // clear ICP interrupt flag
    00271             sbi(TIFR, ICF1);
    00272             // enable ICP interrupt
    00273             sbi(TIMSK, TICIE1);
    00274             // clear start bit flag
    00275             UartswRxBusy = FALSE;
    00276         }
    00277     }
    00278 }
    00279
    00280 /*
    00281 void uartswRxBitService(void)
    00282 {
    00283     u16 thisBitTime;
    00284     u08 bitperiods;
    00285     u08 i;
    00286
    00287     // bit transition was detected
    00288     // record bit's edge time
    00289     thisBitTime = inw(ICR1);
    00290
    00291     cbi(PORTB, 0);
    00292
    00293     if(!UartswRxStartBit)
    00294     {
    00295         // this is a start bit
    00296         // switch to falling-edge trigger
    00297         cbi(TCCR1B, ICES1);
    00298         // record bit time
    00299         UartswRxBitTime = thisBitTime;
    00300         // set start bit flag
    00301         UartswRxStartBit = TRUE;
    00302         // reset bit counter
    00303         UartswRxBitNum = 0;
    00304         // reset data
    00305         UartswRxData = 0;
    00306     }
    00307     else
    00308     {
    00309         // start bit has already been received
    00310         // we're in the data bits
    00311
    00312         // how many bit periods since last edge?
    00313         bitperiods = (thisBitTime - UartswRxBitTime + UartswBaudRateDiv/2)/UartswBaudRateDiv;
    00314         // set last edge time
    00315         UartswRxBitTime = thisBitTime;
    00316
    00317         if(bitperiods > 10)
    00318         {
    00319             // switch to trigger on rising edge
    00320             sbi(TCCR1B, ICES1);
    00321             // clear start bit flag
    00322             UartswRxStartBit = FALSE;
    00323         }
    00324         else
    00325         {
    00326
    00327
    00328         if( inb(TCCR1B) & (1<<ICES1) )
    00329         {
    00330             // just triggered on a rising edge
    00331             // previous bits were zero
    00332             // shift in the data (data bits are inverted)
    00333             for(i=0; i<bitperiods; i++)
    00334             {
    00335                 UartswRxData = UartswRxData<<1;
    00336                 UartswRxData |= 0x01;
    00337             }
    00338             // switch to trigger on falling edge
    00339             cbi(TCCR1B, ICES1);
    00340         }
    00341         else
    00342         {
    00343             // just triggered on a falling edge
    00344             // previous bits were one
    00345             // shift in the data (data bits are inverted)
    00346             for(i=0; i<bitperiods; i++)
    00347             {
    00348                 UartswRxData = UartswRxData<<1;
    00349             }
    00350             // switch to trigger on rising edge
    00351             sbi(TCCR1B, ICES1);
    00352         }
    00353
    00354         // increment bit counter
    00355         UartswRxBitNum += bitperiods;
    00356
    00357         // check if we have a full byte + start bit
    00358         if(bitperiods > 8)
    00359         {
    00360             // save data in receive buffer
    00361             bufferAddToEnd(&uartswRxBuffer, UartswRxData);
    00362             // switch to trigger on rising edge
    00363             sbi(TCCR1B, ICES1);
    00364             // clear start bit flag
    00365             UartswRxStartBit = FALSE;
    00366         }
    00367         }
    00368     }
    00369
    00370     // turn off debug LEDs
    00371     delay(10);
    00372     sbi(PORTB, 0);
    00373     sbi(PORTB, 1);
    00374 }
    00375 */