PIC24 Support Libraries
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
esos.c
Go to the documentation of this file.
1 /*
2  * "Copyright (c) 2008 Robert B. Reese, Bryan A. Jones, J. W. Bruce ("AUTHORS")"
3  * All rights reserved.
4  * (R. Reese, reese_AT_ece.msstate.edu, Mississippi State University)
5  * (B. A. Jones, bjones_AT_ece.msstate.edu, Mississippi State University)
6  * (J. W. Bruce, jwbruce_AT_ece.msstate.edu, Mississippi State University)
7  *
8  * Permission to use, copy, modify, and distribute this software and its
9  * documentation for any purpose, without fee, and without written agreement is
10  * hereby granted, provided that the above copyright notice, the following
11  * two paragraphs and the authors appear in all copies of this software.
12  *
13  * IN NO EVENT SHALL THE "AUTHORS" BE LIABLE TO ANY PARTY FOR
14  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
15  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE "AUTHORS"
16  * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17  *
18  * THE "AUTHORS" SPECIFICALLY DISCLAIMS ANY WARRANTIES,
19  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20  * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE "AUTHORS" HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
23  *
24  * Please maintain this header in its entirety when copying/modifying
25  * these files.
26  *
27  *
28  */
29 
30 
31 
32 // Documentation for this file. If the \file tag isn't present,
33 // this file won't be documented.
34 /** \file
35  * \brief Central code for Embedded Systems Operating System (ESOS)
36  *
37  */
38 
39 #include "esos.h"
40 
41 //*********************************************************************
42 // P R I V A T E D E F I N I T I O N S
43 //*********************************************************************
44 /**
45  * Define the maximum number of user tasks in the system
46  * \note Technically, this is actually the maximum number of
47  * tasks that will be running "concurrently". Usually,
48  * this number is the maximum number of tasks that the
49  * user has defined, UNLESS they are absolutely sure that
50  * two (or more) tasks are mutually exclusive in execution.
51  *
52  * \note BOTH "parent" and "child" tasks use this NUMBER to allocate
53  * their pool of tasks. So this number should be equal to or greater
54  * than the MAXIMUM number of concurrently running child --OR--
55  * parent tasks.
56  */
57 #define MAX_NUM_USER_TASKS 16
58 #define MAX_NUM_CHILD_TASKS MAX_NUM_USER_TASKS
59 
60 #define REMOVE_IDX 0xFE
61 
62 //**********************************************************
63 // GLOBAL variables for ESOS to use/maintain
64 //**********************************************************
65 struct stTask __astUserTaskPool[MAX_NUM_USER_TASKS];
66 uint8 __au8UserTaskStructIndex[MAX_NUM_USER_TASKS];
67 struct stTask __astChildTaskPool[MAX_NUM_CHILD_TASKS];
68 uint8 __u8UserTasksRegistered;
69 uint8 __u8ChildTasksRegistered;
70 
71 struct stTimer __astTmrSvcs[MAX_NUM_TMRS];
72 uint8 __esos_u8TmrSvcsRegistered;
73 uint16 __esos_u16TmrActiveFlags;
74 
75 #ifdef ESOS_USE_BULK_CDC_USB
76 static struct stTask __stUsbCommSystem;
77 #endif
78 
79 uint16 __esos_u16UserFlags, __esos_u16SystemFlags;
80 uint32 __u32_esos_PRNG_Seed;
81 
82 /****************************************************************
83 ** Embedded Systems Operating System (ESOS) code
84 ****************************************************************/
85 /**
86  * Adds a task to the scheduler. Task will start executing at the
87  * next opportunity. (almost immediately)
88  * \param taskname name of task (argument to \ref ESOS_USER_TASK declaration
89  * \retval NULLPTR if no more tasks can execute at this time (scheduler is full)
90  * \retval TaskHandle the handle of the just registered and scheduled task
91  * \sa ESOS_USER_TASK
92  * \sa esos_UnregisterTask
93 */
95  uint8 u8_i;
96  uint8 u8_FoundFcn = FALSE;
97  uint8 u8_IndexFcn;
98  uint8 u8_IndexFree=0;
99  uint8 u8_FoundFree = FALSE;
100 
101  if (__u8UserTasksRegistered < MAX_NUM_USER_TASKS) {
102  for (u8_i=0; u8_i<MAX_NUM_USER_TASKS; u8_i++) {
103  if (__astUserTaskPool[u8_i].pfn == taskname) {
104  u8_FoundFcn = TRUE;
105  u8_IndexFcn = u8_i;
106  break;
107  } // endof if()
108  if ((!u8_FoundFree) && (__astUserTaskPool[u8_i].pfn == NULLPTR)) {
109  u8_FoundFree = TRUE;
110  u8_IndexFree = u8_i;
111  } // endof if()
112  } // endof for()
113  if (u8_FoundFcn) {
114  __ESOS_INIT_TASK( &__astUserTaskPool[u8_IndexFcn]);
115  __astUserTaskPool[u8_IndexFcn].flags = 0;
116  __au8UserTaskStructIndex[__u8UserTasksRegistered] = u8_IndexFcn;
117  __u8UserTasksRegistered++;
118  return &__astUserTaskPool[u8_IndexFcn];
119  } // endof if
120  /* we did NOT find our function in the pool, so allocate a new struct */
121  if (u8_FoundFree) {
122  __astUserTaskPool[u8_IndexFree].pfn = taskname;
123  __ESOS_INIT_TASK(&__astUserTaskPool[u8_IndexFree]);
124  __astUserTaskPool[u8_IndexFree].flags = 0; // reset the task flags
125  __au8UserTaskStructIndex[__u8UserTasksRegistered] = u8_IndexFree;
126  __u8UserTasksRegistered++;
127  return &__astUserTaskPool[u8_IndexFree];
128  } // endof if
129  /* we did NOT find our function in the pool OR a free struct to use, so
130  we will return a NULLPTR for now. In the future, we will do some garbage
131  collection here.
132  */
133  return NULLPTR;
134  } else {
135  return NULLPTR;
136  } //end of if-else
137 }// end esos_RegisterTask()
138 
139 /**
140  * Removes the task from the scheduler
141  * \param taskname name of task (argument to \ref ESOS_USER_TASK declaration
142  * \retval TRUE if task was found in scheduler and removed
143  * \retval FALSE otherwise
144  * \sa ESOS_USER_TASK
145  * \sa esos_RegisterTask
146 */
148  uint8 u8Status=FALSE;
149  uint8 u8_i, u8_z;
150  ESOS_TASK_HANDLE pstNowTask;
151 
152  for (u8_i=0; u8_i<__u8UserTasksRegistered; u8_i++) {
153  // get next index from array so we can get the task handle
154  u8_z = __au8UserTaskStructIndex[u8_i];
155  // check for unregistered tasks that have NOT been garbage collected yet
156  if ((u8_z != NULLIDX) & (u8_z != REMOVE_IDX)) {
157  pstNowTask = &__astUserTaskPool[u8_z];
158  if (pstNowTask->pfn == taskname) {
159  __au8UserTaskStructIndex[u8_i] = REMOVE_IDX;
160  // REMOVE THIS LINE.... let the task packing operation change u8UserTasksRegistered
161  //__u8UserTasksRegistered--;
162  __esos_SetSystemFlag( __ESOS_SYS_FLAG_PACK_TASKS );
163  u8Status=TRUE;
164  break;
165  } // end if (pfn == taskname)
166  } // end if (!NULLIDX)
167  } // end for
168  return u8Status;
169 }// end esos_UnregisterTask()
170 
171 
172 // TODO: I DONT THINK I NEED TO STORE THE ACTUAL PFNs SINCE THE PARENT'S
173 // WAIT FUNCTION WILL DECOMPOSE INTO A CALL TO THE CHILD TASK AUTOMATICALLY.
174 //
175 // INVESTIGATE MORE!
176 //
177 // TODO: make sure childs get restarted if they yield and some other task
178 // executes in the meantime!
179 
180 /**
181 * Searches child task pool to find a free child task structure and returns
182 * a handle (pst) back to the caller
183 * \retval TaskHandle if a child task structure is available
184 * \retval ESOS_BAD_CHILD_TASK_HANDLE if no structures are available at this time
185 */
187  uint16 u16_i = 0;
188 
189  while (u16_i < MAX_NUM_CHILD_TASKS) {
190  if (ESOS_IS_TASK_INITED( &__astChildTaskPool[u16_i]) )
191  return &__astChildTaskPool[u16_i];
192  u16_i++;
193  }
194  return NULLPTR;
195 }// end esos_u16GetFreeChildTaskStruct()
196 
197 
198 /**
199  * Sets the seed value in the ESOS pseudo-random number generator (PRNG).
200  * \note ESOS init code sets a seed value for the PRNG. If the application
201  * desires a sequence that is not predictable at each execution run,
202  * then the seed should be set <em>ONCE</em> with some value that is
203  * different with each execution. An idea is to have the user press
204  * a key during startup. The value of the ESOS tick when the user presses
205  * the key will be different each time. This number would make an
206  * ideal PRNG seed.
207  * \sa esos_GetRandomUint32
208  * See http://www.firstpr.com.au/dsp/rand31/ for more information
209  */
211  __u32_esos_PRNG_Seed = u32_in;
212 } // end esos_SetRandomUint32Seed()
213 
214 /**
215 * Returns a 31-bit pseudo-random number generated by the Park-Miller
216 * algorithm.
217  * \sa esos_SetRandomUint32Seed
218 * \note Visit http://www.firstpr.com.au/dsp/rand31/ for more information
219 */
221  uint32 hi, lo;
222 
223  lo = 16807 * ( __u32_esos_PRNG_Seed * 0xFFFF );
224  hi = 16807 * ( __u32_esos_PRNG_Seed >> 16 );
225  lo += (hi & 0x7FFF) << 16;
226  lo += (hi >> 15);
227  if (lo > 0x7FFFFFFF) lo -= 0x7FFFFFFF;
228  return (__u32_esos_PRNG_Seed = (uint32) lo );
229 } // end esos_getRandomUint32()
230 
231 /**
232 * Returns the number of tasks we can execute
233 * \retval N the number of tasks this version of ESOS can execute
234 */
236  return MAX_NUM_USER_TASKS;
237 } // end osGetMaxNumberTasks()
238 
239 /*
240 * Determine whether a period of time has elapsed. Users
241 * have no need to call this function. It is used by ESOS
242 * internally.
243 * \param u32_startTick system tick count when timer was created
244 * \param u32_period duration of period in system ticks
245 * \retval TRUE if the period of time has elapsed
246 * \retval FALSE if the period of time has not yet elapsed
247 */
248 uint16 __esos_hasTickDurationPassed(uint32 u32_startTick, uint32 u32_period) {
249  uint32 u32_delta, u32_current;
250 
251  u32_current = esos_GetSystemTick();
252  u32_delta = u32_current - u32_startTick;
253  if (u32_current < u32_startTick)
254  u32_delta += 0xFFFFFFFF; // account for rollover (DELTA=0xFFFFFFF-start+current)
255  if (u32_delta > u32_period)
256  return TRUE;
257  else
258  return FALSE;
259 } // end __esos_hasSystemTickDurationPassed()
260 
261 /*
262 * ESOS timer services callback function. HW-specific code
263 * that creates the system tick must call this function at
264 * every ESOS system tick.
265 */
266 void __esos_tmrSvcsExecute(void) {
267  uint8 u8_cnt, u8_index;
268 
269  u8_cnt = __esos_u8TmrSvcsRegistered;
270  u8_index = 0;
271  while (u8_cnt) {
272  // if timer is running, update its structure and call it if necessary
273  if (esos_IsTimerRunning(u8_index)) {
274  __astTmrSvcs[u8_index].u32_cntDown--;
275  if (__astTmrSvcs[u8_index].u32_cntDown == 0 ) {
276  __astTmrSvcs[u8_index].u32_cntDown = __astTmrSvcs[u8_index].u32_period;
277  __astTmrSvcs[u8_index].pfn();
278  } //endif timer has expired
279  u8_cnt--; // denote we've serviced one of the active timers
280  } // endif IsTimerRunning
281  u8_index++; // move index to next timer in array
282  } // end while(u8_cnt)
283 } //end __esos_tmrSvcsExecute()
284 
285 /**
286  * Adds a timer to the ESOS timer service. Timer function will execute at its
287  * next opportunity. Timer functions must have \em void arguments and \em void
288  * returns.
289  * \param timername name under which timer was declared in \ref ESOS_USER_TIMER.
290  * and contains the code to run when software timer expires
291  * \param u32_period period of timer in system ticks (currently, milliseconds)
292  * \retval ESOS_TMR_FAILURE if no more timers can added at this time
293  * \retval timerhandle if timer service was registered
294  * \sa ESOS_USER_TIMER
295  * \sa esos_UnregisterTimer
296  * \sa esos_GetTimerHandle
297  * \sa esos_ChangeTimerPeriod
298  * \sa esos_IsTimerRunning
299  * \sa
300  *
301  */
302 ESOS_TMR_HANDLE esos_RegisterTimer( void (*timername)(void), uint32 u32_period ) {
303  uint8 u8_i;
304 
305  if ( esos_GetNumberRunningTimers() < MAX_NUM_TMRS) {
306  for (u8_i=0; u8_i<MAX_NUM_TMRS; u8_i++ ) {
307  if (!esos_IsTimerRunning(u8_i)) {
308  __astTmrSvcs[u8_i].pfn = timername;
309  __astTmrSvcs[u8_i].u32_period = u32_period;
310  __astTmrSvcs[u8_i].u32_cntDown = u32_period;
311  __esos_u8TmrSvcsRegistered++;
312  __esos_MarkTimerRunning( u8_i );
313  return u8_i;
314  } // endif IsTimerRunning
315  } // endfor
316  return ESOS_TMR_FAILURE;
317  } // endif
318  else
319  return ESOS_TMR_FAILURE;
320 } // end esos_RegisterTimer
321 
322 /**
323  * Removes a timer from the ESOS timer service.
324  * \param hnd_timer handle to timer to remove
325  * \retval FALSE if timer wasn't active in the first place
326  * \retval TRUE if timer was stopped and removed
327  * \sa esos_RegisterTimer
328  * \sa esos_GetTimerHandle
329  * \sa esos_ChangeTimerPeriod
330  */
332 
333  if ( esos_IsTimerRunning(hnd_timer) ) {
334  __astTmrSvcs[hnd_timer].pfn = NULLPTR;
335  __esos_u8TmrSvcsRegistered--;
336  __esos_MarkTimerStopped(hnd_timer);
337  return TRUE;
338  } else
339  return FALSE;
340 } //end esos_UnregisterTimer()
341 
342 /**
343  * Finds the timer handle to the provided and ACTIVE timer function
344  * \param pfnTmrFcn pointer to timer function (will execute each time timer expires)
345  * \retval ESOS_TMR_FAILURE could not find the function in the active timer list
346  * \retval timerHandle handle to timer
347  * \sa esos_RegisterTimer
348  * \sa esos_UnregisterTimer
349  * \sa esos_ChangeTimerPeriod
350  * \sa esos_IsTimerRunning
351  */
352 ESOS_TMR_HANDLE esos_GetTimerHandle( void (*pfnTmrFcn)(void) ) {
353  uint8 u8_i=0;
354  uint8 u8_cnt;
355 
356  u8_cnt = esos_GetNumberRunningTimers();
357  while (u8_cnt) {
358  if (esos_IsTimerRunning(u8_i) ) {
359  if ( __astTmrSvcs[u8_i].pfn == pfnTmrFcn ) return u8_i;
360  u8_cnt--;
361  } //endif
362  u8_i++;
363  } // endwhile
364  return ESOS_TMR_FAILURE;
365 } //end esos_GetTimerHandle()
366 
367 
368 /**
369  * Change a timer period.
370  * \param hnd_timer handle to timer whose period is to be changed
371  * \param u32_period new period for timer selected by mask
372  * \retval FALSE if timer is not currently running
373  * \retval TRUE if timer period was changed
374  * \sa esos_RegisterTimer
375  * \sa esos_UnregisterTimer
376  * \sa esos_GetTimerHandle
377  * \sa esos_IsTimerRunning
378  */
380 
381  if (esos_IsTimerRunning(hnd_timer) ) {
382  __astTmrSvcs[hnd_timer].u32_period = u32_period;
383  return TRUE;
384  } else return FALSE;
385 } //end esos_geTimerHandle()
386 
387 void __esosInit(void) {
388  uint8 i;
389 
390  // initialize the fcn ptrs to point to nothing
391  for (i=0; i<MAX_NUM_USER_TASKS; i++) {
392  __astUserTaskPool[i].pfn = NULLPTR;
393  __au8UserTaskStructIndex[i] = NULLIDX;
394  __astChildTaskPool[i].pfn = NULLPTR;
395  }
396  __esos_u16TmrActiveFlags = 0;
397  for (i=0; i<MAX_NUM_TMRS; i++) {
398  __astTmrSvcs[i].pfn = NULLPTR;
399  }
400 
401  // no user tasks are currently registered
402  __u8UserTasksRegistered = 0;
403  // no child tasks are active
404  __u8ChildTasksRegistered = 0;
405  // no timer services are active
406  __esos_u8TmrSvcsRegistered = 0;
407  // initialize the ESOS pseudo-random number generator
408  // see value. Use MAX_NUM_USER_TASKS for now.
409  __u32_esos_PRNG_Seed = MAX_NUM_USER_TASKS;
410 
411  /* Call the user provided function to initialize the
412  * and start the ESOS system tick..
413  */
414  __esos_hw_InitSystemTick();
415 
416  // initialize the HW interrupts
417  // (This routine is considered HW specific, because
418  // we don't know the number of HW interrupts on the
419  // CPU at this point....)
420 #ifdef ESOS_USE_IRQS
421  _esos_hw_InitUserInterrupts();
422 #endif
423  /*
424  * Now, initialize one of the communication systems if
425  * the user has requested it in user_config.h
426  *
427  * TODO: At some point, I want to be able to run both comm
428  * systems independently so we can use USB and debug via
429  * RS232 or vice-versa. I'll worry about that later.
430  */
431 #ifdef ESOS_USE_BULK_CDC_USB
432  __esos_InitCommSystem();
433 #endif
434 #ifdef ESOS_USE_SERIAL_PORT
435  __esos_InitCommSystem();
436 #endif
437 
438  user_init();
439 
440 } // end osInit()
441 
442 main_t main(void) {
443  uint8 u8TaskReturnedVal=0;
444  uint8 u8i ,u8j, u8NumRegdTasksTemp;
445  ESOS_TASK_HANDLE pstNowTask;
446 
447  __esosInit();
448  /*
449  * Then we schedule the two protothreads by repeatedly calling their
450  * protothread functions and passing a pointer to the protothread
451  * state variables as arguments.
452  */
453  while (TRUE) {
454  /* First, let ESOS get something done.....
455  * service communications, garbage collection, etc.
456  * Whatever a nice little OS needs to do.
457  */
458 
459  u8i = 0;
460  /* get the number of currently registered tasks.... we must make
461  * a local copy, because the tasks themselves may unregister and
462  * change the variable __u8UserTasksRegistered as they go!
463  */
464  u8NumRegdTasksTemp = __u8UserTasksRegistered;
465  while ( u8i < u8NumRegdTasksTemp ) {
466  pstNowTask = &__astUserTaskPool[__au8UserTaskStructIndex[u8i]];
467  u8TaskReturnedVal = pstNowTask->pfn( pstNowTask );
468  if (u8TaskReturnedVal == ESOS_TASK_ENDED) {
469  //printf ("Unregistering an ENDED protothread\n");
470  esos_UnregisterTask( pstNowTask->pfn );
471  } // endif
472  u8i++;
473  OS_ITERATE;
474  } //end while()
475 
476  // see if any tasks have been unregistered this time thru
477  if (__esos_IsSystemFlagSet( __ESOS_SYS_FLAG_PACK_TASKS) ) {
478  // Now, loop over the UserTasks and pack them into
479  // the beginning of our arrays
480  //
481  // Loop over the original number of registered tasks and
482  // look for NULLPTRs. If you find one, scoot all the following
483  // tasks down by one and make the last one a NULLPTR. Reduce
484  // our count of tasks by one to make the next search shorter.
485  //
486  // NOTE: we can't "break" the for-loop search becasue there
487  // may be more than one unregistered tasks in our arrays
488  // u8i=0; while ( u8i < __u8UserTasksRegistered) {
489  u8i = __u8UserTasksRegistered;
490  while(TRUE) {
491  pstNowTask = &__astUserTaskPool[u8i];
492  if (__au8UserTaskStructIndex[u8i] == REMOVE_IDX) {
493  for ( u8j=u8i+1; u8j<u8NumRegdTasksTemp; u8j++) {
494  __au8UserTaskStructIndex[u8j-1] = __au8UserTaskStructIndex[u8j];
495  } // end for
496  __au8UserTaskStructIndex[u8NumRegdTasksTemp-1] = NULLIDX;
497  u8NumRegdTasksTemp--;
498  } // end if
499  //u8i++;
500  if (u8i) u8i--;
501  else break;
502  } // end while
503  __u8UserTasksRegistered=u8NumRegdTasksTemp; // set record the new number of registered tasks
504  __esos_ClearSystemFlag( __ESOS_SYS_FLAG_PACK_TASKS );
505  } // end if
506  } //end while
507 
508  OS_END;
509 
510 } //end main()