Pages: 1 [2]   Go Down
Author Topic: More complex, complete examples that do several things at once  (Read 1037 times)
0 Members and 1 Guest are viewing this topic.
United Kingdom
Offline Offline
Tesla Member
***
Karma: 220
Posts: 6587
Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

If I have to write a system that does more than 3 or 4 things at a time on an Arduino, I use a cooperative multi tasking scheduler. It still requires discipline because you have to split each task into chunks that don't block and always complete within a few hundred microseconds. For example, when updating an LCD, you have to avoid using the clear and home functions, and only write a few characters at a time (I also modified the LiquidCrystal library to make it faster). The big advantage of using a scheduler is that it lets you keep the code for the different tasks separate.

For simpler systems, I have used blink-without-delay style timer polling for most tasks, and a tick interrupt for time-critical regular tasks such as polling rotary encoders and multiplexing displays.
Logged

Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

Worst state in America
Offline Offline
God Member
*****
Karma: 23
Posts: 665
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

If I have to write a system that does more than 3 or 4 things at a time on an Arduino, I use a cooperative multi tasking scheduler. It still requires discipline because you have to split each task into chunks that don't block and always complete within a few hundred microseconds. For example, when updating an LCD, you have to avoid using the clear and home functions, and only write a few characters at a time (I also modified the LiquidCrystal library to make it faster). The big advantage of using a scheduler is that it lets you keep the code for the different tasks separate.

For simpler systems, I have used blink-without-delay style timer polling for most tasks, and a tick interrupt for time-critical regular tasks such as polling rotary encoders and multiplexing displays.

Is that code available for general use?
Logged

Offline Offline
Edison Member
*
Karma: 116
Posts: 2205
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I really would love to see a more complete example of a real use-case

A simple case would be something like this:

Code:
  static unsigned char current_task=0; //task indexer
  switch (current_task) {
    case 0: run_task0(); //run task0
               break;
    case 1: run_task1(); //run task1
               break;
    ...
    default: do_nothing(); //do nothing
  }
  current_task = (current_task == MAX_TASK-1)? 0: (current_task + 1); //increment to the next task
so it will run task0..x, each time it is invoked.

It is essentially a primitive task scheduler. It does require that each task will eventually exit (hopefully quickly).

You can fancy it up with other features but this basically is it.
Logged

Manchester (England England)
Online Online
Brattain Member
*****
Karma: 509
Posts: 31481
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I really would love to see a more complete example of a real use-case doing LCD (or some other form of) UI, serial and some other things at once. In principle I know how it should be done, but like with the simpler examples, seeing how others do it (and do it well, unlike my own first exeriments) would help me, I think.

Are there no examples of larger projects with complete sourcecode to be examined by newbies?

It is also interesting to me how people organize code, what conventions they invent, etc.

Have a look at my project, it is written in a state machine way and monitors the lid continuously while playing the game:-
http://www.thebox.myzen.co.uk/Hardware/Dice_Game.html
Logged

UK
Offline Offline
Shannon Member
****
Karma: 184
Posts: 11179
-
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

A simple case would be something like this:

What do you see as the advantage of that, compared to just calling the tasks directly?

Code:
run_task0();
run_task1();

Of course you wouldn't use those names, you use names that tell you what the different functions actually do, like handleSerialPort(), handleButtons() etc.
Logged

I only provide help via the forum - please do not contact me for private consultancy.

Offline Offline
Edison Member
*
Karma: 116
Posts: 2205
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Here is a short demo: running 5 tasks (the last one isn't actually executed, just to show as an example):

Code:
//demo for multi-tasking
#define MAX_TASKS  5 //maximum number of tasks

//task 0 - lcd1602
#include "LiquidCrystal.h"
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 13, 8, 9, 10, 11);
void task0(void) {
  static unsigned char i=0; //count
  lcd.setCursor(0, 1); //column 0, line 1
  lcd.print(i++); //print count, then increment it
}

//task 1
#define LED_1 1 //led1 on pin1
void task1(void) {
  static unsigned char count=0;
  count+=1; //increment count
  if (count==100) {
    count -= 100;   //reset count
    digitalWrite(LED_1, !digitalRead(LED_1)); //blink led1 every 100 turns
  }
}

//task 2
#define LED_2 2 //led1 on pin1
void task2(void) {
  static unsigned char count=0;
  count+=1; //increment count
  if (count==200) {
    count -= 200;   //reset count
    digitalWrite(LED_2, random(0,2)); //randomly turn on / off led_2
  }
}

//task 3
#define LED_3 3
void task3(void) {
  digitalWrite(LED_3, digitalRead(LED_1)); //follow led_1
}

//task 4
#define LED_4 4
void task4(void) {
  digitalWrite(LED_4, !digitalRead(LED_2)); //follow the opposite of LED_2
}

//task 5
#define LED_5 5
void task5(void) {
  digitalWrite(LED_5, !digitalRead(LED_5)); //flip led5 - never executed
}


void setup(void)  {
  //initialize all tasks
 
  //initialize for task0
  lcd.begin(16,2); //display is 16.2
  lcd.print("Hello, world!");

  //initialize for task1
  pinMode(LED_1, OUTPUT);

  //initialize for task2
  pinMode(LED_2, OUTPUT);

  //initialize for task3
  pinMode(LED_3, OUTPUT);

  //initialize for task4
  pinMode(LED_4, OUTPUT);

  //initialize for task2
  pinMode(LED_5, OUTPUT);


}

void loop(void) {
  static unsigned char current_task = 0; //task indexer
  switch (current_task) {
  case 0: task0(); break;
  case 1: task1(); break;
  case 2: task2(); break;
  case 3: task3(); break;
  case 4: task4(); break;
  case 5: task5(); break;  //never executed
  default: break;
  }
  current_task = (current_task==MAX_TASKS - 1)?0:(current_task+1); //increment task indexer

}


task0 is to run an lcd and count up a value; task1..4 are simple blinking of leds. task5 is excluded from execution, by the definition of MAX_TASKS.

The scheduler doesn't assign a fixed time slot to individual tasks: the next task is run as soon as the previous one finishes. You can fix that by using a systick, like millies() at the end of the task and define a minimum time slot for individual tasks.

You can introduce functional pointers and use them to insert user tasks into a scheduler shell, like the one above, to make it easier to install user tasks into the scheduler - and potentially keep the scheduler entirely out of the user code base.

That's basically the gist of pretty much any RTOS.
Logged

United Kingdom
Offline Offline
Tesla Member
***
Karma: 220
Posts: 6587
Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

If I have to write a system that does more than 3 or 4 things at a time on an Arduino, I use a cooperative multi tasking scheduler. It still requires discipline because you have to split each task into chunks that don't block and always complete within a few hundred microseconds. For example, when updating an LCD, you have to avoid using the clear and home functions, and only write a few characters at a time (I also modified the LiquidCrystal library to make it faster). The big advantage of using a scheduler is that it lets you keep the code for the different tasks separate.

For simpler systems, I have used blink-without-delay style timer polling for most tasks, and a tick interrupt for time-critical regular tasks such as polling rotary encoders and multiplexing displays.

Is that code available for general use?

Yes, it's at https://github.com/dc42/arduino, although it could do with more examples and more documentation. There are plenty of other multitaskers available for the Arduino as well.
Logged

Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

UK
Offline Offline
Shannon Member
****
Karma: 184
Posts: 11179
-
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Here is a short demo: running 5 tasks (the last one isn't actually executed, just to show as an example):

Yes, but why would you add that complexity rather than just executing each of the 'tasks' in turn within loop()? What advantage are you trying to obtain by making loop() call one function at each pass, rather than call all the functions? In this example, there is no advantage that I can see.
Logged

I only provide help via the forum - please do not contact me for private consultancy.

Offline Offline
Newbie
*
Karma: 0
Posts: 42
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

There are a lot of good responses here on this subject and as you might notice, in some cases its a matter of personal preference. Hobby coding is kind of cool that way, once you figure out how to make it go, and it works, its right for you; even if someone else thinks its a total hack. This is not necessarily the case for a professional coding project where maintenance, upgrades, or even personal injury issues are at stake. Anyhow, your solution could be polling, interrupts, RTOS etc. I guess what I'm saying is there is no one universal good answer to the asynchronous problem for a hobbyist. Creative programming can overcome most situations.

For me, I started out looking for an asynchronous platform to build my application upon even though I don't have a concrete project yet, but that's just me. I didn't really find anything completely suitable, so I'm building my own personal asynchronous device framework. Funny thing is, right now I have no idea if it will hold up to a real project. Fortunately for me, building my own framework is just part of the fun of using a micro-controller; even if I throw the code away, I'll have gained good experience and had fun in the process. I encourage you to explore and have fun.

Last night I completed some tests on my framework and blogged about it this morning if you are interested - the code will be available for further hacking by anyone. I do not plan on making a library out of this code, its just a personal project for me.

http://wizmoz.blogspot.com/2012/12/the-start-of-asynchronous-device.html

ddm
Logged

alabama
Offline Offline
Full Member
***
Karma: 1
Posts: 183
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You might try here http://tronixstuff.wordpress.com/tutorials/ starts off simple and adds and adds.
Gets pretty complicated fast, but explained well.
TomJ
Logged

Einstein once said you don't really understand anything until you can explain it to your Grandmother

Pages: 1 [2]   Go Up
Jump to: