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 */

Generated on Sun Oct 29 03:41:08 2006 for Procyon AVRlib by  doxygen 1.4.2