Pages: [1]   Go Down
Author Topic: Serial - Interrupt  (Read 17322 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 10
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

hi,
how can i start an interrupt when my arduino Board gets Data via USB- Serial.
I'm using the Arduino Diecimilia Board with the Atmega168 mc.
Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6255
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I doubt you will want to do that. The serial data is captured using an interrupt to put each character into the serial input buffer. If you add any additional processing to that interrupt or trigger another interrupt then you may lose incoming characters because the arduino disables interrupts when it is in an interrupt handler.

Perhaps if you say a little about what you want your application to do, you can get some suggestions on how to handle serial data while the sketch is also processing some other tasks.
« Last Edit: September 22, 2008, 11:21:40 am by mem » Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 10
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

ok thanks for the fast replie,
i'm programming an frequency generator which uses the timers to generate an freq. and i want to change the freq. while the mc. is running.
So i want to send the new freq. via USB and then the MC. should init the timers with the new freq.
Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6255
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

If your loop is not doing anything once the timers are configured you could sit in a loop checking serial.available(). Read and process the serial data when it is available.

If the loop is busy doing something when the sketch is running then if you say what that is,  we can suggest how you can interleave that with checks for serial data.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 10
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

hmm the loop is the algorithm to init the timers, that means i determine the prescaler ... but i think i can wait for serial input with serial.available and than lunch a new init function its not perfect but it is ok
thanks guys for the help  
Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6255
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Curious to know what it is that your not happy about. Is the prescaler value being constantly changed even if there is no serial input?  Perhaps if you posted the relevant  code and indicated the area where you feel you need a faster response then there may be a way to improve the performance.
Logged

Stockholm
Offline Offline
Newbie
*
Karma: 0
Posts: 31
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I´m very interested on this topic. I´m currently working on a project involving XBee and i want the Arduino to "sleep" until it receives serial data from XBee module.

If I follow the pattern described by mem, that is, waiting in a loop until  Serial.available() > 0 then the Arduino power consumption will be max, right? or am i wrong here? I plan to run my project on batteries so it would be very nice to reduce Arduino power drain to a minimum.

Right now, i'm doing something like this

Code:
void loop() {
   while (Serial.available() < 1) {delay(1)};
   processResponse();
}

But I'm not really sure if this saves power at all (I mean, using delay(1) to sleep). Another question is how much can I increase the delay without risking losing characters? At 9600 baud/s,  up to 960 bytes per second means 1 character per millisecond. But how big is the buffer in the atmel uart?

Looking forward to hear from you. Thx in advance.


Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6255
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Calling delay doesn't put the arduino to sleep. It uses the same power as repeatedly calling Serial.available() , so you might just as well do:
while(Serial.available() < 1)
   ;
processResponse();

The serial receive buffer is 128 bytes (its defined in wiring_seria.c in the hardware/cores/arduino directory
« Last Edit: September 23, 2008, 10:01:43 am by mem » Logged

Stockholm
Offline Offline
Newbie
*
Karma: 0
Posts: 31
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Ok, so delay() doesn't save any power. bummer!. Good to know, thanks to that I searched more and I found  about real sleep on http://www.arduino.cc/playground/Learning/ArduinoSleepCode. Thx.
Logged

Austin, TX USA
Offline Offline
God Member
*****
Karma: 4
Posts: 997
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quite right.  Also, ecerulm, 9600 baud is ~bps, so roughly 1200 bytes per second.

M
« Last Edit: September 23, 2008, 10:46:15 am by mikalhart » Logged

Stockholm
Offline Offline
Newbie
*
Karma: 0
Posts: 31
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

About the baud rate. 9600 8-N-1 needs 10 bits to transmit 1 byte, right? so 9600 bauds translates to 960 bytes. or am i missing something?  :-?

http://en.wikipedia.org/wiki/Measuring_network_throughput#Overheads_and_data_formats

Logged

Austin, TX USA
Offline Offline
God Member
*****
Karma: 4
Posts: 997
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

D'Oh!  Quite right.  Sorry!  smiley-grin
Logged

Stockholm
Offline Offline
Newbie
*
Karma: 0
Posts: 31
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Ok, I investigated a little bit more and it seems that Arduino-12 adds avr/power.h that allows you to have a fine grained power reduction managament. By using POWER_MODE_IDLE and shutting down unused modules I managed to wake the Arduino as soon as it gets serial data (the USART is still working on POWER_MODE_IDLE). The source code is in http://rubenlaguna.com/wp/2008/10/15/arduino-sleep-mode-waking-up-when-receiving-data-on-the-usart/


Logged

Italy
Offline Offline
Newbie
*
Karma: 0
Posts: 5
Arduino changed my life
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Sorry to reply to this topic two years later, but this is the first result I get by Google to the query "Arduino serial interrupt".
I see that someone solved this problem and even provided a library to handle Serial I/O via interrupts. In one case an extension to HardwareSerial provided with Arduino has been written.

You can find that here:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1191505972
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1265509976/3
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1242292909/2

I don't know if this helps, but I just succeeded in doing that in a very simple way.
Maybe it uses the same priciples used by the above solutions, but I don't know, since the provided code is quite complex to me.

My humble solution is to reenable interrupts while in the interrupt service routine to allow Serial to work.
Then wait for data in the Serial data to be available.

Drawback: this steals a bit of CPU when receiving data: the ISR activates on the Start bit of the serial data. Then it has to wait for the 10 bits on 8N1 protocol to be received. At 9600bps this means wasting about 1/960 seconds, i.e. about 16.000 cpu cycles.
The worst thing is that if multiple bytes are sent without any rest, the ISR will be stealing the CPU for about 1/960 seconds and then leave it free for 1/9600, the mere time between the end of a byte and the biginning of the next one, and then continue cycling like this.

So my approach is not suitable for every application.
A solution to this problem might be to disasbile interrupts and reenable them in the main loop, using noInterrupts() and interrupts() functions, reducing the number of ISR calls during main loop.

This is my humble code:
Code:
// To use this example, you have to connect Rx pin (digital pin 0) to interrupt 0 pin (digital pin 2).
void setup()
{
    // Using interrupt 0 on digital pin 2.
    pinMode(2, INPUT);
    digitalWrite(2, LOW);
    
    Serial.begin(9600);
    attachInterrupt(0, serialInterrupt, CHANGE);

    // Used to signal that main loop is alive.
    pinMode(4, OUTPUT);
    digitalWrite(4, LOW);

    // Used to signal that Serial input was read.
    pinMode(5, OUTPUT);
    digitalWrite(5, LOW);
}

void loop()
{
  // Do something using even delays. There is an interrupt for that (Serial I/O)!

  // Blink led to signal loop is alive.
  digitalWrite(4, HIGH);
  delay(500);
  
  digitalWrite(4, LOW);
  delay(500);
}

// Volatile, since it is modified in an ISR.
volatile boolean inService = false;

void serialInterrupt()
{
  // Trick: since Serial I/O in interrupt driven, we must reenable interrupts while in this Interrupt Service Routine.
  // But doing so will cause the routine to be called nestedly, causing problems.
  // So we mark we are already in service.

  // Already in service? Do nothing.
  if (inService) return;

  // You was not in service. Now you are.
  inService = true;
  
  // Reenable interrupts, to allow Serial to work. We do this only if inService is false.
  interrupts();
  
  // Allow serial to read at least one byte.
  while(!Serial.available());

  // Blink led to signal Serial data arrived.
  digitalWrite(5, !digitalRead(5));
  byte data = Serial.read();

  // Echo data back to developer ;-)
  Serial.print(data);

  // Job done.
  inService = false;
}

I  tried this other approach, detaching and reattaching ISR, but it stops the main loop. Do know why.

Code:
void serialInterrupt()
{
  detachInterrupt(0);
  interrupts();
  
  while(!Serial.available());

  digitalWrite(5, !digitalRead(5));
  byte data = Serial.read();
  Serial.print(data);

  attachInterrupt(0, serialInterrupt, CHANGE);
}
Logged

Pages: [1]   Go Up
Jump to: