3
\$\begingroup\$

This is my attempt to realise multitasking (well almost) through interrupts. In this example, Task 1 is: Toggle LED connected to PB1 at 2Hz (Timer 1 is used, OCR1 can hold values upto 65535), Task 2 is: Toggle LED connected to PB3 at 61Hz (since Timer2 OCR2 is 8bit). It seems to be working too.

  • Have i understood the concept of multitasking right?
  • How can i allocate time slices for a "Task" or is it not possible in this case?
  • Can i replace the "Task" from toggling LED to ADC measurement or receiving bytes on USART? Or would that be realised differently?

Here is my Program:

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define COMP_REG_VAL 61250

void timer1_init()
{
    DDRB   |= 1<<PB1;        // Set PortB Pin1 as output
    TIFR1  |= 1<<OCF1A;     // Clear Output compare flag
    TIMSK1 |= 1<<OCIE1A;    // Enable Interrupt on Output compare match  
    TCCR1B |= ( (1<<CS12) | (1<<WGM12) );     // Prescaler Fosc/256 ===> 62500
}

void timer2_init()
{
    DDRB   |= 1<<PB3;        // Set Port B pin3 as Output 
    TIFR2  |= 1<<OCF2A;     // Clear Output compare flag
    TIMSK2 |= 1<<OCIE2A;    // Enable Interrupt on Output compare match  
    TCCR2A |= (  (1<<WGM20) | (1<<WGM21) );     //FAST PWM mode
    TCCR2B |= ( (1<<WGM22) | (1<<CS22) |  (1<<CS21) | (1<<CS20) );  //Fast PWM MOde, Prescaler=fosc/1024
}

int main (void)
{
  sei();
  timer1_init();
  timer2_init();
  OCR1A = COMP_REG_VAL/2;
  OCR2A = 255;

  while(1) 
  {

  }
}

ISR(TIMER1_COMPA_vect)
{
   PORTB ^= (1<<PB1);       // Toggle Pin when overflow occurs
}


ISR(TIMER2_COMPA_vect)
{
   PORTB ^= (1<<PB3);       // Toggle Pin when overflow occurs

}
\$\endgroup\$
5
\$\begingroup\$

Have I understood the concept of multitasking right?

there are different flavours of "multitasking", some more sophisticated than others. in a nutshell, it is about time sharing to provide the perception of concurrency.

How can I allocate time slices for a "Task" or is it not possible in this case?

your approach to multitasking is less conventional. typically, it is a scheduler that switches in and out of tasks periodically. so the "time slice" is fixed.

Can I replace the "Task" from toggling LED to ADC measurement or receiving bytes on USART? Or would that be realised differently?

that can be tricky. the key challenge is to interrupt a UART transmission in the middle of it and only to execute the same UART transmission in the ISR. not all microcontrollers are capable of that, and even if they are, it should be approached with caution.

\$\endgroup\$
  • \$\begingroup\$ Is this example not a kind of scheduling in a way, LED1 toggles every 0.5 sec and LED2 every 0.016sec? If scheduler were a Statemachine, it would be just another switch case right? every case thus blocking the MCU. \$\endgroup\$ – Abel Tom Jul 16 '17 at 14:09
  • \$\begingroup\$ it is, as the definition of "multitasking" isn't strict, and yours is certainly within the realm of all "multitasking" universe. I was simply pointing out that a typical scheduler would run a task for a period of time, swap it out and then run the next, regardless of whether the execution has completed. \$\endgroup\$ – dannyf Jul 16 '17 at 14:35
  • \$\begingroup\$ Ok i guess what you're trying to say that there are more conventional ways of implementing multi-tasking. :) have to do some reading into "implementing scheduler in C" or something similar. Thanks so much for the feedback. \$\endgroup\$ – Abel Tom Jul 16 '17 at 15:59
  • \$\begingroup\$ you got it. there are quite a few simple "multi-tasking" schemes out there. the simplest would be the protothreads. it is literally a switch / case statement. \$\endgroup\$ – dannyf Jul 16 '17 at 17:23
2
\$\begingroup\$

You have programmed pre-emptive multitasking without a central task switcher. This means that your individual processes have no control about when they are interrupted. It would be far, far easier to implement cooperative multitasking where each process gives up its time when it is done with some "atomic" bit of work.

Sure, at first glance, doing it pre-emptive seems promising, but you will, sooner or later, run into bad cases of race conditions. For example, if you call some module from your code that can wait for external conditions (i.e., I2C or USB stuff), then you're screwed. Even just printing some debug output to a slow serial connection (taking longer than the timer duration of one of your interrupts) will get you into bad trouble.

A real OS would also use a central task switcher A.K.A. scheduler, but that is not so much of an issue here as you know exactly what processes are running in your µC.

If you want to give the cooperative multitasking a spin, and I very much invite you to because it is a very powerful concept for µCs, take a look at coroutines. They are like loops but "inside out", i.e., they are loops that "give up their time slice" frequently. They solve the problem of pre-emptive-induced race-conditions (i.e., things getting interrupted when they should not), they are quite easy to implement, and just fun to be around.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.