STM32F103C8T6 FreeRTOS静态任务LED和按键控制项目

项目结构图

项目根目录
User
System
Hardware
FreeRTOS
main.c
Delay.c
Delay.h
MyFreeRTOS.c
MyFreeRTOS.h
LED.c/h
Key.c/h
OLED.c/h
任务相关文件
内核相关文件
event_groups.c/h

整体功能流程图

系统启动
初始化外设
延时初始化
LED初始化
按键初始化
OLED初始化
事件组初始化
启动FreeRTOS
创建启动任务
创建三个应用任务
删除启动任务
任务调度运行
任务1:LED1控制
任务2:LED2控制
任务3:按键监控

各模块流程图

main.c流程图

main函数开始
初始化各个外设
创建事件组
启动FreeRTOS
永不退出

Delay.c流程图

延时模块
Delay_Init初始化
配置SysTick定时器
Delay_us微秒延时
设置重装值
等待计数完成
Delay_ms毫秒延时
循环调用Delay_us
Delay_s秒延时
循环调用Delay_ms

Key.c按键状态机流程图

RELEASE
PRESS
<长按阈值
>=长按阈值
LONG
按键状态机
当前状态?
检测按下
切换到PRESS
开始计时
持续时间?
检测释放
短按事件
切换到LONG
检测释放
长按事件
切换到RELEASE

MyFreeRTOS.c流程图

短按
长按
短按
长按
FreeRTOS启动
FreeRTOS_Start
创建启动任务
启动调度器
启动任务
进入临界区
创建任务1
创建任务2
创建任务3
删除启动任务
退出临界区
任务1
等待按键事件
事件类型?
LED1翻转
LED1常亮
任务2
等待按键事件
事件类型?
LED2翻转
LED2常亮
任务3
按键状态机处理
更新按键状态
发送事件通知

关键功能说明

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最小系统板

让我分析一下代码中可能存在的漏洞

  1. LED驱动模块(Hardware/LED.c)中的潜在问题:

    • GPIO初始化时没有对时钟使能的返回状态进行检查
    • GPIO操作没有临界区保护,在多任务环境下可能导致竞态条件
    • LED状态翻转时的读-改-写操作不是原子的,可能导致状态不一致
  2. 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的有效范围
          // 可能导致内存访问越界
      
  3. 任务管理相关的隐患:

    • 静态任务创建时没有对栈空间使用情况的监控
    • 没有对任务切换时的上下文完整性进行验证
    • 缺少任务运行超时的保护机制
  4. 硬件资源管理方面:

    • I2C总线访问没有互斥保护
    • GPIO资源没有集中管理,可能存在引脚复用冲突
    • 外设时钟配置没有验证机制
  5. 内存安全方面:

    • 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没有结束符,可能会导致缓冲区溢出
      
  6. 中断处理相关:

    • 缺少中断优先级管理
    • 中断处理函数中可能存在重入问题
    • 没有对中断延迟的监控机制
  7. 时序相关的问题:

    • OLED的I2C通信时序没有严格的时间控制
    • 按键消抖可能不够完善
    • 任务延时精度没有补偿机制
  8. 配置参数相关:

    • 硬件配置参数都是硬编码的,缺乏验证
    • 没有对关键参数的合法性检查
    • 配置参数可能存在冲突的情况
  9. 异常处理方面:

    • 大部分函数没有错误返回值
    • 没有系统级的异常处理机制
    • 硬件故障时缺乏保护措施
  10. 电源管理相关:

    • 没有对系统电源状态的监控
    • 缺少低功耗模式的保护
    • 电源不稳定时可能影响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_MUTEXESconfigUSE_COUNTING_SEMAPHORES
  • 开启 configUSE_TICKLESS_IDLE 实现省电
  • 启用 configCHECK_FOR_STACK_OVERFLOW 增强安全

需要我帮你生成一份优化后的配置文件吗?或者根据你的项目场景(比如低功耗 / 任务密集型)提供建议配置?


STM32F103C8T6移植FreeRTOS效果演示


资料下载

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐