For simple projects it’s no problem to do only one thing at a time and waiting until it is done before doing the next thing, but for more advanced projects you sometimes need to do several things at the same time, so called multitasking. You may for example want to read in data from a radio reciever, drive a stepper motor, flash a LED and show status text on a display, all at the same time. There are several ways to accomplish this, but the method we show here is very popular because it is relatively simple to understand and program and gives good results. The method is in fact inspired by how professional industrial systems work and is therefore very well tested. The basic idea is to make sure that all tasks that the Arduino board should perform are kept small and as fast as possible to execute and that you never actively wait for anything. It is therefore vital to never use the delay()-function in your code as this will completely block any other task from being run. Instead you let the processor continuously compare timer values and perform the different tasks whenever the appropriate amount of time has passed.

How is it done?

Simply put, you have a series of if-statements that compares time values and let the processor run through them over and over again as fast as possible. When a timer value exceeds a preset threshold the associated function is called and executed and then the control is returned to the main program with the If-statements. The threshold is then set to the next point in time when the function should be run and the main program continues its loop.

The circuit

In our example we want three LEDs to blink at different speeds. This may at first seem simple, but if we want them to blink independently of each other we need to use some sort of multitasking. Start by building the circuit in the diagram below. Then enter the code from below into Arduinos development environment and upload it to the Arduino board.

Arduino-project 007 – Multitasking

Description of the code

In the code we set up three separate functions, blink_1()blink_2() and blink_3() that we will run in parallel. You can of course use this code as basis for other projects and switch the functions for your own or add more. We also set up a number of variables to keep track of how often each function should run and to keep track of the state of each LED (on or off). The main program resides in loop() as usual and this code will run as fast as possible, thousands of times per second. The loop begins with getting the current time with the millis()function. This function returns the number of milliseconds that have elapsed since the Arduino board was powered on, or since the last reset. After this follows three separate if-statements where we compare the current time with the different timer values for the blink functions. The first time all these values will be 0 meaning that all three if-statements will be true and the three blink-functions will be run. When each blink function has finished running we set the timer values to be current time plus the number milliseconds we want to wait until we run it the next time.

The next time the loop runs current_time will have increased slightly, exactly how much depends on how long it took to run the three blink-functions, but not so much that current_time is greater than any of the timer variables. Therefore all if-statements will be false and won’t run their associated function. This will now repeat over and over again until current_time has increased so much that it is greater than one of the timer values and the associated function is run and its timer value is again increased. Most of the time the processor will do nothing but running around the main loop and comparing timer values, but occasionally one of the blink functions will run. It’s important to remember that even though this method makes it seem as if the processor do multiple things at the same time it will never do more than once thing at a time. It’s just so very fast that it seems to use it’s doing them concurrently. This is what makes it so important that all functions are fast and to never use delay() as this will bind up the processor and prevent the other functions from running.

The code

// Settings for which pins the LEDs are connected to.
const int led_pin_1 = 2;
const int led_pin_2 = 3;
const int led_pin_3 = 4;

// Blinking speed for the LEDs in milliseconds.
const int blink_time_1 = 500;
const int blink_time_2 = 400;
const int blink_time_3 = 150;

// State variables for the LEDs.
// These keeps track of whether the LEDs are on or off.
int led_1 = LOW;
int led_2 = LOW;
int led_3 = LOW;

// Timer-variables for the LEDs.
// These keep track of when it's time to change the state of each LED.
double led_timer_1;
double led_timer_2;
double led_timer_3;

// Function for turning LED 1 on or off.
void blink_1() {
  if (led_1 == LOW) {
    led_1 = HIGH;
  }
  else {
    led_1 = LOW;
  }
  digitalWrite(led_pin_1, led_1);
}

// Function for turning LED 2 on or off. 
void blink_2() {
  if (led_2 == LOW) {
    led_2 = HIGH;
  }
  else {
    led_2 = LOW;
  }
  digitalWrite(led_pin_2, led_2);
}

// Function for turning LED 3 on or off. 
void blink_3() {
  if (led_3 == LOW) {
    led_3 = HIGH;
  }
  else {
    led_3 = LOW;
  }
  digitalWrite(led_pin_3, led_3);
}

// Setup makes sure the pins that the LEDs are connected to are set as outputs.
void setup() {
  pinMode(led_pin_1, OUTPUT);
  pinMode(led_pin_2, OUTPUT);
  pinMode(led_pin_3, OUTPUT);
}

// Loop will be run through over and over again as fast as possible.
// If a function takes too long to finish it will prevent the other functions from
// running when they should.
void loop() {
  // Get the current time. This value is the number of milliseconds since
  // the Arduino board was powered on or since the last reset.
  double current_time = millis();
  // Check if it's time to change state of LED 1.
  if (current_time > led_timer_1) {
    blink_1();
    // Update the timer value for the next event
    led_timer_1 = current_time + blink_time_1;
  }
  // Check if it's time to change state of LED 2. 
  if (current_time > led_timer_2) {
    blink_2();
    // Update the timer value for the next event 
    led_timer_2 = current_time + blink_time_2;
  }
  // Check if it's time to change state of LED 3. 
  if (current_time > led_timer_3) {
    blink_3();
    // Update the timer value for the next event 
    led_timer_3 = current_time + blink_time_3;
  }
}

För enklare projekt är det inga problem att göra en sak åt gången och vänta på att den är färdig innan man gör nästa, men för lite mer avancerade projekt behöver man ibland göra flera saker samtidigt, s.k. multitasking. T.ex. vill man kanske läsa in data från en radiomottagare, driva runt en stegmotor, blinka med en lysdiod och visa statustext på en skärm samtidigt. Det finns flera sätt att åstadkomma detta, men den metod vi ska visa här är väldigt populär eftersom den är förhållandevis enkel att förstå och programmera och ger bra resultat. Metoden är faktiskt inspirerad av hur professionella industriella kontrollsystem arbetar och därmed välbeprövad. Grundidén bygger på att man ser till att alla uppgifter som Arduino-kortet ska utföra hålls väldigt små så att de går väldigt snabbt att utföra och att man aldrig någonsin aktivt väntar på något. Det innebär att man absolut inte får använda funktionen delay() i sin kod eftersom detta kommer helt att blockera alla andra uppgifter som annars skulle köras samtidigt. Istället så låter man processorn hela tiden jämföra timer-värden och utföra de olika uppgifterna när lagom mycket tid har förflutit.

Hur går det till?

Enkelt förklarat så har man en serie med if-satser som jämför timer-värden som man låter processorn kör igenom om och om igen så snabbt som möjligt. När ett timern överstiger ett inställt tröskelvärde så anropar man den funktion som är kopplad till det tröskelvärdet. Funktionen körs och hoppar sedan tillbaka till huvudprogrammet med if-satser. Tröskelvärdet ställs om till nästa tidpunkt man vill köra funktionen och sedan fortsätter huvudprogrammet att köra runt.

Kopplingen

I vårt exempel ska vi få tre lysdioder att blinka i olika takt. Detta kan kanske verka enkelt först, men vill vi att de ska blinka helt oberoende av varandra behöver vi använda multitasking av någon form. Börja med att bygg upp kopplingen i bilden nedan. Skriv sedan in koden längst ner på sidan i Arduinos utvecklingsmiljö och ladda upp den till Arduino-kortet.

Arduino-projekt 007 – Multitasking

Beskrivning av koden

I koden sätter vi upp tre separata funktioner, blinka_1()blinka_2() och blinka_3() som vi kommer att köra parallellt. Du kan självklart använda den här koden som grund till andra projekt och byta ut funktionerna mot dina egna eller lägga till fler. Vi sätter även upp ett antal variabler för att hålla reda på hur ofta varje funktion ska köra och för att hålla reda på i vilket tillstånd varje lysdiod befinner sig i (tänd eller släckt). Huvudprogrammet ligger i vanlig ordning i loop() och denna kod kommer att köras många tusen gånger i sekunden, så snabbt som det bara är möjligt. Loopen börjar med att vi läser ut aktuell tid med funktionen millis(). Denna funktion lämnar ifrån sig antalet millisekunder sedan Arduino-kortet fick ström, eller sedan senaste resetten. Sedan följer ett tre separata if-satser där vi jämför aktuell tid med de olika timer-värdena för blink-funktionerna. Allra första gången som koden körs kommer dessa att vara 0 och därmed kommer alla tre if-satserna att vara sanna och alla tre blink-funktionerna kommer att köras. När varje blink-funktion körts ställer vi om timer-värdena till att vara aktuell tid plus så många millisekunder som vi vill vänta innan vi kör funktionen igen. Nästa gång loopen körs igenom så kommer aktuell_tid att ha ökat lite, exakt hur mycket beror på hur lång tid det tog att köra igenom de tre funktionerna, men inte så mycket att aktuell_tid är större än någon av timer-variablerna. Därmed kommer alla if-satser att vara falska och inte köras. Detta upprepas nu om och om igen tills aktuell_tid har ökat så mycket att den är större än något av timer-värdena och den associerade funktionen körs och dess timer-värde ställs om på nytt. Detta kommer sedan upprepas i oändligheten. Större delen av tiden så kommer processorn inte att göra något annat än snurra runt i huvudloopen och jämföra värden, men lite då och då kommer den att köra en av blink-funktionerna och sedan hoppa tillbaka och fortsätta snurra runt. Det är viktigt att komma ihåg att även om den här metoden får det att verka som att processorn gör flera gånger samtidigt så kan den aldrig göra mer en en enda sak i taget. Det är bara att den är så väldigt snabb att det verkar som att saker sker parallellt. Det är detta som gör att det är så viktigt att alla funktionerna är snabba och att man aldrig använder delay() för då kommer processorn att bli uppbunden och kommer inte att kunna köra övriga funktioner.

Koden

// Inställning för vilka pinnar som lysdioderna är inkopplade till
const int ledpin_1 = 2;
const int ledpin_2 = 3;
const int ledpin_3 = 4;

// Blinkhastigheter för lysdioderna i millisekunder. 
const int blinktid_1 = 500;
const int blinktid_2 = 400;
const int blinktid_3 = 150;

// Tillståndsvariabler för lysdioderna.
// Dessa håller reda på om de individuella lysdioderna är tända eller släckta.
int led_1 = LOW;
int led_2 = LOW;
int led_3 = LOW;

// Timer-variabler för lysdioderna.
// Dessa håller reda på när det är dags att ändra status på var lysdiod.
double ledtimer_1;
double ledtimer_2;
double ledtimer_3;

// Funktion för att tända eller släcka lysdiod 1
void blinka_1() {
  if (led_1 == LOW) {
    led_1 = HIGH;
  }
  else {
    led_1 = LOW;
  }
  digitalWrite(ledpin_1, led_1);
}

// Funktion för att tända eller släcka lysdiod 2
void blinka_2() {
  if (led_2 == LOW) {
    led_2 = HIGH;
  }
  else {
    led_2 = LOW;
  }
  digitalWrite(ledpin_2, led_2);
}

// Funktion för att tända eller släcka lysdiod 3
void blinka_3() {
  if (led_3 == LOW) {
    led_3 = HIGH;
  }
  else {
    led_3 = LOW;
  }
  digitalWrite(ledpin_3, led_3);
}

// Setup ser till att pinnarna som lysdioderna är kopplade sätts till utgångar.
void setup() {
  pinMode(ledpin_1, OUTPUT);
  pinMode(ledpin_2, OUTPUT);
  pinMode(ledpin_3, OUTPUT);
}

// Loop kommer att köras igenom om och om igen så snabbt det bara är möjligt.
// Om en funktion tar lång tid på sig kommer det att hindra övriga funktioner
// från att köras när de ska.
void loop() {
  // Hämta in aktuell tid. Detta värde är antalet millisekunder sedan
  // strömmen kopplades in till Arduino-kortet, eller sedan senaste resetten.
  double aktuell_tid = millis();
  // Kolla om det är dags att ändra status på lysdiod 1
  if (aktuell_tid > ledtimer_1) {
    blinka_1();
    // Ställ om timer för LED 1 till nästa händelse
    ledtimer_1 = aktuell_tid + blinktid_1;
  }
  // Kolla om det är dags att ändra status på lysdiod 2 
  if (aktuell_tid > ledtimer_2) {
    blinka_2();
    // Ställ om timer för LED 2 till nästa händelse 
    ledtimer_2 = aktuell_tid + blinktid_2;
  }
  // Kolla om det är dags att ändra status på lysdiod 3
  if (aktuell_tid > ledtimer_3) {
    blinka_3();
    // Ställ om timer för LED 3 till nästa händelse 
    ledtimer_3 = aktuell_tid + blinktid_3;
  }
}

Pin It on Pinterest

Share This