ChibiOS/RT RTOS trial port for Arduino

I have posted a port of ChibiOS/RT here Google Code Archive - Long-term storage for Google Code Project Hosting.. The file is ChibiOS20111027.zip.

There are five examples. Three are small tests and two are serious data logging sketches.

I used timer 0 for the system timer so the OS tick time is 1024 usec or 976 Hz.

The fastest sketch for the Mega can log 976 samples per second to a csv file where each sample is the value of four analog pins.

Here is a description of the examples:

There are five examples in the ChibiOS/examples folder.

chBlinkPrint - A simple example of three threads. A high priority thread
blinks an LED, a medium priority thread prints a counter
every second, and the low priority idle loop increments the
counter.

chContextTime - Test to determine context switch time with a semaphore.

chFastLogger - Data logger optimized for 328 Arduino. Logs two analog pins
every tick (1024 usec tick).

chJitter - Test of jitter in sleep for one tick.

chMegaLogger - Data logger optimized for Mega. Logs four analog pins
every tick (976 samples per sec, four values per sample)

Great!
FYI - I am running ardu328p @7.37MHz, 57k6. When running chBlinkPrint the timing seems to be 2x slower (led blinks 2.5x per sec, each print 2secs) and the output is
0
490206
979938
1469669
1959827
..
When running chJitter I get constant 2286, 2322.
P.

pito,

I don't setup timer 0 for the system tick. I just use the Arduino setup and the TIMER0_COMPA_vect ISR.

I didn't want to take another timer and the way timer 0 is setup for PWM I get a tick rate of

F_CPU/(64*256)

You could either change the PWM setup and edit the ISR or use another timer if you want a different tick rate.

I need to change the definition in chconf.h

#define CH_FREQUENCY  976

to

#define CH_FREQUENCY   (F_CPU/16384L)

Thanks, it blinks as advertised now :).p

Any hint how to determine the "200" stack size, ie.:
..static WORKING_AREA(waThread3, 200);
? p.

Here is a link http://forum.chibios.org/phpbb/viewtopic.php?f=4&t=82.

I haven't tried it yet but plan to make a version for Arduino.

..interesting, but it is a postmortem check..
BTW - I removed the ATOMIC_BLOCK(ATOMIC_FORCEON) around count++ in chBlinkPrint. I have a third thread making an average and diff on the count var, and it seems it works. It is really necessary to do it atomic way? P.
Edit: it seems it shall be atomic as it starts to print garbage after a minute..

It almost must be a postmortem check.

Stack use depends on interrupts and how the compiler uses the stack. A task can cause various interrupts.

When I build a system I use this kind of test to insure I have a safety margin but am not wasting memory.

For critical systems there are tools like this StackAnalyzer: Stack Usage Analysis.

I see.. Quite a difficult decision on stack sizes when using 328p and several tasks then..

I enabled CH_DBG_FILL_THREADS in chconf.h and hacked the chBlinkPrint example to check the stack usage after it ran for a while.

The result is that ChibiOS allocates a minimal stack size of 69 bytes and the argument to WORKING_AREA is added.

For the LED blink thread 133 = (69 + 64) bytes were allocated and 102 bytes were unused for a max use of 31 bytes.

For the print thread 269 = (69 + 200) bytes were allocated and 198 were unused for a max use of 71 bytes.

Here is the hacked sketch, type any character to trigger the stack use print.

// Simple demo of three threads
// LED blink thread, print thread, and idle loop
#include <ChibiOS.h>
#include <util/atomic.h>
const uint8_t LED_PIN = 13;

volatile uint32_t count = 0;
//------------------------------------------------------------------------------
// thread 1 - high priority for blinking LED
// 64 byte stack beyond task switch and interrupt needs
static WORKING_AREA(waThread1, 64);

static msg_t Thread1(void *arg) {
  pinMode(LED_PIN, OUTPUT);
  while (TRUE) {
    digitalWrite(LED_PIN, HIGH);
    chThdSleepMilliseconds(50);
    digitalWrite(LED_PIN, LOW);
    chThdSleepMilliseconds(150);
  }
  return 0;
}
//------------------------------------------------------------------------------
// thread 2 - print idle loop count every second
// 200 byte stack beyond task switch and interrupt needs
static WORKING_AREA(waThread2, 200);

static msg_t Thread2(void *arg) {
  Serial.begin(9600);
  while (TRUE) {
    Serial.println(count);
    chThdSleepMilliseconds(1000);
  }
  return 0;
}
//------------------------------------------------------------------------------
void setup() {
  // initialize ChibiOS with interrupts disabled
  // ChibiOS will enable interrupts
  cli();
  halInit();
  chSysInit();
  
  // start blink thread
  chThdCreateStatic(waThread1, sizeof(waThread1),
    NORMALPRIO + 2, Thread1, NULL);
    
  // start print thread
  chThdCreateStatic(waThread2, sizeof(waThread2),
    NORMALPRIO + 1, Thread2, NULL);
}
//----------------------------------
size_t get_thd_free_stack(void *wsp, size_t size)
{
  size_t n = 0;
#if CH_DBG_FILL_THREADS
  uint8_t *startp = (uint8_t *)wsp + sizeof(Thread);
  uint8_t *endp = (uint8_t *)wsp + size;
  while (startp < endp)
    if(*startp++ == CH_STACK_FILL_VALUE) ++n;
#endif
  return n;
}
//----------------------------------------------------------------
void stackUse() {

  size_t n1 = get_thd_free_stack(waThread1, sizeof(waThread1));
  size_t n2 = get_thd_free_stack(waThread2, sizeof(waThread2));
  cli();
  Serial.print(sizeof(waThread1) - sizeof(Thread));
  Serial.write(',');
  Serial.println(n1);
  Serial.print(sizeof(waThread2) - sizeof(Thread));  
  Serial.write(',');
  Serial.println(n2);
  
  while(1);
}
//------------------------------------------------------------------------------
// idle loop runs at NORMALPRIO
void loop() {
  // must insure increment is atomic
  // in case of context switch for print
  ATOMIC_BLOCK(ATOMIC_FORCEON) {
    count++;
  }
  if (Serial.available()) stackUse();
}

With
.. Serial.println(sizeof(waThread1) - sizeof(Thread) - n1); ...
gives (in my case, 3tasks):
allocated, used

133,31
269,62
269,86

With smaller stacks:
allocated, used

79,31
79,56
99,73
So not a predictable behaviour (or there are min and max I cannot catch)..

The main reason for the variation is where the task is when an interrupt happens. This adds the interrupt context to the stack.

I suspect you only have timer0 and Serial. Serial probably only happens when you send the character to print the stats.

That just how it is with preemptive RTOSes. You need to add a safety factor to the stack.

It's a lot better to have some measure of what is happening than guess. Many embedded systems programmers use the stack fill trick.

Would it be possible to use timer2 for os_ticks and during os_idle to go into extended standby mode?
Timer2 runs during that mode from xtal clk, can wake up the cpu when os_tick fires and it takes only 6clock cykles to wake up. So when scheduler sees there is nothing to do it can sleep for a while.. p.

Timer2 works fine. Just edit board.c to change boardInit() and CH_IRQ_HANDLER(TIMER0_COMPA_vect) to use timer2.

Also change

#define CH_FREQUENCY                    (F_CPU/16384L)

in chconf.h