【工具使用】STM32CubeMX-FreeRTOS操作系统-任务、延时、定时器篇

一、概述

    无论是新手还是大佬,基于STM32单片机的开发,使用STM32CubeMX都是可以极大提升开发效率的,并且其界面化的开发,也大大降低了新手对STM32单片机的开发门槛。
    本文主要讲述STM32芯片FreeRTOS任务、延时、定时器功能的配置及其相关知识。

二、软件说明

    STM32CubeMX是ST官方出的一款针对ST的MCU/MPU跨平台的图形化工具,支持在Linux、MacOS、Window系统下开发,其对接的底层接口是HAL库,另外习惯于寄存器开发的同学们,也可以使用LL库。STM32CubeMX除了集成MCU/MPU的硬件抽象层,另外还集成了像RTOS,文件系统,USB,网络,显示,嵌入式AI等中间件,这样开发者就能够很轻松的完成MCU/MPU的底层驱动的配置,留出更多精力开发上层功能逻辑,能够更进一步提高了嵌入式开发效率。
    演示版本 6.1.0

三、FreeRTOS功能简介

    嵌入式初学者一般使用的是裸机开发,而大多数所谓的进阶课程,就是使用操作系统开发。其实两者并不存在很大的差距,使用操作系统,更多是在裸机开发的基础上,限制在操作系统要求的框架下进行开发,同时需要留意操作系统的一些特性,以防止出现问题。具体操作系统开发与裸机开发的区别如下:

维度FreeRTOS(RTOS 开发)裸机开发(轮询 / 中断驱动)
任务管理多任务并行(抢占式 / 协作式),自动调度单线程轮询 + 中断处理,手动协调任务优先级
实时性高(可精确控制任务执行顺序和响应时间)依赖中断优先级和轮询顺序,复杂场景易卡顿
系统复杂度适合复杂逻辑(如多外设、通信协议、用户界面)适合简单逻辑(如单一传感器采集、LED 控制)
代码结构模块化(任务独立,通过 IPC 通信)线性代码 + 全局变量,耦合度高
资源占用需额外内存(栈空间、内核数据结构)资源占用极小(仅代码和必要数据)
开发成本学习成本较高(需理解 RTOS 概念)门槛低,适合快速实现简单功能
可维护性任务隔离性好,扩展新功能更方便功能扩展可能需修改全局逻辑,维护困难

    提到操作系统,第一反应也是大家最早接触的,应该就是Windows系统了(当然新生代可能第一接触的是苹果的IOS系统或华为的鸿蒙系统),但由于Windows的交互友好性,让大家很难感知到它的存在;而学习了嵌入式后,又知道了Linux这个天花板般的操作系统存在,虽然是个开源系统,但其体量也让大多数人忘而生畏,从而使操作系统蒙上一层神秘的面纱。而今天的主角FreeRTOS,作为小体量且开源的操作系统,正是敲开操作系统神秘大门的砖头,带着我们了解其中的奥妙。

    FreeRTOS 是一款开源实时操作系统(RTOS),专为嵌入式系统设计,尤其适用于资源受限的微控制器(MCU)。由 Richard Barry 开发并发布首个版本(V1.0),最初名为 FreeRTOS Kernel,旨在提供轻量级、可移植的多任务处理能力。早期版本以代码简洁、易于移植为特点,迅速在嵌入式社区流行。2012 年,成立 Real Time Engineers Ltd 公司,推动 FreeRTOS 商业化,推出付费技术支持和扩展组件(如文件系统、TCP/IP 栈)。逐步支持更多硬件平台(如 ARM Cortex-M、ESP32、RISC-V 等),并构建生态系统,包括中间件和工具链。2016 年,亚马逊(AWS)收购 FreeRTOS,将其纳入 IoT 战略,推出 AWS IoT Greengrass for FreeRTOS,强化物联网(IoT)连接能力(如 MQTT、OTA 升级)。最新版本(截至 2025 年)为 V202212.00,持续优化实时性能、安全性和云集成,并提供免费的认证服务(如功能安全认证 IEC 61508)。

    FreeRTOS 以实时性、轻量级、可配置性为核心,功能模块包括:

  1. 任务调度器
  • 抢占式调度:支持多任务按优先级运行,高优先级任务可中断低优先级任务,确保实时响应。
  • 协作式调度(可选):任务主动释放控制权,适合对实时性要求不高的场景。
  • 支持 任务优先级(最多 32 级,可配置)和 时间片轮转(同优先级任务分时执行)。
  1. 任务间通信(IPC)
  • 队列(Queue):任务 / 中断间传递数据,支持先进先出(FIFO)或优先级队列。
  • 信号量(Semaphore):包括二进制信号量、计数信号量,用于资源同步与互斥(如 mutex 互斥信号量)。
  • 事件组(Event Group):实现任务间多事件同步。
  1. 内存管理
  • 提供多种内存分配策略:
    – 静态分配:编译时分配固定内存,适合关键任务,避免内存碎片。
    – 动态分配:运行时动态申请内存(类似 C 语言 malloc),需注意碎片问题。
  • 支持用户自定义内存管理方案。
  1. 定时器与中断管理
  • 软件定时器:基于系统时钟的周期性或一次性定时器,支持回调函数。
  • 中断安全接口:允许在中断服务程序(ISR)中安全访问 RTOS 资源(如队列、信号量)。
  1. 可配置性与移植性
  • 通过头文件(FreeRTOSConfig.h)配置内核参数(如任务数量、栈大小、调度器行为),灵活适配不同硬件。
  • 提供标准接口,移植到新平台只需实现少量汇编代码(如上下文切换)。
  1. 生态与扩展组件
  • 中间件:集成文件系统(如 FATFS)、TCP/IP 栈(LwIP)、USB 协议栈、图形界面(GUI)等。
  • 物联网支持:通过 AWS IoT 组件实现设备与云端通信,支持 MQTT、TLS 加密、设备管理等。
  • 安全与认证:提供功能安全版本(如 FreeRTOS-Safety),通过 IEC 61508、ISO 26262 等认证,适合工业、汽车电子等场景。

    除了FreeRTOS,其他还有很多其他优秀的嵌入式操作系统,其中就包括很多人学校里会学到的uC/OS-II,FreeRTOS与其他常见的嵌入式操作系统对比如下:

维度FreeRTOSuC/OS-IIRTX(ARM)RIOT OS
许可证开源(GPLv2,修改需开源)/ 商业许可开源(需购买商业许可用于产品)商业许可(需授权)开源(BSD-2-Clause)
实时性抢占式调度,微秒级响应抢占式调度,支持优先级继承抢占式调度,支持 CMSIS-RTOS 标准抢占式 + 协作式,适合 IoT 低功耗
代码复杂度简洁,核心代码约 10k 行 C 语言模块化设计,代码量较大集成于 Keil 工具链,抽象层完善面向 IoT,轻量级(<10KB 内存)
生态系统丰富(AWS IoT、中间件、社区支持)成熟(工业、汽车领域案例多)与 ARM 工具链深度整合(Keil、IAR)专注 IoT,支持传感器网络和低功耗协议
资源占用极小(ROM: ~4KB,RAM: ~1KB)中等(ROM: ~10KB,RAM: ~2KB)中等(依赖组件数量)极轻量(适合 8/16 位 MCU)
典型应用IoT 设备、消费电子、工业控制医疗设备、航空航天、汽车电子嵌入式系统开发(ARM Cortex-M 系列)物联网边缘设备、传感器节点

    FreeRTOS 凭借轻量、开源、易移植的特性,成为嵌入式领域最流行的 RTOS 之一,尤其适合 IoT 和中小型实时系统。其与 AWS 的深度整合进一步强化了物联网能力,而功能安全认证版本则拓展了工业和汽车电子市场。相比裸机开发,它能显著提升复杂系统的设计效率和实时性,但需权衡资源占用和学习成本。对于开发者而言,若项目需要多任务调度、实时响应或未来扩展,FreeRTOS 是理想选择;若需求简单或资源受限,裸机开发仍具优势。

四、FreeRTOS配置及应用

4.1 FreeRTOS配置说明

FreeRTOS配置

4.1.1 核心配置参数解析

  1. API 与版本
  • FreeRTOS API (CMSIS v1):基于 ARM CMSIS 标准,确保与 Cortex-M 系列芯片兼容,简化移植和开发。
  1. 操作系统版本
  • FreeRTOS version (10.0.1):内核版本,决定核心功能(如调度算法、内存管理)。
  • CMSIS-RTOS version (1.02):遵循 CMSIS-RTOS 规范,定义统一 API 接口(如任务创建、信号量操作)。
  1. 内核设置
  • USE_PREEMPTION:启用抢占式调度。抢占式就是高优先级任务可以打断低优先级的,Enabled 表示开启,这样系统实时性更好。
  • CPU_CLOCK_HZ:CPU 时钟频率,这里设为 SystemCoreClock,通常是芯片的系统核心时钟,比如 STM32 里的 SystemCoreClock 变量,运行时会根据时钟配置确定实际值。
  • TICK_RATE_HZ:系统滴答中断的频率,这里是 1000Hz,也就是 1ms 一次滴答。决定了任务延时的精度,比如 osDelay (100) 就是 100ms,基于这个滴答。
  • MAX_PRIORITIES:最大优先级数量,设为 7。FreeRTOS 的优先级是 0 到 MAX_PRIORITIES-1,所以这里是 0~6 共 7 个优先级。数值越大,调度更细致,但调度开销可能增加。
  • MINIMAL_STACK_SIZE:任务的最小栈大小,单位是字(Word,通常 4 字节)。这里 128 Words 就是 512 字节。创建任务时如果没指定栈大小,可能用这个最小值,需确保足够,否则栈溢出。
  • MAX_TASK_NAME_LEN:任务名称的最大长度,设为 16。任务名字符串长度超过会被截断,用于调试时识别任务。
  • USE_16_BIT_TICKS:使用 16 位的滴答计数器,Disabled 表示用 32 位。16 位的话最大滴答数是 65535,若 TICK_RATE_HZ 是 1000,最多计时 65 秒,32 位则更久,一般选 32 位,所以禁用 16 位。
  • IDLE_SHOULD_YIELD:空闲任务是否让出 CPU。Enabled 表示空闲任务在有同优先级任务时让出,让同优先级任务有机会运行,避免空闲任务占满时间。
  • USE_MUTEXES:启用互斥锁,Enabled。互斥锁用于资源互斥访问,比二进制信号量更适合任务间同步,支持优先级继承。
  • USE_RECURSIVE_MUTEXES:启用递归互斥锁,Disabled。递归互斥锁允许同一任务多次获取,适合递归函数中使用,这里没启用。
  • USE_COUNTING_SEMAPHORES:启用计数信号量,Disabled。计数信号量用于管理多个相同资源,比如多个缓冲区,这里没开。
  • QUEUE_SIZE:队列的 默认最大长度(即队列可存储的最大项数)。
  • USE_APPLICATION_TASK_TAG:启用应用任务标签,Disabled。任务标签是给任务附加一个数值,用于调试或标识,这里没开。
  • ENABLE_BACKWARD_COMPATIBILITY:启用向后兼容,Enabled。为了兼容旧版 FreeRTOS 的 API,可能一些旧函数还能使用,新代码可考虑关闭,但这里开启。
  • USE_PORT_OPTIMISED_TASK_SELECTION:启用端口优化的任务选择,Enabled。某些硬件端口有优化的任务调度算法,比如用汇编实现更快的任务切换,提高效率。
  • USE_TICKLESS_IDLE:启用 Tickless 空闲模式,Disabled。Tickless 模式在空闲时关闭滴答中断,降低功耗,这里没启用,保持滴答中断一直运行。
  • USE_TASK_NOTIFICATIONS:启用任务通知,Enabled。任务通知是轻量级的同步机制,比队列更高效,用于任务间单向通知,这里开启。
  • RECORD_STACK_HIGH_ADDRESS:记录栈的高地址,Disabled。用于调试栈溢出,记录栈的最高地址,判断是否溢出,这里没开。
  1. 内存管理设置(Memory management settings)
  • Memory Allocation:支持动态 / 静态内存分配,动态分配(如heap_4)适合灵活内存管理,静态分配则用于资源固定场景。
  • TOTAL_HEAP_SIZE:3072 字节堆内存,需根据任务、队列等动态内存需求调整(如多任务系统可增大)。
  • Memory Management scheme:heap_4支持内存合并,减少碎片,适用于频繁内存操作(如任务创建 / 删除)。
  1. 钩子函数(Hook functions)
  • USE_IDLE_HOOK:Disabled(禁用空闲任务钩子函数,无需在空闲任务中执行自定义逻辑(如低功耗处理)时关闭,节省资源)。
  • USE_TICK_HOOK:Disabled(禁用滴答中断钩子函数,无需在每个滴答周期执行代码(如任务计时)时关闭)。
  • USE_MALLOC_FAILED_HOOK:Disabled(禁用内存分配失败钩子函数,若需处理pvPortMalloc失败(如记录日志、进入安全模式),可启用)。
  • USE_DAEMON_TASK_STARTUP_HOOK:Disabled(禁用守护任务(如软件定时器任务)启动钩子,无需初始化时执行自定义代码时关闭)。
  • CHECK_FOR_STACK_OVERFLOW:Disabled(禁用栈溢出检测,若需调试任务栈溢出(如vApplicationStackOverflowHook),可启用并实现钩子函数)。
  1. 运行时统计与跟踪
  • GENERATE_RUN_TIME_STATS:Disabled(禁用任务运行时间统计,无需分析任务执行时间占比时关闭,节省计算资源)。
  • USE_TRACE_FACILITY:Disabled(禁用跟踪工具(如 FreeRTOS+Trace),无需深度调试系统行为时关闭)。
  • USE_STATS_FORMATTING_FUNCTIONS:Disabled(禁用统计格式化函数(如vTaskList()输出任务列表),若需调试(如查看任务状态),可启用)。
  1. 协程(Co-routines)
  • USE_CO_ROUTINES:禁用,协程为轻量级任务(共享栈),若无需(多任务已满足),关闭以节省资源。
  • MAX_CO_ROUTINE_PRIORITIES:2(协程最大优先级数量,仅在USE_CO_ROUTINES启用时有效,协程优先级低于任务优先级,适合后台处理)。
  1. 软件定时器(Software Timers)
  • USE_TIMERS:禁用,无需定时任务时关闭,减少定时器任务的资源占用(栈、优先级等)。
  1. 中断嵌套配置
  • LIBRARY_LOWEST_INTERRUPT_PRIORITY (15):RTOS 库使用的最低中断优先级(Cortex-M 中优先级 0 最高,15 最低),确保关键中断(如滴答)优先级合理。
  • LIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY (5):允许调用 RTOS API 的最高中断优先级(≤5 时可安全使用,高于则需避免,防止嵌套冲突)。

核心任务控制API

4.1.2 核心任务控制 API

  • vTaskPrioritySet(Enabled):允许动态设置任务优先级,灵活调整任务执行顺序(如根据系统负载提升任务优先级)。
  • vTaskPriorityGet(Enabled):获取任务当前优先级,用于调试或任务状态监控(如判断任务是否按预期优先级运行)。
  • vTaskDelete(Enabled):删除任务,释放资源(需谨慎使用,避免资源泄漏,推荐在任务结束时自动删除或使用静态任务)。
  • vTaskSuspend(Enabled):挂起任务(暂停执行,可通过vTaskResume恢复),用于临时暂停非关键任务(如后台数据处理)。
  • vTaskDelay(Enabled):任务延迟(阻塞指定滴答周期),实现任务间时间同步(如周期性任务每 100ms 执行一次)。
  • xTaskGetSchedulerState(Enabled):获取调度器状态(运行、挂起、未启动),用于系统初始化或错误处理(如确保调度器已启动再创建任务)。
  • xTaskResumeFromISR(Enabled):从中断服务程序(ISR)中恢复任务,支持中断中唤醒高优先级任务(实时响应关键)。
  • vTaskCleanUpResources(Disabled):任务删除时清理资源(如队列、信号量),若系统中任务删除较少或已手动管理,可禁用以减少代码量。
  • vTaskDelayUntil(Disabled):绝对时间延迟(精确周期任务),若无需高精度周期(vTaskDelay已满足),可禁用(节省代码空间)。
  • 任务状态查询 API(如uxTaskGetStackHighWaterMark、xTaskGetCurrentTaskHandle等,Disabled):调试相关 API,生产环境禁用以减少开销;调试时启用,查看任务栈使用、当前任务句柄等,快速定位内存或任务问题。
  • 事件组与定时器 API(如xEventGroupSetBitFromISR、xTimerPendFunctionCall,Disabled):无需事件组或定时器功能时禁用,简化内核,减少资源占用。

在这里插入图片描述

4.1.3 库及工程设置

  1. Newlib settings 配置
  • USE_NEWLIB_REENTRANT (Disabled):控制newlib的重入性(Reentrant)。newlib是嵌入式系统常用的 C 标准库(如 ARM GCC 工具链默认使用),重入性指库函数在多线程(任务)中安全调用的能力。
  1. Project settings 配置
  • Use FW pack heap file (Enabled):启用固件包中的堆内存文件(Heap File)。在 FreeRTOS 中,堆内存用于动态分配(如任务创建、队列创建时的内存申请)。使用固件包提供的堆文件(通常为预配置的内存区域,如heap_4.c中的ucHeap数组),确保内存分配的一致性和安全性。

在这里插入图片描述

4.1.4 堆栈设置

  1. 内存使用(Summary)
  • HEAP STILL AVAILABLE:2448 字节(堆内存剩余,可用于动态资源创建,如任务、队列等)。
  • TOTAL HEAP USED:624 字节
    – Tasks:624 字节(用户任务栈,如defaultTask)。
    队列、定时器、信号量、事件:均为 0(未使用动态分配,或静态创建)。
  1. 任务状态(FreeRTOS tasks)
  • Idle task:0 字节(空闲任务栈,可能因静态配置或统计显示问题,实际由MINIMAL_STACK_SIZE定义)。
  • defaultTask:624 字节(用户任务栈,需检查栈使用是否合理,避免溢出)。

任务配置界面

4.1.5 任务及队列

  1. 任务配置(Tasks Section)
    任务列表中默认有一个defaultTask任务,如果想要创建其他任务,可通过Add按钮进行添加:
  • Task Name:defaultTask(用户定义的任务标识,代码生成后用于任务创建和调试)。
  • Priority:osPriorityNormal(任务优先级,基于 CMSIS-RTOS 标准,决定任务调度顺序,可调整为osPriorityHigh、osPriorityLow等)。
  • Stack Size:128(任务栈大小,以字为单位,32 位系统中 1 字 = 4 字节,故为512字节。需根据任务需求(如函数嵌套深度、局部变量大小)调整,避免栈溢出)。
  • Entry Func:StartDefaultTask(任务入口函数,代码生成工具自动生成框架,开发者在此编写任务逻辑,如循环执行、外设操作等)。
  • Allocation:Dynamic(任务栈从 FreeRTOS 堆中动态分配,灵活且无需手动管理内存;若选Static,需指定静态内存块,适合资源固定场景)。
  1. 队列配置(Queues Section)
    可通过Add按钮添加队列:
  • Queue Name:队列标识,用于任务间数据通信(如SensorDataQueue)。
  • Queue Size:队列可存储的元素数量(如10个传感器数据)。
  • Item Size:每个队列元素的大小(如sizeof(int)表示存储整数)。
  • Allocation:动态 / 静态分配队列内存,与任务栈类似,确保内存管理统一。

4.2 接口说明(任务、延时、定时器)

    因为FreeRTOS内容比较多,这里把接口说明分成三篇分别讲述及演示,以方便更好的理解及学习。使用CubeMX生成的工程,会将FreeRTOS的接口再封装一层统一接口CMSIS-RTOS,这是 ARM 定义的一套 RTOS 抽象层标准,旨在通过统一接口屏蔽不同 RTOS 的差异。这里我们先来认识几个比较常用的接口。

4.2.1 线程

1. 线程定义宏 (osThreadDef 和 osThreadStaticDef)

#define osThreadDef(name, thread, priority, instances, stacksz) ...
#define osThreadStaticDef(name, thread, priority, instances, stacksz, buffer, control) ...

    这两个宏用于定义线程属性。osThreadDef 创建一个动态分配的线程定义,而 osThreadStaticDef 在支持静态内存分配时使用,允许用户指定预分配的栈空间和控制块。
参数:
name:线程名称(字符串化)
thread:线程函数指针
priority:线程优先级
instances:可创建的线程实例数量
stacksz:线程栈大小(字节)
buffer:静态栈缓冲区指针(仅 osThreadStaticDef 使用)
control:静态控制块指针(仅 osThreadStaticDef 使用)

2. 线程访问宏 (osThread)

#define osThread(name)  &os_thread_def_##name

    这个宏用于获取线程定义的指针,返回 osThreadDef_t 类型的结构体指针,后续可用于创建线程。

3. 创建线程 (osThreadCreate)

osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument);

    创建一个线程并将其添加到就绪队列。参数 thread_def 是通过 osThread 宏获取的线程定义指针,argument 是传递给线程函数的参数。成功时返回线程 ID(非 NULL),失败时返回 NULL。

4. 获取当前线程 ID (osThreadGetId)

osThreadId osThreadGetId (void);

    返回当前正在执行的线程的 ID。这在线程需要引用自身时非常有用,例如设置自身优先级或终止自身。

5. 终止线程 (osThreadTerminate)

osStatus osThreadTerminate (osThreadId thread_id);

    终止指定线程的执行并将其从活动线程列表中移除。参数 thread_id 可以通过 osThreadCreate 或 osThreadGetId 获取。返回 osStatus 类型的状态码,表示操作成功或失败。

6. 线程让步 (osThreadYield)

osStatus osThreadYield (void);

    主动让出 CPU 控制权,使调度器选择下一个就绪状态的线程执行。这用于实现协作式调度,让高优先级线程有机会执行。返回状态码表示操作结果。

7. 设置线程优先级 (osThreadSetPriority)

osStatus osThreadSetPriority (osThreadId thread_id, osPriority priority);

    修改指定线程的优先级。参数 priority 是新的优先级值,必须是 osPriority 枚举类型的有效值。返回状态码表示操作是否成功。

8. 获取线程优先级 (osThreadGetPriority)

osPriority osThreadGetPriority (osThreadId thread_id);

    返回指定线程的当前优先级。这在线程需要动态调整优先级或查询其他线程优先级时很有用。

4.2.2 延时

1. osDelay() - 线程延时函数

osStatus osDelay (uint32_t millisec);

    使当前执行的线程暂停(阻塞)指定的毫秒数,然后再恢复执行。这是实现周期性任务或简单延时的常用方法。
参数:
millisec:延时时间(毫秒)。传入 0 会触发一次任务调度,但当前线程不会被阻塞。
返回值:
osOK:延时完成,线程正常恢复。
其他错误码(如osErrorParameter):参数错误或系统异常。

注:如写的是osDelay(1),在不考虑系统切换消耗及其他阻塞的情况下,实际延时时长为0~1ms。

2. osWait() - 通用等待函数(需功能支持)

osEvent osWait (uint32_t millisec);

    等待特定事件(如信号量、消息或邮箱)发生,或超时到期。这是一个多用途的同步函数,允许线程在等待资源时阻塞。
参数:
millisec:超时时间(毫秒),0则不等待,立即返回(非阻塞模式)。
osWaitForever:永久等待,直到事件发生。
其他值:最多等待指定毫秒数。
返回值:
osEvent 结构体,包含事件类型和数据:
status:事件状态码,如:
-osEventSignal:收到信号量。
-osEventMessage:收到消息。
-osEventMail:收到邮件。
-osEventTimeout:超时。
-osErrorResource:资源不可用。

value:联合类型,存储具体事件数据(如信号量值、消息指针等)。

函数主要用途阻塞机制返回值类型
osDelay()简单延时,周期性任务固定时间后自动唤醒osStatus
osWait()等待资源或事件,同步操作事件发生或超时后唤醒osEvent

4.2.3 定时器

1. 定时器定义宏 (osTimerDef 和 osTimerStaticDef)

#define osTimerDef(name, function) ...
#define osTimerStaticDef(name, function, control) ...

    这两个宏用于定义定时器属性。osTimerDef 创建一个动态分配的定时器定义,而 osTimerStaticDef 在支持静态内存分配时使用,允许用户指定预分配的控制块。
参数:
name:定时器名称(用于生成唯一标识符)
function:定时器回调函数指针(当定时器到期时调用)
control:静态控制块指针(仅 osTimerStaticDef 使用)

2. 定时器访问宏 (osTimer)

#define osTimer(name) &os_timer_def_##name

    这个宏用于获取定时器定义的指针,返回 osTimerDef_t 类型的结构体指针,后续可用于创建定时器。

3. 创建定时器 (osTimerCreate)

osTimerId osTimerCreate (const osTimerDef_t *timer_def, os_timer_type type, void *argument);

    创建一个定时器对象并返回其 ID。
参数:
timer_def:通过 osTimer 宏获取的定时器定义指针
type:定时器类型,可选值:
-osTimerOnce:单次触发定时器(到期后自动停止)
-osTimerPeriodic:周期性定时器(到期后自动重启)
argument:传递给定时器回调函数的参数

成功时返回定时器 ID(非 NULL),失败时返回 NULL。

4. 启动 / 重启定时器 (osTimerStart)

osStatus osTimerStart (osTimerId timer_id, uint32_t millisec);

    启动或重新启动一个定时器。如果定时器已在运行,会重置其计数并重新开始。
参数:
timer_id:通过 osTimerCreate 获取的定时器 ID
millisec:定时器超时时间(毫秒)
返回 osStatus 类型的状态码,表示操作成功或失败。

5. 停止定时器 (osTimerStop)

osStatus osTimerStop (osTimerId timer_id);

    停止一个正在运行的定时器。定时器停止后,可以通过 osTimerStart 重新启动。参数 timer_id 是要停止的定时器 ID。返回状态码表示操作结果。

6. 删除定时器 (osTimerDelete)

osStatus osTimerDelete (osTimerId timer_id);

    删除一个已创建的定时器,释放相关资源。删除后,定时器 ID 不再有效。参数 timer_id 是要删除的定时器 ID。返回状态码表示操作是否成功。

7. 定时器回调函数

定时器回调函数的原型通常为:

void timerCallback(void const *argument);

argument:即 osTimerCreate 时传入的 argument 参数
回调函数会在定时器到期时被调用

注:回调函数通常在中断上下文执行,应避免长时间操作或阻塞操作

4.3 应用配置

    因为后面要拿两个灯来演示,所以这里除了打开FreeRTOS的功能外,还需要配置PA3、PA4为输出口,来控制灯闪,还有本文会用到定时器,所以还需要在FreeRTOS配置中开启定时器的功能。
在这里插入图片描述

4.4 代码实现

    在defaultTask任务中分别使用延时跟定时器来实现闪灯逻辑,这里用的是我自己画的板子,原理图如下,如果是使用其他板子,请自行调整对应的GPIO口。这时用LED_ACT来演示延时的用法,用LED_READY来演示定时器的用法。
在这里插入图片描述
在这里插入图片描述

/* USER CODE BEGIN 4 */
/* 定义定时器名称 */
#define BLINK_LED_TIMER

/* 定义定时器回调函数 */
void BlinkLedCallback(void const *arg)
{
    static GPIO_PinState ledState = GPIO_PIN_RESET;
    ledState = (GPIO_PinState)!ledState;
    HAL_GPIO_WritePin(LED_READY_GPIO_Port, LED_READY_Pin, ledState);
}

osTimerDef(BLINK_LED_TIMER, BlinkLedCallback);

/* USER CODE END 4 */

/* 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 5 */
  osTimerId timerId;

  /* 创建循环执行定时器 */
  timerId = osTimerCreate(osTimer(BLINK_LED_TIMER), osTimerPeriodic, NULL);
  if (timerId != NULL)
  {
      /* 启动定时器,每200ms触发一次 */
      osTimerStart(timerId, 200);
  }

  /* Infinite loop */
  for(;;)
  {
    HAL_GPIO_WritePin(LED_ACT_GPIO_Port, LED_ACT_Pin, GPIO_PIN_SET);
    osDelay(500);
    HAL_GPIO_WritePin(LED_ACT_GPIO_Port, LED_ACT_Pin, GPIO_PIN_RESET);
    osDelay(500);
  }
  /* USER CODE END 5 */
}

4.5 效果演示

在这里插入图片描述

五、注意事项

1、使用操作系统要注意共享资源的使用,如果存在两个任务同时读写同一个共用内存时,要留意是否存在重入问题,如果需要保护该临界资源,可以通过任务锁的方式进行保护,这个在下一篇再单独说明。
2、osDelay是一个阻塞接口,在同一个任务过度地使用,会导致该任务里的其他执行语句被阻塞,从而影响执行效率。

六、相关链接

对于刚入门的小伙伴可以先看下STM32CubeMX的基础使用及Keil的基础使用。
【工具使用】STM32CubeMX-基础使用篇
【工具使用】Keil5软件使用-基础使用篇

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知识噬元兽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值