09. Multitasking

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, t.ex. som vi gjorde för vårt projekt där vi blinkade med en lysdiod. 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 visar här är väldigt populär eftersom den är förhållandevis enkel att förstå och programmera och ger bra resultat. 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 aktuell timer ö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.

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 hundtratals eller tusentals 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.

För att avgöra om det är dags att blinka med en lysdiod så har vi tre separta if-satser, en per lysdiod. I if-satserna räknar vi ut skillnaden mellan aktuell_tid och timer-värdet för lysdioden (ledtimer_1, ledtimer_2 och ledtimer_3). Skillnaden kommer motsvara hur lång tid som det förflutit sedan vi sist blinkade med just den lysdioden. Om denna skillnad är större än blink-tiden för lysdioden så betyder det att det är dags att blinka med lysdioden. När detta sker så sätter vi först timer-värdet till aktuell_tid så att vi kan avgöra när det är dags för nästa blinkning. Sedan kör vi funktionen för att blinka med lysdioden.

Eftersom loopen körs väldigt snabbt så kommer aktuell_tid bara öka med några millisekunder varje gång och de flesta gånger så kommer ingen av if-satserna aktiveras och koden gör ingenting. Detta kan verka slösaktigt, men eftersom datorn alltid kör oavsett vad, så spelar det ingen roll att den snurrar runt utan utföra meningsfullt jobba större delen av tiden. Detsamma är sant med delay()-funktionen som helt enkelt låter datorn köra runt i en loop som inte utför något jobb. 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. Men eftersom den är så väldigt snabb så verkar det 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.
// Tidsvärdena i Arduino är stora positiva heltal så vi måste använda unsigned long istället för int.
unsigned long ledtimer_1;
unsigned long ledtimer_2;
unsigned long 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.
  unsigned long aktuell_tid = millis();
  // Kolla om det är dags att ändra status på lysdiod 1
  if (aktuell_tid > ledtimer_1 + blinktid_1) {
    blinka_1();
    // Ställ om timer för LED 1 till aktuell tid
    ledtimer_1 = aktuell_tid;
  }
  // Kolla om det är dags att ändra status på lysdiod 2 
  if (aktuell_tid > ledtimer_2 + blinktid_2) {
    blinka_2();
    // Ställ om timer för LED 2 till aktuell tid
    ledtimer_2 = aktuell_tid;
  }
  // Kolla om det är dags att ändra status på lysdiod 3
  if (aktuell_tid > ledtimer_3 + blinktid_3) {
    blinka_3();
    // Ställ om timer för LED 3 till aktuell tid
    ledtimer_3 = aktuell_tid;
  }
}

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *