STM32 系统级开发之 ucosIII 或 freeRTOS 事件标志组详解

发布者:VS821001最新更新时间:2021-02-07 来源: eefocus关键字:STM32  ucosIII  freeRTOS  事件标志组 手机看文章 扫描二维码
随时随地手机看文章

1、轻型操作系统同步的方案详解


1)信号量

假设有两个任务 Task1 和 Task2,第一个任务进行按键的扫描,第二个任务进行LED灯的点亮

需求:

扫描到按键按下后点亮 LED 灯,也就是说第二个任务永远在等待第一个任务按键的扫描

实现:

首先 Task1 一直检测按键是否按下,如果按键按下以后,使用一个全局变量 flag 并设置 flag=1

而在 Task2 当中,不停检测 flag 值是否为 1,如果为 1,点亮 led 灯并把flag清零

此时 flag 提供的是一个信号量的作用,也就是说 Task1 按下按键以后,开始向 Task2 发送一个信号量 flag,Task2 接收到了 flag 信号量以后,就把LED灯点亮

2)互斥性信号量

假设有两个任务 Task1 和 Task2,都需要来访问一个共享的资源,如要访问一个共享的打印机

需求:

假设第一个任务 Task1 要打印 hello,第二个任务 Task2 要打印 world,Task1 在使用打印机的时候,Task2是绝对不能使用的,两个任务属于互斥关系

如果 Task1 在使用打印机,Task2 也在使用打印机,那打印出来的数据可能会出现乱码

实现:

为了防止 Task1 和 Task2 共同使用打印机,配置的时候就要使用一个约束,假设还是使用一个全局变量 flag 来表示

如果 flag=1,表示这个打印机处于空闲状态,假设这个时候 Task1 要使用打印机,它首先要判断 flag 的状态,如果判断 flag=1,它就开始使用打印机并且把 flag 置 0

同样,假设这个时候,Task2也来使用打印机,它同样要判断 flag 的状态是否等于 1,如果判断这个时候flag=0,它就知道这个时候打印机处于忙的状态

它就要等待 Task1 把打印机使用完毕,同时会把 flag 置为 1,这个时候 Task2 任务就可以使用打印机了

3)事件标志组

假设两个任务 Task1 和 Task2,Task1 进行按键扫描,Task2 进行 LED 灯的点亮

同样的道理,按键按下时 LED 灯点亮,但是如果是 N 个按键 控制 N 个 LED

使用一个全局变量 flag,但是使用 flag 的各个位来表明了按键按下的状态,flag 的第 0 位为 1 表明第一个按键按下

同样 flag 的第 1 位按键为 1,表明第二个按键已经按下,依次类推

此时 flag 已经不再是一个信号量了,而是一个事件的标志,它的一位标志着一个事件是否发生,比如说第0位为0,表明这个事件没发生,第1位为1,表明这个事件发生了,这个时候这个flag就被称为一个事件的标志

那Task2在使用的过程中,它就需要来判断flag这个事件的各个位

当然事件的标志还有一些其他的高级标志,比如说你各个位能判断某一个事件,还可以判断一个组合事件:

比如第一个按键按下了,并且第二个按键也按下了,并且第三个按键也按下了,那你们都按下以后,我才让某个灯亮

这样我们就可以通过事件标志组来通过各个标志位,来相互的判断,那这个就被称为事件标志组,它不是信号量,但是它还是属于信号量的范畴

2、freeRTOS事件标志组详解


需求:

任务可能会需要与多个事件或任务进行同步,此时信号量就无
能为力了。 FreeRTOS 为此提供了一个可选的解决方法,那就是事件标志组。

简介:

1)、事件位(事件标志)

事件位用来表明某个事件是否发生,事件位通常用作事件标志

比如下面的几个例子:

● 当收到一条消息并且把这条消息处理掉以后就可以将某个位(标志)置 1,当队列中没有
消息需要处理的时候就可以将这个位(标志)置 0;

● 当把队列中的消息通过网络发送输出以后就可以将某个位(标志)置 1,当没有数据需要从网络发送出去的话就将这个位(标志)置 0;

● 现在需要向网络中发送一个心跳信息,将某个位(标志)置 1。现在不需要向网络中发送心跳信息,这个位(标志)置 0。

2)、事件组

一个事件组就是一组的事件位, 事件组中的事件位通过位编号来访问

同样,以上面列出的三个例子为例:

● 事件标志组的 bit0 表示队列中的消息是否处理掉。

● 事件标志组的 bit1 表示是否有消息需要从网络中发送出去。

● 事件标志组的 bit2 表示现在是否需要向网络发送心跳信息。

3)、事件标志组和事件位的数据类型

事件标志组的数据类型为 EventGroupHandle_t, 当 configUSE_16_BIT_TICKS 为 1 的时候事件标志组可以存储 8 个事件位,当 configUSE_16_BIT_TICKS 为 0 的时候事件标志组存储 24个事件位。

事件标志组中的所有事件位都存储在一个无符号的 EventBits_t 类型的变量中, EventBits_t在 event_groups.h 中有如下定义:

typedef TickType_t EventBits_t;

数据类型 TickType_t 在文件 portmacro.h 中有如下定义:

#if( configUSE_16_BIT_TICKS == 1 )
typedef uint16_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffff
#else
typedef uint32_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
#define portTICK_TYPE_IS_ATOMIC 1
#endif

可以看出当 configUSE_16_BIT_TICKS 为 0 的时候 TickType_t 是个 32 位的数据类型, 因此 EventBits_t 也是个 32 位的数据类型。 EventBits_t 类型的变量可以存储 24 个事件位,另外的那高 8 位有其他用。事件位 0 存放在这个变量的 bit0 上,变量的 bit1 就是事件位 1,以此类推。对于 STM32 来说一个事件标志组最多可以存储 24 个事件位。

3、测试试验详解


1)、需求

学习 FreeROTS 事件标志组的使用,包括创建事件标志组、将相应的事件位置 1、等待相应
的事件位置 1 等操作。

2)、实现

设计四个任务: start_task、 eventsetbit_task、 eventgroup_task 和 eventquery_task 

这四个任务的任务功能如下:

start_task:用来创建其他三个任务和事件标志组。

eventsetbit_task: 读取按键值,根据不同的按键值将事件标志组中相应的事件位置 1,用来
模拟事件的发生。

eventgroup_task:同时等待事件标志组中的多个事件位,当这些事件位都置 1 的话就执行相应的处理,例程中是刷新 LCD 指定区域的背景色。

eventquery_task:查询事件组的值,也就是各个事件位的值。获取到事件组值以后就将其显示到 LCD 上,并且也通过串口打印出来。

实验中还创建了一个事件标志组: EventGroupHandler,实验中用到了这个事件标志组的三个事件位,分别位 bit0, bit1 和 bit2。

实验中会用到 3 个按键: KEY0、 KEY1 和 KEY2,其中按键 KEY1 和 KEY2 为普通的输入模式。按键 KEY0 为中断输入模式,KEY0 用来演示如何在中断服务程序调用事件标志组的 API函数。

3)、工程

●任务设置

#define START_TASK_PRIO 1 //任务优先级
#define START_STK_SIZE 256 //任务堆栈大小

TaskHandle_t StartTask_Handler; //任务句柄
void start_task(void *pvParameters); //任务函数

#define EVENTSETBIT_TASK_PRIO 2 //任务优先级
#define EVENTSETBIT_STK_SIZE 256 //任务堆栈大小

TaskHandle_t EventSetBit_Handler; //任务句柄
void eventsetbit_task(void *pvParameters); //任务函数

#define EVENTGROUP_TASK_PRIO 3 //任务优先级
#define EVENTGROUP_STK_SIZE 256 //任务堆栈大小

TaskHandle_t EventGroupTask_Handler; //任务句柄
void eventgroup_task(void *pvParameters); //任务函数

#define EVENTQUERY_TASK_PRIO 4 //任务优先级
#define EVENTQUERY_STK_SIZE 256 //任务堆栈大小

TaskHandle_t EventQueryTask_Handler; //任务句柄
void eventquery_task(void *pvParameters); //任务函数

EventGroupHandle_t EventGroupHandler; //事件标志组句柄
#define EVENTBIT_0 (1<<0) //事件位
#define EVENTBIT_1 (1<<1)
#define EVENTBIT_2 (1<<2)
#define EVENTBIT_ALL (EVENTBIT_0|EVENTBIT_1|EVENTBIT_2)

//LCD 刷屏时使用的颜色
int lcd_discolor[14]={ WHITE, BLACK, BLUE, BRED,
GRED, GBLUE, RED, MAGENTA,
GREEN, CYAN, YELLOW, BROWN,
BRRED, GRAY };

● main()函数

int main(void)
{
  HAL_Init(); //初始化 HAL 库
  Stm32_Clock_Init(360,25,2,8); //设置时钟,180Mhz
  delay_init(180); //初始化延时函数
  uart_init(115200); //初始化串口
  LED_Init(); //初始化 LED
  KEY_Init(); //初始化按键
  PCF8574_Init(); //初始化 PCF8574
  EXTI_Init(); //初始化外部中断
  SDRAM_Init(); //初始化 SDRAM
  LCD_Init(); //初始化 LCD
  my_mem_init(SRAMIN); //初始化内部内存池
  POINT_COLOR = RED;
  LCD_ShowString(30,10,200,16,16,"Apollo STM32F4/F7");
  LCD_ShowString(30,30,200,16,16,"FreeRTOS Examp 16-1");
  LCD_ShowString(30,50,200,16,16,"Event Group");
  LCD_ShowString(30,70,200,16,16,"ATOM@ALIENTEK");
  LCD_ShowString(30,90,200,16,16,"2016/11/11");
  POINT_COLOR = BLACK;
  LCD_DrawRectangle(5,130,234,314); //画矩形


  POINT_COLOR = BLUE;
  LCD_ShowString(30,110,220,16,16,"Event Group Value:0");
  //创建开始任务
  xTaskCreate((TaskFunction_t )start_task, //任务函数
  (const char* )"start_task", //任务名称
  (uint16_t )START_STK_SIZE, //任务堆栈大小
  (void* )NULL, //传递给任务函数的参数
  (UBaseType_t )START_TASK_PRIO, //任务优先级
  (TaskHandle_t* )&StartTask_Handler); //任务句柄
  vTaskStartScheduler(); //开启任务调度
}


● 任务函数
//开始任务任务函数
void start_task(void *pvParameters)
{
  taskENTER_CRITICAL(); //进入临界区
  //创建事件标志组
  EventGroupHandler=xEventGroupCreate(); //创建事件标志组 (1)
  //创建设置事件位的任务
  xTaskCreate((TaskFunction_t )eventsetbit_task,
              (const char* )"eventsetbit_task",
              (uint16_t )EVENTSETBIT_STK_SIZE,
              (void* )NULL,
              (UBaseType_t )EVENTSETBIT_TASK_PRIO,
              (TaskHandle_t* )&EventSetBit_Handler);
  //创建事件标志组处理任务
  xTaskCreate((TaskFunction_t )eventgroup_task,
              (const char* )"eventgroup_task",
              (uint16_t )EVENTGROUP_STK_SIZE,
              (void* )NULL,
              (UBaseType_t )EVENTGROUP_TASK_PRIO,
              (TaskHandle_t* )&EventGroupTask_Handler);
  //创建事件标志组查询任务
  xTaskCreate((TaskFunction_t )eventquery_task,
              (const char* )"eventquery_task",
              (uint16_t )EVENTQUERY_STK_SIZE,
              (void* )NULL,
              (UBaseType_t )EVENTQUERY_TASK_PRIO,
              (TaskHandle_t* )&EventQueryTask_Handler);
  vTaskDelete(StartTask_Handler); //删除开始任务
  taskEXIT_CRITICAL(); //退出临界区
}


//设置事件位的任务
void eventsetbit_task(void *pvParameters)
{
  u8 key;
  while(1)
  {
    if(EventGroupHandler!=NULL)
    {
      key=KEY_Scan(0);
      switch(key)
      {
        case KEY1_PRES:
          xEventGroupSetBits(EventGroupHandler,EVENTBIT_1); (2)
        break;
        case KEY2_PRES:
          xEventGroupSetBits(EventGroupHandler,EVENTBIT_2); (3)
        break;
       }
}
}
  
//事件标志组处理任务
void eventgroup_task(void *pvParameters)
{
  u8 num;
  EventBits_t EventValue;
  while(1)
  {
    if(EventGroupHandler!=NULL)
    {
      //等待事件组中的相应事件位
      EventValue=xEventGroupWaitBits((EventGroupHandle_t )EventGroupHandler, (4)
      (EventBits_t ) EVENTBIT_ALL,
      (BaseType_t )pdTRUE,
      (BaseType_t )pdTRUE,
      (TickType_t )portMAX_DELAY);
      printf("事件标志组的值:%drn",EventValue);ALIENTEK 阿波罗 FreeRTOS 开发教程
      303
      STM32F429 FreeRTOS 开发手册
      LCD_ShowxNum(174,110,EventValue,1,16,0);
      num++;
      LED1=!LED1;
      LCD_Fill(6,131,233,313,lcd_discolor[num%14]);
}
    else
    {
      vTaskDelay(10); //延时 10ms,也就是 10 个时钟节拍
    }
   }
}


//事件查询任务
void eventquery_task(void *pvParameters)
{
  u8 num=0;
  EventBits_t NewValue,LastValue;
  while(1)
  {
    if(EventGroupHandler!=NULL)
    {
      NewValue=xEventGroupGetBits(EventGroupHandler); //获取事件组的 (5)
      if(NewValue!=LastValue)
      {
        LastValue=NewValue;
        printf("事件标志组的值:%drn",NewValue);
        LCD_ShowxNum(174,110,NewValue,1,16,0);
      }
      num++;
      if(num==0) //每 500msLED0 闪烁一次
      {
        num=0;
        LED0=!LED0;
      }
        vTaskDelay(50); //延时 50ms,也就是 50 个时钟节拍
    }
}


(1)、首先调用函数 xEventGroupCreate()创建一个事件标志组 EventGroupHandler。


(2)、按下 KEY1 键的时候就调用函数 xEventGroupSetBits()将事件标志组的 bit1 置 1。


(3)、按下 KEY2 键的时候调用函数 xEventGroupSetBits()将事件标志组的 bit2 值 1。


(4)、调用函数 xEventGroupWaitBits()同时等待事件标志组的 bit0, bit1 和 bit2,只有当这三个事件都置 1 的时候才会执行任务中的其他代码。


(5)、调用函数 xEventGroupGetBits()查询事件标志组 EventGroupHandler 的值变化,通过查看这些值的变化就可以分析出当前哪个事件位置 1 了。


● 中断初始化及处理过程


事件标志组 EventGroupHandler的事件位 bit0 是通过 KEY0 的外部中断服务函数来设置的,


注意中断优先级的设置!本例程的中断优先级设置如下:
//中断线 3-PH3
HAL_NVIC_SetPriority(EXTI3_IRQn,6,0); //抢占优先级为 6,子优先级为 0
HAL_NVIC_EnableIRQ(EXTI3_IRQn); //使能中断线 3
KEY0 的外部中断服务函数如下:
//事件标志组句柄
extern EventGroupHandle_t EventGroupHandler;
//中断服务函数
void EXTI3_IRQHandler(void)
{
  BaseType_t Result,xHigherPriorityTaskWoken;
  delay_xms(50); //消抖
  if(KEY0==0)
  {
    Result=xEventGroupSetBitsFromISR(EventGroupHandler,EVENTBIT_0, (1)
    &xHigherPriorityTaskWoken);
    if(Result!=pdFAIL)
    {
      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    }
  }
  __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_3); //清除中断标志位
}


(1)、 在中断服务函数中通过调用 xEventGroupSetBitsFromISR()来将事件标志组的事件位
bit0 置 1。

[1] [2]
关键字:STM32  ucosIII  freeRTOS  事件标志组 引用地址:STM32 系统级开发之 ucosIII 或 freeRTOS 事件标志组详解

上一篇:STM32Cube 工具说明和安装浅析
下一篇:STM32 IIC 详解 之 stm32 IIC 从机模式(中断方式收发数据)

推荐阅读最新更新时间:2024-11-05 21:52

基于STM32实现简单呼吸灯
01第一步; 打开STM32cubeMX软件(我使用的版本是4.18),新建项目,选择STM32F103R6Tx; 02第二步:然后对TIM1或者TIM2进行设置,本次我们使用TIM1做PWM实验,选择内部时钟作为定时器时钟; TIM2定时器的内部时钟 03第三步、STM32时钟配置保持默认不变,因为我们用proteus仿真来验证本次实验; 04第四步、然后对TIM1进行定时中断时间的设置 ,如下图所示: 05第五步:生成代码 06第六步:在keil中,修改代码。 添加两个变量 主函数中的代码 07第七步:在虚拟仿真软件proteus 8.8 版本,绘制出stm32f103的图纸,然后添加刚刚产生的hex文件
[单片机]
基于<font color='red'>STM32</font>实现简单呼吸灯
STM32 位段详解
1 定义 首先需要明确下,位段,位带和别名区这三个名词 位段:STM32用户参考手册使用的名字 位带:CortexM3参考手册使用的 别名区:地址总线上用来位访问地址区域, 所以说,位段和位带是一个意思,是不同手册的不同叫法。 由上述的名词解释得知,位带功能并不是STM32独有的,是CortexM3的功能(CortexM4也有这样的功能)。MCS51有位操作,以一位(bit)为数据对象的操作,MCS51可以简单的将P1口的第2位独立操作:P1.2=0;P1.2=1 ;这样就把P1口的第三个脚(bit2)置0置1。而STM32的位段、位带别名区最重要的就为了实现这样的功能。 2 位带操作 2.1 范围 位带
[单片机]
<font color='red'>STM32</font> 位段详解
STM32 USB学习笔记7
主机环境:Windows 7 SP1 开发环境:MDK5.14 目标板:STM32F103C8T6 开发库:STM32F1Cube库和STM32_USB_Device_Library 现在来分析一下USB器件库中核心文件usbd_ioreq,相对于usbd_ctlreq来说usbd_ioreq文件的代码量要小一些。该文件的主要作用是为控制端点提供IO请求API,更准确来说是为默认的控制端点0提供的API,该文件只有7个函数体,首先看第一个函数发送数据函数 /** * @brief USBD_CtlSendData * send data on the ctl pipe * @param pdev: d
[单片机]
NAVYA和法雷奥合作开发安全系统 以实现L4自动驾驶汽车商业化
据外媒报道,自动驾驶系统供应商NAVYA与汽车供应商法雷奥(VALEO)宣布进一步扩展合作,参与法国政府第四期投资项目,并结合双方专业知识,以开发和设计未来的“自动驾驶车辆故障安全系统(AVFS)” (图片来源:NAVYA) NAVYA首席执行官Sophie Desormière表示:“该独特项目旨在保障自动驾驶交通解决方案运营安全,重点设计对L4级自动驾驶汽车商业化至关重要的故障安全电子系统。” 该第四期未来投资计划(Programme d'Investissements d'Avenir,PIA4)由法国政府提供支持,旨在为处于创新开发各阶段的公司提供支持。因此NAVYA与法雷奥与全球汽车制造商一
[汽车电子]
NAVYA和法雷奥合作<font color='red'>开发</font>安全<font color='red'>系统</font> 以实现L4<font color='red'>级</font>自动驾驶汽车商业化
STM32 PWM占空比和信号周期的控制因素分析
前言 博文基于STM32F103ZET6芯片,标准固件库3.5.0和MDK5编写; 博文并不讨论PWM的基础知识,而是直接去谈对PWM灵活的控制问题;想全面了解的可以看看我写的专门针对PWM的博客: https://blog.csdn.net/wuyuzun/article/details/72851940 如有不足之处还行多多指教; PWM的两个重要参数 占空比:0~100%; 信号周期:PWM信号里一个高电平和一个低电平的时间和; 对这两个参数的设置是配置PWM输出的核心也是今天要讨论的主要任务; 是什么决定上面这两个重要参数 首先要明白一个知识:STM32的定时器输出的PWM的占空比由定时器的三个寄存器决定,分别是 定时
[单片机]
<font color='red'>STM32</font> PWM占空比和信号周期的控制因素分析
ubuntu下建立stm32开发环境:程序烧录openocd+openjtag
上一篇博客写了在ubuntu下,建立stm32开发环境,程序也已经编译好生成main.bin,接下来就是要把该文件烧录到stm32上.在Linux下给arm烧录程序主要使用openocd,这个软件开源,而且支持众多芯片,从ARM9到A8都可以,当然STM32也可以.支持的JTAG工具也很多,JLINK ST-LINK OSBDM都可以,我这正好有一个openjtag基于FT2232C的,也是被支持的. 参考: How-to manual Installing a toolchain for Cortex-M3/STM32 on Ubuntu by Peter Seng 博主最近在电脑上自建了博客,以后会更多的用那个了,欢
[单片机]
STM32_TIM定时-中断
今天讲解STM32F103定时器定时-中断功能,在昨天定时器延时的软件工程上添加TIM3定时的功能,自己也可以试着将昨天的工程添加修改得到。 今天的软件工程下载地址(360云盘): https://yunpan.cn/cPnJ9KYcXbPsP 访问密码 acd8 工程现象:间隔(定时器定时)500ms LED变化一次, 并且串口打印 STM32F103ZE有8个定时器(TIM1 – TIM8), 改工程以TIM3定时为例。 STM32F10x的资料可以在我360云盘下载: https://yunpan.cn/crBUdUGdYKam2 访问密码 ca90 关于TIM延时,我把重要的几点在下面分别讲述,工程中
[单片机]
STM32_TIM定时-中断
STM32之五外部中断(下)
通过对外部中断理论的些许理解,这次我们利用两个按键key1和key2来控制led1和led2的亮灭,按key1进入key1的中断,控制对应的led亮灭,按key2进入key2的中断,控制相应的led的亮灭 同样,涉及到中断,我们要建立两个文件,exti.c及exti.h,首先来看看exti.c 用到中断,自然我们要用到stm32f10x_it.c函数,将中断响应函数放在里面,打开这个文件你会发现里面只是给出了部分中断函数,找来找去也没有我们需要的中断函数体,这时需要我们自己添加函数体,看下我添加的函数体: 但是这个函数名可不是随便起的,具体每个中断函数体的函数名怎么书写,我们可以打开startup_stm32f10x_hd.
[单片机]
<font color='red'>STM32</font>之五外部中断(下)
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved