ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

FreeRTOS 创建单任务与多任务(SRAM 动态内存)

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 的多任务编程。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表