New FreeRTOS and ChibiOS/RT real time operating system libraries

The latest version of ChibiOS/RT and FreeRTOS are now located here: Google Code Archive - Long-term storage for Google Code Project Hosting.

I have ported FreeRTOS version 7.3.0 and ChibiOS/RT 2.4.2 to the Arduino IDE. AVR boards, Due, and Teensy 3.0 are supported.

The files are ChibiOSBeta20121212.zip and FreeRTOSBeta20121215.zip http://code.google.com/p/beta-lib/downloads/list. Read the included html files and try the examples.

See these websites for detailed information: ChibiOS free embedded RTOS - ChibiOS Homepage and http://www.freertos.org/.

Here is a nice paper on FreeRTOS http://stiff.univ-brest.fr/~boukhobza/images/stories/Documents/Teachings/OSM/expo/FreeRTOS_Melot.pdf.

Excellent :o) I would be grateful if you could post a link (to the FreeRTOS part) in the FreeRTOS Interactive site. Here is a link to the Atmel specific forum:
http://interactive.freertos.org/forums/103473-atmel

...or do you think I should create an Arduino specific forum?

There is also a demo that uses Atmel Studio 6 with GCC in the latest Atmel Software Framework (ASF) distribution (http://asf.atmel.com)

Regards,
Richard (http://www.FreeRTOS.org)

Has anyone tried to use Memory Pools and Mailboxes in this port? Here's my two questions:

  1. I'm seeing some odd behavior in the mailboxes, particularly in the first pull from the mailbox. The data of the object pulled seems corrupted though I suspect it's some header information. Likely pilot error.

  2. Also seeing "junk" at the top of every memory pool item allocated. Again, I think from reading the ChibiOS forums and documentation I think this is the pool header though some examples on the ChibiOS forums do not show this needing to be exlicitly defined in the memory structure.

Here's the code in question. Two small tasks delaying a second each, posting to the mailbox a seconds counter, and a third task picking it up and displaying it on an LCD. Pretty simple stuff. Notice I do a bit of error checking on the content to weed out the first mailbox data error.

Here's the structure definition for the mailbox/mempool data:

typedef struct {
	int junk;
	int	thread;
	unsigned long cnt;
} msgLCD;

Here's the RTOS code. Did not include header info and support functions to keep it short.:

#define MB_SIZE	6

#define TEAM_A_TITLE_LOC	0
#define TEAM_B_TITLE_LOC	16
#define TEAM_A_TITLE		"Time A"
#define TEAM_B_TITLE		"Time B"
#define TEAM_A_TIMER_H		7
#define TEAM_B_TIMER_H		16+TEAM_A_TIMER_H
#define TEAM_A_TIMER_M		10
#define TEAM_B_TIMER_M		16+TEAM_A_TIMER_M
#define TEAM_A_TIMER_S		13
#define TEAM_B_TIMER_S		16+TEAM_A_TIMER_S

#define ACTIVE_TEAM_A_LOC	15
#define ACTIVE_TEAM_B_LOC	16+ACTIVE_TEAM_A_LOC
#define ACTIVE_TEAM_INDICATOR	"<"

const uint8_t LED_PIN = 13;
//const uint8_t servo_PIN = 9;

//Servo myServo;

SoftwareSerial mainLCD(2, 3);

static char buffer[sizeof(msgLCD)*MB_SIZE];

//MemoryPool pool;

static MEMORYPOOL_DECL(pool, sizeof(msgLCD)*MB_SIZE, NULL);
static MAILBOX_DECL(mbox, buffer, sizeof(buffer));


// align data, this depends on your compiler. this works for GCC
msgLCD data[MB_SIZE] __attribute__((aligned(sizeof(stkalign_t))));


String formatTwoDigits(unsigned int iVal) {
  String sTens = "";
  sTens += iVal / 10;
  String sOnes = "";
  sOnes += iVal % 10;
  return sTens+sOnes;
}

void printTime(unsigned int h, unsigned int m, unsigned int s, int team) {
	int rowLoc = TEAM_A_TIMER_H;

	if (team == 2) {
		rowLoc = TEAM_B_TIMER_H;
	}
	setLCDCursor(&mainLCD, rowLoc);
	mainLCD.print(formatTwoDigits(h));
	mainLCD.print(":");
	mainLCD.print(formatTwoDigits(m));
	mainLCD.print(":");
	mainLCD.print(formatTwoDigits(s));
}

//------------------------------------------------------------------------------
// Simulated Worker Thread 1
static WORKING_AREA(waThread1, 64);

static msg_t Thread1(void *arg) {

	int counter = 0;
	while (TRUE) {
	    chThdSleepMilliseconds(1000);
	    counter++;

	    //grab a memory pool item
	    msgLCD *msg = (msgLCD *)chPoolAlloc(&pool);
	    if (msg != NULL) {
		    msg->thread = 1;
		    msg->cnt = counter;
		    // Here we'll send something to the LCD thread
		    chMBPost(&mbox, (msg_t)msg, TIME_INFINITE);
	    }
	}
	return 0;
}

//------------------------------------------------------------------------------
// Simulated Worker Thread 2
static WORKING_AREA(waThread2, 64);

static msg_t Thread2(void *arg) {
	int counter = 0;
	while (TRUE) {
	    chThdSleepMilliseconds(1000);
	    counter++;

	    //grab a memory pool item
	    msgLCD *msg = (msgLCD *)chPoolAlloc(&pool);
	    if (msg != NULL) {
		    msg->thread = 2;
		    msg->cnt = counter;
		    // Here we'll send something to the LCD thread
		    chMBPost(&mbox, (msg_t)msg, TIME_INFINITE);
	    }
	}
	return 0;
}

//------------------------------------------------------------------------------
// LCD Thread - This thread waits on a Mailbox (FIFO Queue) and depending on who
// sent the message, displays in the proper quadrant of the LCD
static WORKING_AREA(waLCDThread, 64);

static msg_t LCDThread(void *arg) {
	msg_t msg1;
	msgLCD msg2;
	unsigned int teamA_h = 0, teamA_m = 0, teamA_s = 0;
	unsigned int teamB_h = 0, teamB_m = 0, teamB_s = 0;

	static boolean t1s = FALSE;
	static boolean t2s = FALSE;
	static boolean LED_state = FALSE;

	clearDisplay(&mainLCD);

	setLCDCursor(&mainLCD, TEAM_A_TITLE_LOC);
	mainLCD.print(TEAM_A_TITLE);
	setLCDCursor(&mainLCD, TEAM_A_TIMER_H);
	mainLCD.print("00:00:00");

	setLCDCursor(&mainLCD, TEAM_B_TITLE_LOC);
	mainLCD.print(TEAM_B_TITLE);
	setLCDCursor(&mainLCD, TEAM_B_TIMER_H);
	mainLCD.print("00:00:00");

	while (TRUE) {

		// Wait on a message from the working threads to display
		msg1 = chMBFetch(&mbox, (msg_t *)&msg2, TIME_INFINITE);

		if (msg1 == RDY_OK) {
			LED_state = !LED_state;
			digitalWrite(LED_PIN, LED_state);
			// If we get here, we have a message

			if (msg2.thread == 1) {
				setLCDCursor(&mainLCD, TEAM_A_TIMER_H);
				t1s = !t1s;
				teamA_h = msg2.cnt / 3600;
				teamA_m = (msg2.cnt % 3600) / 60;
				teamA_s = msg2.cnt % 60;

				printTime(teamA_h, teamA_m, teamA_s, msg2.thread);

			}
			if (msg2.thread == 2) {
				setLCDCursor(&mainLCD, TEAM_B_TIMER_H);
				t2s = !t2s;
				teamB_h = msg2.cnt / 3600;
				teamB_m = (msg2.cnt % 3600) / 60;
				teamB_s = msg2.cnt % 60;
				printTime(teamB_h, teamB_m, teamB_s, msg2.thread);
			}
		} else {
			Serial.print("Fetch Status: ");
			Serial.println(msg1);
		}
		// Now we have to free the memory pool object
		chPoolFree(&pool, (void *)&msg2);

	}
	return 0;
}
//------------------------------------------------------------------------------
void setup() {
	Serial.begin(9600);
  mainLCD.begin(9600);
  pinMode(LED_PIN, OUTPUT);

//  myServo.attach(servo_PIN);
//  myServo.write(10);
  chThdSleepMilliseconds(15);


  // Here we can muck with the LCD because nothing in the RTOS system is initialized
  clearDisplay(&mainLCD);
  setLCDCursor(&mainLCD, 2);  // Set cursor to the 3rd spot, 1st line
  mainLCD.print("Initializing");
  setLCDCursor(&mainLCD, 16);  // Set the cursor to the beginning of the 2nd line
  mainLCD.print("RTOS System...");

  // From here on, only the LCD thread should handle this hardware.

  halInit();
  chSysInit();

  // add each element to the pool
  // this is the least obvious part of the process, as far as my experience went

  for(int i=0; i <= MB_SIZE; i++) {
      chPoolFree(&pool, &data[i]);
  }

  //Initialize the mailbox
  chMBInit(&mbox, (msg_t *)buffer, MB_SIZE);
  chMBReset(&mbox);
  delay(2000);

  msg_t msg1;

  msgLCD msg, msg2;

  chMBPost(&mbox, (msg_t)&msg, TIME_INFINITE);
  chMBPost(&mbox, (msg_t)&msg2, TIME_INFINITE);

  chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO + 2, Thread1, NULL);
  chThdCreateStatic(waThread2, sizeof(waThread2), NORMALPRIO + 1, Thread2, NULL);
  chThdCreateStatic(waLCDThread, sizeof(waLCDThread), NORMALPRIO + 3, LCDThread, NULL);

  while(TRUE){}
}
void loop() {
  // never called
}

I notice that you call these functions in setup()

  halInit();
  chSysInit();

I added a function, chBegin(), that calls these functions and fills the stack and never returns on AVR. On ARM chBegin() totally changes stack use and must be called.

I assume you are using the AVR version. You should be able to call these functions on AVR but you must not call any other ChibiOS functions before these calls and interrupts must be disabled when you call the functions. chSysInit will enable interrupts.

I did not enable memory pools and did not try this feature. I assume you enabled memory pools since your code compiles.

I didn't see a call to chPoolInit(). I believe the init call should replace this:

 for(int i=0; i <= MB_SIZE; i++) {
      chPoolFree(&pool, &data[i]);
  }

I have played with mailbox examples but not in this port.

I have several other mysteries so I am planing to port the ChibiOS testsuite to the Arduino environment. That may take some time.

You might download ChibiOS and look at the testsuite functions as examples.

Sorry I can't be of more help. I am very interested in what you are doing so please don't worry about posting too much code. I will be happy to help find problems by writing or looking at example code.

Thanks. Regarding the cbSysInit() I'm initializing the memory pool, mailbox, and tasks after this call so it should be OK.

I've swapped the chPoolInit() for the for loop. No difference in operation.

The oddness is that I have to prime the Mailbox with a post in setup() to get things to work. If I leave this out, the LCD thread does not seem to wake up.

Github is down right now, so I'm unable to look at some of the other examples. From memory though, I don't recall them having to do this.

I'll keep twiddling with it. I will likely add a few interrupts to the mix as well which will kick the tasks off. I'll let you know if I find anything else.

Oh, and thanks again for the port. Happy Holidays!

You are right chPoolInit is not needed and you do need to load the pool.

Here is a simple test sketch for mailboxes with a memory pool that suggests they work. It is a minimal test with the main task posting and fetching mail.

#include <ChibiOS_AVR.h>
//------------------------------------------------------------------------------
// mailbox size and memory pool object count
const size_t MB_COUNT = 6;
// type for a memory pool object
struct PoolObject_t {
  int var1;
  int var2;
};
// array of memory pool objects
PoolObject_t PoolObject[MB_COUNT];

// memory pool structure
MEMORYPOOL_DECL(memPool, MB_COUNT, 0);
//------------------------------------------------------------------------------
// slots for mailbox messages
msg_t letter[MB_COUNT];

// mailbox structure
MAILBOX_DECL(mail, &letter, MB_COUNT);
//------------------------------------------------------------------------------
void setup() {
  // initialize heap/stack memory and start ChibiOS
  chBegin(chSetup);
  while(1);
}
//------------------------------------------------------------------------------
void chSetup() {
  Serial.begin(9600);
  // fill pool with PoolObject array
  for (int i = 0; i < MB_COUNT; i++) {
    chPoolFree(&memPool, &PoolObject[i]);
  }
  // put messages in mailbox
  for (int i = 0; i < MB_COUNT; i++) {
    PoolObject_t* p = (PoolObject_t*)chPoolAlloc(&memPool);
    if (!p) {
      Serial.println("null chPoolAlloc");
      while(1);
    }
    p->var1 = i;
    p->var2 = 2*i;
    msg_t s = chMBPost(&mail, (msg_t)p, TIME_IMMEDIATE);
    if (s != RDY_OK) {
      Serial.println("chMBPost failed");
      while(1);  
    }    
  }
  // fetch and check messages
  for (int i = 0; i < MB_COUNT; i++) {
    PoolObject_t *p;
    msg_t s = chMBFetch(&mail, (msg_t*)&p, TIME_IMMEDIATE);
    if (s != RDY_OK) {
      Serial.println("chMBFetch failed");
      while(1);      
    }
    if (p->var1 != i || p->var2 != 2*i) {
      Serial.println("bad content");
      while(1);
    }
    // put memory back into pool
    chPoolFree(&memPool, p);
  }
  // see if memory is in pool
  for ( int i = 0; i < MB_COUNT; i++) {
    if (!chPoolAlloc(&memPool)) {
      Serial.println("free problem");
      while(1);
    }
  }
  // should be exhausted now
  if (chPoolAlloc(&memPool)) {
    Serial.println("excess pool object");
    while(1);
  }
  
  Serial.println("OK");
}
//------------------------------------------------------------------------------
void loop() {}