FreeRTOS概念-1
实时操作系统(Real-time operating system, RTOS),又称即时操作系统,它会按照排序运行、管理系统资源,并为开发应用程序提供一致的基础。
实时操作系统与一般的操作系统相比,最大的特色就是“实时性”,如果有一个任务需要执行,实时操作系统会马上(在较短时间内)执行该任务,不会有较长的延时。这种特性保证了各个任务的及时执行。
概念:TICK
time slicing 的最小单位,可在menuconfig中更改,一些函数如1
2vTaskDelay(tick);
vTaskDelay(1000/portTICK_PERIOD_MS);
是以tick为单位延时,对应的宏为portTICK_PERIOD_MS,可写第二行代码转为毫秒。
概念:MUTEX
多线程访问全局变量,解决重复写入和冲突等问题。
相当于创建一把钥匙,这个钥匙可以用来访问全局变量,每个任务可以选择性获取和给出这把钥匙,避免多个任务对全局变量的修改冲突。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30// 创建钥匙
SemaphoreHandle xMutexInventory;
//main()
// 创建mutex
xMutexInventory = xSemaphoreCreateMutex();
if (xMutexInventory == NULL) {
printf("创建Semaphore失败,检查是否有足够内存")
} else {
xTaskCreate(...);
...
}
//task()
int timeOut = 1000;
void task() {
while(1) {
// 在timeout时间内如果能够获取就继续
if (xSemaphoreTake(xMutexInventory, timeOut) == pdPASS){
// 被MUTEX保护的内容叫做Critical Section
...
// 返还钥匙
xSemaphoreGive(xMutexInventory);
} else {
// 无法获取钥匙做什么
}
}
}
使用多核心
xPortGetCoreID() 获取当前任务工作在哪个核心。
xTaskCreatePinnedToCore() 设置任务工作在哪个核心1
2
3
4
5
6
7
8xTaskCreatePinnedToCore( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint32_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask,
const BaseType_t xCoreID
)
以绝对的频率运行任务
uint32_t tick = xTaskGetTickCount() 获取tick数。
一种实现方式1
2
3
4
5
6
7
8
9void task(void *pd) {
for (;;) {
uint32_t a = xTaskGetTickCount();
// do something
uint32_t b = xTaskGetTickCount();
vTaskDelay(3000 - (b-a));
}
}
这种方法存在弊端:其中a b的各种运算也需要消耗时间,频率不准确。
另一种方案1
2
3
4
5
6
7
8
9
10
11
12
13void task(void *pd) {
static float price = 99.57;
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xFrequency = 3000; // 间隔3s
for(;;) {
printf("%d", price);
vTaskDelayUntil(&xLastWakeTime, xFrequency); // 第一个参数给指针类型让此函数自动更新XLastWakeTime
}
}
软件定时器
- TimerHandle_t lockHandle, checkHandle; 声明两个Handle。
- 创建Timer
1
2
3
4
5
6
7
8
9void lockCarCallback(TimerHandle_t xTimer) {
// do something
}
lockHandle = xTimerCreate("Lock Car", // 名字
2000, // 时间 2000tick
pdFALSE, // 一次性事件 timer时间内按多次按钮只执行一次
// pdTRUE 多次事件 周期执行
(void*)0, // ID 空指针 多个timer需要改变
lockCarCallback); // 回调函数 - 开始Timer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28void carKey(void *pd) {
gpio_set_direction(GPIO_NUM_23, GPIO_MODE_INPUT);
for(;;){
if (gpio_get_level(GPIO_NUM_23) == 0) {
// timeout 3000 ticks
// xTimerStart 只是开始时间而已,不是创造时间对象
// 所以多次按按钮不会有多个时间对象生成
// 多次按按钮相当于每次重置timer xTimerReset()
if(xTimerStart(lockHandle, 3000) == pdPASS) {
// 将要锁车
} else {
// 不能锁车
}
vTaskDelay(100); // 按钮防抖
}
}
}
// 当pdTRUE 无限大的delay 当此timer负责极其重要的传感器任务时
xTimerStart(checkHandle, portMAX_DELAY);
xTaskCreate(carKey,
"Check if key was pushed",
1024 * 1,
NULL,
1,
NULL);任务内存管理
查看占用
uint32_t size = esp_get_free_heap_size() 获取空闲heap大小int waterMark = uxTaskGetStackHighWaterMark(taskHandle)1
2
3printf("Free heap size: %ld \n", esp_get_free_heap_size());
// 每个任务除stack size 外 还占有大概300多字节的空间 用于恢复和控制任务。
// 调用printf可能会消耗1060字节左右的空间利用此功能来确定任务栈内存的最低水平1
2int waterMark = uxTaskGetStackHighWaterMark(t1);
printf("Water mark: %d \n", waterMark);1
2
3
4
5
6
7
8
9
10void demo_task(void *pd) {
uint8_t statck_hwm=0, temp;
for (;;) {
temp = uxTaskGetStackHighWaterMark(nullptr); // 空指针代表当前任务
if (!stack_hwm || temp<stack_hwm) {
stack_hwm = temp;
printf("Stack size for demo_task %u\n", stack_hwm);
}
}
}任务管理
任务的暂停恢复与删除
vTaskSuspend(), vTaskResume(), vTaskDelete()1
2
3
4
5
6
7TaskHandle_t bHandle = NULL;
vTaskCreate(Task, "TASK", 1024*4, NULL, 1, &bHandle);
vTaskSuspend(bHandle);
vTaskResume(bHandle);
// 一定要判断bHandle是否为空,以及删除后将bHandle置空。
vTaskDelete(bHandle);
bHandle = NULL;