MHVLib  20111011
An efficiency oriented runtime library for AVR microcontrollers
A:/eclipse/mhvlib/MHV_HardwareSerial.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2011, Make, Hack, Void Inc
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *  * Redistributions of source code must retain the above copyright
00008  *    notice, this list of conditions and the following disclaimer.
00009  *  * Redistributions in binary form must reproduce the above copyright
00010  *    notice, this list of conditions and the following disclaimer in the
00011  *    documentation and/or other materials provided with the distribution.
00012  *  * Neither the name of the Make, Hack, Void nor the
00013  *    names of its contributors may be used to endorse or promote products
00014  *    derived from this software without specific prior written permission.
00015  *
00016  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00017  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00018  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00019  * DISCLAIMED. IN NO EVENT SHALL MAKE, HACK, VOID BE LIABLE FOR ANY
00020  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00021  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00022  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00023  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00024  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00025  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00026  */
00027 
00028 
00029 #include <stdio.h>
00030 #include <avr/sfr_defs.h>
00031 #include <avr/pgmspace.h>
00032 #include <stdlib.h>
00033 #include <string.h>
00034 #include <inttypes.h>
00035 
00036 #include "MHV_HardwareSerial.h"
00037 
00054 MHV_HardwareSerial::MHV_HardwareSerial(MHV_RingBuffer *rxBuffer, MHV_RingBuffer *txBuffer,
00055                 volatile uint16_t *ubrr, volatile uint8_t *ucsra, volatile uint8_t *ucsrb,
00056                 volatile uint8_t *udr, uint8_t rxen, uint8_t txen, uint8_t rxcie,
00057                 uint8_t txcie, uint8_t udre, uint8_t u2x, unsigned long baud) :
00058                 MHV_Device_TX(txBuffer), MHV_Device_RX(rxBuffer) {
00059         _echo = false;
00060         _ubrr = ubrr;
00061         _ucsra = ucsra;
00062         _ucsrb = ucsrb;
00063         _udr = udr;
00064         _rxen = rxen;
00065         _txen = txen;
00066         _rxcie = rxcie;
00067         _txcie = txcie;
00068         _udre = udre;
00069         _u2x = u2x;
00070         _tx = NULL;
00071 
00072         setSpeed(baud);
00073 }
00074 
00078 void MHV_HardwareSerial::rx() {
00079         char c = *_udr;
00080         _rxBuffer->append(c);
00081 
00082         if (_echo && ((*_ucsra) & (1 << _udre))) {
00083                 *_udr = c;
00084         }
00085 }
00086 
00090 void MHV_HardwareSerial::tx() {
00091         int c = nextCharacter();
00092 
00093         if (-1 == c) {
00094                 // Nothing more to send, disable the TX interrupt
00095                 _tx = NULL;
00096                 *_ucsrb &= ~_BV( _txcie);
00097                 return;
00098         }
00099 
00100         *_udr = (char)c;
00101 }
00102 
00106 void MHV_HardwareSerial::runTxBuffers() {
00107         int c = nextCharacter();
00108         if (-1 == c) {
00109 // This should never happen
00110                 return;
00111         }
00112 
00113         // Enable tx interrupt
00114         *_ucsrb |= _BV( _txcie);
00115 
00116         // If the UART isn't already sending data, start sending
00117         while (!((*_ucsra) & (1 << _udre))) {}
00118 
00119         *_udr = (char)c;
00120 }
00121 
00126 void MHV_HardwareSerial::setSpeed(unsigned long baud) {
00127 /* Use U2X if the requested baud rate is higher than (F_CPU/16),
00128  * otherwise use whatever has the least error
00129  */
00130         if (baud > F_CPU / 16 ||
00131                         abs((int)(255-((F_CPU/(8*(((F_CPU/4/baud-1)/2)+1))*255)/baud))) < abs((int)(255-((F_CPU/(16*(((F_CPU/8/baud-1)/2)+1))*255)/baud)))) {
00132                 *_ucsra = _BV(_u2x);
00133                 *_ubrr = (uint16_t)(F_CPU / 4 / baud - 1) / 2;
00134         } else {
00135                 *_ucsra = 0;
00136                 *_ubrr = (uint16_t)(F_CPU / 8 / baud - 1) / 2;
00137         }
00138 
00139         *_ucsrb |= _BV( _rxen) | _BV( _txen) | _BV( _rxcie);
00140 }
00141 
00145 void MHV_HardwareSerial::end() {
00146         *_ucsrb &= ~_BV( _rxen) & ~_BV( _txen) & ~_BV( _rxcie) & ~_BV( _txcie);
00147 }
00148 
00155 void MHV_HardwareSerial::echo(bool echoOn) {
00156         _echo = echoOn;
00157 }
00158 
00164 bool MHV_HardwareSerial::canSendBusy() {
00165         return ((NULL == _tx) && ((*_ucsra) & (1 << _udre)));
00166 }
00167 
00173 void MHV_HardwareSerial::busyWrite(char c) {
00174         while (!canSendBusy()) {};
00175         *_ucsrb &= ~_BV( _txcie);
00176 
00177         while (!((*_ucsra) & (1 << _udre))) {}
00178 
00179         *_udr = c;
00180 }
00181 
00187 void MHV_HardwareSerial::busyWrite_P(PGM_P buffer) {
00188         const char *p;
00189 
00190         while (!canSendBusy()) {};
00191         *_ucsrb &= ~_BV( _txcie);
00192 
00193         p = buffer;
00194         char c = pgm_read_byte(p++);
00195         while (c != '\0') {
00196                 while (!((*_ucsra) & (1 << _udre))) {}
00197 
00198                 *_udr = c;
00199                 c = pgm_read_byte(p++);
00200         }
00201 }
00202 
00203 
00209 void MHV_HardwareSerial::busyWrite(const char *buffer) {
00210         const char *p;
00211 
00212         while (!canSendBusy()) {};
00213         *_ucsrb &= ~_BV( _txcie);
00214 
00215         for (p = buffer; *p != '\0';) {
00216                 while (!((*_ucsra) & (1 << _udre))) {}
00217 
00218                 *_udr = *(p++);
00219         }
00220 }
00221 
00227 void MHV_HardwareSerial::busyWrite_P(PGM_P buffer, uint16_t length) {
00228         uint16_t i;
00229 
00230         while (!canSendBusy()) {};
00231         *_ucsrb &= ~_BV( _txcie);
00232 
00233         for (i = 0; i < length; i++) {
00234                 /* Don't need to check return values as we have already checked up front
00235                  * and async writes can't be initiated until we're done
00236                  */
00237                 busyWrite(pgm_read_byte(buffer + i));
00238         }
00239 }
00240 
00246 void MHV_HardwareSerial::busyWrite(const char *buffer, uint16_t length) {
00247         uint16_t i;
00248 
00249         while (!canSendBusy()) {};
00250         *_ucsrb &= ~_BV( _txcie);
00251 
00252         for (i = 0; i < length; i++) {
00253                 /* Don't need to check return values as we have already checked up front
00254                  * and async writes can't be initiated until we're done
00255                  */
00256                 busyWrite(buffer[i]);
00257         }
00258 }
00259 
00266 bool MHV_HardwareSerial::busy(void) {
00267         return !((*_ucsra) & (1 << _udre));
00268 }
00269 
00278 void MHV_HardwareSerial::debug(const char *file, int line, const char *function,
00279                 PGM_P format, ...) {
00280         char    debugBuffer[80];
00281         va_list ap;
00282         va_start(ap, format);
00283 
00284         while (!canSendBusy()) {}
00285 
00286         snprintf_P(debugBuffer, sizeof(debugBuffer), PSTR("%s:%d\t%s():\t\t"),
00287                         file, line, function);
00288         busyWrite(debugBuffer);
00289 
00290         vsnprintf_P(debugBuffer, sizeof(debugBuffer), format, ap);
00291         busyWrite(debugBuffer);
00292         busyWrite_P(PSTR("\r\n"));
00293 }