完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
第18章 FreeRTOS事件标志组 前面的章节我们已经讲解了任务管理和时间管理,从本章节开始讲解任务间的通信和同步机制。首先讲解任务间的通信和同步机制之一,事件标志组。 本章教程配套的例子含Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407以及F429。 18.1 事件标志组 18.2 事件标志组API函数 18.3 实验例程说明(任务间通信) 18.4 实验例程说明(中断方式通信) 18.5 总结 18.1 事件标志组 18.1.1 为什么要使用事件标志 事件标志组是实现多任务同步的有效机制之一。也许有不理解的初学者会问采用事件标志组多麻烦,搞个全局变量不是更简单?其实不然,在裸机编程时,使用全局变量的确比较方便,但是在加上RTOS后就是另一种情况了。使用全局变量相比事件标志组主要有如下三个问题: 1、使用事件标志组可以让RTOS内核有效地管理任务,而全局变量是无法做到的,任务的超时等机制需要用户自己去实现。 2、使用了全局变量就要防止多任务的访问冲突,而使用事件标志组则处理好了这个问题,用户无需担心。 3、使用事件标志组可以有效地解决中断服务程序和任务之间的同步问题。 18.1.2 FreeRTOS任务间事件标志组的实现 任务间事件标志组的实现是指各个任务之间使用事件标志组实现任务的通信或者同步机制。 下面我们来说说FreeRTOS中事件标志的实现,根据用户在FreeRTOSConfig.h文件中的配置: 1、#define configUSE_16_BIT_tiCKS 1 配置宏定义configUSE_16_BIT_TICKS为1时,每创建一个事件标志组,用户可以使用的事件标志是8个。 2、#define configUSE_16_BIT_TICKS 0 配置宏定义configUSE_16_BIT_TICKS为0时,每创建一个事件标志组,用户可以使用的事件标志是24个。 |
|
相关推荐
|
|
硬件外设初始化
硬件外设的初始化是在bsp.c文件实现: 复制代码 /* ********************************************************************************************************* * 函 数 名: bsp_Init * 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void bsp_Init(void) { /* 由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。 启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。 系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件 */ /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); bsp_InitUart(); /* 初始化串口 */ bsp_InitKey(); /* 初始化按键变量 */ bsp_InitLed(); /* 初始LED指示灯端口 */ bsp_InitHardTimer(); /* 初始化TIM2定时器 */ } |
|
|
|
|
|
FreeRTOS任务创建:
复制代码 /* ********************************************************************************************************* * 函 数 名: AppTaskCreate * 功能说明: 创建应用任务 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ static void AppTaskCreate (void) { xTaskCreate( vTaskTaskUserIF, /* 任务函数 */ "vTaskUserIF", /* 任务名 */ 512, /* 任务栈大小,单位word,也就是4字节 */ NULL, /* 任务参数 */ 1, /* 任务优先级*/ &xHandleTaskUserIF ); /* 任务句柄 */ xTaskCreate( vTaskLED, /* 任务函数 */ "vTaskLED", /* 任务名 */ 512, /* 任务栈大小,单位word,也就是4字节 */ NULL, /* 任务参数 */ 2, /* 任务优先级*/ &xHandleTaskLED ); /* 任务句柄 */ xTaskCreate( vTaskMsgPro, /* 任务函数 */ "vTaskMsgPro", /* 任务名 */ 512, /* 任务栈大小,单位word,也就是4字节 */ NULL, /* 任务参数 */ 3, /* 任务优先级*/ &xHandleTaskMsgPro ); /* 任务句柄 */ xTaskCreate( vTaskStart, /* 任务函数 */ "vTaskStart", /* 任务名 */ 512, /* 任务栈大小,单位word,也就是4字节 */ NULL, /* 任务参数 */ 4, /* 任务优先级*/ &xHandleTaskStart ); /* 任务句柄 */ } |
|
|
|
|
|
FreeRTOS事件标志组创建:
复制代码 /* ********************************************************************************************************* * 函 数 名: AppObjCreate * 功能说明: 创建任务通信机制 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ static void AppObjCreate (void) { /* 创建事件标志组 */ xCreatedEventGroup = xEventGroupCreate(); if(xCreatedEventGroup == NULL) { /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */ } } |
|
|
|
|
|
定时器中断回调函数中给任务发送事件标志组设置消息:
定时器中断的初始化和中断函数在bsp_timer.c 文件中实现,这个不是教程的重点,故不作介绍。 这里主要关心函数xEventGroupSetBitsFromISR在中断服务程序中的使用方法。 复制代码 /* ********************************************************************************************************* * 函 数 名: TIM_CallBack1和TIM_CallBack2 * 功能说明: 定时器中断的回调函数,此函数被bsp_StartHardTimer所调用。 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ static void TIM_CallBack1(void) { BaseType_t xResult; BaseType_t xHigherPriorityTaskWoken = pdFALSE; /* 向任务vTaskMsgPro发送事件标志 */ xResult = xEventGroupSetBitsFromISR(xCreatedEventGroup, /* 事件标志组句柄 */ BIT_0 , /* 设置bit0 */ &xHigherPriorityTaskWoken ); /* 消息被成功发出 */ if( xResult != pdFAIL ) { /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */ portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } } static void TIM_CallBack2(void) { BaseType_t xResult; BaseType_t xHigherPriorityTaskWoken = pdFALSE; /* 向任务vTaskMsgPro发送事件标志 */ xResult = xEventGroupSetBitsFromISR(xCreatedEventGroup, /* 事件标志组句柄 */ BIT_1, /* 设置bit1 */ &xHigherPriorityTaskWoken ); /* 消息被成功发出 */ if( xResult != pdFAIL ) { /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */ portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } } |
|
|
|
|
|
18.4.3 STM32F429开发板实验
配套例子: V6-313_FreeRTOS实验_事件标志组(中断方式) 实验目的: 1. 学习FreeRTOS的事件标志组(中断方式)。 实验内容: 1. K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。 2. K2键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送事件标志,设置bit0。 3. K3键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送事件标志,设置bit1。 4. 任务vTaskMsgPro只有接收到bit0和bit1都被设置了才执行串口打印消息。 5. 各个任务实现的功能如下: vTaskUserIF任务 :按键消息处理。 vTaskLED任务 :LED闪烁。 vTaskMsgPro任务 : 消息处理,使用函数xEventGroupWaitBits接收定时器中断的事件标志。 vTaskStart任务 :启动任务,也是最高优先级任务,这里实现按键扫描。 |
|
|
|
|
|
FreeRTOS的配置:
FreeRTOSConfig.h文件中的配置如下: 复制代码 /* Ensure stdint is only used by the compiler, and not the assembler. */ #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__) #include extern volatile uint32_t ulHighFrequencyTimerTicks; #endif #define configUSE_PREEMPTION 1 #define configUSE_IDLE_HOOK 0 #define configUSE_TICK_HOOK 0 #define configCPU_CLOCK_HZ ( ( unsigned long ) 168000000 ) #define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) #define configMAX_PRIORITIES ( 5 ) #define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 ) #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 30 * 1024 ) ) #define configMAX_TASK_NAME_LEN ( 16 ) #define configUSE_TRACE_FACILITY 1 #define configUSE_16_BIT_TICKS 0 #define configIDLE_SHOULD_YIELD 1 /* Run time and task stats gathering related definitions. */ #define configGENERATE_RUN_TIME_STATS 1 #define configUSE_STATS_FORMATTING_FUNCTIONS 1 #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() (ulHighFrequencyTimerTicks = 0ul) #define portGET_RUN_TIME_COUNTER_VALUE() ulHighFrequencyTimerTicks //#define portALT_GET_RUN_TIME_COUNTER_VALUE 1 /* Co-routine definitions. */ #define configUSE_CO_ROUTINES 0 #define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) /* Set the following definitions to 1 to include the API function, or zero to exclude the API function. */ #define INCLUDE_vTaskPrioritySet 1 #define INCLUDE_uxTaskPriorityGet 1 #define INCLUDE_vTaskDelete 1 #define INCLUDE_vTaskCleanUpResources 0 #define INCLUDE_vTaskSuspend 1 #define INCLUDE_xTaskResumeFromISR 1 #define INCLUDE_vTaskDelayUntil 1 #define INCLUDE_vTaskDelay 1 #define INCLUDE_xEventGroupSetBitFromISR 1 #define INCLUDE_xTimerPendFunctionCall 1 /* Software timer definitions. */ #define configUSE_TIMERS 1 #define configTIMER_TASK_PRIORITY ( 5 ) #define configTIMER_QUEUE_LENGTH 20 #define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 ) /* Cortex-M specific definitions. */ #ifdef __NVIC_PRIO_BITS /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */ #define configPRIO_BITS __NVIC_PRIO_BITS #else #define configPRIO_BITS 4 /* 15 priority levels */ #endif /* The lowest interrupt priority that can be used in a call to a "set priority" function. */ #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0x0f /* The highest interrupt priority that can be used by any interrupt service routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER PRIORITY THAN THIS! (higher priorities are lower numeric values. */ #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 0x01 |
|
|
|
|
|
几个重要选项说明:
1、#define configUSE_PREEMPTION 1 使能抢占式调度器 2、#define configCPU_CLOCK_HZ ( ( unsigned long ) 168000000 ) 系统主频168MHz。 3、#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) 系统时钟节拍1KHz,即1ms。 4、#define configMAX_PRIORITIES ( 5 ) 定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4,不包含5,对于这一点,初学者要特别的注意。 5、#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 30 * 1024 ) ) 定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。 6、使用事件标志组中的函数xEventGroupSetBitsFromISR,务必使能以下三个宏定义 (1)#define INCLUDE_xEventGroupSetBitFromISR 1 (2)#define INCLUDE_xTimerPendFunctionCall 1 (3)#define configUSE_TIMERS 1 因为使能了FreeRTOS的定时器组任务,定时器组的其它宏定义也做一下配置,配置如下: (1)#define configTIMER_TASK_PRIORITY ( 5 ) (2)#define configTIMER_QUEUE_LENGTH 20 (3)#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 ) 7、configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 0x01 定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下: (1)使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。 (2)对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为0到15,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。 (3)这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY为0x01表示用户可以在抢占式优先级为1到15的中断里面调用FreeRTOS的API函数,抢占式优先级为0的中断里面是不允许调用的。 |
|
|
|
|
|
FreeRTOS任务调试信息(按K1按键,串口打印):
上面截图中打印出来的任务状态字母B, R, D, S对应如下含义: #definetskBLOCKED_CHAR ( 'B' ) 任务阻塞 #definetskREADY_CHAR ( 'R' ) 任务就绪 #definetskDELETED_CHAR ( 'D' ) 任务删除 #definetskSUSPENDED_CHAR ( 'S' ) 任务挂起 |
|
|
|
|
|
使用事件标志组要包含的头文件:
使用FreeRTOS的事件标志组要包含头文件#include "event_groups.h",这个头文件在includes.h文件中进行了包含,具体如下: 复制代码 /* ********************************************************************************************************* * OS ********************************************************************************************************* */ #include "FreeRTOS.h" #include "task.h" #include "queue.h" #include "croutine.h" #include "semphr.h" #include "event_groups.h" |
|
|
|
|
|
程序设计:
任务栈大小分配: vTaskUserIF任务 :2048字节 vTaskLED任务 :2048字节 vTaskMsgPro任务 :2048字节 vTaskStart任务 :2048字节 任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的 #defineconfigTOTAL_HEAP_SIZE ( ( size_t )( 30 * 1024 ) ) 系统栈大小分配: |
|
|
|
|
|
FreeROTS初始化:
复制代码 /* ********************************************************************************************************* * 函 数 名: main * 功能说明: 标准c程序入口。 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ int main(void) { /* 在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。 这样做的好处是: 1. 防止执行的中断服务程序中有FreeRTOS的API函数。 2. 保证系统正常启动,不受别的中断影响。 3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。 在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1) 和cpsie i是等效的。 */ __set_PRIMASK(1); /* 硬件初始化 */ bsp_Init(); /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项 目中不要使用,因为这个功能比较影响系统实时性。 2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。 */ vSetupSysInfoTest(); /* 创建任务 */ AppTaskCreate(); /* 创建任务通信机制 */ AppObjCreate(); /* 启动调度,开始执行任务 */ vTaskStartScheduler(); /* 如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的 heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小: #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) ) */ while(1); } |
|
|
|
|
|
硬件外设初始化
硬件外设的初始化是在bsp.c文件实现: 复制代码 /* ********************************************************************************************************* * 函 数 名: bsp_Init * 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void bsp_Init(void) { /* 由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。 启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。 系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件 */ /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); SystemCoreClockUpdate(); /* 根据PLL配置更新系统时钟频率变量 SystemCoreClock */ bsp_InitUart(); /* 初始化串口 */ bsp_InitKey(); /* 初始化按键变量 */ bsp_InitExtIO(); /* FMC总线上扩展了32位输出IO, 操作LED等外设必须初始化 */ bsp_InitLed(); /* 初始LED指示灯端口 */ bsp_InitHardTimer(); /* 初始化TIM2定时器 */ } |
|
|
|
|
|
FreeRTOS任务创建:
复制代码 /* ********************************************************************************************************* * 函 数 名: AppTaskCreate * 功能说明: 创建应用任务 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ static void AppTaskCreate (void) { xTaskCreate( vTaskTaskUserIF, /* 任务函数 */ "vTaskUserIF", /* 任务名 */ 512, /* 任务栈大小,单位word,也就是4字节 */ NULL, /* 任务参数 */ 1, /* 任务优先级*/ &xHandleTaskUserIF ); /* 任务句柄 */ xTaskCreate( vTaskLED, /* 任务函数 */ "vTaskLED", /* 任务名 */ 512, /* 任务栈大小,单位word,也就是4字节 */ NULL, /* 任务参数 */ 2, /* 任务优先级*/ &xHandleTaskLED ); /* 任务句柄 */ xTaskCreate( vTaskMsgPro, /* 任务函数 */ "vTaskMsgPro", /* 任务名 */ 512, /* 任务栈大小,单位word,也就是4字节 */ NULL, /* 任务参数 */ 3, /* 任务优先级*/ &xHandleTaskMsgPro ); /* 任务句柄 */ xTaskCreate( vTaskStart, /* 任务函数 */ "vTaskStart", /* 任务名 */ 512, /* 任务栈大小,单位word,也就是4字节 */ NULL, /* 任务参数 */ 4, /* 任务优先级*/ &xHandleTaskStart ); /* 任务句柄 */ } |
|
|
|
|
|
FreeRTOS事件标志组创建:
复制代码 /* ********************************************************************************************************* * 函 数 名: AppObjCreate * 功能说明: 创建任务通信机制 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ static void AppObjCreate (void) { /* 创建事件标志组 */ xCreatedEventGroup = xEventGroupCreate(); if(xCreatedEventGroup == NULL) { /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */ } } |
|
|
|
|
|
定时器中断回调函数中给任务发送事件标志组设置消息:
定时器中断的初始化和中断函数在bsp_timer.c 文件中实现,这个不是教程的重点,故不作介绍。 这里主要关心函数xEventGroupSetBitsFromISR在中断服务程序中的使用方法。 复制代码 /* ********************************************************************************************************* * 函 数 名: TIM_CallBack1和TIM_CallBack2 * 功能说明: 定时器中断的回调函数,此函数被bsp_StartHardTimer所调用。 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ static void TIM_CallBack1(void) { BaseType_t xResult; BaseType_t xHigherPriorityTaskWoken = pdFALSE; /* 向任务vTaskMsgPro发送事件标志 */ xResult = xEventGroupSetBitsFromISR(xCreatedEventGroup, /* 事件标志组句柄 */ BIT_0 , /* 设置bit0 */ &xHigherPriorityTaskWoken ); /* 消息被成功发出 */ if( xResult != pdFAIL ) { /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */ portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } } static void TIM_CallBack2(void) { BaseType_t xResult; BaseType_t xHigherPriorityTaskWoken = pdFALSE; /* 向任务vTaskMsgPro发送事件标志 */ xResult = xEventGroupSetBitsFromISR(xCreatedEventGroup, /* 事件标志组句柄 */ BIT_1, /* 设置bit1 */ &xHigherPriorityTaskWoken ); /* 消息被成功发出 */ if( xResult != pdFAIL ) { /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */ portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } } |
|
|
|
|
|
690 浏览 0 评论
735 浏览 1 评论
基于瑞萨FPB-RA4E2智能床头灯项目——1编译环境搭建与点亮驱动ws2812全彩LED
624 浏览 0 评论
嵌入式学习-飞凌嵌入式ElfBoard ELF 1板卡-LCD显示图片编程示例之介绍mmap
1099 浏览 0 评论
《DNESP32S3使用指南-IDF版_V1.6》第二章 常用的C语言知识点
1092 浏览 0 评论
【youyeetoo X1 windows 开发板体验】少儿AI智能STEAM积木平台
11794 浏览 31 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-26 12:12 , Processed in 0.875861 second(s), Total 74, Slave 66 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号