~April 13 2001~

Calvin Rev 2.0 coded for Pavel Baranov's C2C++ Compiler
Here is the Link to the new code page
Here is a link to Pavel's C2C++ Compiler page
NOTE : development in Calvin 1.x discontinued...

NEW - Calvin revision works on the new Microchip MPLAB-ICD (16F877) See Calvin-ICD

Calvin is a simple RTOS (real time operating system) for the PIC 16Cxx series microcontrollers (Microchip). The system has a very simple task priority system, and only two types of tasks. The tasks are powerful though and are very flexible.

1.0    Program Structure

    Calvin really resides in the Interrupt Service Routine (ISR). The ISR is just one part of the program however

2.0    ISR (Calvin) - Current Version: 0.2beta

    Follow this link to view the ISR code (assembly).

The ISR code uses DEFINEs to configure IO tasks. This allows the entire core code to be used without having to strip out extraneous code segments which are not needed. As an illustration, the following code segment shows the ADC Task being activated;

;* Set Definitions here. Define all IO Interrupts
;* which will be implemented. Defines recognized are:

;  SAVE_FSR save the current FSR value on the ISR Stack
;  EXT_INT  Ext Int enabled (RB0/INT)
;  PORT_CHG State change INT on pins RB[4..7] enabled
;  PPORT_INT Parallel Port Int
;  ADC_INT  ADC Conversion done Int
;  Rx_INT  USART Rx Int
;  Tx_INT  USART Tx Int
;  SSP_INT  Sync Serial Port Int
;  CCP1_INT CCP1 Int (depends on mode)
;  TMR2_M_INT TMR2=PR2 Match Int
;  TMR1_V_INT TMR1 overflow Int
;  CCP2_INT CCP2 Int (depends on mode)
;  TMR0_INT TMR0 overflow Int. Must be set to use
;    background tasks Task[0..t]

  #DEFINE ADC_INT                ;ADC INT Enabled!

In Calvin, any of the above IO Tasks which are defined as shown here will have their code automatically inserted at compilation. For a description of each type of task, see section 4.1

The ISR treats every task with the same priority. Tasks can be masked out by writing to the T_Mask registers. Any program segment (task or main loop) can alter these masks.Tasks are checked one at a time, starting with the Program Tasks.

There is only one interrupt flag which generates an ISR. This flag is the logical OR of all other flags which are enabled. The ISR must then sequentially check each tasks to see if it generated the interrupt. The last check in the ISR catches any illegal flags. This may happen if an ISR is left out by accident.

2.1    ISR Stack

The ISR incorporates an ISR Task Stack which is configured upon compilation. The stack allows tasks to interrupt each other a total of n times, without losing data in the STATUS, W, and FSR registers. Saving the FSR is an option and can be disabled if no other task(s) use indirect addressing (LUTs etc). The Stack is located at the bottom of Bank0. Care should be taken not to use these registers. If the tasks will never interrupt one another, the ISR Stack could be cut out of the ISR, or set to only take up the bottom two registers (one save for STATUS & W).

Hobbes (the next generation RTK) will extend this feature to adopt semaphores and to control task execution. (ie partial task execution to add full multi-tasking).

3.0    Main Loop Code

The main loop is where you can put code which can run at the maximum CPU speed. It can be treated as the lowest priority 'task' as it will be interrupted by everything else. It can be bumped to highest priority by allowing it to disable global (or other) interrupts. I find that most of my smaller embedded programs have no code at all in this portion of the OS (except a loop and several NOPs) as it is not needed. Another program that I developed has a trigger detect in the loop which, when triggered, runs once through a PID loop. In this case, the PID values would only be calculated when a task had finished obtaining data. The PID code is also pre-emptable by all other tasks, to maintain low INT latency.

4.0    Tasks

    All tasks are set up as 'one-shot'. That is, they do not contain an outer loop (like the main loop). Looping is accomplished by running the task every n Clock Ticks. Each task can be disabled by clearing its Interrupt mask bit. Any other task, or the main loop, is able to do this. There are no access restrictions. Modifying the tasks execution is simple. Below is a short list of the modifications you can do.

    There are two types of tasks, IO and program. IO tasks are executed from a hardware event (such as a counter overflow). Program tasks perform the software operations required by the programmer (such as scanning a keypad). Each task is described in the next two sections.

4.1    IO Task

    The ISR assembly code contains the ISR's for all of the IO events in the PIC 16Cxx chipset. Not all IO is used at once though, and so I have used DEFINEs to determine when ISR code segments should be inserted or not. The IO DEFINEs are listed in section 2.0. This is a brief detail of each ISR.

Currently Supported IO Tasks
EXT_INT External interrupt on pin RB0
PORT_CHG One of the pins has changed state in the group RB[4..7]
PPORT_INT Parallel slave port INT request driven by ->
ADC_INT Analog to Digital Convertor finished
Rx_INT USART receive buffer full
Tx_INT USART transmit buffer empty
SSP_INT SSP Buffer full/empty (Read/Write finished)
CCP1_INT CCP1 System INT (read PIC data sheet for more info)
TMR2_M_INT INT generated when Timer2 matches the PR2 register
TMR1_V_INT Timer 1 overflow INT
CCP2_INT CCP2 System INT (read PIC data sheet for more info)
TMR0_INT Timer 0 overflow INT
Future IO Tasks to be included
SADC_TRIP Slope A/D Converter Trip INT
OVF_INT Slope A/D Timer Overflow INT
EE_INT EEPROM Write Complete
LCD_INT LCD Interrupt
CMP_INT Comparator Input Changed

4.2    Program Task

A program task is a software program which executes once every n Clock_Ticks. Several Tasks can be included (up to the point where ISR stack overflow becomes an issue). For example, a task could be designed to generate a PWM signal to implement a DAC convertor in the controller. I am currently working on a library of Tasks which work with displays, tachometers, servos and PWM signalling. I will add them to this website as time permits.

4.3    Clock_Ticks (Timer 0)

    The Clock_Tick is the heartbeat of Calvin. A tick is generated whenever TMR0 overflows. When the ISR detects this, it will increment all Task_Counters and check them for overflow. If a Task_Counter overflows, then it is executed. The Clock_Tick speed can be set by the Timer 0 prescaler. If a Clock_Tick frequency faster than 256 program cycles is needed, then code can be added to the ISR to pre-set Timer 0 when it overflows, similarly to the pre-sets applied to the Task_Counters. CAUTION: Setting the Clock_Tick low (<256) can cause an ISR Stack overflow, if a number of tasks execute within several Clock_Ticks AND each task is quite lengthy.

Last Updated on Apr 13 2001 by Richard Willis