> ## 18. µC/OS-II

> ### Not available with SE versions of Dynamic C.

> µC/OS-II is a simple, clean, efficient, easy-to-use real-time operating system that runs on the Rabbit microprocessor and is fully supported by the Dynamic C development environment. µC/OS-II is capable of intertask communication and synchronization via the use of semaphores, mailboxes, and queues. User-definable system hooks are supplied for added system and configuration control during task creation, task deletion, context switches, and time ticks.

> For more information on µC/OS-II, please refer to Jean J. Labrosse's book, MicroC/OS-II, The Real-Time Kernel (ISBN: 0-87930-543-6). The data structures (e.g. Event Control Block) referenced in the Dynamic C µC/OS-II function descriptions are fully explained in Labrosse's book. It can be purchased at the Z-World store, www.zworld.com/store/home.html, or at http://www.ucos-ii.com/.

> ### 18.1 Changes to µC/OS-II

> To take full advantage of services provided by Dynamic C, minor changes have been made to µC/OS-II.

> #### 18.1.1 Ticks per Second

> In most implementations of µC/OS-II, `OS_TICKS_PER_SEC` informs the operating system of the rate at which `OSTimeTick` is called; this macro is used as a constant to match the rate of the periodic interrupt. In µC/OS-II for the Rabbit, however, changing this macro will change the tick rate of the operating system set up during `OSInit`. Usually, a real-time operating system has a tick rate of 10 Hz to 100 Hz, or 10-100 ticks per second. Since the periodic interrupt on the Rabbit occurs at a rate of 2 kHz, it is recommended that the tick rate be a power of 2 (e.g., 16, 32, or 64). Keep in mind that the higher the tick rate, the more overhead the system will incur.

> In the Rabbit version of µC/OS-II, the number of ticks per second defaults to 64. The actual number of ticks per second may be slightly different than the desired ticks per second if `TicksPerSec` does not evenly divide 2048. To change the default tick rate to 32, do the following:

>     #define OS_TICKS_PER_SEC 32
>     ...
>     OSInit();
>     ...
>     OSSetTicksPerSec(OS_TICKS_PER_SEC);
>     ...
>     OSStart();

> #### 18.1.2 Task Creation

> In a µC/OS-II application, stacks are declared as static arrays, and the address of either the top or bottom (depending on the CPU) of the stack is passed to `OSTaskCreate`. In a Rabbit-based system, the Dynamic C development environment provides a superior stack allocation mechanism that µC/OS-II incorporates. Rather than declaring stacks as static arrays, the number of stacks of particular sizes are declared, and when a task is created using either `OSTaskCreate` or `OSTaskCreateExt,` only the size of the stack is passed, not the memory address. This mechanism allows a large number of stacks to be defined without using up root RAM.

> There are five macros located in ucos2.lib that define the number of stacks needed of five different sizes. In order to have three 256 byte stacks, one 512 byte stack, two 1024 byte stacks, one 2048 byte stack, and no 4096 byte stacks, the following macro definitions would be used:

>     #define STACK_CNT_256      3    // number of 256 byte stacks
>     #define STACK_CNT_512      1    // number of 512 byte stacks
>     #define STACK_CNT_1K       2    // number of 1K stacks
>     #define STACK_CNT_2K       1    // number of 2K stacks
>     #define STACK_CNT_4K       0    // number of 4K stacks

> These macros can be placed into each µC/OS-II application so that the number of each size stack can be customized based on the needs of the application. Suppose that an application needs 5 tasks, and each task has a consecutively larger stack. The macros and calls to `OSTaskCreate` would look as follows

>     #define STACK_CNT_256     2     // number of 256 byte stacks
>     #define STACK_CNT_512     2     // number of 512 byte stacks
>     #define STACK_CNT_1K      1     // number of 1K stacks
>     #define STACK_CNT_2K      1     // number of 2K stacks
>     #define STACK_CNT_4K      1     // number of 4K stacks

>     OSTaskCreate(task1, NULL, 256, 0);
>     OSTaskCreate(task2, NULL, 512, 1);
>     OSTaskCreate(task3, NULL, 1024, 2);
>     OSTaskCreate(task4, NULL, 2048, 3);
>     OSTaskCreate(task5, NULL, 4096, 4);

> Note that the macro `STACK_CNT_256` is set to 2 instead of 1. µC/OS-II always creates an idle task which runs when no other tasks are in the ready state. Note also that there are two 512 byte stacks instead of one. This is because the program is given a 512 byte stack. If the application utilizes the µC/OS-II statistics task, then the number of 512 byte stacks would have to be set to 3. (Statistic task creation can be enabled and disabled via the macro `OS_TASK_STAT_EN` which is located in `ucos2.lib`). If only 6 stacks were declared, one of the calls to `OSTaskCreate` would fail.

> If an application uses `OSTaskCreateExt`, which enables stack checking and allows an extension of the Task Control Block, fewer parameters are needed in the Rabbit version of µC/OS-II. Using the macros in the example above, the tasks would be created as follows:

>     OSTaskCreateExt(task1, NULL, 0, 0, 256, NULL, OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);

>     OSTaskCreateExt(task2, NULL, 1, 1, 512, NULL, OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);

>     OSTaskCreateExt(task3, NULL, 2, 2, 1024, NULL, OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);

>     OSTaskCreateExt(task4, NULL, 3, 3, 2048, NULL, OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);

>     OSTaskCreateExt(task5, NULL, 4, 4, 4096, NULL, OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);

> #### 18.1.3 Restrictions

> At the time of this writing, uC/OS-II for Dynamic C is not compatible with the use of Dynamic C's slice statements. Also, see the function description for `OSTimeTickHook` for important information about preserving registers if that stub function is replaced by a user-defined function.

> ### 18.2 Tasking Aware Interrupt Service Routines (TA-ISR)

> Special care must be taken when writing an interrupt service routine (ISR) that will be used in conjunction with µC/OS-II so that µC/OS-II scheduling will be performed at the proper time.

> #### 18.2.1 Interrupt Priority Levels

> µC/OS-II for the Rabbit reserves interrupt priority levels 2 and 3 for interrupts outside of the kernel. Since the kernel is unaware of interrupts above priority level 1, interrupt service routines for interrupts which occur at interrupt priority levels 2 and 3 should not be written to be tasking aware. Also, a µC/OS-II application should only disable interrupts by setting the interrupt priority level to 1, and should never raise the interrupt priority level above 1.

> #### 18.2.2 Possible ISR Scenarios

> There are several different scenarios that must be considered when writing an ISR for use with µC/OS-II. Depending on the use of the ISR, it may or may not have to be written so that it is tasking aware. Consider the scenario in the Figure below. In this situation, the ISR for Interrupt X does not have to be tasking aware since it does not re-enable interrupts before completion and it does not post to a semaphore, mailbox, or queue.

> Figure 6. Type 1 ISR

> If, however, an ISR needs to signal a task to the ready state, then the ISR must be tasking aware. In the example in the Figure below, the TA-ISR increments the interrupt nesting counter, does the work necessary for the ISR, readies a higher priority task, decrements the nesting count, and returns to the higher priority task.

> Figure 7. Type 2 ISR

> It may seem as though the ISR in this Figure does not have to increment and decrement the nesting count. This is, however, very important. If the ISR for Interrupt X is called during an ISR that re-enables interrupts before completion, scheduling should not be performed when Interrupt X completes; scheduling should instead be deferred until the least nested ISR completes. The next Figure shows an example of this situation.

> Figure 8. Type 2 ISR Nested Inside Type 3 ISR

> As can be seen here, although the ISR for interrupt Z does not signal any tasks by posting to a semaphore, mailbox, or queue, it must increment and decrement the interrupt nesting count since it re-enables interrupts (`ipres`) prior to finishing all of its work.

> #### 18.2.3 General Layout of a TA-ISR

> A TA-ISR is just like a standard ISR except that it does some extra checking and house-keeping. The following table summarizes when to use a TA-ISR.

> Table 16: Use of TA-ISR
> | µC/OS-II Application
> | Type 11 | Type 22 | Type 33
> TA-ISR Required? | No | Yes | Yes

> 1 Type 1--Leaves interrupts disabled and does not signal task to ready state 2 Type 2--Leaves interrupts disabled and signals task to ready state 3 Type 3--Reenables interrupts before completion

> The following Figure shows the logical flow of a TA-ISR.

> Figure 9. Logical Flow of a TA-ISR

> #### 18.2.3.1 Sample Code for a TA-ISR

> Fortunately, the Rabbit BIOS and libraries provide all of the necessary flags to make TA-ISRs work. With the code found in Listing 1, minimal work is needed to make a TA-ISR function correctly with µC/OS-II. TA-ISRs allow µC/OS-II the ability to have ISRs that communicate with tasks as well as the ability to let ISRs nest, thereby reducing interrupt latency.

> Just like a standard ISR, the first thing a TA-ISR does is to save the registers that it is going to use (1). Once the registers are saved, the interrupt source is cleared (2) and the nesting counter is incremented (3). Note that `bios_intnesting` is a global interrupt nesting counter provided in the Dynamic C libraries specifically for tracking the interrupt nesting level. If an `ipres` instruction is executed (4) other interrupts can occur before this ISR is completed, making it necessary for this ISR to be a TA-ISR. If it is possible for the ISR to execute before µC/OS-II has been fully initialized and started multi-tasking, a check should be made (5) to insure that µC/OS-II is in a known state, especially if the TA-ISR signals a task to the ready state (6). After the TA-ISR has done its necessary work (which may include making a higher priority task than is currently running ready to run), `OSIntExit` must be called (7). This µC/OS-II function determines the highest priority task ready to run, sets it as the currently running task, and sets the global flag `bios_swpend` if a context switch needs to take place. Interrupts are disabled since a context switch is treated as a critical section (8). If the TA-ISR decrements the nesting counter and the count does not go to zero, then the nesting level is saved in `bios_intnesting` (9), the registers used by the TA-ISR are restored, interrupts are re-enabled (if not already done in (4)), and the TA-ISR returns (12). However, if decrementing the nesting counter in (9) causes the counter to become zero, then `bios_swpend` must be checked to see if a context switch needs to occur (10). If a context switch is not pending, then the nesting level is set (9) and the TA-ISR exits (12). If a context switch is pending, then the remaining context of the previous task is saved and a long call, which insures that the `xpc` is saved and restored properly, is made to `bios_intexit` (11). `bios_intexit` is responsible for switching to the stack of the task that is now ready to run and executing a long call to switch to the new task. The remainder of (11) is executed when a previously preempted task is allowed to run again.

> ### Listing 1

>     #asm
>     taskaware_isr::
>        push   af                       ;push regs needed by isr    (1)
>        push   hl                       ;clear interrupt source     (2)
>        ld     hl,bios_intnesting       ;increase the nesting count (3)
>        inc   (hl)
>        ; ipres (optional)                                          (4)
>        ; do processing necessary for interrupt
>        ld     a,(OSRunning)            ;MCOS multitasking yet?     (5)
>        or     a
>        jr     z,taisr_decnesting
>        ; possibly signal task to become ready                      (6)
>        call   OSIntExit                 ;sets bios_swpend if higher
>                                         ; prio ready               (7)

>     taisr_decnesting:
>        push   ip                                                   (8)
>        ipset  1
>        ld     hl,bios_intnesting        ;nesting counter == 1?
>        dec    (hl)                                                 (9)
>        jr     nz,taisr_noswitch

>        ld     a,(bios_swpend)           ;switch pending?           (10)
>        or     a
>        jr     z,taisr_noswitch

>        push   de                                                   (11)
>        push   bc
>        ex     af,af'
>        push   af
>        exx
>        push   hl
>        push   de
>        push   bc
>        push   iy
>        lcall  bios_intexit
>        pop    iy
>        pop    bc
>        pop    de
>        pop    hl
>        exx
>        pop    af
>        ex     af,af'
>        pop    bc
>        pop    de

>     taisr_noswitch:
>        pop   ip

>     taisr_done:
>        pop    hl                                                    (12)
>        pop    af
>        ipres
>        ret
>     #endasm

> ### 18.3 Library Reentrancy

> When writing a µC/OS-II application, it is important to know which Dynamic C library functions are non-reentrant. If a function is non-reentrant, then only one task may access the function at a time, and access to the function should be controlled with a µC/OS-II semaphore. The following is a list of Dynamic C functions that are non-reentrant.

> Library | Non-reentrant Functions
> `MATH.LIB` | randg, randb, rand
> `RS232.LIB` | All
> `RTCLOCK.LIB` | write_rtc, tm_wr
> `STDIO.LIB` | kbhit, getchar, gets, getswf, selectkey
> `STRING.LIB` | atof1, atoi1, strtok
> `SYS.LIB` | clockDoublerOn, clockDoublerOff, useMainOsc, useClockDivider, use32kHzOsc
> `VDRIVER.LIB` | VdGetFreeWd, VdReleaseWd
> `XMEM.LIB` | WriteFlash
> `JRIO.LIB` | digOut, digOn, digOff, jrioInit, anaIn, anaOut, cof_anaIn
> `JR485.LIB` | All

> 1 reentrant but sets the global `_xtoxErr` flag

> The serial port functions (`RS232.LIB` functions) should be used in a restricted manner with µC/OS-II. Two tasks can use the same port as long as both are not reading, or both are not writing; i.e., one task can read from serial port X and another task can write to serial port X at the same time without conflict.

> ### 18.4 How to Get a µC/OS-II Application Running

> µC/OS-II is a highly configureable, real-time operating system. It can be customized using as many or as few of the operating system's features as needed. This section outlines:

> - The configuration constants used in µC/OS-II

> - How to override the default configuration supplied in `UCOS2.LIB`

> - The necessary steps to get an application running

> It is assumed that the reader has a familiarity with µC/OS-II or has a µC/OS-II reference (MicroC/OS-II, The Real Time Kernel by Jean J. Labrosse is highly recommended).

> #### 18.4.1 Default Configuration

> µC/OS-II usually relies on the include file `os_cfg.h``` to get values for the configuration constants. In the Dynamic C implementation of µC/OS-II, these constants, along with their default values, are in `os_cfg.lib`. A default stack configuration is also supplied in `os_cfg.lib`. µC/OS-II for the Rabbit uses a more intelligent stack allocation scheme than other µC/OS-II implementations to take better advantage of unused memory.

> The default configuration allows up to 10 normally created application tasks running at 64 ticks per second. Each task has a 512-byte stack. There are 2 queues specified, and 10 events. An event is a queue, mailbox or semaphore. You can define any combination of these three for a total of 10. If you want more than 2 queues, however, you must change the default value of `OS_MAX_QS`.

> Some of the default configuration constants are:

>     // Maximum number of events (semaphores, queues, mailboxes)
>     #define OS_MAX_EVENTS 10

>     // Maximum number of tasks (less stat and idle tasks)
>     #define OS_MAX_TASKS 10

>     // Maximum number of queues in system
>     #define OS_MAX_QS 2

>     // Maximum number of memory partitions
>     #define OS_MAX_MEM_PART 1

>     // Enable normal task creation
>     #define OS_TASK_CREATE_EN 1

>     //Disable extended task creation
>     #defineOS_TASK_CREATE_EXT_EN 0

>     // Disable task deletion
>     #define OS_TASK_DEL_EN 0

>     // Disable statistics task creation
>     #define OS_TASK_STAT_EN 0

>     // Enable queue usage
>     #define OS_Q_EN 1

>     // Disable memory manager
>     #define OS_MEM_EN 0

>     // Enable mailboxes
>     #define OS_MBOX_EN 1

>     #// Enable semaphores
>     define OS_SEM_EN 1

>     // # of ticks in one second
>     #define OS_TICKS_PER_SEC 64

>     // # of 256 byte stacks (idle task stack)
>     #define STACK_CNT_256 1

>     //# of 512-byte stacks (task stacks + initial program stack)
>     #define STACK_CNT_512 OS_MAX_TASKS+1

> If a particular portion of µC/OS-II is disabled, the code for that portion will not be compiled, making the overall size of the operating system smaller. Take advantage of this feature by customizing µC/OS-II based on the needs of each application.

> #### 18.4.2 Custom Configuration

> In order to customize µC/OS-II by enabling and disabling components of the operating system, simply redefine the configuration constants as necessary for the application.

>     #define OS_MAX_EVENTS          2
>     #define OS_MAX_TASKS          20
>     #define OS_MAX_QS              1
>     #define OS_MAX_MEM_PART       15
>     #define OS_TASK_STAT_EN        1
>     #define OS_Q_EN                0
>     #define OS_MEM_EN              1
>     #define OS_MBOX_EN             0
>     #define OS_TICKS_PER_SEC      64

> If a custom stack configuration is needed also, define the necessary macros for the counts of the different stack sizes needed by the application.

>     #define STACK_CNT_256 1  // idle task stack
>     #define STACK_CNT_512 2  // initial program + stat task stack
>     #define STACK_CNT_1K 10  // task stacks
>     #define STACK_CNT_2K 10  // number of 2K stacks

> In the application code, follow the µC/OS-II and stack configuration constants with a `#use "ucos2.lib"` statement. This ensures that the definitions supplied outside of the library are used, rather than the defaults in the library.

> This configuration uses 20 tasks, two semaphores, up to 15 memory partitions that the memory manager will control, and makes use of the statistics task. Note that the configuration constants for task creation, task deletion, and semaphores are not defined, as the library defaults will suffice. Also note that 10 of the application tasks will each have a 1024 byte stack, 10 will each have a 2048 byte stack, and an extra stack is declared for the statistics task.

> #### 18.4.3 Examples

> The following sample programs demonstrate the use of the default configuration supplied in `UCOS2.LIB` and a custom configuration which overrides the defaults.

> ### Example 1

> In this application, ten tasks are created and one semaphore is created. Each task pends on the semaphore, gets a random number, posts to the semaphore, displays its random number, and finally delays itself for three seconds.

> Looking at the code for this short application, there are several things to note. First, since µC/OS-II and slice statements are mutually exclusive (both rely on the periodic interrupt for a "heartbeat"), `#use "ucos2.lib"` must be included in every µC/OS-II application (1). In order for each of the tasks to have access to the random number generator semaphore, it is declared as a global variable (2). In most cases, all mailboxes, queues, and semaphores will be declared with global scope. Next, `OSInit` must be called before any other µC/OS-II function to ensure that the operating system is properly initialized (3). Before µC/OS-II can begin running, at least one application task must be created. In this application, all tasks are created before the operating system begins running (4). It is perfectly acceptable for tasks to create other tasks. Next, the semaphore each task uses is created (5). Once all of the initialization is done, `OSStart` is called to start µC/OS-II running (6). In the code that each of the tasks run, it is important to note the variable declarations. The default storage class in Dynamic C is static, so to ensure that the task code is reentrant, all are declared auto (7). Each task runs as an infinite loop and once this application is started, µC/OS-II will run indefinitely.

>     // 1. Explicitly use uC/OS-II library
>     #use "ucos2.lib"

>     void RandomNumberTask(void *pdata);

>     // 2. Declare semaphore global so all tasks have access
>     OS_EVENT* RandomSem;

>     void main()
>     {
>        int i;

>        // 3. Initialize OS internals
>        OSInit();

>        for(i = 0; i < OS_MAX_TASKS; i++)

>           // 4. Create each of the system tasks
>           OSTaskCreate(RandomNumberTask, NULL, 512, i);

>        // 5. semaphore to control access to random number generator
>        RandomSem = OSSemCreate(1);

>        // 6. Begin multitasking
>        OSStart();
>     }

>     void RandomNumberTask(void *pdata)
>     {
>        // 7. Declare as auto to ensure reentrancy.
>        auto OS_TCB data;
>        auto INT8U  err;
>        auto INT16U RNum;

>        OSTaskQuery(OS_PRIO_SELF, &data);
>        while(1)
>        {
>           // Rand is not reentrant, so access must be controlled
>           // via a semaphore.
>           OSSemPend(RandomSem, 0, &err);
>           RNum = (int)(rand() * 100);
>           OSSemPost(RandomSem);
>           printf("Task%d's random #: %d\n",data.OSTCBPrio,RNum);

>           // Wait 3 seconds in order to view output from each task.
>           OSTimeDlySec(3);
>        }
>     }

> ### Example 2

> This application runs exactly the same code as Example 1, except that each of the tasks are created with 1024 byte stacks. The main difference between the two is the configuration of µC/OS-II.

> First, each configuration constant that differs from the library default is defined. The configuration in this example differs from the default in that it allows only two events (the minimum needed when using only one semaphore), 20 tasks, no queues, no mailboxes, and the system tick rate is set to 32 ticks per second (1). Next, since this application uses tasks with 1024 byte stacks, it is necessary to define the configuration constants differently than the library default (2). Notice that one 512 byte stack is declared. Every Dynamic C program starts with an initial stack, and defining `STACK_CNT_512` is crucial to ensure that the application has a stack to use during initialization and before multi-tasking begins. Finally `ucos2.lib` is explicitly used (3). This ensures that the definitions in (1 and 2) are used rather than the library defaults. The last step in initialization is to set the number of ticks per second via `OSSetTicksPerSec` (4).

> The rest of this application is identical to example 1 and is explained in the previous section.

>     // 1. Define necessary configuration constants for uC/OS-II
>     #define OS_MAX_EVENTS          2
>     #define OS_MAX_TASKS          20
>     #define OS_MAX_QS              0
>     #define OS_Q_EN                0
>     #define OS_MBOX_EN             0
>     #define OS_TICKS_PER_SEC      32

>     // 2. Define necessary stack configuration constants
>     #define STACK_CNT_512 1            // initial program stack
>     #define STACK_CNT_1K OS_MAX_TASKS   // task stacks

>     // 3. This ensures that the above definitions are used
>     #use "ucos2.lib"

>     void RandomNumberTask(void *pdata);

>     // Declare semaphore global so all tasks have access
>     OS_EVENT* RandomSem;

>     void main(){
>        int i;

>        // Initialize OS internals
>        OSInit();

>        for(i = 0; i < OS_MAX_TASKS; i++){

>           // Create each of the system tasks
>           OSTaskCreate(RandomNumberTask, NULL, 1024, i);

>        }
>        // semaphore to control access to random number generator
>        RandomSem = OSSemCreate(1);

>        // 4. Set number of system ticks per second
>        OSSetTicksPerSec(OS_TICKS_PER_SEC);

>        // Begin multi-tasking
>        OSStart();
>     }

>     void RandomNumberTask(void *pdata)
>     {
>        // Declare as auto to ensure reentrancy.
>        auto OS_TCB data;
>        auto INT8U  err;
>        auto INT16U RNum;

>        OSTaskQuery(OS_PRIO_SELF, &data);
>        while(1)
>        {
>           // Rand is not reentrant, so access must be controlled
>           // via a semaphore.
>           OSSemPend(RandomSem, 0, &err);
>           RNum = (int)(rand() * 100);
>           OSSemPost(RandomSem);

>           printf("Task%02d's random #: %d\n",data.OSTCBPrio,RNum);

>           // Wait 3 seconds in order to view output from each task.
>           OSTimeDlySec(3);
>        }
>     }

> ### 18.5 Compatibility with TCP/IP

> The TCP/IP stack is reentrant and may be used with the µC/OS real-time kernel. The line

>     #use ucos2.lib

> must appear before the line

>     #use dcrtcp.lib

> A call to `OSInit()` must be made before calling `sock_init()`.

> #### 18.5.1 Socket Locks

> Each socket used in a µC/OS-II applicatioin program has an associated socket lock. Each socket lock uses one semaphore of type `OS_EVENT`. Therefore, the macro `MAX_OS_EVENTS` must take into account each of the socket locks, plus any events that the application program may be using (semaphores, queues, mailboxes, event flags, or mutexes).

> Determining `OS_MAX_EVENTS` may get a little tricky, but it isn't too bad if you know what your program is doing. Since `MAX_SOCKET_LOCKS` is defined as:

>     #define MAX_SOCKET_LOCKS (MAX_TCP_SOCKET_BUFFERS + MAX_UDP_SOCKET_BUFFERS)

> `OS_MAX_EVENTS` may be defined as:

>     #define OS_MAX_EVENTS MAX_TCP_SOCKET_BUFFERS + MAX_UDP_SOCKET_BUFFERS + 2 + z

> The constant "2" is included for the two global locks used by TCP/IP, and `z` is the number of `OS_EVENTS` (semaphores, queues, mailboxes, event flags, or mutexes) required by the program.

> If either `MAX_TCP_SOCKET_BUFFERS` or `MAX_UDP_SOCKET_BUFFERS` is not defined by the application program prior to the #use statements for `ucos.lib` and `dcrtcp.lib` default values will be assigned.

> If `MAX_TCP_SOCKET_BUFFERS` is not defined in the application program, it will be defined as `MAX_SOCKETS`. If, however, `MAX_SOCKETS` is not defined in the application program, `MAX_TCP_SOCKET_BUFFERS` will be 4.

> If `MAX_UDP_SOCKET_BUFFERS` is not defined in the application program, it will be defined as 1 if `USE_DHCP` is defined, or 0 otherwise.

> For more information regarding TCP/IP, please see the Dynamic C TCP/IP User's Manual, available online at zworld.com or rabbitsemiconductor.com.

> ### 18.6 Debugging Tips

> Dynamic C version 7.20 introduced more control when single-stepping through a µC/OS-II program. Prior to 7.20, single-stepping occured in whichever task was currently running. It was not possible to limit the single-stepping to one task.

> Starting with Dynamic C 7.20, single-stepping may be limited to the currently running task by using F8 (Step over). If the task is suspended, single-stepping will also be suspended. When the task is put back in a running state, single-stepping will continue at the statement following the statement that suspended execution of the task.

> Hitting F7 (Trace into) at a statement that suspends execution of the current task will cause the program to step into the next active task that has debug information. It may be useful to put a watch on the global variable `OSPrioCur` to see which task is currently running.

> For example, if the current task is going to call `OSSemPend()` on a semaphore that is not in the signaled state, the task will be suspended and other tasks will run. If F8 is pressed at the statement that calls `OSSemPend()`, the debugger will not single-step in the other running tasks that have debug information; single-stepping will continue at the statement following the call to `OSSemPend()`. If F7 is pressed at the statement that calls `OSSemPend()` instead of F8, the debugger will single-step in the next task with debug information that is put into the running state..

Z-World http://www.zworld.com Voice: (530) 757-3737 FAX: (530) 757-3792 sales@zworld.com