- •Preface
- •Introduction
- •1.01 INCLUDES.H
- •1.02 Compiler Independent Data Types
- •1.03 Global Variables
- •1.04 OS_ENTER_CRITICAL() and OS_EXIT_CRITICAL()
- •1.05 PC Based Services
- •1.05.01 PC Based Services, Character Based Display
- •1.05.02 PC Based Services, Elapsed Time Measurement
- •1.05.03 PC Based Services, Miscellaneous
- •1.07 Example #1
- •1.08 Example #2
- •1.09 Example #3
- •2.00 Foreground/Background Systems
- •2.01 Critical Section of Code
- •2.02 Resource
- •2.03 Shared Resource
- •2.04 Multitasking
- •2.05 Task
- •2.06 Context Switch (or Task Switch)
- •2.07 Kernel
- •2.08 Scheduler
- •2.09 Non-Preemptive Kernel
- •2.10 Preemptive Kernel
- •2.11 Reentrancy
- •2.12 Round Robin Scheduling
- •2.13 Task Priority
- •2.14 Static Priorities
- •2.15 Dynamic Priorities
- •2.16 Priority Inversions
- •2.17 Assigning Task Priorities
- •2.19 Mutual Exclusion
- •2.20 Deadlock (or Deadly Embrace)
- •2.21 Synchronization
- •2.22 Event Flags
- •2.23 Intertask Communication
- •2.24 Message Mailboxes
- •2.25 Message Queues
- •2.26 Interrupts
- •2.27 Interrupt Latency
- •2.28 Interrupt Response
- •2.29 Interrupt Recovery
- •2.30 Interrupt Latency, Response, and Recovery
- •2.31 ISR Processing Time
- •2.32 Non-Maskable Interrupts (NMIs)
- •2.33 Clock Tick
- •2.34 Memory Requirements
- •2.35 Advantages and Disadvantages of Real-Time Kernels
- •2.36 Real-Time Systems Summary
- •3.00 Critical Sections
- •3.01 Tasks
- •3.02 Task States
- •3.03 Task Control Blocks (OS_TCBs)
- •3.04 Ready List
- •3.05 Task Scheduling
- •3.06 Locking and Unlocking the Scheduler
- •3.07 Idle Task
- •3.08 Statistics Task
- •3.10 Clock Tick
- •3.14 OSEvent???() functions
- •4.00 Creating a Task, OSTaskCreate()
- •4.01 Creating a Task, OSTaskCreateExt()
- •4.02 Task Stacks
- •4.03 Stack Checking, OSTaskStkChk()
- •4.04 Deleting a Task, OSTaskDel()
- •4.05 Requesting to delete a task, OSTaskDelReq()
- •4.06 Changing a Task’s Priority, OSTaskChangePrio()
- •4.07 Suspending a Task, OSTaskSuspend()
- •4.08 Resuming a Task, OSTaskResume()
- •4.09 Getting Information about a Task, OSTaskQuery()
- •5.00 Delaying a task, OSTimeDly()
- •5.01 Delaying a task, OSTimeDlyHMSM()
- •5.02 Resuming a delayed task, OSTimeDlyResume()
- •5.03 System time, OSTimeGet() and OSTimeSet()
- •6.00 Event Control Blocks
- •6.01 Initializing an ECB, OSEventWaitListInit()
- •6.02 Making a task ready, OSEventTaskRdy()
- •6.03 Making a task wait for an event, OSEventTaskWait()
- •6.04 Making a task ready because of a timeout, OSEventTO()
- •6.05 Semaphores
- •6.06 Message Mailboxes
- •6.07 Message Queues
- •7.00 Memory Control Blocks
- •7.01 Creating a partition, OSMemCreate()
- •7.02 Obtaining a memory block, OSMemGet()
- •7.03 Returning a memory block, OSMemPut()
- •7.04 Obtaining status about memory partition, OSMemQuery()
- •7.05 Using memory partitions
- •7.06 Waiting for memory blocks from a partition
- •8.00 Development Tools
- •8.01 Directories and Files
- •8.02 INCLUDES.H
- •9.00 Development Tools
- •9.01 Directories and Files
- •9.02 INCLUDES.H
- •9.06 Memory requirements
- •9.07 Execution times
- •10.00 Directories and Files
- •10.01 INCLUDES.H
- •10.02.01 OS_CPU.H, Compiler specific data types
- •10.02.02 OS_CPU.H, OS_ENTER_CRITICAL() and OS_EXIT_CRITICAL()
- •10.02.03 OS_CPU.H, OS_STK_GROWTH
- •10.02.04 OS_CPU.H, OS_TASK_SW()
- •10.03.01 OS_CPU_A.ASM, OSStartHighRdy()
- •10.03.02 OS_CPU_A.ASM, OSCtxSw()
- •10.03.03 OS_CPU_A.ASM, OSIntCtxSw()
- •10.03.04 OS_CPU_A.ASM, OSTickISR()
- •10.04.01 OS_CPU_C.C, OSTaskStkInit()
- •10.04.02 OS_CPU_C.C, OSTaskCreateHook()
- •10.04.03 OS_CPU_C.C, OSTaskDelHook()
- •10.04.04 OS_CPU_C.C, OSTaskSwHook()
- •10.04.05 OS_CPU_C.C, OSTaskStatHook()
- •10.04.06 OS_CPU_C.C, OSTimeTickHook()
- •10.05 Summary
- •OSInit()
- •OSIntEnter()
- •OSIntExit()
- •OSMboxAccept()
- •OSMboxCreate()
- •OSMboxPend()
- •OSMboxPost()
- •OSMboxQuery()
- •OSMemCreate()
- •OSMemGet()
- •OSMemPut()
- •OSMemQuery()
- •OSQAccept()
- •OSQCreate()
- •OSQFlush()
- •OSQPend()
- •OSQPost()
- •OSQPostFront()
- •OSQQuery()
- •OSSchedLock()
- •OSSchedUnlock()
- •OSSemAccept()
- •OSSemCreate()
- •OSSemPend()
- •OSSemPost()
- •OSSemQuery()
- •OSStart()
- •OSStatInit()
- •OSTaskChangePrio()
- •OSTaskCreate()
- •OSTaskCreateExt()
- •OSTaskDel()
- •OSTaskDelReq()
- •OSTaskResume()
- •OSTaskStkChk()
- •OSTaskSuspend()
- •OSTaskQuery()
- •OSTimeDly()
- •OSTimeDlyHMSM()
- •OSTimeDlyResume()
- •OSTimeGet()
- •OSTimeSet()
- •OSTimeTick()
- •OSVersion()
7.00 Memory Control Blocks
µC/OS-II keeps track of memory partitions through the use of a data structure called amemory control block as shown in listing 7.1. Each memory partition requires its own memory control block.
typedef struct {
void *OSMemAddr; void *OSMemFreeList; INT32U OSMemBlkSize; INT32U OSMemNBlks; INT32U OSMemNFree;
} OS_MEM;
Listing 7.1, Memory control block data structure.
OSMemAddr is a pointer to the beginning (i.e. base) of the memory partition from which memory blocks will be allocated from. This field is initialized when you create a partition (see section 7.01, Creating a partition) and is not used thereafter.
OSMemFreeListis a pointer used by µC/OS-II to point to either the next free memory control block or to the next free memory block. The use depends on whether the memory partition has been created or not (see section 7.01).
OSMemBlkSize determines the size of each memory block in the partition and is a parameter you specify when the memory partition is created (see section 7.01).
OSMemNBlks establishes the total number of memory blocks available from the partition. This parameter is specified when the partition is created (see section 7.01).
OSMemNFreeis used to determine how many memory blocks are available from the partition.
µC/OS-II initializes the memory manager if you configure OS_MEM_EN to 1 in OS_CFG.H. Initialization is done by OSMemInit() (which is automatically called by OSInit()) and consist of creating a linked list of
memory control blocks as shown in figure 7-3. You specify the maximum number of memory partitions with the configuration constant OS_MAX_MEM_PART (see OS_CFG.H) which MUST be set to at least 2.
As can be seen, t he OSMemFreeList field of the control block is used to chain the free control blocks.
OSMemAddr |
|
OSMemAddr |
|
OSMemAddr |
OSMemFreeList OSMemFreeList OSMemFreeList OSMemFreeList 0
OSMemBlkSize |
|
OSMemBlkSize |
|
OSMemBlkSize |
OSMemNBlks |
OSMemNBlks |
|
OSMemNBlks |
|
|
|
|
|
|
OSMemNFree |
OSMemNFree |
|
OSMemNFree |
OS_MAX_MEM_PART
Figure 7-3, List of free memory control blocks.
7.01 Creating a partition, OSMemCreate()
Your application must create each partition before they can be used. You create a memory partition by calling OSMemCreate(). Listing 7.2 shows how you could create a memory partition containing 100 blocks of 32 bytes each.
OS_MEM *CommTxBuf;
INT8U CommTxPart[100][32];
void main (void)
{
INT8U err;
OSInit();
.
.
CommTxBuf = OSMemCreate(CommTxPart, 100, 32, &err);
.
.
OSStart();
}
Listing 7.2, Creating a memory partition.
The code to create a memory partition is shown in listing 7.3. OSMemCreate() requires four arguments: the beginning address of the memory partition., the number of blocks to be allocated from this partition, the size (in bytes) of each block and, a pointer to a variable that will contain an error code when OSMemCreate() returns or a NULL pointer if OSMEMCreate() fails. Upon success, OSMemCreate() returns a pointer to the
allocated memory control block. This pointer must be used in subsequent calls to memory management services (see
OSMemGet(), OSMemPut() and OSMemQuery() in the next sections).
Each memory partition must contain at least 2 memory blocks L7.3(1). Also, each memory block must be able to hold
the size of a pointer because a pointer is used to chain all the memory blocks together L7.3(2). Next, OSMemCreate() obtains a memory control block from the list of free memory control blocks L7.3(3). The
memory control block will contain run-time information about the memory partition. OSMemCreate() will not
be able to create a memory partition unless a memory control block is available L7.3(4). If a memory control block is available and we satisfied all the previous conditions, the memory blocks within the partition are linked together in a
singly linked list L7.3(5). When all the blocks are linked, the memory control block is filled with information about the partition L7.3(6). OSMemCreate() returns the pointer to the memory control block so it can be used in
subsequent calls to access the memory blocks from this partition L7.3(7).
OS_MEM *OSMemCreate (void *addr, INT32U nblks, INT32U blksize, INT8U *err)
{
OS_MEM *pmem;
INT8U *pblk; void **plink;
INT32U i;
if (nblks < 2) { |
|
(1) |
||
*err = OS_MEM_INVALID_BLKS; |
|
|||
return ((OS_MEM *)0); |
|
|||
} |
|
|
|
|
if (blksize < sizeof(void *)) { |
(2) |
|||
*err = OS_MEM_INVALID_SIZE; |
|
|||
return ((OS_MEM *)0); |
|
|||
} |
|
|
|
|
OS_ENTER_CRITICAL(); |
|
|||
pmem = OSMemFreeList; |
(3) |
|||
if (OSMemFreeList != (OS_MEM *)0) { |
|
|||
OSMemFreeList = (OS_MEM *)OSMemFreeList->OSMemFreeList; |
|
|||
} |
|
|
|
|
OS_EXIT_CRITICAL(); |
|
|
||
if (pmem == (OS_MEM *)0) { |
(4) |
|||
*err = OS_MEM_INVALID_PART; |
|
|||
return ((OS_MEM *)0); |
|
|||
} |
|
|
|
|
plink = (void **)addr; |
(5) |
|||
pblk |
= (INT8U *)addr + blksize; |
|
||
for (i = 0; i < (nblks - 1); i++) { |
|
|||
*plink = (void *)pblk; |
|
|||
|
plink |
= (void **)pblk; |
|
|
} |
pblk |
= pblk + blksize; |
|
|
|
|
|
|
|
*plink = (void *)0; |
|
|
||
OS_ENTER_CRITICAL(); |
|
|||
pmem->OSMemAddr |
= addr; |
(6) |
||
pmem->OSMemFreeList = addr; |
|
|||
pmem->OSMemNFree |
= nblks; |
|
||
pmem->OSMemNBlks |
= nblks; |
|
||
pmem->OSMemBlkSize |
= blksize; |
|
||
OS_EXIT_CRITICAL(); |
|
|
||
*err |
= OS_NO_ERR; |
|
|
|
return (pmem); |
|
(7) |
}
Listing 7.3, OSMemCreate()
Figure 7-4 shows how the data structures look like when OSMemCreate() completes successfully. Note that
the memory blocks are sho w nicely linked one after the other. At run -time, as you allocate and de -allocate memory blocks, the blocks will most likely not be in this order.
pmem |
|
|
OSMemAddr = addr |
|
OSMemFreeList= addr
OSMemBlkSize = blksize
OSMemNBlks = nblks
OSMemNFree = nblks
Contiguous memory
OSMemCreate() arguments
0
Figure 7-4, OSMemCreate()
7.02 Obtaining a memory block, OSMemGet()
Your application can get a memory block from one of the created memory partition by calling OSMemGet(). You simply use the pointer returned by OSMemCreate() in the call to OSMemGet() to specify which partition the memory block will come from. Obviously, you application will need to know how big the memory block obtained is so that it doesn’t exceeds its storage capacity. In other words, you must not use more memory than what is available from the memory block. For example, if a partition contains 32 byte blocks then your application can use up to 32 bytes. When you are done using the block, you must return the block to the proper memory partition (see section 7.03,
Returning a memory block, OSMemPut()).
Listing 7.4 shows the code for OSMemGet(). The pointer specify the partition from which you want to get a memory block from L7.4(1). OSMemGet() first checks to see if there are free blocks available L7.4(2). If a block
is available, it is removed from the free list L7.4(3). The free list is then updated L7.4(4) so that it points to the next free memory block and, the number of blocks is decremented indicating that it has been allocated L7.4(5). The pointer to the allocated block is finally returned to your application L7.4(6).
void *OSMemGet (OS_MEM *pmem, INT8U *err) |
(1) |
||
{ |
|
|
|
void |
*pblk; |
|
|
OS_ENTER_CRITICAL(); |
|
|
|
if (pmem->OSMemNFree > 0) { |
(2) |
||
|
pblk |
= pmem->OSMemFreeList; |
(3) |
pmem->OSMemFreeList = *(void **)pblk; |
(4) |
||
pmem->OSMemNFree--; |
|
(5) |
|
OS_EXIT_CRITICAL(); |
|
|
|
*err = OS_NO_ERR; |
|
|
|
return (pblk); |
|
(6) |
|
} else { |
|
|