实时操作系统(RTOS):
实时操作系统是一种专为实时应用程序设计的操作系统。实时应用程序对任务的响应时间有严格的要求,因此实时操作系统必须能够在预定的时间内完成任务的调度和执行。这与通用操作系统(如Windows、Linux)的设计目标有所不同,通用操作系统更注重用户体验和各种应用程序的同时运行。
实时操作系统通常用于嵌入式系统、航空航天、医疗设备、汽车控制系统等领域,这些领域对系统的响应时间和可靠性要求非常高。
FreeRTOS:
FreeRTOS(Real-Time Operating System)是一个开源的实时操作系统内核,它是由英国公司Real Time Engineers Ltd.创建并维护的。FreeRTOS提供了一个轻量级、可移植的内核,适用于多种嵌入式平台。
以下是FreeRTOS的一些特点和功能:
多任务管理: FreeRTOS支持多任务,允许在同一系统中运行多个任务。每个任务都有自己的堆栈和优先级,任务之间可以共享资源。
任务调度: FreeRTOS使用抢占式任务调度,任务按照优先级进行调度,高优先级任务可以打断低优先级任务的执行。
内存管理: FreeRTOS提供内存管理机制,可以灵活地配置和管理任务堆栈、消息队列等资源。
互斥锁和信号量: 用于实现任务之间的同步和互斥,防止资源冲突。
定时器和中断处理: FreeRTOS提供了软件定时器和硬件定时器的支持,以及灵活的中断处理机制。
可移植性: FreeRTOS的内核设计使其能够轻松地移植到不同的嵌入式平台上,支持多种处理器架构。
开源和社区支持: 作为开源项目,FreeRTOS得到了广泛的社区支持,用户可以方便地获取源代码、文档和社区帮助。
在使用FreeRTOS时,可以通过创建任务、使用信号量和队列等机制,以一种结构化和可维护的方式设计和实现复杂的嵌入式系统。
在FreeRTOS中,创建多任务可以使用 xTaskCreate
函数。该函数的原型如下:
BaseType_t xTaskCreate(
TaskFunction_t pvTaskCode,
const char * const pcName,
const uint32_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask
);
这个函数用于创建一个新的任务。下面是各个参数的解释:
TaskFunction_t pvTaskCode
: 这是任务函数的指针,该函数将在任务被创建时运行。任务函数的原型应为 void taskFunction(void *pvParameters)
,其中 pvParameters
是传递给任务的参数。
const char * const pcName
: 这是任务的名称,用于识别任务。这个参数是一个字符串。
const uint32_t usStackDepth
: 这是任务堆栈的深度,以字为单位。堆栈的深度取决于任务的要求和系统的限制。
void * const pvParameters
: 这是传递给任务函数的参数,可以是一个结构体、整数或其他任何类型。
UBaseType_t uxPriority
: 这是任务的优先级。优先级越高的任务将更早得到执行。通常,0是最低优先级,数字越大优先级越高。
TaskHandle_t * const pxCreatedTask
: 这是一个指向任务句柄的指针,用于接收任务的句柄。如果不需要任务句柄,可以将此参数设置为 NULL
。
下面是一个简单的例子,演示如何创建两个任务:
#include
void task1(void *pvParameters) {
while (1) {
Serial.println("Task 1 is running...");
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void task2(void *pvParameters) {
while (1) {
Serial.println("Task 2 is running...");
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
void setup() {
Serial.begin(115200);
xTaskCreate(task1, "Task1", 1024, NULL, 1, NULL);
xTaskCreate(task2, "Task2", 1024, NULL, 1, NULL);
}
void loop() {
// 什么都不做
}
在这个例子中,task1
和 task2
是两个简单的任务函数,分别以不同的频率输出消息。通过 xTaskCreate
函数创建这两个任务,并在 setup
函数中启动它们。这样,两个任务将并行运行,形成多任务系统。
在FreeRTOS中,可以通过 pvParameters
参数将参数传递给任务函数。当使用 xTaskCreate
函数创建任务时,你可以将参数作为最后一个参数传递给该函数。这个参数是一个 void *
类型的指针,可以指向任何类型的数据。
以下是一个简单的例子:
#include
// 定义结构体作为参数
typedef struct {
int ledPin;
int delayTime;
} TaskParameters;
// 任务函数
void blinkTask(void *pvParameters) {
TaskParameters *params = (TaskParameters *)pvParameters;
pinMode(params->ledPin, OUTPUT);
while (1) {
digitalWrite(params->ledPin, !digitalRead(params->ledPin));
vTaskDelay(params->delayTime / portTICK_PERIOD_MS);
}
}
void setup() {
Serial.begin(115200);
// 设置LED1任务参数
TaskParameters params1 = {21, 1000};
// 创建LED1任务
xTaskCreate(blinkTask, "BlinkTask1", 1024, (void *)¶ms1, 1, NULL);
// 设置LED2任务参数
TaskParameters params2 = {23, 2000};
// 创建LED2任务
xTaskCreate(blinkTask, "BlinkTask2", 1024, (void *)¶ms2, 1, NULL);
}
void loop() {
// 什么都不做
}
在这个例子中,我们定义了一个结构体 TaskParameters
来存储任务的参数,其中包括LED引脚号和LED闪烁间隔时间。然后,我们分别为两个LED创建了两个任务,将不同的参数传递给它们。在任务函数 blinkTask
中,通过类型转换将 pvParameters
转换为 TaskParameters
类型,然后就可以访问传递的参数了。
掌握了创建多任务以及向任务传参的方法,我们就可以完成第一个FreeRTOS项目了
项目任务:
通过两个任务,分别控制两个LED的闪烁。每个LED由一个独立的任务来处理,通过在任务中控制LED的状态,实现两LED的闪烁间隔时间不同。
以下是这个项目的基本流程:
结构体定义:
typedef struct {
byte pin;
int DelayTime;
} LEDFLASH;
定义了一个结构体 LEDFLASH
,其中包含了 LED 的引脚号和闪烁的时间间隔。
任务函数定义:
void task(void *pt) {
LEDFLASH *ptLedFlash = (LEDFLASH *)pt;
pinMode(ptLedFlash->pin, OUTPUT);
while (1) {
digitalWrite(ptLedFlash->pin, !digitalRead(ptLedFlash->pin));
vTaskDelay(ptLedFlash->DelayTime); // 通过 FreeRTOS 的延迟函数实现 LED 闪烁
}
}
定义了一个任务函数 task
,该函数通过类型转换获取 LED 结构体指针,然后在一个无限循环中控制 LED 的状态并通过 vTaskDelay
实现 LED 的闪烁效果。
任务创建:
void setup() {
LEDFLASH ledFlash1 = {21, 1000}; // 第一个LED,引脚21,每秒一次
LEDFLASH ledFlash2 = {23, 3000}; // 第二个LED,引脚23,每三秒一次
xTaskCreate(task, "Blink 21", 1024, (void *)&ledFlash1, 1, NULL);
xTaskCreate(task, "Blink 23", 1024, (void *)&ledFlash2, 1, NULL);
Serial.begin(115200);
Serial.println(portTICK_PERIOD_MS); // 打印一个 tick 的时间
}
在 setup
函数中,创建了两个任务,分别用于控制两个 LED。每个任务都独立运行,控制不同 LED 的状态。
串口初始化:
Serial.begin(115200);
初始化串口通信,用于在串口监视器上输出信息。
主循环:
void loop() {
// 什么都不做
}
由于任务在 FreeRTOS 中进行管理和调度,因此 loop
函数为空。
通过 FreeRTOS 实现多任务处理,通过两个任务控制两个 LED 的独立闪烁,展示了 FreeRTOS 在 ESP32 上实时操作系统的应用。源码点击查看