ESP32 FreeRTOS-软件定时器(9)

提示:好记性不如烂笔头。本博客作为学习笔记,有错误的地方希望指正

前言:

  参考资料:FreeRTOS API参考
  几乎所有的单片机都有自己的定时器,单片机的定时器是有限资源的,但是FreeRTOS给我们提供了软件定时器,软件定时器可以实现很多个,不像硬件定时器一样会有数量的限制。

一、xTimerCreate()

API原型:

 TimerHandle_t xTimerCreate
                 ( const char * const pcTimerName,
                   const TickType_t xTimerPeriod,
                   const UBaseType_t uxAutoReload,
                   void * const pvTimerID,
                   TimerCallbackFunction_t pxCallbackFunction );

  创建一个新的软件定时器实例, 并返回一个可以引用定时器的句柄。
  要使此 RTOS API 函数可用:

  1. configUSE_TIMERS 和 configSUPPORT_DYNAMIC_ALLOCATION 必须在 FreeRTOSConfig.h 中同时设置为 1(configSUPPORT_DYNAMIC_ALLOCATION 也可以不定义,此时其默认值为 1)。
  2. FreeRTOS/Source/timers.c C 源文件必须包含在 构建中。
    每个软件定时器都需要少量 RAM 来保存定时器的状态。 如果使用 xTimerCreate() 创建定时器, 则此 RAM 由 FreeRTOS 堆自动分配。 如果使用 xTimerCreateStatic() 创建软件定时器, 则 RAM 由应用程序编写器提供,这需要用到一个附加参数, 但允许在编译时静态分配 RAM 。 请参阅静态分配与 动态分配页面了解更多信息。
      定时器是在休眠状态下创建的。 xTimerStart()、 xTimerReset()、 xTimerStartFromISR()、 xTimerResetFromISR()、 xTimerChangePeriod() 以及 xTimerChangePeriodFromISR() API 函数都可以用于将定时器转换为活动状态。

参数:
  pcTimerName 分配给定时器的可读文本名称。 这样做纯粹是为了协助 调试。 RTOS 内核本身只通过句柄引用定时器, 而从不通过名字引用。
  xTimerPeriod 定时器的周期。 以 tick 为单位指定此周期,宏 pdMS_TO_TICKS() 可用于将以毫秒为单位指定的时间转换为以 tick 为单位指定的时间。 例如, 如果定时器必须要在 100 次 tick 后到期,那么只需将 xTimerPeriod 设置为 100。 或者,如果定时器 必须在 500 毫秒后到期,则需要将 xTimerPeriod 设置为 pdMS_TO_TICKS( 500 )。 使用 pdMS_TO_TICKS() 的唯一前提条件是 configTICK_RATE_HZ 小于或等于 1000。 定时器周期必须大于 0。
  uxAutoReload 如果 uxAutoReload 设置为 pdTRUE ,则定时器将按 xTimerPeriod 参数设置的频率重复到期。 如果 uxAutoReload 设置为 pdFALSE,则此定时器为一次性定时器, 它会在到期后进入休眠状态。
  pvTimerID 分配给正在创建的定时器的标识符。 通常,此标识符用于定时器回调函数: 当同一个回调函数分配给了多个定时器时,此标识符可以识别哪个定时器已到期。 或者此标识符可与 vTimerSetTimerID() 和 pvTimerGetTimerID() API 函数一起使用, 以便保存调用 定时器回调函数之间的值。
  pxCallbackFunction 定时器到期时调用的函数。 回调函数必须有 TimerCallbackFunction_t 定义的原型,即:
void vCallbackFunction( TimerHandle_t xTimer );
返回值:
  如果定时器创建成功, 则返回新创建的定时器的句柄。 如果由于剩余的 FreeRTOS 堆不足以分配定时器结构体而无法创建定时器, 则返回 NULL。

用法示例:

#define NUM_TIMERS 5

/* 一个数组,用于保存创建的定时器的手柄。*/
TimerHandle_t xTimers[ NUM_TIMERS ];

/* 定义一个回调函数,该函数将被多个定时器实例所使用。
实例使用。 这个回调函数不做任何事情,只是计算
计时器过期的次数,并在计时器过期10次后停止计时器。
计时器过期10次后停止。 该计数被保存为
定时器的ID。*/
void vTimerCallback( TimerHandle_t xTimer )
{
    
    
	const uint32_t ulMaxExpiryCountBeforeStopping = 10;
	uint32_t ulCount;

	/* 如果pxTimer参数为NULL,可以选择做一些事情。*/
	configASSERT( xTimer );

	/* 该定时器过期的次数被保存为该定时器的ID。
	定时器的ID。 获取该计数。*/
	ulCount = ( uint32_t ) pvTimerGetTimerID( xTimer );
	
	/* 递增计数,然后测试定时器是否已经过期
	ulMaxExpiryCountBeforeStopping了。*/
	ulCount++;

	/* 如果定时器已经过期10次,则停止其运行。*/
	if( ulCount >= ulMaxExpiryCountBeforeStopping )
	{
    
    
		/* 如果从定时器回调函数中调用定时器API函数,请不要使用阻断时间。
		如果从一个定时器回调函数中调用一个定时器API函数,不要使用阻断时间,因为这样做可能会导致
		死锁! */
		xTimerStop( xTimer, 0 );
	}
	else
	{
    
    
		/* 将增加的计数存回定时器的ID字段中
		以便在下次这个软件定时器过期时可以再次读回它。
		过期。*/
		vTimerSetTimerID( xTimer, ( void * ) ulCount )}
}

void main( void )
{
    
    
	long x;

    /* 创建然后启动一些定时器。 在RTOS调度器启动之前启动计时器
    在RTOS调度程序启动之前就开始计时,这意味着计时器将在RTOS调度程序启动后立即开始运行。
    在RTOS调度程序启动前启动计时器,意味着计时器将在RTOS调度程序启动后立即开始运行。*/
    for( x = 0; x < NUM_TIMERS; x++ )
    {
    
    
        xTimers[ x ] = xTimerCreate
                  ( /* 只是一个文本名称,不被RTOS内核使用。
                    内核使用。*/
                    "定时器"/* 定时器的周期,以ticks为单位,必须大于0。
                    大于0。 */
                    ( 100 * x ) + 100,
                    /* 计时器将在到期时自动重新加载
                    过期时自动重新加载。*/
                    pdTRUE,
                    /* 该ID用于存储计时器过期次数的计数。
                    计时器过期的次数。
                    被初始化为0。*/
                    ( void * ) 0/* 每个定时器在过期时都会调用相同的回调。
                    过期时调用相同的回调。*/
                    vTimerCallback
                  );

        if( xTimers[ x ] == NULL )
        {
    
    
            /* 定时器没有被创建。*/
        }
        else
        {
    
    
            /* 启动该定时器。 没有指定阻塞时间,即使有也会被忽略,因为RTOS
            即使有,也会被忽略,因为RTOS的
            调度器还没有启动。*/
            if( xTimerStart( xTimers[ x ], 0 ) != pdPASS )
            {
    
    
                /* 定时器不能被设置为激活状态。
                状态。*/
            }
        }
    }

    /* ...
    在这里创建任务。
    ... */

    /* 启动RTOS调度器将开始运行定时器
    因为它们已经被设置为活动状态。*/
    vTaskStartScheduler();

    /* 不应到达这里。*/
    for( ; ; )}

二、xTimerCreateStatic()

API原型:

TimerHandle_t xTimerCreateStatic( 
					const char * const pcTimerName,
					const TickType_t xTimerPeriod,
					const UBaseType_t uxAutoReload,
					void * const pvTimerID,
					TimerCallbackFunction_t pxCallbackFunction
					StaticTimer_t *pxTimerBuffer );

  创建一个新的软件定时器实例, 并返回一个可以引用定时器的句柄。
  要使此 RTOS API 函数可用:

  1. configUSE_TIMERS 和 configSUPPORT_STATIC_ALLOCATION 都必须在 FreeRTOSConfig.h 中设置为 1。
  2. FreeRTOS/Source/timer.c C 源文件必须包含在构建中。
    每个软件定时器都需要少量 RAM 来保存定时器的状态。 如果定时器是使用 xTimerCreate() 创建的, 则会从 FreeRTOS 堆中自动分配所需的 RAM。 如果软件定时器是使用 xTimerCreateStatic() 创建的, 则 RAM 由应用程序编写器提供,这需要用到一个附加参数, 但允许在编译时静态分配 RAM 。 请参阅静态分配与 动态分配页面了解更多信息。

  定时器是在休眠状态下创建的。 xTimerStart()、 xTimerReset()、 xTimerStartFromISR()、 xTimerResetFromISR()、 xTimerChangePeriod() 以及 xTimerChangePeriodFromISR() API 函数都可以用于将定时器转换为活动状态。

参数:
  pcTimerName 分配给定时器的可读文本名称。 这样做纯粹是为了协助 调试。 RTOS 内核本身只通过句柄引用定时器, 而从不通过名字引用。
xTimerPeriod 定时器的周期。 以 tick 为单位指定此周期,宏 pdMS_TO_TICKS() 可用于将以毫秒为单位指定的时间转换为以 tick 为单位指定的时间。 例如, 如果定时器必须要在 100 次 tick 后到期,那么只需将 xTimerPeriod 设置为 100。 或者,如果定时器 必须在 500 毫秒后到期,则需要将 xTimerPeriod 设置为 pdMS_TO_TICKS( 500 )。 使用 pdMS_TO_TICKS() 的唯一前提条件是 configTICK_RATE_HZ 小于或等于 1000。 定时器周期必须大于 0。
  uxAutoReload 如果 uxAutoReload 设置为 pdTRUE ,则定时器将按 xTimerPeriod 参数设置的频率重复到期。 如果 uxAutoReload 设置为 pdFALSE,则此定时器为一次性定时器, 它会在到期后进入休眠状态。
  pvTimerID 分配给正在创建的定时器的标识符。 通常,此标识符用于定时器回调函数: 当同一个回调函数分配给了多个定时器时,此标识符可以识别哪个定时器已到期。 或者此标识符可与 vTimerSetTimerID() 和 pvTimerGetTimerID() API 函数一起使用, 以便保存调用 定时器回调函数之间的值。
  pxCallbackFunction 定时器到期时调用的函数。 回调函数必须有 TimerCallbackFunction_t 定义的原型,即:
void vCallbackFunction( TimerHandle_t xTimer );
  pxTimerBuffer 必须指向 StaticTimer_t 类型的变量,然后 用该变量保存定时器的状态。
返回值:
  如果定时器创建成功, 则返回新创建的定时器的句柄。 如果 pxTimerBuffer 为 NULL,则不会创建定时器, 同时返回 NULL。
用法示例:

 pxTimerBuffer #define NUM_TIMERS 5

 /* 一个数组,用于保存创建的定时器的手柄。*/
 TimerHandle_t xTimers[ NUM_TIMERS ];

 /* 一个StaticTimer_t结构的数组,用于存储每个创建的定时器的状态。
 用于存储每个创建的定时器的状态。*/
 StaticTimer_t xTimerBuffers[ NUM_TIMERS ];

 /* 定义一个回调函数,将被多个定时器实例使用。
 实例使用。 这个回调函数不做任何事情,只是计算相关定时器的
 计时器过期的次数,并在计时器过期10次后停止该计时器。
 计时器过期10次后停止。 该计数被保存为
 定时器的ID。*/
 void vTimerCallback( TimerHandle_t xTimer )
 {
    
    
	const uint32_t ulMaxExpiryCountBeforeStopping = 10;
	uint32_t ulCount;

    /* 如果pxTimer参数为NULL,可以选择做一些事情。*/
    configASSERT( pxTimer );

    /* 该定时器过期的次数被保存为该定时器的ID。
    定时器的ID。 获取该计数。*/
    ulCount = ( uint32_t ) pvTimerGetTimerID( xTimer );

    /* 递增计数,然后测试定时器是否已经过期
    ulMaxExpiryCountBeforeStopping了。*/
    ulCount++;

    /* 如果定时器已经过期10次,则停止其运行。*/
    if( ulCount >= ulMaxExpiryCountBeforeStopping )
    {
    
    
        /* 如果从定时器回调函数中调用定时器API函数,请不要使用块状时间。
        如果从一个定时器回调函数中调用一个定时器API函数,不要使用阻断时间,因为这样做可能会导致
        死锁! */
        xTimerStop( xTimer, 0 );
    }
    else
    {
    
    
       /* 将增加的计数存回定时器的ID字段中
       以便在下次这个软件定时器过期时可以再次读回它。
       过期。*/
       vTimerSetTimerID( xTimer, ( void * ) ulCount );
    }
 }

 void main( void )
 {
    
    
	long x;
    /* 创建然后启动一些定时器。 在RTOS调度器启动之前启动计时器
    在RTOS调度程序启动之前就开始计时,这意味着计时器将在RTOS调度程序启动后立即开始运行。
    在RTOS调度程序启动前启动计时器,意味着计时器将在RTOS调度程序启动后立即开始运行。*/
    for( x = 0; x < NUM_TIMERS; x++ )
    {
    
    
        xTimers[ x ] = xTimerCreateStatic
                  ( /* 只是一个文本名称,不被RTOS内核使用。
                    内核使用。*/
                    "定时器"/* 定时器的周期,以ticks为单位,必须大于0。
                    大于0。 */
                    ( 100 * x ) + 100,
                    /* 计时器将在到期时自动重新加载
                    过期时自动重新加载。*/
                    pdTRUE,
                    /* 该ID用于存储计时器过期次数的计数。
                    计时器过期的次数。
                    被初始化为0。*/
                    ( void * ) 0/* 每个定时器在过期时都会调用相同的回调。
                    过期时调用相同的回调。*/
                    vTimerCallback,
                    /*传入一个StaticTimer_t变量的地址。
                    变量的地址,它将保存与正在创建的定时器相关的数据。
                    计时器的相关数据。*/
                    &( xTimerBuffers[ x ] ));

        if( xTimers[ x ] == NULL )
        {
    
    
            /* 定时器没有被创建。*/
        }
        else
        {
    
    
            /* 启动该定时器。 没有指定阻塞时间,即使有也会被忽略,因为RTOS
            即使有,也会被忽略,因为RTOS的
            调度器还没有启动。*/
            if( xTimerStart( xTimers[ x ], 0 ) != pdPASS )
            {
    
    
                /* 定时器不能被设置为激活状态。
                状态。*/
            }
        }
    }

    /* ...
    在这里创建任务。
    ... */

    /* 启动RTOS调度器将开始运行定时器
    因为它们已经被设置为活动状态。*/
    vTaskStartScheduler();

    /* 不应到达这里。*/
    for( ; ; );
 }

三、xTimerIsTimerActive()

API原型:

BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer );

  查询软件定时器是否处于活动或休眠状态。

  如果出现以下情况,定时器将处于休眠状态:

    1. 已创建但尚未启动,或这是一个尚未重启的过期的一次性计时器。
    1. 定时器是在休眠状态下创建的。 xTimerStart()、 xTimerReset()、 xTimerStartFromISR()、 xTimerResetFromISR()、 xTimerChangePeriod() 以及 xTimerChangePeriodFromISR() API 函数都可以用于将定时器转换为活动状态。

参数:
  xTimer 被查询的定时器。
返回值:
  如果定时器处于休眠状态,将返回 pdFALSE。 如果定时器处于活动状态,将返回 pdFALSE 以外的值。
用法示例:

 /*此函数假定xTimer已经被创建。
 已经创建。*/
 void vAFunction( TimerHandle_t xTimer )
 {
    
    
     /* 或者更简单、更等价地
     "if( xTimerIsTimerActive( xTimer ))" */
     if( xTimerIsTimerActive( xTimer ) != pdFALSE )
     {
    
    
         /* xTimer是活动的,做一些事情。*/
     }
     else
     {
    
    
         /* xTimer没有被激活,做一些其他事情。*/
     }
 }

四、xTimerStart()

API原型:

BaseType_t xTimerStart( TimerHandle_t xTimer,TickType_t xBlockTime );

用法示例:
  软件定时器功能由定时器服务/守护进程任务提供。 许多 公共 FreeRTOS 定时器 API 函数通过定时器命令队列 向定时器服务任务发送命令。 定时器命令队列是 RTOS 内核本身的私有队列, 应用程序代码无法直接访问该队列。 定时器命令队列的长度 由 configTIMER_QUEUE_LENGTH 配置常量设置。
  xTimerStart() 启动先前使用 xTimerCreate() API 函数创建的定时器。 如果定时器已经启动且已处于活动状态, 那么 xTimerStart() 具有与 xTimerReset() API 函数等效的功能。
  启动定时器可确保定时器处于活动状态。 如果定时器 在此期间没有被停用、删除或重置, 那么在调用 xTimerStart() 并经过了 “n” 个 tick 之后,将调用与定时器相关联的回调函数, 其中 “n” 表示规定的定时器周期。
  在 RTOS 调度器启动之前调用 xTimerStart() 是有效的, 但是完成此操作后,直到启动 RTOS 调度器之前,定时器都不会真正启动, 并且定时器的到期时间与 RTOS 调度器的启动时间有关, 与调用 xTimerStart() 的时间无关。
  configUSE_TIMERS 配置常量必须设置为 1,xTimerStart() 才可用。
参数:
  xTimer 正在启动/重新启动的定时器的句柄。
  xBlockTime 在调用 xTimerStart() 时队列已满的情况下, 指定调用任务应保持阻塞状态 以等待启动命令成功发送到定时器命令队列的时间 (以 tick 为单位)。 如果在 启动 RTOS 调度器之前调用 xTimerStop(),则会忽略 xBlockTime。
返回值:
  如果即便经过了 xBlockTime(以 tick 为单位)后启动命令仍无法发送到定时器命令队列,则返回 pdFAIL。 如果该命令成功发送到定时器命令队列, 则返回 pdPASS。 实际处理命令的时间取决于 定时器服务/守护进程任务相对于系统中其他任务的优先级, 尽管定时器到期时间与实际调用 xTimerStart() 的时间有关。 定时器 服务/守护进程任务的优先级由 configTIMER_TASK_PRIORITY 配置常量设置。

五、xTimerStop()

API原型:

BaseType_t xTimerStop( TimerHandle_t xTimer,TickType_t xBlockTime );

  软件定时器功能由定时器服务/守护进程任务提供。 许多 公共 FreeRTOS 定时器 API 函数通过定时器命令队列 向定时器服务任务发送命令。 定时器命令队列是 RTOS 内核本身的私有队列, 应用程序代码无法直接访问该队列。 定时器命令队列的长度 由 configTIMER_QUEUE_LENGTH 配置常量设置。

  xTimerStop() 停止先前使用 xTimerStart()、 xTimerReset()、 xTimerStartFromISR()、 xTimerResetFromISR()、 xTimerChangePeriod() 以及 xTimerChangePeriodFromISR() API 函数之一启动的定时器。
  停用定时器可确保定时器不处于活动状态。
  configUSE_TIMERS 配置常量必须设置为 1,xTimerStop() 才可用。
参数:
  xTimer 正在停止的定时器的句柄。
  xBlockTime 在调用 xTimerStop() 时队列已满的情况下, 指定调用任务应保持阻塞状态 以等待停止命令成功发送到定时器命令队列的时间 (以 tick 为单位)。 如果在 启动 RTOS 调度器之前调用 xTimerStop(),则会忽略 xBlockTime。
返回值:
  如果即使经过了 xBlockTime(以 tick 为单位)之后, 停止命令仍无法发送到定时器命令队列,则返回 pdFAIL。 如果该命令成功发送到定时器命令队列, 则返回 pdPASS。 实际处理命令的时间取决于 定时器服务/守护进程任务相对于系统中其他任务的优先级。 定时器服务/守护进程任务的优先级由 服务/守护进程任务的优先级由 configTIMER_TASK_PRIORITY 配置常量设置。

六、xTimerChangePeriod()

API原型:

BaseType_t xTimerChangePeriod( TimerHandle_t xTimer,
                                   TickType_t xNewPeriod,
                                   TickType_t xBlockTime );

  软件定时器功能由定时器服务/守护进程任务提供。 许多 公共 FreeRTOS 定时器 API 函数通过定时器命令队列 向定时器服务任务发送命令。 定时器命令队列是 RTOS 内核本身的私有队列, 应用程序代码无法直接访问该队列。 定时器命令队列的长度 由 configTIMER_QUEUE_LENGTH 配置常量设置。
  xTimerChangePeriod() 可以改变先前使用 xTimerCreate() API 函数创建的定时器的周期。
  可以调用 xTimerChangePeriod() 来更改活动 或休眠状态的定时器的周期。 更改休眠定时器的周期也会启动 定时器。
  必须将 configUSE_TIMERS 配置常量设置为 1, xTimerChangePeriod() 才可用。
参数:
  xTimer 正在更改其周期的定时器的句柄。
  xNewPeriod xTimer 的新周期。定时器周期 在 tick 周期中指定,因此常量 portTICK_PERIOD_MS 可用于转换 已指定的时间(以毫秒为单位)。 例如,如果定时器必须 在100个 tick 后过期,则 xNewPeriod 应设置为100。 或者, 如果定时器必须在500 毫秒后过期,则 xNewPeriod 可以设置为 (500/portTICK_PERIOD_MS) ,前提是 configTICK_RATE_HZ 小于 或等于 1000。
  xBlockTime 在调用 xTimerDelete() 时队列已满的情况下, 指定调用任务应保持阻塞状态 以等待变更周期命令成功发送到定时器命令队列的时间 (以 tick 为单位)。 如果在 启动 RTOS 调度器之前调用 xTimerChangePeriod(),则会忽略 xBlockTime。
返回值:
  如果即使经过 xBlockTime(以 tick 为单位) 之后仍无法将更改周期命令发送到定时器命令队列,则将返回 pdFAIL。 如果能将此命令成功发送到定时器命令队列,则返回 pdPASS 则返回 pdPASS。 实际处理命令的时间取决于 定时器服务/守护进程任务相对于系统中其他任务的 优先级。 定时器服务/守护进程任务的优先级 由 configTIMER_TASK_PRIORITY 配置常量设置。
用法示例:

/*此函数假定xTimer已经被创建。 如果在调用该函数时,xTimer所引用的定时器
所引用的定时器已经激活,那么该定时器将被删除。
将被删除。 如果xTimer引用的定时器在它被调用时没有被激活,那么该定时器的周期被设置为
计时器的周期被设置为500ms,并且计时器被启动。
启动。*/
void vAFunction( TimerHandle_t xTimer )
{
    
    
    /* 或者更简单、更等价地
    "if( xTimerIsTimerActive( xTimer ))" */
    if( xTimerIsTimerActive( xTimer ) != pdFALSE )
    {
    
    
        /* xTimer已经处于活动状态 - 删除它。*/
        xTimerDelete( xTimer );
    }
    else
    {
    
    
        /* xTimer未被激活,将其周期改为500ms。 这也将
        导致定时器启动。 如果改变周期的命令不能立即发送给定时器,则最多阻断100次。
        如果改变周期的命令不能立即被发送到定时器的
        命令队列。*/
        if( xTimerChangePeriod( xTimer, 500 / portTICK_PERIOD_MS, 100 ) == pdPASS )
        {
    
    
            /* 该命令已成功发送。*/
        }
        else
        {
    
    
            /* 该命令无法被发送,即使在等待了100次后
            也无法发送。 在这里采取适当的行动。*/
        }
    }
}

七、xTimerDelete()

API原型:

BaseType_t xTimerDelete( TimerHandle_t xTimer,TickType_t xBlockTime );

  软件定时器功能由定时器服务/守护进程任务提供。 许多 公共 FreeRTOS 定时器 API 函数通过定时器命令队列 向定时器服务任务发送命令。 定时器命令队列是 RTOS 内核本身的私有队列, 应用程序代码无法直接访问该队列。 定时器命令队列的长度 由 configTIMER_QUEUE_LENGTH 配置常量设置。
  xTimerDelete() 可删除以前使用 xTimerCreate() API 函数创建的定时器。请注意,删除静态分配的定时器时,在 xTimerIsTimerActive() 指示该定时器处于非活动状态之前,无法重复使用其静态内存。
  必须将 configUSE_TIMERS 配置常量设置为 1, xTimerDelete() 才可用。
参数:
  xTimer 正在删除的定时器的句柄。
  xBlockTime 在调用 xTimerDelete() 时队列已满的情况下, 指定调用任务应保持阻塞状态 以等待删除命令成功发送到定时器命令队列的时间 (以 tick 为单位)。 如果 在 RTOS 调度器启动之前调用 xTimerDelete(),则忽略 xBlockTime。
返回值:
  如果即使经过 xBlockTime(以 tick 为单位) 之后仍无法将删除命令发送到定时器命令队列,则将返回 pdFAIL。 如果能将此命令成功发送到定时器命令队列,则返回 pdPASS 则返回 pdPASS。 实际处理命令的时间取决于 定时器服务/守护进程任务相对于系统中其他任务的 优先级。 定时器服务/守护进程任务的优先级 由 configTIMER_TASK_PRIORITY 配置常量设置。

八、xTimerReset()

API原型:

BaseType_t xTimerReset( TimerHandle_t xTimer,TickType_t xBlockTime );

  软件定时器功能由定时器服务/守护进程任务提供。 许多 公共 FreeRTOS 定时器 API 函数通过定时器命令队列 向定时器服务任务发送命令。 定时器命令队列是 RTOS 内核本身的私有队列, 应用程序代码无法直接访问该队列。 定时器命令队列的长度 由 configTIMER_QUEUE_LENGTH 配置常量设置。
  xTimerReset() 重新启动先前使用 xTimerCreate() API 函数创建的定时器。 如果定时器已经启动且已处于活动状态, 那么 xTimerReset() 会使定时器 重新评估其到期时间,因此到期时间与 xTimerReset() 的调用时间有关。 如果定时器处于休眠状态,则 xTimerReset() 具有与 xTimerStart() API 函数等效的功能。
  重置定时器可确保定时器处于活动状态。 如果定时器 在此期间没有被停用、删除或重置, 那么在调用 xTimerReset() 并经过了 “n” 个 tick 之后,将调用与定时器相关联的回调函数, 其中“n”表示规定的定时器周期。
  在 RTOS 调度器启动之前调用 xTimerReset() 是有效的, 但是完成此操作后,直到启动 RTOS 调度器之前,定时器都不会真正启动, 并且定时器的到期时间与 RTOS 调度器的启动时间有关, 与调用 xTimerReset() 的时间无关。
  configUSE_TIMERS 配置常量必须设置为 1,xTimerReset() 才可用。
参数:
  xTimer 正在重置/启动/重新启动的定时器的句柄。
  xBlockTime 在调用 xTimerReset() 时队列已满的情况下, 指定调用任务应保持阻塞状态 以等待重置命令成功发送到定时器命令队列的时间 (以 tick 为单位)。 如果在 启动 RTOS 调度器之前调用 xTimerReset(),则忽略 xBlockTime。
返回值:
  如果即使经过 xBlockTime(以 tick 为单位) 之后仍无法将重置命令发送到定时器命令队列,则返回 pdFAIL。 如果能将此命令成功发送到定时器命令队列,则返回 pdPASS 。 实际处理命令的时间取决于 定时器服务/守护进程任务相对于系统中其他任务的优先级, 尽管定时器到期时间与实际调用 xTimerReset() 的时间有关。 定时器 服务/守护进程任务的优先级由 configTIMER_TASK_PRIORITY 配置常量设置。
用法示例:

/* 当一个键被按下时,LCD背光灯被打开。 如果5秒钟过去了
5秒后没有按键,那么LCD背光就会关闭。 在这种情况下
在这种情况下,该定时器是一个一次性的定时器。*/

TimerHandle_t xBacklightTimer = NULL/* 分配给一次性定时器的回调函数。 在这种情况下
参数不被使用。*/
void vBacklightTimerCallback( TimerHandle_t pxTimer )
{
    
    
    /* 计时器过期了,因此在按下一个键之后,必须有5秒钟的时间。
    计时器已经过期,因此必须在按下一个键之后过了5秒钟。 关闭LCD背光灯。*/
    vSetBacklightState( BACKLIGHT_OFF )}

/* 按键事件处理程序。*/
void vKeyPressEventHandler( char cKey )
{
    
    
    /* 确保LCD背光灯打开,然后重置负责在5秒后关闭背光灯的计时器。
    负责在按键不活动5秒后关闭背光灯的计时器。
    5秒后关闭背光。 如果不能立即发送命令,则等待10秒钟后成功发送。
    如果不能立即发送。*/
    vSetBacklightState( BACKLIGHT_ON )if( xTimerReset( xBacklightTimer, 10 ) != pdPASS )
    {
    
    
        /* 重置命令没有成功执行。 采取适当的
        在这里采取适当的行动。*/
    }

    /* 在此执行其余的按键处理。*/
}

void main( void )
{
    
    
	long x;/* 创建并启动单次定时器,负责在5秒内没有按键时关闭背光灯。
    如果在5秒钟内没有按任何键,则负责关闭背光灯。*/
    xBacklightTimer = xTimerCreate
             (
                  /* 只是一个文本名称,不被RTOS内核使用。*/
                  "BacklightTimer",
                  /* 定时器的周期,以ticks为单位。*/
                  ( 5000 / portTICK_PERIOD_MS)/* 定时器是一个一次性的定时器。*/
                  pdFALSE,
                  /* id不被回调使用,所以可以使用任何值。
                  值。*/
                  0,
                  /* 回调函数,用于关闭LCD背光。
                  关闭的回调函数。*/
                  vBacklightTimerCallback
              );

    if( xBacklightTimer == NULL )
    {
    
    
        /* 定时器没有被创建。*/
    }
    else
    {
    
    
        /* 启动该定时器。 没有指定阻塞时间,即使指定了
        也会被忽略,因为RTOS的调度程序还没有被启动。
        启动。*/
        if( xTimerStart( xBacklightTimer, 0 ) != pdPASS )
        {
    
    
            /* 定时器不能被设置为激活状态。*/
        }
    }

    /* ...
     在这里创建任务。
    ... */

    /* 启动RTOS调度器将启动定时器的运行,因为它已经被设置为活动状态。
    已经被设置为活动状态。*/
    vTaskStartScheduler()/* 不应到达这里。*/
    for( ; ; )}

九、xTimerStartFromISR()

API原型:

BaseType_t xTimerStartFromISR(
                  TimerHandle_t xTimer,
                  BaseType_t *pxHigherPriorityTaskWoken);

  可从中断服务例程调用的 xTimerStart() 的版本。
参数:
  xTimer 正在启动/重新启动的定时器的句柄。
  pxHigherPriorityTaskWoken 定时器服务/守护进程任务大部分时间 都处于阻塞状态,等待消息到达定时器 命令队列。 调用 xTimerStartFromISR() 会将消息写入定时器 命令队列,从而让定时器服务/守护进程 任务有可能脱离阻塞状态。 如果调用 xTimerStartFromISR() 导致 定时器服务/守护进程任务脱离阻塞状态,且定时器服务/ 守护进程任务的优先级等于或大于当前执行的 任务(被中断的任务),那 *pxHigherPriorityTaskWoken 将在 xTimerStartFromISR() 函数内部被设置为 pdTRUE。 如果 xTimerStartFromISR() 将此值设置为 pdTRUE, 那应在中断退出前执行上下文切换。
返回值:
  如果启动命令无法发送至定时器命令队列, 则返回 pdFAIL。 如果命令成功发送至定时器命令队列, 则返回 pdPASS。 实际处理命令的时间 取决于定时器服务/守护进程任务相对于系统中其他任务的优先级, 尽管定时器到期时间 是相对于实际调用 xTimerStartFromISR() 的时间而言。 定时器服务/守护进程 任务优先级由 configTIMER_TASK_PRIORITY 配置常量设置。
用法示例:

/* 本方案假设xBacklightTimer已经被创建。 当一个
键被按下时,LCD背光灯被打开。 如果5秒钟过去了
5秒后没有按键,LCD背光就会关闭。 在这种情况下
在这种情况下,定时器是一个一次性的定时器,与所给的例子不同的是
xTimerReset()函数的例子不同,按键事件处理程序是一个中断
服务例程。*/

/* 指派给一次性定时器的回调函数。 在这种情况下
参数不被使用。*/
void vBacklightTimerCallback( TimerHandle_t pxTimer )
{
    
    
    /* 计时器过期了,因此在按下一个键之后,必须有5秒钟的时间。
    计时器已经过期,因此必须在按下一个键之后过了5秒钟。 关闭LCD背光灯。*/
    vSetBacklightState( BACKLIGHT_OFF )}

/* 按键中断服务例程。*/
void vKeyPressEventInterruptHandler( void )
{
    
    
	BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    /* 确保LCD背光灯是亮着的,然后重新启动负责关闭背光灯的定时器。
    负责在按键不活动5秒后关闭背光灯的计时器。
    键不活动后,重新启动负责关闭背光的计时器。 这是一个中断服务例程,所以只能
    调用以 "FromISR "结尾的FreeRTOS API函数。*/
    vSetBacklightState( BACKLIGHT_ON );

    /* 这里可以调用xTimerStartFromISR()或xTimerResetFromISR()。
    因为两者都会导致定时器重新计算其到期时间。
    xHigherPriorityTaskWoken在声明时被初始化为pdFALSE。
    声明时被初始化为pdFALSE(在这个函数中)。*/
    if( xTimerStartFromISR( xBacklightTimer,&xHigherPriorityTaskWoken ) != pdPASS )
    {
    
    
        /* 启动命令没有成功执行。 采取适当的
        在这里采取适当的行动。*/
    }

    /* 在此执行其余的键处理。*/

    /* 如果xHigherPriorityTaskWoken等于pdTRUE,那么就应该进行上下文切换。
    就应该执行上下文切换。 在ISR内部执行上下文切换所需的语法
    所需的语法在不同的端口和不同的编译器中都有所不同。
    编译器有所不同。 检查你正在使用的端口的演示,以找到
    所需的实际语法。*/
    if( xHigherPriorityTaskWoken != pdFALSE )
    {
    
    
        /* 在这里调用中断安全输出函数(实际函数
        取决于正在使用的FreeRTOS端口)。*/
    }
}

十、xTimerStopFromISR()

API原型:

BaseType_t xTimerStopFromISR(
                TimerHandle_t xTimer,
                BaseType_t *pxHigherPriorityTaskWoken);

  可从中断服务例程调用的 xTimerStop() 的版本。
参数:
  xTimer 正在停止的定时器的句柄。
  pxHigherPriorityTaskWoken 定时器服务/守护进程任务大部分时间 都处于阻塞状态,等待消息到达定时器 命令队列。 调用 xTimerStopFromISR() 会将消息写入 定时器命令队列,因此有可能将定时器服务/ 守护进程任务转换为非阻塞状态。 如果调用 xTimerStopFromISR(), 会导致定时器服务/守护进程任务退出阻塞状态,并且 守护进程任务的优先级等于或大于当前执行的 当前正在执行的任务(中断的任务),则会在 xTimerStopFromISR() 函数内部 将 *pxHigherPriorityTaskWoken 设置为 pdTRUE。 如果 xTimerStopFromISR() 将此值设置为 pdTRUE,则应在中断退出之前 那应在中断退出前执行上下文切换。
返回值:
  如果即使经过了 xBlockTime(以 tick 为单位)之后, 则返回 pdFAIL。 如果命令成功发送至定时器命令队列, 则返回 pdPASS。 实际处理命令的时间 取决于定时器服务/守护进程任务相对于系统中其他任务的 优先级。 定时器服务/守护进程 任务优先级由 configTIMER_TASK_PRIORITY 配置常量设置。
用法示例:

/* 本方案假设xTimer已经被创建并启动。 当
中断发生时,定时器应该被简单地停止。*/

/*停止定时器的中断服务例程。*/
void vAnExampleInterruptServiceRoutine( void )
{
    
    
	BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    /* 中断已经发生--只需停止定时器。
    xHigherPriorityTaskWoken被设置为pdFALSE,它被定义在
    (在这个函数中)。 由于这是一个中断服务例程,只有
    FreeRTOS API中以 "FromISR "结尾的函数可以被使用。*/
    if( xTimerStopFromISR( xTimer, &xHigherPriorityTaskWoken ) != pdPASS )
    {
    
    
        /* 停止命令没有成功执行。 采取适当的
        在这里采取适当的行动。*/
    }

    /* 如果xHigherPriorityTaskWoken等于pdTRUE,那么就应该进行上下文切换。
    应该被执行。 在ISR内部执行上下文切换所需的语法
    所需的语法在不同的端口和不同的编译器中都有所不同。
    编译器有所不同。 检查你正在使用的端口的演示,以找到
    所需的实际语法。*/
    if( xHigherPriorityTaskWoken != pdFALSE )
    {
    
    
        /* 在这里调用中断安全输出函数(实际函数
        取决于正在使用的FreeRTOS端口)。*/
    }
}

十一、xTimerChangePeriodFromISR()

API原型:

BaseType_t xTimerChangePeriodFromISR(
						             TimerHandle_t xTimer,
						             TickType_t xNewPeriod,
						             BaseType_t *pxHigherPriorityTaskWoken);

  可从中断服务例程调用的 xTimerChangePeriod() 的版本。
参数:
  xTimer 正在更改其周期的软件定时器的句柄。
  xNewPeriod xTimer 的新周期。定时器周期 在 tick 周期中指定,因此常量 portTICK_PERIOD_MS 可用于转换 已指定的时间(以毫秒为单位)。 例如,如果定时器必须 在100个 tick 后过期,则 xNewPeriod 应设置为100。 或者, 如果定时器必须在500 毫秒后过期,则 xNewPeriod 可以设置为 (500/portTICK_PERIOD_MS) ,前提是 configTICK_RATE_HZ 小于 或等于1000。
  pxHigherPriorityTaskWoken 定时器服务/守护进程任务大部分时间 都处于阻塞状态,等待消息到达定时器 命令队列。 调用 xTimerChangePeriodFromISR() 会将消息写入 定时器命令队列,因此有可能将定时器服务/ 守护进程任务转换为非阻塞状态。 如果调用 xTimerChangePeriodFromISR() 导致定时器服务/守护进程任务退出阻塞状态,并且 定时器服务/守护进程任务的优先级等于或大于 当前正在执行任务(被中断的任务) ,则 *pxHigherPriorityTaskWoken 将获得在 xTimerChangePeriodFromISR() 函数内部 设置为 pdTRUE 。 如果 xTimerChangePeriodFromISR() 将此值设置为 pdTRUE ,则应在中断退出前执行上下文切换 。
返回值:
  如果更改定时器周期的命令无法发送到定时器命令队列, 则返回 pdFAIL 。 如果命令已成功发送到定时器命令队列, 则返回 pdPASS 。 实际处理命令的时间 将取决于定时服务/守护进程任务相对于系统中的其他任务的 优先级。 定时服务/守护进程任务 优先级由 configTIMER_TASK_PRIORITY 配置常量设置。
用法示例:

/* 本方案假设xTimer已经被创建并启动。 当时,xTimer的周期应该改变为500ms。*/
/*改变xTimer周期的中断服务程序。*/
void vAnExampleInterruptServiceRoutine( void )
{
    
    
	BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    /* 中断已经发生--将xTimer的周期改为500ms。
    xHigherPriorityTaskWoken被设置为pdFALSE,它被定义在
    (在这个函数中)。 由于这是一个中断服务例程,只有
    FreeRTOS API中以 "FromISR "结尾的函数可以被使用。*/
    if( xTimerChangePeriodFromISR( xTimer,
                                   pdMS_TO_TICKS( 500 ),
                                   &xHigherPriorityTaskWoken ) != pdPASS )
    {
    
    
        /* 更改定时器周期的命令没有被成功执行。
        成功执行。 在这里采取适当的行动。*/
    }

    /* 如果xHigherPriorityTaskWoken等于pdTRUE,那么就应该进行上下文切换。
    应该被执行。 在ISR内部执行上下文切换所需的语法
    所需的语法在不同的端口和不同的编译器中都有所不同。
    编译器有所不同。 检查你正在使用的端口的演示,以找到
    所需的实际语法。*/
    if( xHigherPriorityTaskWoken != pdFALSE )
    {
    
    
        /* 在这里调用中断安全输出函数(实际函数
        取决于正在使用的FreeRTOS端口)。*/
    }
}

十二、xTimerResetFromISR()

API原型:

BaseType_t xTimerResetFromISR(
                  TimerHandle_t xTimer,
                  BaseType_t *pxHigherPriorityTaskWoken);

  可从中断服务例程调用的 xTimerReset() 的版本。
参数:
  xTimer 待启动、重置或重启的定时器的句柄。
  pxHigherPriorityTaskWoken 定时器服务/守护进程任务大部分时间 都处于阻塞状态,等待消息到达定时器 命令队列。 调用 xTimerResetFromISR() 将消息写入定时器 命令队列,从而让定时器服务/守护进程 任务有可能脱离阻塞状态。 如果调用 xTimerResetFromISR() 导致 定时器服务/守护进程任务脱离阻塞状态,且定时器服务/ 守护进程任务的优先级等于或大于当前执行的 任务(被中断的任务),那 *pxHigherPriorityTaskWoken 将在 xTimerResetFromISR() 函数内部被设置为 pdTRUE。 如果 xTimerResetFromISR() 将此值设置为 pdTRUE, 那应在中断退出前执行上下文切换。
返回值:
  如果重置命令无法发送至定时器命令队列, 则将返回 pdFAIL。 如果命令成功发送至定时器命令队列, 则返回 pdPASS。 实际处理命令的时间 取决于定时器服务/守护进程任务相对于系统中其他任务的优先级, 尽管定时器到期时间 和实际调用 xTimerResetFromISR() 的时间相关。 定时器服务/守护进程 任务优先级由 configTIMER_TASK_PRIORITY 配置常量设置。
用法示例:

/* 本方案假设xBacklightTimer已经被创建。 当一个
键被按下时,LCD背光灯被打开。 如果5秒钟过去了
5秒后没有按键,LCD背光就会关闭。 在这种情况下
在这种情况下,定时器是一个一次性的定时器,与所给的例子不同的是
xTimerReset()函数的例子不同,按键事件处理程序是一个中断
服务例程。*/

/* 指派给一次性定时器的回调函数。 在这种情况下
参数不被使用。*/
void vBacklightTimerCallback( TimerHandle_t pxTimer )
{
    
    
    /* 计时器过期了,因此在按下一个键之后,必须有5秒钟的时间。
    计时器已经过期,因此必须在按下一个键之后过了5秒钟。 关闭LCD背光灯。*/
    vSetBacklightState( BACKLIGHT_OFF );
}

/* 按键中断服务例程。*/
void vKeyPressEventInterruptHandler( void )
{
    
    
	BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    /* 确保LCD背光灯打开,然后重置负责关闭背光灯的定时器。
    负责在5秒后关闭背光灯的计时器。
    5秒后关闭背光。 这是一个中断服务例程,所以只能
    调用以 "FromISR "结尾的FreeRTOS API函数。*/
    vSetBacklightState( BACKLIGHT_ON );

    /* 这里可以调用xTimerStartFromISR()或xTimerResetFromISR()。
    因为两者都会导致定时器重新计算其到期时间。
    xHigherPriorityTaskWoken在声明时被初始化为pdFALSE。
    声明时被初始化为pdFALSE(在这个函数中)。*/
    if( xTimerResetFromISR( xBacklightTimer,&xHigherPriorityTaskWoken ) != pdPASS )
    {
    
    
        /* 重置命令没有成功执行。 采取适当的
        在这里采取适当的行动。*/
    }

    /* 在此执行其余的按键处理。*/

    /* 如果xHigherPriorityTaskWoken等于pdTRUE,那么就应该进行上下文切换。
    就应该执行上下文切换。 在ISR内部执行上下文切换所需的语法
    所需的语法在不同的端口和不同的编译器中都有所不同。
    编译器有所不同。 检查你正在使用的端口的演示,以找到
    所需的实际语法。*/
    if( xHigherPriorityTaskWoken != pdFALSE )
    {
    
    
        /* 在这里调用中断安全输出函数(实际函数
        取决于正在使用的FreeRTOS端口)。*/
    }
}

十三、pvTimerGetTimerID()

API原型:

void *pvTimerGetTimerID( TimerHandle_t xTimer );

  返回分配给软件计时器的 ID。
  使用调用 xTimerCreate() 时的 pvTimerID 参数将 ID 分配给定时器, 该函数用于创建定时器。
  创建定时器时,会为定时器分配一个标识符 (ID), 您随时可以使用 vTimerSetTimerID() API 函数更改此 ID。
  如果将同一个回调函数分配给多个定时器, 则可以在回调函数内检查定时器标识符, 以确定哪个定时器实际已到期。
  在定时器回调函数的调用之间,定时器标识符也可用于将数据存储在定时器中 。
参数:
  xTimer 被查询的定时器。
返回值:
  分配给被查询的定时器的 ID。

十四、vTimerSetReloadMode()

API原型:

 void vTimerSetReloadMode( TimerHandle_t xTimer,const UBaseType_t uxAutoReload );

  将软件定时器的“模式”更新为自动重新加载定时器或一次性 定时器。
  自动重新加载定时器每次过期都会自行重置,从而导致定时器 定期到期(并因此执行其回调)。
  一次性定时器不会自动重置,因此除非手动重新启动, 否则只会过期一次(并因此执行其回调)。
  此 API 函数仅在已构建项目中包含 FreeRTOS/Source/timers.c 源文件时 可用。
参数:
  xTimer 要更新的定时器的句柄。 该句柄 将从对用于创建定时器的 xTimerCreate() 或 xTimerCreateStatic() 调用中返回。
  uxAutoReload 将 uxAutoReload 设置为 pdTRUE 以将定时器设置为自动重新加载 模式,或设置为 pdFALSE 以将定时器设置为一次性模式。

十五、vTimerSetTimerID()

API原型:

void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID );

  创建定时器时,会为软件定时器分配一个标识符 (ID), 您可随时使用 vTimerSetTimerID() API 函数更改此标识符。
  如果将同一个回调函数分配给多个定时器, 则可以在回调函数内检查定时器标识符, 以确定哪个定时器实际已到期。
  在定时器回调函数的调用之间,定时器标识符也可用于将数据存储在定时器中 。
参数:
  xTimer 更新的计时器。
  pvNewID 句柄,定时器标识符将被设置为此句柄。
用法示例:

/* 一个分配给定时器的回调函数。*/
void TimerCallbackFunction( TimerHandle_t pxExpiredTimer )
{
    
    
	uint32_t ulCallCount;
    /* 这个定时器已经过期并执行其回调函数的次数。
    并执行其回调函数的次数被存储在
    定时器的ID中。 检索该计数,将其递增,然后保存到
    检索该计数,将其递增,然后将其存回定时器的ID中。*/
    ulCallCount = ( uint32_t ) pvTimerGetTimerID( pxExpiredTimer );
    ulCallCount++vTimerSetTimerID( pxExpiredTimer, ( void * ) ulCallCount );
}

十六、xTimerGetTimerDaemonTaskHandle()

API原型:

TaskHandle_t xTimerGetTimerDaemonTaskHandle( void );

返回值:
返回与软件定时器守护进程(或服务)任务 关联的任务句柄。 如果在 FreeRTOSConfig.h 中将 configUSE_TIMERS 设置为 1, 则在启动 RTOS 调度器时会自动创建定时器守护进程任务。

十七、xTimerPendFunctionCall()

API原型:

 BaseType_t xTimerPendFunctionCall(
                            PendedFunction_t xFunctionToPend,
                            void *pvParameter1,
                            uint32_t ulParameter2,
                            TickType_t xTicksToWait );

  用于将函数的执行挂起到 RTOS 守护进程任务(定时器 服务任务,因此此函数前缀为“Timer”)。
  可延迟到 RTOS 守护进程任务的函数必须具有以下 原型:

void vPendableFunction( void * pvParameter1, uint32_t ulParameter2 );

  pvParameter1 和 ulParameter2 供 应用程序代码使用。
  INCLUDE_xTimerPendFunctionCall() 和 configUSE_TIMERS 必须同时设置为 1,xTimerPendFunctionCall() 才可用。
参数:
  xFunctionToPend 要从定时器服务/ 守护进程任务执行的函数。 函数必须符合上面所示的 PendedFunction_t 如上所示的 PendedFunction_t 原型。
  pvParameter1 回调函数的第一个参数的值。 该参数为 void * 类型,可用于传递任何类型。 例如,整数类型可转换为 void *, 或者可使用 void * 指向结构体。
  ulParameter2 回调函数的第二个参数的值。
  xTicksToWait 调用此函数将导致 向队列中的定时器守护进程任务发送一条消息。 xTicksToWait 是指在队列已满的情况下,需要阻塞调用任务 (不使用任何处理时间)以等待定时器队列 释放可用空间的时间。 队列的长度由 FreeRTOSConfig.h 中的 configTIMER_QUEUE_LENGTH 值设置。
返回值:
  如果消息成功发送到 RTOS 则返回 pdPASS,否则返回 pdFALSE。

十八、xTimerPendFunctionCallFromISR()

API原型:

 BaseType_t xTimerPendFunctionCallFromISR(
                       PendedFunction_t xFunctionToPend,
                       void *pvParameter1,
                       uint32_t ulParameter2,
                       BaseType_t *pxHigherPriorityTaskWoken );

  在应用程序中断服务程序中使用此函数, 用于将函数的执行推迟到 RTOS 守护进程任务(即定时器服务任务,因此该函数 在 timers.c 中实现,并以“Timer”为前缀)。
  理想情况下,中断服务程序 (ISR) 需尽可能短, 但有时 ISR 要么需要处理很多任务, 要么需要执行非确定性任务。 在这些情况下,可使用 xTimerPendFunctionCallFromISR() 将 函数的处理推迟到 RTOS 守护进程任务。
  这里提供了一种允许中断直接返回到 随后将执行挂起函数任务的机制。 使用该机制, 回调函数可在中断时间内连续执行, 就像回调在中断本身中执行一样。
  可延迟到 RTOS 守护进程任务的函数必须具有以下 原型:

void vPendableFunction( void * pvParameter1, uint32_t ulParameter2 );

  pvParameter1 和 ulParameter2 供 应用程序代码使用。
  INCLUDE_xTimerPendFunctionCall() 和 configUSE_TIMERS 必须同时 设置为 1,xTimerPendFunctionCallFromISR() 才可用。
参数:
  xFunctionToPend 要从定时器服务/ 守护进程任务执行的函数。 函数必须符合上面所示的 PendedFunction_t 如上所示的 PendedFunction_t 原型。
  pvParameter1 回调函数的第一个参数的值。 该参数为 void * 类型,可用于传递任何类型。 例如,整数类型可转换为 void *, 或者可使用 void * 指向结构体。
  ulParameter2 回调函数的第二个参数的值。
  pxHigherPriorityTaskWoken 如上所述,调用 xTimerPendFunctionCallFromSR() 将意味着向 RTOS 定时器守护进程任务发送一条消息。 如果 守护进程任务的优先级(该任务 使用 configTIMER_TASK_PRIORITY 设置, 位于 FreeRTOSConfig.h 中)高于 当前正在运行的任务(中断中断的任务), 则需在 xTimerPendFunctionCallFromISR() 中 将 *pxHigherPriorityTaskWoken 设置为 pdTRUE, 表示应在中断退出之前请求上下文切换。 因此 必须将 *pxHigherPriorityTaskWoken 初始化为 pdFALSE。 请参阅 下面的代码示例。
返回值:
  如果消息成功发送到 RTOS 定时器守护进程任务, 则返回 pdPASS,否则返回 pdFALSE。
用法示例:

/* 回调函数,将在守护任务的上下文中执行。
注意回调函数必须全部使用这个相同的原型。*/
void vProcessInterface( void *pvParameter1, uint32_t ulParameter2 )
{
    
    
	BaseType_t xInterfaceToService;

    /* 需要服务的接口在第二个参数中被传递。
    参数中传递。 在这种情况下,第一个参数不被使用。*/
    xInterfaceToService = ( BaseType_t ) ulParameter2;
    /* ...在这里进行处理... */
}

/* 一个从多个接口接收数据包的ISR */
void vAnISR( void )
{
    
    
	BaseType_t xInterfaceToService, xHigherPriorityTaskWoken;

    /* 查询硬件以确定哪个接口需要处理。*/
    xInterfaceToService = prvCheckInterfaces();

    /* 实际的处理要推迟到一个任务。 要求在
    vProcessInterface()回调函数被执行,传入需要处理的接口的编号。
    需要处理的接口的编号。 需要服务的接口
    服务的接口在第二个参数中被传递。 第一个参数
    在这种情况下不使用。*/
    xHigherPriorityTaskWoken = pdFALSE;
    xTimerPendFunctionCallFromISR( vProcessInterface,
                               NULL,
                               ( uint32_t ) xInterfaceToService,
                               xHigherPriorityTaskWoken);

    /* 如果xHigherPriorityTaskWoken现在被设置为pdTRUE,那么就应该请求一个上下文切换。
    则应请求进行上下文切换。 使用的宏是特定于端口的,将是
    是portYIELD_FROM_ISR()或portEND_SWITCHING_ISR()--请参考所使用的端口的文档。
    参考所使用端口的文档页面。*/
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

十九、pcTimerGetName()

API原型:

const char * pcTimerGetName( TimerHandle_t xTimer );

  返回软件定时器的人类可读文本名称。
  将文本名称分配给定时器需使用 调用 xTimerCreate() 函数的 pcTimerName 参数, 该函数用于创建定时器。
参数:
  xTimer 被查询的定时器。
返回值:
  指向定时器文本名称的指针,该指针为以 NULL 结尾的标准 C 字符串。
用法示例:

const char *pcTimerName = "ExampleTimer";

/* 一个创建定时器的函数。*/
static void prvCreateTimer( void )
{
    
    
	TimerHandle_t xTimer;

    /* 创建一个定时器。*/
    xTimer = xTimerCreate( pcTimerName, /* Text name. */
                           pdMS_TO_TICKS( 500 ), /*周期。*/
                           pdTRUE, /* 自动加载。*/
                           NULL, /* 没有ID。*/
                           prvExampleCallback ); /*回调函数。*/

    if( xTimer != NULL )
    {
    
    
        xTimerStart( xTimer, portMAX_DELAY );

        /* 只是为了演示pcTimerGetName(),查询定时器的名称,如果不等于pcTimerGetName,则断言
        如果它不等于pcTimerName则断言。*/
        configASSERT( strcmp( pcTimerGetName( xTimer ), pcTimerName ) == 0 );
    }
}

二十、xTimerGetPeriod()

API原型:

TickType_t xTimerGetPeriod( TimerHandle_t xTimer );

  返回一个软件计时器的周期。 周期以滴答为单位。
  初始时,一个定时器的周期由 用于创建定时器的 xTimerCreate() 函数的 xTimerPeriod 参数设置。 可以使用 xTimerChangePeriod() 和 xTimerChangePeriodFromISR() API 函数改变此周期。
参数:
  xTimer 被查询的定时器。
返回值:
  计时器的周期,以滴答为单位。
用法示例:

/* 一个分配给软件定时器的回调函数。*/
static void prvExampleTimerCallback( TimerHandle_t xTimer )
{
    
    
	TickType_t xTimerPeriod;
    /* 查询到期的定时器的周期。*/
    xTimerPeriod = xTimerGetPeriod( xTimer );
}

二一、xTimerGetExpiryTime()

API原型:

TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer );

  返回软件定时器到期的时间, 即执行定时器回调函数的时间。
  如果 xTimerGetExpiryTime() 返回的值小于当前时间, 则定时器会在 tick 计数溢出并归 0 后到期。 计数溢出在 RTOS 实现中进行处理, 因此定时器的回调函数会在正确时间执行, 无论是在 tick 计数溢出之前还是之后。
参数:
  xTimer 被查询的定时器。
返回值:
  如果 xTimer 引用的定时器处于活动状态, 则返回定时器下一次到期的时间 (可能是在当前 tick 计数溢出之后, 请参阅上方注释)。
  如果 xTimer 引用的定时器未处于活动状态, 则未定义返回值。
用法示例:

static void prvAFunction( TimerHandle_t xTimer )
{
    
    
	TickType_t xRemainingTime;
    /* 计算由xTimer引用的定时器到期前的剩余时间。
    过期的时间。 TickType_t是一个无符号类型,所以即使计时器过期,减法也会得到正确的答案。
    正确的答案,即使定时器在Tick_txt溢出后才会过期。
    溢出。*/
    xRemainingTime = xTimerGetExpiryTime( xTimer ) - xTaskGetTickCount( );
}

二二、xTimerGetReloadMode()、uxTimerGetReloadMode()

API原型:

BaseType_t  xTimerGetReloadMode( TimerHandle_t xTimer );
UBaseType_t uxTimerGetReloadMode( TimerHandle_t xTimer );

  查询 xTimer 句柄引用的软件计时器的“模式”。
  该模式可以是自动重载定时器(每次到期都会自动重置),或者 一次性计时器(除非手动重新启动,否则仅到期一次)。
  xTimerGetReloadMode 和 uxTimerGetReloadMode 仅在其返回类型上有所不同。xTimerGetReloadMode 返回 BaseType_t 以匹配实际返回值 pdTRUE/pdFALSE 的类型。 uxTimerGetReloadMode 是为了向后兼容而提供的, 新的应用程序应该使用 xTimerGetReloadMode 来代替。
  这些 API 函数仅在已构建项目中包含 FreeRTOS ‘timers.c’ 源文件, 并且在 FreeRTOSConfig.h 中将 configUSE_TIMERS 设置为 1 时才可用。
参数:
  xTimer 要查询的定时器的句柄。该句柄通过调用用于创建计时器的 xTimerCreate() 或 xTimerCreateStatic() 返回。
返回值:
  如果句柄为 xTimer 的定时器为自动重载定时器,则返回 pdTRUE,否则返回 pdFALSE。

二三、软件定时器示例

/**
 * @file 11_SoftwareTimer.c
 * @author WSP
 * @brief 软件定时器 不受定时器个数限制
 * @version 0.1
 * @date 2022-10-18
 *
 * @copyright Copyright (c) 2022
 *
 */
#include "FreeRTOS_Include.h"

const static char *TAG = "APP_SoftwareTimer";

#define USER_AUTORELOAD 1           // 0的时候不开启定时器自动重装载 1开启启定时器自动重装载

#define USER_TIMER_SAME_CALLBACK    
// #define USER_SET_TIMER_PERIOD
// #define USER_TIMER_DELETE
// #define REST_TIME2


void Timer1_callback(TimerHandle_t xTimer)
{
    
    
    static int Timer_count = 0;
    if(strcmp(pcTimerGetName(xTimer),"Timer1") == 0){
    
      // 定时器1的传入ID方式和定时器2的不同,处理方法不同
        int * Timer_ID;
        Timer_ID = (int *) pvTimerGetTimerID(xTimer);
        ESP_LOGI(TAG, "Timer name is Timer1, timer id is %d callback count:%d", * Timer_ID,Timer_count);
    }else{
    
    
        ESP_LOGI(TAG, "Timer name is %s, timer id is %d callback count:%d",pcTimerGetName(xTimer), (uint32_t)pvTimerGetTimerID(xTimer),Timer_count);
    }
    Timer_count ++;
}
/**
 * @brief   创建函数初始化
 * @param   NULL
 * @return  NULL
 */
void SoftwareTimer_Init(void)
{
    
    
    TimerHandle_t xTimer1;
    int Timer1_ID = 1;
    xTimer1 = xTimerCreate("Timer1",            // 定时器名字
                           pdMS_TO_TICKS(1000), // 定时器周期
                        #if USER_AUTORELOAD
                           pdTRUE,              // 自动重装载
                        #else
                           pdFALSE,             // 不自动重装载
                        #endif
                           (void *)&Timer1_ID,  // TImer ID
                           Timer1_callback);    // 定时器回调函数
#ifdef USER_TIMER_SAME_CALLBACK    
    TimerHandle_t xTimer2;                  
    xTimer2 = xTimerCreate("Timer2",            // 定时器名字
                           pdMS_TO_TICKS(2000), // 定时器周期
                        #if USER_AUTORELOAD
                           pdTRUE,              // 自动重装载
                        #else
                           pdFALSE,             // 不自动重装载
                        #endif
                           (void *)2,           // TImer ID
                           Timer1_callback);    // 定时器回调函数
    xTimerStart(xTimer2, 0);                    // 开始定时器 等待0ms开启时间器
#endif
    xTimerStart(xTimer1, 0);                    // 开始定时器 等待0ms开启时间器
    vTaskDelay(10000 / portTICK_PERIOD_MS);     // 延时等待

#ifdef USER_TIMER_DELETE
    xTimerDelete(xTimer1,0);                    // 删除定时器1
    xTimerDelete(xTimer2,0);                    // 删除定时器2
    ESP_LOGI(TAG,"The timer is deleted");
#endif

#ifdef USER_SET_TIMER_PERIOD 
    xTimerChangePeriod(xTimer1,pdMS_TO_TICKS(4000),0);
#endif
    while (1)
    {
    
    
        vTaskDelay(1000 / portTICK_PERIOD_MS);  // 延时等待
    #ifdef REST_TIME2
        xTimerReset(xTimer2,0);                   // 模拟看门狗,直接给软件定时器复位
    #endif
    }
}

猜你喜欢

转载自blog.csdn.net/believe666/article/details/127205618