Show Posts
Pages: 1 [2] 3 4 5
16  Forum 2005-2010 (read only) / Syntax & Programs / Re: Interrupts... on: May 27, 2009, 03:56:42 am
Quote
Ideally, I will need to disable all external interrupts and all timers except the one I'll be using...

If I'm understanding your goals right, you won't need to disable the other timers.  While in an interrupt, no other interrupts can run, so nothing can interfere with your 1000Hz timer.

As far as generating a 1000Hz square wave, though, you could probably get that just by manually configuring the PWM frequency.  That way, it would be set and forget, and you would have 100% of your processor speed left over to do other things.

Also, beware of trying to do an analogRead() inside an interrupt-- it takes a long time to run, and so you risk locking up your program.

17  Forum 2005-2010 (read only) / Syntax & Programs / Re: using the Timer1 library for PCM audio output on: May 26, 2009, 03:22:39 pm
Okay, I did get it working by manually configuring timer1.  Changes in capacitance are reflected as a change in frequency in the speaker output.

It's definitely quick and dirty, but fwiw here's the code (I'm using Paul Badger's capacitance sensing code here: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1171076259):
Code:

int  i;
unsigned int x, y;
float accum, fval = .20;    // these are variables for a simple low-pass (smoothing) filter - fval of 1 = no filter - .001 = max filter
long fout;

int period = 160;


char sineovertones[] = {0,84,106,99,113,127,107,79,71,51,1,-26,7,43,23,-17,-26,-23,-43,-60,-49,-44,-66,-63,0,63,66,44,49,60,43,23,26,17,-23,-43,-8,26,-1,-51,-71,-79,-107,-127,-113,-99,-106,-84};


char sine[] = {0, 22, 44, 64, 82, 98, 111, 120, 126, 128, 126, 120, 111, 98, 82, 64, 44, 22, 0, -22, -44, -64, -82, -98, -111, -120, -126, -128, -126, -120, -111, -98, -82, -64, -44, -22};
//a simple sine wave with 36 samples

int analog0 = 160; //variable used to store analog input pin 0

byte speakerpin = 3;  //audio playback on pin 3.  This can also be set to pin 11.
 
volatile byte waveindex = 0; //index variable for position in waveform array Sine[]
volatile byte currentvalue = 0;

void setup() {
  
Serial.begin(57600);
  
/************************** PWM audio configuration ****************************/
// Configures PWM on pins 3 and 11 to run at maximum speed, rather than the default
// 500Hz, which is useless for audio output

pinMode(3,OUTPUT); //Speaker on pin 3

cli(); //disable interrupts while registers are configured

bitSet(TCCR2A, WGM20);
bitSet(TCCR2A, WGM21); //set Timer2 to fast PWM mode (doubles PWM frequency)

bitSet(TCCR2B, CS20);
bitClear(TCCR2B, CS21);
bitClear(TCCR2B, CS22);
/* set prescaler to /1 (no prescaling).  The timer will overflow every
*  62.5nS * 256ticks = 16uS, giving a PWM frequency of 62,500Hz, I think.   */

sei(); //enable interrupts now that registers have been set
  
  
/************************* Timer 1 interrupt configuration *************************/

cli(); //disable interrupts while registers are configured

bitClear(TCCR1A, COM1A1);
bitClear(TCCR1A, COM1A1);
bitClear(TCCR1A, COM1A1);
bitClear(TCCR1A, COM1A1);
/* Normal port operation, pins disconnected from timer operation (breaking pwm).  
*  Should be set this way by default, anyway. */

bitClear(TCCR1A, WGM10);
bitClear(TCCR1A, WGM11);
bitSet(TCCR1B, WGM12);
bitClear(TCCR1B, WGM13);
/* Mode 4, CTC with TOP set by register OCR1A.  Allows us to set variable timing for
*  the interrupt by writing new values to OCR1A. */

bitClear(TCCR1B, CS10);
bitSet(TCCR1B, CS11);
bitClear(TCCR1B, CS12);
/* set the clock prescaler to /8.  Since the processor ticks every 62.5ns, the timer
*  will increment every .5uS.  Timer 1 is a 16-bit timer, so the maximum value is 65536,
*  Giving us a theoretical range of .5us-32.7mS.  There are 48 samples, so the
*  theoretical frequency range is 41.7KHz - .635Hz, which neatly covers the audio
*  spectrum of 20KHz-20Hz.  Theoretical, because I wouldn't recommend actually calling
*  the Timer1 interrupt every .5uS :)  */

bitClear(TCCR1C, FOC1A);
bitClear(TCCR1C, FOC1B);
/* Disable Force Output Compare for Channels A and B, whatever that is.
*  Should be set this way by default anyway. */

OCR1A = 160;
/* Initializes Output Compare Register A at 160, so a match will be generated every
*  62.5nS * 8 * 160 = 80uS, for a 1/(80uS*48) = 260Hz tone. */

bitClear(TIMSK1, ICIE1); //disable input capture interrupt
bitClear(TIMSK1, OCIE1B); //disable Output Compare B Match Interrupt
bitSet(TIMSK1, OCIE1A); //enable Output Compare A Match Interrupt
bitClear(TIMSK1, TOIE1); //disable Overflow Interrupt Enable

sei(); //enable interrupts now that registers have been set

delay(1000);

}//end setup()
 
 
 
ISR(TIMER1_COMPA_vect) {
/* timer1 ISR.  Every time it is called it sets
*  speakerpin to the next value in Sine[].  frequency modulation is done by changing
*  the timing between successive calls of this function, e.g. for a 1KHz tone,
*  set the timing so that it runs through Sine[] 1000 times a second. */

if (waveindex > 47) { //reset waveindex if it has reached the end of the array
  waveindex = 0;
  }

analogWrite(speakerpin, sineovertones[waveindex] + 128);
waveindex++;

OCR1A = period;

} //end Timer1 ISR
 
void loop()
{
  
  
//analog0 = analogRead(0) + 4;
//Serial.println(analog0);
  
 y = 0;        // clear out variables
 x = 0;

 for (i=0; i < 4 ; i++ ){       // do it four times to build up an average - not really neccessary but takes out some jitter

   // LOW-to-HIGH transition
   digitalWrite(8, HIGH);    
   // output pin is PortB0 (Arduino 8), sensor pin is PortB1 (Arduinio 9)                                  

   while ((PINB & B100) != B100 ) {        // while the sense pin is not high
     //  while (digitalRead(9) != 1)     // same as above port manipulation above - only 20 times slower!                
     x++;
   }
   delay(1);

   //  HIGH-to-LOW transition
   digitalWrite(8, LOW);              
   while((PINB & B100) != 0 ){            // while pin is not low  -- same as below only 20 times faster
     // while(digitalRead(9) != 0 )      // same as above port manipulation - only 20 times slower!
     y++;  
   }

   delay(1);
 }

 fout =  (fval * (float)x) + ((1-fval) * accum);  // Easy smoothing filter "fval" determines amount of new data in fout
 accum = fout;  
period = constrain(fout / 3, 50, 500);
Serial.println(period);


}//end loop()



Perhaps whoever maintains the timer1 library should take a gander at why you can't change timer frequency dynamically?
18  Forum 2005-2010 (read only) / Syntax & Programs / Re: using the Timer1 library for PCM audio output on: May 25, 2009, 03:09:10 am
Sorry, example code here:
Code:
#include "TimerOne.h"

char sineovertones[] = {0,84,106,99,113,127,107,79,71,51,1,-26,7,43,23,-17,-26,-23,-43,-60,-49,-44,-66,-63,0,63,66,44,49,60,43,23,26,17,-23,-43,-8,26,-1,-51,-71,-79,-107,-127,-113,-99,-106,-84};


char sine[] = {0, 22, 44, 64, 82, 98, 111, 120, 126, 128, 126, 120, 111, 98, 82, 64, 44, 22, 0, -22, -44, -64, -82, -98, -111, -120, -126, -128, -126, -120, -111, -98, -82, -64, -44, -22};
//a simple sine wave with 36 samples

long analog0 = 160; //variable to store input from analog input pin 0

byte speakerpin = 3;  //audio playback on pin 3.  This can also be set to pin 11.
 
volatile byte waveindex = 0; //index variable for position in waveform array Sine[]
volatile byte currentvalue = 0;

void setup() {
  
Serial.begin(57600);
  
  
/************************** PWM audio configuration ****************************/
// Configures PWM on pins 3 and 11 to run at maximum speed, rather than the default
// 500Hz, which is useless for audio output

pinMode(speakerpin,OUTPUT); //Speaker on pin 3

cli(); //disable interrupts while registers are configured

bitSet(TCCR2A, WGM20);
bitSet(TCCR2A, WGM21); //set Timer2 to fast PWM mode (doubles PWM frequency)

bitSet(TCCR2B, CS20);
bitClear(TCCR2B, CS21);
bitClear(TCCR2B, CS22);
/* set prescaler to /1 (no prescaling).  The timer will overflow every
*  62.5nS * 256ticks = 16uS, giving a frequency of 62,500Hz, I think.   */

sei(); //enable interrupts now that registers have been set
  
  
/************************* Timer 1 interrupt configuration *************************/

Timer1.initialize(160);         // initialize timer1, and set a 1/2 second period
Timer1.attachInterrupt(playtone);  // attaches playtone() as a timer overflow interrupt
}
 
 
 
void playtone() {
/* This function is called by the timer1 interrupt.  Every time it is called it sets
*  speakerpin to the next value in Sine[].  frequency modulation is done by changing
*  the timing between successive calls of this function, e.g. for a 1KHz tone,
*  set the timing so that it runs through Sine[] 1000 times a second. */
if (waveindex > 47) {
  waveindex = 0;
  }

analogWrite(speakerpin, sineovertones[waveindex] + 128);
waveindex++;
}
 
void loop()
{

analog0 = analogRead(0) / 2;
Serial.println(analog0);
delay(100);
Timer1.setPeriod(analog0);
 
}

19  Forum 2005-2010 (read only) / Syntax & Programs / using the Timer1 library for PCM audio output on: May 25, 2009, 03:02:59 am
Hi All,
    I'm trying to generate audio from the Arduino, using Pulse Code Modulation.  

If you're not familiar with this technique, I take a waveform (let's say a sine wave), divide it up into 48 samples, and encode it's level at that point into a char array.  Then I set up an interrupt on timer1 so that every time it fires, it sets one of the timer2 pwm pins the current sample point, then increments the array index.  I've changed the prescaler on timer2 so that the pwm frequency is 62,500 Hz, so once I put the output through a low-pass filter it sounds...surprisingly decent, actually.

So this all works as expected with a static frequency.  On my oscilloscope, I can see the waveform that I've encoded, and I can hear it on my speaker.

Now, I should be able to vary the frequency by changing how quickly the timer1 interrupt is called, i.e. how quickly it steps through the array of samples.  I'm currently using the Timer1 library in the Arduino playground, but when I try to vary the interrupt period, I get squat-- no output at all.

Has anyone used this library with varying interrupt frequencies?  Or had success generating sound with this method?  I'd like to use the library because manually configuring timers makes me  :'(
20  Forum 2005-2010 (read only) / Syntax & Programs / Re: newbie, need critique/suggestions for clock code on: May 25, 2009, 03:05:38 pm
If you're interested in doing more advanced projects with the Arduino, I think it will repay you many times over to learn how to configure a timer and its associated interrupt(s) manually.  It's a real pain to figure out the first time, but get ye to the datasheet, I say.  

You could use Timer1 in CTC mode with a /256 prescaler.  Then the timer will tick 62,500 times per second, so you set your output compare register to 62500, and write the interrupt to increment your seconds variable every time it fires.  Voila, you have a subroutine that will run exactly every 1 second, independent of how much you're loading down your processor with your main program routine.
21  Forum 2005-2010 (read only) / Development / Re: Using own interrupt handlers: Collision of ISRs on: March 07, 2009, 04:54:03 pm
I found a sort of work-around, which I've posted here:

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1236223363/0

sorry about the double post.
22  Forum 2005-2010 (read only) / Development / Re: Using own interrupt handlers: Collision of ISRs on: March 06, 2009, 03:14:28 am
Thanks for the info, but I'm trying to receive DMX, not send it.

How do I override the default USART_RX_vect ISR?
23  Forum 2005-2010 (read only) / Development / Re: Using own interrupt handlers: Collision of ISRs on: March 03, 2009, 10:10:41 pm
Hello,
       I am working on the same project, in a similar plan of attack as you, Zeb.

My serial receive operations are giving me junk at times, and I think it is because of other interrupts disrupting the serial read at critical moments.  So I thought I could put my time sensitive code inside the USART_RX_vect ISR, which would not be interrupted by other interrupts while running.

When compiling, I get a similar error message:

C:\Users\Max\AppData\Local\Temp\build3450.tmp\wiring_serial.c.o: In function `__vector_18':


C:\Program Files (x86)\arduino-0013-win\arduino-0013\hardware\cores\arduino/wiring_serial.c:112: multiple definition of `__vector_18'


o:C:\Users\Max\AppData\Local\Temp\build3450.tmp/Temporary_6403_5722.cpp:42: first defined here
Couldn't determine program size: C:\Program Files (x86)\arduino-0013-win\arduino-0013\hardware/tools/avr/bin/avr-size: 'C:\Users\Max\AppData\Local\Temp\build3450.tmp\receiver_rev3.hex': No such file

I tried moving the ISR code over to blink.pde as suggested by mem, but got the same error message.  Has anyone had any luck setting up a custom serial receive ISR?

Thanks,
Max


24  Forum 2005-2010 (read only) / Troubleshooting / Re: Duemilanove Vista Driver Problems on: May 10, 2009, 01:34:02 am
I'm having problems like this in my Vista Home Premium x64 install.  The ports work fine in Ubuntu, so it's not a physical hardware issue.

Does anyone know where I could get a copy of the 2.04.06 drivers?  I'd like to try installing them instead of the latest 2.04.16 version, even though it says it's digitally signed.
25  Forum 2005-2010 (read only) / Troubleshooting / Re: arduino pro : usb + external power supply on: June 10, 2009, 08:22:18 pm
I'm putting together finishing touches on a project with the Arduino pro and an external 9V power supply.  I'd like to be able to connect the USB cable after I've soldered the power supply connection in and do software uploads.  Will that damage the board (5V + 9V > 12V) or is the power select module smart enough to power off one source, but not both?

It's the 16Mhz/5V version, btw.
26  Forum 2005-2010 (read only) / Interfacing / Re: Receive DMX-512 with an Arduino! on: June 06, 2009, 06:02:26 pm
Update:

Code is confirmed working on the Atmega 328.  Turns out the register names are the same, so it works without any adjustment.

I have designed a shield for it, which can also be used for DMX output:
http://blog.wingedvictorydesign.com/2009/04/29/arduino-dmx-512-io-shield/

The shield allows in-the-field addressing, without permanently disabling any of the pins.

Address is stored in non-volatile EEPROM, so it is retained when power is lost to the Arduino.

Drop me a line in this thread or the comments section on my blog if it worked for you  smiley, or didn't work for you  smiley-sad
27  Forum 2005-2010 (read only) / Interfacing / Receive DMX-512 with an Arduino! on: March 20, 2009, 02:50:17 pm
Hi All,
     I've written a sketch that allows you to receive DMX-512 with an Arduino.  If you're not familiar with it, DMX is the control protocol for theatrical/live event lighting.  It's been possible to send DMX with an arduino for quite awhile, but this will allow you to receive DMX values and execute your custom code accordingly.

The code is quite long, and there's some hardware setup and malarkey with the wiring_serial.c file involved, so I'm just going to give you the link:

http://blog.wingedvictorydesign.com/2009/03/20/receive-dmx-512-with-an-arduino/

It's my first project with the Arduino, and I worked really hard on it, so please let me know if it works for you and how you like it.

It has the following features:
  • Capable of receiving 8 contiguous addresses from 1 to 505.
  • Works with controllers that send less than the full 512 address DMX frame.
  • Break detection is done correctly by detecting a Low value of >88[ch956]S per ANSI E1.11-2008, rather than the frame error hack used by many devices.
  • Uses interrupt-based algorithms to eliminate processor-load related timing problems.  It may be of general interest to those in this forum who are trying to get interrup-based sketches off the ground
  • If the DMX data signal is lost, the Arduino will maintain the current state until new values are received.
  • The reception and user code run sequentially rather than at the same time, so they won't interfere with each others timing.
And the following limitations, as of now:
  • Atmega168 based processors only (you will have to rename the registers and interrupt vectors if you want to use it for another processor).
  • Currently only tested with a USB-DMX Pro controlled via Lightfactory and MagicQ software, since that's what I could get my hands on.  But it handles a variety of frame rates, break lengths, etc. just fine.  If you're in the Portland area, and you'd like to let me test it with other controllers, drop me a line.  Also, if you use it with other controllers, let me know how it turned out.
  • Because I needed access to exact timing, I had to use the Timer2 functionality, so pins 3 and 11 cannot be used for PWM.
  • Will not detect bad addressing, such as “dmxaddress=510;” or ”dmxaddress=50;” when only 55 slots are sent by the controller.
I'm still developing and improving the software, so check the link above for the latest version.

Max
28  Forum 2005-2010 (read only) / Interfacing / Re: interrupt-driven USART reception: I'm stumped. on: January 13, 2010, 10:00:49 am
Hi Dolby,
      If you want to send DMX, you can check out the playground on this site.  If you want to receive it, try my project:
http://blog.wingedvictorydesign.com/2009/03/20/receive-dmx-512-with-an-arduino/

both sites are the top results of a Google search, so you may want to start there.
29  Forum 2005-2010 (read only) / Interfacing / Re: interrupt-driven USART reception: I'm stumped. on: March 11, 2009, 08:21:45 pm
Quote
Does this means that when i need serial in version 0013 i need to manually to modify wiring_serial.c back again to the original?

It depends on whether you need Serial.flush(), which as near as I can tell is the only thing this change to wiring_serial.c would disable.  I started this thread in hopes that someone would chime in with a better work-around, but at the moment this is the only one I've got.

Quote
yes it works

Interestingly, this code doesn't work with my controller (an Enttec USB-DMX Pro with Lightfactory software), even when I compile it in the 0012 version of the software, with an unmodified wiring_serial.c

What controller(s) have you used it with?

I'm currently trying to write some software that will work with my (or any) controller, so I'm interested to know.  Thanks,

Max
30  Forum 2005-2010 (read only) / Interfacing / Re: interrupt-driven USART reception: I'm stumped. on: March 10, 2009, 10:49:51 pm
Well, it compiles fine on my computer, with my altered wiring_serial.c

Once I load it onto the Arduino, it doesn't do anything, though -- from poking at it it looks like it's not setting the values in DMX[] to anything, they just stay at their default value of 0.  I haven't gone through it in detail, though.

Have you managed to receive DMX correctly with this code, under any version of the Arduino library?  

If you want to compile it, here's what I did to wiring_serial.c  (towards the end, there's a part that's commented out):
Code:
/*
  wiring_serial.c - serial functions.
  Part of Arduino - http://www.arduino.cc/

  Copyright (c) 2005-2006 David A. Mellis

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General
  Public License along with this library; if not, write to the
  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  Boston, MA  02111-1307  USA

  $Id: wiring.c 248 2007-02-03 15:36:30Z mellis $

  Modified on March 1, 2009 by Norbert Pozar
*/

#include "wiring_private.h"

// Define constants and variables for buffering incoming serial data.  We're
// using a ring buffer (I think), in which rx_buffer_head is the index of the
// location to which to write the next incoming character and rx_buffer_tail
// is the index of the location from which to read.

// use only powers of 2 for the buffer size to produce optimal code
// 8, 16, 32, 64, 128 or 256

// input buffer
#define RX_BUFFER_SIZE 128
// output buffer
// set to 0 to disable the output buffer altogether (saves space)
#define TX_BUFFER_SIZE 32

unsigned char rx_buffer[RX_BUFFER_SIZE];

unsigned char rx_buffer_head = 0;
unsigned char rx_buffer_tail = 0;


void beginSerial(long baud)
{

#if defined(__AVR_ATmega8__)
      UBRRH = ((F_CPU / 16 + baud / 2) / baud - 1) >> 8;
      UBRRL = ((F_CPU / 16 + baud / 2) / baud - 1);
      
      // enable rx and tx
      sbi(UCSRB, RXEN);
      sbi(UCSRB, TXEN);
      
      // enable interrupt on complete reception of a byte
      sbi(UCSRB, RXCIE);
#else
      UBRR0H = ((F_CPU / 16 + baud / 2) / baud - 1) >> 8;
      UBRR0L = ((F_CPU / 16 + baud / 2) / baud - 1);
      
      // enable rx and tx
      sbi(UCSR0B, RXEN0);
      sbi(UCSR0B, TXEN0);
      
      // enable interrupt on complete reception of a byte
      sbi(UCSR0B, RXCIE0);
#endif

      // defaults to 8-bit, no parity, 1 stop bit
}

// advanced function, saves ~200 bytes because it doesn't use the long division
// prescaler for the baud rate can be found using the formula
// prescaler = fClock / (16 * baud rate) - 1
// see beginSerial
void beginSerialWithPrescaler(unsigned int prescaler)
{

#if defined(__AVR_ATmega8__)
      UBRRH = prescaler >> 8;
      UBRRL = prescaler;
      
      // enable rx and tx
      sbi(UCSRB, RXEN);
      sbi(UCSRB, TXEN);
      
      // enable interrupt on complete reception of a byte
      sbi(UCSRB, RXCIE);
#else
      UBRR0H = prescaler >> 8;
      UBRR0L = prescaler;
      
      // enable rx and tx
      sbi(UCSR0B, RXEN0);
      sbi(UCSR0B, TXEN0);
      
      // enable interrupt on complete reception of a byte
      sbi(UCSR0B, RXCIE0);
#endif

      // defaults to 8-bit, no parity, 1 stop bit
}

int serialAvailable()
{
      unsigned char i = RX_BUFFER_SIZE + rx_buffer_head - rx_buffer_tail;
      i %= RX_BUFFER_SIZE;

      return i;
}

int serialRead()
{
      // if the head isn't ahead of the tail, we don't have any characters
      if (rx_buffer_head == rx_buffer_tail) {
            return -1;
      } else {
            unsigned char c = rx_buffer[rx_buffer_tail];
            rx_buffer_tail = rx_buffer_tail + 1;
            rx_buffer_tail %= RX_BUFFER_SIZE;
            return c;
      }
}

void serialFlush()
{
      // don't reverse this or there may be problems if the RX interrupt
      // occurs after reading the value of rx_buffer_head but before writing
      // the value to rx_buffer_tail; the previous value of rx_buffer_head
      // may be written to rx_buffer_tail, making it appear as if the buffer
      // were full, not empty.
      rx_buffer_head = rx_buffer_tail;
}

/* #if defined(__AVR_ATmega8__)  *********THIS PART COMMENTED OUT*********
SIGNAL(SIG_UART_RECV)
#else
SIGNAL(USART_RX_vect)
#endif
{
#if defined(__AVR_ATmega8__)
      unsigned char c = UDR;
#else
      unsigned char c = UDR0;
#endif

      unsigned char i = rx_buffer_head + 1;
      i %= RX_BUFFER_SIZE;

      // if we should be storing the received character into the location
      // just before the tail (meaning that the head would advance to the
      // current location of the tail), we're about to overflow the buffer
      // and so we don't write the character or advance the head.
      if (i != rx_buffer_tail) {
            rx_buffer[rx_buffer_head] = c;
            rx_buffer_head = i;
      }
} */

// buffered output
#if (TX_BUFFER_SIZE > 0 )

// TX is buffered
// ************************
// tested only for ATmega168
// ************************

unsigned char tx_buffer[TX_BUFFER_SIZE];

unsigned char tx_buffer_head = 0;
volatile unsigned char tx_buffer_tail = 0;


// interrupt called on Data Register Empty
#if defined(__AVR_ATmega8__)
SIGNAL(SIG_UART_DATA)
#else
SIGNAL(USART_UDRE_vect)
#endif
{
  // temporary tx_buffer_tail
  // (to optimize for volatile, there are no interrupts inside an interrupt routine)
  unsigned char tail = tx_buffer_tail;

  // get a byte from the buffer      
  unsigned char c = tx_buffer[tail];
  // send the byte
#if defined(__AVR_ATmega8__)
      UDR = c;
#else
      UDR0 = c;
#endif

  // update tail position
  tail ++;
  tail %= TX_BUFFER_SIZE;

  // if the buffer is empty,  disable the interrupt
  if (tail == tx_buffer_head) {
#if defined(__AVR_ATmega8__)
    UCSRB &=  ~(1 << UDRIE);
#else
    UCSR0B &=  ~(1 << UDRIE0);
#endif

  }

  tx_buffer_tail = tail;

}


void serialWrite(unsigned char c) {

#if defined(__AVR_ATmega8__)
       if ((!(UCSRA & (1 << UDRE)))
#else
      if ((!(UCSR0A & (1 << UDRE0)))
#endif
  || (tx_buffer_head != tx_buffer_tail)) {
    // maybe checking if buffer is empty is not necessary,
    // not sure if there can be a state when the data register empty flag is set
    // and read here without the interrupt being executed
    // well, it shouldn't happen, right?

    // data register is not empty, use the buffer
    unsigned char newhead = tx_buffer_head + 1;
    newhead %= TX_BUFFER_SIZE;

    // wait until there's a space in the buffer
    while (newhead == tx_buffer_tail) ;

    tx_buffer[tx_buffer_head] = c;
    tx_buffer_head = newhead;

    // enable the Data Register Empty Interrupt
#if defined(__AVR_ATmega8__)
    UCSRB |=  (1 << UDRIE);
#else
    UCSR0B |=  (1 << UDRIE0);
#endif

  }
  else {
    // no need to wait
#if defined(__AVR_ATmega8__)
      UDR = c;
#else
      UDR0 = c;
#endif
  }
}

#else

// unbuffered write
void serialWrite(unsigned char c)
{
#if defined(__AVR_ATmega8__)
      while (!(UCSRA & (1 << UDRE)))
            ;

      UDR = c;
#else
      while (!(UCSR0A & (1 << UDRE0)))
            ;

      UDR0 = c;
#endif
}

#endif


Pages: 1 [2] 3 4 5