Pages: 1 ... 5 6 [7] 8 9 ... 13   Go Down
Author Topic: DuinOS: small and simple rtos  (Read 38167 times)
0 Members and 2 Guests are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 20
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Julian,

Thanks for taking a look!

Here's what I have:

Code:
#include <DuinOS.h>
  #include <DuinOS/queue.h>
  #include <DuinOS/semphr.h>


  
  #define PIR_SENSOR_0  B00000001
  #define PIR_SENSOR_1  B00000010  
  #define STACK_SIZE 250
  
  #define pinLED  13
  
  #define TESTBED
  
 volatile byte PIR_state = 0;
 
 xSemaphoreHandle sem_PIR = NULL;


  //****************************************
  // Name: PRI_sensor_task()
  // Description:
  //   This is the task that processes the sensor reading done by the ISR
  // Creation Date:  2009/12/13
  // History:
  //    2009/12/13 - Created.
  //****************************************
   taskLoop(PIR_sensor_task)
   {
       byte  local_PIR_state, sensor_state;
        
      vSemaphoreCreateBinary(sem_PIR);

      if( sem_PIR == NULL )
      {
         // set an LED to indicate error
      }


       for( ;; )
       {
           // Here we'll take the sensor state and send it along to the MCP
           if (xSemaphoreTake(sem_PIR, portMAX_DELAY) == pdTRUE)
           {
             // enter a critical section and grab the state of the PIR sensors
             taskENTER_CRITICAL();
             local_PIR_state = PIR_state;
             taskEXIT_CRITICAL();
  
             // Send the sensor status
#ifdef TESTBED
              sensor_state = (local_PIR_state & B00000001);
              local_PIR_state = local_PIR_state >> 1;
              if (sensor_state == 0) {
                 Serial.println("  - Sensor is OFF - ");
                 digitalWrite(pinLED, LOW);
              }
              else {
                 Serial.println("  - Sensor is ON  - ");
                 digitalWrite(pinLED, HIGH);
              }
#endif
             //Serial.println(PIR_state, HEX);
           }  
           else
           {
             Serial.println("*****UNBLOCKED*****");
           }      
       }
   }
  

  //****************************************
  // Name: PRI0_interrupt_CHANGE()
  // Description:
  //  ISR to handle changes in sensor reading.
  // Creation Date:  2009/12/13
  // History:
  //    2009/12/13 - Created.
  //****************************************
  void PIR0_interrupt_CHANGE()
  {
    
   static signed portBASE_TYPE xHigherPriorityTaskWoken;
  
    // Here we handle the PIR sensor notification on falling
    xHigherPriorityTaskWoken = pdFALSE;
  
    PIR_state = PIR_state ^ PIR_SENSOR_0;

    xSemaphoreGiveFromISR(sem_PIR, &xHigherPriorityTaskWoken);
  }
  
  
 //****************************************
  // Name: setup()
  // Description:
  //  standard setup function.
  // Creation Date:  2009/12/13
  // History:
  //    2009/12/13 - Created.
  //****************************************
  void setup()
  {
    pinMode(pinLED, OUTPUT);
    digitalWrite(pinLED, LOW);
    
    // Attach the interrupts for the first PIR sensor.  We want to know when it's on and off.
    attachInterrupt(0, PIR0_interrupt_CHANGE, CHANGE);

    createTaskLoop(PIR_sensor_task, NORMAL_PRIORITY);
    
    // Set up serial connection with our MCP
    Serial.begin(19200);
 
  }
  
  
 //****************************************
  // Name: loop()
  // Description:
  //  standard loop function.
  // Creation Date:  2009/12/13
  // History:
  //    2009/12/13 - Created.
  //****************************************
  void loop()
  {
      Serial.println("Looping...");
      delay(1000);
    
  }

I was able to get it working as is but notice the println() in the PRI_sensor_task() has been commented out.  Uncommenting that will crash the uC.
Logged

Argentina
Offline Offline
Full Member
***
Karma: 0
Posts: 186
Sé feliz
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Mark, I think the problem is not with the printl(), we tested the following code, and it worked ok (with a Seeeduino Mega):

Code:
#define pinLED  13

 volatile byte PIR_state = 0;

taskLoop(PIR_sensor_task)
{
      Serial.println("Task...");
      Serial.println(PIR_state+1, HEX);
      delay(1000);
}
  
  
void setup()
  {
    pinMode(pinLED, OUTPUT);
    digitalWrite(pinLED, LOW);
    
    // Attach the interrupts for the first PIR sensor.  We want to know when it's on and off.
    //attachInterrupt(0, PIR0_interrupt_CHANGE, CHANGE);

    createTaskLoop(PIR_sensor_task,LOW_PRIORITY);
    
    // Set up serial connection with our MCP
    Serial.begin(19200);

  }
  
  void loop()
  {
      Serial.println("Looping...");
      Serial.println(PIR_state, HEX);
      delay(1000);
  }

Does your sample hang the mcu at the begining, or it works for a while and then hangs?

Regards,
Julián


Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 20
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Julian,

Try this.  I think it has something to do with the semaphore or maybe the digitalWrite()?!?

Looks like the main loop() never gets back from the println() as the LED never goes to LOW.

Code:
#include <DuinOS.h>
#include <DuinOS/queue.h>
#include <DuinOS/semphr.h>


#define pinLED  13

 volatile byte PIR_state = 0;
 xSemaphoreHandle sem_pintest = NULL;
 
taskLoop(PIR_sensor_task)
{
      vSemaphoreCreateBinary(sem_pintest);

      if( sem_pintest == NULL )
      {
         Serial1.println("Semaphonre not created in task()");
      }
      
      for(;;) {
        if (xSemaphoreTake(sem_pintest, portMAX_DELAY) == pdTRUE) {
      
          Serial.println("Task...");
          Serial.println(PIR_state+1, HEX);
          digitalWrite(pinLED, HIGH);
          delay(1000);
        }
      }
}
  
  
void setup()
  {
    pinMode(pinLED, OUTPUT);
    digitalWrite(pinLED, LOW);
    
    // Attach the interrupts for the first PIR sensor.  We want to know when it's on and off.
    //attachInterrupt(0, PIR0_interrupt_CHANGE, CHANGE);

    createTaskLoop(PIR_sensor_task,LOW_PRIORITY);
    
    // Set up serial connection with our MCP
    Serial.begin(19200);

  }
  
  void loop()
  {
      Serial.println("Looping...");
      Serial.println(PIR_state, HEX);
      delay(1000);
      digitalWrite(pinLED, LOW);
      xSemaphoreGive(sem_pintest);

  }
 

« Last Edit: December 31, 2009, 12:26:05 am by mcgski » Logged

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

One thing you should be aware of which may or may not be the problem here is that the macro that replaces delay in DuinOS assumes the task scheduler has already been started and the task scheduler is not started until after setup is called.  If any code calls the delay routine in Setup your app will freeze.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 20
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks Chumbud.  Didn't know that but it doesn't seem to be the problem.  My setup() is clean.  The only delays are in the task and in loop().
Logged

Argentina
Offline Offline
Full Member
***
Karma: 0
Posts: 186
Sé feliz
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Mark:

Although I found the bug, I would like to make first a few comments (anyway, in the point "6" below is the description of the bug and it's solution).

I see the following potential problems in your code:

1. We do not recommend to call vSemaphoreCreateBinary(sem_pintest) from a taskLoop. It has to be done only once, so it will be better to call it in another place, like setup().

2. Why use a for( ; ; ) inside a taskLoop? The taskLoops are already inside a for( ; ; ) cycle. So, a new cycle inside the taskLoop will not be the best practice, although it's possible, of course.
I know this (as the whole project yet) is underdocumented, so I added a small section explaining the differences between FreeRTOS' tasks and DuinOS' tasks in the Tracker (still in the Tracker):

http://www.multiplo.org/duinos/wiki/index.php?title=Tracker

Please feel free to contribute!

I think in the v0.2 will add standard tasks (without the internal loop), comments are welcome. I'm studying if they will be better in some situations, and more efficient (due to the use of the stack).

3. The xSemaphoreGive(sem_pintest) has to be called when your task frees the shared resource, so I do not understand why it's called from the loop() task, and not inside the if (xSemaphoreTake(sem_pintest, portMAX_DELAY) == pdTRUE). Please see the original FreeRTOS example, and the code below (point 6): http://www.freertos.org/a00122.html

4. For the code below, please note that I used mutexes, cause think they are better for protecting resources (this is in general a case of mutual exclusion). If you want to use them, you will need to edit the FreeRTOS.h file, and define as "1" the configUSE_MUTEXES:

Code:
#ifndef configUSE_MUTEXES
      #define configUSE_MUTEXES 1
#endif

This will be the default in the DuinOS v0.2.

5. This is not a problem, just a comment: If you only use binary semaphores, you do not need to include DuinOS/queue.h. But you will need it if using mutexes.

6. The bug: The problem is that the println function uses too much LOCAL RAM when called from tasks. In DuinOS v0.1 the user can not redefine the RAM that the task STACK will alloc. So each new tasks, allocates only 85 bytes. I edited the main.cxx from the core, and modified the following line:

Code:
xTaskCreate(main_Task, (signed portCHAR *) "main", configMINIMAL_STACK_SIZE, NULL, mainLoopPriority, NULL);

changing it for this one:

Code:
xTaskCreate(main_Task, (signed portCHAR *) "main", 200, NULL, mainLoopPriority, NULL);

Fortunatly, DuinOS v0.2 will let the user to define the stack size for every task.

And finally, here you have a working sample, using mutexes, and I think a bit more clear, but please remember to make the modifications mentioned above (loop()'s stack size and MUTEXes inclussion):

#include <DuinOS.h>
#include <DuinOS/queue.h>
#include <DuinOS/semphr.h>

#define pinLED  13
volatile byte PIR_state = 0;
xSemaphoreHandle mutex_pintest = NULL;


taskLoop(PIR_sensor_task)
{
  if(mutex_pintest != NULL)
  {
    if (xSemaphoreTake(mutex_pintest, portMAX_DELAY) == pdTRUE)
    {      
      Serial.println("Task...");
      Serial.println(0x1234, HEX);
      xSemaphoreGive(mutex_pintest);
    }
    digitalWrite(pinLED, HIGH);  
    delay(200);  
  }
}


void setup()
{
  pinMode(pinLED, OUTPUT);
  digitalWrite(pinLED, LOW);
  
  mutex_pintest = xSemaphoreCreateMutex();
  if(mutex_pintest == NULL)
  {
    Serial.println("Mutex not created.");
  }
  createTaskLoop(PIR_sensor_task, LOW_PRIORITY);
  
  Serial.begin(19200);
  Serial.println("Hola!");
}

void loop()
{
  Serial.println("Looping...");
  Serial.println(PIR_state, HEX);
  Serial.println(0x888, HEX);
  delay(500);
  digitalWrite(pinLED, LOW);
}

Regards (and happy 2010!)
Julián
http://robotgroup.com.ar
Logged

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

This is why I think it is a bad idea to be hiding code in macros like the task loop.  FreeRTOS is an advanced OS and will not/should not be used by beginning Arduino programmers.  Once you are past the newbie stage and realize the limitations of the current Arduino environment you may want to start using DuinOS instead.  In this case I think it is far better to stick with the function names that are already defined in FreeRTOS and not rename/simplify them in most cases.  There is already documentation and examples that use FreeRTOS on a dozen+ different  micro controllers but  introducing new routine names simply complicates matters when looking at other examples.    I have even changed my main.cxx to be completely blank and added it to my "sketch" myself since otherwise you are again hiding code that is critical to understanding easily what is going on in your application.
« Last Edit: December 31, 2009, 03:18:46 pm by chumbud » Logged

Argentina
Offline Offline
Full Member
***
Karma: 0
Posts: 186
Sé feliz
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks Chumbud!

DuinOS is just an experiment, and we thought it was better to make it more Arduino-like, without prefixes in the sintax, etc.. But don't know exactly how will the users finally use it, because, of course, it's always possible to use directly the FreeRTOS functions.

By now, we will improve a bit the basic current macros, and, for all the advanced functions, will continue using the original ones. This is just a way of doing things, but not the only. I'm not quite sure now if it's better or not, I'm just happy with the names instead handlers, and little things like that, but that's all.

I think the main benefit from this project is that the FreeRTOS kernel is working with the Arduino hard and software, although yes, it still has some problems with libraries. Another good point is that it's an active RTOS project (there are a lot of AVR-RTOS "dead" out there).

I hope we soon will release the v0.2, which will include some inprovements suggested by you (like the taskLoopType macro) and a more flexible Arduino-like API. But advanced users will probably use the FreeRTOS kernel without the DuinOS layer, as you correctly say.

Anyway, comments are very welcome!
 
Regards,
Julián

Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 20
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Julian,

Thanks for the detailed code review.  smiley 

I think I mentioned before that I'm an old time (mid-80's) RTOS programmer who's used to preemptive multitasking and interrupts in the code I wrote.  That being said, I'm just getting my feet wet again after about 15 years being away from it and I appreciate what you're doing to ease the transition.  As Chumbud points out, it will help those who need their Arduino projects to "do more" once they're ready.

I did have a few questions/comments:

I.  My original code had the semaphore being posted from an ISR.  The FreeRTOS API reference recommends against a mutex in an ISR.  Will this still work?  I think the benefit of an RTOS is combining interrupts with tasks to optimize the CPU.

II. Your details about differences are important.  I used the FreeRTOS as a guide and found the creation of the semaphore in the task.  Since it's in a loop then your right that trying to create it a second time probably whacked out the queuing and ultimately the context switch.  Might be worth browsing through the API on FreeRTOS and noting any other differences.

III. As mentioned, my original code had an ISR and the protection of the resource was the intent.  In the example posted, I was more using the semaphore as a task synchronization mechanism.   Is this valid between two tasks?  I got the impression in the FreeRTOS docs that it was.

Thanks for the new sample code.  I'll load it up and give it a try.  I will also try it with an ISR to see how well that works.  I'll let you know what I find.  Looking forward to 0.2!!
« Last Edit: December 31, 2009, 10:29:38 pm by mcgski » Logged

Argentina
Offline Offline
Full Member
***
Karma: 0
Posts: 186
Sé feliz
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Mark, thanks for your comments.

Regarding your question about the mutex, I think it will be better to use the binary semaphore from an ISR (using the xSemaphoreGiveFromISR API function if you give it back in the ISR). And I think too that the binary sem will be ok for the sincronization, as you said.

All this matter, combined with the Chumbud comments are pushing me to rethink if the taskLoops are really usefull.

Again, comments are welcome, and we are really needing feedback, about the v0.1 experiment and the FreeRTOS original sintax for Arduino users. Thanks in advance!

Regards,
Julián
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 25
Robots love Arduinos
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
I based the code below on "MoreComplexBlinking" from the examples directory.

When I run the code I see nothing but the serial print in setup().
The two tasks don't seem to do anything as far as printing.

I realize this is an alpha and the code is not finished, but why the
main_Task. As I understand it taskLoop more or less invokes
xTaskCreate which creates a task marked as ready and places
it in the ready queue. Then, when vTaskStartScheduler is
called, they should go merrily on their way if I understand
things. So the two taskLoops should run without main_Task
and the call to loop() unless there is something you wanted to
do in loop().

Perhaps you haven't gotten to all the Arduino code
to verify that it is thread safe. I haven't read all
the posts, so this may already been discussed..

At any rate, I'm very excited about the work
you have done and plan to use it.

If there is anything I can do to help, please
let me know. I'm familiar with operating system internals, but
will do just about anything including documentation.

BTW, I'm running itt on an Arduino Duemilanove and maybe
move up to an AT91SAM7X.
 
/*
 A simple demonstration of multi-tasking

 */
 
#define mainCHECK_PERIOD ( ( portTickType ) 1000 / portTICK_RATE_MS  )

#include "WProgram.h"
void setup();
taskLoop(loopOne)
{
  
  Serial.println( "taskLoop(loopOne)" );
  vTaskDelay( mainCHECK_PERIOD );

  for (;;) {
    Serial.println( "In Task One" );
    vTaskDelay( mainCHECK_PERIOD );
  }

}

taskLoop(loopTwo)
{
  
  Serial.println( "taskLoop(loopTwo)" );
  vTaskDelay( mainCHECK_PERIOD );

  for (;;) {
    Serial.println( "In Task Two" );
    vTaskDelay( mainCHECK_PERIOD );
  }

}

// The setup() method runs once, when the sketch starts

void setup()  
{

  Serial.begin( 9600 );
  Serial.println( "setup()" );
  
}

void loop() {

}

unsigned portBASE_TYPE mainLoopPriority;

void main_Task(void *pvParameters)
{
      for(;;)
            loop();
}


int main(void)
{
      //This is made prior to setup(), so this priority could be changed in setup():
      mainLoopPriority = LOW_PRIORITY;

      init();
      setup();

      vTaskStartScheduler();

      //Will not get here unless a task calls vTaskEndScheduler():
      for (;;);

      return 0;
}

Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 25
Robots love Arduinos
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I would suggest that DuinOS be moved to Google Code or Kenai at, http://kenai.com/projects/help/pages/KenaiOverview. They offer code hosting and issue tracking among other things like a web page. The only reason I mention these two is that I think they are easier to use than SourceForge and some of the other older open source hosting systems.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 20
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Julian, any ETA on v 0.02?
Logged

Argentina
Offline Offline
Full Member
***
Karma: 0
Posts: 186
Sé feliz
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi! I was not at Buenos Aires, so I will answer now to the posts:

1. Stonechild:

- First, thanks for the post in the wiki (the References section)! Making the documentation grow is one of the most important things we are needing.

- About the suggestion on moving the project to Google Code, Kenai, SourceForge, or the like, I'm afraid it will not be possible (at least by now). RobotGroup is making a big effort not only actively supporting the project DuinOS, but developing a bigger hardware/software project about which I hope will have notices in march. So, it has to be hosted it on one of the RobotGroup's sites (multiplo.org, for example, RGLabs, or any other). By the moment, the wiki is all we can do, but it's a very open way of doing things, so I think it's not so bad. smiley Anyway, our sites will be improved too in the next few months, so it will be possible to make a better source code site.

- Regarding your questions about the main task, I do not understand well. Please tell me more about your thoughts in this matter.

- And, about what can be done, there is a very important thing to do (I think it has a very hight priority): Test the Arduino libraries to see if they are thread safe. Then, adding the testing results to the wiki's tracker will help us and the community. This may include the user made libraries, etc.. As I said before, all the rest of the documentation (the wiki) is important too. Many thanks for your help!

2. Mark G:

We are in a dilemma here: The v0.2 was to be a little improved v0.1, specially with the suggestions and bugs reported in this forum plus a C++ compatiblity library to add new/delete/virtual/pure virtual and may be templates to the Arduino environment.  Now, because of the feedback, we are thinking in go back with things such as taskLoops.

We could release something next week (or the other), with the improvements mentioned above, but it will not include much more than that. I think it's more something like the v0.15.

If the community thinks it will be useful, we could do some effort to put it online very soon.

What do you think?

Finally, I have to say that we will be a little absent by some time (it's summer in Argentina!)

Regards,
Julián
http://robotgroup.com.ar

Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 20
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Julian,

I'd go ahead and post your next release now.  At least it will help those just starting out with it to avoid the issues to date and could provide more testing.

Logged

Pages: 1 ... 5 6 [7] 8 9 ... 13   Go Up
Jump to: