A port of FreeRTOS for PIC18F and XC8.
I needed a 5V solution. There is a port for PIC18F452 (old...) and C18 (old...). So I tried to port for PIC18F and XC8.
I only tested with PIC18F27Q43 but I believe other recent PIC18F MCUs are applicable. Because FreeRTOS code size is a bit large, 64KB or more flash memory is favored.
If I needed a 3.3V solution, maybe I chose STM32L. I am not familiar with AVR MCUs, by the way.
Because the stack pointer consists of two registers FSR1L and FSR1H, calculations of the stack pointer are not atomic. There is a critical section between modifying FSR1L and modifying FSR1H.
The following is an example picked up from .lst file. Beginning of a function which has total 6-byte local variables:
2454 004964 0E06 movlw 6
2455 004966 26E1 addwf fsr1l,f,c
2456 004968 0E00 movlw 0
2457 00496A 22E2 addwfc fsr1h,f,c
FreeRTOS usually saves a task context on top of the stack.
If the preemptive scheduler is used and the tick timer interrupt occurs after
addwf fsr1l,f,c
but before addwfc fsr1h,f,c
, the stack pointer calculation
is incomplete, and then the task context could be saved on the bad location.
This is a very small window but cannot be ignored. So I decided this port is for the cooperative scheduler only.
An extension of this porting aimed at adding preemptive support is present under
the branch preemption
.
The code that contains the critical sections mentioned above is still there, the occurrences can be checked in the .lst file.
By checking the state of the INTCON0.GIEH
bit with the simulator, every time the
critical code gets executed, the interrupts are inhibited, or it's an ISR itself.
This suggests that the XC8 compiler could be aware of the state of the interrupts while dealing with the stack, and so put the critical code only when the interrupts are disabled.
This is still a speculation and needs further analysis.
Multi-tasking is not suitable scope of PIC18F MCUs. Many instructions are needed to maintain context switching.
The portYIELD()
macro performs a context switch, which gets about 650 instruction
cycles.
When Fosc=64MHz, 16 instruction cycles per microsecond, about 40 microseconds
per context switching.
This repository contains a MPLAB X IDE project of LED flashing demo. Open your git cloned folder with MPLAB X IDE.
Demo/ : written by me.
FreeRTOS/ : copied from the FreeRTOS repository.
Demo/ : contains the LED flashing demo: flash.c
Source/ : contains the FreeRTOS kernel.
mcc_generated_files/ : generated by the MCC, MPLAB Code Configurator.
nbproject/ : MPLAB X IDE project folder.
portable/ : written by me.
FreeRTOSConfig.h : copied from the FreeRTOS repository and modified.
main.c : generated by the MCC and modified.
Makefile : generated by the MCC.
PIC18F_XC8_FreeRTOS.mc3 : MCC config file.
- MPLAB X IDE v5.50
- XC8 v2.32
- PIC18F-Q_DFP 1.12.193
- MPLAB Code Configurator 5.0.3
Under the preemption
branch, the demo has been extended with a blocking task
vBlockingTask
which has a lower priority than the vLEDFlashTask
s.
When the scheduler is working in cooperative mode, the blocking task blocks the system and prevents other tasks from running.
When the scheduler is working in preemptive mode, all the tasks get CPU time.
The XC8 tries to optimize aggressively. If the XC8 considers one function is not called any time, the function is not compiled and is ignored. If the XC8 considers one pointer is not used any time, the pointer is regarded as always NULL and is compiled in that way.
Task processing functions are sometimes considered "not called" by the XC8.
To prevent this, I put dummy call of task processing function in pxPortInitialiseStack
.
Also I put dummy call of vPortAdjustCompiler
in xPortStartScheduler
, however
sometimes the XC8 overkills.
You might need to examine .lst and .map files carefully if you face a strange behavior.
Some files are copied from the FreeRTOS repository and its license is applied. Some files are generated by the MPLAB X IDE and the MPLAB Code Configurator and their license is applied. Other files are written by me and in public domain.