Go Down

Topic: Can Arduino Multitask? (Read 4353 times) previous topic - next topic

Nick Gammon


My project I'd like to do has two speakers, that will each play a line of music. (One bass, and another treble)  Is it possible for the arduino to have both playing at the same time?


I suppose it depends how this music is going to be generated. But certainly the Arduino can do multiple things at the same time, in this sense ...

Say you want to have two lights on "at the same time". You can do this:


  • Turn on light 1

  • Turn on light 2 (a millisecond later)



To people watching both lights seemed to come on at once, and they are now both on together. It took an action to turn them on, it doesn't take an action to keep them on. In the same way that, in your house, you have lots of lights on at once, once you initially go around turning them on, one by one.

Now for the music, the Arduino with only 2 Kb of RAM doesn't have a lot to hold sampled music, so you aren't going to get too far with that, unless you have external peripherals. It has 3 timers capable of generating tones, so you could get Timer 1 to start a tone on one speaker, and Timer 2 to do a tone on the other speaker. Then when it was time to change notes you change the appropriate tones.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Paranemertes

WOAH. did not expect to see 15 replies today. thank you to everyone for the quick replies! A little more detail on the use of this, I want to put a RGB led in an arcade style button, I have some code to make it fade between colors. I want to run this (looping) while also reading the button and doing other stuff from the resulting state of the button. How do any of you suggest going about this?

Button(im going to replace the white led with a RGB one):http://www.paradisearcadeshop.com/en/234-led-convex-arcade-pushbuttonwhite-led-convex-arcade-pushbutton.html

Fading Code:
Code: [Select]
#define GREEN 3
#define BLUE 5
#define RED 6
#define delayTime 20

void setup() {

  pinMode(GREEN, OUTPUT);
  pinMode(BLUE, OUTPUT);
  pinMode(RED, OUTPUT);
  digitalWrite(GREEN, HIGH);
  digitalWrite(BLUE, HIGH);
  digitalWrite(RED, HIGH);
}

int redVal;
int blueVal;
int greenVal;

void loop() {

  int redVal = 255;
  int blueVal = 0;
  int greenVal = 0;
  for( int i = 0 ; i < 255 ; i += 1 ){
    greenVal += 1;
    redVal -= 1;
    analogWrite( GREEN, 255 - greenVal );
    analogWrite( RED, 255 - redVal );

    delay( delayTime );
  }

  redVal = 0;
  blueVal = 0;
  greenVal = 255;
  for( int i = 0 ; i < 255 ; i += 1 ){
    blueVal += 1;
    greenVal -= 1;
    analogWrite( BLUE, 255 - blueVal );
    analogWrite( GREEN, 255 - greenVal );

    delay( delayTime );
  }

  redVal = 0;
  blueVal = 255;
  greenVal = 0;
  for( int i = 0 ; i < 255 ; i += 1 ){
    redVal += 1;
    blueVal -= 1;
    analogWrite( RED, 255 - redVal );
    analogWrite( BLUE, 255 - blueVal );

    delay( delayTime );
  }
}
"It's Dr. Evil. I didn't spend six years in Evil Medical School to be called 'mister,' thank you very much."

Nick Gammon

Get rid of the delays.

http://gammon.com.au/blink
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Morris Dovey

#18
Feb 15, 2012, 04:48 am Last Edit: Feb 15, 2012, 06:50 pm by Morris Dovey Reason: 1
(I'm curious - where is UKF?)

I couldn't resist the urge to play with your code and ended up withis this (untested):

Code: [Select]
#define GREEN 3
#define BLUE 5
#define RED 6

void (*execute)();
unsigned long runTime,delayTime = 20;
int index,redVal,blueVal,greenVal;

void cycle1_head(void)
{  redVal = 255;
   index = blueVal = greenVal = 0;
   execute = cycle1_body;
}
void cycle1_body(void)
{  if (index++ < 255)
   {  analogWrite(GREEN,255 - ++greenVal);
      analogWrite(RED,255 - --redVal);
      runTime += delayTime;
   }
   else execute = cycle2_head;
}
void cycle2_head(void)
{  greenVal = 255;
   index = redVal = blueVal = 0;
   execute = cycle2_body;
}
void cycle2_body(void)
{  if (index++ < 255)
   {  analogWrite(BLUE,255 - ++blueVal);
      analogWrite(GREEN,255 - --greenVal);
      runTime += delayTime;
   }
   else execute = cycle3_head;
}
void cycle3_head(void)
{  blueVal = 255;
   index = redVal = greenVal = 0;
   execute = cycle3_body;
}
void cycle3_body(void)
{  if (index++ < 255)
   {  analogWrite(RED,255 - ++redVal);
      analogWrite(BLUE,255 - --blueVal);
      runTime += delayTime;
   }
   else execute = cycle1_head;
}
void setup(void)
{  pinMode(GREEN,OUTPUT);
   pinMode(BLUE,OUTPUT);
   pinMode(RED,OUTPUT);
   digitalWrite(GREEN,HIGH);
   digitalWrite(BLUE,HIGH);
   digitalWrite(RED,HIGH);
   runTime = millis();
   execute = cycle1_head;
}
void loop(void)
{  while (millis() >= runTime) execute();

   /* Whatever else you want to do in loop() */
}


If you do try it with a multi-color LED, I'd like to know how well it works.   :smiley-mr-green:

Edited for compactness and clarity
There's always a better way!

Paranemertes

Awesome. It did exactly the same thing as when it was running on the old code, nice job. Frome. But as to the original question, there isnt a way to do a simple loop1 and loop2? you HAVE to mess around so there is no delay?
"It's Dr. Evil. I didn't spend six years in Evil Medical School to be called 'mister,' thank you very much."

Nick Gammon

You tell it to delay and it delays.

This chip is not sophisticated enough to do pre-emptive multi-tasking. And even it it was, you would have to learn how to do that. So you substitute one lot of learning for another one.

Back to my earlier example. Say you want to turn on two lights for an hour (at the same time). No reasonable person would do this:


  • Turn on the first light.

  • Wait for an hour to elapse.

  • Turn off the first light.

  • Turn on the second light.

  • Wait for an hour to elapse.

  • Turn off the second light.



You wouldn't do that, right? It's hardly "messing around" to turn on light 1, then light 2, and then wait an hour, is it?
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Morris Dovey

@Paranemertes...
There are ways, but the cost/benefit ratio would be probably be out of proportion for the Arduino market. For its intended market, the Arduino is inexpensive and remarkably capable. It's human nature to always want more than what's provided - and so folks busily tinker away to implement the missing capabilities they (as individuals working on different projects) want - to work around the limitations.
From a programmer's perspective, the hard limits are memory space ("M") and processor cycles ("V"), and the challenge is to use both efficiently. The delay() function, by design, throws away some (usually large) number of cycles and so reduces the machine's capacity to do work.
The short answer is you probably will have to mess around in order to have several different (asynchronous) things going on concurrently.
There's always a better way!

Paranemertes

cool beans, thanks a bunch everyone. And thanks for the awesome code Morris! If you dont mind i think i will use it...
"It's Dr. Evil. I didn't spend six years in Evil Medical School to be called 'mister,' thank you very much."

oric_dan

Interesting thread, and I like all of the solutions tendered. However,
might I suggest that the loop1,2 sketch be written in a more structured
and readable format.

I've included a scheme that I've used in the past, which is a variation
on the BlinkWithoutDelay sketch. It uses separate tasks that each keep
track of their own trigger times and internal states, and are written as
simple state-machines using a switch{} construct (could be if-then-else
too). You simply pass the global time variable to each task.

With this scheme, it's easy to add extra tasks, using the previous
ones as template, and also to add additional states to any one task.
Each state increments its internal state variable to the next state.

Also, it works best to keep the amount of code in each task to a
minimum, in order to keep overall timing accurate, since this scheme,
like the others, does not use preemptive multitasking. Any round-robin
tasking scheme has to be set up for short task execution and frequent
breaks.


Code: [Select]

/*
file: timed_tasks
(revised: 16.02.2012).

Turns on and off LED using millisec() timing function, and
runs multiple timed tasks.

Based upon "File > Examples > 2.Digital > BlinkWithoutDelay".
**************************************************************/

const int ledPin =  13;     

/**** Variables ****/
long interval = 1000UL;  // blink interval, in msec.


/************************************************/
void setup()
{
  pinMode(ledPin, OUTPUT);     
}

/************************************************/
void loop()
{
  unsigned long currtime = millis();
 
  task1_led(interval, currtime);
  task2_x(currtime);
  task3_y(currtime);
}

/*
  interval = blink interval, in msec.
  currtime = current time from millisecond counter.
***************************************************************/
void task1_led(unsigned long interval, unsigned long currtime)
{
  static long prevtime = 0;
  static int state = 0;
 
  if( (currtime - prevtime) > interval ) {
    prevtime = currtime;  // save the last time Led blinked. 

    // if the LED is off, turn it on and vice-versa (written
    //   so you don't have to keep track of the Led state).
    switch( state ) {
     case 0: digitalWrite(ledPin, HIGH);  // set the LED.
             state=1;
             break;
     case 1: digitalWrite(ledPin, LOW);  // set the LED.
             //state=2; //optional instead of state=0;
             state=0;
             break;
     // add more states (optional).
//   case 2: //do something.
//           state=3;
//           break;
//   case 3: //do something.
//           state=0;
//           break;
     default: break;
    }
  }
}

/************************************************/
void task2_x(unsigned long currtime)
{
  // similar to task1.
}

/************************************************/
void task3_y(unsigned long currtime)
{
  // similar to task1.
}


jfhaugh

Yeah, but what about some nice pre-emptive round-robin scheduling?  ]:D  Who's responsible for stacking and restoring ALU flags during an interrupt?

oric_dan

Can you point to some nice "easy to use" multitasker that is fairly standard
in the Arduino community, and won't take a month to wring out learning
how to use?

Coding Badly

Quote
Who's responsible for stacking and restoring ALU flags during an interrupt?


Interrupt service routine.  The hardware only handles the return address and the Global Interrupt Enable flag.

jfhaugh


Quote
Who's responsible for stacking and restoring ALU flags during an interrupt?


Interrupt service routine.  The hardware only handles the return address and the Global Interrupt Enable flag.



Cool!  So, in my ISR I save the flags and PC in a task state structure, the load the previous saved flags from a different task state structure, stack the PC, and RETI?

Time to port Linux to an ATmega 2560!  :smiley-mr-green:

mromani

Keywords (recently mentioned in this forum)

ChiboOS

FreeRTOS

DuinoOS

I'm sure there are others.

Also, the timing section of the Playground is a nice starting point.

Nick Gammon


Cool!  So, in my ISR I save the flags and PC in a task state structure, the load the previous saved flags from a different task state structure, stack the PC, and RETI?


Just remind me of how you are going to handle stack-based variables.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Go Up