欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 旅游 > STM32 HAL库 FreeRTOS 软件定时器的使用

STM32 HAL库 FreeRTOS 软件定时器的使用

2025/4/22 16:12:19 来源:https://blog.csdn.net/m0_75187370/article/details/147380364  浏览:    关键词:STM32 HAL库 FreeRTOS 软件定时器的使用

一、引言

在嵌入式系统开发中,STM32 微控制器以其高性能、低功耗和丰富的外设资源受到广泛应用。而 FreeRTOS 作为一个开源的实时操作系统,为开发者提供了任务管理、内存管理、中断管理等强大的功能,使得复杂的嵌入式系统开发变得更加高效和可靠。软件定时器是 FreeRTOS 提供的一个重要特性,它允许开发者在不使用硬件定时器的情况下实现定时功能,这在资源有限的系统中尤为有用。本文将详细介绍基于 STM32 HAL 库和 FreeRTOS 的软件定时器的使用方法。

二、STM32 HAL 库与 FreeRTOS 简介

2.1 STM32 HAL 库

STM32 HAL(Hardware Abstraction Layer)库是 ST 公司为 STM32 微控制器提供的一套硬件抽象层库。它的主要目的是简化开发者对 STM32 硬件的操作,通过提供统一的 API 接口,使得开发者可以在不同型号的 STM32 微控制器上快速移植代码。HAL 库封装了底层的寄存器操作,提供了高级的函数调用,如 GPIO 操作、定时器配置、串口通信等,大大提高了开发效率。

2.2 FreeRTOS

FreeRTOS 是一个开源的、轻量级的实时操作系统内核,适用于各种嵌入式系统。它提供了任务管理、队列、信号量、互斥量等内核服务,支持多任务并发执行。FreeRTOS 的特点包括可裁剪性强、占用资源少、实时性高、易于移植等。在嵌入式系统中,使用 FreeRTOS 可以更好地管理系统资源,提高系统的稳定性和可靠性。

三、FreeRTOS 软件定时器概述

3.1 软件定时器的概念

软件定时器是 FreeRTOS 提供的一种机制,它允许开发者在不使用硬件定时器的情况下实现定时功能。软件定时器基于 FreeRTOS 的系统时钟节拍(tick)运行,通过设置定时器的周期和回调函数,当定时器到期时,会自动调用回调函数执行相应的操作。软件定时器可以是一次性的(只执行一次回调函数),也可以是周期性的(每隔一定时间执行一次回调函数)。

3.2 软件定时器的优点

  • 节省硬件资源:在一些资源有限的系统中,硬件定时器的数量可能不足,使用软件定时器可以在不占用硬件定时器的情况下实现定时功能。
  • 灵活性高:软件定时器的周期和回调函数可以在运行时动态调整,开发者可以根据实际需求灵活配置定时器。
  • 易于实现:软件定时器的实现相对简单,开发者只需要创建定时器、设置周期和回调函数,然后启动定时器即可。

3.3 软件定时器的局限性

  • 精度相对较低:由于软件定时器基于 FreeRTOS 的系统时钟节拍运行,其定时精度受到系统时钟节拍的影响。如果系统时钟节拍设置得较大,定时器的精度会相应降低。
  • 受任务调度影响:软件定时器的回调函数是在任务上下文中执行的,因此其执行时间会受到任务调度的影响。如果系统中存在高优先级任务长时间占用 CPU,定时器的回调函数可能会延迟执行。

四、开发环境搭建

4.1 硬件平台

本文以 STM32F4 Discovery 开发板为例进行介绍,该开发板采用了 STM32F407VG 微控制器,具有丰富的外设资源,适合进行嵌入式系统开发。

4.2 软件开发工具

  • Keil MDK:Keil MDK 是一款专业的 ARM 微控制器开发工具,支持 STM32 系列微控制器的开发。它提供了集成开发环境(IDE)、编译器、调试器等功能,方便开发者进行代码编写、编译和调试。
  • STM32CubeMX:STM32CubeMX 是 ST 公司提供的一款图形化配置工具,它可以帮助开发者快速配置 STM32 微控制器的外设和时钟,生成初始化代码。使用 STM32CubeMX 可以大大减少手动配置代码的工作量,提高开发效率。

4.3 安装 FreeRTOS

在 STM32CubeMX 中配置工程时,可以选择添加 FreeRTOS 操作系统。具体步骤如下:

  1. 打开 STM32CubeMX,选择相应的芯片型号。
  2. 在 “Pinout & Configuration” 选项卡中,配置系统时钟和外设。
  3. 点击 “Middleware” 选项卡,选择 “FreeRTOS”,并根据需要配置 FreeRTOS 的参数,如任务数量、堆大小等。
  4. 点击 “Project Manager” 选项卡,配置工程的输出路径和工具链,然后生成代码。

五、FreeRTOS 软件定时器的使用步骤

5.1 创建软件定时器

在 FreeRTOS 中,使用 xTimerCreate 函数来创建软件定时器。该函数的原型如下:

TimerHandle_t xTimerCreate(const char * const pcTimerName,TickType_t xTimerPeriod,UBaseType_t uxAutoReload,void * pvTimerID,TimerCallbackFunction_t pxCallbackFunction
);
  • pcTimerName:定时器的名称,用于调试和日志记录,可为 NULL
  • xTimerPeriod:定时器的周期,以系统时钟节拍为单位。
  • uxAutoReload:定时器的模式,pdTRUE 表示周期性定时器,pdFALSE 表示一次性定时器。
  • pvTimerID:定时器的标识符,可用于在回调函数中区分不同的定时器。
  • pxCallbackFunction:定时器到期时调用的回调函数。

示例代码如下:

#include "FreeRTOS.h"
#include "timers.h"// 定时器回调函数
void vTimerCallback(TimerHandle_t xTimer) {// 定时器到期时执行的操作
}// 创建定时器
TimerHandle_t xTimer = xTimerCreate("MyTimer",pdMS_TO_TICKS(1000),  // 定时器周期为 1 秒pdTRUE,               // 周期性定时器(void *)0,            // 定时器标识符vTimerCallback        // 定时器回调函数
);

5.2 启动软件定时器

创建定时器后,需要使用 xTimerStart 函数来启动定时器。该函数的原型如下:

BaseType_t xTimerStart(TimerHandle_t xTimer,TickType_t xTicksToWait
);
  • xTimer:要启动的定时器句柄。
  • xTicksToWait:如果定时器服务任务处于阻塞状态,等待定时器服务任务解锁的最大时间,以系统时钟节拍为单位。如果设置为 0,表示不等待。

示例代码如下:

if (xTimer != NULL) {// 启动定时器if (xTimerStart(xTimer, 0) != pdPASS) {// 启动定时器失败}
}

5.3 停止软件定时器

如果需要停止定时器的运行,可以使用 xTimerStop 函数。该函数的原型如下:

BaseType_t xTimerStop(TimerHandle_t xTimer,TickType_t xTicksToWait
);

参数含义与 xTimerStart 函数相同。

示例代码如下:

if (xTimer != NULL) {// 停止定时器if (xTimerStop(xTimer, 0) != pdPASS) {// 停止定时器失败}
}

5.4 删除软件定时器

当不再需要某个定时器时,可以使用 xTimerDelete 函数来删除定时器。该函数的原型如下:

BaseType_t xTimerDelete(TimerHandle_t xTimer,TickType_t xTicksToWait
);

参数含义与 xTimerStart 函数相同。

示例代码如下:

if (xTimer != NULL) {// 删除定时器if (xTimerDelete(xTimer, 0) != pdPASS) {// 删除定时器失败}
}

5.5 改变软件定时器的周期

在定时器运行过程中,可以使用 xTimerChangePeriod 函数来改变定时器的周期。该函数的原型如下:

BaseType_t xTimerChangePeriod(TimerHandle_t xTimer,TickType_t xNewPeriod,TickType_t xTicksToWait
);
  • xNewPeriod:新的定时器周期,以系统时钟节拍为单位。

示例代码如下:

if (xTimer != NULL) {// 改变定时器周期为 2 秒if (xTimerChangePeriod(xTimer, pdMS_TO_TICKS(2000), 0) != pdPASS) {// 改变定时器周期失败}
}

六、基于 STM32 HAL 库和 FreeRTOS 软件定时器的示例代码

6.1 代码功能概述

本示例代码将创建一个周期性的软件定时器,每隔 1 秒翻转一次 LED 灯的状态。

6.2 代码实现

#include "stm32f4xx_hal.h"
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"// 定义 LED 引脚
#define LED_PIN GPIO_PIN_12
#define LED_PORT GPIOD// 定时器回调函数
void vTimerCallback(TimerHandle_t xTimer) {HAL_GPIO_TogglePin(LED_PORT, LED_PIN);
}// 系统时钟配置函数
void SystemClock_Config(void);
// GPIO 初始化函数
static void MX_GPIO_Init(void);
// FreeRTOS 初始化函数
static void MX_FREERTOS_Init(void);int main(void) {HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_FREERTOS_Init();// 启动调度器vTaskStartScheduler();// 如果调度器启动失败,程序将执行到这里while (1) {}
}void SystemClock_Config(void) {RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** 初始化 CPU, AHB 和 APB 总线时钟*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {Error_Handler();}/** 初始化 CPU, AHB 和 APB 总线时钟*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) {Error_Handler();}
}static void MX_GPIO_Init(void) {GPIO_InitTypeDef GPIO_InitStruct = {0};/* GPIO Ports Clock Enable */__HAL_RCC_GPIOD_CLK_ENABLE();/*Configure GPIO pin Output Level */HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_RESET);/*Configure GPIO pin : LED_PIN */GPIO_InitStruct.Pin = LED_PIN;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct);
}static void MX_FREERTOS_Init(void) {// 创建一个周期性定时器,周期为 1000 个 tickTimerHandle_t xTimer = xTimerCreate("MyTimer",pdMS_TO_TICKS(1000),  // 定时器周期为 1 秒pdTRUE,               // 周期性定时器(void *)0,            // 定时器标识符vTimerCallback        // 定时器回调函数);if (xTimer != NULL) {// 启动定时器if (xTimerStart(xTimer, 0) != pdPASS) {// 启动定时器失败}}
}void Error_Handler(void) {// 错误处理函数while(1) {}
}

6.3 代码解释

  1. 定时器回调函数vTimerCallback 函数在定时器到期时被调用,在该函数中,使用 HAL_GPIO_TogglePin 函数翻转 LED 灯的状态。
  2. 系统时钟配置SystemClock_Config 函数用于配置系统时钟,这里使用内部高速时钟(HSI)作为系统时钟源。
  3. GPIO 初始化MX_GPIO_Init 函数用于初始化 LED 引脚,将其配置为推挽输出模式。
  4. FreeRTOS 初始化MX_FREERTOS_Init 函数用于创建和启动软件定时器。

七、注意事项

7.1 定时器回调函数的执行时间

定时器回调函数是在任务上下文中执行的,因此其执行时间应尽量短,避免长时间占用 CPU,影响其他任务的执行。如果回调函数需要执行较长时间的操作,建议将操作封装成一个任务,在回调函数中发送消息或信号量来触发该任务的执行。

7.2 定时器服务任务的优先级

FreeRTOS 有一个专门的定时器服务任务,用于处理定时器的到期事件。定时器服务任务的优先级应根据实际需求进行设置,一般建议设置为较高的优先级,以确保定时器事件能够及时处理。

7.3 系统时钟节拍的设置

系统时钟节拍的设置会影响定时器的精度。如果需要较高的定时精度,应将系统时钟节拍设置得较小,但同时会增加 CPU 的开销。因此,需要根据实际需求进行权衡。

八、总结

本文详细介绍了基于 STM32 HAL 库和 FreeRTOS 的软件定时器的使用方法。通过使用 FreeRTOS 软件定时器,开发者可以在不使用硬件定时器的情况下实现定时功能,节省硬件资源,提高系统的灵活性。同时,本文还给出了一个基于 STM32F4 Discovery 开发板的示例代码,帮助开发者更好地理解和应用软件定时器。在实际开发中,开发者应根据具体需求合理使用软件定时器,并注意定时器回调函数的执行时间、定时器服务任务的优先级和系统时钟节拍的设置等问题。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词