To make uC/OS-II more flexible, the oSchedule was slightly modified so when in RTOS mode, it simply serves as the clock manager rather than the clock and task manager as in the non preemptive mode. Thus before entering the first task, the oSchedule period (default 10 milliseconds) must be configured and it must be set to operate. Technically, this also can be used to halt OS scheduler outside of uC/OS-II control (untested).
As part of a streamlined architecture, rather than have the seperate JAUS-like classes of data (platform_specification, local_pose_sensor, etc.), we have only an API to the global data. for instance:
wheel_diameter(LEFT,120); // Sets the left wheel diameter to 120 millimeters. wheel_diameter(RIGHT,120); // Sets the right wheel diameter to 120 millimeters.
Furthermore, where a local (class) variable and a persistent form of a variable existed, the local one was removed using only the global one. Thus as soon as the X or Y position is calculated it is automatically reflected in the shared data. The API also will guarantee that interrupts are disabled and enabled around global variables as needed. Thus no global data will exist apart from the access API set.
The only exception, isn't there always one, is with occupancy_grid. Because there are so many routines that iterate over the whole grid, if an access lock is put on it each time it is read or updated, then the structure would be locked for a large chunk of time. For instance, this was most evident with a screen refresh in the simulator--it reads the shared occupancy grid and completely locks out the user program from executing causing erratic behavior.
Since the platform_invariant class was only simulator use, this will only exist on the host PC anyways. Thus, the class interface was left unchanged and only the code was changed to access shared variables rather than global variables.
Non RTOS operation has not yet been completely tested. It will be the responsibility of the end user to apply proper polling to operate without the RTOS functions.
OSFlagAccept and OSFlagPost are used extensively in place boolean values used previously. Flags were a good Mechanism as certain function would only execute if the boolean value was true which was translated into a Flag Pend. The functions will automatically revert back into simple boolean operation without RTOS support through macros defined in <include/model/state_flags.h>. For instance, to set PD_FLAG to false the macro UPDATE_STATE is used:
UPDATE_STATE(PD_FLAG,OOMRM_FLAG_CLR);
Note that this macro uses a unsigned char error flag called err which (for now) must be declared locally to the routine in which it is used. Similarly to read the PD_FLAG use the macro READ_STATE:
if (READ_STATE(PD_FLAG)) ...
Note that for non RTOS operation this translates to a function call wich simply returns the value of PD_FLAG whereas in RTOS operation it translates to a OSFlagAccept call.
The following are functions that make OS calls apart from the flag calls:
Setting the base period differs depending upon whether it is in DOS mode or not as follows:
NOTE:The base period (scheduler_period/RTOS_scheduler_period) used to be initialized in World_Model_Store where shared memory is init (and WsTimerPeriod set; see below) but this delcaration occurs before load_RAM and load_RAM will overwrite all shared memory including value just assigned. Thus scheduler_period() initialization should now be set before the oSchedule rate is set in the user code.
The base rate for uCOS/II is established through OS_TICKS_PER_SECOND (include/ucos/os_cfg.h) as follows:
The DOS RTOS base period is established in the WSInit(X,timerperiod) called in model/world_model_store::ShareInit called from world_model_store's constructor (if host). WSInit is located in host/wcos/wsim_apci.c. This set's WSTimerPeriod as follows:
if (!timerperiod)
{
WSTimerPeriod = WS_DEFAULT_TIMER_PERIOD;
}
else WSTimerPeriod = timerperiod;
Thus, WSTimerPeriod (wsim_api.c) is equal to period specified on WSInit or WS_DEFAULT_TIMER_PERIOD if timerperiod == 0.
Also, WSTimerPeriod is used in WSInit to set up a periodic interrupt timer from the windows function
WSTimer = timeSetEvent(WSTimerPeriod, 0, (LPTIMECALLBACK) WSTimeEvent, 0, TIME_PERIODIC | TIME_CALLBACK_EVENT_SET);.
In addition, WSInit converts the period into
cycles per second (pIPC->tickspersec) by pIPC->tickspersec = 1000 / WSTimerPeriod;. As mentioned earlier, the RTOS environment has access to this value
through the WCGetTicksPerSec (wcos_api.c) function:
UINT WCGetTicksPerSec(void)
{
return pIPC->tickspersec;
}
Many problems were encountered finding an accpetable way to automatically initialize the base rate. Possible alternatives are:
The period used in dead reckoning is EM_UPDATE_PERIOD (include/types.h); furthermore, this also is the fundamental rate used to convert encoder counts per interval to counts per second (F_time_conversion (persistent.cpp) = 1000/EM_UPDATE_PERIOD. This sets the period at which the encoded motors are updated; thus must be greater (or equal?) than base rate. Not sure if both are 10 whether EM_UPDATE_PERIOD will really do every clock tick (10) or every other (20).
May Try OS_TICKS_PER_SECOND (base rate [WSInit(x,5)] 200 and EM_UPDATE_PERIOD 10, although may waste a lot of time context switching.
Usually, I set WSInit to 10 ms in simulator main (World_Model_Store world(true,10); ) and set the OS_TICKS_PER_SECOND to 100 also for 10 ms period. The EM_UPDATE_PERIOD can be anything greater.
Previously, certain large structures were delcared locally to functions such as the A* search structure. This way the 200K block of memory can be freed and reused in other functions such as vision processing etc. With uC/OS-II, each task has its own stack declared globally; thus the stack space simply becomes simply another global object that cannot be reused. To allow this, most tasks will have a very limited stack except for one task with increased stack space who will serve as the broker for all functions with large structures. This probably is better in that it ensures that no two function will try to both allocate its structures and exhaust memory. This also allow the previous code to exist in place without modification. The largest structures are the motion_chain about 3K and the AStar which depending upon size of grid is usually around 200K.
As mentioned previously, one task, TaskDriver, is set up with a large stack (250K). In Windows, there is no problem simply declaring a 250K stack, but on the 68332 MRM, declaring it globally forces downloading the structure every time (adding 500K to download), thus this structure was preallocated in RAM and a pointer to the OS_STK is set the the global symbol TASK_RAM where the memory is located.
Adding console (oSCI) output will cause the RTOS to crash in most cases--either in the Windows simulator (user portion) or under the 68332.