CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

csRtos ported to PIC - first file main.c
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> Code Library
View previous topic :: View next topic  
Author Message
mhahn@hvc.rr.com



Joined: 03 Jan 2005
Posts: 9
Location: upstate NY

View user's profile Send private message

csRtos ported to PIC - first file main.c
PostPosted: Mon Jan 03, 2005 11:51 am     Reply with quote

csRtos is a simple salvo-like rtos for small microcontrollers. It was developed by Glen Worstell. You can find more info on it at:
http://www.circuitcellar.com/avr2004/DA3650.html

Over the holidays I ported csRtos to work with the CCS compiler. I've tested this with some simple tasks on a picdem2 board and I've ported a client's application to use csRtos.

I'll be splitting this up into 4 posts, one for each of the files below.

main.c - contains main() and some sample tasks
main.h - defines used by main.c
rtos.c - the meat of the rtos code
rtos.h - defines for rtos.h

This code is being released into the public domain with no restrictions.

Please don't flood me with email questions about this, as I have limited time to respond. Try to discuss issues, improvements, etc. in this forum.

Enjoy,

Mark R Hahn

Code:

//*****************************************************************************
//
// csRtos test program
//
// file: main.c
//
// description:
//    Simple test of the port of csRtos to the PICDEM2 board.
//
//    This code was compiled with the CCS C compiler (PCH & PCM version 3.211).
//
//    This example is fairly lame. It just lights 3 LEDs on the PICDEM2 board.
//    LEDs labeled RB1 and RB2 flash in an alternating pattern at about a 1 Hz.
//    rate. LED RB3 is lit when button S3 is pressed.
//
//    This is pretty preliminary code. It probably is a bit buggy. I've tested
//    most of the functions in another application that I wrote for a client.
//    Since the client owns the application code, I can't share it. I'll try
//    to add some better examples and better descriptions of how to use the
//    rtos.
//
//    As mentioned in rtos.c, csRtos was developed by Glen Worstell for the
//    Atmel AVR. You should probably read his Circuit Cellar contest entry:
//    http://www.circuitcellar.com/avr2004/DA3650.html
//
//    I've added a few features that aren't explained in the code very well
//    yet, so I'll explain them here:
//
//    Near the top of rtos.h you will find defines of OS_ROUNDROBIN and
//    OS_ROUNDPRIORITY. If you deine OS_ROUNDROBIN, tasks are just executed
//    in the order they are ready to run. OS_ROUNDROBIN is handy if you don't
//    want to figure out priorities for your tasks.
//
//    The define OS_WAITFORTASK causes the __osSchedule routine to wait till
//    a task is ready to run. If it isn't defined, when no tasks are ready
//    to run (rtr) code in the loop in main() will run (search for osMainTask
//    in the main() function in this file). In this example, nothing much
//    happens when no tasks are rtr, I just increment a variable. You could
//    do other stuff at this point, like check for timer overflows if running
//    on a PIC that doesn't support interrupts.
//
// history:
//    Written 12/26/04 ... MRH
//    Ported to 16F877 1/2/05 ... MRH
//
//*****************************************************************************

// this example works with either a PIC16F877 or a PIC18F452
// uncomment one of the defines below to select a processor
#define PIC18
//#define PIC16

// standard compiler header for selected processor
// must be first include
#ifdef PIC18
#include <18f452.h>
#else
#include <16f877.h>
#endif

//
// compiler specific directives
//

// compiler specific directive for use with ICD
#DEVICE  ICD=TRUE

// have AtoD routines return 10 bit values
#DEVICE  ADC=10

// 4.00Mhz clock on PICDEM2
#use DELAY(CLOCK=4000000)

// use crystal oscilator - XT
// disable watchdog for now - NOWDT
// disable low voltage programming - NOLVP
// disable brownout detect - NOBROWNOUT
// disable power up timer - NOPUT
// disable code protection - NOPROTECT
// enable ICD - DEBUG
#fuses XT,NOWDT,NOLVP,NOBROWNOUT,PUT,NOPROTECT,DEBUG

// using fast_io saves a bit of rom and time
// just be sure to explicitly set IO pin direction
#use FAST_IO(A)
#use FAST_IO(B)
#use FAST_IO(C)
#use FAST_IO(D)
#use FAST_IO(E)

// includes that describe system wide definintions
#include <main.h>

// Note: you must define the number of tasks (nTasks), as well as the
// number of semaphores (nSema), and the number of events (nEvents).
// These defines should be non-zero.
// If you do not wish to use semaphores or events, leave nSema or nEvents
// undefined.

// list all task numbers here in priority order, highest priority first
enum {led1TaskNumber,led2TaskNumber,led3TaskNumber,adcTaskNumber,buttonTaskNumber,nTasks};

// Semaphores are defined here.  Each semaphore uses two bytes: one
// has a bit set for each task waiting for the semaphore, and the
// other contains the number of the task that owns the semaphore.
// If the semaphore is available, the owner byte will be 255.
// Semaphores are numbered sequentially from zero.

enum {SOUTSEMA,nSemas};

// Events are defined here.  Each event uses one byte, one
// bit set for each task waiting for the event to happen.
// Events are numbered sequentially from zero.

enum {KEYPRESSEVENT,KEYRELEASEEVENT,nEvents};

// include os macros and definitions
#include "rtos.h"

//
// include c source modules since CCS doesn't have a linker
//

#include "rtos.c"


//
// initIoPins:
//    Set all IO pins to their proper state.
//

void initIoPins (void)
{

   // set io direction
   // 0 = output, 1 = input

   _TRISA = 0b00010001;    // a0, a4 are inputs
   _TRISB = 0b00000001;    // b0 is input
   _TRISC = 0b10011011;    // c0, c1, c3, c5, c7 are inputs
   _TRISD = 0b00000000;    // d is all outputs
   _TRISE = 0b00000000;    // e is all outputs

   #ifdef __PCH__

   // set all outputs to 0
   _LATA = 0b00000000;
   _LATB = 0b00000000;
   _LATC = 0b00000000;
   _LATD = 0b00000000;
   _LATE = 0b00000000;

   #else

   // set all outputs to 0
   _PORTA = 0b00000000;
   _PORTB = 0b00000000;
   _PORTC = 0b00000000;
   _PORTD = 0b00000000;
   _PORTE = 0b00000000;

   #endif
}


#ifdef __PCH__

// the +18 in the defines below is to handle the time it
// takes to add the value of timer0 to our delay value
#define MS_DELAY  (64536+18)

//
// timeIsr:
//    ISR that updates 1 mSec system timers. Uses timer 0.
//    Keep this as short as possible, to keep interrupt latency
//    as short as possible.
//

#INT_TIMER0

void timeIsr (void)
{
   UINT16 delay;

   // calculate when to wake up next
   delay = get_timer0() + MS_DELAY;

   set_timer0(delay);

   // update task timers
   __osTimer();
}


//
// timeInit:
//    Initialize timer task and variables
//

void timeInit (void)
{
   setup_timer_0(RTCC_INTERNAL | RTCC_DIV_1);
   enable_interrupts(INT_TIMER0);
}

#else

// the +18 in the defines below is to handle the time it
// takes to add the value of timer0 to our delay value
#define MS_DELAY  (64536+18)

//
// timeIsr:
//    ISR that updates 1 mSec system timers. Uses timer 1.
//    Keep this as short as possible, to keep interrupt latency
//    as short as possible.
//

#INT_TIMER1

void timeIsr (void)
{
   UINT16 delay;

   // calculate when to wake up next
   delay = get_timer1() + MS_DELAY;

   set_timer1(delay);

   // update task timers
   __osTimer();
}


//
// timeInit:
//    Initialize timer task and variables
//

void timeInit (void)
{
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
   enable_interrupts(INT_TIMER1);
}

#endif

//
// some sample tasks
//    led1Task - blink led1
//    led2Task - blink led2
//    led3Task - lite led3 when s3 is pressed
//

void led1Task(void)
{
   osTaskInit(led1TaskNumber);

   if (osNotReady)
      return;

   while (TRUE)
   {
      led1Pin = TRUE;
      osWait(100);

      led1Pin = FALSE;
      osWait(900);
   }
}


void led2Task(void)
{
   osTaskInit(led2TaskNumber);

   if (osNotReady)
      return;

   while (TRUE)
   {
      led2Pin = TRUE;
      osWait(900);

      led2Pin = FALSE;
      osWait(100);
   }
}


void led3Task(void)
{
   osTaskInit(led3TaskNumber);

   if (osNotReady)
      return;

   while (TRUE)
   {
      osWaitEvent(KEYPRESSEVENT);
      led3Pin = TRUE;
      osWaitEvent(KEYRELEASEEVENT);
      led3Pin = FALSE;
   }
}


#define ADC_NUM_SAMPLES     (16)

// array of adc samples
UINT16 adcSamples[ADC_NUM_SAMPLES];

// index into input arrays
UINT8 adcSampleIndex;

// wait this many cycles befoe doing next adc read
#define  ADC_READ_DELAY (4)


//
// adcTask:
//    Task that runs A to D converter and updates reading of AN0.
//

void adcTask (void)
{
   // initialize adc hardware
   setup_adc_ports(AN0);

   setup_adc(ADC_CLOCK_DIV_8);

   set_adc_channel(0);

   // initialize up variables used to average input
   for (adcSampleIndex = 0; adcSampleIndex < ADC_NUM_SAMPLES; adcSampleIndex++)
   {
      adcSamples[adcSampleIndex] = 0;
   }

   adcSampleIndex = 0;

   // init task
   osTaskInit(adcTaskNumber);

   if (osNotReady)
      return;

   // main loop of task
   while(TRUE)
   {
      // give adc mux time to settle
      // you can set the rate that ad readings get updated here
      osWait(6);

      adcSamples[adcSampleIndex] = read_adc(ADC_START_AND_READ );

      adcSampleIndex++;

      if (adcSampleIndex >= ADC_NUM_SAMPLES)
      {
         adcSampleIndex = 0;
      }

   }
}


//
// adcRead:
//    Returns an averaged AN0 adc reading.
//

UINT16 adcRead(void)
{
   UINT16 sum;
   UINT8 ii;

   sum = 0;

   for (ii=0; ii<ADC_NUM_SAMPLES; ii++)
   {
      sum += adcSamples[ii];
   }

   return(sum/ADC_NUM_SAMPLES);
}


//
// buttonTask:
//    Posts an event when the switch S3 changes state.
void buttonTask(void)
{
   static UINT8 debounceCount;

   osTaskInit(buttonTaskNumber);

   if (osNotReady)
      return;

   while (TRUE)
   {

wait_press:

      while (inSw3 != 0)
      {
         osWait(20);
      }

      debounceCount = 50;

      while (inSw3 == 0)
      {
         debounceCount--;
         if (debounceCount == 0)
         {
            break;
         }
         osWait(2);
      }

      if (debounceCount > 0)
      {
         goto wait_press;
      }

      osPostEvent(KEYPRESSEVENT);

wait_release:

      while (inSw3 == 0)
      {
         osWait(20);
      }

      debounceCount = 50;

      while (inSw3 != 0)
      {
         debounceCount--;
         if (debounceCount == 0)
         {
            break;
         }
         osWait(2);
      }

      if (debounceCount > 0)
      {
         goto wait_release;
      }

      osPostEvent(KEYRELEASEEVENT);
   }
}


//
// main:
//    Everybody's favorite C function.
//

void main (void)
{
   UINT32 ii;

   // do hardware initializations
   initIoPins();
   timeInit();

   // init os variables
   // call before calling tasks
   osInit();

   // start tasks, they run till they hit osTaskInit()
   led1Task();
   led2Task();
   led3Task();
   adcTask();
   buttonTask();

   // now that we are initialized, enable all interrupts
   enable_interrupts(GLOBAL);

   // Set some tasks ready to run.
   osSetRtr(led1TaskNumber);
   osSetRtr(led2TaskNumber);
   osSetRtr(led3TaskNumber);
   osSetRtr(adcTaskNumber);
   osSetRtr(buttonTaskNumber);

   ii = 0;

   // main loop
   while(TRUE)
   {
      // run the OS
      osMainTask();

      // note: when no tasks are ready to run, ii gets incremented
      ii++;
   }

   #ifdef __PCH__
   // should never get here
   // this call forces the compiler to treat key os functions as subroutines
   osCallAll();
   #endif
}

mhahn@hvc.rr.com



Joined: 03 Jan 2005
Posts: 9
Location: upstate NY

View user's profile Send private message

csRtos ported to PIC - second file main.h
PostPosted: Mon Jan 03, 2005 11:54 am     Reply with quote

here is main.h
Code:

//*****************************************************************************
//
// file: main.h
//
// description:
//    Definitions of system wide variables, functions, and defines.
//
// history:
//    Created 12/26/04 ... MRH
//
//*****************************************************************************

//
// types:
//    Define our own.
//    CCS doesn't always follow C standards.
//

typedef unsigned int32 UINT32;
typedef unsigned int16 UINT16;
typedef unsigned int8 UINT8;
typedef unsigned char UCHAR;

typedef signed int32 SINT32;
typedef signed int16 SINT16;
typedef signed int8 SINT8;
typedef signed char SCHAR;

typedef int1 BOOL;


// register definitions

#ifdef __PCH__

// io registers
#byte _PORTA = 0xF80
#byte _PORTB = 0xF81
#byte _PORTC = 0xF82
#byte _PORTD = 0xF83
#byte _PORTE = 0xF84

// latched registers - for read-modify writes
#byte _LATA = 0xF89
#byte _LATB = 0xF8A
#byte _LATC = 0xF8B
#byte _LATD = 0xF8C
#byte _LATE = 0xF8D

// TRIS registers - define IO direction
#byte _TRISA = 0xF92
#byte _TRISB = 0xF93
#byte _TRISC = 0xF94
#byte _TRISD = 0xF95
#byte _TRISE = 0xF96

#byte _TXREG = 0xFAD

#byte _TXSTA = 0xFAC
#bit _BRGH =   _TXSTA.2
#bit _SYNC =   _TXSTA.4
#bit _TXEN =   _TXSTA.5
#bit _TX9 =    _TXSTA.6

#byte _RCREG = 0xFAE

#byte _RCSTA = 0xFAB
#bit _ADDEN = _RCSTA.3
#bit _CREN =  _RCSTA.4
#bit _RX9 =   _RCSTA.6
#bit _SPEN =  _RCSTA.7

#byte _SPBRG = 0xFAF

#byte _PIR1 = 0xF9E

#byte _PIR2 = 0xFA1
#bit  _EEIF = _PIR2.4

#byte _PIR3 = 0xFA4

#byte _EECON1 = 0xFA6
#bit  _EEPGD = _EECON1.7
#bit  _CFGS =  _EECON1.6
#bit  _WREN =  _EECON1.2
#bit  _WR =    _EECON1.1
#bit  _RD =    _EECON1.0

#byte _EECON2 = 0xFA7
#byte _EEDATA = 0xFA8
#byte _EEADR =  0xFA9
#byte _EEADRH = 0xFAA

#byte _INTCON = 0xFF2
#bit  _GIE = _INTCON.7

//
// IO pin definitions
//

#bit led1Pin = _LATB.1
#bit led2Pin = _LATB.2
#bit led3Pin = _LATB.3

#bit inSw2 = _PORTA.4
#bit inSw3 = _PORTB.0

#else

// io registers
#byte _PORTA = 0x05
#byte _PORTB = 0x06
#byte _PORTC = 0x07
#byte _PORTD = 0x08
#byte _PORTE = 0x09

// TRIS registers - define IO direction
#byte _TRISA = 0x85
#byte _TRISB = 0x86
#byte _TRISC = 0x87
#byte _TRISD = 0x88
#byte _TRISE = 0x89

#byte _TXREG = 0x19

#byte _TXSTA = 0x98
#bit _BRGH =  _TXSTA.2
#bit _SYNC =  _TXSTA.4
#bit _TXEN =  _TXSTA.5
#bit _TX9 =   _TXSTA.6

#byte _RCREG = 0x1A

#byte _RCSTA = 0x18
#bit _ADDEN = _RCSTA.3
#bit _CREN =  _RCSTA.4
#bit _RX9 =   _RCSTA.6
#bit _SPEN =  _RCSTA.7

#byte _SPBRG = 0x99

#byte _PIR1 = 0x0C

#byte _PIR2 = 0x0D
#bit  _EEIF = _PIR2.4

#byte _EECON1 = 0x18C
#bit  _EEPGD = _EECON1.7
#bit  _WREN =  _EECON1.2
#bit  _WR =    _EECON1.1
#bit  _RD =    _EECON1.0

#byte _EECON2 = 0x18D
#byte _EEDATA = 0x10C
#byte _EEADR =  0x10D
#byte _EEADRH = 0x10F

#byte _INTCON = 0x10b
#bit  _GIE = _INTCON.7

//
// IO pin definitions
//

#bit led1Pin = _PORTB.1
#bit led2Pin = _PORTB.2
#bit led3Pin = _PORTB.3

#bit inSw2 = _PORTA.4
#bit inSw3 = _PORTB.0

#endif

mhahn@hvc.rr.com



Joined: 03 Jan 2005
Posts: 9
Location: upstate NY

View user's profile Send private message

csRtos ported to PIC - third file rtos.c
PostPosted: Mon Jan 03, 2005 11:55 am     Reply with quote

here is rtos.c

Code:

//*****************************************************************************
//
// file: rtos.c
//
// description:
//    Definitions of csRtos variables, functions, and defines.
//
// history:
//    Created 12/26/04 ... MRH
//
// CSRTOS:
//
// CSRTOS is a very lightweight cooperative multitasking OS. It may be
// configured to run tasks in priority order, or tasks may be run in
// a round robin fashion (see defines of OS_PRIORITY & OS_ROUNDROBIN).
//
// CSRTOS requires two simple rules to work:
//
// 1) Operating System calls that may result in task switches can occur
//    only at the top level of a task. That is, you cannot make os calls
//    from subroutines called within a task.
//
// 2) Local variables of a task subroutine are not preserved across
//    OS calls. If you need to preserve the value of a local variable,
//    declare it to be "static".
//
// Most of csRTOS was written by Glen Worstell (worstell@copper.net) for the
// Atmel AVR. It was ported to the Microchip PIC18 (using the CCS PCWH C
// compiler) by Mark Hahn (mhahn@hvc.rr.com). The original code and an article
// about it can be found at: http://www.circuitcellar.com/avr2004/DA3650.html
//
// Most of the following code is the same as the original source.
// A few additional features (such as events, and timeouts for events and
// semaphores) were added. Some names have been changed for clarity.
//
// This code was written and tested using the CCS PCWH C compiler. You will
// need to change a couple of assembly language routines (see __osSave
// and __osRestore) if you want to use the code with a different C compiler.
//
// As with the original code written by Glen Worstell, this code is released to
// the public domain with no restrictions.
//
// This code is very much a work in progress. Please feel free to contact the
// original authors with suggestions or comments.
//
//*****************************************************************************

// start of csRTOS code

// figure how big to make priority que and assorted temp variables
// definition of OS_SIZE depends on the number of tasks
#if nTasks <= 8
#define OS_SIZE   UINT8
#elif nTasks <= 16
#define OS_SIZE   UINT16
#else
#define OS_SIZE   UINT32
#endif

// Note: you must define the number of tasks (nTasks), as well as the
// number of semaphores (nSema), and the number of events (nEvents).
// These defines should be non-zero.
// If you do not wish to use semaphores or events, leave nSema or nEvents
// undefined.

// placeholder task in case user did not define any
#if !defined(nSemas)
enum {nullTask,nSemas};
#endif

// placeholder semaphore in case user did not define any
#if !defined(nSemas)
enum {nullSema,nSemas};
#endif

// placeholder event in case user did not define any
#if !defined(nEvents)
enum {nullEvent,nEvents};
#endif

// set FALSE after all tasks have called osTaskInit().
BOOL osNotReady = TRUE;

// The number of the currently running task.
static UINT8 __osCurrentTask;

// Address to get back to the main loop and some registers.
static REGSAVE __osMainAddr;

// Pointer to the address of the currently running task.
static REGSAVE *__osTaskAddressPtr;

// array of address last executed in tasks
static REGSAVE __osTaskAddr[nTasks];

// If a task is ready to run, a bit corresponding to
// the task number is set in rtr.
static OS_SIZE __osRtr;

// Tick timers, 1 per task.
static UINT16 __osTicks[nTasks];

// One bit high for each task waiting for the sema.
static OS_SIZE __osWantSema[nSemas];

// Task number of sema owner, or 255 if sema is available.
static UINT8 __osSemaOwner[nSemas];

// One bit high for each task waiting for the event.
static OS_SIZE __osWantEvent[nEvents];

// Some temp variables.
static OS_SIZE __osTemp;
static UINT8 __osTempSmall;
static OS_SIZE __osTempBig;

// power of two lookup table and function
#if nTasks <= 8
const UINT16 two2nArray[] = {1,2,4,8,16,32,64,128};
#elif nTasks <= 16
const UINT16 two2nArray[] = {1,2,4,8,16,32,64,128,
                             256,512,1024,2048,4096,8192,16384,32768};
#else
const UINT32 two2nArray[] = {1,2,4,8,16,32,64,128,
                             256,512,1024,2048,4096,8192,16384,32768,
                             65536,131072,262144,524288,1048576,2097152,4194304,8388608,
                             16777216,33554432,67108864,134217728,268435456,536870912,1073741824,2147483648};
#endif


// OS FUNCTIONS
// These functions are normally not called from user code, except via the supplied os macros.


//
// __osDisableIrq:
//    Disable interrupts.
//

void __osDisableIrq(void)
{
   disable_interrupts(GLOBAL);
}


//
// __osEnableIrq:
//    Enable interrupts.
//

void __osEnableIrq(void)
{
   enable_interrupts(GLOBAL);
}


//
// __osSave:
//    Save registers and task return address.
//    The behavior of this routine is very compiler dependent.
//    All OS calls follow a sequence like:
//
//    void osFuncName(SOMETYPE parm)
//    {
//       // save address of return to current task and any needed registers
//       __osSave();
//
//       ... do some OS stuff
//
//       // figure out what task to run next, note: __osCurrentTask may change
//       __osSchedule();
//
//       // jump to the return address of the current task
//       // and restore any saved registers
//       __osRestore();
//    }
//
//    If your compiler does not assume registers are preserved across function
//    calls, you may get away with just saving the return address. For the
//    PIC18FXXX processor you may need to save:
//    W, STATUS, BSR, FSR0L, FSR0H, FSR1L, FSR1H, FSR2L, FSR2H, PRODH, PRODL,
//    TBLPTRL, TBLPTRH, TBLPTRH, TABLAT, PCLATH and PCLATU
//

#ifdef __PCH__
#separate
void __osSave()
{
   __osDisableIrq();

   #asm
   MOVFF    __osTaskAddressPtr,0xFE9
   MOVFF    &__osTaskAddressPtr+1,0xFEA
                        // FSR0 = taskAddr

   DECF     0xFFC,f     // STKPTR--
                        // STKPTR now points to return address to current task

   MOVF     0xFFD,w     // *FSR0++ = TOSL
   MOVWF    0xFEE

   MOVF     0xFFE,w     // *FSR0++ = TOSH
   MOVWF    0xFEE

   MOVF     0xFFF,w     // *FSR0 = TOSU
   MOVWF    0xFEF

   INCF     0xFFC,f     // STKPTR++
                        // STKPTR now points to return address for this func
   #endasm

   __osEnableIrq();
}

#else
#define __osSave() {*__osTaskAddressPtr=label_address(task_exit);}
#endif

//
// __osRestore:
//    Resume execution at point task was suspended.
//

#ifdef __PCH__
#separate
void __osRestore()
{
   __osDisableIrq();

   #asm
   MOVFF    __osTaskAddressPtr,0xFE9
   MOVFF    &__osTaskAddressPtr+1,0xFEA
                        // FSR0 = taskAddr

   DECF     0xFFC,f     // STKPTR--

   MOVF     0xFEE,w     // TOSL = *FSR0++
   MOVWF    0xFFD

   MOVF     0xFEE,w     // TOSH = *FSR0++
   MOVWF    0xFFE

   MOVF     0xFEF,w     // TOSU = *FSR0
   MOVWF    0xFFF

   INCF     0xFFC,f     // STKPTR++
   #endasm

   __osEnableIrq();
}

#else
#define __osRestore() {goto_address(*__osTaskAddressPtr); task_exit: return;}
#endif


const UINT8 priorityTbl[] =
{0xFF,   0,   1,   0,   2,   0,   1,   0,   3,   0,   1,   0,   2,   0,   1,   0};
//idx 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111

//
// __osSchedule:
//    Find the highest priority task that is rtr (ready to run) an run it.
//

void __osSchedule(void)
{

   #ifndef OS_WAITFORTASK

   // An isr will set a bit in rtr to resume a task.
   __osDisableIrq();
   __osTemp = __osRtr;
   __osEnableIrq();

   if (__osTemp == 0)
   {
      // if there are no tasks ready to run
      // you jump back to the call to osRun made in main()
      __osTaskAddressPtr = &__osMainAddr;
      return;
   }

   #else

   // alternative to returning to main
   // just loop till a task is rtr
   do {
      // you can add code here to keep track of idle time
      // note: you could put the processor in low power mode here.

      __osDisableIrq();
      __osTemp = __osRtr;
      __osEnableIrq();
   }
   while (__osTemp == 0);

   #endif

   #ifdef OS_ROUNDROBIN

   // round robin scheduling
   // find the next task in list that is ready to run
   while ((__osTemp & two2nArray[++__osCurrentTask]) == 0)
   {
      if (__osCurrentTask >= nTasks)
      {
         __osCurrentTask = 0xFF;
      }
   }

   #else

   // priority based scheduling
     // Find highest priority task that is rtr.
   __osTempSmall = make8(__osRtr,0);
   if (__osTempSmall)
   {
      if ((__osTempSmall & 0x0F))
      {
         __osCurrentTask = priorityTbl[__osTempSmall & 0x0F];
      }
      else
      {
         __osTempSmall = swap(__osTempSmall) & 0x0F;
         __osCurrentTask = priorityTbl[__osTempSmall] + 4;
      }
   }

   #if nTasks > 8
   else
   {
      __osTempSmall = make8(__osRtr,1);
      if (__osTempSmall)
      {
         if ((__osTempSmall & 0x0F))
         {
            __osCurrentTask = priorityTbl[__osTempSmall & 0x0F] + 8;
         }
         else
         {
            __osTempSmall = swap(__osTempSmall) & 0x0F;
            __osCurrentTask = priorityTbl[__osTempSmall] + 12;
         }
      }

      #if nTasks > 16
      else
      {
         __osTempSmall = make8(__osRtr,2);
         if (__osTempSmall)
         {
            if ((__osTempSmall & 0x0F))
            {
               __osCurrentTask = priorityTbl[__osTempSmall & 0x0F] + 16;
            }
            else
            {
               __osTempSmall = swap(__osTempSmall) & 0x0F;
               __osCurrentTask = priorityTbl[__osTempSmall] + 20;
            }
         }
         else
         {
            __osTempSmall = make8(__osRtr,3);
            if ((__osTempSmall & 0x0F) != 0)
            {
               __osCurrentTask = priorityTbl[__osTempSmall & 0x0F] + 24;
            }
            else
            {
               __osTempSmall = swap(__osTempSmall) & 0x0F;
               __osCurrentTask = priorityTbl[__osTempSmall] + 28;
            }
         }
      }
      #endif

   }
   #endif

   #endif

   __osTaskAddressPtr = &__osTaskAddr[__osCurrentTask];
}


//
// __osWait:
//    Suspend the calling task for some number of ticks.
//    Calling this with nTicks=0 will result in a suspension.
//    The task can can only be resumed by an ISR or another task
//    setting the rtr bit.
//

void __osWait(UINT16 nTicks)
{
   __osDisableIrq();
   __osTicks[__osCurrentTask] = nTicks;
   __osClearRtrCritical(__osCurrentTask);
   __osEnableIrq();
   __osSchedule();
}


//
// __osWaitEvent:
//    Suspend the calling task till an event happens or the tasks tick
//    timer expires. Calling with nTicks equal to 0 will cause task to
//    wait forever foir the event.
//

void __osWaitEvent(UINT8 eventNumber,UINT16 nTicks)
{
   __osDisableIrq();

   __osWantEvent[eventNumber] |= two2nArray[__osCurrentTask];
   __osTicks[__osCurrentTask] = nTicks;
   __osClearRtrCritical(__osCurrentTask);

   __osEnableIrq();
   __osSchedule();
}


//
// __osGetSema:
//    Try to get a semaphore. Suspend if not available.
//    Calling with nTicks equal to 0 will cause task to
//    wait forever for the semaphore.

void __osGetSema(UINT8 semaNumber,UINT16 nTicks)
{
   __osDisableIrq();

   // Aquire a semaphore, or suspend.
   if (__osSemaOwner[semaNumber] == 255)
   {
      __osSemaOwner[semaNumber] = __osCurrentTask;
   }
   else
   {
      __osWantSema[semaNumber] |= two2nArray[__osCurrentTask];
      __osTicks[__osCurrentTask] = nTicks;
      __osClearRtrCritical(__osCurrentTask);
   }

   __osEnableIrq();
   __osSchedule();
}


//
// __osReleaseSema:
//    Give up a semaphore. Run next task that wants it.
//

void __osReleaseSema(UINT8 semaNumber)
{
   __osDisableIrq();

   // remove current task from wantSema list
   __osTemp = __osWantSema[semaNumber] & (~(two2nArray[__osCurrentTask]));
   __osWantSema[semaNumber] = __osTemp;

   // see someone else wants the semaphore
   if (__osTemp != 0)
   {
      __osTempSmall = 0;
      // find highest priority task that wants it
      while ((__osTemp & 1) == 0)
      {
         __osTemp >>= 1;
         __osTempSmall += 1;
      }

      __osSemaOwner[semaNumber] = __osTempSmall;
      __osSetRtrCritical(__osTempSmall);
   }
   else
   {
      __osSemaOwner[semaNumber] = 255;
   }

   __osEnableIrq();

   // go figure out who runs next
   __osSchedule();
}


// __osTimer:
//    Decrement wait timers.
//    Note: this should be called from an interrupt service routine.
//

void __osTimer(void)
{
   UINT8 ii;
   UINT16 jj;
   
   // decrement timers for any tasks that are waiting
   for (ii = 0; ii < nTasks; ii++)
   {
      jj = __osTicks[ii];
     
      if (jj)
      {
         jj--;

         if (jj==0)
         {
            // this task is now ready to run.
            __osSetRtrCritical(ii);
         }
         
         __osTicks[ii] = jj;
      }
   }
}


//
// __osClearRtr:
//    clear rtr bit. Should never be called from an ISR.
//

void __osClearRtr(UINT8 task)
{
   __osDisableIrq();
   __osRtr = __osRtr & ~two2nArray[task];
   __osEnableIrq();
}


//
// __osSetRtr:
//    set rtr bit. Should never be called from an ISR.
//

void __osSetRtr(UINT8 task)
{
   __osDisableIrq();
   __osRtr = __osRtr | two2nArray[task];
   __osEnableIrq();
}


//
// __osClearRtrCritical:
//    clear rtr bit. Should only be called from an ISR. Or in a
//    task that has the call inside a critical section.
//

void __osClearRtrCritical(UINT8 task)
{
   __osRtr = __osRtr & ~two2nArray[task];
}


//
// __osSetRtrCritical:
//    set rtr bit. Should only be called from an ISR. Or in a
//    task that has the call inside a critical section.
//

void __osSetRtrCritical(UINT8 task)
{
   __osRtr = __osRtr | two2nArray[task];
}


//
// osPostEvent:
//    set rtr bits for all tasks waiting for an event.
//    Should never be called from an ISR.
//

void osPostEvent(UINT8 eventNumber)
{
   __osDisableIrq();
   __osRtr = __osRtr | __osWantEvent[eventNumber];
   __osEnableIrq();
}


//
// osPostEventCritical:
//    set rtr bits for all tasks waiting for an event. Should only be called
//    from an ISR. Or in a task that has the call inside a critical section.
//

void osPostEventCritical(UINT8 eventNumber)
{
   __osRtr = __osRtr | __osWantEvent[eventNumber];
}


// OS FUNCTIONS
// Task switching occurs only when a task makes certain os calls. After an os call the highest
// priority task that is ready to run (rtr) is resumed.

//
// osGetTicks:
//    Gets timer ticks for current task.
//

UINT16 osGetTicks(void)
{
   __osDisableIrq();
   __osTempBig = __osTicks[__osCurrentTask];
   __osEnableIrq();
   return(__osTempBig);
}


// osTimeout:
//    Returns TRUE if current task has a timer value of 0.
//    Can be used to see if a timeout happened while waiting
//    for an event or a semaphore.
//

BOOL osTimeout(void)
{
   __osTempSmall = FALSE;

   __osDisableIrq();

   if (__osTicks[__osCurrentTask]==0)
   {
      __osTempSmall = TRUE;
   }

   __osEnableIrq();

   return(__osTempSmall);
}


//
// osClearTicks:
//    Clears timer ticks for current task.
//

void osClearTicks(void)
{
   __osDisableIrq();
   __osTicks[__osCurrentTask] = 0L;
   __osEnableIrq();
}


//
// osInit:
//    Init os static variables.
//

void osInit(void)
{
   for (__osCurrentTask = 0; __osCurrentTask < nSemas; __osCurrentTask++)
   {
      __osWantSema[__osCurrentTask] = 0;       // Nobody wants the semaphore.
       __osSemaOwner[__osCurrentTask] = 0xFF;   // Nobody owns the semaphore.
   }

   for (__osCurrentTask = 0; __osCurrentTask < nEvents; __osCurrentTask++)
   {
      __osWantEvent[__osCurrentTask] = 0;      // Nobody wants the event.
   }

   __osRtr = 0;
   __osCurrentTask = 0;

   osNotReady = TRUE;
}


//
// osRun:
//    Start running the OS.
//

#ifdef __PCH__
#separate
#else
#inline
#endif
void osRun(void)
{
   osNotReady = FALSE;

   __osTaskAddressPtr = &__osMainAddr;
   __osSave();
   __osSchedule();
   __osRestore();
}


//
// osTaskInit:
//    Must be called for each task, before starting multitasking.
//

#ifdef __PCH__
#separate
#else
#inline
#endif
void osTaskInit(UINT8 taskNumber)
{
   __osTaskAddressPtr = &__osTaskAddr[taskNumber];
   __osSave();
#ifndef __PCH__
   task_exit:
      return;
#endif
}


//
// osWait:
//    Suspends the calling task for some number of ticks (1 mSec).
//

#ifdef __PCH__
#separate
#else
#inline
#endif
void osWait(UINT16 ticks)
{
   __osSave();
   __osWait(ticks);
   __osRestore();
}


//
// osWaitEventTimeout:
//    Suspends the calling task till event happens or ticks expire.
//    Setting ticks to 0 causes the task to wait forever.
//

#ifdef __PCH__
#separate
#else
#inline
#endif
void osWaitEventTimeout(UINT8 eventNumber,UINT16 ticks)
{
   __osSave();
   __osWaitEvent(eventNumber,ticks);
   __osRestore();
}


//
// osWaitEvent:
//    Suspends the calling task till event happens.
//

#ifdef __PCH__
#separate
#else
#inline
#endif
void osWaitEvent(UINT8 eventNumber)
{
   __osSave();
   __osWaitEvent(eventNumber,0);
   __osRestore();
}


//
// osYield:
//    Resumes the highest priority task that is rtr. The rtr status of the calling
//    task is not changed - so if it is the highest priority rtr it immediately resumes.
//

#ifdef __PCH__
#separate
#else
#inline
#endif
void osYield(void)
{
   __osSave();
   __osSchedule();
   __osRestore();
}


//
// osSuspend:
//    Makes the calling task not rtr. Usually it will be made rtr at some future
//    time by an interrupt service routine or by another task.
//

#ifdef __PCH__
#separate
#else
#inline
#endif
void osSuspend()
{
   __osSave();
   __osWait(0);
   __osRestore();
}


//
// osGetSemaTimeout:
//    Makes the calling task the owner of the given semaphore,
//    if it is available. If the semaphore is not available the
//    task is suspended. When the semaphore becomes available
//    the highest priority task that is waiting for it is set rtr.
//    Note: this may result in a priority inversion, a low priority
//    task that has taken a semaphore will prevent a higher
//    priority task from running.
//    Setting ticks to 0 causes the task to wait forever.

#ifdef __PCH__
#separate
#else
#inline
#endif
void osGetSemaTimeout(UINT8 semaNumber,UINT16 ticks)
{
   __osSave();
   __osGetSema(semaNumber,ticks);
   __osRestore();
}


//
// osGetSema:
//    Makes the calling task the owner of the given semaphore,
//    if it is available. If the semaphore is not available the task is
//    suspended. When the semaphore becomes available the highest priority
//    task that is waiting for it is set rtr.
//    Note: this may result in a priority inversion, a low priority task that has
//    taken a semaphore will prevent a higher priority task from running.

#ifdef __PCH__
#separate
#else
#inline
#endif
void osGetSema(UINT8 semaNumber)
{
   __osSave();
   __osGetSema(semaNumber,0);
   __osRestore();
}


//
// osReleaseSema:
//    Releases the semaphore, which results in another task being set
//    rtr if another task is waiting for the semaphore. Of course a
//    task should not release a semaphore unless it owns it - no error
//    checking is done for this condition.

#ifdef __PCH__
#separate
#else
#inline
#endif
void osReleaseSema(UINT8 semaNumber)
{
   __osSave();
   __osReleaseSema(semaNumber);
   __osRestore();
}


//
// osMainTask:
//    This function is mostly to keep osRun executing at the same stack level
//    as other os functions. Some compilers optimize out the call to this and
//    just place the function's code inline.
//

void osMainTask(void)
{
   osRun();
}


//
// osSuspendTask:
//    Suspends a task. May be called by one task (or main program) to suspend
//    another task.
//

void osSuspendTask(UINT8 nTask)
{
   __osDisableIrq();
   __osTicks[nTask] = 0L;
   __osClearRtrCritical(nTask);
   __osEnableIrq();
}

//
// osCallAll:
//    Some compilers optimize out subroutine calls and returns for functions
//    called only once. By calling osCallAll once in your main() function,
//    someplace it will never run, you fake the compiler out and it does
//    subroutine calls for all essential os functions.
//
//    eg:
//       while(FALSE)
//       {
//          osCallAll();
//       }
//

void osCallAll(void)
{
   osRun();
   osTaskInit(0);
   osWait(0);
   osWaitEvent(0);
   osWaitEventTimeout(0,0);
   osYield();
   osSuspend();
   osGetSema(0);
   osGetSemaTimeout(0,0);
   osReleaseSema(0);
}

// osPrint(UINT8 string) this macro prints a string.
// The task that calls this may be suspended till osPrint finishes.
// This macro is handy if you have a task that does a enough sequential
// printing that it may fill up your system's serial output buffer, or
// if printing may block because serial output has been disabled by flagging.
// You must supply functions serialOutRoom and serialPuts.
// serialOutRoom returns number of bytes left on serial output buffer.
// serialPuts puts a string in the serial output buffer.
#define osPrint(string) { \
   osGetSema(SOUTSEMA); \
   while (serialOutRoom() < sizeof(string)) \
   { osWAIT(2); } \
   serialPuts(string); \
   osReleaseSema(SOUTSEMA); \
}
mhahn@hvc.rr.com



Joined: 03 Jan 2005
Posts: 9
Location: upstate NY

View user's profile Send private message

csRtos ported to PIC - fourth file rtos.h
PostPosted: Mon Jan 03, 2005 11:58 am     Reply with quote

here is rtos.h
Code:

//*****************************************************************************
//
// file: rtos.h
//
// description:
//    Definitions of csRtos variables, functions, and defines.
//
// history:
//    Created 11/10/04 ... MRH
//
// CSRTOS:
//
// CSRTOS is a very lightweight cooperative multitasking OS. It may be
// configured to run tasks in priority order, or tasks may be run in
// a round robin fashion (see defines of OS_PRIORITY & OS_ROUNDROBIN).
//
// CSRTOS requires two simple rules to work:
//
// 1) Operating System calls that may result in task switches can occur
//    only at the top level of a task. That is, you cannot make os calls
//    from subroutines called within a task.
//
// 2) Local variables of a task subroutine are not preserved across
//    OS calls. If you need to preserve the value of a local variable,
//    declare it to be "static".
//
// Most of csRTOS was written by Glen Worstell (worstell@copper.net) for the
// Atmel AVR. It was ported to the Microchip PIC18 (using the CCS PCWH C
// compiler) by Mark Hahn (mhahn@hvc.rr.com). The original code and an article
// about it can be found at: http://www.circuitcellar.com/avr2004/DA3650.html
//
// Most of the following code is the same as the original source.
// A few additional features (such as events, and timeouts for events and
// semaphores) were added. Some names have been changed for clarity.
//
// This code was written and tested using the CCS PCWH C compiler. You will
// need to change a couple of assembly language routines (see __osSave
// and __osRestore) if you want to use the code with a different C compiler.
//
// As with the original code written by Glen Worstell, this code is released to
// the public domain with no restrictions.
//
// This code is very much a work in progress. Please feel free to contact the
// original authors with suggestions or comments.
//
// Note: you must define the number of tasks (nTasks), as well as the
// number of semaphores (nSema), and the number of events (nEvents).
// These defines should be non-zero.
// If you do not wish to use semaphores or events, leave nSema or nEvents
// undefined.
//
//*****************************************************************************


// start of csRTOS defines

// define one of the next 2 defines
// OS_PRIORITY - priority based scheduling
// OS_ROUNDROBIN - run the next task in the list
#define OS_PRIORITY
//#define OS_ROUNDROBIN

// define this to wait for a task instead of running loop in main()
//#define OS_WAITFORTASK

// some handy typedefs
#if !defined(UINT32)
typedef unsigned int32 UINT32;
#endif

#if !defined(UINT16)
typedef unsigned int16 UINT16;
#endif

#if !defined(UINT8)
typedef unsigned int8 UINT8;
#endif

#if !defined(BOOL)
typedef int1 BOOL;
#endif

// regsave structure
// the size depends on compiler behavior, see __osSave for details
#ifdef __PCH__
#define REGSAVE_SIZE 3
typedef struct {UINT8 bytes[REGSAVE_SIZE];} REGSAVE;
#else
#define REGSAVE_SIZE 2
typedef UINT16 REGSAVE;
#endif


// osClearRtr(task) clears the rtr bit of the given task, but DOES NOT call the scheduler.
#define osClearRtr(task) __osClearRtr(task)

// osSET_RTR(task) sets the rtr bit of the given task, but DOES NOT call the scheduler.
#define osSetRtr(task) __osSetRtr(task)

// osClearRtrCritical(task) clears the rtr bit of the given task, but DOES NOT call the scheduler.
// May be called inside an interrupt context.
#define osClearRtrCritical(task) __osClearRtrCritical(task)

// osSetRtrCritical(task) sets the rtr bit of the given task, but DOES NOT call the scheduler.
// May be called inside an interrupt context.
#define osSetRtrCritical(task) __osSetRtrCritical(task)

// osCRITICAL_END() re-enables interrupts (if previously enabled).
#define osCriticalBegin() __osDisableIrq()

// osCRITICAL_END() re-enables interrupts (if previously enabled).
#define osCriticalEnd() __osEnableIrq()


//
// __osSave:
//    Save address to resume current task at.
//
#ifdef __PCH__
#separate
void __osSave();
#endif


//
// __osRestore:
//    Resume execution at point task was suspended.
//
#ifdef __PCH__
#separate
void __osRestore();
#endif

//
// __osSchedule:
//    Find the highest priority task that is rtr (ready to run) an run it.
//

void __osSchedule(void);


//
// __osWait:
//    Suspend the calling task for some number of ticks.
//

void __osWait(UINT16 nTicks);


//
// __osWaitEvent:
//    Suspend the calling task till requested event happens.
//

void __osWaitEvent(UINT8 eventNumber, UINT16 nTicks);


//
// __osGetSema:
//    Try to get a semaphore. Suspend if not available.
//

void __osGetSema(UINT8 semaNumber, UINT16 nTicks);


//
// __osReleaseSema:
//    Give up a semaphore. Run next task that wants it.
//

void __osReleaseSema(UINT8 semaNumber);


//
// __osTimer:
//    Decrement wait timers.
//

void __osTimer(void);


//
// __osTaskInit:
//    Init task variables.
//

void __osTaskInit(UINT8 taskNumber);


//
// __osDisableIrq:
//    if interrupts are enabled, disable them
//

void __osDisableIrq(void);


//
// __osDisableIrq:
//    if interrupts were enabled (before we disabled them,
//    enable them again
//

void __osEnableIrq(void);


//
// osClearRtrCritical:
//    clear RTR bit.
//

void osClearRtrCritical(UINT8 task);


//
// osSetRtrCritical:
//    set RTR bit.
//

void osSetRtrCritical(UINT8 task);


//
// __osClearRtr:
//    clear RTR bit.
//

void __osClearRtr(UINT8 task);


//
// __osSetRtr:
//    set RTR bit.
//

void __osSetRtr(UINT8 task);


// osTimeout:
//    Returns TRUE if current task has a timer value of 0.
//    Can be used to see if a timeout happened while waiting
//    for an event or a semaphore.
//

BOOL osTimeout(void);


//
// osGetTicks:
//    Gets timer ticks for current task.
//

UINT16 osGetTicks(void);


//
// osClearTicks:
//    Clears timer ticks for current task.
//

void osClearTicks(void);


//
// osInit:
//    Init os static variables.
//

void osInit(void);


//
// osRun:
//    Start running the OS.
//

#ifdef __PCH__
#separate
#else
#inline
#endif
void osRun(void);


//
// osTaskInit:
//    Must be called for each task, before starting multitasking.
//

#ifdef __PCH__
#separate
#else
#inline
#endif
void osTaskInit(UINT8 taskNumber);


//
// osWait:
//    Suspends the calling task for some number of ticks (1 mSec).
//

#ifdef __PCH__
#separate
#else
#inline
#endif
void osWait(UINT16 ticks);


//
// osWaitEventTimeout:
//    Suspends the calling task till event happens or ticks expire.
//    Setting ticks to 0 causes the task to wait forever.
//

#ifdef __PCH__
#separate
#else
#inline
#endif
void osWaitEventTimeout(UINT8 eventNumber,UINT16 ticks);


//
// osWaitEvent:
//    Suspends the calling task till event happens.
//

#ifdef __PCH__
#separate
#else
#inline
#endif
void osWaitEvent(UINT8 eventNumber);


//
// osYield:
//    Resumes the highest priority task that is rtr. The rtr status of the calling
//    task is not changed - so if it is the highest priority rtr it immediately resumes.
//

#ifdef __PCH__
#separate
#else
#inline
#endif
void osYield(void);


//
// osSuspend:
//    Makes the calling task not rtr. Usually it will be made rtr at some future
//    time by an interrupt service routine or by another task.
//

#ifdef __PCH__
#separate
#else
#inline
#endif
void osSuspend();


//
// osGetSemaTimeout:
//    Makes the calling task the owner of the given semaphore,
//    if it is available. If the semaphore is not available the
//    task is suspended. When the semaphore becomes available
//    the highest priority task that is waiting for it is set rtr.
//    Note: this may result in a priority inversion, a low priority
//    task that has taken a semaphore will prevent a higher
//    priority task from running.
//    Setting ticks to 0 causes the task to wait forever.

#ifdef __PCH__
#separate
#else
#inline
#endif
void osGetSemaTimeout(UINT8 semaNumber,UINT16 ticks);


//
// osGetSema:
//    Makes the calling task the owner of the given semaphore,
//    if it is available. If the semaphore is not available the task is
//    suspended. When the semaphore becomes available the highest priority
//    task that is waiting for it is set rtr.
//    Note: this may result in a priority inversion, a low priority task that has
//    taken a semaphore will prevent a higher priority task from running.

#ifdef __PCH__
#separate
#else
#inline
#endif
void osGetSema(UINT8 semaNumber);


//
// osReleaseSema:
//    Releases the semaphore, which results in another task being set
//    rtr if another task is waiting for the semaphore. Of course a
//    task should not release a semaphore unless it owns it - no error
//    checking is done for this condition.

#ifdef __PCH__
#separate
#else
#inline
#endif
void osReleaseSema(UINT8 semaNumber);


//
// osMainTask:
//    This function is mostly to keep osRun executing at the same stack level
//    as other os functions. Some compilers optimize out the call to this and
//    just place the functions code inline.
//

void osMainTask(void);


//
// osSuspendTask:
//    Suspends a task. May be called by one task (or main program) to suspend
//    another task.
//

void osSuspendTask(UINT8 nTask);


//
// osCallAll:
//    Some compilers optimize out subroutine calls and returns for functions
//    called only once. By calling osCallAll once in your main() function,
//    someplace it will never run, you fake the compiler out and it does
//    subroutine calls for all essential os functions.
//

void osCallAll(void);
j_s_connell



Joined: 02 Feb 2004
Posts: 17

View user's profile Send private message

PostPosted: Tue Apr 05, 2005 5:11 pm     Reply with quote

What are the rules for using this rtos with other interrupts, say like external interrupts? Do i simply disable the timer interrupt (thus suspending the entire os) and process my interrupt? I don't want to poll in every case, but I will if i have to....

Other than that, great work. This thing runs like a dream and takes away 90% of the headache (and spaghetti code) of programming these pics. I would never have heard of it if you hadn't decided to port this to CCS.
mhahn@hvc.rr.com



Joined: 03 Jan 2005
Posts: 9
Location: upstate NY

View user's profile Send private message

PostPosted: Tue Apr 19, 2005 10:58 am     Reply with quote

There are no rules really, other than the ones in the comment block at the top of the file.

You should be able to use csRtos with any other interrupt. I've used it with eeprom, usart, and i2c interrupts all enabled and never seen any problems. CCS disables all other interrupts while it is running an interrupt service routine (isr).

I have found this to be an issue when I have lots of tasks and use the rtos timers. The osTimer routine can take quite a while to run, especially on a lower frequency clock. If you run osTimer from within your timer isr, you may block other interrupts long enough to miss things.

One solution is to have a very high priority task that gets started by your timer isr and have it call osTimer( ).

I have a few new tweaks to the os that make some of the timer and task switching code run a bit faster. I'll try to post them later this week.

Thanks for the positive feedback,
Mark
RobM



Joined: 29 Apr 2005
Posts: 13
Location: Santa Cruz, CA

View user's profile Send private message

PostPosted: Wed Jun 15, 2005 10:20 am     Reply with quote

I have succesfully compiled csRTOS for the PICDEM2 16F877, and it is running, although as a new CCS user I am having problems with more detailed evaluation of the suitability for my project.

mhahn@hvc.rr.com wrote:
I have found this to be an issue when I have lots of tasks and use the rtos timers. The osTimer routine can take quite a while to run, especially on a lower frequency clock. If you run osTimer from within your timer isr, you may block other interrupts long enough to miss things.


I don't understand why this would take long to run:
Code:
// __osTimer:
// Decrement wait timers.
// Note: this should be called from an interrupt service routine.
//

void __osTimer(void)
{
UINT8 ii;
UINT16 jj;

// decrement timers for any tasks that are waiting
for (ii = 0; ii < nTasks; ii++)
{
jj = __osTicks[ii];

if (jj)
{
jj--;

if (jj==0)
{
// this task is now ready to run.
__osSetRtrCritical(ii);
}

__osTicks[ii] = jj;
}
}
}


I am having difficulty with a few concepts, including understanding what the structure of a "real" program is. Do I put all of the code that will be scheduled in "tasks" and then communicate status (via semiphores) to the code in main?

After proper initialization (#use RS232) I added
Code:
   if((ii % 0x100)== 0) { printf("\rADC = %4lu", adcread());}



inside main and it prints to the terminal as expected. I have no idea what this is doing to the scheduler, and I will need to determine if other tasks are running duriing the serial send.

Any debug tips are appreciated.

RobM
_________________
Rob
_______
mhahn@hvc.rr.com



Joined: 03 Jan 2005
Posts: 9
Location: upstate NY

View user's profile Send private message

PostPosted: Thu Jun 16, 2005 5:34 am     Reply with quote

The timer code is pretty slow. Look at the assembly listsing to see how many instructions something as simple as jj = __osTicks[ii] compiles to. If you have more than a pretty small number of tasks (I think it's like 8 or 9) it will take longer than 1 millisecond to process the 1 millisecond timer tick (this is a bad thing). Think about it, with a 4 MHz clock (1 million instructions per second), you can only execute 1000 instructions in 1 millisecond.

With regards to figuring out how to structure your program: The example code is a pretty bad example of how to write tasks. If you've never used a rtos before, you need to do a bit of reading. Take a look at Glen Worstell's original article (it's url is in the top comment block in rtos.h & rtos.c). Microchip has a couple of app notes on rtoses. If you've never used a priority based rtos before, I'd suggest you start using csRtos with round robin based scheduling.

Your printf statement in main won't hurt anything, but it will affect how quickly a tasks runs after it has been set ready to run (called task latency for those who've never used an rtos). The printf function, as supplied by ccs, will sit in a loop waiting for the uart (if you specified to use the hardware uart) to send each character in the string you are sending. This can take a while. At 9600 baud you are sending about 1 character per millisecond. Your example printf will take at least 12 milliseconds to be sent. During that time no tasks will run. If you have a task that needs to respond to some event faster than that ...

I'm in the process of re-writing the CCS version of csRtos. I finished a port of csRtos to the microchip C18 compiler last week and want to incorporate some improvements I made.

Mark
DigiGuy



Joined: 30 Aug 2005
Posts: 1
Location: Arizona

View user's profile Send private message

Moving to 20mhz base PIC
PostPosted: Tue Aug 30, 2005 8:01 pm     Reply with quote

I am attempting to run your RTOS on a 20mhz PIC. It appears more than just changing the clock speed to #use DELAY( CLOCK=20000000) is required. Any suggestions?

Thanks in advance

Very Happy Carl
RobM



Joined: 29 Apr 2005
Posts: 13
Location: Santa Cruz, CA

View user's profile Send private message

20MHz 16F877 - csRTOS working
PostPosted: Wed Aug 31, 2005 10:11 am     Reply with quote

Hi! I am using csRTOS on a PICDEM2 with a PIC16F877 at 20MHz.

I modified the code:
Code:
#define OSC 20000000
 /* 20.00Mhz clock on PICDEM2 */
#use DELAY(CLOCK=OSC)


// the +18 in the defines below is to handle the time it
// takes to add the value of timer0 to our delay value
#define MS_DELAY (long) (0x10000 + 80 - .001*OSC/4  )


And it works.
I defined OSC so I could use the oscillator frequency to calculate the MS delay value. There may be a preproccesor variable for OSC (like _OSC ?), but I don't know how to access it.

In any case, the above calculation for MS_DELAY should work for any oscillator speed. The comment says that there is a fudge factor of +18, but I had to add 80 to make it produce 5E-3 intervals.

The MS_DELAY constant is a number used to seed a timer so that when the timer counts up it produces a 1mSec delay. the number of counts up is
counts = delay_time *counter_f
because the counter is the OSC/4 we use .001*OSC/4
To get the seed for the counter you have to subtract the counts from the roll-over value of the 16 bit counter which is 0x10000.
Then you have to add a fudge factor because it taks time to load the register.

Notice that mark was very clever in
Code:
#INT_TIMER0

void timeIsr    (void)
{
   UINT16 delay;

   // calculate when to wake up next
   delay = get_timer0    () + MS_DELAY;

   set_timer0    (delay);

   // update task timers
   __osTimer    ();
}
He doen't just stuff MS_DELAY into the timer, he adds the current value to the delay. Why? Imagine the counter has rolled over, but it takes an indeterminate time to answer the interupt. The counter continues to count, and the value (when you finally get around to servicing the interrupt) is how late you are. Adding that value to the seed for the counter will Shorten the time by that much. This keeps the interrupt latency from accumulating, and provides more stable long term timing.

More than you wanted to know, but change the MS_DELAY defines and it should work.
_________________
Rob
_______
RobM



Joined: 29 Apr 2005
Posts: 13
Location: Santa Cruz, CA

View user's profile Send private message

csRtos - new LCD driver for PICDEM2
PostPosted: Sun Sep 04, 2005 12:19 pm     Reply with quote

mhahn@hvc.rr.com wrote:
csRtos is a simple salvo-like rtos for small microcontrollers. It was developed by Glen Worstell. You can find more info on it at:
http://www.circuitcellar.com/avr2004/DA3650.html

Over the holidays I ported csRtos to work with the CCS compiler. I've tested this with some simple tasks on a picdem2 board and I've ported a client's application to use csRtos.


I have been working with this code from Mark, and have had very few problems. I will present here a modified example that encorporates the LCD and further demonstrates how tasks can share resources using events and sema. In this code the LCD buffer access protected by LCDSEMA. When a task owns the semaphore it is allowed to alter the buffer and get the LCD driver's attention, thus:
Code:
osGetSema    (LCDSEMA);
      strcpy    (LcdStr,"\r         \nLED off ");
      osPOSTevent    (LCDREQEVENT);
      osWaitEvent(LCDDONEEVENT) ;
      osReleaseSema(LCDSEMA);


I have also tried to condense the rules for using this code, and offer them here.
Rules for Creating New Tasks
Each task needs initialization. Here are the rules to create a new task.

1) Add the task label to the task list in the form <name>TaskNumber, where "name" is descriptive of the task.
In the examples the function Led1Task is associated with the label Led1TaskNumber

2) Make a function definition for the task, in the form <name>Task
All of the following elements are required:
Code:
void newTask(void)
{    <initialize hardware etc> //optional, see adcTask in the example
    osTaskInit(newTaskNumber);
    if (osNotReady)  {  return;  }
    <code runs at startup> // optional "run once" code,
    while(TRUE)
    {
       //body of task inside infinite loop.
      // The execution can be suspended, but the loop is never exited.
    }
}


Each task has a call to osTaskInit() to execute the run-time OS initialization. Each task may require other initialization,
as seen in the example function adcTask()


4) in main, after the other initialization is complete, add newTask() to the other task function calls.

5)After enable interrupts with enable_interrupts(GLOBAL),
add and osSetRtr(newTaskNumber); to the other osSetRtr calls
Alternately, one or several task could be setRtr, and they in turn might begin the remaining tasks as conditions warrant. An example would be a task that parses an input string. This task would not be made ready to run until a CRLF had been received, and it would be suspended when the task was complete.

6) Don't forget to add any new Sema or Events to their respective lists.

Use caution adding anything to the main loop, it change the task switch latency, and it will only execute when no tasks are ready to run. Main is a good place to toggle an output bit as a monitor, or to perform a background task...I'd like to see someone experiment with sleep.

Suggestions are invited.

Here's how to try it...
The code which follows are the files:
csRTOS LCD1.c the main file for this example
PICDEM2_LCD.C the LCD sourde code, this is a
modified version of code found
elswhere in theis forum
rtos.h USE THE ORIGINAL
COPY rtos.c to rtos_rm1.c and add the folloing to the end:
Code:
//
// osLCDsend(UINIT8 string) this macro sends a string to the LCD
// The caller waits for the  LCDSEMA
// This task uses osPostEvent(LCDDONEEVENT) to alert the caller.
// the flaw is that the caller can't run until this is done
// too bad there isn't a way to "pass" a SEMA
#define  osLCDsend(lcdstr) \
{    int idx ;              \
     idx = 0;                \
     while(lcdstr[idx])         \
     {   osYield();            \
          if(!bit_test(LCD_Read_Byte(),7) ) \
             LCD_putC(lcdstr[idx++] );   \
     }                                 \
     osPostEvent(LCDdoneEvent);        \
}



Good luck! Please submit your questions and comments.
_________________
Rob
_______
RobM



Joined: 29 Apr 2005
Posts: 13
Location: Santa Cruz, CA

View user's profile Send private message

Re: csRtos - new LCD driver for PICDEM2
PostPosted: Sun Sep 04, 2005 12:25 pm     Reply with quote

duplicate deleted

Last edited by RobM on Sun Sep 04, 2005 12:40 pm; edited 1 time in total
RobM



Joined: 29 Apr 2005
Posts: 13
Location: Santa Cruz, CA

View user's profile Send private message

Re: csRtos - new LCD driver for PICDEM2
PostPosted: Sun Sep 04, 2005 12:26 pm     Reply with quote

Code:
//*****************************************************************************
//
// csRtos test program for evaluation of LCD
// ~~~~~~~~~~~~~~~~Push Button to see LCD output ~~~~~~~~~~~~~~~~~~
//
// file: csRTOS LCD1.c
// 9/3/2005 posted to CCS forum by RM
// This code is being released into the public domain with no restrictions.
//
// 8/30/2005 tested with PIC16F877 on PICDEM2 - RM
// 8/22/2005 added switch to LCD_Send_Byte to allow the wait to be disabled.
//    This allows cdRTOS to send data in a task and use OS waits
//    Added LcdWriteTask and I am talking to it from LED3task
//    When it works I will prove the concept  - RM
// 8/24 task switching failure because initialize in LCDwritetask was after "if ( osNotReady ) "  - RM
// 8/25 change LCDwriteTask name for consistancy, beautify code  - RM
//      
//
//************************ parent code notes: *********************************
//
// The following by Mark R Hahn , mhahn@hvc.rr.com
// main.c - contains main() and some sample tasks
// main.h - defines used by main.c
// rtos.c - the meat of the rtos code
// rtos.h - defines for rtos.h
//
// This code is being released into the public domain with no restrictions.
//
//
// As mentioned in rtos.c, csRtos was developed by Glen Worstell for the
// Atmel AVR. You should probably read his Circuit Cellar contest entry:
// http://www.circuitcellar.com/avr2004/DA3650.html
// snip
// Written 12/26/04 ... MRH
// Ported to 16F877 1/2/05 ... MRH
//
//*****************************************************************************
// project description:
// Test of the port of csRtos to the PICDEM2 board. Modify for actual hardware when
// it becomes available.
// Incorporate PICDEM2LCD.c from CCS forum, modify to work with crRTOS
// This code was compiled with the CCS C compiler PCM version 3.222
//
// 6/10/2005 goal is to add the serial port and some one character command structure
// then evaluate for further usefulness
//
//*****************************************************************************
//
//
// this <should> work with either a PIC16F877 or a PIC18F452
// uncomment one of the defines below to select a processor
//#define PIC18
#define PIC16

// standard compiler header for selected processor
// must be first include
#ifdef PIC18
#include <18f452.h>
#else
#include <16f877.h>
#device *=16  // include for f452?
#endif

#ignore_warnings 202, 203

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EEPROM ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#define ID_string 0x2100
#rom ID_string = {"csRTOS LCD 0.2 R.Milby August 30, 2005"}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// compiler specific directives
//

// compiler specific directive for use with ICD
#DEVICE ICD=TRUE
//#DEVICE ICD=FALSE

// have AtoD routines return 10 bit values
#DEVICE ADC=10

#define OSC 20000000
 /* 20.00Mhz clock on PICDEM2 */
#use DELAY(CLOCK=OSC)

// the +18 in the defines below is to handle the time it
// takes to add the value of timer0 to our delay value
#define MS_DELAY (long) (0x10000 + 80 - .001*OSC/4  )

// use crystal oscilator - XT
// disable watchdog for now - NOWDT
// disable low voltage programming - NOLVP
// disable brownout detect - NOBROWNOUT
// disable power up timer - NOPUT
// disable code protection - NOPROTECT
// enable ICD - DEBUG
//#fuses XT,NOWDT,NOLVP,NOBROWNOUT,PUT,NOPROTECT,DEBUG
#fuses XT,NOWDT,NOLVP,NOBROWNOUT,PUT,NOPROTECT

// using fast_io saves a bit of rom and time
// just be sure to explicitly set IO pin direction
#use FAST_IO(A)
#use FAST_IO(B)
#use FAST_IO(C)
//#use FAST_IO(D)
#use standard_io(D)
#use FAST_IO(E)
   
// includes that describe system wide definintions
#include <csRTOS.h>

 

// Note: you must define the number of tasks (nTasks), as well as the
// number of semaphores (nSema), and the number of events (nEvents).
// These defines should be non-zero.
// If you do not wish to use semaphores or events, leave nSema or nEvents
// undefined.

// list all task numbers here in priority order, highest priority first
enum {
       led1TaskNumber,led2TaskNumber,led3TaskNumber,
       adcTaskNumber,buttonTaskNumber,LcdWriteTaskNumber,nTasks
     };

// Semaphores are defined here. Each semaphore uses two bytes: one
// has a bit set for each task waiting for the semaphore, and the
// other contains the number of the task that owns the semaphore.
// If the semaphore is available, the owner byte will be 255.
// Semaphores are numbered sequentially from zero.

enum {    SOUTSEMA,LCDSEMA,nSemas };

// Events are defined here. Each event uses one byte, one
// bit set for each task waiting for the event to happen.
// Events are numbered sequentially from zero.

enum {    KEYPRESSEVENT,KEYRELEASEEVENT,LCDREQEVENT,LCDDONEEVENT,nEvents };

// include os macros and definitions
#include "rtos.h"
#include "rtos_rm1.c"

//#include <lcd.c>
#include <PICDEM2_LCD.C>
//
// initIoPins:
// Set all IO pins to their proper state.
//

void initIoPins    (void)
{

   // set io direction
   // 0 = output, 1 = input

   _TRISA = 0b00010001; // a0, a4 are inputs
   _TRISB = 0b00000001; // b0 is input
   _TRISC = 0b10011011; // c0, c1, c3, c5, c7 are inputs
   _TRISD = 0b00000000; // d is all outputs
   _TRISE = 0b00000000; // e is all outputs

#ifdef __PCH__

   // set all outputs to 0
   _LATA = 0b00000000;
   _LATB = 0b00000000;
   _LATC = 0b00000000;
   _LATD = 0b00000000;
   _LATE = 0b00000000;

#else

   // set all outputs to 0
   _PORTA = 0b00000000;
   _PORTB = 0b00000000;
   _PORTC = 0b00000000;
   _PORTD = 0b00000000;
   _PORTE = 0b00000000;

#endif
}


#ifdef __PCH__

//
// timeIsr:
// ISR that updates 1 mSec system timers. Uses timer 0.
// Keep this as short as possible, to keep interrupt latency
// as short as possible.
//

#INT_TIMER0

void timeIsr    (void)
{
   UINT16 delay;

   // calculate when to wake up next
   delay = get_timer0    () + MS_DELAY;

   set_timer0    (delay);

   // update task timers
   __osTimer    ();
}


//
// timeInit:
// Initialize timer task and variables
//

void timeInit    (void)
{
   setup_timer_0    (RTCC_INTERNAL | RTCC_DIV_1);
   enable_interrupts    (INT_TIMER0);
}

#else

// this is redundant, I move it outside the if #define MS_DELAY (64536+18)

//
// timeIsr:
// ISR that updates 1 mSec system timers. Uses timer 1.
// Keep this as short as possible, to keep interrupt latency
// as short as possible.
//

#INT_TIMER1

void timeIsr    (void)
{
   UINT16 delay;

   // calculate when to wake up next
   delay = get_timer1    () + MS_DELAY;

   set_timer1    (delay);

   // update task timers
   __osTimer    ();
}


//
// timeInit:
// Initialize timer task and variables
//

void timeInit    (void)
{
   setup_timer_1    (T1_INTERNAL | T1_DIV_BY_1);
   enable_interrupts    (INT_TIMER1);
}

#endif

//~~~~~~~~~~~~~~~~~~~~~~~ Serial buffer functions and interrupt code ~~~~~~~~~~~~~~

#use rs232(baud=19200, xmit=PIN_C6, rcv=PIN_C7)

int8 buffer[40];
int8 next_in  = 0;
int8 next_out = 0;

#INT_RDA
void serial_isr    ()
{
   int8 t;

   buffer[next_in] = getch    ();
   t = next_in;
   next_in = (next_in+1) % sizeof(buffer);
   if( next_in == next_out )
      next_in=t;        // Buffer full !!
}

#define bkbhit (next_in != next_out)

int8 bgetc    ()
{
   int8 c;

   while( !bkbhit );
   c = buffer[next_out];
   next_out = (next_out+1) % sizeof(buffer);
   return c;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// some sample tasks
// led1Task - blink led1
// led2Task - blink led2
// led3Task - lite led3 when s3 is pressed
//

// include the task for LED
//#include <csRTOSinitLCD.inc>

void led1Task    (void)
{
   osTaskInit    (led1TaskNumber);

   if ( osNotReady )
      return;

   while ( TRUE )
   {
      led1Pin = TRUE;
      osWait    (200);

      led1Pin = FALSE;
      osWait    (450);
   }
}


void led2Task    (void)
{
   osTaskInit    (led2TaskNumber);

   if ( osNotReady )
      return;

   while ( TRUE )
   {
      led2Pin = TRUE;
      osWait    (200);

      led2Pin = FALSE;
      osWait    (455);
   }
}


void led3Task    (void)
{
   osTaskInit    (led3TaskNumber);

   if ( osNotReady )
   {   
      return;
   }
      //osYIELD();  // this does not work
      osWAIT(1);   // this works
      led3Pin = TRUE;
      osGetSema    (LCDSEMA);
      strcpy    (LcdStr,"\f"); // LcdPadOut()  function needed
      //osWAIT(1);      // or it works if you put it here, but
      // osYIELD();   // doesn't work
      osPOSTevent    (LCDREQEVENT);
      osWaitEvent(LCDDONEEVENT) ;
      osReleaseSema(LCDSEMA);
      led3Pin = FALSE;
   while ( TRUE )
   {

      osWaitEvent    (KEYPRESSEVENT);
      led3Pin = TRUE;

      osGetSema(LCDSEMA);
//      LcdDelay = 4 ; // might help
      strcpy(LcdStr,"\r                "); // LcdPadOut()  function needed
      osPOSTevent    (LCDREQEVENT);
      osWaitEvent(LCDDONEEVENT) ;
      osReleaseSema(LCDSEMA);

      osGetSema(LCDSEMA);
 //     LcdDelay = 4 ; // might help
      lcd_gotoxy(1,1); // is this safe?
      strcpy(LcdStr,"\rLED on         "); // LcdPadOut()  function needed
      osPOSTevent    (LCDREQEVENT);
      osWaitEvent(LCDDONEEVENT) ;
      osReleaseSema(LCDSEMA);

      osWaitEvent    (KEYRELEASEEVENT);
      led3Pin = FALSE;
      osGetSema    (LCDSEMA);
      strcpy    (LcdStr,"\r         \nLED off ");
      osPOSTevent    (LCDREQEVENT);
      osWaitEvent(LCDDONEEVENT) ;
      osReleaseSema(LCDSEMA);

   }
}



#define ADC_NUM_SAMPLES (16)

// array of adc samples
UINT16 adcSamples[ADC_NUM_SAMPLES];

// index into input arrays
UINT8 adcSampleIndex;

// wait this many cycles befoe doing next adc read
#define ADC_READ_DELAY (4)


//
// adcTask:
// Task that runs A to D converter and updates reading of AN0.
//

void adcTask    (void)
{
   // initialize adc hardware
   setup_adc_ports    (AN0);

   setup_adc    (ADC_CLOCK_DIV_8);

   set_adc_channel    (0);

   // initialize up variables used to average input
   for ( adcSampleIndex = 0; adcSampleIndex < ADC_NUM_SAMPLES; adcSampleIndex++ )
   {
      adcSamples[adcSampleIndex] = 0;
   }

   adcSampleIndex = 0;

   // init task
   osTaskInit    (adcTaskNumber);

   if ( osNotReady )
      return;

   // main loop of task
   while( TRUE )
   {
      // give adc mux time to settle
      // you can set the rate that ad readings get updated here
      osWait    (6);

      adcSamples[adcSampleIndex] = read_adc    (ADC_START_AND_READ );

      adcSampleIndex++;

      if ( adcSampleIndex >= ADC_NUM_SAMPLES )
      {
         adcSampleIndex = 0;
      }

   }
}


//
// adcRead:
// Returns an averaged AN0 adc reading.
//

UINT16 adcRead    (void)
{
   UINT16 sum;
   UINT8 ii;

   sum = 0;

   for ( ii=0; ii<ADC_NUM_SAMPLES; ii++ )
   {
      sum += adcSamples[ii];
   }

   return(sum/ADC_NUM_SAMPLES);
}


//
// buttonTask:
// Posts an event when the switch S3 changes state.
void buttonTask    (void)
{
   static UINT8 debounceCount;

   osTaskInit    (buttonTaskNumber);

   if ( osNotReady )
      return;

   while ( TRUE )
   {
wait_press:
      while ( inSw3 != 0 )
      {
         osWait    (20);
      }

      debounceCount = 50;

      while ( inSw3 == 0 )
      {
         debounceCount--;
         if ( debounceCount == 0 )
         {
            break;
         }
         osWait    (2);
      }

      if ( debounceCount > 0 )
      {
         goto wait_press;
      }

      osPostEvent(KEYPRESSEVENT);

wait_release:

      while ( inSw3 == 0 )
      {
         osWait    (20);
      }

      debounceCount = 50;

      while ( inSw3 != 0 )
      {
         debounceCount--;
         if ( debounceCount == 0 )
         {
            break;
         }
         osWait    (2);
      }

      if ( debounceCount > 0 )
      {
         goto wait_release;
      }

      osPostEvent    (KEYRELEASEEVENT);
   }
}

// this task will release until it comes up in the que again,
// then check ready
// it communicates to the LCD routines through semaphore Lcd_Sem
void LcdWriteTask    (void)
{
   osTaskInit(LcdWriteTaskNumber);

   if ( osNotReady )
      return;

   while( TRUE )
   {
      osWAITevent(LCDREQEVENT);
      osLCDsend(LcdStr) ;
      osPostEvent(LCDDONEEVENT) ;
   }
}
//
//
// main:
//    main:
//       main:
//          main:
//             main:    Everybody's favorite C function.
//          main:
//       main:
//    main:
// main:
//
//

void main    (void)
{

   UINT32 ii;

   int c , i;

   //
   // do hardware initializations
   initIoPins    ();

   //#use rs232(baud=19200, xmit=PIN_C6, rcv=PIN_C7)

   //~~~~~~~~~LCD init ~~~~~~~~~~
   // it uses LCD_send_byte with WAITBUSY
   lcd_init    ();

   timeInit    ();


   // init os variables
   // call before calling tasks
   osInit    ();




   // start tasks, they run till they hit osTaskInit()
   led1Task    ();
   led2Task    ();
   led3Task    ();
   adcTask    ();
   buttonTask    ();
   LcdWriteTask    ();
   // now that we are initialized, enable all interrupts
   enable_interrupts    (GLOBAL);

   // Set some tasks ready to run.
   osSetRtr    (led1TaskNumber);
   osSetRtr    (led2TaskNumber);
   osSetRtr    (led3TaskNumber);
   osSetRtr    (adcTaskNumber);
   osSetRtr    (buttonTaskNumber);
   osSetRtr    (LcdWriteTaskNumber);
   puts    ("\n");
   c = '\r' ;
   for( i = 0; c ;i++ )
   {
      putc    (c);
      c = read_eeprom    (i);
   }
 
   printf    ("\r\nMS_DELAY seed = 0x%LX\r\n",MS_DELAY) ;


   printf    ("\r\n\nPotentiometer RA0 value:\r\n");
   ii = 0;
   // main loop
   while( TRUE )
   {
      // run the OS
      osMainTask    ();

      // note: when no tasks are ready to run, ii gets incremented
      ii++;
      if((ii % 0x2000)== 0) { printf("\rADC = %4lu", adcread());}

   }

#ifdef __PCH__
   // should never get here
   // this call forces the compiler to treat key os functions as subroutines
   osCallAll    ();
#endif
}


RobM



Joined: 29 Apr 2005
Posts: 13
Location: Santa Cruz, CA

View user's profile Send private message

Re: csRtos - new LCD driver for PICDEM2
PostPosted: Sun Sep 04, 2005 12:33 pm     Reply with quote

Code:
//*****************************************************************************
//
// csRtos test program for evaluation of LCD
// ~~~~~~~~~~~~~~~~Push Button to see LCD output ~~~~~~~~~~~~~~~~~~
//
// file: csRTOS LCD1.c
// 9/3/2005 posted to CCS forum by RM
// This code is being released into the public domain with no restrictions.
//
// 8/30/2005 tested with PIC16F877 on PICDEM2 - RM
// 8/22/2005 added switch to LCD_Send_Byte to allow the wait to be disabled.
//    This allows cdRTOS to send data in a task and use OS waits
//    Added LcdWriteTask and I am talking to it from LED3task
//    When it works I will prove the concept  - RM
// 8/24 task switching failure because initialize in
//       LCDwritetask was after "if (osNotReady ) "  - RM
// 8/25 change LCDwriteTask name for consistancy, beautify code  - RM
//      
//
//************************ parent code notes: *********************************
//
// The following by Mark R Hahn , mhahn@hvc.rr.com
// main.c - contains main() and some sample tasks
// main.h - defines used by main.c
// rtos.c - the meat of the rtos code
// rtos.h - defines for rtos.h
//
// This code is being released into the public domain with no restrictions.
//
//
// As mentioned in rtos.c, csRtos was developed by Glen Worstell for the
// Atmel AVR. You should probably read his Circuit Cellar contest entry:
// http://www.circuitcellar.com/avr2004/DA3650.html
// snip
// Written 12/26/04 ... MRH
// Ported to 16F877 1/2/05 ... MRH
//
//*****************************************************************************
// project description:
// Test of the port of csRtos to the PICDEM2 board. Modify for actual hardware when
// it becomes available.
// Incorporate PICDEM2LCD.c from CCS forum, modify to work with crRTOS
// This code was compiled with the CCS C compiler PCM version 3.222
//
// 6/10/2005 goal is to add the serial port and some one character command structure
// then evaluate for further usefulness
//
//*****************************************************************************
//
//
// this <should> work with either a PIC16F877 or a PIC18F452
// uncomment one of the defines below to select a processor
//#define PIC18
#define PIC16

// standard compiler header for selected processor
// must be first include
#ifdef PIC18
#include <18f452.h>
#else
#include <16f877.h>
#device *=16  // include for f452?
#endif

#ignore_warnings 202, 203

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EEPROM ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#define ID_string 0x2100
#rom ID_string = {"csRTOS LCD 0.2 R.Milby August 30, 2005"}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// compiler specific directives
//

// compiler specific directive for use with ICD
#DEVICE ICD=TRUE
//#DEVICE ICD=FALSE

// have AtoD routines return 10 bit values
#DEVICE ADC=10

#define OSC 20000000
 /* 20.00Mhz clock on PICDEM2 */
#use DELAY(CLOCK=OSC)

// the +18 in the defines below is to handle the time it
// takes to add the value of timer0 to our delay value
#define MS_DELAY (long) (0x10000 + 80 - .001*OSC/4  )

// use crystal oscilator - XT
// disable watchdog for now - NOWDT
// disable low voltage programming - NOLVP
// disable brownout detect - NOBROWNOUT
// disable power up timer - NOPUT
// disable code protection - NOPROTECT
// enable ICD - DEBUG
//#fuses XT,NOWDT,NOLVP,NOBROWNOUT,PUT,NOPROTECT,DEBUG
#fuses XT,NOWDT,NOLVP,NOBROWNOUT,PUT,NOPROTECT

// using fast_io saves a bit of rom and time
// just be sure to explicitly set IO pin direction
#use FAST_IO(A)
#use FAST_IO(B)
#use FAST_IO(C)
//#use FAST_IO(D)
#use standard_io(D)
#use FAST_IO(E)
   
// includes that describe system wide definintions
#include <csRTOS.h>

 

// Note: you must define the number of tasks (nTasks), as well as the
// number of semaphores (nSema), and the number of events (nEvents).
// These defines should be non-zero.
// If you do not wish to use semaphores or events, leave nSema or nEvents
// undefined.

// list all task numbers here in priority order, highest priority first
enum {
       led1TaskNumber,led2TaskNumber,led3TaskNumber,
       adcTaskNumber,buttonTaskNumber,LcdWriteTaskNumber,nTasks
     };

// Semaphores are defined here. Each semaphore uses two bytes: one
// has a bit set for each task waiting for the semaphore, and the
// other contains the number of the task that owns the semaphore.
// If the semaphore is available, the owner byte will be 255.
// Semaphores are numbered sequentially from zero.

enum {    SOUTSEMA,LCDSEMA,nSemas };

// Events are defined here. Each event uses one byte, one
// bit set for each task waiting for the event to happen.
// Events are numbered sequentially from zero.

enum {    KEYPRESSEVENT,KEYRELEASEEVENT,LCDREQEVENT,LCDDONEEVENT,nEvents };

// include os macros and definitions
#include "rtos.h"
#include "rtos_rm1.c"

//#include <lcd.c>
#include <PICDEM2_LCD.C>
//
// initIoPins:
// Set all IO pins to their proper state.
//

void initIoPins    (void)
{

   // set io direction
   // 0 = output, 1 = input

   _TRISA = 0b00010001; // a0, a4 are inputs
   _TRISB = 0b00000001; // b0 is input
   _TRISC = 0b10011011; // c0, c1, c3, c5, c7 are inputs
   _TRISD = 0b00000000; // d is all outputs
   _TRISE = 0b00000000; // e is all outputs

#ifdef __PCH__

   // set all outputs to 0
   _LATA = 0b00000000;
   _LATB = 0b00000000;
   _LATC = 0b00000000;
   _LATD = 0b00000000;
   _LATE = 0b00000000;

#else

   // set all outputs to 0
   _PORTA = 0b00000000;
   _PORTB = 0b00000000;
   _PORTC = 0b00000000;
   _PORTD = 0b00000000;
   _PORTE = 0b00000000;

#endif
}


#ifdef __PCH__

//
// timeIsr:
// ISR that updates 1 mSec system timers. Uses timer 0.
// Keep this as short as possible, to keep interrupt latency
// as short as possible.
//

#INT_TIMER0

void timeIsr    (void)
{
   UINT16 delay;

   // calculate when to wake up next
   delay = get_timer0    () + MS_DELAY;

   set_timer0    (delay);

   // update task timers
   __osTimer    ();
}


//
// timeInit:
// Initialize timer task and variables
//

void timeInit    (void)
{
   setup_timer_0    (RTCC_INTERNAL | RTCC_DIV_1);
   enable_interrupts    (INT_TIMER0);
}

#else

// this is redundant, I move it outside the if #define MS_DELAY (64536+18)

//
// timeIsr:
// ISR that updates 1 mSec system timers. Uses timer 1.
// Keep this as short as possible, to keep interrupt latency
// as short as possible.
//

#INT_TIMER1

void timeIsr    (void)
{
   UINT16 delay;

   // calculate when to wake up next
   delay = get_timer1    () + MS_DELAY;

   set_timer1    (delay);

   // update task timers
   __osTimer    ();
}


//
// timeInit:
// Initialize timer task and variables
//

void timeInit    (void)
{
   setup_timer_1    (T1_INTERNAL | T1_DIV_BY_1);
   enable_interrupts    (INT_TIMER1);
}

#endif

//~~~~~~~~~~~~~~~~~~~~~~~ Serial buffer functions and interrupt code ~~~~~~~~~~~~~~

#use rs232(baud=19200, xmit=PIN_C6, rcv=PIN_C7)

int8 buffer[40];
int8 next_in  = 0;
int8 next_out = 0;

#INT_RDA
void serial_isr    ()
{
   int8 t;

   buffer[next_in] = getch    ();
   t = next_in;
   next_in = (next_in+1) % sizeof(buffer);
   if( next_in == next_out )
      next_in=t;        // Buffer full !!
}

#define bkbhit (next_in != next_out)

int8 bgetc    ()
{
   int8 c;

   while( !bkbhit );
   c = buffer[next_out];
   next_out = (next_out+1) % sizeof(buffer);
   return c;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// some sample tasks
// led1Task - blink led1
// led2Task - blink led2
// led3Task - lite led3 when s3 is pressed
//

// include the task for LED
//#include <csRTOSinitLCD.inc>

void led1Task    (void)
{
   osTaskInit    (led1TaskNumber);

   if ( osNotReady )
      return;

   while ( TRUE )
   {
      led1Pin = TRUE;
      osWait    (200);

      led1Pin = FALSE;
      osWait    (450);
   }
}


void led2Task    (void)
{
   osTaskInit    (led2TaskNumber);

   if ( osNotReady )
      return;

   while ( TRUE )
   {
      led2Pin = TRUE;
      osWait    (200);

      led2Pin = FALSE;
      osWait    (455);
   }
}


void led3Task    (void)
{
   osTaskInit    (led3TaskNumber);

   if ( osNotReady )
   {   
      return;
   }
      //osYIELD();  // this does not work
      osWAIT(1);   // this works
      led3Pin = TRUE;
      osGetSema    (LCDSEMA);
      strcpy    (LcdStr,"\f"); // LcdPadOut()  function needed
      //osWAIT(1);      // or it works if you put it here, but
      // osYIELD();   // doesn't work
      osPOSTevent    (LCDREQEVENT);
      osWaitEvent(LCDDONEEVENT) ;
      osReleaseSema(LCDSEMA);
      led3Pin = FALSE;
   while ( TRUE )
   {

      osWaitEvent    (KEYPRESSEVENT);
      led3Pin = TRUE;

      osGetSema(LCDSEMA);
//      LcdDelay = 4 ; // might help
      strcpy(LcdStr,"\r                "); // LcdPadOut()  function needed
      osPOSTevent    (LCDREQEVENT);
      osWaitEvent(LCDDONEEVENT) ;
      osReleaseSema(LCDSEMA);

      osGetSema(LCDSEMA);
 //     LcdDelay = 4 ; // might help
      lcd_gotoxy(1,1); // is this safe?
      strcpy(LcdStr,"\rLED on         "); // LcdPadOut()  function needed
      osPOSTevent    (LCDREQEVENT);
      osWaitEvent(LCDDONEEVENT) ;
      osReleaseSema(LCDSEMA);

      osWaitEvent    (KEYRELEASEEVENT);
      led3Pin = FALSE;
      osGetSema    (LCDSEMA);
      strcpy    (LcdStr,"\r         \nLED off ");
      osPOSTevent    (LCDREQEVENT);
      osWaitEvent(LCDDONEEVENT) ;
      osReleaseSema(LCDSEMA);

   }
}



#define ADC_NUM_SAMPLES (16)

// array of adc samples
UINT16 adcSamples[ADC_NUM_SAMPLES];

// index into input arrays
UINT8 adcSampleIndex;

// wait this many cycles befoe doing next adc read
#define ADC_READ_DELAY (4)


//
// adcTask:
// Task that runs A to D converter and updates reading of AN0.
//

void adcTask    (void)
{
   // initialize adc hardware
   setup_adc_ports    (AN0);

   setup_adc    (ADC_CLOCK_DIV_8);

   set_adc_channel    (0);

   // initialize up variables used to average input
   for ( adcSampleIndex = 0; adcSampleIndex < ADC_NUM_SAMPLES; adcSampleIndex++ )
   {
      adcSamples[adcSampleIndex] = 0;
   }

   adcSampleIndex = 0;

   // init task
   osTaskInit    (adcTaskNumber);

   if ( osNotReady )
      return;

   // main loop of task
   while( TRUE )
   {
      // give adc mux time to settle
      // you can set the rate that ad readings get updated here
      osWait    (6);

      adcSamples[adcSampleIndex] = read_adc    (ADC_START_AND_READ );

      adcSampleIndex++;

      if ( adcSampleIndex >= ADC_NUM_SAMPLES )
      {
         adcSampleIndex = 0;
      }

   }
}


//
// adcRead:
// Returns an averaged AN0 adc reading.
//

UINT16 adcRead    (void)
{
   UINT16 sum;
   UINT8 ii;

   sum = 0;

   for ( ii=0; ii<ADC_NUM_SAMPLES; ii++ )
   {
      sum += adcSamples[ii];
   }

   return(sum/ADC_NUM_SAMPLES);
}


//
// buttonTask:
// Posts an event when the switch S3 changes state.
void buttonTask    (void)
{
   static UINT8 debounceCount;

   osTaskInit    (buttonTaskNumber);

   if ( osNotReady )
      return;

   while ( TRUE )
   {
wait_press:
      while ( inSw3 != 0 )
      {
         osWait    (20);
      }

      debounceCount = 50;

      while ( inSw3 == 0 )
      {
         debounceCount--;
         if ( debounceCount == 0 )
         {
            break;
         }
         osWait    (2);
      }

      if ( debounceCount > 0 )
      {
         goto wait_press;
      }

      osPostEvent(KEYPRESSEVENT);

wait_release:

      while ( inSw3 == 0 )
      {
         osWait    (20);
      }

      debounceCount = 50;

      while ( inSw3 != 0 )
      {
         debounceCount--;
         if ( debounceCount == 0 )
         {
            break;
         }
         osWait    (2);
      }

      if ( debounceCount > 0 )
      {
         goto wait_release;
      }

      osPostEvent    (KEYRELEASEEVENT);
   }
}

// this task will release until it comes up in the que again,
// then check ready
// it communicates to the LCD routines through semaphore Lcd_Sem
void LcdWriteTask    (void)
{
   osTaskInit(LcdWriteTaskNumber);

   if ( osNotReady )
      return;

   while( TRUE )
   {
      osWAITevent(LCDREQEVENT);
      osLCDsend(LcdStr) ;
      osPostEvent(LCDDONEEVENT) ;
   }
}
//
//
// main:
//    main:
//       main:
//          main:
//             main:    Everybody's favorite C function.
//          main:
//       main:
//    main:
// main:
//
//

void main    (void)
{

   UINT32 ii;

   int c , i;

   //
   // do hardware initializations
   initIoPins    ();

   //#use rs232(baud=19200, xmit=PIN_C6, rcv=PIN_C7)

   //~~~~~~~~~LCD init ~~~~~~~~~~
   // it uses LCD_send_byte with WAITBUSY
   lcd_init    ();

   timeInit    ();


   // init os variables
   // call before calling tasks
   osInit    ();




   // start tasks, they run till they hit osTaskInit()
   led1Task    ();
   led2Task    ();
   led3Task    ();
   adcTask    ();
   buttonTask    ();
   LcdWriteTask    ();
   // now that we are initialized, enable all interrupts
   enable_interrupts    (GLOBAL);

   // Set some tasks ready to run.
   osSetRtr    (led1TaskNumber);
   osSetRtr    (led2TaskNumber);
   osSetRtr    (led3TaskNumber);
   osSetRtr    (adcTaskNumber);
   osSetRtr    (buttonTaskNumber);
   osSetRtr    (LcdWriteTaskNumber);
   puts    ("\n");
   c = '\r' ;
   for( i = 0; c ;i++ )
   {
      putc    (c);
      c = read_eeprom    (i);
   }
 
   printf    ("\r\nMS_DELAY seed = 0x%LX\r\n",MS_DELAY) ;


   printf    ("\r\n\nPotentiometer RA0 value:\r\n");
   ii = 0;
   // main loop
   while( TRUE )
   {
      // run the OS
      osMainTask    ();

      // note: when no tasks are ready to run, ii gets incremented
      ii++;
      if((ii % 0x2000)== 0) { printf("\rADC = %4lu", adcread());}

   }

#ifdef __PCH__
   // should never get here
   // this call forces the compiler to treat key os functions as subroutines
   osCallAll    ();
#endif
}
RobM



Joined: 29 Apr 2005
Posts: 13
Location: Santa Cruz, CA

View user's profile Send private message

Re: csRtos ported to PIC - first file main.c
PostPosted: Sun Sep 04, 2005 12:48 pm     Reply with quote

csRTOS OPERATING SYSTEM SERVICE ROUTINES

As I have been evaluating the csRTOS operating system I found myself confused about what I should and should not try to do in my user code. I created this list as I was learning, and I hope others will find it useful. It is a re-organization of material in the article by Glen Worstell which accompanies his original code, with the addition of function information taken from Mark Hahn’s port to CCS and PIC processors. I take responsibility for any incorret information, or ommision of useful functions from this list.

A number of macros are defined that allow tasks to make operating system calls for various purposes.

Initialization macros
These are used to start the OS or initialize individual tasks.

osTaskInit(UINT8 taskNumber);Must be called for each task, before starting multitasking.
osMainTask(void);This function is mostly to keep osRun executing at the same stack level as other os functions. Some compilers optimize out the call to this and just place the functions code inline.
osCallAll(void); Some compilers optimize out subroutine calls and returns for functions called only once. By calling osCallAll once in your main() function, someplace it will never run, you fake the compiler out and it does subroutine calls for all essential os functions.

Task suspension Macros
These are used to release the processor from performing a task. Select the appropriate one to start the task: after time passes, after an event occurs or after another task has restarted the current task.

osWAIT(ticks); Suspends the calling task for a number of ticks.
osTASK_INIT(TaskNumber); Used at the beginning of every task.
osYield(void); Resumes the highest priority task that is rtr. The rtr status of the calling task is not changed - so if it is the highest priority rtr it immediately resumes.
osSuspend(void); Makes the calling task not rtr. Usually it will be made rtr at some future time by an interrupt service routine or by another task.
osSuspendTask(TaskNumber);Suspends a task. May be called by one task (or main program) to suspend another task.
osCLEAR_RTR(TaskNumber); Makes a task not ready to run. If the parameter is the current task it is NOT suspended.
osSET_RTR(TaskNumber); Makes a task ready to run, but does not suspend the current task.
osTimeout(void); Returns TRUE if current task has a timer value of 0. Can be used to see if a timeout happened while waiting for an event or a semaphore.
osGetTicks(void); Gets timer ticks for current task.
osWaitEventTimeout(eventNumber, ticks); Suspends the calling task till event happens or ticks expire. Setting ticks to 0 causes the task to wait forever.
osWaitEvent(eventNumber); Suspends the calling task till event happens.


Resource Sharing Macros
System resources can be shared, yet only one task may have control at any time. The operating system provides Semaphores as a method to manage this.

osGET_SEMA(semaNumber); Makes the calling task the owner of the given semaphore if it is available. If the semaphore is not available the task is suspended. When the semaphore becomes available the highest priority task that is waiting for it is set rtr. Note: this may result in a priority inversion, a low priority task that has taken a semaphore will prevent a higher priority task from running.

osRELEASE_SEMA(semaNumber); Releases the semaphore, which results in another task being set rtr if another task is waiting for the semaphore. Of course a task should not release a semaphore unless it owns it - no error checking is done for this condition.
osGetSemaTimeout(semaNumber, ticks); Makes the calling task the owner of the given semaphore, if it is available. If the semaphore is not available the task is suspended. When the semaphore becomes available the highest priority task that is waiting for it is set rtr. Note: this may result in a priority inversion, a low priority task that has taken a semaphore will prevent a higher priority task from running. Setting ticks to 0 causes the task to wait forever.



Interrupt Related Macros

osClearRtrCritical(TaskNumber) clears the rtr bit of the given task, but DOES NOT call the scheduler.
osSetRtrCritical(TaskNumber) sets the rtr bit of the given task, but DOES NOT call the scheduler. May be called inside an interrupt context.
osCRITICAL_END(void) re-enables interrupts (if previously enabled).
osCRITICAL_END(void) re-enables interrupts (if previously enabled).
_________________
Rob
_______
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> Code Library All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group