当前位置:首页 > 技术知识 > 正文内容

如何优雅地使用嵌入式事件标志组?

maynowei10个月前 (08-03)技术知识159

事件标志组

嵌入式事件标志组是一种在嵌入式系统中广泛使用的同步机制,主要用于实现多任务间的同步与通信。

事件标志组是一组事件标志位的集合,每个位代表一个事件是否发生。它允许任务等待特定的事件发生,当事件发生时,相关任务将被唤醒并执行相应的操作。

特点

  • 灵活性:用户可以根据需要自定义每个位事件的含义,如bit0表示按键是否按下。支持一对多、多对多的同步模式,即一个任务可以等待多个事件的发生,也可以是多个任务同步多个事件。
  • 高效性:使用位操作,效率高,占用资源少。
  • 扩展性:虽然常用的是16位或32位无符号的数据类型来存储事件标志,但其中的高8位可能用作控制信息,低24位用作存储事件标志,因此可以存储多个事件标志。

工作原理

  • 等待事件:任务通过调用相应的API函数(如FreeRTOS中的xEventGroupWaitBits)来等待一个或多个事件标志位的发生。可以设置等待条件,如等待所有指定的事件标志位都为1,或等待其中任意一个事件标志位为1。
  • 触发事件:当事件发生时,通过调用相应的API函数(如FreeRTOS中的xEventGroupSetBits)来设置相应的事件标志位为1,从而触发等待该事件的任务。唤醒所有符合条件的任务,类似于“广播”的作用。
  • 执行任务:被唤醒的任务根据事件标志位的状态执行相应的操作,并可以选择是否清除事件标志位。

应用场景

  • 多任务同步:在需要多个任务协同工作的场景中,可以使用事件标志组来同步这些任务,但无数据传输。
  • 中断处理:在中断服务程序中设置事件标志位,以通知主任务或其他任务进行相应的处理。
  • 状态监控:用于监控系统的各种状态,如设备是否就绪、数据是否到达等。

例子:

在嵌入式系统中,处理USB数据的同步发送通常涉及多线程编程,并使用适当的同步机制来确保数据的一致性和完整性。在这种情况下,可以使用事件标志和消息队列来协调一个生产线程(生成USB数据)和一个消费线程(发送USB数据)。

设计思路:

  • 消息队列:用于存储从生产线程到消费线程的数据。每个数据项可能是一个指向USB数据包缓冲区的指针或包含数据包信息的结构体。
  • 事件标志:用于通知消费线程有新的数据可供处理,或者当队列为空时通知生产线程暂停生产。
  • 互斥锁:保护消息队列和事件标志的访问,防止竞态条件。

实现步骤:

1. 定义消息队列和事件标志

  • 使用RTOS提供的API来创建消息队列和事件标志。
  • 消息队列应能够存储指向USB数据包的指针或相关结构体。

2. 生产线程

生产线程负责生成USB数据,并将其放入消息队列中。

void producer_thread(void *arg) 
{  
    while (1) 
    {  
        // 生成USB数据包  
        usb_packet_t *packet = generate_usb_packet();  
  
        // 锁定互斥锁  
        rtos_mutex_lock(&mutex);  
  
        // 将数据包放入队列  
        if (rtos_queue_send(&usb_queue, &packet, portMAX_DELAY) == pdPASS) 
        {  
            // 通知消费线程有新数据  
            rtos_event_group_set_bits(&event_group, EVENT_BIT_DATA_READY);  
        }  
  
        // 解锁互斥锁  
        rtos_mutex_unlock(&mutex);  
  
        // 等待一段时间或根据其他条件继续生成数据  
        vTaskDelay(pdMS_TO_TICKS(100));  
    }  
}

3. 消费线程

消费线程从消息队列中取出数据,并发送USB数据包。

void consumer_thread(void *arg) 
{  
    usb_packet_t *packet;  
  
    while (1) 
    {  
        // 等待数据就绪事件  
        EventBits_t uxBits = xEventGroupWaitBits(  
            &event_group,   
            EVENT_BIT_DATA_READY,   
            pdTRUE,   
            pdFALSE,   
            portMAX_DELAY  
        );  
  
        if (uxBits & EVENT_BIT_DATA_READY) 
        {  
            // 锁定互斥锁  
            rtos_mutex_lock(&mutex);  
  
            // 从队列接收数据包  
            if (rtos_queue_receive(&usb_queue, &packet, portMAX_DELAY) == pdPASS) 
            {  
                // 发送USB数据包  
                send_usb_packet(packet);  
  
                // 释放数据包(如果需要)  
                free_usb_packet(packet);  
            }  
  
            // 解锁互斥锁  
            rtos_mutex_unlock(&mutex);  
        }  
    }  
}

4. 初始化与启动

创建消息队列、事件标志和互斥锁,并启动生产者和消费者线程。

void app_main(void) 
{  
    // 初始化消息队列、事件标志和互斥锁  
    rtos_queue_create(&usb_queue, ...);  
    rtos_event_group_create(&event_group);  
    rtos_mutex_create(&mutex);  
  
    // 创建并启动生产者和消费者线程  
    xTaskCreate(producer_thread, "Producer", STACK_SIZE, NULL, PRIORITY, NULL);  
    xTaskCreate(consumer_thread, "Consumer", STACK_SIZE, NULL, PRIORITY, NULL);  
  
    // 其他初始化...  
}

注意事项

  • 在使用事件标志组时,需要注意避免竞态条件,确保任务间的同步与通信的正确性。
  • 合理安排事件标志位的数量和使用方式,避免资源浪费和效率低下。
  • 在设计系统时,应充分考虑任务间的依赖关系和同步需求,以选择合适的同步机制。

相关文章

Android主流UI开源库整理(android完整开源项目)

前言最近老大让我整理一份 Android主流UI开源库 的资料,以补充公司的Android知识库。由于对格式不做特别限制,于是打算用博客的形式记录下来,方便查看、防丢并且可以持续维护、不断更新。标题隐...

C语言编写多线程,什么时候要使用互斥锁?为什么要使用互斥锁?

在多线程编程中,当多个线程同时访问共享资源(如变量、文件等)时,会出现竞态条件(Race Condition)问题,导致程序的行为不可预测。为了避免这种问题,需要使用互斥锁来保护共享资源的访问。互斥锁...

Oracle 不是有效的导出文件,标头验证失败 解决方法

第一种:网上搜索到的大多解决方法是说导出文件时使用的Oracle版本不一致问题,需要修改dmp文件的版本号。如果确定版本号确实不一样,请自行搜索一下解决方法。第二种:备份dmp文件时,备份的语句可能使...

Oracle高级数据库特性揭秘:存储过程、触发器与权限管理

当谈论Oracle高级数据库特性时,存储过程和函数、触发器、权限管理和安全性以及数据库连接和远程访问是关键概念。下面我将为每个主题提供详细的解释,并附上高质量示例。存储过程和函数: 存储过程和函数是预...

面试官:说说Oracle数据库result cache的原理是什么?

概述前面已经用实验给大家介绍了Result Cache相关内容,今天主要讨论一下Oracle 11g Result Cache的深层原理。从参数看,Oracle提供了Client Result Cac...

Oracle数据库安装 | 步骤详细(oracle数据库怎么安装及配置)

部署环境系统:CentOS 7.1 (Redhat版本也可以)数据库:Oracle 11gR2 1.修改hosts文件1.1 查询主机名和IP地址1.2 修改/etc/hosts文件如下2.挂载操作系...