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

FreeRTOS的软件定时器与事件标志组

目录

1.FreeRTOS软件定时器

1.1 什么是FreeRTOS软件定时器?

1.2 学习软件定时器的意义

1.3 软件定时器的简介

      1.3.1 软件定时器概述

       1.3.2 编写回调函数的注意事项

1.4 定时器服务/Daemon 任务

1.4.1 定时器服务任务与队列

1.4.2 定时器相关配置

configUSE_TIMERS

configTIMER_TASK_PRIORITY

 configTIMER_QUEUE_LENGTH

configTIMER_TASK_STACK_DEPTH

1.5 单次定时器和周期定时器

1.6 复位软件定时器

1、函数 xTimerReset()

2、函数xTimerResetFromISR()

 1.7 创建软件定时器

1、函数 xTiemrCreate()

实际操作例子:

2、函数 xTimerCreateStatic()

1.8 开启软件定时器

1、函数 xTimerStart()

实际操作例子:

2、函数xTimerStartFromISR()

1.9 停止软件定时器

1、函数 xTimerStop()

实际操作例子:

2、函数xTimerStopFromISR()

2. FreeRTOS事件标志组

2.1 什么是事件标志组?为什么要学习它?

 2.1.1 事件标志组简介

1、事件位(事件标志)

2、事件组

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

2.2  创建事件标志组

1、函数 xEventGroupCreate()

函数的实际操作应用:

2、函数xEventGroupCreateStatic() 

2.3  设置事件位

1、函数xEventGroupClearBits()

2、函数xEventGroupClearBitsFromISR()

3、函数xEventGroupSetBits()

该函数的实际操作应用:

4 、函数xEventGroupSetBitsFromISR()

该函数的实际操作应用:

2.4 获取事件标志组值 

1、函数xEventGroupGetBits()

2、函数xEventGroupGetBitsFromISR()

2.5  等待指定的事件位

1.函数xEventGroupWaitBits()

总结:


1.FreeRTOS软件定时器

1.1 什么是FreeRTOS软件定时器?

       定时器可以说是每个 MCU 都有的外设,有的 MCU 其定时器功能异常强大,比如提供PWM、输入捕获等功能。但是最常用的还是定时器最基础的功能——定时,通过定时器来完成需要周期性处理的事务。MCU自带的定时器属于硬件定时器,不同的 MCU 其硬件定时器数量不同,因为要考虑成本的问题。FreeRTOS 也提供了定时器功能,不过是软件定时器,即通过软件定时来模拟定时器,软件定时器的精度肯定没有硬件定时器那么高,但是对于普通的精度要求不高的周期性处理的任务来说够了。

1.2 学习软件定时器的意义

       每一种MCU所拥有的定时器是有限的,例如STM32F103C8T6就只有4个定时器,如果做复杂的项目,是略显不足的,因此当MCU的硬件定时器不够的时候就可以考虑使用 FreeRTOS 的软件定时器,这也是我们学习它的意义。

1.3 软件定时器的简介

      1.3.1 软件定时器概述

       软件定时器允许设置一段时间,当设置的时间到达之后就执行指定的功能函数,被定时器调用的这个功能函数叫做定时器的回调函数。回调函数的两次执行间隔叫做定时器的定时周期, 简而言之,当定时器的定时周期到了以后就会执行回调函数。

       1.3.2 编写回调函数的注意事项

       软件定时器的回调函数是在定时器服务任务中执行的,所以一定不能在回调函数中调用任 何会 阻塞任务  API  函数!     时器 回调   中千   能调用 vTaskDelay()  vTaskDelayUnti(),还有一些访问队列或者信号量的非零阻塞时间的 API 函数也不能调用。

1.4 定时器服务/Daemon 任务

1.4.1 定时器服务任务与队列

         定时器是一个可选的、不属于 FreeRTOS 内核的功能,它是由定时器服务( Daemon)任务来提供的。FreeRTOS 提供了很多定时器有关的 API 函数,这些 API 函数大多都使用FreeRTOS的队列发送命令给定时器服务任务。这个队列叫做定时器命令队列。定时器命令队列是提供给 FreeRTOS 的软件定时器使用的,用户不能直接访问!下图描述了这个过程

      左侧部分属于用户应用程序的一部分,并且会在某个用户创建的用户任务中调用。图中右侧部分是定时器服务任务的任务函数,定时器命令队列将用户应用任务和定时器服务任务连接在一起。在这个例子中,应用程序调用了函数 xTimerReset(),结果就是复位命令会被发送到定时器命令队列中,定时器服务任务会处理这个命令。应用程序是通过函数xTimerReset() 间接的向定时器命令队列发送了复位命令,并不是直接调用类似 xQueueSend()这样的队列操作函数发送的。

1.4.2 定时器相关配置

     上一小节我们知道了软件定时器有一个定时器服务任务和定时器命令队列,这两个东西肯定是要配置的,配置方法和我们前面讲解的 FreeRTOSCofig.h 一样,而且相关的配置也是放到文件FreeRTOSConfig.h 中的,涉及到的配置如下:

configUSE_TIMERS

如果要使用软件定时器的话宏 configUSE_TIMERS 一定要设置为 1当设置为 1 的话定时器服务任务就会在启动 FreeRTOS 调度器的时候自动创建。

configTIMER_TASK_PRIORITY

设置软件定时器服务任务的任务优先级,可以为 0~( configMAX_PRIORITIES-1)。优先级 一定要根据实际的应用要求来设置。如果定时器服务任务的优先级设置的高的话,定时器命令队列中的命令和定时器回调函数就会及时的得到处理

 configTIMER_QUEUE_LENGTH

 此宏用来设置定时器命令队列的队列长度

configTIMER_TASK_STACK_DEPTH

此宏用来设置定时器服务任务的任务堆栈大小,单位为字,不是字节!,对于 STM32 来说一个字是 4 字节。由于定时器服务任务中会执行定时器的回调函数,因此任务堆栈的大小一定要根据定时器的回调函数来设置。

1.5 单次定时器和周期定时器

       软件定时器分两种:单次定时器和周期定时器,单次定时器的话定时器回调函数就执行一次,比如定时 1s,当定时时间到了以后就会执行一次回调函数,然后定时器就会停止运行。对于单次定时器我们可以再次手动重新启动(调用相应的 API 函数即可),但是单次定时器不能自动重启。相反的,周期定时器一旦启动以后就会在执行完回调函数以后自动的重新启动,这样回调函数就会周期性的执行。下图描述了单次定时器和周期定时器的不同:

图中 Timer1 为单次定时器,定时器周期为 100Timer2 为周期定时器,定时器周期为 200

1.6 复位软件定时器

有时候我们可能会在定时器正在运行的时候需要复位软件定时器,复位软件定时器的话会重新计算定时周期到达的时间点,这个新的时间点是相对于复位定时器的那个时刻计算的,并不是第一次启动软件定时器的那个时间点。下图演示了这个过程,Timer1 是单次定时器,定时周期是 5s

        上中我们展示了定时器复位过程,这是一个通过按键打开 LCD 背光的例子,我们假定当唤醒键被按下的时候应用程序打开 LCD 背光,当LCD 背光点亮以后如果5s 之内唤醒键没有再次按下就自动熄灭。如果在这 5s 之内唤醒键被按下了,LCD 背光就从按下的这个时刻起再亮 5s

        FreeRTOS 提供了两个 API 函数来完成软件定时器的复位,如下表:

1、函数 xTimerReset()

复位一个软件定时器,此函数只能用在任务中,不能用于中断服务函数!此函数是一个宏, 真正执行的是函数 xTimerGenericCommand(),函数原型如下:

BaseType_t xTimerReset( TimerHandle_t    xTimer,

                                            TickType_t        xTicksToWait

参数:

xTimer      要复位的软件定时器的句柄。

xTicksToWait 设置阻塞时间,调用函数 xTimerReset ()开启软件定时器其实就是向定时器命

令队列发送一条 tmrCOMMAND_RESET 命令,既然是向队列发送消息,那 肯定会涉及到入队阻塞时间的设置。

返回值:

pdPASS:          软件定时器复位成功,其实就是命令发送成功。

pdFAIL:           软件定时器复位失败,命令发送失败。

2函数xTimerResetFromISR()

此函数是 xTimerReset()的中断版本,此函数用于中断服务函数中!此函数是一个宏,真正 执行的是函数 xTimerGenericCommand(),函数原型如下:

BaseType_t xTimerResetFromISR( TimerHandle_t      xTimer,

                                                        BaseType_t *        pxHigherPriorityTaskWoken );     

参数:

xTimer      要复位的软件定时器的句柄。

pxHigherPriorityTaskWoken  记退出此函数以后是否进行任务切换,这个变量的值函数会自

动设置的,用户不用进行设置,用户只需要提供一个变量来保 存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函 数之前一定要进行一次任务切换。

返回值:

pdPASS:          软件定时器复位成功,其实就是命令发送成功。

pdFAIL:           软件定时器复位失败,命令发送失败。

 1.7 创建软件定时器

使用软件定时器之前要先创建软件定时器,软件定时器创建函数如下表:

1、函数 xTiemrCreate()

此函数用于创建一个软件定时器,所需要的内存通过动态内存管理方法分配。新创建的软                   ,和函数xTimerStart()、xTimerReset()、xTimerStartFromISR()、xTimerResetISR()、xTimerChangePeriod()和xTimerChangePeriodFromISR()可以使新创建的定时器进入活动状态,此函数的原型如下:

 TimerHandle_txTimerCreate( const char * const    pcTimerName,

                                                  TickType_t                xTimerPeriodInTicks,

                                               UBaseType_t             uxAutoReload,

                                                void *                          pvTimerID,

                                                TimerCallbackFunction_t    pxCallbackFunction )

参数:

pcTimerName   软件定时器名字,名字是一串字符串,用于调试使用。

xTimerPeriodInTicks  软件定时器的定时器周期, 单位是时钟节拍数。可以借助portTICK_PERIOD_MSms单位转换为时钟节拍数。举个例子,定时器的周期为100 个时钟节拍的话,那么 xTimerPeriodInTicks  就为 100,当定时器周期为 500ms 的时候 xTimerPeriodInTicks 就可以设置 (500/ portTICK_PERIOD_MS)

uxAutoReload        设置定时器模式,单次定时器还是周期定时器?当此参数为pdTRUE的时候表示创建的是周期定时器。如果为 pdFALSE  的话表示创建的是单次定时器。

pvTimerID           定时器 ID 号,一般情况下每个定时器都有一个回调函数,当定时器定时周期到了以后就会执行这个回调函数。但是 FreeRTOS 也支持多个 定时器共用同一个回调函数,在回调函数中根据定时器的 ID  号来处 理不同的定时器。

pxCallbackFunction  定时器回调函数,当定时器定时周期到了以后就会调用这个函数。

返回值:

NULL:           软件定时器创建失败。

其他值:          创建成功的软件定时器句柄。

实际操作例子:

2、函数 xTimerCreateStatic()

此函数用于创建一个软件定时器,所需要的内存需要用户自行分配。新创建的软件定时器 处于休眠状态,也就是未运行的。函数 xTimerStart() xTimerReset() xTimerStartFromISR() xTimerResetFromISR() xTimerChangePeriod() xTimerChangePeriodFromISR()可以使新创建的 定时器进入活动状态,此函数的原型如下:

参数:

pcTimerName        软件定时器名字,名字是一串字符串,用于调试使用。

xTimerPeriodInTicks  软件定时器的定时器周期, 单位是时钟节拍数。可以借助 portTICK_PERIOD_MS  ms 单位转换为时钟节拍数。举个例子,定 时器的周期为 100  个时钟节拍的话,那么 xTimerPeriodInTicks  就为 100,当定时器周期为 500ms 的时候xTimerPeriodInTicks 就可以设置 为(500/ portTICK_PERIOD_MS)

uxAutoReload        设置定时器模式,单次定时器还是周期定时器?当此参数为pdTRUE

的时候表示创建的是周期定时器。如果为 pdFALSE  的话表示创建的 是单次定时器。

pvTimerID           定时器 ID 号,一般情况下每个定时器都有一个回调函数,当定时器定时周期到了以后就会执行这个回调函数。当时 FreeRTOS 也支持多个 定时器共用同一个回调函数,在回调函数中根据定时器的 ID  号来处 理不同的定时器。

pxCallbackFunction  定时器回调函数,当定时器定时周期到了以后就会调用这个函数。

pxTimerBuffer       参数指向一个 StaticTimer_t 类型的变量,用来保存定时器结构体。

返回值:

NULL:           软件定时器创建失败。

其他值:          创建成功的软件定时器句柄。

1.8 开启软件定时器

如果软件定时器停止运行的话可以使用 FreeRTOS 提供的两个开启函数来重新启动软件定时器,这两个函数如下表:

1、函数 xTimerStart()

       启动软件定时器,函数 xTimerStartFromISR()是这个函数的中断版本,可以用在中断服务函数中。如果软件定时器没有运行的话调用函数 xTimerStart()就会计算定时器到期时间,如果软件定时器正在运行的话调用函数 xTimerStart()的结果 xTimerReset()一样。此函数是个宏,真正执行的是函数 xTimerGenericCommand ,函数原型如下:

BaseType_t xTimerStart( TimerHandle_t           xTimer,

                                           TickType_t               xTicksToWait )

参数:

xTimer      要开启的软件定时器的句柄。

xTicksToWait 设置阻塞时间,调用函数 xTimerStart()开启软件定时器其实就是向定时器命令

队列发送一条 tmrCOMMAND_START 命令,既然是向队列发送消息,那肯 定会涉及到入队阻塞时间的设置。

返回值:

pdPASS:          软件定时器开启成功,其实就是命令发送成功。

pdFAIL:           软件定时器开启失败,命令发送失败。

实际操作例子:

2、函数xTimerStartFromISR()

此函数是函数 xTimerStart()的中断版本,用在中断服务函数中,此函数是一个宏,真正执行 的是函数 xTimerGenericCommand() ,此函数原型如下:

BaseType_t xTimerStartFromISR( TimerHandle_t       xTimer,

                                                         BaseType_t *      pxHigherPriorityTaskWoken );

参数:

xTimer      要开启的软件定时器的句柄。

pxHigherPriorityTaskWoken  标记退出此函数以后是否进行任务切换,这个变量的值函数会自动设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为pdTRUE的时候在退出中断服务 函数之前一定要进行一次任务切换。

返回值:

pdPASS:          软件定时器开启成功,其实就是命令发送成功。

pdFAIL:           软件定时器开启失败,命令发送失败。

1.9 停止软件定时器

既然有开启软件定时器的 API 函数,那么肯定也有停止软件定时器的函数,FreeRTOS提供了两个用于停止软件定时器的 API 函数,如表 

1、函数 xTimerStop()

此函数用于停止一个软件定时器,此函数用于任务中,不能用在中断服务函数中!此函数是一个宏,真正调用的是函数 xTimerGenericCommand(),函数原型如下:

BaseType_t xTimerStop ( TimerHandle_t       xTimer,

                                         TickType_t             xTicksToWait

参数:

xTimer      要停止的软件定时器的句柄。

xTicksToWait 设置阻塞时间,调用函数 xTimerStop()停止软件定时器其实就是向定时器命令

队列发送一条 tmrCOMMAND_STOP 命令,既然是向队列发送消息,那肯定 会涉及到入队阻塞时间的设置。

返回值:

pdPASS:          软件定时器停止成功,其实就是命令发送成功。

pdFAIL:           软件定时器停止失败,命令发送失败。

实际操作例子:

2、函数xTimerStopFromISR()

此函数是 xTimerStop()的中断版本,此函数用于中断服务函数中!此函数是一个宏,真正执行的是函数 xTimerGenericCommand(),函数原型如下:

BaseType_t xTimerStopFromISR( TimerHandle_t    xTimer,

                                                        BaseType_t *      pxHigherPriorityTaskWoken );

参数:

xTimer      要停止的软件定时器句柄。

pxHigherPriorityTaskWoken  标记退出此函数以后是否进行任务切换,这个变量的值函数会自动设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为pdTRUE的时候在退出中断服务 函数之前一定要进行一次任务切换。

返回值:

pdPASS:          软件定时器停止成功,其实就是命令发送成功。

pdFAIL:           软件定时器停止失败,命令发送失败。

2. FreeRTOS事件标志组

2.1 什么是事件标志组?为什么要学习它?

      我们学习了使用信号量来完成同步,但是使用信号量来同步的话任务只能与单个的事件或任务进行同步。有时候某个任务可能会需要与多个事件或任务进行同步,此时信号量就无能为力了。FreeRTOS为此提供了一个可选的解决方法,那就是事件标志组。

 2.1.1 事件标志组简介

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 )

typedefuint16_t TickType_t;

#define portMAX_DELAY ( TickType_t ) 0xffff #else

typedefuint32_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个事件位,如下图 

2.2  创建事件标志组

FreeRTOS 提供了两个用于创建事件标志组的函数,如下表:

1、函数 xEventGroupCreate()

此函数用于创建一个事件标志组,所需要的内存通过动态内存管理方法分配。由于内部处理的原 因 , 事件标志组可用的bit数取决于configUSE_16_BIT_TICKS, configUSE_16_BIT_TICKS1    1         8       (bit0~bit7)  configUSE_16_BIT_TICKS  0 的时候事件标志组有 24 个可用的位(bit0~bit23) EventBits_t  型的变量用来存储事件标志组中的各个事件位,函数原型如下:

EventGroupHandle_t xEventGroupCreate( void )

参数:

无。

返回值:

NULL:           事件标志组创建失败。

其他值:          创建成功的事件标志组句柄。

函数的实际操作应用:

2、函数xEventGroupCreateStatic() 

此函数用于创建一个事件标志组定时器,所需要的内存需要用户自行分配,此函数原型如下:

EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ) 

参数:

pxEventGroupBuffer 参数指向一个 StaticEventGroup_t 类型的变量,用来保存事件标志组结

构体。

返回值:

NULL:           事件标志组创建失败。

其他值:          创建成功的事件标志组句柄。

2.3  设置事件位

FreeRTOS 提供了 4 个函数用来设置事件标志组中事件位(标志),事件位(标志)的设置包括 清零和置 1 两种操作,这 4 个函数如下表:

1、函数xEventGroupClearBits()

将事件标志组中的指定事件位清零,此函数只能用在任务中,不能用在中断服务函数中!中断服务函数有其他的 API 函数。函数原型如下:

EventBits_t xEventGroupClearBits( EventGroupHandle_t   xEventGroup,

                                                          const EventBits_t       uxBitsToClear );

参数:

xEventGroup   要操作的事件标志组的句柄。

uxBitsToClear  要清零的事件位,比如要清除 bit3  的话就设置 0X08。可以同时清除多个

bit,如设置为 0X09 的话就是同时清除 bit3  bit0

返回值:

任何值:       将指定事件位清零之前的事件组值。

2、函数xEventGroupClearBitsFromISR()

此函数为函数 xEventGroupClearBits()的中断级版本,也是将指定的事件位(标志)清零。此 函数用在中断服务函数中,此函数原型如下:

BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t   xEventGroup,

                                                                        const EventBits_t         uxBitsToSet );

参数:

xEventGroup   要操作的事件标志组的句柄。

uxBitsToClear  要清零的事件位,比如要清除 bit3  的话就设置 0X08。可以同时清除多个bit,如设置为 0X09 的话就是同时清除 bit3  bit0

返回值:

pdPASS        事件位清零成功。

pdFALSE:             事件位清零失败

3、函数xEventGroupSetBits()

设置指定的事件位为 1,此函数只能用在任务中,不能用于中断服务函数,此函数原型如下:

EventBits_t xEventGroupSetBits( EventGroupHandle_t       xEventGroup,

                                                       const EventBits_t          uxBitsToSet );

参数:

xEventGroup   要操作的事件标志组的句柄。

uxBitsToClear  指定要置 1 的事件位,比如要将 bit3 1 的话就设置 0X08。可以同时将多个 bit  1,如设置为 0X09 的话就是同时将 bit3  bit0  1

返回值:

任何值:       在将指定事件位置 1 的事件组值。

该函数的实际操作应用:

这里解释一下(1<<2),是将1往左移两位的意思,在实际操作中就是将该24或8位的事件组中的第三位置1。

、函数xEventGroupSetBitsFromISR()

此函数也用于将指定的事件位置 1,此函数是 xEventGroupSetBits()的中断版本,用在中断 服务函数中,函数原型如下:

参数:

xEventGroup   要操作的事件标志组的句柄。

uxBitsToClear  指定要置 1 的事件位,比如要将 bit3 1 的话就设置 0X08。可以同时将多个 bit  1,如设置为 0X09 的话就是同时将 bit3  bit0  1

pxHigherPriorityTaskWoken标记退出此函数以后是否进行任务切换,这个变量的值函数会自动设置的,用户不用进行设置,用户只需要提供一个变量来保存 这个值就行了。当此值为pdTRUE的时候在退出中断服务函数之 前一定要进行一次任务切换。

返回值:

pdPASS        事件位置 1 成功。

pdFALSE:              事件位置 1 失败。

该函数的实际操作应用:

根据返回值进行判断是否进行任务切换,案例给出的判断是不等于pdFAIL,相当于不等于pdFALSE。

2.4 获取事件标志组值 

我们可以通过 FreeRTOS 提供的 API 函数来查询事件标准组值,FreeRTOS 一共提供了两个这样的 API 函数,如下表

1、函数xEventGroupGetBits()

此函数用于获取当前事件标志组的值,也就是各个事件位的值。此函数用在任务中,不能用在中断服务函数中。此函数是个宏,真正执行的是函数 xEventGroupClearBits(),函数原型如下:

EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup )

参数:

xEventGroup   要获取的事件标志组的句柄。

返回值:

任何值:当前事件标志组的值。

2、函数xEventGroupGetBitsFromISR()

获取当前事件标志组的值,此函数是 xEventGroupGetBits()的中断版本,函数原型如下:

EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup )

参数:

xEventGroup   要获取的事件标志组的句柄。

返回值:

任何值:        当前事件标志组的值。

2.5  等待指定的事件位

1.函数xEventGroupWaitBits()

       某个任务可能需要与多个事件进行同步,那么这个任务就需要等待并判断多个事件位( ),使用函数 xEventGroupWaitBits()可以完成这个功能。调用函数以后如果任务要等待的事件位还没有准备好(1 或清零)的话任务就会进入阻塞态,直到阻塞时间到达或者所等待的事件位准备好。函数原型如下:

参数:

xEventGroup     指定要等待的事件标志组。

uxBitsToWaitFord:指定要等待的事件位,比如要等待bit0 ()bit2 的时候此参数就是0X05,如果要等待 bit0 ()bit1 ()bit2 的时候此参数就是 0X07 ,以此类推。

xClearOnExit:   此参数要是为pdTRUE 的话,那么在退出此函数之前由参数uxBitsToWaitFor所设置的这些事件位就会清零。如果设置位 pdFALSE  的话这些事件位就 不会改变。

xWaitForAllBits :  此参数如果设置为 pdTRUE  的话,当 uxBitsToWaitFor 所设置的这些事件位都置 1 ,或者指定的阻塞时间到的时候函数 xEventGroupWaitBits()才会返回。当此函数为pdFALSE的话,只要uxBitsToWaitFor 所设置的这些事件位其中的任意一个置1,或者指定的阻塞时间到了,函数xEventGroupWaitBits()就会返回。

xTicksToWait    设置阻塞时间,单位为节拍数。

返回值:

任何值:   返回当所等待的事件位置 1 以后的事件标志组的值,或者阻塞时间到。根据这个值我们就知道哪些事件位置 1 了。如果函数因为阻塞时间到而返回的话那么这个返回值就不代表任何的含义。

该函数的实际操作应用:

总结:

          FreeRTOS的软件定时器和事件标志组,是使用RTOS(实时操作系统)有了更深入的了解,掌握他们的原理、函数参数、函数用法是这一节的要求,希望这一篇文章能帮助到大家,谢谢!

相关文章:

FreeRTOS的软件定时器与事件标志组

目录 1.FreeRTOS软件定时器 1.1 什么是FreeRTOS软件定时器&#xff1f; 1.2 学习软件定时器的意义 1.3 软件定时器的简介 1.3.1 软件定时器概述 1.3.2 编写回调函数的注意事项 1.4 定时器服务/Daemon 任务 1.4.1 定时器服务任务与队列 1.4.2 定时器相关配置 configUSE_T…...

生产制造领域的多元化模式探索

在当今全球化和信息化的时代背景下&#xff0c;生产制造领域正经历着前所未有的变革。随着消费者需求的多样化、市场竞争的加剧以及技术的不断进步&#xff0c;传统的生产制造模式已经难以满足现代企业的需求。因此&#xff0c;多种生产制造模式应运而生&#xff0c;以适应不同…...

大数据技术之SparkCore

RDD概述 什么是RDD RDD&#xff08;Resilient Distributed Dataset&#xff09;叫做弹性分布式数据集&#xff0c;是Spark中最基本的数据抽象。代码中是一个抽象类&#xff0c;它代表一个弹性的、不可变、可分区、里面的元素可并行计算的集合。 RDD五大特性 RDD编程 RDD的创…...

element-plus的组件数据配置化封装 - table

目录 一、封装的table、table-column组件以及相关ts类型的定义 1、ATable组件的封装 - index.ts 2、ATableColumn组件的封装 - ATableColumn.ts 3、ATable、ATableColumn类型 - interface.ts 二、ATable、ATableColumn组件的使用 三、相关属性、方法的使用以及相关说明 1. C…...

蓝桥杯每日真题 - 第15天

题目&#xff1a;&#xff08;钟表&#xff09; 题目描述&#xff08;13届 C&C B组B题&#xff09; 解题思路&#xff1a; 理解钟表指针的运动&#xff1a; 秒针每分钟转一圈&#xff0c;即每秒转6度。 分针每小时转一圈&#xff0c;即每分钟转6度。 时针每12小时转一圈…...

c#:winform调用bartender实现打印(学习整理笔记)

效果 学习路径 C# winform调用Bartender进行自定义打印、批量打印、检索文件夹中的模板_哔哩哔哩_bilibili 一、初始环境搭建见&#xff1a; c#:winform引入bartender-CSDN博客https://blog.csdn.net/weixin_46001736/article/details/143989473?sharetypeblogdetail&s…...

周期法频率计的设计

目录 周期法频率计 分析&#xff1a; 设计过程&#xff1a; 周期法频率计 对于低频信号&#xff0c;应用周期法进行测频。周期法测频的基本原理是&#xff1a;应用标准频率信号统计被测信号两个相邻脉冲之间的脉冲数&#xff0c;然后通过脉冲数计算出被测信号的周期&#xff…...

【2024亚太杯亚太赛APMCM C题】数学建模竞赛|宠物行业及相关产业的发展分析与策略|建模过程+完整代码论文全解全析

第一个问题是&#xff1a;请基于附件 1 中的数据以及你的团队收集的额外数据&#xff0c;分析过去五年中国宠物行业按宠物类型的发展情况。并分析中国宠物行业发展的因素&#xff0c;预测未来三年中国宠物行业的发展。 第一个问题&#xff1a;分析中国宠物行业按宠物类型的发展…...

uniapp的renderjs使用

‌uniapp中的RenderJS主要服务于APP和H5平台&#xff0c;其作用包括降低逻辑层和视图层的通讯损耗&#xff0c;提供高性能视图交互能力&#xff0c;以及在视图层操作DOM和运行Web的JS库‌。 RenderJS是uni-app中一个特性&#xff0c;它允许开发者在页面中使用JavaScript直接渲…...

CPU命名那些事

一、Intel CPU命名 1. 命名结构 Intel CPU 的命名通常包含以下几个部分&#xff1a; 品牌 产品线 系列 代数 具体型号 后缀 例如&#xff1a;Intel Core i7-13700K 2. 各部分含义 品牌 Intel&#xff1a;表示厂商&#xff08;几乎所有命名中都有&#xff09;。不同品…...

【LLM】一文学会SPPO

博客昵称&#xff1a;沈小农学编程 作者简介&#xff1a;一名在读硕士&#xff0c;定期更新相关算法面试题&#xff0c;欢迎关注小弟&#xff01; PS&#xff1a;哈喽&#xff01;各位CSDN的uu们&#xff0c;我是你的小弟沈小农&#xff0c;希望我的文章能帮助到你。欢迎大家在…...

鸿蒙多线程开发——线程间数据通信对象03(sendable)

1、简 介 在传统JS引擎上&#xff0c;对象的并发通信开销的优化方式只有一种&#xff0c;就是把实现下沉到Native侧&#xff0c;通过Transferable对象的转移或共享方式降低并发通信开销。而开发者仍然还有大量对象并发通信的诉求&#xff0c;这个问题在业界的JS引擎实现上并没…...

web前端开发--动画效果

1、3D旋转 <!DOCTYPE html> <html><head><meta charset"UTF-8"><title>3D旋转</title><style type"text/css">div{/*设置大盒子样式*/width: 100px;height: 100px;/*background-color: rgba(255,0,0,0.5);*/ba…...

【数据分享】中国价格统计年鉴(2013-2024) PDF

数据介绍 犹如一座珍贵的宝库&#xff0c;全面而系统地记录了中国在这一时期的价格变动情况。它涵盖了丰富的内容&#xff0c;包括宏观经济指标、商品价格、居民消费价格以及城市物价监测等多个方面。 在宏观经济指标方面&#xff0c;年鉴中收录了 GDP、CPI、PPI 等重要数据&…...

鸿蒙NEXT开发案例:字数统计

【引言】 本文将通过一个具体的案例——“字数统计”组件&#xff0c;来探讨如何在鸿蒙NEXT框架下实现这一功能。此组件不仅能够统计用户输入文本中的汉字、中文标点、数字、以及英文字符的数量&#xff0c;还具有良好的用户界面设计&#xff0c;使用户能够直观地了解输入文本…...

TritonServer中加载模型,并在Gunicorn上启动Web服务调用模型

TritonServer中加载模型,并在Gunicorn上启动Web服务调用模型 一、TritonServer中加载模型1.1 搭建本地仓库1.2 配置文件1.3 服务端代码1.4 启动TritonServer二、Gunicorn上启动Web服务2.1 安装和配置Gunicorn2.2 启动Gunicorn三、调用模型四、性能优化与监控五、总结在深度学习…...

[UE5学习] 一、使用源代码安装UE5.4

一、简介 本文介绍了如何使用源代码安装编译UE5.4&#xff0c;并且新建简单的项目&#xff0c;打包成安卓平台下的apk安装包。 二、使用源代码安装UE5.4 注意事项&#xff1a; 请保证可以全程流畅地科学上网。请保证C盘具有充足的空间。请保证接下来安装下载的visual studi…...

2023AE软件、Adobe After Effects安装步骤分享教程

2023AE软件是一款由Adobe公司开发的视频编辑软件&#xff0c;也被称为Adobe After Effects。它在广告、电影、电视和网络视频等领域广泛应用&#xff0c;用于制作动态图形、特效、合成和其他视觉效果。该软件支持多种视频和音频文件格式&#xff0c;具有丰富的插件和预设&#…...

Xilinx 7 系列 FPGA的各引脚外围电路接法

Xilinx 7系列FPGA的外围电路接法涉及到多个方面&#xff0c;包括电源引脚、时钟输入引脚、FPGA配置引脚、JTAG调试引脚&#xff0c;以及其他辅助引脚。 本文参考资料&#xff1a; ds180 - 7 Series FPGAs Data Sheet - Overview ds181 - Artix 7 FPGAs Data Sheet - DC and AC…...

【LeetCode热题100】队列+宽搜

这篇博客是关于队列宽搜的几道题&#xff0c;主要包括N叉树的层序遍历、二叉树的锯齿形层序遍历、二叉树最大宽度、在每个数行中找最大值。 class Solution { public:vector<vector<int>> levelOrder(Node* root) {vector<vector<int>> ret;if(!root) …...

<Sqlite><websocket>使用Sqlite与websocket,实现网页端对数据库的【读写增删】操作

前言 本文是在websocket进行通讯的基础,添加数据库进行数据的存储,数据库软件使用的是sqlite。 环境配置 系统:windows 平台:visual studio code 语言:javascript、html 库:nodejs、sqlite 概述 此前,我们实现在利用websocket和socket,将网页端与下位控制器如PLC进行…...

summernote富文本批量上传音频,视频等附件

普通项目,HTML的summernote富文本批量上传音频,视频等附件(其他附件同理) JS和CSS的引入 <head><th:block th:include"include :: summernote-css" /> </head> <body><th:block th:include"include :: summernote-js" /> …...

第六十五周周报 UP2ME

文章目录 week 65 UP2ME摘要Abstract1. 题目2. Abstract3. 文献解读3.1 Introduction3.2 创新点 4. 网络结构4.1 单变量预训练4.1.1 样例生成4.1.2 掩码自动编码器预训练4.1.3 即时反应模式 4.2 多元微调4.2.1 稀疏依赖图构造4.2.2 时域频道层 5. 实验结果6. 结论7. 部分关键代…...

Unity 使用 Excel 进行配置管理(读Excel配置表、Excel转保存Txt 文本、读取保存的 Txt 文本配置内容)

Unity 使用 Excel 进行配置管理(读Excel配置表、Excel转保存Txt 文本、读取保存的 Txt 文本配置内容) 目录 Unity 使用 Excel 进行配置管理(读Excel配置表、Excel转保存Txt 文本、读取保存的 Txt 文本配置内容) 一、简单介绍 二、实现原理 三、注意事项 四、案例简单步…...

【STM32】MPU6050简介

文章目录 MPU6050简介MPU6050关键块带有16位ADC和信号调理的三轴MEMS陀螺仪具有16位ADC和信号调理的三轴MEMS加速度计I2C串行通信接口 MPU6050对应的数据手册&#xff1a;MPU6050 陀螺仪加速度计 链接: https://pan.baidu.com/s/13nwEhGvsfxx0euR2hMHsyw?pwdv2i6 提取码: v2i6…...

学习日记_20241123_聚类方法(MeanShift)

前言 提醒&#xff1a; 文章内容为方便作者自己后日复习与查阅而进行的书写与发布&#xff0c;其中引用内容都会使用链接表明出处&#xff08;如有侵权问题&#xff0c;请及时联系&#xff09;。 其中内容多为一次书写&#xff0c;缺少检查与订正&#xff0c;如有问题或其他拓展…...

Qt常用控件 按钮

文章目录 1. QAbstractButton 简介2. QPushButton2.1 例子1&#xff0c;设置按钮的图标2.2 例子2&#xff0c;设置按钮快捷键 3. QRadioButton3.1 介绍3.2 例子1&#xff0c;选择性别3.3 例子2&#xff0c;试试其他的信号3.3 例子3&#xff0c;分组 4. QCheckBox4.1 介绍4.2 例…...

医院信息化与智能化系统(22)

医院信息化与智能化系统(22) 这里只描述对应过程&#xff0c;和可能遇到的问题及解决办法以及对应的参考链接&#xff0c;并不会直接每一步详细配置 如果你想通过文字描述或代码画流程图&#xff0c;可以试试PlantUML&#xff0c;告诉GPT你的文件结构&#xff0c;让他给你对应…...

嵌入式硬件实战基础篇(二)-稳定输出3.3V的太阳能电池-无限充放电

引言&#xff1a;本内容主要用作于学习巩固嵌入式硬件内容知识&#xff0c;用于想提升下述能力&#xff0c;针对学习稳压芯片和电容以及电池之间的运用&#xff0c;对于硬件PCB以及原理图的练习和前面硬件篇的实际运用&#xff1b;太阳能是一种清洁、可再生的能源&#xff0c;广…...

UE5材质篇5 简易水面

不得不说&#xff0c;UE5里搞一个水面实在是相比要自己写各种反射来说太友好了&#xff0c;就主要是开启一堆开关&#xff0c;lumen相关的&#xff0c;然后稍微连一些蓝图就几乎有了 这里要改一个shading model&#xff0c;要这个 然后要增加一个这个node 并且不需要连接base …...

Rollup配置实战:多产物与多入口的完美结合 (2)

常用配置 多产物配置 我们可以将 output 改造成一个数组&#xff0c;对外暴露出不同格式的产物供他人使用&#xff0c;不仅包括 ESM&#xff0c;也需要包括诸如CommonJS、UMD等格式&#xff0c;保证良好的兼容性 import { defineConfig } from rollupexport default defineC…...

iced源码分析

前言 iced是一个比较流行的UI库&#xff0c;设计思路还是挺有意思的&#xff0c;不过因为rust复杂的语法&#xff0c;这个库确实很难让一个不精通rust的开发者那么容易理解。这里记录下这几天的阅读源码心得。 正文 iced核心包括四个模块。 iced库&#xff0c;主要控制应用…...

Hadoop的MapReduce详解

文章目录 Hadoop的MapReduce详解一、引言二、MapReduce的核心概念1、Map阶段1.1、Map函数的实现 2、Reduce阶段2.1、Reduce函数的实现 三、MapReduce的执行流程四、MapReduce的使用实例Word Count示例1. Mapper类2. Reducer类3. 执行Word Count 五、总结 Hadoop的MapReduce详解…...

【Python系列】字典灵活的数据存储与操作

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

【MCU】微控制器的编程技术:ISP 与 IAP

在嵌入式领域中&#xff0c;将程序下载到内置 Flash 有两种技术 ISP (In-system programming) ISP 即在系统编程&#xff0c;是指一些可编程逻辑器件、微控制器、芯片组和其他嵌入式设备在安装到完整嵌入式系统后能够进行编程&#xff0c;而不需要在将芯片安装到系统中之前对…...

TCP/IP 协议:网络世界的基石(2/10)

一、引言 在当今数字化时代&#xff0c;互联网已经成为人们生活中不可或缺的一部分。而在互联网的背后&#xff0c;TCP/IP 协议扮演着至关重要的角色&#xff0c;堪称互联网的基石。 TCP/IP 协议是一组用于数据通信的协议集合&#xff0c;它的名字来源于其中最重要的两个协议…...

小R的二叉树探险 | 模拟

问题描述 在一个神奇的二叉树中&#xff0c;结构非常独特&#xff1a; 每层的节点值赋值方向是交替的&#xff0c;第一层从左到右&#xff0c;第二层从右到左&#xff0c;以此类推&#xff0c;且该二叉树有无穷多层。 小R对这个二叉树充满了好奇&#xff0c;她想知道&#xf…...

Redis ⽀持哪⼏种数据类型?适⽤场景,底层结构

目录 Redis 数据类型 一、String&#xff08;字符串&#xff09; 二、Hash&#xff08;哈希&#xff09; 三、List&#xff08;列表&#xff09; 四、Set&#xff08;集合&#xff09; 五、ZSet(sorted set&#xff1a;有序集合) 六、BitMap 七、HyperLogLog 八、GEO …...

十、事件类型(鼠标事件、焦点.. 、键盘.. 、文本.. 、滚动..)、事件对象、事件流(事件捕获、事件冒泡、阻止冒泡和默认行为、事件委托)

1. 事件类型 1.1 鼠标事件 1.1.1 click 鼠标点击 1.1.2 mouseenter 鼠标进入 1.1.3 mouseleave 鼠标离开 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widt…...

RabbitMQ学习-One

同步调用和异步调用 1.假设我们现在又两个服务&#xff0c;分别是修改商品服务和查询商品服务&#xff0c;每个服务都有自己的数据库&#xff1b; 2.左侧的流程假设我们总共需要耗时40ms&#xff1b; 3.因为不同服务数据库不一样&#xff0c;所以我们就要考虑修改了左边服务的…...

蓝队基础,网络七杀伤链详解

声明&#xff01; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&#…...

机器学习实战:银行客户是否认购定期存款

项目结构与步骤 1. 项目概述 项目名称&#xff1a;葡萄牙银行电话营销活动分析与定期存款认购预测目标&#xff1a;通过分析银行的电话营销数据&#xff0c;构建模型预测客户是否会认购定期存款。数据来源&#xff1a;葡萄牙银行营销活动数据集关键挑战&#xff1a;数据不平衡…...

【一篇搞定配置】网络分析工具WireShark的安装与入门使用

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;各种软件安装与配置_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1.…...

气膜场馆照明设计:科技与环保的完美结合—轻空间

气膜场馆的照明设计&#xff0c;选用高效节能的400瓦LED灯具&#xff0c;结合现代节能技术&#xff0c;提供强大而均匀的光照。LED灯具在光效和寿命方面优势显著&#xff0c;不仅降低运营能耗&#xff0c;还有效减少碳排放&#xff0c;为绿色场馆建设贡献力量。 科学分布&…...

C语言程序编译和链接

编译环境和运行 编译环境也可以称为翻译环境&#xff0c;是将源代码转换为机器可以识别的二进制指令&#xff1b; 运行环境也可以称为执行环境&#xff0c;用于实际执行代码&#xff1b; 翻译环境 翻译环境由编译和链接两个部分组成&#xff0c;而编译又可以分解为&#x…...

springBoot整合 Tess4J实现OCR识别文字(图片+PDF)

1. 环境准备 JDK 8 或更高版本Maven 3.6 或更高版本Spring Boot 2.4 或更高版本Tesseract OCR 引擎Tess4J 库 2. 安装 Tesseract OCR 引擎 下载地址&#xff1a; Home UB-Mannheim/tesseract Wiki GitHub linux直接安装&#xff1a;sudo apt-get install tesseract-ocr 3.…...

阿里数字人工作 Emote Portrait Alive (EMO):基于 Diffusion 直接生成视频的数字人方案

TL;DR 2024 年 ECCV 阿里智能计算研究所的数字人工作&#xff0c;基于 diffusion 方法来直接的从音频到视频合成数字人&#xff0c;避免了中间的三维模型或面部 landmark 的需求&#xff0c;效果很好。 Paper name EMO: Emote Portrait Alive - Generating Expressive Portra…...

Java将PDF保存为图片

将 PDF 文件转换为图片是常见的需求之一&#xff0c;特别是在需要将 PDF 内容以图像形式展示或处理时。其中最常用的是 Apache PDFBox。 使用 Apache PDFBox Apache PDFBox 是一个开源的 Java 库&#xff0c;可以用来处理 PDF 文档。它提供了将 PDF 页面转换为图像的功能。 …...

医药企业的终端市场营销策略

近年来&#xff0c;随着医药行业的快速发展&#xff0c;终端市场逐渐成为企业竞争的关键领域。在政策趋严、市场环境变化以及数字化转型的大背景下&#xff0c;医药企业如何在终端市场中立于不败之地&#xff1f;本文结合我们在医药数字化领域的经验&#xff0c;为大家剖析终端…...

使用EFK收集k8s日志

首先我们使用EFK收集Kubernetes集群中的日志&#xff0c;本次实验讲解的是在Kubernetes集群中启动一个Elasticsearch集群&#xff0c;如果企业内已经有了Elasticsearch集群&#xff0c;可以直接将日志输出至已有的Elasticsearch集群。 文章目录 部署elasticsearch创建Kibana创建…...