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);
    }  
}

  

 

  

posted @   轻轻的吻  阅读(422)  评论(0编辑  收藏  举报
编辑推荐:
· 基于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)
点击右上角即可分享
微信分享提示