The Calvin C
RTOS Page


 



~April 13 2001~

CALVIN CODE RELEASED IN THE C LANGUAGE!
Calvin Rev 2.0 coded for Pavel Baranov's C2C++ Compiler
Here is a link to Pavel's C2C++ Compiler page
NOTE : development in Calvin 1.x discontinued...
 



April 13 2001

Greetings all!

Thanks for all the emails. I know that you have been waiting for awhile for some new stuff to occur here, well here it is! I have released Rel.2.0 of Calvin for the C Language. The new OS has some added Tasks, including onboard EEPROM and an improved ISR stack. I have used Pavel's C2C++ Compiler as it is the best & easiest one that I have found to-date, and his ISR can be easily merged with my ISR stack. On this page, you will find the Code and several useful header files. I will add code snippets as I release them. For now, the Calvin C-code will show one task blinking at the TICK rate.

New Stuff in Release 2.0

In the works - to be released soon. If anyone would like to contribute code that they have written using Calvin, eg. complete listings or ISR routines, please email them to me and I will put them into the new Library section for Calvin C.


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 C) - Current Version: 2.0

   Calvin C code example link. Click here to get the code!

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 Timer 0 Overflow INT Task being activated;

//*******************************************************************************
//* Set Definitions here. Define all IO Interrupts                              *
//* which will be implemented. Defines recognized                               *
//* are:                                                                        *
//*******************************************************************************
//* 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                                               *
//* SSPBC_INT Sync Serial Port (I2C Mode) Bus Collision Detected                *
//* 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)                                         *
//* EEWR_INT EEPROM (Finished Write Cycle) Interrupt                            *
//* TMR0_INT TMR0 overflow Int. Must be set to use                              *
//*******************************************************************************

#define TMR0_INT 1  // Using TMR0 for the Master Task Counter
 

In Calvin, any of the above IO Tasks which are defined as shown here will have their code automatically inserted at compilation.

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

//* Task Enable Masks
char TMask1 = 0;   // Tasks (1-8) mask bits
#define TM1_En0 0  // Task 1 Mask Bit
#define TM2_En1 1  // Task 2 Mask Bit
#define TM3_En2 2  // Task 3 Mask Bit
#define TM4_En3 3  // Task 4 Mask Bit
#define TM5_En4 4  // Task 5 Mask Bit
#define TM6_En5 5  // Task 6 Mask Bit
#define TM7_En6 6  // Task 7 Mask Bit
#define TM8_En7 7  // Task 8 Mask Bit
// Add more here if required...
 

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

Below is a printout of the new ISR Stack. It adds on to the built-in ISR routine offered in C2C++ by saving 4 variables into the ISR Stack. ISR Stack overflow is now handled by the __StackOverFlow() function.

//*******************************************************************************
//* ISR Stack Equates *                                                         *
//*******************************************************************************
//* The ISR Stack is a software stack for storing multiple instances of :       *
//* {W, STATUS, FSR, PCLATH } = STATE. This version of Calvin saves all these   *
//* values, with no option for saving just some. If the Maximum stack size is   *
//* zero, the entire stack operation is disabled. Stack size now refers to the  *
//* number of STATE saves, so the numbewr of bytes involved is ( #SAVES * 4 )   *
//*******************************************************************************
//* C2C++ Interrupt Routine saves the four registers into                       *
//*  __int_save_cont_W                                                          *
//*  __int_save_cont_STATUS                                                     *
//*  __int_save_cont_FSR                                                        *
//*  __int_save_cont_PCLATH                                                     *
//* Save these registers into the stack in the interrupt() function.            *
//*                                                                             *
//*******************************************************************************


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 16F877 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 ->
(/RD AND /CS) OR (/WR AND /CS)
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)
SSPBC_INT Sync Serial Port (I2C Mode) Bus Collision Detected
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)
EEWR_INT EEPROM (Finished Write Cycle) Interrupt
TMR0_INT Timer 0 overflow INT

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