STM32入门笔记(04):STM32F103C8T6 移植 FreeRTOS 模板 (动态、静态创建任务模板及点灯操作,按键非阻塞,事件任务间通信,消息队列,信号量)
fill:#333;color:#333;color:#333;fill:none;项目根目录UserSystemHardwareFreeRTOSmain.cDelay.cDelay.hLED.c/hKey.c/hOLED.c/h任务相关文件内核相关文件。
·
STM32F103C8T6 FreeRTOS静态任务LED和按键控制项目
项目结构图
整体功能流程图
各模块流程图
main.c流程图
Delay.c流程图
Key.c按键状态机流程图
MyFreeRTOS.c流程图
关键功能说明
1. 任务优先级
- 启动任务: 1级(最低)
- 任务1(LED1): 2级
- 任务2(LED2): 3级
- 任务3(按键): 4级(最高)
2. 任务功能
- 任务1:响应按键事件控制LED1
- 短按:翻转LED1状态
- 长按:LED1常亮
- 任务2:响应按键事件控制LED2
- 短按:翻转LED2状态
- 长按:LED2常亮
- 任务3:
- 实现非阻塞按键检测
- 按键状态机处理
- 发送事件通知其他任务
3. 按键处理优化
3.1 非阻塞按键检测
- 采用状态机方式处理按键
- 无延时等待,提高系统响应性
- 支持多种按键事件识别
3.2 按键状态机
- 状态定义:
- RELEASE:按键释放状态
- PRESS:按键按下状态
- LONG:长按状态
- 事件触发:
- 短按:按下后在长按阈值时间内释放
- 长按:按下时间超过阈值
- 抖动处理:
- 硬件去抖:使用电容
- 软件去抖:状态机延时判断
3.3 事件组(EventGroup)通知机制
- 事件位定义:
- BIT_0:短按事件
- BIT_1:长按事件
- BIT_2:LED1状态改变
- BIT_3:LED2状态改变
- 任务同步:
- 按键任务发送事件
- LED任务等待并响应事件
- 优势:
- 无阻塞等待
- 支持多事件触发
- 任务间解耦
4. 静态任务创建
- 使用静态方式创建任务,更适合资源受限的嵌入式系统
- 每个任务都有固定的堆栈大小(128字)
- 使用StaticTask_t结构体存储任务控制块
5. 通信显示
- 使用OLED显示器显示各任务运行状态
- Task1Run: 表示任务1正在运行
- Task2Run: 表示任务2正在运行
- KeyEvent: 显示当前按键事件类型
6. 延时实现
- 基于SysTick定时器实现精确延时
- 支持微秒级(us)、毫秒级(ms)和秒级(s)延时
- 使用72MHz系统时钟
7. 开发工具
- 编译器:ARMCC V5.06
- 调试器:ST-LINK
- 操作系统:FreeRTOS V10.x
- 开发板:STM32F103C8T6最小系统板
让我分析一下代码中可能存在的漏洞:
-
LED驱动模块(Hardware/LED.c)中的潜在问题:
- GPIO初始化时没有对时钟使能的返回状态进行检查
- GPIO操作没有临界区保护,在多任务环境下可能导致竞态条件
- LED状态翻转时的读-改-写操作不是原子的,可能导致状态不一致
-
OLED驱动模块(Hardware/OLED.c)中的风险:
- I2C通信没有超时机制,如果硬件出现问题可能导致系统卡死
- 没有对OLED是否正常初始化的状态检查
- 显示函数中的参数边界检查不完整:
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char) { uint8_t i; OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8); // 没有检查Line和Column的有效范围 // 可能导致内存访问越界
-
任务管理相关的隐患:
- 静态任务创建时没有对栈空间使用情况的监控
- 没有对任务切换时的上下文完整性进行验证
- 缺少任务运行超时的保护机制
-
硬件资源管理方面:
- I2C总线访问没有互斥保护
- GPIO资源没有集中管理,可能存在引脚复用冲突
- 外设时钟配置没有验证机制
-
内存安全方面:
- OLED显示字符串时没有长度限制:
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String) { uint8_t i; for (i = 0; String[i] != '\0'; i++) { OLED_ShowChar(Line, Column + i, String[i]); } // 如果String没有结束符,可能会导致缓冲区溢出
- OLED显示字符串时没有长度限制:
-
中断处理相关:
- 缺少中断优先级管理
- 中断处理函数中可能存在重入问题
- 没有对中断延迟的监控机制
-
时序相关的问题:
- OLED的I2C通信时序没有严格的时间控制
- 按键消抖可能不够完善
- 任务延时精度没有补偿机制
-
配置参数相关:
- 硬件配置参数都是硬编码的,缺乏验证
- 没有对关键参数的合法性检查
- 配置参数可能存在冲突的情况
-
异常处理方面:
- 大部分函数没有错误返回值
- 没有系统级的异常处理机制
- 硬件故障时缺乏保护措施
-
电源管理相关:
- 没有对系统电源状态的监控
- 缺少低功耗模式的保护
- 电源不稳定时可能影响I2C通信
这些潜在的漏洞主要涉及以下几个方面:
- 并发安全性
- 内存安全性
- 硬件通信可靠性
- 异常处理完整性
- 资源管理有效性
需要注意的是,这些问题在一般使用场景下可能不会造成明显影响,但在特定条件下(如高负载、资源受限、硬件异常等情况)可能会导致系统不稳定或者功能异常。
FreeRTOSConfig.h
/*
* FreeRTOS V202212.01
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
/*-----------------------------------------------------------
* Application specific definitions.
*
* These definitions should be adjusted for your particular hardware and
* application requirements.
*
* THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
* FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
*
* See http://www.freertos.org/a00110.html
*----------------------------------------------------------*/
#define configUSE_PREEMPTION 1
/***************************************************************
FreeRTOS与钩子函数有关的配置选项
**************************************************************/
/* 置1:使用空闲钩子(Idle Hook类似于回调函数);置0:忽略空闲钩子
*
* 空闲任务钩子是一个函数,这个函数由用户来实现,
* FreeRTOS规定了函数的名字和参数:void vApplicationIdleHook(void ),
* 这个函数在每个空闲任务周期都会被调用
* 对于已经删除的RTOS任务,空闲任务可以释放分配给它们的堆栈内存。
* 因此必须保证空闲任务可以被CPU执行
* 使用空闲钩子函数设置CPU进入省电模式是很常见的
* 不可以调用会引起空闲任务阻塞的API函数
*/
#define configUSE_IDLE_HOOK 0 //空闲时 钩子函数 回调函数
/* 置1:使用时间片钩子(Tick Hook);置0:忽略时间片钩子
*
*
* 时间片钩子是一个函数,这个函数由用户来实现,
* FreeRTOS规定了函数的名字和参数:void vApplicationTickHook(void )
* 时间片中断可以周期性的调用
* 函数必须非常短小,不能大量使用堆栈,
* 不能调用以”FromISR" 或 "FROM_ISR”结尾的API函数
*/
/*xTaskIncrementTick函数是在xPortSysTickHandler中断函数中被调用的。因此,vApplicationTickHook()函数执行的时间必须很短才行*/
#define configUSE_TICK_HOOK 0
//使用内存申请失败钩子函数
#define configUSE_MALLOC_FAILED_HOOK 0
#define configCPU_CLOCK_HZ ( ( unsigned long ) 72000000 )
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
#define configMAX_PRIORITIES ( 32 ) //可使用的最大优先级
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 ) //是128字,并非字节 即128*4个字节,堆栈大小以字为单位
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 16 * 1024 ) )
#define configMAX_TASK_NAME_LEN ( 16 )
#define configUSE_TRACE_FACILITY 0
#define configUSE_16_BIT_TICKS 0 //系统节拍计数器变量数据类型,1表示为16位无符号整形,0表示为32位无符号整形
#define configIDLE_SHOULD_YIELD 1 //1:空闲任务放弃CPU使用权,给其他同优先级的用户任务 2:空闲优先级和其他优先级相同。避免2,多使用1
#define configUSE_TIME_SLICING 1 //时间片调度,当优先级相同时执行
#define configUSE_QUEUE_SETS 0 //队列 为1开启,为0关闭
#define configUSE_TASK_NOTIFICATIONS 1 //开启任务通知功能,默认开启
#define configUSE_MUTEXES 0 //互斥信号量开关
#define configUSE_RECURSIVE_MUTEXES 0 //使用递归互斥信号量
#define configUSE_COUNTING_SEMAPHORES 0 //为1时使用计数信号量
/* 设置可以注册的信号量和消息队列个数 */
#define configQUEUE_REGISTRY_SIZE 10
#define configUSE_APPLICATION_TASK_TAG 0
/*****************************************************************
FreeRTOS与内存申请有关配置选项
*****************************************************************/
//支持动态内存申请
#define configSUPPORT_DYNAMIC_ALLOCATION 1
//支持静态内存
#define configSUPPORT_STATIC_ALLOCATION 1
/*
* 大于0时启用堆栈溢出检测功能,如果使用此功能
* 用户必须提供一个栈溢出钩子函数,如果使用的话
* 此值可以为1或者2,因为有两种栈溢出检测方法 */
#define configCHECK_FOR_STACK_OVERFLOW 0
/********************************************************************
FreeRTOS与运行时间和任务状态收集有关的配置选项
**********************************************************************/
//启用运行时间统计功能
#define configGENERATE_RUN_TIME_STATS 0
//启用可视化跟踪调试
#define configUSE_TRACE_FACILITY 0
/* 与宏configUSE_TRACE_FACILITY同时为1时会编译下面3个函数
* prvWriteNameToBuffer()
* vTaskList(),
* vTaskGetRunTimeStats()
*/
/********************************************************************
FreeRTOS与协程有关的配置选项
*********************************************************************/
//启用协程,启用协程以后必须添加文件croutine.c
#define configUSE_CO_ROUTINES 0
//协程的有效优先级数目
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
/***********************************************************************
FreeRTOS与软件定时器有关的配置选项
**********************************************************************/
//启用软件定时器
#define configUSE_TIMERS 1
//软件定时器优先级
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-1)
//软件定时器队列长度
#define configTIMER_QUEUE_LENGTH 10
//软件定时器任务堆栈大小
#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE*2)
/*
* 某些运行FreeRTOs的硬件有两种方法选择下一个要执行的任务:
*
* 通用方法和特定于硬件的方法(以下简称"特殊方法")。
* 通用方法:
* 1.configUSE_PORT_OPTIMISED_TASK_SELECTION为О或者硬件不支持这种特殊方法。
* 2.可以用于所有FreeRTOS支持的硬件
* 3.完全用c实现,效率略低于特殊方法。
* 4.不强制要求限制最大可用优先级数目
* 特殊方法:
* 1.必须将configUSE_PORT_OPTIMISED_TASK_SELECTION设置为1。
* 2.依赖一个或多个特定架构的汇编指令(一般是类似计算前导零[CLZ]指令)。
* 3.比通用方法更高效
* 4.一般强制限定最大可用优先级数目为32
* 一般是硬件计算前导零指令,如果所使用的,MCU没有这些硬件指令的话此宏应该设置为0 !
*/
#define configUSE_PORT_OPTIMISED_TASK SELECTION 1
/*
* configUSE_TICKLESS_IDLE
* 置1:使能低功耗tickless模式;置0:保持系统节拍(tick)中断一直运行
* 假设开启低功耗的话可能会导致下载出现问题,因为程序在睡眠中,可用以下办法解决
* 下载方法:
* 1.将开发版正常连接好
* 2.按住复位按键,点击下载瞬间松开复位按键
*
* 1.通过跳线帽将BO0T 0接高电平(3.3v)
* 2.重新上电,下载
* 1.使用FlyMcu擦除一下芯片,然后进行下载STMISP ->清除芯片(z)
*
*/
#define configUSE_TICKLESS_IDLE 0
/*配置必要的声明*/
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
#define INCLUDE_xTaskGetSchedulerState 1
/************************************************************
FreeRTOS可选函数配置选项
************************************************************/
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskCleanUpResources 1
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_eTaskGetState 1
#define INCLUDE_xTimerPendFunctionCall 0
//#define INCLUDE_xTaskGetCurrentTaskHandle 1
//#define INCLUDE_uxTaskGetStackHighWaterMark 0
//#define INCLUDE_xTaskGetIdleTaskHandle 0
/******************************************************************
FreeRTOS与中断有关的配置选项
******************************************************************/
#ifdef __NVIC_PRIO_BITS
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4
#endif
//中断最低优先级
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
//系统可管理的最高中断优先级,
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 //5指 中断优先级 0~5不被管控,中断5~15被freertos管控
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) /* 240 */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* This is the value being used as per the ST library which permits 16
priority values, 0 to 15. This must correspond to the
configKERNEL_INTERRUPT_PRIORITY setting. Here 15 corresponds to the lowest
NVIC value of 255. */
#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY 15
#endif /* FREERTOS_CONFIG_H */
MyFreeRTOS.c
#include "stm32f10x.h" // STM32F10x标准外设库头文件
#include "FreeRTOS.h" // FreeRTOS核心头文件
#include "task.h" // FreeRTOS任务管理头文件
#include "event_groups.h" // FreeRTOS事件组头文件
#include "MyFreeRTOS.h" // 自定义FreeRTOS配置头文件
#include "LED.h" // LED驱动头文件
#include "Key.h" // 按键驱动头文件
#include "OLED.h" // OLED显示器驱动头文件
/*=========================== 任务参数配置区 ===========================*/
/*启动任务配置*/
#define STACK_DEPTH_TYPE 128 // 任务堆栈大小(单位:字)
#define START_TASK_PRIORITY 1 // 启动任务优先级(最低)
TaskHandle_t START_TASK_HANDLER; // 启动任务句柄
StackType_t start_task_stack[STACK_DEPTH_TYPE]; // 启动任务堆栈(静态分配)
StaticTask_t start_task_tcb; // 启动任务控制块
void FreeRTOS_StartTask(void *pvParameters); // 启动任务函数声明
/*任务1配置(LED1控制)*/
#define TASK1_DEPTH_TYPE 128 // 任务1堆栈大小
#define TASK1_PRIORITY 2 // 任务1优先级
TaskHandle_t task1_handler; // 任务1句柄
StackType_t task1_stack[TASK1_DEPTH_TYPE]; // 任务1堆栈
StaticTask_t task1_tcb; // 任务1控制块
void FreeRTOS_Task1(void *pvParameters); // 任务1函数声明
/*任务2配置(LED2控制)*/
#define TASK2_DEPTH_TYPE 128 // 任务2堆栈大小
#define TASK2_PRIORITY 3 // 任务2优先级
TaskHandle_t task2_handler; // 任务2句柄
StackType_t task2_stack[TASK2_DEPTH_TYPE]; // 任务2堆栈
StaticTask_t task2_tcb; // 任务2控制块
void FreeRTOS_Task2(void *pvParameters); // 任务2函数声明
/*任务3配置(按键监控)*/
#define TASK3_DEPTH_TYPE 128 // 任务3堆栈大小
#define TASK3_PRIORITY 4 // 任务3优先级(最高)
TaskHandle_t task3_handler; // 任务3句柄
StackType_t task3_stack[TASK3_DEPTH_TYPE]; // 任务3堆栈
StaticTask_t task3_tcb; // 任务3控制块
void FreeRTOS_Task3(void *pvParameters); // 任务3函数声明
/*空闲任务配置*/
#define IDEL_DEPTH_TYPE 128 // 空闲任务堆栈大小
StackType_t idel_task_stack[IDEL_DEPTH_TYPE]; // 空闲任务堆栈
StaticTask_t idel_task_tcb; // 空闲任务控制块
/*定时器任务配置*/
#define Timer_DEPTH_TYPE 128 // 定时器任务堆栈大小
StackType_t Timer_task_stack[Timer_DEPTH_TYPE]; // 定时器任务堆栈
StaticTask_t Timer_task_tcb; // 定时器任务控制块
/*======================= FreeRTOS系统回调函数 =======================*/
/**
* @brief 获取空闲任务内存
* @note 由FreeRTOS内核调用,用于静态分配空闲任务所需内存
* @param ppxIdleTaskTCBBuffer: 任务控制块指针
* @param ppxIdleTaskStackBuffer: 任务堆栈指针
* @param pulIdleTaskStackSize: 堆栈大小指针
* @retval 无
*/
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize)
{
*ppxIdleTaskTCBBuffer = &idel_task_tcb; // 分配任务控制块内存
*ppxIdleTaskStackBuffer = idel_task_stack; // 分配任务堆栈内存
*pulIdleTaskStackSize = IDEL_DEPTH_TYPE; // 设置堆栈大小
}
/**
* @brief 获取定时器任务内存
* @note 由FreeRTOS内核调用,用于静态分配定时器任务所需内存
* @param ppxTimerTaskTCBBuffer: 任务控制块指针
* @param ppxTimerTaskStackBuffer: 任务堆栈指针
* @param pulTimerTaskStackSize: 堆栈大小指针
* @retval 无
*/
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer,
uint32_t *pulTimerTaskStackSize)
{
*ppxTimerTaskTCBBuffer = &Timer_task_tcb; // 分配任务控制块内存
*ppxTimerTaskStackBuffer = Timer_task_stack; // 分配任务堆栈内存
*pulTimerTaskStackSize = Timer_DEPTH_TYPE; // 设置堆栈大小
}
/*========================== 任务函数实现区 ==========================*/
/**
* @brief FreeRTOS启动函数
* @note 创建启动任务,启动调度器
* @param pvParameters: FreeRTOS任务参数(未使用)
* @retval 无
*/
void FreeRTOS_Start(void *pvParameters)
{
/* 1. 创建启动任务 */
START_TASK_HANDLER = xTaskCreateStatic(
(TaskFunction_t)FreeRTOS_StartTask, // 任务函数
(char *)"FreeRTOS_StartTask", // 任务名称
(uint32_t)STACK_DEPTH_TYPE, // 任务堆栈大小
(void *)NULL, // 任务参数
(UBaseType_t)START_TASK_PRIORITY, // 任务优先级
(StackType_t *)start_task_stack, // 任务堆栈
(StaticTask_t *)&start_task_tcb); // 任务控制块
/* 2. 启动调度器 */
vTaskStartScheduler();
}
/**
* @brief 启动任务函数
* @note 创建其他应用任务,完成后删除自身
* @param pvParameters: FreeRTOS任务参数(未使用)
* @retval 无
*/
void FreeRTOS_StartTask(void *pvParameters)
{
/* 进入临界区,防止任务创建过程被打断 */
taskENTER_CRITICAL();
/* 创建任务1:LED1控制任务 */
task1_handler = xTaskCreateStatic(
(TaskFunction_t)FreeRTOS_Task1, // 任务函数
(char *)"FreeRTOS_Task1", // 任务名称
(uint32_t)TASK1_DEPTH_TYPE, // 任务堆栈大小
(void *)NULL, // 任务参数
(UBaseType_t)TASK1_PRIORITY, // 任务优先级
(StackType_t *)task1_stack, // 任务堆栈
(StaticTask_t *)&task1_tcb); // 任务控制块
/* 创建任务2:LED2控制任务 */
task2_handler = xTaskCreateStatic(
(TaskFunction_t)FreeRTOS_Task2, // 任务函数
(char *)"FreeRTOS_Task2", // 任务名称
(uint32_t)TASK2_DEPTH_TYPE, // 任务堆栈大小
(void *)NULL, // 任务参数
(UBaseType_t)TASK2_PRIORITY, // 任务优先级
(StackType_t *)task2_stack, // 任务堆栈
(StaticTask_t *)&task2_tcb); // 任务控制块
/* 创建任务3:按键监控任务 */
task3_handler = xTaskCreateStatic(
(TaskFunction_t)FreeRTOS_Task3, // 任务函数
(char *)"FreeRTOS_Task3", // 任务名称
(uint32_t)TASK3_DEPTH_TYPE, // 任务堆栈大小
(void *)NULL, // 任务参数
(UBaseType_t)TASK3_PRIORITY, // 任务优先级
(StackType_t *)task3_stack, // 任务堆栈
(StaticTask_t *)&task3_tcb); // 任务控制块
/* 删除启动任务 */
vTaskDelete(NULL); // NULL表示删除自身任务
/* 退出临界区 */
taskEXIT_CRITICAL();
}
/**
* @brief 任务1:LED1控制任务
* @note 等待按键事件并控制LED1
* - 短按:翻转LED1状态
* - 长按:LED1常亮
* @param pvParameters: FreeRTOS任务参数(未使用)
* @retval 无
*/
void FreeRTOS_Task1(void *pvParameters)
{
EventBits_t uxBits;
const EventBits_t xBitsToWaitFor = KEY_EVENT_SHORT | KEY_EVENT_LONG;
while(1)
{
// 等待按键事件(短按或长按)
uxBits = xEventGroupWaitBits(
Key_GetEventGroup(), // 事件组句柄
xBitsToWaitFor, // 感兴趣的事件位
pdTRUE, // 收到事件后清除事件位
pdFALSE, // 任一事件位置位即可
portMAX_DELAY); // 永久等待
// 根据事件类型处理
if((uxBits & KEY_EVENT_SHORT) != 0)
{
// 短按:翻转LED1
LED1_Toggle();
OLED_ShowString(1,1,"LED1 Toggle");
}
else if((uxBits & KEY_EVENT_LONG) != 0)
{
// 长按:LED1常亮
LED1_ON();
OLED_ShowString(1,1,"LED1 ON ");
}
// 通知LED1状态已改变
xEventGroupSetBits(Key_GetEventGroup(), KEY_EVENT_LED1);
}
}
/**
* @brief 任务2:LED2控制任务
* @note 等待按键事件并控制LED2
* - 短按:翻转LED2状态
* - 长按:LED2常亮
* @param pvParameters: FreeRTOS任务参数(未使用)
* @retval 无
*/
void FreeRTOS_Task2(void *pvParameters)
{
EventBits_t uxBits;
const EventBits_t xBitsToWaitFor = KEY_EVENT_SHORT | KEY_EVENT_LONG;
while(1)
{
// 等待按键事件(短按或长按)
uxBits = xEventGroupWaitBits(
Key_GetEventGroup(), // 事件组句柄
xBitsToWaitFor, // 感兴趣的事件位
pdTRUE, // 收到事件后清除事件位
pdFALSE, // 任一事件位置位即可
portMAX_DELAY); // 永久等待
// 根据事件类型处理
if((uxBits & KEY_EVENT_SHORT) != 0)
{
// 短按:翻转LED2
LED2_Toggle();
OLED_ShowString(2,1,"LED2 Toggle");
}
else if((uxBits & KEY_EVENT_LONG) != 0)
{
// 长按:LED2常亮
LED2_ON();
OLED_ShowString(2,1,"LED2 ON ");
}
// 通知LED2状态已改变
xEventGroupSetBits(Key_GetEventGroup(), KEY_EVENT_LED2);
}
}
/**
* @brief 任务3:按键监控任务
* @note 处理按键状态机并发送事件到事件组
* @param pvParameters: FreeRTOS任务参数(未使用)
* @retval 无
*/
void FreeRTOS_Task3(void *pvParameters)
{
uint8_t event;
char* eventStr;
while(1)
{
// 运行按键状态机,获取事件
event = Key_StateMachine();
// 根据事件类型更新显示
switch(event)
{
case KEY_EVENT_SHORT:
eventStr = "Short Press";
break;
case KEY_EVENT_LONG:
eventStr = "Long Press ";
break;
default:
eventStr = "No Event ";
break;
}
// 在OLED上显示当前事件
OLED_ShowString(3,1,eventStr);
// 短暂延时,让出CPU使用权
vTaskDelay(pdMS_TO_TICKS(10));
}
}
这是你 FreeRTOS 项目的 FreeRTOSConfig.h 配置文件,它定义了 FreeRTOS 的各项行为方式。下面是完整的配置项总结,并附带对其结果和系统影响的分析。
🧠 一、内核行为类配置
| 配置项 | 值 | 含义 | 最终结果 & 系统影响 |
|---|---|---|---|
configUSE_PREEMPTION |
1 | 启用抢占式调度 | 任务能被更高优先级任务打断执行,响应性好 |
configUSE_TIME_SLICING |
1 | 启用时间片轮转调度 | 同优先级任务轮流使用CPU,公平性提升 |
configIDLE_SHOULD_YIELD |
1 | 空闲任务是否放弃CPU | 空闲任务会主动让出CPU,避免饥饿现象 |
configUSE_PORT_OPTIMISED_TASK_SELECTION |
1 | 启用硬件优化任务选择 | 使用硬件指令(如CLZ)加快任务调度 |
configUSE_TICKLESS_IDLE |
0 | 是否使用Tickless低功耗模式 | 不启用低功耗模式,节能性降低,但调试方便 |
🧩 二、钩子函数配置
| 配置项 | 值 | 含义 | 最终结果 & 系统影响 |
|---|---|---|---|
configUSE_IDLE_HOOK |
0 | 是否启用Idle钩子函数 | 空闲时不会调用自定义函数(如省电代码) |
configUSE_TICK_HOOK |
0 | 是否启用Tick钩子函数 | 每次系统节拍中断不会触发自定义函数 |
configUSE_MALLOC_FAILED_HOOK |
0 | 启用内存分配失败钩子 | 内存耗尽时无法立即获知或处理 |
📶 三、节拍 & 系统时钟配置
| 配置项 | 值 | 含义 | 最终结果 & 系统影响 |
|---|---|---|---|
configCPU_CLOCK_HZ |
72 MHz | CPU时钟频率 | 基于STM32F103常见配置,系统时钟设定 |
configTICK_RATE_HZ |
1000 | 每秒节拍数 | 1ms一次系统tick,任务调度精度为1ms |
configUSE_16_BIT_TICKS |
0 | Tick使用32位 | Tick溢出周期变长,稳定性更好 |
🗃️ 四、任务与调度器配置
| 配置项 | 值 | 含义 | 最终结果 & 系统影响 |
|---|---|---|---|
configMAX_PRIORITIES |
32 | 最大任务优先级数 | 允许较多任务层次,需管理得当防止饥饿 |
configMINIMAL_STACK_SIZE |
128 | 空闲/定时器任务的最小栈深度 | 每个任务起始栈空间为128 * 4字节(512B) |
configTOTAL_HEAP_SIZE |
16KB | 堆内存总大小 | 支持动态任务/队列创建,任务过多会不足 |
configMAX_TASK_NAME_LEN |
16 | 任务名最大长度 | 支持调试任务名称显示 |
configQUEUE_REGISTRY_SIZE |
10 | 可注册的队列数量 | 用于调试或跟踪10个队列 |
⛓️ 五、同步机制配置
| 配置项 | 值 | 含义 | 最终结果 & 系统影响 |
|---|---|---|---|
configUSE_MUTEXES |
0 | 启用互斥量 | 不支持资源访问保护或优先级继承 |
configUSE_RECURSIVE_MUTEXES |
0 | 启用递归互斥量 | 不支持递归加锁操作 |
configUSE_COUNTING_SEMAPHORES |
0 | 启用计数信号量 | 不支持“资源池”类的并发访问管理 |
configUSE_TASK_NOTIFICATIONS |
1 | 启用任务通知 | 启用轻量级事件通信机制 |
🧮 六、内存分配相关配置
| 配置项 | 值 | 含义 | 最终结果 & 系统影响 |
|---|---|---|---|
configSUPPORT_DYNAMIC_ALLOCATION |
1 | 支持动态分配 | 可以在运行时创建任务等对象 |
configSUPPORT_STATIC_ALLOCATION |
1 | 支持静态分配 | 支持在编译时分配内存,更安全可控 |
configCHECK_FOR_STACK_OVERFLOW |
0 | 栈溢出检测 | 没有开启栈溢出检测,有风险 |
⏲️ 七、软件定时器配置
| 配置项 | 值 | 含义 | 最终结果 & 系统影响 |
|---|---|---|---|
configUSE_TIMERS |
1 | 使用软件定时器 | 可延时执行回调,增强系统响应力 |
configTIMER_TASK_PRIORITY |
31 | 定时器任务优先级 | 软件定时器任务将获得最高优先权 |
configTIMER_QUEUE_LENGTH |
10 | 定时器消息队列长度 | 最多并发等待执行的10个定时器回调 |
configTIMER_TASK_STACK_DEPTH |
256 | 定时器任务栈大小 | 满足回调函数中较大栈需求 |
🧵 八、协程支持(较少使用)
| 配置项 | 值 | 含义 | 最终结果 & 系统影响 |
|---|---|---|---|
configUSE_CO_ROUTINES |
0 | 启用协程 | 未使用协程相关机制 |
configMAX_CO_ROUTINE_PRIORITIES |
2 | 协程优先级 | 协程未启用,此项无效 |
📊 九、运行时间与调试可视化
| 配置项 | 值 | 含义 | 最终结果 & 系统影响 |
|---|---|---|---|
configGENERATE_RUN_TIME_STATS |
0 | 生成运行时间统计 | 不能记录各任务运行占比 |
configUSE_TRACE_FACILITY |
0 | 启用跟踪工具支持 | 无法使用 FreeRTOS+Trace 等工具 |
⏱️ 十、中断优先级配置(与 Cortex-M 内核有关)
| 配置项 | 值 | 含义 | 最终结果 & 系统影响 |
|---|---|---|---|
configPRIO_BITS |
4 | NVIC优先级位数 | STM32F103为4位,共16级优先级 |
configLIBRARY_LOWEST_INTERRUPT_PRIORITY |
15 | 最低中断优先级 | 可作为操作系统中断 |
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY |
5 | 可使用FreeRTOS API的最大中断优先级 | 优先级5~15可以使用FreeRTOS API |
configKERNEL_INTERRUPT_PRIORITY |
240 | 内核中断优先级实际值 | 0xF << 4 = 0xF0 = 240 |
configMAX_SYSCALL_INTERRUPT_PRIORITY |
80 | 系统调用中断最高优先级 | 0x5 << 4 = 0x50 = 80 |
✅ 可选API函数启用情况
| 配置项 | 含义 | 是否启用 |
|---|---|---|
INCLUDE_vTaskDelay |
延时函数 | ✅ |
INCLUDE_vTaskDelete |
删除任务 | ✅ |
INCLUDE_vTaskSuspend |
挂起/恢复任务 | ✅ |
INCLUDE_vTaskPrioritySet |
设置优先级 | ✅ |
INCLUDE_uxTaskPriorityGet |
获取优先级 | ✅ |
INCLUDE_eTaskGetState |
获取任务状态 | ✅ |
INCLUDE_vTaskCleanUpResources |
清理资源 | ✅ |
总结
| 类别 | 系统特点总结 |
|---|---|
| ⚙️ 调度方式 | 抢占式+时间片调度,任务响应及时 |
| ⏲️ 节拍系统 | 1ms精度,32位tick,调度精度高 |
| 📦 内存模型 | 支持静态+动态分配,总堆16KB |
| ⛓️ 同步机制 | 信号量/互斥量未启用,功能有限 |
| ⏱️ 软件定时器 | 启用,优先级高,响应快 |
| 🔒 安全性 | 栈溢出检测关闭,调试阶段需注意 |
| 🌙 省电特性 | Tickless未启用,适合开发调试期 |
| 🛠️ 调试工具 | Trace工具未启用,不利于任务分析 |
如果你打算进入低功耗场景或者启用多任务间互斥,推荐修改:
- 开启
configUSE_MUTEXES、configUSE_COUNTING_SEMAPHORES - 开启
configUSE_TICKLESS_IDLE实现省电 - 启用
configCHECK_FOR_STACK_OVERFLOW增强安全
需要我帮你生成一份优化后的配置文件吗?或者根据你的项目场景(比如低功耗 / 任务密集型)提供建议配置?
STM32F103C8T6移植FreeRTOS效果演示
资料下载
更多推荐



所有评论(0)