Multi-Tasking and Real-Time Operating Systems:CCS PIC C Compiler RTOS

CCS PIC C Compiler RTOS

The CCS PIC C compiler is one of the popular C compilers for the PIC16 and PIC18 series of microcontrollers. In addition to their PIC compilers, Customer Computer Services offers PIC in-circuit emulators, simulators, microcontroller programmers, and various development kits. The syntax of the CCS C language is slightly different from that of the mikroC language, but readers who are familiar with mikroC should find CCS C easy to use.

CCS C supports a rudimentary multi-tasking cooperative RTOS for the PIC18 series of microcontrollers that uses their PCW and PCWH compilers. This RTOS allows a PIC microcontroller to run tasks without using interrupts. When a task is scheduled to run, control of the processor is given to that task. When the task is complete or does not need the processor any more, control returns to a dispatch function, which gives control of the processor to the next scheduled task. Because the RTOS does not use interrupts and is not preemptive, the user must make sure that a task does not run forever.

Further details about the RTOS are available in the compiler’s user manual.

The CCS language provides the following RTOS functions in addition to the normal C functions:

rtos_run() initiates the operation of RTOS. All task control operations are implemented after calling this function.

rtos_terminate() terminates the operation of RTOS. Control returns to the original program without RTOS. In fact, this function is like a return from rtos_run().

rtos_enable() receives the name of a task as an argument. The function enables the task so function rtos_run() can call the task when its time is due.

rtos_disable() receives the name of a task as an argument. The function disables the task so it can no longer be called by rtos_run() unless it is re-enabled by calling rtos_enable().

rtos_ yield(), when called from within a task, returns control to the dispatcher. All tasks should call this function to release the processor so other tasks can utilize the processor time.

rtos_msg_send() receives a task name and a byte as arguments. The function sends the byte to the specified task, where it is placed in the task’s message queue.

rtos_msg_read() reads the byte located in the task’s message queue.

rtos_msg_ poll() returns true if there is data in the task’s message queue. This function should be called before reading a byte from the task’s message queue.

rtos_signal() receives a semaphore name and increments that semaphore.

rtos_wait() receives a semaphore name and waits for the resource associated with the semaphore to become available. The semaphore count is then decremented so the task can claim the resource.

rtos_await() receives an expression as an argument, and the task waits until the expression evaluates to true.

rtos_overrun() receives a task name as an argument, and the function returns true if that task has overrun its allocated time.

rtos_stats() returns the specified statistics about a specified task. The statistics can be the minimum and maximum task run times and the total task run time. The task name and the type of statistics are specified as arguments to the function.

Preparing for RTOS

In addition to the preceding functions, the #use rtos() preprocessor command must be specified at the beginning of the program before calling any of the RTOS functions. The format of this preprocessor command is:

Multi-Tasking and Real-Time Operating Systems-0227

where timer is between 0 and 4 and specifies the processor timer that will be used by the RTOS, and minor_cycle is the longest time any task will run. The number entered here must be followed by s, ms, us, or ns.

In addition, a statistics option can be specified after the minor_cycle option, in which case the compiler will keep track of the minimum and maximum processor times the task uses at each call and the task’s total time used.

Declaring a Task

A task is declared just like any other C function, but tasks in a multi-tasking application do not have any arguments and do not return any values. Before a task is declared, a

#task preprocessor command is needed to specify the task options. The format of this preprocessor command is:

#task(rate¼n, max¼m, queue¼p)

where rate specifies how often the task should be called. The number specified must be followed by s, ms, us, or ns. max specifies how much processor time a task will use in one execution of the task. The time specifed here must be equal to or less than the time specified by minor_cycle. queue is optional and if present specifies the number of bytes to be reserved for the task to receive messages from other tasks.

The default value is 0.

In the following example, a task called my_ticks is every 20ms and is expected to use no more than 100ms of processor time. This task is specified with no queue option:

Multi-Tasking and Real-Time Operating Systems-0228