NetBurner 3.5.7
PDF Version
NetBurner RTOS

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, // Highest priority currently unused, 0->OS_LO_PRIO
    Above = -1, // Lowest priority that is higher than the current task, OSTaskID()->0
    Below = 0, // Highest priority that is lower than the current task, OSTaskID()->OS_LO_PRIO
    Next = Below, // Highest priority that is lower than the current task, OSTaskID()->OS_LO_PRIO
    Minimum = 1 // Lowest priority currently unused, OS_LO_PRIO->0
    };
    OSNextPrio
    Search direction for OSGetNextPrio() when finding an available task priority.
    Definition nbrtos.h:2749
    @ Next
    Highest priority that is lower than the current task, OSTaskID()->OS_LO_PRIO.
    @ Above
    Lowest priority that is higher than the current task, OSTaskID()->0.
    @ Maximum
    Highest priority currently unused, 0->OS_LO_PRIO.
    @ Minimum
    Lowest priority currently unused, OS_LO_PRIO->0.
    @ Below
    Highest priority that is lower than the current task, OSTaskID()->OS_LO_PRIO.

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) │
└──────────────┘ └──────────────┘
uint8_t OSSemPend(OS_SEM *psem, uint16_t timeout)
Wait for the semaphore count to become non-zero, then decrement it.
Definition nbrtos.h:2341
void OSTimeDly(uint32_t to_count)
Delay the calling task for the specified number of system ticks.
Definition nbrtos.h:2202

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 │
read(fd, buf, len) │
│ │ │
│ 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:

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:

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 │
│ - init() │
│ - StartHttp() │
│ - 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**:

  1. Task function name
  2. Optional parameter to pass (NULL if unused)
  3. Top of task stack
  4. Bottom of task stack
  5. Task priority
  6. Task name (string)

    Returns**: Status code (OS_NO_ERR on success)

Method 2: OSSimpleTaskCreatewName() - Simplified

Automatically allocates stack space, requires only:

  1. Task function name
  2. Task priority
  3. 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

/*
Task Creation Example
Demonstrates both OSTaskCreatewName() and OSSimpleTaskCreatewName()
*/
#include <init.h>
#include <stdlib.h>
#include <nbrtos.h>
#include <system.h>
#include <utils.h>
const char *AppName = "Task Creation Example";
// Stack allocation for full task creation method
uint32_t TaskAllParamsStack[USER_TASK_STK_SIZE];
/*
Task created with OSTaskCreatewName()
Demonstrates passing parameters and full control
*/
void TaskAllParams(void *pd)
{
uint32_t loopCount = 0;
uint32_t delayTime = (uint32_t)pd; // Cast parameter
printf("TaskAllParams delay: %ld seconds\r\n", delayTime);
while (1)
{
printf("TaskAllParams iteration: %ld\r\n", loopCount);
loopCount++;
}
}
/*
Task created with OSSimpleTaskCreatewName()
Simplified creation with automatic stack allocation
*/
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++;
}
}
/*
Main entry point
*/
void UserMain(void *pd)
{
uint32_t delayTime = 3;
int returnCode;
init(); // Initialize network stack
WaitForActiveNetwork(TICKS_PER_SECOND * 5); // Wait for DHCP
// Create task with full parameter control
printf("Creating TaskAllParams...");
returnCode = OSTaskCreatewName(
TaskAllParams, // Task function
(void *)delayTime, // Parameter to pass
&TaskAllParamsStack[USER_TASK_STK_SIZE], // Stack top
TaskAllParamsStack, // Stack bottom
MAIN_PRIO - 1, // Priority
"TaskAllParams" // Name
);
if (returnCode == OS_NO_ERR)
printf("Success\r\n");
else
printf("*** Error: %d\r\n", returnCode);
// Create task with simplified method
printf("Creating TaskSimple\r\n");
OSSimpleTaskCreatewName(TaskSimple, MAIN_PRIO - 2, "TaskSimple");
while (1)
{
}
}
const char * AppName
Application name displayed in web interface and console output.
Definition aes/src/main.cpp:10
#define MAIN_PRIO
Recommend UserMain priority.
Definition constants.h:130
uint8_t OSTaskCreatewName(void(*task)(void *dptr), void *data, void *pstktop, void *pstkbot, uint8_t prio, const char *name, OS_TCB **pRetHandle=NULL)
Create a new RTOS task with full control over stack allocation and priority.
#define OSSimpleTaskCreatewName(x, p, n)
Create a new task with an automatically allocated stack.
Definition nbrtos.h:2081
#define OS_NO_ERR
No error.
Definition nbrtos.h:67
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) │
└────────────────────────────────────────────────────┘
void OSUnlock(void)
Re-enable task switching after a previous call to OSLock().
void OSLock(void)
Prevent the RTOS scheduler from switching tasks.

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] │
│ │
uint8_t OSSemPost(OS_SEM *psem)
Increment the semaphore count by one, releasing any waiting higher-priority tasks.
Definition nbrtos.h:2323

Usage Pattern

// 1. Declare semaphore
OS_SEM MySemaphore;
// 2. Initialize semaphore (typically in UserMain)
OSSemInit(&MySemaphore, 0); // Initial value: 0
// 3. Post to semaphore (signal/release)
OSSemPost(&MySemaphore);
// 4. Pend on semaphore (wait/acquire)
OSSemPend(&MySemaphore, 0); // 0 = wait forever
// 5. Pend with timeout
OSSemPend(&MySemaphore, TICKS_PER_SECOND * 5); // Wait 5 seconds
uint8_t OSSemInit(OS_SEM *psem, long value)
Initialize a semaphore object.
Definition nbrtos.h:2306
Counting semaphore for task synchronization and resource management.
Definition nbrtos.h:550

Timeout Specification

Timeout Parameter:
┌──────────────────────────────────────┐
│ 0 = Wait forever │
TICKS_PER_SECOND = 1 second │
TICKS_PER_SECOND * 5 = 5 seconds │
TICKS_PER_SECOND / 2 = 500 milliseconds │
└──────────────────────────────────────┘

Resource Protection Example

OS_SEM SerialPortSem;
void UserMain(void *pd) {
init();
OSSemInit(&SerialPortSem, 1); // Initial count: 1 (available)
// Create tasks that share serial port...
}
void TaskA(void *pd) {
while (1) {
OSSemPend(&SerialPortSem, 0); // Acquire
// Protected: exclusive access to serial port
SerialPort.Write("Task A\r\n");
OSSemPost(&SerialPortSem); // Release
}
}

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
uint8_t OSQPost(OS_Q *pq, void *msg)
Post a message to the tail of the queue.
Definition nbrtos.h:2462
void * OSQPend(OS_Q *pq, uint16_t timeout, uint8_t *err)
Wait for a message to be posted to the queue.
Definition nbrtos.h:2536

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

// Define message structure
typedef struct {
uint32_t sensorId;
float temperature;
uint32_t timestamp;
} SensorReading;
// Declare queue
OS_Q SensorQueue;
void *SensorQueueStorage[10]; // Storage for 10 messages
void UserMain(void *pd) {
init();
// Initialize queue with 10 entries
OSQInit(&SensorQueue, SensorQueueStorage, 10);
// Create producer and consumer tasks...
}
void SensorTask(void *pd) {
SensorReading reading;
while (1) {
// Read sensor
reading.sensorId = 1;
reading.temperature = ReadSensor();
reading.timestamp = Secs;
// Post to queue
OSQPost(&SensorQueue, (void *)&reading);
}
}
void ProcessingTask(void *pd) {
while (1) {
// Wait for message
SensorReading *msg = (SensorReading *)OSQPend(&SensorQueue, 0);
// Process message
printf("Sensor %lu: %.2fC at %lu\r\n",
msg->sensorId, msg->temperature, msg->timestamp);
}
}
vuint32_t Secs
Number of seconds elapsed since system start (updated once per second).
uint8_t OSQInit(OS_Q *pq, void **start, uint8_t size)
Initialize a message queue object.
Definition nbrtos.h:2444
Fixed-size FIFO message queue for passing void-pointer messages between tasks.
Definition nbrtos.h:903

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; // REQUIRED: Must be first element
uint32_t data1; // Your data
float data2; // Your data
char message[64]; // Your data
} MyFifoStruct;

FIFO Architecture

┌──────────────────────────────────────────────┐
OS_FIFO Linked List │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ [next]──┼───>│ [next]──┼───>│ [next]──┼─>NULL
│ │ data1 │ │ data1 │ │ data1 │ │
│ │ data2 │ │ data2 │ │ data2 │ │
│ │ ... │ │ ... │ │ ... │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ ^ │
│ │ │
OSFifoPost() │
│ │
│ │ │
│ v │
OSFifoPend() │
└──────────────────────────────────────────────┘
uint8_t OSFifoPost(OS_FIFO *pFifo, OS_FIFO_EL *pToPost)
Post an element to the tail of the FIFO.
Definition nbrtos.h:2585
OS_FIFO_EL * OSFifoPend(OS_FIFO *pFifo, uint16_t timeout)
Wait for an element to be posted to the FIFO.
Definition nbrtos.h:2616
A linked-list FIFO for passing pointers to user-defined structures between tasks.
Definition nbrtos.h:1177

Memory Management Strategies

Strategy 1: Static Pool (Recommended)

#define FIFO_POOL_SIZE 20
// Create static pool of FIFO structures
MyFifoStruct fifoPool[FIFO_POOL_SIZE];
bool poolInUse[FIFO_POOL_SIZE] = {false};
// Allocate from pool
MyFifoStruct* AllocateFifoStruct() {
for (int i = 0; i < FIFO_POOL_SIZE; i++) {
if (!poolInUse[i]) {
poolInUse[i] = true;
return &fifoPool[i];
}
}
return NULL; // Pool exhausted
}
// Return to pool
void FreeFifoStruct(MyFifoStruct *ptr) {
int index = ptr - fifoPool;
if (index >= 0 && index < FIFO_POOL_SIZE) {
poolInUse[index] = false;
}
}

Strategy 2: Dynamic Allocation (Not Recommended)

// Avoid in embedded systems due to fragmentation risk
MyFifoStruct *ptr = (MyFifoStruct *)malloc(sizeof(MyFifoStruct));
// ... use ...
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


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)
uint8_t OSCritEnter(OS_CRIT *pCrit, uint16_t timeout)
Wait to claim the critical section with a timeout.
Definition nbrtos.h:2680

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); // Enter critical section
// Protected: manipulate linked list
linkedList.Add(data);
OSCritExit(&linkedListGuard); // Exit critical section
}
}

C++ Scoped Object (Recommended)

OSCritObj linkedListGuard;
void TaskB(void *pd) {
while (1) {
{
// Automatic enter on construction
OSCritObj lock(&linkedListGuard);
// Protected: manipulate linked list
linkedList.Remove(data);
} // Automatic exit on destruction (scope end)
}
}

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(); // Disable task switching
// Very brief critical operation
// (keep this SHORT!)
globalCounter++;
OSUnlock(); // Enable task switching
}
// C++ scoped version
void QuickOperationCpp() {
{
OSLockObj lock; // Auto OSLock() on creation
// Brief critical operation
globalCounter++;
} // Auto OSUnlock() on destruction
}
RAII wrapper for OSLock()/OSUnlock() that prevents task switching within a scope.
Definition nbrtos.h:2977

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] │
│ │
└────────────────────────────────────────┘
void OSFlagSet(OS_FLAGS *flags, uint32_t bits_to_set)
Set the specified flag bits in an OS_FLAGS object.
Definition nbrtos.h:1808
void OSFlagClear(OS_FLAGS *flags, uint32_t bits_to_clr)
Clear the specified flag bits in an OS_FLAGS object.
Definition nbrtos.h:1821

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 │
└─────────────────────────────────────┘
uint8_t OSFlagPendAll(OS_FLAGS *flags, uint32_t bit_mask, uint16_t timeout)
Wait for all of the specified flag bits to be set.
Definition nbrtos.h:1871
uint8_t OSFlagPendAny(OS_FLAGS *flags, uint32_t bit_mask, uint16_t timeout)
Wait for any of the specified flag bits to be set.
Definition nbrtos.h:1838

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 bits
#define FLAG_SENSOR_READY 0x0001 // Bit 0
#define FLAG_NETWORK_UP 0x0002 // Bit 1
#define FLAG_DATA_AVAILABLE 0x0004 // Bit 2
#define FLAG_ERROR 0x0008 // Bit 3
void UserMain(void *pd) {
init();
OSFlagCreate(&systemFlags);
// Create tasks...
}
void SensorTask(void *pd) {
while (1) {
// Read sensor
if (SensorValid()) {
OSFlagSet(&systemFlags, FLAG_SENSOR_READY);
}
}
}
void ProcessingTask(void *pd) {
while (1) {
// Wait for sensor AND network
uint32_t flags = OSFlagPendAll(
&systemFlags,
FLAG_SENSOR_READY | FLAG_NETWORK_UP,
);
if (flags) {
// Both conditions met, process data
ProcessSensorData();
// Clear flags
OSFlagClear(&systemFlags, FLAG_SENSOR_READY);
}
}
}
void MonitorTask(void *pd) {
while (1) {
// Check for any error condition (non-blocking)
uint32_t flags = OSFlagPendAnyNoWait(
&systemFlags,
FLAG_ERROR
);
if (flags & FLAG_ERROR) {
printf("Error detected!\r\n");
OSFlagClear(&systemFlags, FLAG_ERROR);
}
}
}
void OSFlagCreate(OS_FLAGS *pf)
Initialize an OS_FLAGS object.
Definition nbrtos.h:1795
uint8_t OSFlagPendAnyNoWait(OS_FLAGS *flags, uint32_t bit_mask)
Check if any of the specified flag bits are set without waiting.
Definition nbrtos.h:1854
A 32-bit event flag group that allows tasks to pend on multiple events.
Definition nbrtos.h:1614

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**:

  1. File changes frequently between NNDK releases
  2. Difficult to track your custom changes
  3. Merge conflicts during updates
  4. Confusing compilation errors after upgrades

    Benefits of predef-overload.h**:

  1. Only contains YOUR changes
  2. Survives NNDK updates cleanly
  3. Easy to review and maintain
  4. 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

/*
predef-overload.h
Custom system configuration overrides
*/
// Best practice: #undef before #define
// Prevents duplicate definition warnings
// Example 1: Disable IPv6
#undef IPV6
#undef IPV4ONLY
#define IPV4ONLY (1)
// Example 2: Custom buffer sizes
#undef TCP_BUFFER_SIZE
#define TCP_BUFFER_SIZE (8192)
// Example 3: Enable debug features
#undef DEBUG_LEVEL
#define DEBUG_LEVEL (2)
// Example 4: Stack sizes
#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

// Network Configuration
#undef IPV4ONLY
#define IPV4ONLY (1) // IPv4-only mode
#undef DEFAULT_NET_TIMEOUT
#define DEFAULT_NET_TIMEOUT (30) // 30 second timeout
// Task Configuration
#undef USER_TASK_STK_SIZE
#define USER_TASK_STK_SIZE (8192) // 8KB stack per task
#undef MAX_USER_TASKS
#define MAX_USER_TASKS (20) // Support 20 user tasks
// System Resources
#undef NUM_OS_FIFOS
#define NUM_OS_FIFOS (32) // 32 FIFO objects
#undef NUM_OS_SEMS
#define NUM_OS_SEMS (64) // 64 semaphores
// Debug Configuration
#undef DEBUG_BUILD
#define DEBUG_BUILD (1) // Enable debug features
#undef ENABLE_REMOTE_CONSOLE
#define ENABLE_REMOTE_CONSOLE (1) // Enable remote console

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

  1. RTOS Fundamentals
    • Highest priority ready task always runs
    • Tasks must block for lower priority tasks to execute
    • 255 priority levels (1 = highest, 255 = lowest)
  2. Task Creation
  3. Data Protection Hierarchy
    Semaphore < Mailbox < Queue < FIFO < OSCrit < OSLock < Critical Section
    (Least impact) ──────────────────────────────────> (Most impact)
  4. 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
  5. 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