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. The clock application shown here and the BRTOS together consume less than 8% of the resource of a PIC 18F 252 running at 20MHz.
Below is a block diagram of the hardware to which the BRTOS is targeted
For more information see detailed circuit diagram which can also be downloaded in the Downloads section.
At its heart is the 18F252 PIC. 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 an IP interface via a Lantronix XPort (or directly via an RS232 interface - not shown).
A PC can be connected to the LAN for Program loading and for monitoring the active tasks in BRTOS. Currently the PDS boot-loader only supports the RS232 connection so I have developed both a boot-loader program and a Task Monitor program using Visual Basic ™ which can connect to the PIC via serial or LAN interfaces.
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 the current implementation tasks 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. Finally a 20mSec interrupt generated from Timer 1 is used 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 (See TaskMan_P+.inc in Downloads - BRTOS Core)
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. Constants are used to set up the maximum number of tasks to run at each frequency and the maximum array size is calculated from that. These can be adjusted to suit the application.
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 for archiving data off to a remote site or other housekeeping tasks.
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.
Note that the use of square brackets when calling macros is optional and is used here to retain consistency with Partons native syntax.
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 interrupts.
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 (See I_Schedule_P+.inc in Downloads - BRTOS Core)
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.
(Limitations - 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.
BRTOS has used a couple of techniques which may not be familiar.
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 under Interrupts. This enables various configurations of code to be run.
BRTOS Task Monitoring
One configuration option is TASK_DEBUG. This adds additional code which can report back via the serial port 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+).
Building Messages for Display and for Serial Transmission
There are plenty of times when it is necessary to send a text string either to a display or to a serial port. You could take the obvious approach of using the string building facilities built into the Proton HSerOut and Print commands but using these commands restricts you to the specific peripheral. In BRTOS messages are built in a common Message Array (MsgArry) which can be sent to any peripheral supported by the hardware without having to rebuild the message each time. Some of you may have noted that this technique was used in the TASK_DEBUG code, it is also used in the Clock module.