FreeRTOS 创建单任务与多任务(SRAM 动态内存) 一、前言在 FreeRTOS 中任务创建有两种方式动态内存创建和静态内存创建。本文使用最常见的动态内存创建方式通过STM32F103 标准库 FreeRTOS演示创建单任务让一个 LED 闪烁创建多任务让两个 LED 以不同频率闪烁。硬件STM32F103C8T6LED1 接 PC13LED2 接 PA0若你的板子不同修改引脚即可。软件Keil MDK5、标准外设库 V3.5、FreeRTOS V10.x。二、动态内存创建任务原理调用xTaskCreate()时FreeRTOS 会从堆中动态申请两块内存任务控制块TCB保存任务状态、优先级、栈指针等信息任务栈任务运行时局部变量和函数调用使用。因此必须满足configSUPPORT_DYNAMIC_ALLOCATION为1configTOTAL_HEAP_SIZE足够大例如 8KB。三、工程准备标准库移植注意将 FreeRTOS 源码加入工程并添加头文件路径准备一个标准库点灯工程重要注释掉stm32f10x_it.c中的以下三个中断函数避免与 FreeRTOS 冲突// stm32f10x_it.c 中注释掉下面三个函数// void SVC_Handler(void) {}// void PendSV_Handler(void) {}// void SysTick_Handler(void) {}然后在FreeRTOSConfig.h中做中断映射#definevPortSVCHandlerSVC_Handler#definexPortPendSVHandlerPendSV_Handler#definexPortSysTickHandlerSysTick_Handler四、FreeRTOSConfig.h 关键配置#ifndefFREERTOS_CONFIG_H#defineFREERTOS_CONFIG_H#defineconfigUSE_PREEMPTION1#defineconfigUSE_IDLE_HOOK0#defineconfigUSE_TICK_HOOK0#defineconfigCPU_CLOCK_HZ(72000000UL)#defineconfigTICK_RATE_HZ(1000UL)#defineconfigMAX_PRIORITIES(5)#defineconfigMINIMAL_STACK_SIZE((unsignedshort)128)#defineconfigTOTAL_HEAP_SIZE(8*1024)#defineconfigMAX_TASK_NAME_LEN(16)#defineconfigUSE_16_BIT_TICKS0#defineconfigIDLE_SHOULD_YIELD1#defineconfigUSE_MUTEXES0#defineconfigQUEUE_REGISTRY_SIZE0#defineconfigCHECK_FOR_STACK_OVERFLOW0#defineconfigUSE_RECURSIVE_MUTEXES0#defineconfigUSE_MALLOC_FAILED_HOOK0#defineconfigUSE_APPLICATION_TASK_TAG0#defineconfigUSE_COUNTING_SEMAPHORES0#defineconfigGENERATE_RUN_TIME_STATS0/* 动态内存创建必须为 1 */#defineconfigSUPPORT_DYNAMIC_ALLOCATION1#defineconfigSUPPORT_STATIC_ALLOCATION0/* 使能 vTaskDelay 等 API */#defineINCLUDE_vTaskDelay1#defineINCLUDE_vTaskDelete1/* 中断函数映射 */#definevPortSVCHandlerSVC_Handler#definexPortPendSVHandlerPendSV_Handler#definexPortSysTickHandlerSysTick_Handler#endif/* FREERTOS_CONFIG_H */五、创建单任务完整代码#includestm32f10x.h#includeFreeRTOS.h#includetask.h/* LED1 定义 */#defineLED1_PORTGPIOC#defineLED1_PINGPIO_Pin_13/* 任务函数声明 */staticvoidLed1_Task(void*pvParameters);/* LED1 初始化 */staticvoidLED1_Init(void){GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);GPIO_InitStructure.GPIO_PinLED1_PIN;GPIO_InitStructure.GPIO_ModeGPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz;GPIO_Init(LED1_PORT,GPIO_InitStructure);GPIO_SetBits(LED1_PORT,LED1_PIN);/* 默认熄灭 */}/* LED1 闪烁任务 */staticvoidLed1_Task(void*pvParameters){(void)pvParameters;while(1){GPIO_ResetBits(LED1_PORT,LED1_PIN);/* 点亮 */vTaskDelay(500/portTICK_PERIOD_MS);/* 延时 500ms */GPIO_SetBits(LED1_PORT,LED1_PIN);/* 熄灭 */vTaskDelay(500/portTICK_PERIOD_MS);/* 延时 500ms */}}intmain(void){NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);/* 必须 */LED1_Init();/* 动态内存创建单任务 */BaseType_t xReturnxTaskCreate(Led1_Task,/* 任务函数 */Led1Task,/* 任务名称 */configMINIMAL_STACK_SIZE,/* 栈深度字 */NULL,/* 任务参数 */1,/* 优先级 */NULL/* 任务句柄 */);if(xReturnpdPASS){vTaskStartScheduler();/* 启动调度器 */}while(1){}/* 不会执行到这里 */}说明这就是最简单的单任务创建流程LED1 以 1Hz 频率闪烁。六、创建多任务完整代码在单任务基础上增加第二个 LEDPA0和第二个任务两个任务并发运行。#includestm32f10x.h#includeFreeRTOS.h#includetask.h/* LED 定义 */#defineLED1_PORTGPIOC#defineLED1_PINGPIO_Pin_13#defineLED2_PORTGPIOA#defineLED2_PINGPIO_Pin_0/* 任务函数声明 */staticvoidLed1_Task(void*pvParameters);staticvoidLed2_Task(void*pvParameters);/* LED 初始化 */staticvoidLED_Init(void){GPIO_InitTypeDef GPIO_InitStructure;/* 使能 GPIOC 和 GPIOA 时钟 */RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOA,ENABLE);/* LED1: PC13 推挽输出 */GPIO_InitStructure.GPIO_PinLED1_PIN;GPIO_InitStructure.GPIO_ModeGPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz;GPIO_Init(LED1_PORT,GPIO_InitStructure);GPIO_SetBits(LED1_PORT,LED1_PIN);/* 默认熄灭 *//* LED2: PA0 推挽输出 */GPIO_InitStructure.GPIO_PinLED2_PIN;GPIO_InitStructure.GPIO_ModeGPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz;GPIO_Init(LED2_PORT,GPIO_InitStructure);GPIO_SetBits(LED2_PORT,LED2_PIN);/* 默认熄灭 */}/* LED1 闪烁任务慢闪 */staticvoidLed1_Task(void*pvParameters){(void)pvParameters;while(1){GPIO_ResetBits(LED1_PORT,LED1_PIN);/* 点亮 */vTaskDelay(500/portTICK_PERIOD_MS);GPIO_SetBits(LED1_PORT,LED1_PIN);/* 熄灭 */vTaskDelay(500/portTICK_PERIOD_MS);}}/* LED2 闪烁任务快闪 */staticvoidLed2_Task(void*pvParameters){(void)pvParameters;while(1){GPIO_ResetBits(LED2_PORT,LED2_PIN);/* 点亮 */vTaskDelay(200/portTICK_PERIOD_MS);GPIO_SetBits(LED2_PORT,LED2_PIN);/* 熄灭 */vTaskDelay(200/portTICK_PERIOD_MS);}}intmain(void){NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);/* 必须 */LED_Init();/* 创建任务1LED1 闪烁优先级 1 */BaseType_t xReturn1xTaskCreate(Led1_Task,Led1Task,configMINIMAL_STACK_SIZE,NULL,1,/* 优先级 1 */NULL);/* 创建任务2LED2 闪烁优先级 2更高 */BaseType_t xReturn2xTaskCreate(Led2_Task,Led2Task,configMINIMAL_STACK_SIZE,NULL,2,/* 优先级 2 */NULL);/* 两个任务都创建成功则启动调度器 */if(xReturn1pdPASSxReturn2pdPASS){vTaskStartScheduler();}while(1){}/* 不会执行到这里 */}现象LED1 慢闪1HzLED2 快闪2.5Hz互不干扰这就是多任务并发的直观体现。七、代码解析1. 任务函数必须是无返回的死循环每个任务都是一个独立执行的函数不能return否则会导致系统异常。2.vTaskDelay()的作用让任务进入阻塞状态CPU 可以执行其他任务。单任务时 CPU 大部分空闲多任务时两个任务轮流获得 CPU。3.xTaskCreate()参数参数含义任务函数名任务入口函数任务名称字符串仅用于调试最长configMAX_TASK_NAME_LEN栈深度单位是字4字节configMINIMAL_STACK_SIZE128即 512 字节任务参数传给任务函数的指针不需要传NULL优先级数字越大优先级越高0 为最低configMAX_PRIORITIES-1为最高任务句柄用于后续操作任务删除、挂起等不需要时传NULL4. 多任务与单任务的区别多任务只是多次调用xTaskCreate()而已。每个任务拥有独立的栈和 TCB调度器自动切换。注意每个任务栈大小要足够否则栈溢出优先级可相同或不同相同优先级会时间片轮转任务数量受限于堆大小configTOTAL_HEAP_SIZE。八、常见问题1. 任务创建失败检查configSUPPORT_DYNAMIC_ALLOCATION是否为1增大configTOTAL_HEAP_SIZE。2. 编译报错SVC_Handler重复定义确保已注释stm32f10x_it.c中的SVC_Handler、PendSV_Handler、SysTick_Handler。3. 进入 HardFault中断优先级分组必须为NVIC_PriorityGroup_4确认FreeRTOSConfig.h中的中断映射正确。4. 两个任务似乎不同步闪烁这是正常的因为 FreeRTOS 调度器按时间片切换任务LED 闪烁频率由各自的vTaskDelay决定互不影响。总结本文使用标准库 FreeRTOS 动态内存演示了单任务和多任务的创建方法。核心就是xTaskCreate()函数多任务只是重复调用它。掌握这个流程后复杂应用不过是创建更多任务并通过队列、信号量进行通信。希望本文能帮你快速入门 FreeRTOS 的多任务编程。

相关新闻

最新新闻

HTML-in-Canvas API 实战:在 Canvas 中嵌入可交互 UI 组件

HTML-in-Canvas API 实战:在 Canvas 中嵌入可交互 UI 组件

在实际前端开发中,Canvas 因其强大的像素级绘图能力,常被用于图表、游戏、特效等场景。然而,当我们需要在 Canvas 中嵌入复杂的、可交互的 UI 组件(如按钮、输入框、列表)时,传统做法要么是使用 SVG&#x…

2026/8/20 11:01:13
县域即时配送系统的技术架构与实现——诚心呈意共享配送系统深度解析

县域即时配送系统的技术架构与实现——诚心呈意共享配送系统深度解析

一、引言:县域配送的技术困境跑腿创业,很多人觉得就是找几个骑手、建个微信群、接单送单。但真正做过的人才知道:一套能稳定运行、智能调度、数据驱动、持续进化的配送系统,背后是极高的技术门槛。2026年,国内即时物流…

2026/8/20 11:01:13
ERP采购订单变更失控:从根源分析到SAP系统实现完整控制方案

ERP采购订单变更失控:从根源分析到SAP系统实现完整控制方案

如果你在ERP系统里处理过采购订单,大概率经历过这种场景:业务部门一个电话过来:“王工,采购单号PO2024052001的到货日期要提前两天,供应商那边催得急,帮忙改一下。”你熟练地打开系统,找到订单&…

2026/8/20 11:01:13
快捷键突然失灵?Hotkey Detective 帮你锁定占用热键的进程

快捷键突然失灵?Hotkey Detective 帮你锁定占用热键的进程

快捷键突然失灵?Hotkey Detective 帮你锁定占用热键的进程 【免费下载链接】hotkey-detective A small program for investigating stolen key combinations under Windows 7 and later. 项目地址: https://gitcode.com/gh_mirrors/ho/hotkey-detective 某个…

2026/8/20 11:01:13
Claude Code在Linux系统管理中的实战应用与效率提升指南

Claude Code在Linux系统管理中的实战应用与效率提升指南

1. 先搞清楚 Claude Code 在 Linux 管理里到底能做什么如果你在找一种能帮你写脚本、解释命令、排查系统问题的“智能副驾”,那 Claude Code 确实值得花时间研究一下。它不是一个独立的 Linux 发行版或图形化管理工具,而是一个集成在代码编辑器&#xff…

2026/8/20 11:01:13
羽山数智手把手搭建本地AI大脑完结篇:从 0 搭建本地大脑的全景路线图——六篇系列总结与踩坑清单

羽山数智手把手搭建本地AI大脑完结篇:从 0 搭建本地大脑的全景路线图——六篇系列总结与踩坑清单

羽山数智 本地AI大脑解决方案 系列总结篇(第 6/6 篇)上一篇:仿生增强与工程化落地——从"能用"到"能上线"本系列 6 篇全部完结,本文是全景回顾 方法论 踩坑清单适合收藏:未来要自建本地大脑的团…

2026/8/20 10:56:13