This is a super simple Queued Task Manager, aka qTask.
Note, This is not an RTOS, most people throw that around loosely, in fact, this is not even remotely crudely an RTOS!
An RTOS is a Real Time Operating System. qTask is not real time, and its definitely not an operating system!
qTask is simply a queued task manager, that is, it queues tasks for execution. These tasks have periods (time) that
they need to be executed.
This is where the Non-Real Time comes in. Tasks are scheduled during an interrupt, with priorities, thus, if a
task is already queued to be run, and a higher priority task comes up, then it will be executed first, causing a bit
of a delay for the other task.
Using this technique, I am able to get around the round robin style of executing tasks, instead, just executing tasks
that need to be run.
Just added, some code to aid in creating a state machine. I call it slices, basically slices of a pie :)
This slices are to help break up the size of a large task. Thus when a task is executed, it can just execute a portion of the pie.
Another note: This is a work in progress for me, so I don't have it hashed out 100%, but it is functional, and I am
using it in a retail product at the moment.
Here is a video with several tasks as well as bar graph task sliced up.
A simple view of how to use it
This will turn on an LED every .5 second, then turn it off .5 second later
(note, xtal = 10mhz, x4 pll is on, timer1 is used for the task manager)
Example Usage
/vhost/mculabs/driver_db/qtask/files/ex1.c
1 2 // define some task ID's 3 #define TASKID_LEDBLINK 1 4 #define TASKID_COUNT 2 5 6 // define a pie (basically name of a state machine) 7 qDefinePie pieBlinkLed; 8 9 // how many uSeconds in a tick? 10 // with this test, pic has 10mhz xtal with x4 PLL 11 #define QTASK_TOCK 500 12 13 #include"qtask.c" 14 15 16 voidslice_ledOn(void) { 17 mPwrLed_on(); 18 } 19 20 voidslice_ledOff(void) { 21 mPwrLed_off(); 22 } 23 24 voidtask_blink_led(void) 25 { 26 // manage slices of the pie 27 mqSliceManager(pieBlinkLed) 28 { 29 // define the order of the slices, (constant, function) 30 mqOrderSlice(1,slice_ledOn()); 31 mqOrderSlice(2,slice_ledOff()); 32 } 33 // we need to define what the last slice was (this resets slice counter) 34 mqLastSlice(pieBlinkLed,2); 35 } 36 37 38 voidtask_count(void) 39 { 40 c++; 41 } 42 43 44 voidmain(void) 45 { 46 47 // initialize the queued task system 48 qTaskInitialize(); 49 50 // set task parameter ( task id, how many tics) 51 mqTaskSetTmr(TASKID_LEDBLINK,1000000); 52 mqTaskEnable(TASKID_LEDBLINK); 53 mqTaskSetPri(TASKID_LEDBLINK, QTPRI_HIGHEST);// set highest priority 54 55 mqTaskSetTmr(TASKID_COUNT,50000); 56 mqTaskEnable(TASKID_COUNT); 57 58 mqTaskSchedulerEnable();// turn on the task scheduler 59 enable_interrupts(GLOBAL);// since scheduler runs on int, must enable global 60 61 // initialize the blink led pie (state machine) 62 mqPieIni(pieBlinkLed); 63 64 while(1) 65 { 66 // ** your main program here / or run all in task manager ** 67 68 // some tasks to manage 69 mqTaskManager() 70 { 71 // tell it what function to execute per task id 72 mqTaskFunction(TASKID_LEDBLINK,task_blink_led()); 73 mqTaskFunction(TASKID_COUNT,task_count()); 74 } 75 } 76 77 }
And here is the complete driver source file.
qtask.c
/vhost/mculabs/driver_db/qtask/files/qtask.c
1 /* 2 file: qtask.c 3 version: 1.02 4 description: Queued Task Manager/Scheduler 5 (C): (C) michael bradley 6 7 Note: this driver currently is using timer1, so don't use it! 8 you can change, and modify if you wish 9 10 Changelog: 11 12/12/2009 mqBeginSlice() removed, no longer needed 12 12/07/2009 ISR is only called when needed vs ever x uSeconds 13 Code added to aid in creating state machines, called slices 14 12/05/2009 Overall, faster code, smaller footprint 15 now working in ticks, instead of uSec 16 defined pri levels 17 changed flags internally 18 changed counter to count down 19 */ 20 21 #define QTASK_DEBUG_PIN PIN_C0// i use this for monitoring timing 22 23 #DEFINE QTPRI_HIGHEST 0 24 #DEFINE QTPRI_HIGH 1 25 #DEFINE QTPRI_NORMAL 2 26 #DEFINE QTPRI_LOW 3 27 #DEFINE QTPRI_LOWEST 4 28 29 #define QTFLAG_RUNNING 0 30 #define QTFLAG_ENABLED 1 31 #define QTFLAG_QUED 2 32 33 #define QTFLAG_MANAGETASK 0b00000010// basically enabled bit only 34 35 // a few macros 36 #define mqTaskSetPri(id,n) qTasks[id].priority = n; 37 #define mqTaskSetTmr(id,t) qTasks[id].threshold = t; qTasks[id].counter = t; 38 #define mqTaskEnable(id) bit_set(qTasks[id].flags,QTFLAG_ENABLED); 39 #define mqTaskDisable(id) bit_clear(qTasks[id].flags,QTFLAG_ENABLED); 40 #define mqTSR(id) bit_set(qTasks[id].flags,QTFLAG_RUNNING); 41 #define mqTCR(id) bit_clear(qTasks[id].flags,QTFLAG_RUNNING); 42 #define mqTaskManager() switch (qTaskGet()) 43 #define mqTaskFunction(id,fn) case id: { mqTSR(id); fn; mqTCR(id); break; } 44 45 #define mqTaskSchedulerEnable() enable_interrupts(INT_TIMER1); 46 #define mqTaskSchedulerDisable() disable_interrupts(INT_TIMER1); 47 #define mqTaskTimeOut() set_timer1(65535-(tocks*10)); 48 #define mqTaskRest() set_timer1(0); 49 #define mqTaskTimerCur() get_timer1(); 50 51 #define qDefinePie int8 52 #define mqSliceManager(slice) switch (slice) 53 #define mqOrderSlice(sid,fn) case sid: { fn; break; } 54 #define mqPieIni(slice) slice = 1; 55 #define mqLastSlice(slice,num) slice++; if (slice > num) { slice = 1; } 56 57 // total tasks 58 #ifndef TASKS_MAX// define this on your own if you wish 59 #define TASKS_MAX 16// max number of tasks 60 #endif 61 62 #if TASKS_MAX > 32 63 #error Too many tasks, must be <= 32 64 #endif 65 66 unsigned int8 qTaskFiFo[TASKS_MAX]; 67 unsigned int8 qTaskPointer; 68 unsigned int16 qTaskTocDelta; 69 70 71 struct stTaskItem
72 { 73 int8 flags; 74 int8 priority; 75 unsigned int16 threshold; 76 unsigned int16 counter; 77 } qTasks[TASKS_MAX]; 78 79 80 unsigned int8 qTaskGet(void) 81 { 82 unsigned int8 i,res; 83 84 res =0; 85 86 if(qTaskPointer !=0) 87 { 88 res = qTaskFiFo[0]; 89 for(i=0; i<qTaskPointer; i++) 90 { 91 qTaskFiFo[i] = qTaskFiFo[i+1]; 92 } 93 qTaskPointer--; 94 bit_clear(qTasks[res].flags,QTFLAG_QUED); 95 } 96 97 return res; 98 } 99 100 voidqTaskQue(unsigned int8 id) 101 { 102 int i,pa,pb; 103 unsigned int8 tid; 104 bit_set(qTasks[id].flags,QTFLAG_QUED); 105 qTasks[id].counter = qTasks[id].threshold; 106 107 qTaskFiFo[qTaskPointer] = id; 108 109 for(i=qTaskPointer; i>1; i--) 110 { 111 if( qTasks[qTaskFiFo[i]].priority < qTasks[qTaskFiFo[i-1]].priority ) 112 { 113 tid = qTaskFiFo[i-1]; 114 qTaskFiFo[i-1] = qTaskFiFo[i]; 115 qTaskFiFo[i] = tid; 116 } 117 } 118 119 qTaskPointer++; 120 //if (qTaskPointer == TASKS_MAX) { qTaskPointer--; } 121 } 122 123 voidqTaskInitialize() 124 { 125 unsigned int8 i; 126 unsigned int16 tocks; 127 128 // used to set qtask ticking 129 setup_timer_1(T1_INTERNAL|T1_DIV_BY_1); 130 tocks = QTASK_TOCK; 131 qTaskTocDelta =1; 132 mqTaskTimeOut(); 133 134 qTaskPointer =0; 135 for(i=0; i<TASKS_MAX; i++) 136 { 137 qTaskFiFo[i] =0; 138 qTasks[i].flags =0; 139 qTasks[i].priority = QTPRI_NORMAL; 140 } 141 } 142 143 // we use the timer 1 interrupt for scheduling 144 // this should be high, not 1us!!! 145 #int_TIMER1 146 voidisr_scheduler(void) 147 { 148 unsigned int8 i; 149 unsigned int16 tocks; 150 151 //mqTaskRest(); // set timer to 0 so we can time how long we took in this isr 152 tocks = qTaskTocDelta; 153 #ifdef QTASK_DEBUG_PIN 154 output_high(QTASK_DEBUG_PIN); 155 #endif 156 for(i=1; i<TASKS_MAX; i++) 157 { 158 // manage task if its enabled only, not running, not queued 159 if( qTasks[i].flags == QTFLAG_MANAGETASK ) 160 { 161 qTasks[i].counter = qTasks[i].counter - tocks; 162 if( qTasks[i].counter ==0) 163 { 164 qTaskQue(i); 165 } 166 else 167 { 168 if( qTasks[i].counter < qTaskTocDelta ) { qTaskTocDelta = qTasks[i].counter; } 169 } 170 } 171 } 172 173 // adjust for tock resolution, and reduce by time spend in this isr 174 // (at least try to reduce some, hey, were not an RTOS!) 175 //tocks = (qTaskTocDelta * QTASK_TOCK) - mqTaskTimerCur(); 176 tocks = (qTaskTocDelta * QTASK_TOCK); 177 178 #ifdef QTASK_DEBUG_PIN 179 output_low(QTASK_DEBUG_PIN); 180 #endif 181 mqTaskTimeOut();// set the next isr entry time 182 }