4

I'm using FreeRTOS port for PIC32 microcontroller on the PIC32MX starter kit. Was just playing with tasks but the tasks aren't context switching. Here are my main config settings:

#define configMAX_PRIORITIES    ( ( unsigned portBASE_TYPE ) 5 )
#define configKERNEL_INTERRUPT_PRIORITY         0x01
#define configMAX_SYSCALL_INTERRUPT_PRIORITY    0x03
#define configTICK_RATE_HZ              ( ( portTickType ) 100 )

Now I have two tasks defined which blink two LEDs. Both have priority of 4(highest). Under normal operation the LEDs should alternatively blink for each 100 ticks. But this doesn't happen. The second LED blinks for 100 ticks and the control goes to the general exception handler. Why does this happen? Seems like there is no scheduling at all.

Clifford
  • 88,407
  • 13
  • 85
  • 165
Laz
  • 6,036
  • 10
  • 41
  • 54
  • 2
    Without seeing your task code, who can tell? What does your debugger tell you? When you hit the exception handler, check the call stack to see how it got there, and from what task (or the scheduler). – Clifford Feb 28 '11 at 19:42
  • Are you using a port/demo intended for your board? Did it ever work? – XTL Nov 18 '11 at 11:20

5 Answers5

6

FreeRTOS is a priority based pre-emptive scheduler, tasks of equal priority that do not yield processor time will be round-robin scheduled. Relying on round-robin scheduling is seldom suitable for real-time tasks, and depending on the configured time slice, that may mess up your timing. Time-slicing may even be disabled.

Your tasks must enter the Blocked state waiting on some event (such as elapsed time) to allow each other to run as intended.

That said, entering the exception handler rather than simply one task starving another or not running with the intended timing is a different matter. For that you will need to post additional information, though your first approach should be to deploy your debugger.

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • I would be ex-tre-mely keen to know why you believe round-robin is selsdom suitable for real time tasks! IMO it is one of the most suited scheduling policies thanks to the fact that it is very deterministic, i.e. all tasks have a predetermined time slice, and thus allows for proper analysis regarding time variances. It also allows for a much better understanding of the worst case execution times. I simply don't get why anybody would ever argue that round-robin is seldom suited for real-time tasks. On the contrary, it is IMHO mostly the best solution – LandonZeKepitelOfGreytBritn Aug 01 '22 at 08:47
  • @LandonZeKepitelOfGreytBritn In brief it is about deterministc response to asynchronous external events. With a set of round-robin tasks, an event will occur, and will be processed the next time it's task gets scheduled, which in relation to the asynchronous event is non-deterministic. The handling time will be widely variable, and the max will change as new tasks are added. RR is clearly useless for real-time deadlines in the order of microseconds. RR is a simple solution for tasks without hard real-time deadlines - which may often be most or all tasks - just not the real-time ones. – Clifford Aug 01 '22 at 16:01
5

The absolute first thing to check is your "tick" interrupt. Often interrupts are not enabled, timers aren't set up right, clocks are not configured properly in the #pragma's that set up the PIC32.. and all those issues manifest themselves first in a lack of a "tick".

This is the #1 cause of not task switching: if you're not getting the tick interrupt. That's where the normal pre-emptive task switching happens.

Assuming you're using the "off the shelf demo", in MPLAB, set a breakpoint in the void vPortIncrementTick( void ) function (around line 177 in FreeRTOS\Source\portable\MPLAB\PIC32MX\port.c) and run your code. If it breakpoints in there, your timer tick is working.

bstpierre
  • 30,042
  • 15
  • 70
  • 103
4

Do you have a round-robin scheduler? Are your tasks sleeping for any length of time, or just yielding (or busy waiting)?

A very common gotcha in embedded OSs is that the scheduler will frequently not attempt to schedule multiple processes of the same priority fairly. That is, once A yields, if A is runnable A may get scheduled again immediately even if B has not had any CPU for ages. This is very counterintuitive if you're used to desktop OSs which go to a lot of effort to do fair scheduling (or at least, it was to me).

If you're running into this, you'll want to make sure that your tasks look like this:

for (;;)
{
  led(on); sleep(delay);
  led(off); sleep(delay);
}

...to ensure that the task actually stops being runnable between blinks. It won't work if it looks like this:

for (;;)
{
  led(on);
  led(off);
}

(Also, as a general rule, you want to use normal priority rather than high priority unless you know you'll really need it --- if you starve a system task the system can behave oddly or crash.)

Morten Jensen
  • 5,818
  • 3
  • 43
  • 55
David Given
  • 13,277
  • 9
  • 76
  • 123
4

Are you sure both tasks are well registered and the scheduler has been launched?

Something like the following code would do the job:

xTaskCreate( yourFirstTask, "firstTask", STACK_SIZE, NULL, TASK_PRIORITY, NULL );
xTaskCreate( yourSecondTask, "secondTask", STACK_SIZE, NULL, TASK_PRIORITY, NULL );
vTaskStartScheduler();

You can also add an application tick hook to see if the tick interruption occurs correctly or if there is a problem with the tick timer.

greydet
  • 5,509
  • 3
  • 31
  • 51
  • 1
    Be sure to check the return values of `xTaskCreate` to make sure the task creation did not fail for lack of sufficient memory for the task's stack space. – semaj Feb 28 '11 at 16:47
4

There are standard demo tasks that just blink LEDs in the FreeRTOS/Demo/Common/Minimal/flash.c source file. The tasks created in that file are included in the standard PIC32 demo application (which targets the Microchip Explorer16 board).

In its very simplest form, a task that just toggles and LED every 500ms would look like this:

/* Standard task prototype, the parameter is not used in this case. */    
void vADummyTask( void *pvParameters )
{
const portTickType xDelayTime = 500 / portTICK_RATE_MS;

    for( ;; )
    {
        ToggleLED();
        vTaskDelay( xDelayTime );
    }        
}
Richard
  • 256
  • 1
  • 4