使用 OSAL 的多任务¶
This tutorial teaches you how to create multi-tasking applications using the Nexus OSAL (OS Abstraction Layer). You'll learn how to create tasks, use synchronization primitives, and build concurrent applications.
学习目标¶
完成本教程后,您将能够:
Understand RTOS concepts and task scheduling
Create and manage multiple tasks
Use mutexes for resource protection
Use semaphores for task synchronization
Use message queues for inter-task communication
Implement producer-consumer patterns
前置条件¶
Completed 您的第一个 Nexus 应用程序, GPIO Control 教程, and UART Communication 教程 tutorials
支持 FreeRTOS 的 STM32F4 Discovery 板
Understanding of concurrent programming concepts
OSAL 概述¶
The Nexus OSAL provides a portable interface to RTOS features:
Tasks: Independent threads of execution
Mutexes: Mutual exclusion for protecting shared resources
Semaphores: Signaling between tasks
Queues: Message passing between tasks
Timers: Software timers for periodic operations
The OSAL abstracts the underlying RTOS (FreeRTOS, RT-Thread, etc.), allowing your code to be portable.
Part 1: Creating Your First Task¶
Task Creation Workflow¶
The following diagram shows the workflow for creating and managing tasks:
flowchart TD
START([Start]) --> INIT_HAL[Initialize HAL]
INIT_HAL --> INIT_GPIO[Configure GPIO]
INIT_GPIO --> INIT_OSAL[Initialize OSAL]
INIT_OSAL --> CHECK_OSAL{OSAL Init OK?}
CHECK_OSAL -->|No| ERROR[Error Handler]
ERROR --> HALT[Halt System]
CHECK_OSAL -->|Yes| CREATE_TASK[Create Task]
CREATE_TASK --> CHECK_TASK{Task Created?}
CHECK_TASK -->|No| ERROR
CHECK_TASK -->|Yes| START_SCHED[Start OSAL Scheduler]
START_SCHED --> TASK_RUN[Task Running]
TASK_RUN --> TASK_WORK[Execute Task Code]
TASK_WORK --> TASK_DELAY[Task Delay]
TASK_DELAY --> TASK_RUN
style START fill:#e1f5ff
style INIT_HAL fill:#fff4e1
style INIT_OSAL fill:#ffe1f5
style CREATE_TASK fill:#e1ffe1
style START_SCHED fill:#f5e1ff
style TASK_RUN fill:#ffe1e1
style ERROR fill:#ffcccc
Basic 任务 Creation¶
#include "hal/hal.h"
#include "osal/osal.h"
/**
* \brief LED blink task
* \param[in] arg: Task argument (unused)
*/
static void led_task(void* arg) {
(void)arg; /* Unused */
while (1) {
/* Toggle LED */
hal_gpio_toggle(HAL_GPIO_PORT_D, 12);
/* Delay 500ms */
osal_task_delay(500);
}
}
int main(void) {
/* Initialize HAL */
hal_init();
/* Initialize LED */
hal_gpio_config_t led_config = {
.direction = HAL_GPIO_DIR_OUTPUT,
.pull = HAL_GPIO_PULL_NONE,
.output_mode = HAL_GPIO_OUTPUT_PP,
.speed = HAL_GPIO_SPEED_LOW,
.init_level = HAL_GPIO_LEVEL_LOW
};
hal_gpio_init(HAL_GPIO_PORT_D, 12, &led_config);
/* Initialize OSAL */
if (osal_init() != OSAL_OK) {
while (1) { /* Error */ }
}
/* Create task */
osal_task_config_t task_config = {
.name = "LED",
.func = led_task,
.arg = NULL,
.priority = OSAL_TASK_PRIORITY_NORMAL,
.stack_size = 512
};
osal_task_handle_t task_handle;
if (osal_task_create(&task_config, &task_handle) != OSAL_OK) {
while (1) { /* Error */ }
}
/* Start OSAL scheduler - this function does not return */
osal_start();
return 0;
}
Key Points:
osal_init()initializes the OSAL subsystemosal_task_create()creates a new taskosal_start()starts the scheduler (never returns)osal_task_delay()suspends the task for a specified time
任务 Priorities¶
OSAL supports multiple priority levels:
OSAL_TASK_PRIORITY_IDLE: Lowest priority (background tasks)OSAL_TASK_PRIORITY_LOW: Low priorityOSAL_TASK_PRIORITY_NORMAL: Normal priority (default)OSAL_TASK_PRIORITY_HIGH: High priorityOSAL_TASK_PRIORITY_REALTIME: Highest priority (time-critical tasks)
Higher priority tasks preempt lower priority tasks.
Part 2: Multiple Tasks¶
Creating Multiple Tasks¶
#define TASK_STACK_SIZE 1024
/**
* \brief Green LED task
*/
static void green_led_task(void* arg) {
(void)arg;
while (1) {
hal_gpio_toggle(HAL_GPIO_PORT_D, 12); /* Green */
osal_task_delay(500);
}
}
/**
* \brief Orange LED task
*/
static void orange_led_task(void* arg) {
(void)arg;
while (1) {
hal_gpio_toggle(HAL_GPIO_PORT_D, 13); /* Orange */
osal_task_delay(300);
}
}
/**
* \brief Red LED task
*/
static void red_led_task(void* arg) {
(void)arg;
while (1) {
hal_gpio_toggle(HAL_GPIO_PORT_D, 14); /* Red */
osal_task_delay(700);
}
}
int main(void) {
hal_init();
/* Initialize all LEDs */
hal_gpio_config_t config = {
.direction = HAL_GPIO_DIR_OUTPUT,
.pull = HAL_GPIO_PULL_NONE,
.output_mode = HAL_GPIO_OUTPUT_PP,
.speed = HAL_GPIO_SPEED_LOW,
.init_level = HAL_GPIO_LEVEL_LOW
};
hal_gpio_init(HAL_GPIO_PORT_D, 12, &config);
hal_gpio_init(HAL_GPIO_PORT_D, 13, &config);
hal_gpio_init(HAL_GPIO_PORT_D, 14, &config);
/* Initialize OSAL */
osal_init();
/* Create tasks */
osal_task_config_t task_configs[] = {
{.name = "Green", .func = green_led_task, .arg = NULL,
.priority = OSAL_TASK_PRIORITY_NORMAL, .stack_size = TASK_STACK_SIZE},
{.name = "Orange", .func = orange_led_task, .arg = NULL,
.priority = OSAL_TASK_PRIORITY_NORMAL, .stack_size = TASK_STACK_SIZE},
{.name = "Red", .func = red_led_task, .arg = NULL,
.priority = OSAL_TASK_PRIORITY_NORMAL, .stack_size = TASK_STACK_SIZE}
};
for (size_t i = 0; i < 3; i++) {
osal_task_handle_t handle;
osal_task_create(&task_configs[i], &handle);
}
/* Start scheduler */
osal_start();
return 0;
}
Result: All three LEDs blink independently at different rates.
Part 3: Mutexes for Resource Protection¶
The Solution: Mutexes¶
static uint32_t shared_counter = 0;
static osal_mutex_handle_t counter_mutex;
/**
* \brief Increment counter safely
*/
static void increment_counter(void) {
/* Lock mutex */
if (osal_mutex_lock(counter_mutex, 1000) == OSAL_OK) {
/* Critical section - only one task can be here */
shared_counter++;
/* Unlock mutex */
osal_mutex_unlock(counter_mutex);
}
}
static void task1(void* arg) {
while (1) {
increment_counter();
osal_task_delay(10);
}
}
static void task2(void* arg) {
while (1) {
increment_counter();
osal_task_delay(10);
}
}
int main(void) {
hal_init();
osal_init();
/* Create mutex */
if (osal_mutex_create(&counter_mutex) != OSAL_OK) {
while (1) { /* Error */ }
}
/* Create tasks */
osal_task_config_t config1 = {
.name = "Task1", .func = task1, .arg = NULL,
.priority = OSAL_TASK_PRIORITY_NORMAL, .stack_size = 1024
};
osal_task_config_t config2 = {
.name = "Task2", .func = task2, .arg = NULL,
.priority = OSAL_TASK_PRIORITY_NORMAL, .stack_size = 1024
};
osal_task_handle_t h1, h2;
osal_task_create(&config1, &h1);
osal_task_create(&config2, &h2);
osal_start();
return 0;
}
Key Points:
Always lock mutex before accessing shared resource
Always unlock mutex after accessing shared resource
Use timeout to avoid deadlocks
Keep critical sections short
Part 4: Semaphores for Synchronization¶
Binary Semaphores¶
Use binary semaphores for signaling between tasks:
static osal_sem_handle_t button_sem;
/**
* \brief Button monitoring task
*/
static void button_task(void* arg) {
(void)arg;
while (1) {
/* Check button state */
if (hal_gpio_read(HAL_GPIO_PORT_A, 0) == HAL_GPIO_LEVEL_HIGH) {
/* Button pressed - signal LED task */
osal_sem_give(button_sem);
/* Wait for button release */
while (hal_gpio_read(HAL_GPIO_PORT_A, 0) == HAL_GPIO_LEVEL_HIGH) {
osal_task_delay(10);
}
}
osal_task_delay(10);
}
}
/**
* \brief LED control task
*/
static void led_control_task(void* arg) {
(void)arg;
while (1) {
/* Wait for button press signal */
if (osal_sem_take(button_sem, OSAL_WAIT_FOREVER) == OSAL_OK) {
/* Toggle LED */
hal_gpio_toggle(HAL_GPIO_PORT_D, 12);
}
}
}
int main(void) {
hal_init();
/* Initialize button and LED */
hal_gpio_config_t btn_config = {
.direction = HAL_GPIO_DIR_INPUT,
.pull = HAL_GPIO_PULL_DOWN,
.output_mode = HAL_GPIO_OUTPUT_PP,
.speed = HAL_GPIO_SPEED_LOW,
.init_level = HAL_GPIO_LEVEL_LOW
};
hal_gpio_init(HAL_GPIO_PORT_A, 0, &btn_config);
hal_gpio_config_t led_config = {
.direction = HAL_GPIO_DIR_OUTPUT,
.pull = HAL_GPIO_PULL_NONE,
.output_mode = HAL_GPIO_OUTPUT_PP,
.speed = HAL_GPIO_SPEED_LOW,
.init_level = HAL_GPIO_LEVEL_LOW
};
hal_gpio_init(HAL_GPIO_PORT_D, 12, &led_config);
osal_init();
/* Create binary semaphore */
if (osal_sem_create_binary(&button_sem) != OSAL_OK) {
while (1) { /* Error */ }
}
/* Create tasks */
osal_task_config_t btn_task_config = {
.name = "Button", .func = button_task, .arg = NULL,
.priority = OSAL_TASK_PRIORITY_HIGH, .stack_size = 1024
};
osal_task_config_t led_task_config = {
.name = "LED", .func = led_control_task, .arg = NULL,
.priority = OSAL_TASK_PRIORITY_NORMAL, .stack_size = 1024
};
osal_task_handle_t h1, h2;
osal_task_create(&btn_task_config, &h1);
osal_task_create(&led_task_config, &h2);
osal_start();
return 0;
}
Counting Semaphores¶
Use counting semaphores to track resource availability:
#define MAX_RESOURCES 5
static osal_sem_handle_t resource_sem;
/**
* \brief Acquire resource
* \return true if acquired, false if timeout
*/
static bool acquire_resource(uint32_t timeout_ms) {
return (osal_sem_take(resource_sem, timeout_ms) == OSAL_OK);
}
/**
* \brief Release resource
*/
static void release_resource(void) {
osal_sem_give(resource_sem);
}
int main(void) {
hal_init();
osal_init();
/* Create counting semaphore with 5 resources */
if (osal_sem_create_counting(MAX_RESOURCES, MAX_RESOURCES, &resource_sem) != OSAL_OK) {
while (1) { /* Error */ }
}
/* Tasks can now acquire/release resources */
/* ... */
osal_start();
return 0;
}
Part 5: Message Queues¶
生产者-消费者模式¶
/**
* \brief Sensor data structure
*/
typedef struct {
uint32_t timestamp;
int32_t temperature;
int32_t humidity;
} sensor_data_t;
#define QUEUE_SIZE 10
static osal_queue_handle_t sensor_queue;
/**
* \brief Producer task - reads sensors
*/
static void producer_task(void* arg) {
(void)arg;
uint32_t sample_count = 0;
while (1) {
/* Read sensor data (simulated) */
sensor_data_t data = {
.timestamp = hal_get_tick(),
.temperature = 25 + (sample_count % 10),
.humidity = 60 + (sample_count % 20)
};
/* Send to queue */
if (osal_queue_send(sensor_queue, &data, 100) == OSAL_OK) {
/* Success */
sample_count++;
} else {
/* Queue full */
hal_gpio_write(HAL_GPIO_PORT_D, 14, HAL_GPIO_LEVEL_HIGH); /* Red LED */
}
/* Sample every 1 second */
osal_task_delay(1000);
}
}
/**
* \brief Consumer task - processes data
*/
static void consumer_task(void* arg) {
(void)arg;
sensor_data_t data;
while (1) {
/* Receive from queue */
if (osal_queue_receive(sensor_queue, &data, OSAL_WAIT_FOREVER) == OSAL_OK) {
/* Process data */
/* In real application, would log or transmit data */
/* Toggle LED to show activity */
hal_gpio_toggle(HAL_GPIO_PORT_D, 12); /* Green LED */
/* Clear error LED */
hal_gpio_write(HAL_GPIO_PORT_D, 14, HAL_GPIO_LEVEL_LOW);
}
}
}
int main(void) {
hal_init();
/* Initialize LEDs */
hal_gpio_config_t config = {
.direction = HAL_GPIO_DIR_OUTPUT,
.pull = HAL_GPIO_PULL_NONE,
.output_mode = HAL_GPIO_OUTPUT_PP,
.speed = HAL_GPIO_SPEED_LOW,
.init_level = HAL_GPIO_LEVEL_LOW
};
hal_gpio_init(HAL_GPIO_PORT_D, 12, &config);
hal_gpio_init(HAL_GPIO_PORT_D, 14, &config);
osal_init();
/* Create queue */
if (osal_queue_create(sizeof(sensor_data_t), QUEUE_SIZE, &sensor_queue) != OSAL_OK) {
while (1) { /* Error */ }
}
/* Create tasks */
osal_task_config_t producer_config = {
.name = "Producer", .func = producer_task, .arg = NULL,
.priority = OSAL_TASK_PRIORITY_NORMAL, .stack_size = 1024
};
osal_task_config_t consumer_config = {
.name = "Consumer", .func = consumer_task, .arg = NULL,
.priority = OSAL_TASK_PRIORITY_HIGH, .stack_size = 1024
};
osal_task_handle_t h1, h2;
osal_task_create(&producer_config, &h1);
osal_task_create(&consumer_config, &h2);
osal_start();
return 0;
}
Part 6: Complete Example¶
Here's a complete multi-tasking application:
/**
* \file multitask_demo.c
* \brief Complete OSAL Multi-Tasking Demo
*/
#include "hal/hal.h"
#include "osal/osal.h"
/*-----------------------------------------------------------------------*/
/* Configuration */
/*-----------------------------------------------------------------------*/
#define TASK_STACK_SIZE 1024
#define SENSOR_QUEUE_SIZE 10
/*-----------------------------------------------------------------------*/
/* Data Structures */
/*-----------------------------------------------------------------------*/
typedef struct {
uint32_t timestamp;
uint32_t sensor_id;
int32_t value;
} sensor_msg_t;
typedef struct {
uint32_t samples_produced;
uint32_t samples_consumed;
uint32_t queue_overflows;
} stats_t;
/*-----------------------------------------------------------------------*/
/* Global Variables */
/*-----------------------------------------------------------------------*/
static osal_queue_handle_t g_sensor_queue;
static osal_mutex_handle_t g_stats_mutex;
static osal_sem_handle_t g_data_ready_sem;
static stats_t g_stats = {0};
/*-----------------------------------------------------------------------*/
/* Helper Functions */
/*-----------------------------------------------------------------------*/
static void update_stats(int produced, int consumed, int overflow) {
if (osal_mutex_lock(g_stats_mutex, 100) == OSAL_OK) {
g_stats.samples_produced += produced;
g_stats.samples_consumed += consumed;
g_stats.queue_overflows += overflow;
osal_mutex_unlock(g_stats_mutex);
}
}
/*-----------------------------------------------------------------------*/
/* Tasks */
/*-----------------------------------------------------------------------*/
static void producer_task(void* arg) {
(void)arg;
uint32_t sensor_id = 0;
while (1) {
sensor_msg_t msg = {
.timestamp = hal_get_tick(),
.sensor_id = sensor_id,
.value = (int32_t)(hal_get_tick() % 1000)
};
if (osal_queue_send(g_sensor_queue, &msg, 10) == OSAL_OK) {
osal_sem_give(g_data_ready_sem);
update_stats(1, 0, 0);
} else {
update_stats(0, 0, 1);
hal_gpio_write(HAL_GPIO_PORT_D, 14, HAL_GPIO_LEVEL_HIGH);
}
sensor_id = (sensor_id + 1) % 4;
osal_task_delay(100);
}
}
static void consumer_task(void* arg) {
(void)arg;
sensor_msg_t msg;
while (1) {
if (osal_sem_take(g_data_ready_sem, 500) == OSAL_OK) {
if (osal_queue_receive(g_sensor_queue, &msg, 10) == OSAL_OK) {
update_stats(0, 1, 0);
hal_gpio_toggle(HAL_GPIO_PORT_D, 13);
}
}
}
}
static void heartbeat_task(void* arg) {
(void)arg;
while (1) {
hal_gpio_toggle(HAL_GPIO_PORT_D, 12);
osal_task_delay(500);
}
}
static void stats_task(void* arg) {
(void)arg;
stats_t local_stats;
while (1) {
osal_task_delay(2000);
if (osal_mutex_lock(g_stats_mutex, 100) == OSAL_OK) {
local_stats = g_stats;
osal_mutex_unlock(g_stats_mutex);
/* In real app, would print stats via UART */
if (local_stats.queue_overflows == 0) {
hal_gpio_write(HAL_GPIO_PORT_D, 14, HAL_GPIO_LEVEL_LOW);
}
}
}
}
/*-----------------------------------------------------------------------*/
/* Main Function */
/*-----------------------------------------------------------------------*/
int main(void) {
hal_init();
/* Initialize LEDs */
hal_gpio_config_t config = {
.direction = HAL_GPIO_DIR_OUTPUT,
.pull = HAL_GPIO_PULL_NONE,
.output_mode = HAL_GPIO_OUTPUT_PP,
.speed = HAL_GPIO_SPEED_LOW,
.init_level = HAL_GPIO_LEVEL_LOW
};
hal_gpio_init(HAL_GPIO_PORT_D, 12, &config);
hal_gpio_init(HAL_GPIO_PORT_D, 13, &config);
hal_gpio_init(HAL_GPIO_PORT_D, 14, &config);
/* Initialize OSAL */
osal_init();
/* Create synchronization objects */
osal_queue_create(sizeof(sensor_msg_t), SENSOR_QUEUE_SIZE, &g_sensor_queue);
osal_mutex_create(&g_stats_mutex);
osal_sem_create_counting(SENSOR_QUEUE_SIZE, 0, &g_data_ready_sem);
/* Create tasks */
osal_task_config_t tasks[] = {
{.name = "Producer", .func = producer_task, .arg = NULL,
.priority = OSAL_TASK_PRIORITY_NORMAL, .stack_size = TASK_STACK_SIZE},
{.name = "Consumer", .func = consumer_task, .arg = NULL,
.priority = OSAL_TASK_PRIORITY_HIGH, .stack_size = TASK_STACK_SIZE},
{.name = "Heartbeat", .func = heartbeat_task, .arg = NULL,
.priority = OSAL_TASK_PRIORITY_LOW, .stack_size = TASK_STACK_SIZE},
{.name = "Stats", .func = stats_task, .arg = NULL,
.priority = OSAL_TASK_PRIORITY_LOW, .stack_size = TASK_STACK_SIZE}
};
for (size_t i = 0; i < 4; i++) {
osal_task_handle_t handle;
osal_task_create(&tasks[i], &handle);
}
/* Start scheduler */
osal_start();
return 0;
}
最佳实践¶
Task Design: Keep tasks focused on a single responsibility
Stack Size: Allocate sufficient stack for each task (monitor usage)
Priority Assignment: Use appropriate priorities (avoid priority inversion)
Synchronization: Always protect shared resources with mutexes
Deadlock Prevention: - Always acquire mutexes in the same order - Use timeouts - Keep critical sections short
Queue Sizing: Size queues appropriately for your data rate
Error Handling: Always check return values from OSAL functions
Task Cleanup: Tasks should run forever or call
osal_task_delete()before returning
常见问题¶
Stack Overflow:
Symptoms: System crashes, hard faults Solution: Increase stack size, reduce local variables, avoid deep recursion
Priority Inversion:
Symptoms: High-priority task blocked by low-priority task Solution: Use priority inheritance mutexes
Deadlock:
Symptoms: System hangs Solution: Always acquire locks in same order, use timeouts
Queue Overflow:
Symptoms: Data loss Solution: Increase queue size, process data faster, add flow control
下一步¶
Explore the 操作系统抽象层 (OSAL) for complete OSAL API reference
Check out the FreeRTOS demo application in
applications/freertos_demo/Learn about 日志框架 for logging from multiple tasks
Read about STM32F4 平台 Guide for platform-specific details