当前位置: 首页 > news >正文

新闻资讯网站模板网站后台怎么给图片做水印

新闻资讯网站模板,网站后台怎么给图片做水印,徐州市丰县建设局网站,四平做网站佳业首页目录 1.概念2.事件组的操作3.事件组相关函数3.1 创建3.2 删除3.3 设置事件3.4 等待事件3.5 同步点 4.示例4.1 等待事件4.2 同步点 5.内部源码5.1 结构体5.2 等待事件5.3 设置事件5.4 为什么不是关闭中断 疑问疑问1疑问2疑问3 1.概念 事件组可以简单地认为是一个整数#xff0… 目录 1.概念2.事件组的操作3.事件组相关函数3.1 创建3.2 删除3.3 设置事件3.4 等待事件3.5 同步点 4.示例4.1 等待事件4.2 同步点 5.内部源码5.1 结构体5.2 等待事件5.3 设置事件5.4 为什么不是关闭中断 疑问疑问1疑问2疑问3 1.概念 事件组可以简单地认为是一个整数每一位代表一个独立的事件。程序员可以为每一位事件赋予不同的含义例如 Bit0表示串口是否就绪Bit1表示按键是否被按下等等。 当某个位的值为1时表示该事件已发生为0则表示事件未发生。 事件组使用一个整数来存储所有事件。 该整数的高位部分8位或更多由内核保留供内部使用。 事件组可用的位数取决于配置参数 configUSE_16_BIT_TICKS 如果 configUSE_16_BIT_TICKS 为1则表示处理器使用16位计时器此时事件组是16位的低8位用于表示事件如果 configUSE_16_BIT_TICKS 为0则使用32位计时器事件组就是32位的其中低24位用于表示事件。 一个或多个任务、甚至 ISR 都可以设置写入事件位也可以读取这些事件。 不同任务可以同时等待同一个事件组中的不同位或组合位发生变化。 2.事件组的操作 唤醒行为广播效果 队列、信号量 当事件发生时通常只唤醒一个等待该资源的任务。事件组 当满足等待条件的事件发生时所有等待该条件的任务都会被唤醒。这种机制具有“广播”效果适合多个任务需要同时知道事件发生的情况。 事件清除消耗型与非消耗型 队列、信号量 数据或计数值被读取后就被消耗掉。事件组 当一个任务等待事件组时可以选择在获得事件后是否清除对应的事件位。也就是说任务获得事件时可以选择“保留”事件允许后续任务继续看到该事件或“清除”事件将事件位复位为0。 假设系统中有两个任务一个任务任务A等待“串口就绪”事件Bit0另一个任务任务B等待“按键按下”事件Bit1。 当外部中断或另一个任务检测到串口就绪时会设置事件组中 Bit0 为1任务A立即被唤醒检查到事件成立后可以选择清除 Bit0 或保留它。同样当检测到按键按下时会设置 Bit1 为1任务B被唤醒并执行相应处理。 此外如果有任务需要同时等待这两个事件中的任一一个发生则可以用“或”关系等待如果要求必须同时满足两个事件才能继续操作则设置为“与”关系等待。 3.事件组相关函数 3.1 创建 动态创建 EventGroupHandle_t xEventGroupCreate(void);此函数在内部会动态分配内存来存储事件组的数据结构。创建成功后返回一个非 NULL 的事件组句柄否则返回 NULL。使用动态创建的事件组适合内存充足的系统且方便使用。 静态创建 EventGroupHandle_t xEventGroupCreateStatic(StaticEventGroup_t *pxEventGroupBuffer);静态创建要求用户先定义一个 StaticEventGroup_t 类型的变量内存缓冲区并将其地址传入。该函数不使用动态内存分配而是利用用户提供的内存来存储事件组结构。成功返回非 NULL 的句柄适用于对内存管理要求严格或不希望使用动态分配的系统。 3.2 删除 当不再需要事件组时可以通过删除事件组来回收内存仅适用于动态创建的事件组。 void vEventGroupDelete(EventGroupHandle_t xEventGroup);删除指定的事件组释放由动态分配内存创建的事件组所占用的资源。静态创建的事件组的内存由用户管理因此调用此函数不会释放用户提供的内存。 3.3 设置事件 设置事件组的操作即是将某个位或多个位置为1表示对应的事件已发生。事件的写操作有两种版本任务中使用和 ISR 中使用。 在任务中设置事件 EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet);参数 xEventGroup 指定要设置的事件组。参数 uxBitsToSet 指定要设置的位例如 0x15 表示设置 bit4、bit2、bit0。该函数将事件组中指定的位设置为1并返回设置之前的事件组值由于其他任务可能同时修改返回值通常用作调试。设置操作会唤醒所有等待这些位或这些位组合的任务具有广播效果。 在ISR中设置事件 BaseType_t xEventGroupSetBitsFromISR(EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet,BaseType_t *pxHigherPriorityTaskWoken);用于在中断服务例程中设置事件组中的位。与任务中使用的版本类似但由于在 ISR 中不允许直接阻塞或做较复杂操作此函数不是直接设置事件组而是通过向 FreeRTOS 后台任务daemon task发送队列数据来间接设置事件组。参数 pxHigherPriorityTaskWoken 用于指示是否有更高优先级任务因该设置操作而进入就绪状态。如果后台任务的优先级高于当前中断任务则该变量会被设置为 pdTRUE。返回值为 pdPASS 表示操作成功。 3.4 等待事件 任务可以使用事件组等待某一位或多位事件发生。等待函数允许你指定等待的条件“与”或“或”关系、是否在退出时清除事件位以及等待的最长时间。 EventBits_t xEventGroupWaitBits(EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToWaitFor,const BaseType_t xClearOnExit,const BaseType_t xWaitForAllBits,TickType_t xTicksToWait);xEventGroup要等待的事件组句柄。 uxBitsToWaitFor指定位掩码指定任务需要等待哪些位。例如0x15 表示任务等待 bit0、bit2 和 bit4。 xClearOnExit pdTRUE在退出等待前自动清除 uxBitsToWaitFor 指定的位。pdFALSE退出前不清除。清除操作在函数内部以原子方式完成可以避免等待和清除之间被其他任务打断。 xWaitForAllBits pdTRUE表示任务需要等待 uxBitsToWaitFor 指定的所有位均为1。pdFALSE表示只要其中任意一位为1即可。 xTicksToWait如果所等待的事件未发生任务将阻塞等待指定的 Tick 数。可以设置为 0不阻塞立即返回当前事件组值。portMAX_DELAY无限等待。其他值阻塞指定 Tick 数通常用 pdMS_TO_TICKS() 转换为 Tick Count。 返回值 返回的是事件组当前的位值即在“非阻塞条件成立”时的事件组值如果超时则返回超时时刻的事件组值。 xEventGroupWaitBits返回前一定会清除事件吗 参数xClearOnExit为真以及成功等待事件时才会清除 3.5 同步点 事件组不仅可以用来等待单个或多个事件还可以用于实现多个任务的同步。所谓“同步点”指的是多个任务各自完成某个操作后需要等待其他任务也完成才能共同进入下一阶段。 EventBits_t xEventGroupSync(EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet,const EventBits_t uxBitsToWaitFor,TickType_t xTicksToWait);uxBitsToSet每个任务调用该函数时会将自己对应的事件位设置为1表示“我已经完成了我的部分”。uxBitsToWaitFor任务需要等待的其他任务所设置的位。只有当这些位满足条件时通常为所有任务都完成该函数才返回表示同步点达成。同步成功返回后事件组中这些位会被自动清除以便下次使用。xTicksToWait 指定了等待的最长时间超时则返回。 4.示例 4.1 等待事件 // 全局变量共享数据与句柄static int sum 0; // 全局变量用于累加计算由Task1更新 static int dec 0; // 全局变量用于累减计算由Task2更新static volatile int flagCalcEnd 0; // 标志计算是否结束本例中未使用可作扩展 static volatile int flagUARTused 0; // 标志UART是否使用本例中未使用static QueueHandle_t xQueueCalcHandle; // 队列句柄用于在任务间传递计算结果数据为int类型 static EventGroupHandle_t xEventGroupCalc; // 事件组句柄用于同步两个任务的事件事件组中不同位表示不同任务已完成操作/*-----------------------------------------------------------* Task1Function:* 任务1负责执行一个简单的累加操作计算完成后将计算结果发送到队列中* 同时设置事件组中的位0表示任务1的操作已完成。*/ void Task1Function(void * param) {volatile int i 0;while (1){// 累加操作从0到99999每次累加1更新全局变量sumfor (i 0; i 100000; i)sum;// 将计算结果sum通过队列发送出去参数0表示不阻塞如果队列已满则丢弃数据xQueueSend(xQueueCalcHandle, sum, 0);/* 设置事件组中的事件位0 (即Bit0) 为1表示Task1已完成其累加操作 */xEventGroupSetBits(xEventGroupCalc, (1 0));// 由于任务中没有延时因此此循环会连续不断执行// 在实际应用中可能需要添加vTaskDelay()避免CPU占用过高。} }/*-----------------------------------------------------------* Task2Function:* 任务2负责执行累减操作将结果发送到同一个队列中* 并设置事件组中的位1表示任务2的操作已完成。*/ void Task2Function(void * param) {volatile int i 0;while (1){// 累减操作从0到-99999每次递减1更新全局变量decfor (i 0; i 100000; i)dec--;// 将累减后的结果通过队列发送出去xQueueSend(xQueueCalcHandle, dec, 0);/* 设置事件组中的事件位1 (即Bit1) 为1表示Task2已完成其累减操作 */xEventGroupSetBits(xEventGroupCalc, (1 1));// 同样此处可添加适当延时以平衡任务执行频率} }/*-----------------------------------------------------------* Task3Function:* 任务3负责等待事件组中的两个事件都发生后再从队列中读取数据* 并打印出读取到的数据。** 等待条件同时等待事件组中Bit0和Bit1均为1 (xWaitForAllBits pdTRUE)* 清除模式等待成功后自动清除这两位 (xClearOnExit pdTRUE)* 阻塞时间无限等待 (portMAX_DELAY)*/ void Task3Function(void * param) {int val1, val2;while (1){/* 等待事件* 任务等待事件组中Bit0和Bit1同时为1。* 当两个事件都发生后会清除这两位因为xClearOnExit设置为pdTRUE。*/xEventGroupWaitBits(xEventGroupCalc, (1 0) | (1 1), pdTRUE, pdTRUE, portMAX_DELAY);// 从队列中读取数据首先读取Task1传送的累加结果xQueueReceive(xQueueCalcHandle, val1, 0);// 再读取Task2传送的累减结果xQueueReceive(xQueueCalcHandle, val2, 0);// 打印两任务传递的结果printf(val1 %d, val2 %d\r\n, val1, val2);} }/*-----------------------------------------------------------* main函数* 1. 硬件初始化* 2. 创建事件组与队列* 3. 创建三个任务Task1, Task2, Task3* 4. 启动调度器进入多任务调度状态*/ int main( void ) {TaskHandle_t xHandleTask1;#ifdef DEBUGdebug(); #endif// 硬件初始化用户需根据具体平台实现prvSetupHardware();printf(Hello, world!\r\n);/* 创建事件组用于同步Task1和Task2的操作事件 */xEventGroupCalc xEventGroupCreate();/* 创建队列长度为2每个数据项大小为int用于传递Task1和Task2的计算结果 */xQueueCalcHandle xQueueCreate(2, sizeof(int));if (xQueueCalcHandle NULL){printf(can not create queue\r\n);}// 创建任务Task1进行累加Task2进行累减Task3等待事件并打印数据xTaskCreate(Task1Function, Task1, 100, NULL, 1, xHandleTask1);xTaskCreate(Task2Function, Task2, 100, NULL, 1, NULL);xTaskCreate(Task3Function, Task3, 100, NULL, 1, NULL);// 启动调度器开始任务调度vTaskStartScheduler();// 如果调度器启动失败例如内存不足将执行到这里return 0; }事件组 在本例中用于同步两个任务的执行。Task1和Task2分别完成计算后通过事件组设置自己的事件位Task3等待两个事件同时满足后再从队列中读取数据并打印。队列 用于传递实际的计算结果数据确保数据不会丢失或混淆。 4.2 同步点 三个任务分别负责准备演示、设置设备和冲咖啡只有当三个任务都完成自己的准备工作后才能“开会”。任务之间通过 xEventGroupSync 来达到同步点。 #include FreeRTOS.h #include task.h #include event_groups.h #include stdio.h/*-----------------------------------------------------------* 模拟场景会议开始前的准备工作同步* 有三个任务* - 演示任务Presentation准备演示文稿* - 设备任务Equipment设置会议设备* - 咖啡任务Coffee冲咖啡** 每个任务完成自己工作后调用 xEventGroupSync() 将自己对应的事件位设置* 并等待其他任务也完成。只有所有任务都完成后* xEventGroupSync() 才返回然后各任务输出“会议开始”的信息。*-----------------------------------------------------------*//* 定义事件组中各个事件位 */ #define PRESENTATION (1 0) // Bit0演示任务完成 #define EQUIPMENT (1 1) // Bit1设备任务完成 #define COFFEE (1 2) // Bit2咖啡任务完成 #define ALL_EVENTS (PRESENTATION | EQUIPMENT | COFFEE)/* 事件组句柄 */ EventGroupHandle_t xMeetingEventGroup;/* 函数原型 */ static void vPresentationTask(void *pvParameters); static void vEquipmentTask(void *pvParameters); static void vCoffeeTask(void *pvParameters);int main(void) {/* 硬件初始化具体实现视平台而定 */prvSetupHardware();printf(System starting: Meeting preparation simulation...\r\n);/* 创建事件组 */xMeetingEventGroup xEventGroupCreate();if (xMeetingEventGroup ! NULL){/* 创建3个任务各自负责一项准备工作* 这里传入的参数是一个字符串用于打印任务名称*/xTaskCreate(vPresentationTask, PresentationTask, 1000, Presenter, 1, NULL);xTaskCreate(vEquipmentTask, EquipmentTask, 1000, Equipment, 1, NULL);xTaskCreate(vCoffeeTask, CoffeeTask, 1000, Barista, 1, NULL);/* 启动调度器 */vTaskStartScheduler();}else{/* 如果事件组创建失败则打印错误信息 */printf(Error: Unable to create event group!\r\n);}/* 如果程序运行到这里通常表示内存不足等严重错误 */return 0; }/*-----------------------------------------------------------* vPresentationTask:* 模拟准备演示文稿的任务* 每次准备完成后通过 xEventGroupSync() 设置 PRESENTATION 位* 并等待所有任务都完成ALL_EVENTS。* 当同步点成立后任务打印“会议开始”信息。*-----------------------------------------------------------*/ static void vPresentationTask(void *pvParameters) {const TickType_t xDelay pdMS_TO_TICKS(100UL); // 100毫秒延时int iteration 0;for (;;){/* 执行自己的工作准备演示文稿 */printf(%s: Preparing presentation, iteration %d...\r\n, (char *)pvParameters, iteration);vTaskDelay(xDelay);/* 到达同步点* 将 PRESENTATION 位设置为1并等待 ALL_EVENTS 中所有位都为1。* xClearOnExit设置为pdTRUE表示当等待成功后自动清除这些位。* 阻塞等待时间设置为 portMAX_DELAY表示一直等待直到所有任务都完成。*/xEventGroupSync(xMeetingEventGroup, PRESENTATION, ALL_EVENTS, portMAX_DELAY);/* 同步点成立所有任务都完成准备工作会议开始 */printf(%s: Presentation ready. Meeting is starting, iteration %d.\r\n, (char *)pvParameters, iteration);iteration;vTaskDelay(xDelay);} }/*-----------------------------------------------------------* vEquipmentTask:* 模拟设置会议设备的任务* 完成工作后设置 EQUIPMENT 位并等待所有任务同步后打印“会议开始”信息。*-----------------------------------------------------------*/ static void vEquipmentTask(void *pvParameters) {const TickType_t xDelay pdMS_TO_TICKS(100UL); // 100毫秒延时int iteration 0;for (;;){/* 执行自己的工作设置会议设备 */printf(%s: Setting up equipment, iteration %d...\r\n, (char *)pvParameters, iteration);vTaskDelay(xDelay);/* 到达同步点* 将 EQUIPMENT 位设置为1并等待 ALL_EVENTS 中所有位都为1* 自动清除等待位后继续执行。*/xEventGroupSync(xMeetingEventGroup, EQUIPMENT, ALL_EVENTS, portMAX_DELAY);/* 同步点成立会议开始 */printf(%s: Equipment ready. Meeting is starting, iteration %d.\r\n, (char *)pvParameters, iteration);iteration;vTaskDelay(xDelay);} }/*-----------------------------------------------------------* vCoffeeTask:* 模拟冲咖啡的任务* 完成工作后设置 COFFEE 位并等待所有任务同步后打印“会议开始”信息。*-----------------------------------------------------------*/ static void vCoffeeTask(void *pvParameters) {const TickType_t xDelay pdMS_TO_TICKS(100UL); // 100毫秒延时int iteration 0;for (;;){/* 执行自己的工作冲咖啡 */printf(%s: Brewing coffee, iteration %d...\r\n, (char *)pvParameters, iteration);vTaskDelay(xDelay);/* 到达同步点* 将 COFFEE 位设置为1并等待所有任务都完成ALL_EVENTS。*/xEventGroupSync(xMeetingEventGroup, COFFEE, ALL_EVENTS, portMAX_DELAY);/* 同步点成立会议开始 */printf(%s: Coffee ready. Meeting is starting, iteration %d.\r\n, (char *)pvParameters, iteration);iteration;vTaskDelay(xDelay);} }5.内部源码 5.1 结构体 /* 事件组结构体定义 */ typedef struct EventGroupDef_t {/* 当前事件位事件标志集合* 类型 EventBits_t 通常为 uint32_t最多支持 32 个独立事件位bit* 每个位表示一个事件状态如 0x00000001 表示位0被置位* 使用 xEventGroupSetBits() 设置位xEventGroupClearBits() 清除位* 任务通过 xEventGroupWaitBits() 等待特定位的组合* 示例等待位0和位1同时置位uxEventBits 0x03 0x03*/EventBits_t uxEventBits;/* 等待事件位的任务列表* 使用 FreeRTOS 的链表List_t管理所有因等待事件位而阻塞的任务* 当调用 xEventGroupSetBits() 时检查列表中的任务是否满足等待条件* 满足条件的任务会被唤醒并移至就绪队列*/List_t xTasksWaitingForBits;/* 跟踪和调试标识符仅在启用 configUSE_TRACE_FACILITY 时生效* 每个事件组创建时被分配唯一的编号便于调试工具如 Tracealyzer追踪* 通过 uxEventGroupGetNumber() 可获取此标识符*/#if ( configUSE_TRACE_FACILITY 1 )UBaseType_t uxEventGroupNumber;#endif/* 静态分配标记仅在同时启用静态和动态分配时生效* 若事件组通过 xEventGroupCreateStatic() 静态创建此字段设为 pdTRUE* 防止误用 vEventGroupDelete() 删除静态分配的事件组因其内存不可释放*/#if ( ( configSUPPORT_STATIC_ALLOCATION 1 ) ( configSUPPORT_DYNAMIC_ALLOCATION 1 ) )uint8_t ucStaticallyAllocated;#endif } EventGroup_t;5.2 等待事件 在之前的队列、信号量或者是互斥量实现互斥访问都是依靠关闭中断。事件组却不是这样而是靠关闭调度器。这意味着并不会在中断中去使用事件组 来看看等待事件发生的函数xEventGroupWaitBits EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToWaitFor,const BaseType_t xClearOnExit,const BaseType_t xWaitForAllBits,TickType_t xTicksToWait ) {/* 将通用事件组句柄转换为内部事件组结构指针 */EventGroup_t * pxEventBits xEventGroup;/* 用于存储最终返回的事件位值 */EventBits_t uxReturn;/* 用于记录调用者的控制要求如清除标志和“等待所有位”标志 */EventBits_t uxControlBits 0;BaseType_t xWaitConditionMet, xAlreadyYielded;BaseType_t xTimeoutOccurred pdFALSE;/* 断言检查* 1. xEventGroup 必须有效。* 2. 请求等待的位中不能包含内核使用的控制位 (eventEVENT_BITS_CONTROL_BYTES)。* 3. 至少有一位被请求。*/configASSERT( xEventGroup );configASSERT( ( uxBitsToWaitFor eventEVENT_BITS_CONTROL_BYTES ) 0 );configASSERT( uxBitsToWaitFor ! 0 );/* 如果调度器处于挂起状态则不允许阻塞等待 */#if ( ( INCLUDE_xTaskGetSchedulerState 1 ) || ( configUSE_TIMERS 1 ) ){configASSERT( !( ( xTaskGetSchedulerState() taskSCHEDULER_SUSPENDED ) ( xTicksToWait ! 0 ) ) );}#endif/* 暂停任务调度以便原子性操作不被其他任务打断 */vTaskSuspendAll();{/* 读取当前事件组中的事件位 */const EventBits_t uxCurrentEventBits pxEventBits-uxEventBits;/* 调用内部函数检查是否满足等待条件* 如果 xWaitForAllBits 为 pdTRUE则要求 uxCurrentEventBits 包含所有 uxBitsToWaitFor 指定的位* 否则只要求其中任一位被置位。*/xWaitConditionMet prvTestWaitCondition( uxCurrentEventBits, uxBitsToWaitFor, xWaitForAllBits );if( xWaitConditionMet ! pdFALSE ){/* 如果条件已经满足* 1. 直接返回当前的事件位。* 2. 同时将 xTicksToWait 设置为 0表示不需要阻塞等待。*/uxReturn uxCurrentEventBits;xTicksToWait ( TickType_t ) 0;/* 根据参数判断是否在返回前清除等待的位 */if( xClearOnExit ! pdFALSE ){pxEventBits-uxEventBits ~uxBitsToWaitFor;}else{mtCOVERAGE_TEST_MARKER();}}else if( xTicksToWait ( TickType_t ) 0 ){/* 如果等待条件不满足且不允许阻塞等待则* 返回当前事件位同时标记为超时未满足条件 */uxReturn uxCurrentEventBits;xTimeoutOccurred pdTRUE;}else{/* 如果条件不满足且允许阻塞等待则设置任务的等待行为** uxControlBits 用于记录以下调用者要求* 如果传入参数 xClearOnExit 为 pdTRUE则在任务解除阻塞时清除事件位。* 如果传入参数 xWaitForAllBits 为 pdTRUE则要求等待所有指定的位。*/if( xClearOnExit ! pdFALSE ){uxControlBits | eventCLEAR_EVENTS_ON_EXIT_BIT;}else{mtCOVERAGE_TEST_MARKER();}if( xWaitForAllBits ! pdFALSE ){uxControlBits | eventWAIT_FOR_ALL_BITS;}else{mtCOVERAGE_TEST_MARKER();}/* 将任务希望等待的事件位和控制标志通过按位或组合存储到任务的事件列表项中* 并将任务放入 pxEventBits-xTasksWaitingForBits 等待列表中同时指定最大等待时间。* 当这些事件位满足等待条件时内核会解除任务阻塞。 */vTaskPlaceOnUnorderedEventList( ( pxEventBits-xTasksWaitingForBits ),( uxBitsToWaitFor | uxControlBits ),xTicksToWait );/* 虽然在任务解除阻塞后 uxReturn 会被重新设置但为了防止某些编译器发出未初始化变量警告* 这里先将 uxReturn 初始化为 0 */uxReturn 0;/* 跟踪记录任务进入等待状态 */traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor );}}/* 恢复任务调度xAlreadyYielded 表示是否有任务因为恢复调度而已切换 */xAlreadyYielded xTaskResumeAll();/* 如果任务进入阻塞等待即 xTicksToWait 仍不为 0 */if( xTicksToWait ! ( TickType_t ) 0 ){/* 如果恢复调度后没有发生上下文切换则调用 yield 进行任务切换 */if( xAlreadyYielded pdFALSE ){portYIELD_WITHIN_API();}else{mtCOVERAGE_TEST_MARKER();}/* 当任务解除阻塞后等待的事件位会存储在任务的事件列表项中* 调用 uxTaskResetEventItemValue() 获取解除阻塞时设置的事件位。* 此时可能是因为指定的事件位被置位而解除阻塞也可能是因为等待超时解除阻塞。 */uxReturn uxTaskResetEventItemValue();/* 检查返回值中是否包含 eventUNBLOCKED_DUE_TO_BIT_SET 标志* 如果没有该标志则说明任务是因超时而解除阻塞 */if( ( uxReturn eventUNBLOCKED_DUE_TO_BIT_SET ) ( EventBits_t ) 0 ){/* 进入临界区保护对事件组的访问 */taskENTER_CRITICAL();{/* 将返回值设置为当前的事件组事件位 */uxReturn pxEventBits-uxEventBits;/* 由于任务解除阻塞后事件组的位可能已被更新* 再次检查等待条件如果满足则根据 xClearOnExit 参数决定是否清除事件位 */if( prvTestWaitCondition( uxReturn, uxBitsToWaitFor, xWaitForAllBits ) ! pdFALSE ){if( xClearOnExit ! pdFALSE ){pxEventBits-uxEventBits ~uxBitsToWaitFor;}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}/* 标记此次解除阻塞是由于超时而非事件位被设置 */xTimeoutOccurred pdTRUE;}taskEXIT_CRITICAL();}else{/* 如果任务解除阻塞原因是事件位被置位则无需超时处理 */}/* 清除返回值中可能设置的控制位确保返回值只包含实际的事件位 */uxReturn ~eventEVENT_BITS_CONTROL_BYTES;}/* 跟踪记录等待结束事件传入事件组句柄、等待的位和是否超时 */traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred );/* 防止当跟踪宏未使用时产生编译器警告 */( void ) xTimeoutOccurred;/* 返回最终的事件位表示任务等待结束时事件组的状态 */return uxReturn; }无非就是内部调用prvTestWaitCondition检查是否满足等待条件。 然后根据其返回值判断 不满足的话根据用户要求来决定是否阻塞如果阻塞的话就调用vTaskPlaceOnUnorderedEventList将当前任务放到任务等待链表当中其实现如下已经标注了详细的代码 void vTaskPlaceOnUnorderedEventList( List_t * pxEventList,const TickType_t xItemValue,const TickType_t xTicksToWait ) {/* 1. 检查传入的事件列表指针是否有效 */configASSERT( pxEventList );/* 2. 断言要求调度器必须处于挂起状态。* 这保证了本函数在调用期间不会发生任务切换* 从而确保对当前任务的事件列表项和事件列表的操作具有原子性。* 该函数主要用于事件组实现要求在调度器挂起时调用。 */configASSERT( uxSchedulerSuspended ! 0 );/* 3. 设置当前任务的事件列表项的值。* 将传入的 xItemValue 与标志 taskEVENT_LIST_ITEM_VALUE_IN_USE 进行按位或运算* 表示该事件列表项正被使用并存储指定的值。* 注意这里使用 pxCurrentTCB-xEventListItem代表当前任务控制块TCB的事件列表项* 并且由于当前任务处于阻塞状态其他中断不会访问其事件列表项。 */listSET_LIST_ITEM_VALUE( ( pxCurrentTCB-xEventListItem ), xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE );/* 4. 将当前任务的事件列表项插入到指定的事件列表pxEventList的末尾。* 这样任务就被放入等待列表中等待相关事件发生。* 由于该事件列表属于事件组实现且中断不会直接访问事件组* 所以这里的插入操作是安全的。 */listINSERT_END( pxEventList, ( pxCurrentTCB-xEventListItem ) );/* 5. 将当前任务放入延时列表中等待 xTicksToWait 指定的时钟节拍数。* 该函数 prvAddCurrentTaskToDelayedList() 将当前任务设置为延时状态* 在超时时间到达或等待的事件条件满足时将任务从延时列表中移除。* 参数 pdTRUE 通常表示任务阻塞后任务状态被标记为等待事件。 */prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); }xEventGroupWaitBits函数的下半部分则是一些解除阻塞后的一些处理分两者情况 超时的话事件组的位可能已被更新再次检查等待条件如果满足则根据 xClearOnExit 参数决定是否清除事件位不超时而是被xEventGroupSetBits设置了等待位满足条件而退出阻塞的话则不做处理 5.3 设置事件 来看看xEventGroupSetBits是怎么去设置事件让阻塞等待事件的任务被唤醒进行运行。 其实没看之前也能猜一下是不是也是将等待事件的任务从任务等待链表中取出放到就绪任务等待链表呢 EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet ) {/* 定义用于遍历等待任务的列表项指针 */ListItem_t * pxListItem, * pxNext;/* 定义事件列表的结束标记指针用于判断遍历是否结束 */ListItem_t const * pxListEnd;/* 定义指向事件组中等待任务列表的指针 */List_t const * pxList;/* 记录待清除的事件位初始为 0 */EventBits_t uxBitsToClear 0;/* 存储每个任务等待的事件位不包含控制位 */EventBits_t uxBitsWaitedFor;/* 存储每个任务等待条件中的控制位如等待全部位、退出时清除位 */EventBits_t uxControlBits;/* 将传入的事件组句柄转换为内部使用的事件组结构指针 */EventGroup_t * pxEventBits xEventGroup;/* 标记是否找到满足等待条件的任务 */BaseType_t xMatchFound pdFALSE;/* 断言检查* 1. xEventGroup 必须有效。* 2. 要设置的位不能包含内核保留的控制位eventEVENT_BITS_CONTROL_BYTES。*/configASSERT( xEventGroup );configASSERT( ( uxBitsToSet eventEVENT_BITS_CONTROL_BYTES ) 0 );/* 获取等待该事件组的任务列表 */pxList ( pxEventBits-xTasksWaitingForBits );/* 获取该列表的结束标记用于遍历判断 */pxListEnd listGET_END_MARKER( pxList ); /* 此处使用 mini list 作为结束标记节省内存 *//* 暂停任务调度确保下面对事件组状态的修改原子性 */vTaskSuspendAll();{/* 跟踪记录事件组设置位操作用于调试或性能分析 */traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet );/* 从事件组等待列表的头部开始遍历 */pxListItem listGET_HEAD_ENTRY( pxList );/* 1. 设置指定的事件位* 使用按位或操作将 uxBitsToSet 加入当前事件组的事件位中 */pxEventBits-uxEventBits | uxBitsToSet;/* 2. 遍历等待列表检查是否有任务等待的事件条件已满足 */while( pxListItem ! pxListEnd ){/* 保存下一个列表项因为当前列表项可能被移除 */pxNext listGET_NEXT( pxListItem );/* 取得该等待任务所要求的事件位和控制信息 */uxBitsWaitedFor listGET_LIST_ITEM_VALUE( pxListItem );/* 初始化匹配标志为未匹配 */xMatchFound pdFALSE;/* 3. 分离等待的事件位和控制位* 控制位在 eventEVENT_BITS_CONTROL_BYTES 中包含等待全部位和退出时清除位等标志。* 剩下的位就是该任务真正等待的事件位。 */uxControlBits uxBitsWaitedFor eventEVENT_BITS_CONTROL_BYTES;uxBitsWaitedFor ~eventEVENT_BITS_CONTROL_BYTES;/* 4. 判断当前事件组的事件位是否满足任务等待条件* 如果任务不要求等待所有位xWaitForAllBits pdFALSE只需有任一位匹配即可。* 如果任务要求等待所有位xWaitForAllBits pdTRUE则必须所有位都匹配。 */if( ( uxControlBits eventWAIT_FOR_ALL_BITS ) ( EventBits_t ) 0 ){/* 如果任一位匹配则满足条件 */if( ( uxBitsWaitedFor pxEventBits-uxEventBits ) ! ( EventBits_t ) 0 ){xMatchFound pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}else if( ( uxBitsWaitedFor pxEventBits-uxEventBits ) uxBitsWaitedFor ){/* 如果要求所有位匹配且当前事件位包含所有这些位则满足条件 */xMatchFound pdTRUE;}else{/* 等待条件不满足对于要求所有位匹配的情况并非所有等待位都已置位 */}/* 5. 如果任务等待的条件满足则进行解除阻塞操作 */if( xMatchFound ! pdFALSE ){/* 判断是否在任务解除阻塞时需要清除等待的位* 如果控制位中设置了 eventCLEAR_EVENTS_ON_EXIT_BIT则需要在退出时清除这些位 */if( ( uxControlBits eventCLEAR_EVENTS_ON_EXIT_BIT ) ! ( EventBits_t ) 0 ){/* 累加这些等待的位到待清除变量中 */uxBitsToClear | uxBitsWaitedFor;}else{mtCOVERAGE_TEST_MARKER();}/* 将当前任务的等待条件以及当前事件组的事件位状态写入任务的事件列表项中* 并通过设置 eventUNBLOCKED_DUE_TO_BIT_SET 标志表明该任务解除阻塞的原因是等待位匹配成功* 而非超时。随后调用 vTaskRemoveFromUnorderedEventList 将该任务从等待列表中移除* 并将任务放入就绪或待处理列表中。 */vTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits-uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET );}/* 6. 继续遍历下一个等待任务。* 注意直接使用保存的 pxNext 而不是当前 pxListItem-pxNext* 因为当前列表项可能已经从等待列表中移除。 */pxListItem pxNext;}/* 7. 在解除阻塞任务后如果有任务要求在退出时清除它们等待的位* 则将这些位从事件组中清除。 */pxEventBits-uxEventBits ~uxBitsToClear;}/* 恢复任务调度 */( void ) xTaskResumeAll();/* 返回当前事件组中的事件位状态 */return pxEventBits-uxEventBits; }可以看出其实也是差不多的。 5.4 为什么不是关闭中断 因为中断是不会使用到事件组的假设使用事件组处理事件的时间是无法确定的因为是可以设置多个事件的这就会导致中断的执行程序的时间无法确定违背了freeRTOS中关于中断程序的要求越早结束越好。 但是实际上确实是有这么一个函数xEventGroupSetBitsFromISR可以在中断程序中去设置事件但是实际上它其实并不是在中断程序中去设置事件的而是交由定时器任务去设置的可以来看一下这个函数的内部 BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet,BaseType_t * pxHigherPriorityTaskWoken ){BaseType_t xReturn;traceEVENT_GROUP_SET_BITS_FROM_ISR( xEventGroup, uxBitsToSet );xReturn xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken ); /*lint !e9087 Cant avoid cast to void* as a generic callback function not specific to this use case. Callback casts back to original type so safe. */return xReturn;}继续进入xTimerPendFunctionCallFromISR函数查看这里需要注意一下传入的参数函数vEventGroupSetBitsCallback BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend,void * pvParameter1,uint32_t ulParameter2,BaseType_t * pxHigherPriorityTaskWoken ){DaemonTaskMessage_t xMessage;BaseType_t xReturn;/* Complete the message with the function parameters and post it to the* daemon task. */xMessage.xMessageID tmrCOMMAND_EXECUTE_CALLBACK_FROM_ISR;xMessage.u.xCallbackParameters.pxCallbackFunction xFunctionToPend;xMessage.u.xCallbackParameters.pvParameter1 pvParameter1;xMessage.u.xCallbackParameters.ulParameter2 ulParameter2;xReturn xQueueSendFromISR( xTimerQueue, xMessage, pxHigherPriorityTaskWoken );tracePEND_FUNC_CALL_FROM_ISR( xFunctionToPend, pvParameter1, ulParameter2, xReturn );return xReturn;}实际上就是将带有vEventGroupSetBitsCallback()的xMessage结构体存放进定时器队列xTimerQueue 那么既然存放到定时器任务的队列中那定时器也肯定是会去取出然后执行该vEventGroupSetBitsCallback事件设置函数。 所以说实际上在中断程序去执行这个设置事件函数xEventGroupSetBitsFromISR实际上却还是在任务中去操作的 疑问 疑问1 使用事件组可以适用于哪些场景? 某个事件若干个事件中的某个事件若干个事件中的所有事件 等待的事件中它们要么是或的关系要么是与的关系。也就是可以等待若干个事件中的任一个可以等待若干个事件中的所有。不能在若于个事件中指定某些事件。 疑问2 事件组能进行数据的传输或是保存吗 不行要想实现不同任务之间的数据的传输或是保存可以借助队列等 疑问3 为什么变量的定义必须放在函数开头那里?不能放在代码之后? Keil默认支持的C语言标准是C89必须这样做如果是C99的话变量的定义可以放在任何地方 可以在Keil中指定使用C99标准
http://www.dnsts.com.cn/news/173546.html

相关文章:

  • 网站有哪些推荐找别人网站开发没给我源代码
  • 安徽 网站建设中山市网站开发公司
  • 益阳市建设网站太原百度seo优化推广
  • 手机版的学习网站wordpress 标题字号
  • 电子商务网站建设核心网络公司网站制作岗位职责
  • 上海网站建设空间微营销手机
  • 嘉兴网站公众号怎么开通留言功能
  • 舟山高端网站建设如何做网站结构优化
  • 垦利网站制作企业网站建设的价格
  • 上海网站制作怎么样线上引流的八种推广方式
  • 公司网站域名如何续费隧道建设杂志网站
  • 移动端网站建设原则wordpress虚拟卡密
  • 我要表白网站在线制作dedecms网站备份
  • 网站下载图标wordpress更换中文字体
  • 有的域名怎样做网站acg wordpress模板
  • 实验室网站建设方案亚马逊电子商务网站的建设
  • 骗别人做网站佛山外贸建站
  • 网站建设过程中服务器的搭建方式王烨燃大夫简介
  • 建设企业网站首页扬中做网站的公司
  • 主色调为绿色的网站青海专业网页设计免费建站
  • 烟台专业做网站公司哪家好网站建设与管理案例教程期末考试
  • 各大门户网站用什么做的代运营工作内容
  • h5网站的优势经典网络营销案例
  • 网站建设 外包是什么意思怎样用linux做网站
  • 郎溪网站建设沈阳唐朝网站建设
  • 天津网站建站推广大众公司网页设计
  • 电影视频网站建设费用wordpress积分内容
  • 视频做动图的网站建设银行个人网银登录入口
  • 东莞网站开发定制商城网站制作深圳网站制作
  • 保健品企业网站wordpress论坛论文