Introduction
The NetBurner Real-Time Operating System (NBRTOS) is a full-featured preemptive multitasking RTOS supporting:
- Multiple tasks with 255 priority levels
- Semaphores for resource protection
- Mailboxes for message passing
- FIFOs for flexible queuing
- Message queues for structured communication
- Critical sections for mutual exclusion
The RTOS comes pre-configured as part of the NetBurner development package. Your application begins execution in the UserMain()
task.
Priority Levels
To conserve system resources, the default number of tasks is 64, with 64 being the Idle task that will run when all other higher priority tasks are blocked. You can increase the number of tasks up to 255. There are a number of system tasks running at priorities defined in constants.h. The recommended UserMain() priority is MAIN_PRIO, which is a value of 50. When creating additonal tasks:
- Add or subtract from MAIN_PRIO for readability. For example, MAIN_PRIO-1 is a higher priority task, which MAIN_PRIO+1 is a lower priority task.
- Use the helper task function:
OSGetNextPrio()
to choose. For example, OSGetNextPrio(OSNextPrio::Above)
enum class OSNextPrio {
Maximum = -2,
Above = -1,
Below = 0,
Next = Below,
Minimum = 1
};
Task Priority Range:
Priority Range: 1 - 255 (Default maximum is 64).
┌─────────────────────────────────────┐
│ Lower Number = Higher Priority │
│ │
│ 1 <--- Highest Priority │
│ 2 │
│ . │
│ . Application Tasks │
│ . │
│ 64 <--- Lowest Priority (Idle) │
│ . (Default Maximum) │
│ . │
│ . │
│ . │
│ 254 │
│ 255 <--- Lowest Priority │
└─────────────────────────────────────┘
Important**: Each priority level can only be used by ONE task at a time. Always check return values when creating tasks or changing priorities.
What is a Preemptive RTOS?
Core Principle
The highest priority task ready to run will ALWAYS run.**
This is fundamentally different from time-sliced operating systems like Windows or Unix. In NBRTOS:
- No round-robin scheduling
- No time slices
- Priority-based execution only
Task Execution Flow
Task A (Priority 50) Task B (Priority 51)
┌──────────────┐ ┌──────────────┐
│ Running │ │ Blocked │
│ │ │ │
└──────┬───────┘ └──────────────┘
│
│ Calls blocking function
│ (OSTimeDly, OSSemPend, etc.)
v
┌──────────────┐ ┌──────────────┐
│ Blocked │ │ Running │
│ │ │ │
└──────────────┘ └──────┬───────┘
│
│ Task A unblocks
v
┌──────────────┐ ┌──────────────┐
│ Running │ │ Blocked │
│ (Higher │ │ (Lower │
│ Priority) │ │ Priority) │
└──────────────┘ └──────────────┘
Blocking Scenarios
A higher priority task MUST block for lower priority tasks to execute:
┌─────────────────────────────────────────────┐
│ When Tasks Block │
├─────────────────────────────────────────────┤
│ │
│ 1. Time Delay │
│ │ │
│ v │
│ [Task sleeps
for specified
time] │
│ │
│ 2. Resource Wait │
│ OSSemPend(&sem, timeout) │
│ │ │
│ v │
│ [Task waits until resource available] │
│ │
│ 3. I/O Operation │
│ │ │
│ v │
│ [Task waits for data] │
│ │
└─────────────────────────────────────────────┘
#define TICKS_PER_SECOND
System clock ticks per second.
Definition constants.h:49
int read(int fd, char *buf, int nbytes)
Read data from a file descriptor (fd).
time_t time(time_t *pt)
Gets the current system GMT time.
Function Categories
RTOS Blocking Functions
Functions that pend on resources or create time delays:
OSTimeDly()
- Time delay
OSSemPend()
- Semaphore wait
OSMboxPend()
- Mailbox wait
OSQPend()
- Queue wait
OSFlagPendAny()
- Flag wait (any)
OSFlagPendAll()
- Flag wait (all)
I/O Functions That Block
Functions that perform read operations or pend on file descriptors:
select()
- Wait for I/O readiness
read()
- Read data (blocks until data available)
write()
- Write data (blocks until space available)
gets()
- Get string from input
getchar()
- Get character
fgets()
- Get string from file
Network Functions That Block
Functions that wait for connections or data:
accept()
- Wait for incoming connection
UDPPacket()
- Wait to receive UDP packet (when configured for receive)
Functions That Unblock Tasks
Functions that post to pending resources:
OSMboxPost()
- Post message to mailbox
OSQPost()
- Post message to queue
OSSemPost()
- Post to semaphore
System Task Priorities
System tasks use predefined priority levels defined in nbrtos/include/constants.h
. The exact tasks depend on:
- Your platform (MOD5441X, SOMRT1061, etc.)
Enabled system features (HTTP server, network stack, etc.)
Example**: Calling StartHTTP()
creates a system task for web server request handling.
Task Creation
Entry Point: UserMain()
Every NetBurner application begins in the UserMain()
task, which is created automatically by the system.
System Boot
│
v
┌─────────────────────┐
│ System Init │
│ - Hardware setup │
│ - RTOS startup │
└──────────┬──────────┘
│
v
┌─────────────────────┐
│ UserMain() starts │
│ - Create tasks │
│ - Main loop │
└─────────────────────┘
void StartHttp(uint16_t port, bool RunConfigMirror)
Start the HTTP web server. Further documentation in the Initialization section Initialization - Syste...
void init()
System initialization. Ideally called at the beginning of all applications, since the easiest Recover...
Task Creation Methods
Method 1: OSTaskCreatewName() - Full Control
Provides complete control over all task parameters with status return value.
Parameters**:
- Task function name
- Optional parameter to pass (NULL if unused)
- Top of task stack
- Bottom of task stack
- Task priority
Task name (string)
Returns**: Status code (OS_NO_ERR on success)
Method 2: OSSimpleTaskCreatewName() - Simplified
Automatically allocates stack space, requires only:
- Task function name
- Task priority
Task name (string)
Note**: Does not return status code
Priority Selection Helper
Use OSGetNextPrio()
to automatically find an available priority, but ensure:
- Not higher than critical system tasks (e.g., ETHER_TASK_PRIO)
- Not too low for your real-time requirements
- Refer to constants.h for system priority definitions
Complete Example
#include <init.h>
#include <stdlib.h>
#include <nbrtos.h>
#include <system.h>
#include <utils.h>
const char *AppName = "Task Creation Example";
uint32_t TaskAllParamsStack[USER_TASK_STK_SIZE];
void TaskAllParams(void *pd)
{
uint32_t loopCount = 0;
uint32_t delayTime = (uint32_t)pd;
printf("TaskAllParams delay: %ld seconds\r\n", delayTime);
while (1)
{
printf("TaskAllParams iteration: %ld\r\n", loopCount);
loopCount++;
}
}
void TaskSimple(void *pd)
{
uint32_t loopCount = 0;
uint32_t delayTime = 6;
printf("TaskSimple delay: %ld seconds\r\n", delayTime);
while (1)
{
printf("TaskSimple iteration: %ld\r\n", loopCount);
loopCount++;
}
}
void UserMain(void *pd)
{
uint32_t delayTime = 3;
int returnCode;
printf("Creating TaskAllParams...");
returnCode = OSTaskCreatewName(
TaskAllParams,
(void *)delayTime,
&TaskAllParamsStack[USER_TASK_STK_SIZE],
TaskAllParamsStack,
"TaskAllParams"
);
if (returnCode == OS_NO_ERR)
printf("Success\r\n");
else
printf("*** Error: %d\r\n", returnCode);
printf("Creating TaskSimple\r\n");
OSSimpleTaskCreatewName(TaskSimple,
MAIN_PRIO - 2,
"TaskSimple");
while (1)
{
}
}
#define MAIN_PRIO
Recommend UserMain priority.
Definition constants.h:130
bool WaitForActiveNetwork(uint32_t ticks_to_wait=120 *TICKS_PER_SECOND, int interface=-1)
Wait for an active network connection on at least one interface.
Protecting Shared Data
Mechanism Selection Guide
Choose the appropriate protection mechanism based on your needs:
┌────────────────────────────────────────────────────┐
│ Data Protection Mechanism │
│ │
│ Need to signal event only (no data)? │
│ │ │
│ └──> Use Semaphore │
│ (32-bit counter) │
│ │
│ Need to pass single 32-bit value/pointer? │
│ │ │
│ └──> Use Mailbox or Queue │
│ (pointer or integer) │
│ │
│ Need to pass structures/objects? │
│ │ │
│ └──> Use FIFO │
│ (linked list of structures) │
│ │
│ Need temporary exclusive access? │
│ │ │
│ └──> Use Critical Section (OSCrit) │
│ (mutex-like protection) │
│ │
│ Need to block all task switching? │
│ │ │
│ └──> Use OSLock/OSUnlock │
│ (tasks disabled, IRQs enabled) │
│ │
│ Need to block tasks AND interrupts? │
│ │ │
│ └──> Use USER_ENTER/EXIT_CRITICAL │
│ (complete system lock) │
└────────────────────────────────────────────────────┘
Protection Mechanisms (Ordered by System Impact)
System Impact: Low ──────────────────────────> High
│ │
│ │
┌───────────────┼─────────────────────────────────┼─────┐
│ │ │ │
│ Semaphore │ OSCrit/OSLock │ CRITICAL
│ Mailbox │ │ SECTION
│ Queue │ │
│ FIFO │ │
│ │ │
└───────────────┴─────────────────────────────────┴─────┘
(Task waits) (Task blocks only) (All blocked)
Detailed Mechanism Comparison
Mechanism | Purpose | Blocks | IRQ Impact | Best For |
OSSemPend()/Post() | Signal/protect resource | Task only | None | Event signaling, simple resource protection |
OSMboxPend()/Post() | Pass pointer message | Task only | None | Passing single message/pointer between tasks |
OSQPend()/Post() | Message queue (FIFO) | Task only | None | Multiple messages, bounded queue |
OSFifoPend()/Post() | Linked list queue | Task only | None | Unbounded message passing, complex structures |
OSCritEnter()/Exit() | Counted mutex | Task only | None | Multi-task resource protection |
OSLock()/Unlock() | Disable task switch | All tasks | None | Short critical sections |
USER_ENTER/EXIT_CRITICAL() | Disable all | Tasks + IRQs | All disabled | Absolute protection (use sparingly) |
Important Considerations
Queue vs. FIFO
Queue**:
- Fixed size (declared at compile time)
- Bounded memory usage
- Predictable behavior
Good for embedded systems
FIFO**:
- Dynamic size (linked list)
- Memory allocated by application
- Flexible but requires memory management
Avoid dynamic allocation in embedded systems
Recommendation**: Use static memory pools for FIFO structures to avoid fragmentation.
Semaphores
Concept
A semaphore is a protected counter used for:
- Controlling access to shared resources
- Signaling event occurrence
- Task synchronization
Operation Flow
Task A Semaphore Task B
(Counter)
│ │
│ OSSemPost() │
├──────────────────> [Count: 0 -> 1] │
│ │
│ [Count: 1] │
│ │
│ │ OSSemPend()
│ [Count: 1 -> 0] <─────────┤
│ │
│ │ (Gets semaphore)
│ │
│ OSSemPost() │
├──────────────────> [Count: 0 -> 1] │
│ │
Usage Pattern
OS_SEM MySemaphore;
OSSemInit(&MySemaphore, 0);
OSSemPost(&MySemaphore);
OSSemPend(&MySemaphore, 0);
Timeout Specification
Timeout Parameter:
┌──────────────────────────────────────┐
│ 0 = Wait forever │
└──────────────────────────────────────┘
Resource Protection Example
OS_SEM SerialPortSem;
void UserMain(void *pd) {
OSSemInit(&SerialPortSem, 1);
}
void TaskA(void *pd) {
while (1) {
OSSemPend(&SerialPortSem, 0);
SerialPort.Write("Task A\r\n");
OSSemPost(&SerialPortSem);
}
}
Message Queues
Concept
Message queues enable tasks and ISRs to send and receive pointer-sized messages. The pointers typically reference structures or objects containing actual data.
Queue Structure
┌─────────────────────────────────────────┐
│ Message Queue │
│ │
│ Entry 0: [ptr] ──> [Message Data] │
│ │ │
│ Entry 1: [ptr] ──> [Message Data] │
│ │ │
│ Entry 2: [ptr] ──> [Message Data] │
│ │ │
│ Entry 3: [empty] │
│ │ │
│ Entry N: [empty] │
│ │ │
│ v │
│ [Max Size: Defined at Creation] │
└─────────────────────────────────────────┘
^ ^
│ │
Dequeue Enqueue
(OSQPend) (OSQPost)
Message Flow
Producer Task Queue Consumer Task
(FIFO)
│ │
│ OSQPost(msg1) │
├─────────────> [msg1] │
│ │
│ OSQPost(msg2) │
├─────────────> [msg1][msg2] │
│ │
│ │ OSQPend()
│ [msg2] <─────────────────┤
│ │
│ │ (Receives msg1)
│ │
│ OSQPost(msg3) │
├─────────────> [msg2][msg3] │
│ │
Usage Example
typedef struct {
uint32_t sensorId;
float temperature;
uint32_t timestamp;
} SensorReading;
OS_Q SensorQueue;
void *SensorQueueStorage[10];
void UserMain(void *pd) {
OSQInit(&SensorQueue, SensorQueueStorage, 10);
}
void SensorTask(void *pd) {
SensorReading reading;
while (1) {
reading.sensorId = 1;
reading.temperature = ReadSensor();
reading.timestamp = Secs;
OSQPost(&SensorQueue, (void *)&reading);
}
}
void ProcessingTask(void *pd) {
while (1) {
SensorReading *msg = (SensorReading *)OSQPend(&SensorQueue, 0);
printf("Sensor %lu: %.2fC at %lu\r\n",
msg->sensorId, msg->temperature, msg->timestamp);
}
}
FIFOs
Concept
FIFOs are similar to queues but specifically designed for linked-list-based message passing. Unlike queues with fixed size, FIFOs grow dynamically within available memory.
Structure Requirements
The first element of any FIFO structure MUST be a void *
pointer for OS internal linking:
typedef struct MyFifoStruct {
void *next;
uint32_t data1;
float data2;
char message[64];
} MyFifoStruct;
FIFO Architecture
┌──────────────────────────────────────────────┐
│ OS_FIFO Linked List │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ [next]──┼───>│ [next]──┼───>│ [next]──┼─>NULL
│ │ data1 │ │ data1 │ │ data1 │ │
│ │ data2 │ │ data2 │ │ data2 │ │
│ │ ... │ │ ... │ │ ... │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ ^ │
│ │ │
│ OSFifoPost() │
│ │
│ │ │
│ v │
│ OSFifoPend() │
└──────────────────────────────────────────────┘
Memory Management Strategies
Strategy 1: Static Pool (Recommended)
#define FIFO_POOL_SIZE 20
MyFifoStruct fifoPool[FIFO_POOL_SIZE];
bool poolInUse[FIFO_POOL_SIZE] = {false};
MyFifoStruct* AllocateFifoStruct() {
for (int i = 0; i < FIFO_POOL_SIZE; i++) {
if (!poolInUse[i]) {
poolInUse[i] = true;
return &fifoPool[i];
}
}
return NULL;
}
void FreeFifoStruct(MyFifoStruct *ptr) {
int index = ptr - fifoPool;
if (index >= 0 && index < FIFO_POOL_SIZE) {
poolInUse[index] = false;
}
}
Strategy 2: Dynamic Allocation (Not Recommended)
MyFifoStruct *ptr = (MyFifoStruct *)malloc(sizeof(MyFifoStruct));
free(ptr);
Queue vs. FIFO Decision
Choose Queue When: Choose FIFO When:
┌────────────────────┐ ┌────────────────────┐
│ Fixed message count│ │ Variable msg count │
│ Predictable memory │ │ Complex structures │
│ Simple data types │ │ Need flexibility │
│ Bounded behavior │ │ Can manage memory │
└────────────────────┘ └────────────────────┘
FIFO Functions
OSFifoInit()
- Initialize FIFO
OSFifoPost()
- Add to end of FIFO
OSFifoPostFirst()
- Add to front of FIFO (priority)
OSFifoPend()
- Wait for and remove first item
OSFifoPendNoWait()
- Check and remove without waiting
Critical Sections
OSCrit - Counted Critical Section
A counting mutex that restricts resource access to one task at a time.
Operation Flow
Task A OSCrit Object Task B
(Resource Guard)
│ │
│ OSCritEnter() │
├────────────> [Lock: Task A] │
│ │
│ (Accessing resource) │
│ │
│ │ OSCritEnter()
│ [Lock: Task A] <─────────────┤
│ │
│ │ (BLOCKED)
│ │
│ OSCritExit() │
├────────────> [Lock: Released] │
│ │
│ │ (UNBLOCKED)
│ [Lock: Task B] <─────────────┤
│ │
│ │ (Accessing resource)
Counting Behavior
Critical Section Counter:
┌─────────────────────────────────────┐
│ │
│ OSCritEnter() -> Count++ │
│ OSCritExit() -> Count-- │
│ │
│ When Count reaches 0: │
│ -> Resource released │
│ -> Next task can enter │
│ │
└─────────────────────────────────────┘
Usage Examples
Manual Enter/Exit
OSCritObj linkedListGuard;
void TaskA(void *pd) {
while (1) {
OSCritEnter(&linkedListGuard, 0);
linkedList.Add(data);
OSCritExit(&linkedListGuard);
}
}
C++ Scoped Object (Recommended)
OSCritObj linkedListGuard;
void TaskB(void *pd) {
while (1) {
{
OSCritObj lock(&linkedListGuard);
linkedList.Remove(data);
}
}
}
OSCrit vs. OSLock
┌─────────────────────────────────────────────────┐
│ OSCrit vs OSLock │
├─────────────────────────────────────────────────┤
│ │
│ OSCrit (Resource Mutex): │
│ - Blocks only tasks accessing same resource │
│ - Other tasks can still run │
│ - IRQs not affected │
│ - Good for: Protecting shared data │
│ │
│ OSLock (Task Switch Disable): │
│ - Blocks ALL task switching │
│ - No tasks can preempt │
│ - IRQs still run │
│ - Good for: Brief critical operations │
│ │
└─────────────────────────────────────────────────┘
OSLock Usage Pattern
void QuickOperation() {
OSLock();
globalCounter++;
OSUnlock();
}
void QuickOperationCpp() {
{
OSLockObj lock;
globalCounter++;
}
}
Warning**: Keep OSLock sections as short as possible to maintain system responsiveness.
OS Flags
Concept
OSFlags enable monitoring multiple events simultaneously using a 32-bit bitmap where each bit represents a distinct flag or event.
Flag Bitmap Structure
32-bit Flag Register:
┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
│31│30│29│...│ 7│ 6│ 5│ 4│ 3│ 2│ 1│ 0│
└─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
│ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ └─> Event 0
│ │ │ │ │ │ │ │ │ └───> Event 1
│ │ │ │ │ │ │ │ └─────> Event 2
│ │ │ │ │ │ │ └───────> Event 3
│ │ │ │ │ │ └─────────> Event 4
...
Flag Operations
Flag Manipulation:
┌────────────────────────────────────────┐
│ │
│ OSFlagSet(flags, 0x0001) │
│ ──────────────────────────────> │
│ [Bit 0 = 1] │
│ │
│ OSFlagSet(flags, 0x0004) │
│ ──────────────────────────────> │
│ [Bit 0 = 1, Bit 2 = 1] │
│ │
│ OSFlagClear(flags, 0x0001) │
│ ──────────────────────────────> │
│ [Bit 2 = 1] │
│ │
└────────────────────────────────────────┘
Pending Modes
Wait for ANY flag (OR):
┌─────────────────────────────────────┐
│ Mask: 0x0007 (bits 0, 1, 2) │
│ │
│ OSFlagPendAny(flags, 0x0007, ...) │
│ │
│ Unblocks when: │
│ Bit 0 = 1 OR │
│ Bit 1 = 1 OR │
│ Bit 2 = 1 │
└─────────────────────────────────────┘
Wait for ALL flags (AND):
┌─────────────────────────────────────┐
│ Mask: 0x0007 (bits 0, 1, 2) │
│ │
│ OSFlagPendAll(flags, 0x0007, ...) │
│ │
│ Unblocks when: │
│ Bit 0 = 1 AND │
│ Bit 1 = 1 AND │
│ Bit 2 = 1 │
└─────────────────────────────────────┘
Flag Functions Summary
Function | Description |
OSFlagCreate() | Create flag object |
OSFlagSet() | Set specified bits |
OSFlagClear() | Clear specified bits |
OSFlagState() | Read current flag value |
OSFlagPendAll() | Wait until ALL specified flags set |
OSFlagPendAny() | Wait until ANY specified flag set |
OSFlagPendNoWait() | Check ALL flags without waiting |
OSFlagPendAnyNoWait() | Check ANY flag without waiting |
Usage Example
OS_FLAGS systemFlags;
#define FLAG_SENSOR_READY 0x0001
#define FLAG_NETWORK_UP 0x0002
#define FLAG_DATA_AVAILABLE 0x0004
#define FLAG_ERROR 0x0008
void UserMain(void *pd) {
OSFlagCreate(&systemFlags);
}
void SensorTask(void *pd) {
while (1) {
if (SensorValid()) {
OSFlagSet(&systemFlags, FLAG_SENSOR_READY);
}
}
}
void ProcessingTask(void *pd) {
while (1) {
uint32_t flags = OSFlagPendAll(
&systemFlags,
FLAG_SENSOR_READY | FLAG_NETWORK_UP,
);
if (flags) {
ProcessSensorData();
OSFlagClear(&systemFlags, FLAG_SENSOR_READY);
}
}
}
void MonitorTask(void *pd) {
while (1) {
uint32_t flags = OSFlagPendAnyNoWait(
&systemFlags,
FLAG_ERROR
);
if (flags & FLAG_ERROR) {
printf("Error detected!\r\n");
OSFlagClear(&systemFlags, FLAG_ERROR);
}
}
}
System Configuration
Predef.h and Predef-overload.h
The NetBurner system uses predef.h
for base-level system configuration through #define
statements.
Configuration Architecture
System Configuration Hierarchy:
┌────────────────────────────────────────┐
│ predef.h (System Default) │
│ - Core system settings │
│ - Platform defaults │
│ - Network stack config │
│ - Changes between NNDK releases │
└─────────────────┬──────────────────────┘
│
│ Overridden by
v
┌────────────────────────────────────────┐
│ predef-overload.h (User Custom) │
│ - Only settings you want to change │
│ - Survives NNDK updates │
│ - Recommended method │
└────────────────────────────────────────┘
Why Use Predef-overload.h?
Problems with modifying predef.h directly**:
- File changes frequently between NNDK releases
- Difficult to track your custom changes
- Merge conflicts during updates
Confusing compilation errors after upgrades
Benefits of predef-overload.h**:
- Only contains YOUR changes
- Survives NNDK updates cleanly
- Easy to review and maintain
- No merge conflicts
Setup Methods
NBEclipse Setup
Refer to NBEclipse Getting Started Guide - Overload Directory section
Command Line Tools Setup
Refer to Command Line Tools - Overload section
Predef-overload.h Best Practices
#undef IPV6
#undef IPV4ONLY
#define IPV4ONLY (1)
#undef TCP_BUFFER_SIZE
#define TCP_BUFFER_SIZE (8192)
#undef DEBUG_LEVEL
#define DEBUG_LEVEL (2)
#undef USER_TASK_STK_SIZE
#define USER_TASK_STK_SIZE (4096)
Configuration Workflow
Development Process:
┌───────────────────────────────────────────┐
│ 1. Identify setting to change │
│ - Check predef.h for current value │
│ - Read documentation comments │
└────────────────┬──────────────────────────┘
│
v
┌───────────────────────────────────────────┐
│ 2. Create/edit predef-overload.h │
│ - Start with blank file │
│ - Add only needed changes │
└────────────────┬──────────────────────────┘
│
v
┌───────────────────────────────────────────┐
│ 3. Add override │
│ #undef SETTING_NAME │
│ #define SETTING_NAME (new_value) │
└────────────────┬──────────────────────────┘
│
v
┌───────────────────────────────────────────┐
│ 4. Rebuild application │
│ make clean && make │
└────────────────┬──────────────────────────┘
│
v
┌───────────────────────────────────────────┐
│ 5. Test changes │
└───────────────────────────────────────────┘
Common Configuration Overrides
#undef IPV4ONLY
#define IPV4ONLY (1)
#undef DEFAULT_NET_TIMEOUT
#define DEFAULT_NET_TIMEOUT (30)
#undef USER_TASK_STK_SIZE
#define USER_TASK_STK_SIZE (8192)
#undef MAX_USER_TASKS
#define MAX_USER_TASKS (20)
#undef NUM_OS_FIFOS
#define NUM_OS_FIFOS (32)
#undef NUM_OS_SEMS
#define NUM_OS_SEMS (64)
#undef DEBUG_BUILD
#define DEBUG_BUILD (1)
#undef ENABLE_REMOTE_CONSOLE
#define ENABLE_REMOTE_CONSOLE (1)
Verification After Changes
# Clean build to ensure all changes applied
make clean
# Rebuild with new configuration
make
# Verify settings at runtime
# Check console output or use system diagnostics
Summary
Key Takeaways
- RTOS Fundamentals
- Highest priority ready task always runs
- Tasks must block for lower priority tasks to execute
- 255 priority levels (1 = highest, 255 = lowest)
- Task Creation
- Entry point:
UserMain()
- Full control:
OSTaskCreatewName()
- Simplified:
OSSimpleTaskCreatewName()
- Data Protection Hierarchy
Semaphore < Mailbox < Queue < FIFO < OSCrit < OSLock < Critical Section
(Least impact) ──────────────────────────────────> (Most impact)
- Best Practices
- Use predef-overload.h for configuration
- Prefer static memory over dynamic allocation
- Keep critical sections short
- Check return values from task creation
- Match enter/exit calls for counted resources
- Configuration Management
- Never modify predef.h directly
- Use predef-overload.h for customization
- Always
#undef
before #define
- Clean rebuild after configuration changes
Quick Reference
Common RTOS Calls:
├── Timing
├── Semaphores
│ ├── OSSemInit(&sem, 0)
│ ├── OSSemPend(&sem, timeout)
│ └── OSSemPost(&sem)
├── Queues
│ ├── OSQInit(&queue, storage, size)
│ ├── OSQPend(&queue, timeout)
│ └── OSQPost(&queue, msg)
├── Flags
│ ├── OSFlagCreate(&flags)
│ ├── OSFlagSet(&flags, bits)
│ ├── OSFlagPendAny(&flags, mask, timeout)
│ └── OSFlagPendAll(&flags, mask, timeout)
└── Critical Sections
├── OSCritEnter(&crit, timeout)
├── OSCritExit(&crit)
├── OSLock()
└── OSUnlock()
Further Reading
Refer to the following documentation:
constants.h
- System task priorities and constants
- API reference documentation - Complete function details
- Platform-specific guides - Hardware integration
- Example applications - Working implementations