Featherweight OS for Arduino - Page 1
Forum section: electronics / general

author: ehsmeng
author_rank: Contributor
timestamp: June 24, 2014, 03:54:00 pm
content: Looking at Arduino examples, many seem content with making a stepper go 20 ticks and have the program blocked until done.How does it look in real life if you have say a serial listener receiving commands and controlling 10 steppers? Would you have an event loop with say sleep(100us) and iterating a data structure or do you do something more intricate like Protothreads or some other featherweight "os"?

author: dannyf
author_rank: Super Contributor
timestamp: June 24, 2014, 03:58:21 pm
content: Implement an interrupt-driven receiver and you don't need stinky OS, featherweight or otherwise.

author: retrolefty
author_rank: Super Contributor
timestamp: June 24, 2014, 04:07:52 pm
content: Many arduino projects have utilized simple 'state machine' concept using the internal millis() counter to allow the program to continue to loop without blocking, just check time status of each function and if interval has been reached, execute function, otherwise return to main loop function. User interrupts are also available if and when needed.

author: Kremmen
author_rank: Super Contributor
timestamp: June 25, 2014, 12:24:39 pm
content: That is what i usually do if / when i do something with Arduino. Like just now i have written a small app to drive a variable speed air rifle target trolley. The state machine cycles continuously and does all timings based on millis() without ever busy waiting for any changes. Works nicely but results in slightly complex looking applications due to the explicitly visible state machine.I have actually run FreeRTOS on the ATMega 328 that powers many Arduinos and it runs without problems. There will of course be rather low limits on how many tasks and other OS resources can be made to work but at least 3 tasks and 3 message queues worked like a charm with pre-emptive scheduling. 1 ms schedule timer was not a problem, meaning 1000 context changes per second.

author: nctnico
author_rank: Super Contributor
timestamp: June 25, 2014, 12:27:35 pm
content: Besides an OS there are two ways:- use a timer interrupt (mostly when timing is critical)- or use the statemachine approach using a system wide timer tick counter

author: Dago
author_rank: Frequent Contributor
timestamp: June 25, 2014, 01:33:58 pm
content: Usually program structure for these kinds of "multitasking" applications is a main event loop which "does everything" and then a bunch of interrupts that do everything that needs to be tended immediately (such as receiving UART data).So basically for your example you'd have a main event loop where you'd keep checking some kind of a command buffer if there is any new commands, and stepping the motors if necessary. A separate interrupt would load data to the command buffer that would get parsed in the main event loop. After the last packet of the command would be loaded the main event loop would parse and empty the buffer and command the steppers to do something. Might set timer based interrupts for controlling the stepper motor steps or such. Idea is to keep the interrupts as light as possible and do all the "processing" in the main event loop. So instead of calculating something in the interrupts you'd just set a flag that this needs to be calculated and then you'd do it in the event loop.Depending on the application and the peripherals of the MCU etc. this can be rather simple and straightforward or complicated. If it gets really complicated then you'd prolly need some other way to solve the problem (such as an RTOS). 10 steppers controlled via UART sounds like it should be fairly simple but depends on the speed of the steppers and amount of timers available etc.

author: rob77
author_rank: Super Contributor
timestamp: June 25, 2014, 02:40:07 pm
content: Quote from: ehsmeng on June 24, 2014, 03:54:00 pmARM or other CPU suitable for such a thingi do like Atmega, i use it a lot, but for complex controls i wouldn't use ityou know.. it's like trying to do CAD/CAM on ZXspectrum or C64

author: mikerj
author_rank: Super Contributor
timestamp: June 25, 2014, 09:31:49 pm
content: Quote from: dannyf on June 24, 2014, 03:58:21 pmStinky OSFor 8 bit code that has a large number of separate "tasks" that must run concurrently, then state machines are the obvious answer. Just implement a simple system timer with e.g. a 1 millisecond period, and then have each state machine test whether it is time to start running. All state machines are called in sequence from the main loop, and simply return without doing anything if they are not ready to run. I have implemented some fairly complex control systems like this, but you do have to be careful about how much time is used by each state.Adam DunkelsProtothreadsallow you to hide the state machine by clever use of the pre-processor, which can make you code look cleaner and easier to read. However, it's not quite as efficient as a simple hand written state machine using enumerated state values, especially for large numbers of states.You may also need FIFO's etc. for "inter-task" comms.

author: zapta
author_rank: Super Contributor
timestamp: June 26, 2014, 06:50:21 pm
content: Quote from: ehsmeng on June 24, 2014, 03:54:00 pmHow do you generate the stepper signals, by software toggling pins high/low? If so what is the timing accuracy that you need to make these bit flips?An arduino program looks like thisvoid setup() {}void loop() {}If you want to have multi tasking you can do it by emulating multiple arduino programsvoid setup1() {}void loop1() {}void setup2() {}void loop2() {}...and then implement the main asvoid setup() {setup1();setup2();...}void loop() {loop1();loop2();...}Just make sure not to use delay() and similar busy loops in the loop() functions and make your timing decisions based on state variables and milliseconds(). For example, a led blinking task can look like this pseudo codestatic long ms_at_last_chagne;static boolean last_value;void setup_led_blinking() {set LED pin mode to outputset LED pin highms_at_last_chagne = millseconds();boolean last_value = true;}void loop_led_blinking {// Need to do anything?if ((millisecods() - ms_at_last_chagne) < 500) {return;}// Flip pin.last_value = !last_value;set LED pin to last_value;// You can use the current time or keep tracking the old schedule.ms_at_last_chagne += 500;}

author: ehsmeng
author_rank: Contributor
timestamp: June 29, 2014, 11:33:41 am
content: Hi all,Thanks for your suggestions! I did a small test, and I think I tentatively like this below. I suppose that if I need some accurate pulse, I'll add a timer IRQ.Code:#include <limits.h>const int ledpin = 7;const int cpupin = 8;// -----------library code---------------------------unsigned int m_tickstart;class TimeSlot{public:int m_skipTicks; // How many ticks to skipTimeSlot(){m_skipTicks = 0;}virtual void init();// Return value is how many ticks should be skipped until this function is called the next time// If unsure, do a return 0virtual int tick();//static unsigned int m_tickstart; // why does this not work?static void timerstart_tasklist(){m_tickstart = micros();}static void timerstop_tasklist(unsigned int tickinterval){unsigned int us_stop, us_total, us_start;us_start = m_tickstart;us_stop = micros();if (us_stop > us_start) {us_total = us_stop - us_start;} else {us_total = UINT_MAX - us_start;us_total += us_stop;}if (us_total < tickinterval) {delayMicroseconds(tickinterval - us_total);}}static void init_tasklist(TimeSlot *tasklist[]){for (int ii=0; NULL != tasklist; ii++){tasklist->init();}}static void tick_tasklist(TimeSlot *tasklist[]){for (int ii=0; NULL != tasklist; ii++){TimeSlot *ts = tasklist;if (0 < ts->m_skipTicks){ts->m_skipTicks--;}else if (0 == ts->m_skipTicks){ts->m_skipTicks = ts->tick();}else{// if negative, then not called!}}}};class Button : public TimeSlot{public:int m_port;Button(int port){m_port = port;}void init(){pinMode(m_port, INPUT);}virtual int tick();};// -----------Project code---------------------------class myButton : public Button{public:int m_state;myButton(int a) : Button(a){m_state = LOW; // to get it predictable}int tick(){int present_state = digitalRead(m_port);if (present_state != m_state){m_state = present_state;digitalWrite(ledpin, m_state);return 10; // ignore mechanic bounce}return 0;}};TimeSlot *tasklist[] = {new myButton(2),NULL};void setup(){pinMode(ledpin, OUTPUT);pinMode(cpupin, OUTPUT);TimeSlot::init_tasklist(tasklist);}int ledstate = 0;void loop(){digitalWrite(cpupin, HIGH);TimeSlot::timerstart_tasklist();TimeSlot::tick_tasklist(tasklist);digitalWrite(cpupin, LOW);TimeSlot::timerstop_tasklist(1000);}And a meme:I don't usually use ii for loop variable(o_o)But when i do, it's because  does not mean italic in bbcode

author: sunnyhighway
author_rank: Frequent Contributor
timestamp: June 29, 2014, 03:15:54 pm
content: Quote from: ehsmeng on June 29, 2014, 11:33:41 amCode: don't seem to have this problem. ;-)