freeRTOS 任务的创建、挂起、恢复

freeRTOS

  1. FreeRTOS 是一个开源的实时操作系统(RTOS),专为嵌入式系统特别是微控制器设计。
  2. 它是轻量级的,提供了一个多任务环境,允许多个任务在单个处理器上并发运行。
  3. 主要特性:抢占式多任务、任务调度、可移植性,任务间通信,内存管理
  4. 虽然 FreeRTOS 使用了“任务”这一术语,但它们在功能和概念上类似于传统操作系统中的“线程”。这两个术语都表示了并行执行的基本单位,并且都涉及到优先级、堆栈管理、调度等关键概念。
  5. 在 FreeRTOS 中,任务的管理和调度是通过 FreeRTOS 提供的 API 实现的,而在传统操作系统中,线程的管理则是由操作系统的线程库或内核提供的。

常用函数解析

TaskHandle_t 创建任务句柄
TaskHandle_t 实际上是一个指向任务控制块(TCB)的指针,TCB 是 FreeRTOS 内部用于存储任务状态的数据结构。

用法
保存任务句柄: 在调用 xTaskCreate 创建任务时,可以将任务句柄传递到一个 TaskHandle_t 变量中。这个句柄可以用来引用任务,以便后续对任务进行操作。

// 定义任务句柄
TaskHandle_t taskFlushled;

// 任务函数
void task_fun_flushled(void *args) {
    while (1) {
        // 切换 GPIO 状态
        HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8);
        osDelay(1000);  // 延迟 1 秒
    }
}

void setup() {
    // 创建任务并将句柄存储在 taskFlushled 中
    BaseType_t ret = xTaskCreate(
        task_fun_flushled,      // 任务函数
        "taskFlushled",         // 任务名称
        128,                    // 堆栈大小
        NULL,                   // 任务参数
        osPriorityNormal,       // 优先级
        &taskFlushled           // 任务句柄
    );

    // 检查任务创建是否成功
    if (ret == pdFALSE) {
        printf("任务创建失败\n");
    }
}

xTaskCreate() 任务创建函数
BaseType_t xTaskCreate(TaskFunction_t pvTaskCode,
                       const char * const pcName,
                       const configSTACK_DEPTH_TYPE usStackDepth,
                       void *pvParameters,
                       UBaseType_t uxPriority,
                       TaskHandle_t *pxCreatedTask);
参数
		TaskFunction_t pvTaskCode: 指向任务执行的函数,这个函数需要符合 void func(void *) 的形式。
		const char * const pcName: 任务的名字,用于调试。
		const configSTACK_DEPTH_TYPE usStackDepth: 任务的堆栈大小,以字为单位。
		void *pvParameters: 传递给任务的参数。
		UBaseType_t uxPriority: 任务的优先级,数值越大,优先级越高。
		TaskHandle_t *pxCreatedTask: 用于传出任务句柄的指针,可以用于后续的任务引用或删除等操作。即为任务本身

返回值
	pdFALSE:表示任务创建失败
-------------------优先级--------------------------
typedef enum {
  osPriorityIdle          = -3,          ///< 优先级:空闲(最低优先级)
  osPriorityLow           = -2,          ///< 优先级:低
  osPriorityBelowNormal   = -1,          ///< 优先级:低于正常
  osPriorityNormal        =  0,          ///< 优先级:正常(默认优先级)
  osPriorityAboveNormal   = +1,          ///< 优先级:高于正常
  osPriorityHigh          = +2,          ///< 优先级:高
  osPriorityRealtime      = +3,          ///< 优先级:实时(最高优先级)
  osPriorityError         =  0x84        ///< 优先级:错误(系统无法确定优先级或线程具有非法优先级)
} osPriority;
-------------------------------------------------------------

具体用法
	xTaskCreate(task_fun_printf,"taskPrintf",128,NULL,osPriorityNormal,&taskPrintHandle);
-----以下是具体执行函数----------
任务不是只执行一次所以有一个函数体
并且不能光是你一个人执行,故有osDelay函数
	void task_fun_printf(void *args)
{
	while(1)
	{
		printf("%s-%d\r\n",__func__,__LINE__);
		osDelay(1000);
	}
}
----------以下是任务句柄,当然它应该放在最前面----------------
TaskHandle_t taskPrintHandle;
portSET_INTERRUPT_MASK_FROM_ISR() 禁用中断
这个宏在中断服务例程(ISR)中使用。
确保操作的原子性和数据的一致性。
防止中断处理过程中被其他中断打断。

portSET_INTERRUPT_MASK_FROM_ISR(): 设置中断掩码,禁用中断。
portCLEAR_INTERRUPT_MASK_FROM_ISR(): 恢复中断掩码,重新启用中断。

用法
	int __io_putchar(int ch)
{
    UBaseType_t vxx;
    vxx = portSET_INTERRUPT_MASK_FROM_ISR();
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1);
    portCLEAR_INTERRUPT_MASK_FROM_ISR(vxx);
    return ch;
}

defaultTask 默认任务
defaultTask 在系统中扮演了多个角色,包括初始化系统、**保持**系统运行(占位)、优化任务调度、处理异常和调试开发。

该任务由STM32CubeIDE自动创建,代码如下
// 定义 defaultTask
osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);
// 创建 defaultTask 任务
osThreadId defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);
// 任务函数
void StartDefaultTask(void const * argument)
{
  // 执行系统初始化或其他必要操作
  for(;;)
  {
    // 空循环,保持任务在运行状态
    osDelay(1);  // 延迟以允许其他任务执行
  }
}
osKernelStart() 启动内核调度器
osKernelStart() 是 FreeRTOS 中的一个函数,用于启动 RTOS 内核的调度器。
在调用 osKernelStart() 后,系统的控制权将转交给 RTOS 内核。通常,main() 函数中的 while (1) 循环会继续存在,但它不会执行任何操作,因为控制权已交给内核调度。
vTaskResume 恢复(激活)被挂起的任务
函数原型
void vTaskResume(TaskHandle_t xTaskToResume);

它的作用是将任务从挂起状态重新设置为就绪状态,使得该任务可以被调度和执行。

升级版
xTaskResumeFromISR()
允许在 ISR 中将任务的状态从挂起变为就绪,从而使任务能够在 ISR 处理完后尽快被调度执行。
vTaskSuspend 挂起任务
函数原型:
	void vTaskSuspend(TaskHandle_t xTaskToSuspend);

用于将一个正在运行或就绪的任务挂起。挂起的任务将不会被调度或执行,直到它被恢复。

升级版
vTaskSuspendFromISR
vTaskDelete 删除任务
函数原型:
void vTaskDelete(TaskHandle_t xTaskToDelete);
用于删除一个任务。调用这个函数会释放与任务相关的所有资源,并将任务从系统中移除。

xTaskToDelete: 要删除的任务的句柄。如果句柄为 NULL,则当前正在运行的任务将被删除。如果要删除的任务不是当前任务,传入任务句柄即可。
注意,删除任务的时候要注意同步问题;一般是 自我删除;

void task_test_fun(void *args)
		{
			while(1){
				干活; if(干完了){break;}
			}
			vTaskDelete(NULL);  //删除自己
		}
疑问为什么删除osDelay后,其它函数也会执行
  • 各个任务在 FreeRTOS 中是独立的,除非有显式的同步机制(如信号量、互斥量等),一个任务的执行状态不会直接影响其他任务。
osDelay和HAL_Delay的区别
osDelay
  • osDelay 是 FreeRTOS 提供的延迟函数
  • osDelay 会将当前任务挂起一段指定的时间,在这段时间内,调度器可以将 CPU 分配给其他任务执行。等延迟时间结束后,当前任务会被重新放入就绪队列,等待调度器再次调度。
HAL_Delay
  • HAL_Delay 是 STM32 HAL 库中的一个延迟函数。它的工作原理是利用 SysTick 定时器,通过轮询计数器来产生延迟。在调用 HAL_Delay() 时,程序会进入一个忙等待循环(也叫阻塞等待),直到指定的时间过去。这意味着在这段延迟时间内,微控制器不会执行其他任务,完全被阻塞住了。
在freeRTOS中禁止使用HAL_Delay
  • 在多任务环境中使用 HAL_Delay 会阻塞整个系统,导致其他任务无法执行,系统无法实现真正的并发。

代码示例

1.RCC设置Crystal
2.USART1设置为异步通信
3.FREERTOS 的interface选择CMSIS_C1(简单但功能有限)
4.配置测试管脚,我用的是PC6,7,8作为LED灯管脚

具体代码如下 功能:交替打印连续数
#include "main.h"  // 引入项目主头文件,包含系统配置和外设定义。
#include "cmsis_os.h"  // 引入CMSIS-RTOS API库,用于FreeRTOS的任务管理。

UART_HandleTypeDef huart1;  // 声明一个UART句柄,用于管理USART1外设。
osThreadId defaultTaskHandle;  // 声明一个线程句柄,用于默认任务管理。

void SystemClock_Config(void);  // 声明系统时钟配置函数。
static void MX_GPIO_Init(void);  // 声明GPIO初始化函数。
static void MX_USART1_UART_Init(void);  // 声明USART1初始化函数。
void StartDefaultTask(void const * argument);  // 声明默认任务的实现函数。

int __io_putchar(int ch)  // 定义一个输出函数,用于重定向printf的输出到UART。
{
    UBaseType_t vxx;  // 声明一个变量,用于保存中断状态。
    vxx = portSET_INTERRUPT_MASK_FROM_ISR();  // 禁止中断,确保原子性操作。
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1);  // 通过UART发送单个字符。
    portCLEAR_INTERRUPT_MASK_FROM_ISR(vxx);  // 恢复中断。
    return ch;  // 返回发送的字符。
}

int cnt = 0;  // 定义一个全局计数器变量。
TaskHandle_t taskOuHandle;  // 定义一个任务句柄,用于管理 "task_fun_ou" 任务。
TaskHandle_t taskJiHandle;  // 定义一个任务句柄,用于管理 "task_fun_ji" 任务。

void task_fun_ou(void *args)  // 定义一个名为 "task_fun_ou" 的任务函数。
{
    int Acnt = 0;  // 定义局部计数变量。
    while (1)  // 无限循环,任务主循环。
    {
        printf(" %d \r\n", Acnt);  // 打印当前计数值。
        Acnt += 2;  // 每次循环计数器加2。
        vTaskResume(taskJiHandle);  // 恢复 "task_fun_ji" 任务。
        vTaskSuspend(taskOuHandle);  // 挂起当前任务(task_fun_ou)。
    }
}

void task_fun_ji(void *args)  // 定义一个名为 "task_fun_ji" 的任务函数。
{
    vTaskSuspend(taskJiHandle);  // 首先挂起自己,等待 "task_fun_ou" 任务恢复。
    int Bcnt = 1;  // 定义局部计数变量。
    while (1)  // 无限循环,任务主循环。
    {
        printf(" %d \r\n", Bcnt);  // 打印当前计数值。
        Bcnt += 2;  // 每次循环计数器加2。
        vTaskResume(taskOuHandle);  // 恢复 "task_fun_ou" 任务。
        vTaskSuspend(taskJiHandle);  // 挂起当前任务(task_fun_ji)。
        osDelay(1000);  // 延时1秒(1000毫秒)。
    }
}

int main(void)  // 主程序入口点。
{
    HAL_Init();  // 初始化硬件抽象层(HAL库),配置外设和中断。
    SystemClock_Config();  // 配置系统时钟。
    MX_GPIO_Init();  // 初始化GPIO引脚。
    MX_USART1_UART_Init();  // 初始化USART1外设。

    osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);  // 定义默认任务属性。
    defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);  // 创建并启动默认任务。

    BaseType_t ret;  // 定义任务创建的返回值变量。

    ret = xTaskCreate(task_fun_ou, "taskPrintfOU", 128, NULL, osPriorityNormal, &taskOuHandle);  // 创建并启动 "task_fun_ou" 任务。
    if (ret == pdFALSE)  // 检查任务创建是否成功。
    {
        printf("xTaskCreate for print err\r\n");  // 打印错误信息。
        return -34;  // 返回错误代码。
    }

    ret = xTaskCreate(task_fun_ji, "taskPrintfODD", 128, NULL, osPriorityNormal, &taskJiHandle);  // 创建并启动 "task_fun_ji" 任务。
    if (ret == pdFALSE)  // 检查任务创建是否成功。
    {
        printf("xTaskCreate for flushled err\r\n");  // 打印错误信息。
        return -34;  // 返回错误代码。
    }

    osKernelStart();  // 启动FreeRTOS调度器,开始多任务运行。

    while (1)  // 主循环,实际上不会执行到这里,因为控制权交给了FreeRTOS调度器。
    {
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值