打印
[研电赛技术支持]

GD32开发之RTOS

[复制链接]
312|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2025-4-10 14:02 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
1. 基础知识简介
1.1 操作系统概述
操作系统从整体上分为两大类:通用操作系统和实时操作系统。

通用操作系统(General-Purpose Operating System)
通用操作系统是广泛应用于个人电脑、服务器、移动设备等的多用途操作系统,能够同时处理多种任务,支持多用户和多任务环境。它的设计目标是灵活性、兼容性和用户友好性,而非对时间的严格约束。。例如 Windows、macOS、Linux 等。

实时操作系统(RTOS)
定义:实时操作系统专为严格时间约束场景设计,确保任务在限定时间内完成响应。例如 VxWorks、QNX、FreeRTOS 等。主要分为两类:
硬实时系统: 任务必须在严格的时间限制内完成(如导弹控制系统)。
软实时系统: 任务需要尽快完成,但允许一定延迟(如视频流播放)。



1.2 Free RTOS 简介
FreeRTOS 是一个 轻量级、开源的实时操作系统内核(RTOS),专为资源受限的嵌入式设备设计,支持多任务处理和实时任务调度。其核心特点包括:

开源免费:采用 MIT 许可证,允许免费使用、修改和分发。
实时性:确保任务在指定时间内完成,适用于硬实时和软实时场景。
低资源占用:适用于内存有限的微控制器(MCU)和小型微处理器。
可移植性:支持多种处理器架构(如 ARM Cortex-M、RISC-V、x86 等)和开发平台(如 STM32、ESP32、Arduino 等)。
FreeRTOS 由 Richard Barry 于 2003 年开发,2017 年被亚马逊收购,现称为 AWS FreeRTOS,并持续更新维护(最新版本为 10.4.4)。

FreeRTOS的代码可以分解为三个主要区块:任务,通讯,和硬件接口。
任务: 大约有一半的FreeRTOS的核心代码用来处理多数操作系统首要关注的问题:任务。任务是给定优先级的用户定义的C函数。task.c和task.h完成了所有有关创建,调度,和维护任务的繁重工作。
通讯: 任务很重要,不过任务间可以互相通讯则更为重要!它给我们带来FreeRTOS的第二项任务:通讯。大约40%的FreeRTOS核心代码是用来处理通讯的。queue.c和queue.h是负责处理FreeRTOS的通讯的。任务和中断使用队列互相发送数据,并且使用信号灯和互斥来发送临界资源的使用情况。
硬件接口: 接近9000行的代码拼凑起基本的FreeRTOS,是硬件无关的;相同的代码都能够运行,不论FreeRTOS是运行在不起眼的8051,还是最新、最炫的ARM内核上。大约有6%的FreeRTOS的核心代码,在硬件无关的FreeRTOS内核与硬件相关的代码间扮演着垫片的角色。我们将在下个部分讨论硬件相关的代码。

内核与调度机制
内核架构
任务调度器:基于优先级抢占式调度,高优先级任务可立即执行。
中断管理:快速响应硬件中断,支持中断服务例程(ISR)与任务通信。
上下文切换:任务切换时间极短(微秒级),确保实时性。

调度策略
优先级调度:任务按优先级从高到低执行,优先级相同则轮转调度。
动态优先级调整:运行时可通过 API 调整任务优先级,灵活适应需求。

1.3 Ffee RTOS核心功能
1. 任务管理
多任务调度:基于优先级的抢占式调度算法,高优先级任务可立即中断低优先级任务。
任务创建与删除:通过 xTaskCreate() 创建任务,vTaskDelete() 删除任务。
任务状态:包括运行态、就绪态、阻塞态、挂起态等。

2. 同步与通信
信号量(Semaphore):用于任务间同步或资源互斥(如二进制信号量、计数信号量)。
消息队列(Queue):任务间数据传递的高效机制。
互斥锁(Mutex):支持优先级继承,避免死锁。

3. 时间管理
软件定时器:支持周期性或一次性任务触发。
延迟函数:vTaskDelay() 可精确控制任务休眠时间。

4. 内存管理
提供多种堆内存分配方案(Heap_1 至 Heap_5),适应不同场景需求:
Heap_1:固定内存分配,无碎片,但无法释放内存。
Heap_2/Heap_4:支持动态分配和释放,采用最佳匹配或优先匹配算法。
Heap_5:支持多堆分区,适用于复杂内存管理需求。

5. 中断与硬件交互
提供 API(如 xTaskNotifyFromISR())实现中断与任务的高效通信。

2. Free RTOS常用函数
2.1 任务
xTaskCreate()  //创建一个新任务。
vTaskDelete()  //删除一个任务。
vTaskDelay() //延迟当前任务指定的时间周期(滴答数)。
vTaskPrioritySet() //动态设置任务优先级。

xTaskCreate(vTask1, "Task 1", 100, NULL, 1, NULL);
xTaskCreate(vTask1, "Task 2", 100, NULL, 2, NULL);  // 优先级2

vTaskDelay(1000 / portTICK_PERIOD_MS);  // 延时1000ms (1秒)
vTaskDelete(NULL);  // 删除当前任务
vTaskStartScheduler();    // 启动调度器


2.2 队列
xQueueCreate() //创建一个队列。
xQueueSend()  // 于向队列发送数据
xQueueReceive() //分别用和从队列接收数据。

xQueueSend(xQueue, &data, portMAX_DELAY);
xQueueReceive(xQueue, &receivedData, portMAX_DELAY);
xQueue = xQueueCreate(10, sizeof(int));  // 创建一个队列,最多10个整数


2.3 信号量
xSemaphoreCreateBinary()  // 创建一个二进制信号量。

xSemaphoreTake()    // 获取信号量
xSemaphoreGive()  // 释放信号量。
SemaphoreHandle_t xSemaphore;  // 创建信号量
xSemaphore = xSemaphoreCreateBinary();  // 初始信号量
xSemaphoreGive(xSemaphore);  // 释放信号量

SemaphoreHandle_t xCountingSemaphore = xSemaphoreCreateCounting(maxCount, initialCount); // 创建计数信号量


2.4 互斥量
xSemaphoreCreateMutex()  //创建一个互斥锁。
xSemaphoreTake()
xSemaphoreGive()    //释放互斥锁
xMutex = xSemaphoreCreateMutex();  // 初始互斥量

if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {
    // 访问受保护资源的代码
}


2.5 软定时器
TimerHandle_t xTimer;
void vTimerCallback(TimerHandle_t xTimer) {
}// 定时器回调函数代码

xTimer = xTimerCreate("Timer", pdMS_TO_TICKS(1000), pdTRUE, 0, vTimerCallback);// 创建定时器
xTimerStart(xTimer, 0); // 启动定时器


2.6 配置参数
#define configUSE_PREEMPTION                1
#define configUSE_IDLE_HOOK                 0
#define configUSE_TICK_HOOK                 0
#define configCPU_CLOCK_HZ                  ( ( unsigned long ) 72000000 )  // 时钟频率
#define configTICK_RATE_HZ                  ( ( TickType_t ) 1000 )  // 系统节拍时间,单位是 Hz
#define configMAX_PRIORITIES                ( 5 )
#define configMINIMAL_STACK_SIZE            ( ( unsigned short ) 128 )
#define configTOTAL_HEAP_SIZE               ( ( size_t ) ( 10 * 1024 ) )  // 堆内存大小


2.7 状态
void check_task_state(TaskHandle_t xTask)
{
    eTaskState state = eTaskGetState(xTask);

    switch(state) {
        case eRunning:
            printf("Task is Running\r\n");
            break;
        case eReady:
            printf("Task is Ready\r\n");
            break;
        case eBlocked:
            printf("Task is Blocked\r\n");
            break;
        case eSuspended:
            printf("Task is Suspended\r\n");
            break;
        case eDeleted:
            printf("Task is Deleted\r\n");
            break;
        case eInvalid:
            printf("Task State is Invalid\r\n");
            break;
    }




2.8 延时函数
FreeRTOS 中常用的延时函数有两个 vTaskDelay 和 vTaskDelayUntil



vTaskDelay(相对延时)
功能: 使任务暂停执行一段固定时间,从调用函数的时刻开始计算。

特点
相对性:延时时间仅与调用时刻相关,不考虑系统全局时间。
简单直接:适合需要短暂暂停的任务(如等待传感器数据准备就绪)。
不保证周期性:若任务执行时间不确定,可能导致周期漂移。

// xTicksToDelay:延时时间,单位为 Tick(可通过 pdMS_TO_TICKS() 将毫秒转换为 Tick)。
vTaskDelay(xTicksToDelay)

void vTaskExample1(void *pvParameters) {
    while (1) {
        // 任务逻辑(如执行某操作)
        printf("Task is running...\n");
        // 延时1秒(相对延时)
        vTaskDelay(pdMS_TO_TICKS(1000)); // 1秒后继续执行
    }
}


vTaskDelayUntil(绝对延时)
功能: 使任务以固定周期执行,确保任务在指定时间间隔后重新激活。

特点:
绝对性:基于系统时间戳,确保任务周期严格固定。
自适应调整:即使任务执行时间波动,仍能保持周期性。
需初始化:首次调用前必须用 xTaskGetTickCount() 初始化 pxPreviousWakeTime。

// pxPreviousWakeTime:指向一个变量,记录上次任务唤醒的时间(需初始化)
// xTimeIncrement:任务周期(单位:Tick,可通过 pdMS_TO_TICKS() 转换)
vTaskDelayUntil(pxPreviousWakeTime, xTimeIncrement)

void vTaskExample2(void *pvParameters) {
    TickType_t xLastWakeTime; // 记录上次唤醒时间
    const TickType_t xFrequency = pdMS_TO_TICKS(1000); // 周期1秒

    xLastWakeTime = xTaskGetTickCount(); // 初始化时间戳
    while (1) {
        // 任务逻辑(如定时采样)
        printf("Task is running every 1 second...\n");
        // 延时直到下一个周期
        vTaskDelayUntil(&xLastWakeTime, xFrequency);
    }
}



3. 编译报错
报错1: identifier “SystemCoreClock” is undefined

compiling port.c…
…\Driver\FreeRTOS\port\RVDS\ARM_CM4F\port.c(813): error: #20: identifier “SystemCoreClock” is undefined
portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;



解决办法
1:在FreeRTOSConfig.h中添加芯片头文件 #include “gd32f4xx.h”
2:将下面代码替换工程内的代码

#if defined(__ICCARM__)  || defined(__CC_ARM) || defined(__GNUC__)
    #include <stdint.h>
    extern uint32_t SystemCoreClock;
#endif


报错2:multiply defind

.\output\gd450_demo.axf: Error: L6200E: Symbol SVC_Handler multiply defined (by port.o and gd32f4xx_it.o).
.\output\gd450_demo.axf: Error: L6200E: Symbol PendSV_Handler multiply defined (by port.o and gd32f4xx_it.o).
.\output\gd450_demo.axf: Error: L6200E: Symbol SysTick_Handler multiply defined (by port.o and gd32f4xx_it.o).




解决办法

在gd32f4xx_it.c中注释掉相关代码



报错3:Undefined symbol

linking...
.\output\gd450_demo.axf: Error: L6218E: Undefined symbol vApplicationIdleHook (referred from tasks.o).
.\output\gd450_demo.axf: Error: L6218E: Undefined symbol vApplicationStackOverflowHook (referred from tasks.o).
.\output\gd450_demo.axf: Error: L6218E: Undefined symbol vApplicationTickHook (referred from tasks.o).
.\output\gd450_demo.axf: Error: L6218E: Undefined symbol vApplicationMallocFailedHook (referred from heap_4.o).



解决方法
在FreeRTOSConfig.h中更改对应宏定义



————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/qq_45113070/article/details/147035067

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2201

主题

16568

帖子

17

粉丝