Arduino Due - How to implement multitasking?

I am trying to write a simple operating system to run on my Arduino Due. The basic idea is simple: I have a timer interrupt that periodically invokes the scheduler to select a new process to run. It's not much, but I have to start somewhere.

My program is successfully jumping to the interrupt handler, which is good. For reference this is the code I'm using:

void TC3_Handler()
{
  TC_GetStatus(TC1, 0);  // Accept the interrupt

  // The scheduler would invoke a new process here <--

  return;
}

void setup()
{
  // ...

  createTimerInterrupt(TC1, 0, TC3_IRQn, 1);  // Enable the timer interrupt with a frequency of 1 Hz

  // ...
}

The problem is that if I pass control to a new process by invoking the scheduler, the interrupt handler might not return. This is because there's no guarantee the new process will complete before the next interrupt.

But forcibly ejecting from the handler like this seems to create problems. For example, certain functions such as delay() cause the system to freeze after the first interrupt is handled -- it's as if the system timer is disabled while handling the interrupt and doesn't get turned back on unless the handler returns. This makes me suspect there's a behind-the-scenes wrapper function that calls the handler, though I could be wrong.

I am also concerned that there are consequences associated with stack management, since the return address is never popped. (Also while we're on the subject, how is there a stack if there isn't an operating system yet? Is it statically allocated by the compiler?)

I hope I've made all of this clear. Bottom line: how can I implement multitasking in my operating system? I appreciate any help or possible alternatives.

Forgot to include the function that sets up the interrupt. This may or may not be relevant.

void createTimerInterrupt(Tc* tc, uint32_t channel, IRQn_Type irq, uint32_t frequency)
{
  pmc_set_writeprotect(false);
  pmc_enable_periph_clk((uint32_t) irq);
  TC_Configure(tc, channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK4);
  uint32_t rc = (VARIANT_MCK / 128) / frequency; // 128 because we selected TIMER_CLOCK4 above
  TC_SetRA(tc, channel, rc/2); // 50% high, 50% low
  TC_SetRC(tc, channel, rc);
  TC_Start(tc, channel);
  tc->TC_CHANNEL[channel].TC_IER = TC_IER_CPCS;
  tc->TC_CHANNEL[channel].TC_IDR = ~TC_IER_CPCS;
  NVIC_EnableIRQ(irq);
}

Hi,

In summary:

To implement multitasking may be you can use interrupts (which is reecommendable if you need a -very- fast answer to an external event by swaping tasks -stops what the controller is currently doing to make another thing, and, when finished, returns to the suspended task). It has, among others, the disadvantage that you have a limited number of interrupts.

There exists a mature technique that consists, mainly, in the conscutive calling to the modules you want to work "at the same time". Have a look on this thread:

http://forum.arduino.cc/index.php?topic=299189.0

Best regards

For a tutorial with the related code about real preemptive multitask on Due, you have have a look at my implementation of Babix and its document (with all the required background to understand the principles and the implementation).
It implements task-switching under interrupt, with context save, processes creation with stacks.

Have a look at the thread I opened at [Announce] Babix a small educational preemptive multitask kernel for Due - Arduino Due - Arduino Forum

I hope it will help you.

Thank you for the quick responses. The Babix source code is especially helpful. I'm just confused about one implementation detail: how did you register your interrupt handlers with the system? You don't appear to make any explicit calls to tell the CPU the names of your handlers or when to branch to them.

At one point you mention in a comment that the pendSVHook() handler "overrides the weak symbol of the Arduino 'standard library'". Is this simply because Arduino normally implements a handler of the same name? Then what about your other handlers like HardFault_Handler(), MemManage_Handler(), etc...?

I just noticed another comment underneath the other handlers that says they also override "weak" functions using extern "C" redefinitions. So all of your handlers are using this same mechanism, but I still don't understand how it works. Could you explain? From my understanding extern "C" is used to provide linkage to C functions when using a C++ compiler.

All it written in the PDF :wink:

In fact you can't attach an interrupt directly with attachinterrupt. For several reasons.
The most important is that the "stdlib" of the Arduino uses the SysTick interrupt for other things. So to keep these these done and allows the users to add their own "handler", the choice was to add a hook (systickHook, pendSVHook) at which users may branch their own code. And the mechanism used is via weak symbols. These symbols have default values (functions) which do nothing / "stop" the CPU. That's why we override them.

You may directly change the handler address in the vectors table manually, but you would break some functions of the "stdlib" of the Arduino environment.

The extern C {} is needed to prevent C++ compilation to attempt function overloading which seems to conflict with the weak symbol mechanism (and prevent the override of the symbol).

Just have a look at the PDF I wrote, you will find all the details, with deeper explanations than in the comments :wink:

I cannot thank you enough for your help; you have been an incredible resource. I think with the information provided in your PDF I can finally resolve my timer interrupt issue and start multitasking successfully.

Thanks again!

You are welcome :slight_smile:
The aim of this code was to write one of the pieces of code I ever wanted to write (there still remains some ^^) and to help people interested in understanding this "magic" stuff that multitask is :wink:
So feel free to use it !
Best,