软件定时器
概述
AliOS Things操作系统内核使用tick作为时间片轮转调度以及延迟操作的时间度量单位,tick是实现定时触发功能的基础。tick计数发生在每次时钟中断处理的过程,时钟中断是定时产生的,系统在默认情况下为1ms触发一次,即一个tick代表1ms,用户可根据应用需要调整该时间。 软件定时器是用来在指定时间或者触发一次或多次某个功能函数的调用。这种由定时器来执行的函数叫做定时器回调函数,定时器回调函数以及触发时间由应用程序来设定。当定时器的触发时间到来,则定时器的回调函数会被执行。定时器支持两种工作模式:单次模式和周期性模式。定时器在创建时,用户可通过option参数来制定该定时器的工作模式是单次还是周期性的,若option设置了AOS_TIMER_REPEAT则为周期性的,否则为单次。
单次模式
定时器会从应用程序设置的初始时间开始,以tick为计时单位进行倒计时,当计数值减为0时调用回调函数执行。回调函数执行完毕,则定时器停止。
周期性模式
周期定时器在时间到期执行完回调函数后,重新开始计时,直到下次时间到期,再次执行回调函数,然后一直循环下去。
定时器任务
为了在定时器创建、删除、启动、停止以及参数并更的过程中,避免锁操作,系统采用一个定时器任务来处理这些操作。当应用程序调用定时器管理接口时,将接口调用命令发送到一个消息队列,定时器任务从消息队列中读取消息并处理该命令。 定时器任务除了处理命令外,还对当前已运行的定时器进行实时调度。所有正在运行的定时器会被挂接到g_timer_head链表,定时器任务循环的从g_timer_head链表中取出时间最近一次的定时器,通过当前tick计数和该定时器的超时tick数来判断定时时间是否到,如果该定时器触发时间已到则立即执行其处理函数,否则代表最近一次的定时器时间尚未到来,则在此时差,继续从命令buffer中收取消息,直到时间差到来后,再立即执行定时器处理函数。 上述流程在g_timer_head中有待处理定时器时才会进入,如果没有待处理定时器,在定时器任务将只会进入定时器管理循环中。
定时器管理函数
函数名 | 描述 |
aos_timer_create() | 定时器创建函数(推荐) |
aos_timer_new() | 定时器创建函数(兼容3.1) |
aos_timer_new_ext() | 定时器创建函数 |
aos_timer_free() | 定时器删除函数 |
aos_timer_start() | 定时器启动函数 |
aos_timer_stop() | 定时器停止函数 |
aos_timer_change() | 定时器初始时长和周期间隔参数变更函数 |
aos_timer_change_once() | 定时器初始时长参数变更函数 |
aos_timer_gettime() | 获取定时器时间参数函数 |
常用配置
软件定时器功能:默认使能,如需修改,在YAML中修改RHINO_CONFIG_TIMER配置
def_config:
RHINO_CONFIG_TIMER: 0
软件定时器任务栈:默认200Bytes,如需修改,在YAML中修改RHINO_CONFIG_TIMER_TASK_STACK_SIZE配置
def_config:
RHINO_CONFIG_TIMER_TASK_STACK_SIZE: 512
软件定时器任务优先级:默认5,如需修改,在YAML中修改RHINO_CONFIG_TIMER_TASK_PRI配置
def_config:
RHINO_CONFIG_TIMER_TASK_PRI: 5
定时器消息队列消息数:默认20,如需修改,在YAML中修改RHINO_CONFIG_TIMER_MSG_NUM配置
def_config:
RHINO_CONFIG_TIMER_MSG_NUM: 24
每秒tick数:默认100,如需修改,在YAML中修改RHINO_CONFIG_TICKS_PER_SECOND配置
def_config:
RHINO_CONFIG_TICKS_PER_SECOND: 1000
时间片:默认50个tick,如需修改,在YAML中修改RHINO_CONFIG_TIME_SLICE_DEFAULT配置
def_config:
RHINO_CONFIG_TIME_SLICE_DEFAULT: 100
API说明
使用示例
示例代码参考example/timer_example.c,该示例使用定时器管理函数来控制定时器的执行,具体场景为创建一个周期性定时器,定时调用回调函数执行,停止定时器该变定时器的时间参数,则定时器按照修改后的时间间隔定时调用回调函数执行。
示例说明如下:
t0时刻,测试任务调用aos_timer_create()创建一个周期性的定时器,周期间隔为1秒,回调函数为timer1_func。然后测试任务调用aos_sleep()进入休眠状态。
t1时刻,相对t0过去1秒,定时器到期,回调函数timer1_func被执行。该过程重复10次。
tn时刻,测试任务休眠到期,调用aos_timer_stop()停止定时器。然后调用aos_timer_change()接口修改周期间隔为2秒。
tn+1时刻,相对tn过去2秒,定时器到期,回调函数timer1_func被执行。该过程重复5次。
该示例可配置到helloworld_demo中运行,相关代码的下载、编译和固件烧录均依赖AliOS Things配套的开发工具 ,所以首先需要参考《AliOS Things集成开发环境使用说明之搭建开发环境》,下载安装 。 待开发环境搭建完成后,可以按照以下步骤进行示例的测试。
步骤1 创建或打开工程
打开已有工程
如果用于测试的案例工程已存在,可参考《AliOS Things集成开发环境使用说明之打开工程》打开已有工程。
创建新的工程
组件的示例代码可以通过编译链接到AliOS Things的任意案例(solution)来运行,这里选择helloworld_demo案例。helloworld_demo案例相关的源代码下载可参考《AliOS Things集成开发环境使用说明之创建工程》。
步骤2 添加组件
helloworld_demo组件的package.yaml中添加
depends:
- osal_aos: dev_aos # helloworld_demo中引入osal_aos组件
步骤3 下载组件
在已安装了的开发环境工具栏中,选择Terminal -> New Terminal启动终端,并且默认工作路径为当前工程的workspace,此时在终端命令行中输入:
aos install osal_aos
上述命令执行成功后,组件源码则被下载到了./components/osal_aos路径中。
步骤4 添加示例
osal_aos组件的package.yaml中添加example示例代码:
depends:
- rhino: dev_aos
- cli: dev_aos # 添加cli依赖
source_file:
- "*.c"
- "example/timer_example.c" # 添加 timer_example.c
步骤5 编译固件
在示例代码已经添加至组件的配置文件,并且helloworld_demo已添加了对该组件的依赖后,就可以编译helloworld_demo案例来生成固件了,具体编译方法可参考《AliOS Things集成开发环境使用说明之编译固件》。
步骤6 烧录固件
helloworld_demo案例的固件生成后,可参考《AliOS Things集成开发环境使用说明之烧录固件》来烧录固件。
步骤7 打开串口
固件烧录完成后,可以通过串口查看示例的运行结果,打开串口的具体方法可参考《AliOS Things集成开发环境使用说明之查看日志》。
当串口终端打开成功后,可在串口中输入help来查看已添加的测试命令。
步骤8 测试示例
CLI命令行输入:
timer_example
关键日志:
[aos_timer_example]timer expires 10 times
[aos_timer_example]timer expires 5 times
注意事项
tick是内核调度和延迟操作的基础,所以RHINO_CONFIG_TICKS_PER_SECOND参数必须配置,而定时器是基于tick实现的软件定时器功能,可以通过配置RHINO_CONFIG_TIMER参数来使能或关闭该功能。
FAQ
Q1: 定时器回调函数的执行上下文是在哪里?
答:定时器回调函数是在定时器任务中执行的,所以定时器回调函数的执行上下文为定时器任务,当回调函数所需栈空间超过定时器任务栈大小时,用户需要通过配置 RHINO_CONFIG_TIMER_TASK_STACK_SIZE参数来增加栈大小。