ucos(七)事件标志组
一、概述
前面讲述了UCOSIII的信号量、互斥信号量,它们都可以完成任务的同步。但是有时候一个任务可能需要和多个事件同步,这个时候就需要使用事件标志组。事件标志组与任务之间有两种同步机制:
- “或”同步:等待多个事件时,任何一个事件发生 ,任务都被同步,这个就称为“或”同步;
- “与”同步:当所有的事件都发生时任务才被同步,这种同步机制被称为“与”同步。
事件标志组是专门管理标志位,一个事件标志组可以管理32个标志位。
在前后台系统,经常会用到标志位,查询标志位是否置位,有明显的缺点,CPU得不到休息,会一直工作查询,增加CPU的功耗。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | void main( void ) { while (1) { if (g_iwdg_reset) { } if (g_wwdg_reset) { g_wwdg_reset=0; } if (g_usart1_event) { } if (g_usart3_event) { } if (g_rtc_wakeup_event) { } if (g_rtc_alarm_event) { } } } |
1.创建事件标志组
1 | void OSFlagCreate (OS_FLAG_GRP *p_grp, CPU_CHAR *p_name, OS_FLAGS flags, OS_ERR *p_err) |
参数:
p_grp:事件标志组对象
p_name:事件标志组的名字
flags:事件标志组里所有标志位的初值,默认写0
p_err:返回错误码,没有错误的就返回OS_ERR_NONE
2.等待事件标志组
1 | OS_FLAGS OSFlagPend (OS_FLAG_GRP *p_grp, OS_FLAGS flags, OS_TICK timeout, OS_OPT opt, CPU_TS *p_ts, OS_ERR *p_err) |
参数:
p_grp:事件标志组对象
flags:要等待哪些标志位;0x01(0001'b),则等待bit0;0x05(0101'b),则等待bit0和bit2;0x83,则等待bit0、bit1、bit7。
timeout:超时时间,默认写0,一直等待
opt:默认写以下格式
1 2 3 4 5 | OS_OPT_PEND_FLAG_SET_ANY + OS_OPT_PEND_FLAG_CONSUME+OS_OPT_PEND_BLOCKING OS_OPT_PEND_FLAG_SET_ANY: 等待任意一个标志位置位 OS_OPT_PEND_FLAG_CONSUME: 等待任意一个标志位成功后,就自动将其清零 OS_OPT_PEND_BLOCKING: 阻塞等待 OS_OPT_PEND_FLAG_SET_ALL: 等待所有标志位置位,才能跳出阻塞等待 |
p_ts: 用于记录等待事件花了多长时间,默认写NULL,不记录。
p_err: 返回错误码,没有错误的就返回OS_ERR_NONE.
返回值:
返回等待成功的标志位。
3.设置事件标志组
1 | OS_FLAGS OSFlagPost (OS_FLAG_GRP *p_grp, OS_FLAGS flags, OS_OPT opt, OS_ERR *p_err) |
参数:
p_grp:事件标志组对象
flags: 结合opt参数一起使用。设置/清零哪些标志位,0x01,则对应bit0;0x05,则对应bit0和bit2;0x83,则对应bit0、bit1、bit7。
opt: OS_OPT_POST_FLAG_SET,对应的bit置位 ,OS_OPT_POST_FLAG_CLR,对应的bit清零.
p_err: 返回错误码,没有错误的就返回OS_ERR_NONE。
二、实例
按键触发中断,在中断中设置中断标志位
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | #include "sys.h" #include "delay.h" #include "usart.h" #include "led.h" #include "includes.h" #include "exti.h" //任务1控制块 OS_TCB Task1_TCB; void task1( void *parg); CPU_STK task1_stk[128]; //任务1的任务堆栈,大小为128字,也就是512字节 OS_FLAG_GRP g_flag_grp; //事件标志组的对象 //主函数 int main( void ) { OS_ERR err; systick_init(); //时钟初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组配置 usart_init(9600); //串口初始化 LED_Init(); //LED初始化 Exti_Init(); //OS初始化,它是第一个运行的函数,初始化各种的全局变量,例如中断嵌套计数器、优先级、存储器 OSInit(&err); //创建任务1 OSTaskCreate( (OS_TCB *)&Task1_TCB, //任务控制块,等同于线程id (CPU_CHAR *) "Task1" , //任务的名字,名字可以自定义的 (OS_TASK_PTR)task1, //任务函数,等同于线程函数 ( void *)0, //传递参数,等同于线程的传递参数 (OS_PRIO)6, //任务的优先级6 (CPU_STK *)task1_stk, //任务堆栈基地址 (CPU_STK_SIZE)128/10, //任务堆栈深度限位,用到这个位置,任务不能再继续使用 (CPU_STK_SIZE)128, //任务堆栈大小 (OS_MSG_QTY)0, //禁止任务消息队列 (OS_TICK)0, //默认时间片长度 ( void *)0, //不需要补充用户存储区 (OS_OPT)OS_OPT_TASK_NONE, //没有任何选项 &err //返回的错误码 ); if (err!=OS_ERR_NONE) { printf ( "task 1 create fail\r\n" ); while (1); } //创建事件标志组 OSFlagCreate(&g_flag_grp, "g_flag_grp" ,0,&err); //启动OS,进行任务调度 OSStart(&err); printf ( ".......\r\n" ); while (1); } void task1( void *parg) { uint32_t t=0; OS_ERR err; OS_FLAGS flag = 0; printf ( "task1 is create ok\r\n" ); while (1) { //等待事件标志组 //0x01|0x02 就是等待bit0和bit1 //0:不做超时等待 //OS_OPT_PEND_FLAG_SET_ANY 等待其中一个标志位 //OS_OPT_PEND_FLAG_CONSUME 等待对应的时间成功后,将对应的时间标志位清零 //OS_OPT_PEND_BLOCKING 阻塞形式等待信号量,若等不了信号量,则让出CPU使用权给其他任务 //不需要时间戳(时间标记):用于记录等待信号量花了多长时间,默认写NULL,不记录 //或同步 //flag = OSFlagPend(&g_flag_grp,0x01|0x02,0,OS_OPT_PEND_FLAG_SET_ANY + OS_OPT_PEND_FLAG_CONSUME+OS_OPT_PEND_BLOCKING,NULL,&err); //与同步 flag = OSFlagPend(&g_flag_grp,0x01|0x02,0,OS_OPT_PEND_FLAG_SET_ALL + OS_OPT_PEND_FLAG_CONSUME+OS_OPT_PEND_BLOCKING,NULL,&err); if (flag & 0x01) { printf ( "bit0 set\r\n" ); } if (flag & 0x02) { printf ( "bit1 set\r\n" ); } } } //中断服务函数 在CORE里面的start_stm32f40_41xxx.s这个汇编里面找 void EXTI0_IRQHandler( void ) //PA0 { uint8_t b=0; OS_ERR err; //进入中断 OSIntEnter(); //判断确实进中断标志 //if(EXTI_GetITStatus(EXTI_Line9) !=RESET) //==SET if ((EXTI->PR & (0x1<<0)) !=0) { b=1; //清楚中断标志位 往里面写1 记住一定要清空 //EXTI_ClearITPendingBit(EXTI_Line0); EXTI->PR = (0x1<<0); } //退出中断 OSIntExit(); if (b) { //设置事件标志组的bit0 OSFlagPost(&g_flag_grp,0x01,OS_OPT_POST_FLAG_SET,&err); } } //中断服务函数 在CORE里面的start_stm32f40_41xxx.s这个汇编里面找 void EXTI2_IRQHandler( void ) //PA0 { uint8_t b=0; OS_ERR err; //判断确实进中断标志 if (EXTI_GetITStatus(EXTI_Line2) !=RESET) //==SET { b=1; //清楚中断标志位 往里面写1 记住一定要清空 EXTI_ClearITPendingBit(EXTI_Line2); } if (b) { //设置事件标志组的bit1 OSFlagPost(&g_flag_grp,0x02,OS_OPT_POST_FLAG_SET,&err); } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)