Proton BASIC Compiler - BRTOS: A Real Time Operating System for the PIC – Part 1

  • Pic® Basic

  • BRTOS: A Real Time Operating System for the PIC®– Part 1

    By John Barrat

    This is the first of a series of articles describing a basic real time operating system (let’s call it BRTOS) for PIC® microprocessors. It is the result of two projects with which I have been involved and which had similarities in that they were required to monitor slow moving parameters and to provide a simple 4 key and 2 line LCD user interface. Both projects also had to communicate with a PC.


    I always like to build software which is reusable where practical so I set about defining a set of fundamental functions on which I could build my applications. The original build used PBPro but was switched halfway through to Proton when PDS was released which caused some rework to really exploit the power of Proton. The result is a program which uses macros, assembler, interrupts, serial communications and I2C.
    The application described is for a simple clock/calendar display which can be set up from the keypad or remotely via a serial interface. This is used to show how many of the underlying functions in the BRTOS are used. I will also be describing a Visual Basic™ program which can monitor the activities of the BRTOS in operation.
    I hope you find the project interesting and are able to make use of some of the concepts and/or can employ extracts from the code.
    Many thanks go to Tim Box for his help and support during this project and in particular in the transition to Proton and for introducing me to the great new features in the compiler.

    What is BRTOS?

    At the centre of BRTOS are a Task Manager and Scheduler which are driven from a real time clock. Around this are a Clock, a Menu and Communications Modules. The code is provided in a number of “Include” files. Where possible these files have been written to be independent of each other although there is an inevitable inter-dependence on some functions.
    The application is controlled from a Main module using calls to functions in the include files plus additional logic to reflect the application. The theory is by using some of these include files you can quickly assemble a fresh application without having to re-invent the same wheel each time.
    I originally started out using a 16F PIC® and the original version did in fact run on this processor, however, as the code grew and the need for deeper nesting increased I switched to the PIC® 18F range.
    It should be pointed out that this approach is unlikely to be suitable for very time critical and high speed applications such as an IC engine management or model aircraft autopilots as the overhead would be too heavy. My clock application and the BRTOS consume less than 14% of the resource of a PIC® 18F 252 running at 20MHz.

    The Hardware Base

    Below is a diagram of the hardware to which the BRTOS is targeted.

    View a larger picture Here
    I have removed the application specific parts and left the basic components with which BRTOS interacts.
    At its heart is the 18F252 PIC®C Port A drives a 2 line LCD in standard 4 bit mode. Port B interfaces to a 4 key keypad and the square wave input from a DS1307 RTC chip. Port C connects to the IC2 bus on the DS1307 using the MSSP hardware in the PIC®. Finally, the USART connects to either an IP interface via a Lantronix XPort or directly via an RS232 interface. A single jumper is used to select between IP and RS232.
    Currently the PDS boot-loader only supports the RS232 connection although my Visual Basic ™ application mentioned earlier will connect to either.

    BRTOS structure

    The diagram below shows the basic structure of BRTOS.

    The scheduler is driven by the 500mSec ticks from the Real Time Clock which triggers the Task Manager at defined time intervals. In my system I have tasks which run at various repetition rate from 2Hz through to once per day. Other random functions are handled by interrupts, e.g. Serial I/O and keypad inputs. I also use a 20mSec interrupt for managing keyboard de-bounce and key repeat.
    When the scheduler is idling the idle time is free to be used by background processes which are not time dependent and can (and will) be interrupted by the scheduler and the interrupt control logic as and when necessary.

    The Task Manager

    The key to BRTOS is the concept of tasks and the task manager. The task manager holds a single array of addresses of tasks which have to be run. A task is a standard sub-routine which has to be run at a specific frequency. E.g. read Time and Date, measure a temperature, sample a chemical reading etc…
    The Task Array is divided into segments containing addresses of tasks which will be activated at specific frequencies. I use constants to set up the maximum number of tasks I wish to run at each frequency and the maximum array size is calculated from that.
    As coded, the task manager, in conjunction with the scheduler, supports the following task rates: 500mSec, 1 per Second, 1 per minute, 1 per hour and 1 per day. This division of time works well for a data logging system where parameters change relatively slowly. The change of day task slot is useful if you are archiving data off to a remote site or have other housekeeping to do.
    Every task will be called by the task manager and must finish in a Return instruction which will return it to the task manager. At the appropriate time, the scheduler will call the task manager with a parameter defining the current rate. The task manager will point to the set of task addresses in the Task list for the defined rate and execute a call to each valid address in the list for that rate. An address is considered valid when the Address value is greater than zero. At the first encounter of a zero address the task manager will complete.
    The task manager extends the Proton language with the following macro commands:
    ADDTask TaskLabel, TaskRate – This adds the address of a specific task to the task list. If the list is full it returns with a List Full bit set. If the task is already there it returns with Already there bit set. (Only one entry of a task is permitted in any task rate).
    REMTask TaskLabel, TaskRate – this removes a task from the list. If the task does not exist in the list AlreadyThere returns false.
    POSTask FirstTaskLabel, SecondTaskLabel, TaskRate – this will check whether FirstTaskLabel appears before the SecondTaskLabel in the task list and swap their respective positions if necessary.
    REPTask ExistingTask, NewTask , TaskRate – this simply swaps an existing task in the list with another task. If the existing task cannot be found the AlreadyThere flag returns false and the task is not swapped out.
    In the case of the commands listed above, once a task has been added to the task list that task will get repeatedly called at the task rate allocated until it is removed from the list.
    However, and this is crucial, there is one additional stack of addresses held in the task list. These are called the immediate tasks and are tasks which must be carried out on a once off basis and as a priority higher than the repeating tasks. This mechanism is there primarily to support actions in response to an interrupt.
    A quick diversion on interrupts - As Tim Box will confirm, when using hardware interrupts it is essential to minimise the time spent responding to the Interrupt. As a general guide, an Interrupt Service Routine should simply note the fact that an event has taken place, move any data associated with the event into or out of storage and return. If there is processing to be done as a result of the event this should be carried out outside the interrupt subroutine.
    Back to the Task Manager – there is a final command called ADDImmd TaskLabel.
    ADDImmd TaskLabel – this command adds a Task address to the immediate part of the task array. This part of the array is handled as a first in first out (FIFO) buffer. Tasks in this part of the task list take precedence over any other tasks in the task list. The task manager will only ever execute a task in the immediate section once, and it will have to be added again to be executed again.
    To ensure the same task is not added to the task list before the first one has been executed, each task address is cleared to 0 after execution. ADDImmd checks that the address at the head of the FIFO is not the same as the address being added.

    The Scheduler

    The scheduler provides the timing triggers to the Task Manager. When there is no other activity the program idles in the scheduler.
    There are 3 parts to the scheduler: A Scheduler Interrupt handler, a Timed Events Task and the looping Scheduler itself. During idle, the scheduler simply loops back on itself looking for any high priority (immediate) tasks to perform. Any low priority background tasks which will not be corrupted by interruption can be added in this loop. (See note below for limitations)
    The Scheduler Interrupt handler is triggered by each edge of the 1Sec square wave from the DS1307 RTC chip. This clock appears on the INT0 input pin. At each edge of the clock, the Interrupt handler issues an ADDImmd command adding the “Timed Events” task.
    On returning to the scheduler the scheduler will run the Immediate Tasks. This will run the Timed Events task just added. This task compares the state of the Real time clock and calls the task handler with the relevant task rate.
    (Note - these events, whilst being high priority, are being run outside the interrupt handler. In order to ensure timely execution of these tasks, it is important that any background code running makes regular calls to the Immediate_Tasks routine. If there are no tasks to execute the program will be returned immediately to the background code.)
    Other functions can ADDImmd tasks to the task list; this is used by the interrupt handlers when there is a requirement for response to an interrupt. E.g. When serial data has been received and needs to be processed.

    Debug and Configuration

    You will note that there are quite a few #IFDEF ,#IFNDEF etc. statements throughout the code. These are Assembler directives and which you can use to control various build options during assembly. These commands are discussed further in Part 4. This enables various configurations of code to be run. One configuration option is TASK_DEBUG. This adds additional code which can report back via the serial port (using IP or RS232) which tasks are in the task list, when they are active and the processor loading. (See ReportTasks, SendActTask in TaskMan_P+ and SendIdleChar in the I_Schedule_P+).
    A Visual Basic ™ program will be described in a later article which presents this data on a PC.

    Attached Code The include file for the Scheduler The include file for the Task Handler.

    Further Reading

    The Next Article will describe the clock module.

    About the Proton Compiler

    Crownhill's Proton Plus Compiler is a part of the Proton Development Suite - A suite of British-developed applications enabling fast development of PIC® micro's using the PIC® BASIC Language.
    For more information on the Proton Development Suite, please visit