CMSIS-RTOS 时间管理之时间延迟Time Delay

本文介绍RTOS中的时间管理功能,包括基本的延时函数和等待事件等,并通过实例展示了如何利用这些功能进行线程调度,实现高效的CPU资源分配。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

时间管理 Time Management

此RTOS除了可以把你的应用代码作为线程运行,它还可以提供一些时间服务功能,使用这些功能你就可以访问RTOS的一些系统调用。

时间延迟Time Delay

在所有的时间服务功能中,最基本的一个就是延时函数。它可以在你的应用中提供非常简单易用的延时功能。也许你会觉得CMSIS-RTOS已经占用了5k字节的代码量,但是在非RTOS的应用中,我们也常会用到一些延时循环、简单的调度循环等,这些循环功能同样会占用一些字节,而我们的RTOS在这方面可能会占用更少的代码量。

void osDelay(uint32_t millisec)

上面这个调用会导致当前线程进入等待延时状态(WAIT_DELAY),延时时间由用户指定。与此同时调度器将会执行下一个处于准备状态(READY)的线程。

这里写图片描述

注:在线程的生命周期中,它会进入多种状态。这里一个处于运行状态(RUN)的线程被osDelay阻塞,然后它就会进入等待状态(WAIT)。当延时时间结束时,它就会进入准备状态(READY),调度器就会把它置于运行状态(RUN)。如果它的时间片结束了,它就会返回准备状态(READY)。

当定时时间结束时,线程会离开等待延时状态,进入READY状态。当调度器把线程移入RUNNING状态时,它就会继续运行。如果线程在以后的执行过程中没有任何阻塞调用,在它的时间片结束时就会被置于READY状态,同时另外一个同优先级的线程就会进入运行状态。

等待事件

除了单纯的时间延迟,同样可以使用事件等待让线程暂停并进入等待状态,当有另外一个RTOS事件出现时,就会触发线程继续运行。RTOS事件可以是一个信号,消息或者邮件。如果没有事件出现,就可以osWait()这个API,它有一个毫秒级别的超时机制,可以允许线程的唤醒和继续执行。

osStatus osWait(uint32_t millisec)//RTX不支持此函数

当设定的时间结束,线程就会由WAIT状态进入到READY状态,随后被调度器置于RUN状态。osWait在CMSIS RTOS里面是一个可选API。如果你打算使用这个函数,必须先确定你使用的RTOS是支持的。需要注意的是,CMSIS RTOS目前封装的keil RTX 是不支持这个API的。

练习:时间管理

在这个练习里我们将看到如何使用基本的时间延迟函数。

打开Pack Installer,选择“Ex5 Time Management”,然后把它复制到自定义路径。

从代码中可以看到,者就是我们最开始的LED闪烁程序,不同之处在于之前简单的延时函数delay()被替换成了osDelay()。LED2以100ms为间隔翻转,LED1以500ms为间隔翻转。

void ledOn(void const *argument) {
    for(;;) {
        LED_On(1);
        osDelay(500);
        LED_Off(1);
        osDelay(500);
    }
}

编译工程,启动debug仿真

全速运行!并打开事件观察器

这里写图片描述

现在我们可以看到活跃的代码完全不同,当每一个LED任务执行到osDelay 函数时,线程就会“阻塞”,并进入WAIT状态,main 线程就会进入READY状态,这时调度器就会启动它运行。当延时时间结束,LED任务就会进入READY状态,下一步就会被调度器置于RUN状态。就像这样,多个线程可以有效率地分享CPU资源。

/* USER CODE BEGIN Header */ /** ****************************************************************************** * File Name : freertos.c * Description : Code for freertos applications ****************************************************************************** * @attention * * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "FreeRTOS.h" #include "task.h" #include "main.h" #include "cmsis_os.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "rtc.h" #include "oled.h" extern int flag; extern struct tm time_data, alarm_data; /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN Variables */ /* USER CODE END Variables */ osThreadId defaultTaskHandle; osThreadId myTaskHandle; /* Private function prototypes -----------------------------------------------*/ /* USER CODE BEGIN FunctionPrototypes */ /* USER CODE END FunctionPrototypes */ void StartDefaultTask(void const * argument); void StartTask(void const * argument); void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */ /* GetIdleTaskMemory prototype (linked to static allocation support) */ void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ); /* USER CODE BEGIN GET_IDLE_TASK_MEMORY */ static StaticTask_t xIdleTaskTCBBuffer; static StackType_t xIdleStack[configMINIMAL_STACK_SIZE]; void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ) { *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer; *ppxIdleTaskStackBuffer = &xIdleStack[0]; *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; /* place for user code */ } /* USER CODE END GET_IDLE_TASK_MEMORY */ /** * @brief FreeRTOS initialization * @param None * @retval None */ void MX_FREERTOS_Init(void) { /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* USER CODE BEGIN RTOS_MUTEX */ /* add mutexes, ... */ /* USER CODE END RTOS_MUTEX */ /* USER CODE BEGIN RTOS_SEMAPHORES */ /* add semaphores, ... */ /* USER CODE END RTOS_SEMAPHORES */ /* USER CODE BEGIN RTOS_TIMERS */ /* start timers, add new ones, ... */ /* USER CODE END RTOS_TIMERS */ /* USER CODE BEGIN RTOS_QUEUES */ /* add queues, ... */ /* USER CODE END RTOS_QUEUES */ /* Create the thread(s) */ /* definition and creation of defaultTask */ osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128); defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL); /* definition and creation of myTask */ osThreadDef(myTask, StartTask, osPriorityLow, 0, 128); myTaskHandle = osThreadCreate(osThread(myTask), NULL); /* USER CODE BEGIN RTOS_THREADS */ /* add threads, ... */ /* USER CODE END RTOS_THREADS */ } /* USER CODE BEGIN Header_StartDefaultTask */ /** * @brief Function implementing the defaultTask thread. * @param argument: Not used * @retval None */ /* USER CODE END Header_StartDefaultTask */ void StartDefaultTask(void const * argument) { /* USER CODE BEGIN StartDefaultTask */ /* Infinite loop */ for(;;) { osDelay(1); } /* USER CODE END StartDefaultTask */ } /* USER CODE BEGIN Header_StartTask */ /** * @brief Function implementing the myTask thread. * @param argument: Not used * @retval None */ /* USER CODE END Header_StartTask */ void StartTask(void const * argument) { /* USER CODE BEGIN StartTask */ /* Infinite loop */ for(;;) { if(flag == 1) { rtc_get_time(); HAL_Delay(1000); if(time_data.tm_sec >10) { oled_show_string(0,5,"TIME_STOP",16); } } osDelay(1); } /* USER CODE END StartTask */ } /* Private application code --------------------------------------------------*/ /* USER CODE BEGIN Application */ /* USER CODE END Application */ 该程序用到了freertos的哪些东西
06-14
该程序使用了 FreeRTOS 的以下核心功能和组件: --- ### 1. **任务管理** FreeRTOS 提供了多任务调度功能,允许用户创建多个任务并让它们并发运行(实际上是通过时间片轮转实现的)。在代码中可以看到两个任务的定义和创建: #### 定义任务: ```c osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128); osThreadDef(myTask, StartTask, osPriorityLow, 0, 128); ``` - `osThreadDef` 是 CMSIS-RTOS API 中用于定义任务的宏。 - 参数说明: - `defaultTask` 和 `myTask` 是任务的名字。 - `StartDefaultTask` 和 `StartTask` 是任务的入口函数。 - `osPriorityNormal` 和 `osPriorityLow` 是任务的优先级。 - `0` 表示任务的参数(这里是空)。 - `128` 是任务栈的大小(单位为字节)。 #### 创建任务: ```c defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL); myTaskHandle = osThreadCreate(osThread(myTask), NULL); ``` - `osThreadCreate` 是用于创建任务的函数。 - 返回值是任务的句柄,用于后续操作(如删除或挂起任务)。 --- ### 2. **延迟机制** FreeRTOS 提供了任务延迟功能,可以让任务暂停一段时间以释放 CPU 资源。 #### 延迟任务: ```c osDelay(1); ``` - `osDelay` 是 CMSIS-RTOS API 中的任务延迟函数。 - 参数表示延迟的时间(单位为毫秒)。 #### 硬件延迟: ```c HAL_Delay(1000); ``` - `HAL_Delay` 是 STM32 HAL 库提供的硬件延迟函数。 - 它与 FreeRTOS 的任务调度无关,仅用于阻塞当前任务。 --- ### 3. **静态内存分配** FreeRTOS 支持动态和静态内存分配。在这段代码中,使用了静态内存分配来创建空闲任务。 #### 静态分配空闲任务内存: ```c static StaticTask_t xIdleTaskTCBBuffer; static StackType_t xIdleStack[configMINIMAL_STACK_SIZE]; void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ) { *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer; *ppxIdleTaskStackBuffer = &xIdleStack[0]; *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; } ``` - `vApplicationGetIdleTaskMemory` 是一个回调函数,用于为 FreeRTOS 的空闲任务分配静态内存。 - 这种方式可以避免使用动态内存分配,从而减少内存碎片问题。 --- ### 4. **初始化函数** FreeRTOS 提供了一个初始化函数,用于启动调度器并创建任务。 #### 初始化 FreeRTOS: ```c void MX_FREERTOS_Init(void) { osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128); defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL); osThreadDef(myTask, StartTask, osPriorityLow, 0, 128); myTaskHandle = osThreadCreate(osThread(myTask), NULL); } ``` - `MX_FREERTOS_Init` 是 FreeRTOS 的初始化函数。 - 在这个函数中,定义并创建了两个任务:`defaultTask` 和 `myTask`。 --- ### 5. **任务入口函数** 每个任务都有一个入口函数,它是一个无限循环,任务的所有逻辑都在这里实现。 #### 默认任务: ```c void StartDefaultTask(void const * argument) { for(;;) { osDelay(1); } } ``` - 这个任务只是简单地执行一个无限循环,并每次循环后延迟 1 毫秒。 - 主要作用是保持任务运行状态。 #### 自定义任务: ```c void StartTask(void const * argument) { for(;;) { if(flag == 1) { rtc_get_time(); HAL_Delay(1000); if(time_data.tm_sec >10) { oled_show_string(0,5,"TIME_STOP",16); } } osDelay(1); } } ``` - 这个任务的主要功能是检查 `flag` 的值。 - 如果 `flag == 1`,则读取 RTC 时间并在 OLED 屏幕上显示特定信息。 - 每次循环结束后延迟 1 毫秒。 --- ### 6. **其他组件** 虽然这段代码没有直接用到以下 FreeRTOS 功能,但它们是 FreeRTOS 的核心特性: - **信号量**:用于任务间的同步。 - **消息队列**:用于任务间的数据传递。 - **互斥量**:用于保护共享资源。 - **定时器**:用于周期性任务调度。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值