?lang_form? ?lang_select? ?lang_submit? ?lang_endform?
{HEADER END}
{FILE START}

library

?curdirlinks? - Rev 6

?prevdifflink? - Blame - ?getfile?

/*! \file uartsw2.c \brief Software Interrupt-driven UART Driver. */
//*****************************************************************************
//
// File Name    : 'uartsw2.c'
// Title                : Software Interrupt-driven UART Driver
// Author               : Pascal Stang - Copyright (C) 2002-2004
// Created              : 7/20/2002
// Revised              : 4/27/2004
// Version              : 0.6
// Target MCU   : Atmel AVR Series (intended for the ATmega16 and ATmega32)
// Editor Tabs  : 4
//
// This code is distributed under the GNU Public License
//              which can be found at http://www.gnu.org/licenses/gpl.txt
//
//*****************************************************************************

#include <avr/io.h>
#include <avr/interrupt.h>

#include "global.h"
#include "timer.h"
#include "uartsw2.h"

// Program ROM constants

// Global variables

// uartsw transmit status and data variables
static volatile u08 UartswTxBusy;
static volatile u08 UartswTxData;
static volatile u08 UartswTxBitNum;

// baud rate common to transmit and receive
static volatile u08 UartswBaudRateDiv;

// uartsw receive status and data variables
static volatile u08 UartswRxBusy;
static volatile u08 UartswRxData;
static volatile u08 UartswRxBitNum;
// receive buffer
static cBuffer uartswRxBuffer;               ///< uartsw receive buffer
// automatically allocate space in ram for each buffer
static char uartswRxData[UARTSW_RX_BUFFER_SIZE];

// functions

//! enable and initialize the software uart
void uartswInit(void)
{
    // initialize the buffers
        uartswInitBuffers();
        // initialize the ports
        sbi(UARTSW_TX_DDR, UARTSW_TX_PIN);
        #ifdef UARTSW_INVERT
        cbi(UARTSW_TX_PORT, UARTSW_TX_PIN);
        #else
        sbi(UARTSW_TX_PORT, UARTSW_TX_PIN);
        #endif
        cbi(UARTSW_RX_DDR, UARTSW_RX_PIN);
        cbi(UARTSW_RX_PORT, UARTSW_RX_PIN);
        // initialize baud rate
        uartswSetBaudRate(9600);
        
        // setup the transmitter
        UartswTxBusy = FALSE;
        // disable OC2 interrupt
        cbi(TIMSK, OCIE2);
        // attach TxBit service routine to OC2
        timerAttach(TIMER2OUTCOMPARE_INT, uartswTxBitService);
                
        // setup the receiver
        UartswRxBusy = FALSE;
        // disable OC0 interrupt
        cbi(TIMSK, OCIE0);
        // attach RxBit service routine to OC0
        timerAttach(TIMER0OUTCOMPARE_INT, uartswRxBitService);
        // INT2 trigger on rising/falling edge
        #ifdef UARTSW_INVERT
        sbi(MCUCSR, ISC2);      // rising edge
        #else
        cbi(MCUCSR, ISC2);      // falling edge
        #endif
        // enable INT2 interrupt
        sbi(GICR, INT2);

        // turn on interrupts
        sei();
}

//! create and initialize the uart buffers
void uartswInitBuffers(void)
{
        // initialize the UART receive buffer
        bufferInit(&uartswRxBuffer, uartswRxData, UARTSW_RX_BUFFER_SIZE);
}

//! turns off software UART
void uartswOff(void)
{
        // disable interrupts
        cbi(TIMSK, OCIE2);
        cbi(TIMSK, OCIE0);
        cbi(GICR, INT2);
        // detach the service routines
        timerDetach(TIMER2OUTCOMPARE_INT);
        timerDetach(TIMER0OUTCOMPARE_INT);
}

void uartswSetBaudRate(u32 baudrate)
{
        u16 div;

        // set timer prescaler
        if( baudrate > (F_CPU/64L*256L) )
        {
                // if the requested baud rate is high,
                // set timer prescalers to div-by-64
                timer2SetPrescaler(TIMERRTC_CLK_DIV64);
                timer0SetPrescaler(TIMER_CLK_DIV64);
                div = 64;
        }
        else
        {
                // if the requested baud rate is low,
                // set timer prescalers to div-by-256
                timer2SetPrescaler(TIMERRTC_CLK_DIV256);
                timer0SetPrescaler(TIMER_CLK_DIV256);
                div = 256;
        }

        // calculate division factor for requested baud rate, and set it
        //UartswBaudRateDiv = (u08)(((F_CPU/64L)+(baudrate/2L))/(baudrate*1L));
        //UartswBaudRateDiv = (u08)(((F_CPU/256L)+(baudrate/2L))/(baudrate*1L));
        UartswBaudRateDiv = (u08)(((F_CPU/div)+(baudrate/2L))/(baudrate*1L));
}

//! returns the receive buffer structure 
cBuffer* uartswGetRxBuffer(void)
{
        // return rx buffer pointer
        return &uartswRxBuffer;
}

void uartswSendByte(u08 data)
{
        // wait until uart is ready
        while(UartswTxBusy);
        // set busy flag
        UartswTxBusy = TRUE;
        // save data
        UartswTxData = data;
        // set number of bits (+1 for stop bit)
        UartswTxBitNum = 9;
        
        // set the start bit
        #ifdef UARTSW_INVERT
        sbi(UARTSW_TX_PORT, UARTSW_TX_PIN);
        #else
        cbi(UARTSW_TX_PORT, UARTSW_TX_PIN);
        #endif
        // schedule the next bit
        outb(OCR2, inb(TCNT2) + UartswBaudRateDiv);
        // enable OC2 interrupt
        sbi(TIMSK, OCIE2);
}

//! gets a byte (if available) from the uart receive buffer
u08 uartswReceiveByte(u08* rxData)
{
        // make sure we have a receive buffer
        if(uartswRxBuffer.size)
        {
                // make sure we have data
                if(uartswRxBuffer.datalength)
                {
                        // get byte from beginning of buffer
                        *rxData = bufferGetFromFront(&uartswRxBuffer);
                        return TRUE;
                }
                else
                {
                        // no data
                        return FALSE;
                }
        }
        else
        {
                // no buffer
                return FALSE;
        }
}

void uartswTxBitService(void)
{
        if(UartswTxBitNum)
        {
                // there are bits still waiting to be transmitted
                if(UartswTxBitNum > 1)
                {
                        // transmit data bits (inverted, LSB first)
                        #ifdef UARTSW_INVERT
                        if( !(UartswTxData & 0x01) )
                        #else
                        if( (UartswTxData & 0x01) )
                        #endif
                                sbi(UARTSW_TX_PORT, UARTSW_TX_PIN);
                        else
                                cbi(UARTSW_TX_PORT, UARTSW_TX_PIN);
                        // shift bits down
                        UartswTxData = UartswTxData>>1;
                }
                else
                {
                        // transmit stop bit
                        #ifdef UARTSW_INVERT
                        cbi(UARTSW_TX_PORT, UARTSW_TX_PIN);
                        #else
                        sbi(UARTSW_TX_PORT, UARTSW_TX_PIN);
                        #endif
                }
                // schedule the next bit
                outb(OCR2, inb(OCR2) + UartswBaudRateDiv);
                // count down
                UartswTxBitNum--;
        }
        else
        {
                // transmission is done
                // clear busy flag
                UartswTxBusy = FALSE;
                // disable OC2 interrupt
                cbi(TIMSK, OCIE2);
        }
}

void uartswRxBitService(void)
{
        // this function runs on either:
        // - a rising edge interrupt
        // - Timer 0 output compare
        if(!UartswRxBusy)
        {
                // UART was not previously busy,
                // this must be is a start bit
                
                // disable INT2 interrupt
                cbi(GICR, INT2);
                // schedule data bit sampling 1.5 bit periods from now
                outb(OCR0, inb(TCNT0) + UartswBaudRateDiv + UartswBaudRateDiv/2);
                // clear OC0 interrupt flag
                sbi(TIFR, OCF0);
                // enable OC0 interrupt
                sbi(TIMSK, OCIE0);
                // set busy flag
                UartswRxBusy = TRUE;
                // reset bit counter
                UartswRxBitNum = 0;
                // reset data
                UartswRxData = 0;
        }
        else
        {
                // start bit has already been received
                // we're in the data bits
                
                // shift data byte to make room for new bit
                UartswRxData = UartswRxData>>1;

                // sample the data line
                #ifdef UARTSW_INVERT
                if( !(inb(UARTSW_RX_PORTIN) & (1<<UARTSW_RX_PIN)) )
                #else
                if( (inb(UARTSW_RX_PORTIN) & (1<<UARTSW_RX_PIN)) )
                #endif
                {
                        // serial line is marking
                        // record '1' bit
                        UartswRxData |= 0x80;
                }

                // increment bit counter
                UartswRxBitNum++;
                // schedule next bit sample
                outb(OCR0, inb(OCR0) + UartswBaudRateDiv);

                // check if we have a full byte
                if(UartswRxBitNum >= 8)
                {
                        // save data in receive buffer
                        bufferAddToEnd(&uartswRxBuffer, UartswRxData);
                        // disable OC0 interrupt
                        cbi(TIMSK, OCIE0);
                        // clear INT2 interrupt flag
                        sbi(GIFR, INTF2);
                        // enable INT interrupt
                        sbi(GICR, INT2);
                        // clear busy flag
                        UartswRxBusy = FALSE;
                }
        }
}

SIGNAL(SIG_INTERRUPT2)
{
        // run RxBit service routine
        uartswRxBitService();
}
{FILE END}
{FOOTER START}

Powered by WebSVN v2.8.3