Microchip PIC32 RTOS port
with a MIPS M4K core
[RTOS Ports]
This page presents the FreeRTOS.org port and demo application for the PIC32, a MIPS based 32bit microcontroller offerings from Microchip.
The port and demo are preconfigured to use:
Thanks to Darren Wenn for his contributions during the development of this port.
IMPORTANT! Notes on using the PIC32 RTOS port
Please read all the following points before using this RTOS port.
- Source Code Organization
- The Demo Application
- Configuration and Usage Details
See also the FAQ My application does not run, what could be wrong?
Source Code Organization
The FreeRTOS download contains the source code for all the FreeRTOS ports so contains many more files that used by this demo.
See the Source Code Organization section for a description of the
downloaded files and information on creating a new project.
The MPLAB demo application workspace for the PIC32 / MIPS port is called RTOSDemo.mcw and can be located in the FreeRTOS/Demo/PIC32MX_MPLAB directory.
The Demo Application
Demo application hardware setup
All the Explorer 16 jumpers can remain in their default positions - in particular, JP2 should be fitted to ensure correct operation of the LEDs.
The demo application includes tasks that send and receive characters over UART2. The characters sent by one task are received by another - with an error condition
being flagged should any characters be missed or received out of order. A loopback connector is required on the Explorer16 9way D socket for this mechanism to
operation (pins 2 and 3 the socket should be connected together). The internal loopback mode of the UART itself is not used by default.
The demo application uses the LEDs built onto the prototyping board so no other hardware setup is required.
Functionality
The demo application creates twenty five tasks (including the idle task), and a high frequency interrupt test. When executing correctly the demo will behave as follows:
- LEDs marked D3 to D5 are under control of the 'flash' tasks. Each will flash at a constant frequency, with LED D3 being
the fastest and LED D5 being the slowest.
- LED D7 will toggle each time a character is transmitted on the serial port. The frequency is such that you cannot distinguish the individual toggles between
characters, although the spacing between complete message transfers is perceptible. The delay between each message is pseudo randomized for test purposes.
- LED D8 will flash each time a character is received and validated on the serial port (though the loopback connector). Again the frequency is too
fast to distinguish the individual toggles.
- Most of the tasks do not update an LED so have no visible indication that they are operating correctly.
Therefore a 'Check' task is created whose job it is to ensure that no errors have been detected in any of the
other tasks. The check task monitors the system operation every 3 seconds then writes a PASS or FAIL message to the LCD. Any failures are
latched, and the message displayed indicates in which task the failure was detected (this functionality can be tested by removing the loopback connector while the demo is executing).
The PASS message is a time measurement in nano seconds, as described in the next bullet point. The check task also toggles LED D10. The toggle frequency
will increase should an error have been detected.
-
A high frequency periodic interrupt is generated
using a free running timer to demonstrate the use of the
configKERNEL_INTERRUPT_PRIORITY configuration constant. The interrupt
service routine measures the number of processor clocks that occur between
each interrupt - and in so doing measures the jitter in the interrupt
timing. The maximum measured jitter time is latched in the ulMaxJitter
variable, and displayed on the LCD by the 'Check' task as just described. The interrupt frequency is set to 20KHz.
This test demonstrates how the kernel can be configured such that is has no impact on the latency of interrupt service routines (other than the time taken
following tick interrupt entry to re-enable interrupts - this would be the same for any interrupt, not just RTOS related interrupts). NOTE: It
is necessary to remove this test to successfully run the demo within the MPLAB simulator. This can be achieved by simply commenting out the call to vSetupTimerTest
within main().
- The application also demonstrates the use of a 'gatekeeper' task, in this case an LCD gatekeeper. The LCD task is the only task that
is permitted to access the LCD directly. Other tasks wishing to write a
message to the LCD send the message on a queue to the LCD task instead of
accessing the LCD themselves. The LCD task just blocks on the queue waiting
for messages - waking and displaying the messages as they arrive.
Building and executing the demo application
These instructions assume you have MPLAB and the MPLAB C32 compiler correctly installed on your host computer.
-
To build the application:
- Open the demo application workspace from within the MPLAB IDE.
- From the 'Project' menu, select 'Build Configuration', then 'Release' if you are going to run the code stand-alone, or 'Debug' if you are going to run the code
via an MPLAB debug session.
- From the 'Project' menu within the IDE, select 'Build Options', then 'Project'. The project options dialogue box will open.
- From the 'Project' menu, select 'Build All'. The project should build with no errors or warnings.
-
To run the application in the MPLAB simulator:
NOTE: The high speed interrupt test cannot be executed within the simulated environment.
To use the MPLAB simulator the call to vSetupTimerTest() must be commented out within main().
Also when executing within the simulator, the standard ComTest receive task will not receive any characters (there is no loopback connector), causing the
'check' task to detect an error within the communications test tasks. This is the expected behaviour.
- From the 'Debugger' menu within the MPLAB IDE, select 'Select Tool', followed by 'MPLAB SIM'.
- From the 'Project' menu, select 'Build Configuration', then 'Debug'. A complete rebuild will be necessary if Debug was not previously the selected configuration.
- Set a break point on the first instruction within the main() function (contained within main.c).
- From the 'Debugger' menu, select 'Run'. The application will start executing, stopping when the break point is reached. The application can then
be manipulated using the standard debugger commands.
-
To debug the application on the Explorer 16 using the ICD2 or Real ICE interface:
- Connect the ICD2 or Real ICE to the Explorer 16 development board as described in the Explorer 16 manual.
- From the 'Project' menu, select 'Build Configuration', then 'Debug'. A complete rebuild will be necessary if Debug was not previously the selected configuration.
- From the 'Debugger' menu within the MPLAB IDE, select 'Select Tool', then 'MPLAB ICD 2' or 'REAL ICE' as appropriate.
- Again from the 'Debugger' menu, select 'Program' to program the demo application and debugger stub into the microcontroller flash memory.
- Set a break point on the first instruction within the main() function (contained within main.c).
- From the 'Debugger' menu, select 'Run'. The application will start executing, stopping when the break point is reached. The application can then
be manipulated using the standard debugger commands.
-
To run the application stand alone from the microcontroller flash memory:
- Connect the ICD 2 or Real ICE to the Explorer 16 development board as described in the Explorer 16 manual.
- From the 'Project' menu, select 'Build Configuration', then 'Release'. A complete rebuild will be necessary if Release was not previously the selected configuration.
- From the 'Programmer' menu within the MPLAB IDE, select 'Select Programmer', then 'MPLAB ICD 2' or 'MPLAB Real ICE' as appropriate.
- Again from the 'Programmer' menu within the MPLAB IDE, select 'Program'. This will program the demo application into the microcontroller flash.
- Remove power from the Explorer 16 board, then remove the ICD 2 or Real ICE interface cable.
- Finally, apply power again to start the application executing.
RTOS Port specific configuration
Configuration items specific to this port are contained in FreeRTOS/Demo/PIC32_MPLAB/FreeRTOSConfig.h. The
constants defined in this file can be edited to suit your application. In particular -
- configTICK_RATE_HZ
This sets the frequency of the RTOS tick. The supplied value of 1000Hz is useful for testing the kernel functionality but is faster
than most applications require. Lowering this value will improve efficiency.
- configKERNEL_INTERRUPT_PRIORITY
This sets the interrupt priority used by the kernel. The kernel should use a low interrupt priority, allowing higher priority interrupts
to be unaffected by the kernel entering critical sections. Instead of critical sections globally disabling interrupts, they only disable
interrupts that are below the kernel interrupt priority.
This permits very flexible interrupt handling:
- At the kernel priority level interrupt handling 'tasks' can be written and prioritised as per any other task in the system.
These are tasks that are woken by an interrupt. The interrupt service routine (ISR) itself should be written to be as short as
it possibly can be - it just grabs the data then wakes the high priority handler task. The ISR then returns directly into the woken
handler task - so interrupt processing is contiguous in time just as if it were all done in the ISR itself. The benefit of this is
that all interrupts remain enabled while the handler task executes.
- ISR's running above the kernel priority are never masked out by the kernel itself, so their responsiveness is not effected by
the kernel functionality. However, such ISR's cannot use the FreeRTOS.org API functions. The fast timer interrupt test within this demo
demonstrates this configuration.
Each port #defines 'portBASE_TYPE' to equal the most efficient data type for that processor. This port defines
portBASE_TYPE to be of type long.
Note that vPortEndScheduler() has not been implemented.
Interrupt service routines
Interrupt service routines that cannot cause a context switch have no special requirements and can be written as per the compiler documentation. The high
frequency timer interrupt within the PIC32 demo can be used as an example, an outline of which is shown in Listing 1.
void __attribute__( (interrupt(ipl2), vector(_TIMER_2_VECTOR))) vT2InterruptHandler( void );
void vT2InterruptHandler( void )
{
/* Perform interrupt processing here. */
/* Clear the timer interrupt. */
IFS0bits.T2IF = 0;
}
Listing 1: An ISR that does not cause a context switch written as per the compiler documentation
Interrupts service routines that can cause a context switch do have have a few special requirements:
- The interrupt entry must be via an assembly wrapper.
- The interrupt handler C function must only use the portEXIT_SWITCHING_ISR() macro to perform the context switch, if one is required.
- The interrupt must be assigned the same priority as the tick interrupt - defined by portKERNEL_INTERRUPT_PRIORITY within FreeRTOSConfig.h.
The UART interrupt within the PIC32 demo can be used as an example - an outline of the assembly code wrapper for which is shown in Listing 2, and an outline
of the corresponding C function handler is shown in Listing 3:
/* Prototype to be included in a C file to ensure the vector is
correctly installed. */
void __attribute__( (interrupt(ipl1), vector(_UART2_VECTOR))) vU2InterruptWrapper( void );
/* Header file in which portSAVE_CONTEXT and portRESTORE_CONTEXT are defined. */
#include "ISR_Support.h"
/* Interrupt entry point. */
vU2InterruptWrapper:
/* Save the current task context. This line MUST be included! */
portSAVE_CONTEXT
/* Call the C function to handle the interrupt. */
jal vU2InterruptHandler
nop
/* Restore the context of the next task to execute. This line
MUST be included! */
portRESTORE_CONTEXT
.end vU2InterruptWrapper
Listing 2: Assembly code wrapper for handling an interrupt that can cause a context switch
Some notes on the assembly file wrapper:
- I have found that the assembly file in which the wrapper is placed must have a .S extension (with a capitol S). Using a lower case .s may
result in the portSAVE_CONTEXT and portRESTORE_CONTEXT macros being incorrectly inlined.
- The portSAVE_CONTEXT and portRESTORE_CONTEXT macros must be used as the very first and very last executable lines in the function respectively.
Second, the C function called by the assembly file wrapper:
void vU2InterruptHandler( void )
{
/* Declared static to minimise stack use. */
static portBASE_TYPE xYieldRequired;
xYieldRequired = pdFALSE;
/* Are any Rx interrupts pending? */
if( mU2RXGetIntFlag() )
{
/* Process Rx data here. */
/* Clear Rx interrupt. */
mU2RXClearIntFlag();
}
/* Are any Tx interrupts pending? */
if( mU2TXGetIntFlag() )
{
/* Process Tx data here. */
/* Clear Tx interrupt. */
mU2TXClearIntFlag();
}
/* If sending or receiving necessitates a context switch, then switch now. */
portEND_SWITCHING_ISR( xYieldRequired );
}
Listing 3: The C portion of an ISR that can cause a context switch
Some notes on the C function:
- portEND_SWITCHING_ISR() must be called as the very last line within the C function.
- The parameter passed to portEND_SWITCHING_ISR() should be zero if no context switch is required, and non zero if a context switch is required.
- The C function does not use any special qualifiers or attributes - it is just a standard C function.
See the full UART interrupt handler within the PIC32 demo application for a complete example - note however that, as downloaded, the UART driver is intended to generate
lots of interrupts (with the intention of testing the robustness of the MIPS port) and should therefore not be regarded as an optimal solution.
Critical sections
Exiting a critical section will always set the interrupt priority such that all interrupts are enabled, no matter what its level when the critical section
was entered. FreeRTOS.org API functions themselves will use critical sections.
Execution context
Inline with the conventions documented in the C32 manual, the RTOS kernel assumes all access to the K0 and K1 registers will be atomic. Code generated
by the C32 compiler conforms to this convention so if you are writing application purely in C then this is of no concern.
Care must be taken however if any hand written assembly code is used to ensure that this too conforms to the same convention.
Shadow registers
The interrupt shadow registers are not used and are therefore available for use by the host application. Shadow registers should not be used within an interrupt
service routine that causes a context switch.
Switching between the pre-emptive and co-operative real time kernels
Set the definition configUSE_PREEMPTION within FreeRTOS/Demo/PIC32_MPLAB/FreeRTOSConfig.h to 1 to use pre-emption or 0
to use co-operative. The demo application will only execute correctly with configUSE_PREEMPTION set to 0 if configIDLE_SHOULD_YIELD is set to 1.
Compiler options
As with all the ports, it is essential that the correct compiler options are used. The best way to ensure this is to base your
application on the provided demo application files.
Memory allocation
Source/Portable/MemMang/heap_2.c is included in the PIC32 demo application project to provide the memory
allocation required by the real time kernel.
Please refer to the Memory Management section of the API documentation for
full information.
Serial port driver
It should also be noted that the serial drivers are written to test some of the real time kernel features - and they are not
intended to represent an optimized solution. In particular, they make heavy use of the queue mechanism and do not use the available FIFO or DMA.
Copyright (C) 2003 - 2008 Richard Barry
Any and all data, files, source code, html content and documentation included in the FreeRTOS distribution or available on this site are the exclusive property of Richard Barry.
See the files license.txt (included in the distribution) and this
copyright notice for more information. FreeRTOS
TM and FreeRTOS.org
TM are trade marks of Richard Barry.