实际应用中,时间管理往往是必不可少的。例如:定时完成某件事情、周期性地触发某一动作、测量评估程序运行时间等。AWorks提供了一系列时间相关的服务,包括标准时间和定时器等,用户可以据此实现灵活的时间相关应用。
本文为《面向AWorks框架和接口的编程(上)》第三部分软件篇——第8章时间管理——第1~5小节:时间的表示形式、RTC通用接口、系统时间、系统节拍和软件定时器。
8.1 时间的表示形式
在AWorks中,时间有3种表示形式:细分时间、日历时间和精确日历时间。
8.1.1 细分时间
细分时间包含年、月、日、时、分、秒等信息。在AWorks中,细分时间使用aw_tm_t类型表示,该类型的具体定义详见程序清单8.1。
程序清单8.1 细分时间类型定义(aw_time.h)
其中,tm_sec表示秒,有效值为 0 ~ 59;tm_min表示分,有效值为0 ~ 59;tm_hour表示小时,有效值为 0 ~ 23;tm_mday表示日期,有效值为1 ~ 31;tm_mon表示月份,有效值为0 ~ 11,分别对应1 ~ 12月,即实际月份为该值加上1;tm_year表示1900年至今的年数,实际年为该值加上1900; tm_wday表示星期,0 ~ 6分别对应星期日 ~ 星期六;tm_yday表示该年1月1日至今的天数(0 ~ 365),0对应1月1日;tm_isdst表示是否使用夏令时,若tm_isdst为正,则夏令时有效,系统会在夏季将时间调快一小时,若tm_isdst为0或负数,表示不使用夏令时。现在一般不使用夏令时,tm_isdst设置为-1即可。
夏令时(Daylight Saving Time:DST)是一种为节约能源而人为规定地方时间的制度,在这一制度实行期间所采用的统一时间称为“夏令时间”。一般在天亮早的夏季人为将时间提前一小时,可以使人早起早睡,从而节约照明用电。各个采纳夏时制的国家具体规定不同。目前全世界有近110个国家每年要实行夏令时。我国在1986年至1991年实行了六年的夏令时,每年从4月中旬的第一个星期日2时整(北京时间)到9月中旬第一个星期日的凌晨2时整(北京夏令时)。在夏令时实施期间将时间调快一小时。1992年4月5日后不再实行。
例如,当前时间是2016年8月26日09:32:30,则可以定义如下细分时间:
一般地,星期(tm_wday)和一年中的天数(tm_yday)两个成员的值无需用户手动设置,这些值是在获取细分时间时,反馈给用户的信息。
8.1.2 日历时间
日历时间表示从1970年1月1日00:00:00开始至当前时刻经历的秒数。在AWorks中,日历时间使用aw_time_t类型表示,该类型定义如下(aw_time.h):
例如,当前时间是2016年8月26日09:32:30,至1970年1月1日00:00:00以来的秒数为1472203950。则可以定义如下日历时间:
实际中,用户往往并不需要直接计算秒数,而是通过AWorks提供的相关接口,将细分时间转换为日历时间,以得到某一细分时间对应的日历时间。
8.1.3 精确日历时间
日历时间精度为秒,精确日历时间的精度可以达到纳秒,精确日历时间在日历时间的基础上,增加了一个纳秒计数器。在AWorks中,精确日历时间使用aw_timespec_t类型表示,该类型定义如下(aw_time.h):
其中,tv_sec是秒值;tv_nsec是纳秒计数值。纳秒计数值从0开始计数,当纳秒计数值达到1000000000时,秒值加1,同时,纳秒计数值复位为0,重新开始计数。
8.1.4 细分时间与日历时间的相互转换
为了便于用户使用,AWorks提供了两个接口函数,用于细分时间和日历时间的相互转换。相关函数的原型详见表8.1。
表8.1 时间转换接口函数(aw_time.h)
1. 细分时间转换为日历时间
该函数用于将细分时间转换为日历时间,其函数原型为:
其中,p_tm作为输入参数,指向待转换的细分时间;p_time为输出参数,用以输出转换的结果(日历时间)。函数返回值为标准的错误号,返回AW_OK时表示转换成功,其它值表示转换失败。
例如,需要获取2016年8月26日09:32:30对应的日历时间,则范例程序详见程序清单8.2。
程序清单8.2 细分时间转换为日历时间范例程序
运行程序,可以得到转换的结果为:1472203950。
2. 日历时间转换为细分时间
该函数用于将日历时间转换为细分时间,其函数原型为:
其中,p_time作为输入参数,指向待转换的日历时间;p_tm为输出参数,用以输出转换的结果(细分时间)。函数返回值为标准的错误号,返回AW_OK时表示转换成功,其它值表示转换失败。
例如,需要将日历时间1472203950转换为细分时间,则范例程序详见程序清单8.3。
程序清单8.3 日历时间转换为细分时间范例程序
运行程序,可以得到转换的结果为:2016-08-26 09:32:30。
8.2 RTC通用接口
RTC(Real-Time Clock)设备是能够提供基本时钟服务的设备。一个系统中若存在RTC设备,则可以使用RTC通用接口从RTC设备中获取到年、月、日、时、分、秒等基本的时间信息,一般地,为了修正时间值,往往还可以设置RTC设备当前的时间值。
通常情况下,在硬件设计上,都会为RTC设备分配一个独立的后备电源(如电池),当系统主电源掉电后,RTC设备仍然能够继续正确运行,使得时间信息一直保持有效。
RTC通用接口包含获取时间和设置时间的接口,其函数原型详见表8.2。
表8.2 RTC通用接口函数(aw_rtc.h)
1. 获取时间
该函数用于获取RTC器件当前的时间值,其函数原型为:
其中,rtc_id表示RTC设备的编号,系统为每个RTC设备都分配了一个唯一ID,通常都是从0开始顺序为各个RTC设备编号。例如,i.MX28x片内具有RTC外设,其可以作为一个RTC设备使用,若还使用I²C总线外接了PCF85063器件,则系统中将新增一个RTC设备,此时,系统中共计将有2个RTC设备,它们的编号分别为0、1。p_tm为指向细分时间的指针,其为输出参数,用以返回获取到的时间值,返回值为标准的错误号,返回AW_OK时表示获取成功,否则表示获取失败,失败的原因可能是该rtc_id对应的设备不存在。
例如,获取ID为0的RTC设备时间,其范例程序详见程序清单8.4。
程序清单8.4 获取RTC设备时间的范例程序
2. 设置时间
该函数用于设置RTC器件当前的时间值,其函数原型为:
其中,rtc_id表示RTC设备的编号,p_tm为指向细分时间的指针,返回值为标准的错误号,返回AW_OK时表示设置成功,否则表示设置失败。
例如,修改ID为0的RTC设备时间,设置其时间为2016-08-26 09:32:30的范例程序详见程序清单8.5。
程序清单8.5 设置RTC设备时间的范例程序
8.3系统时间
在使用RTC通用接口获取或设置时间时,必须通过rtc_id指定一个RTC器件,略显繁琐,并且在绝大多数应用中,在获取时间值时,可能并不关心时间是从哪个RTC设备中获取到的。为了使应用程序在使用时间服务时更加便捷,AWorks提供了一个统一的系统时间,用户可以实时获取系统时间,必要时,也可以修改系统时间。
8.3.1 获取系统时间
根据系统时间表示形式的不同,可以有3种获取系统时间的方式:细分时间、日历时间和精确日历时间。相关函数的原型详见表8.3。
表8.3 获取系统时间接口函数(aw_time.h)
1.获取细分时间
该函数以细分时间的形式获取当前的系统时间,其函数原型为:
其中,p_tv为指向细分时间的指针,用于获取细分时间。函数返回值为标准的错误号,返回AW_OK时表示获取成功,其它值表示获取失败。范例程序详见程序清单8.6。
程序清单8.6 获取细分时间范例程序
程序中,每隔1s打印一次当前的时间值。初始时,若未对时间作任何设置,则系统时间默认为:1970-01-01 00:00:00。
2. 获取日历时间
该函数以日历时间的形式获取当前的系统时间,其函数原型为:
其中,p_time为指向日历时间的指针,用于获取日历时间,不需要通过参数获取日历时间时,该值可以为NULL。返回值同样为日历时间,特别地,若返回值为-1,表明获取失败。由此可见,既可以通过参数获得日历时间,也可以通过返回值获得日历时间。
通过参数获得日历时间的范例程序详见程序清单8.7。
程序清单8.7 通过参数获得日历时间范例程序
程序中,每隔1s打印一次当前的日历时间值。初始时,若未对时间作任何设置,则系统的日历时间默认为0(即起始时间为:1970-01-01 00:00:00)。
也可以直接通过返回值获取日历时间,范例程序详见程序清单8.8。
程序清单8.8 通过返回值获取日历时间范例程序
3. 获取精确日历时间
该函数以精确日历时间的形式获取当前的系统时间,其函数原型为:
其中,p_tv为指向精确日历时间的指针,用于获取精确日历时间。函数返回值为标准的错误号,返回AW_OK时表示获取成功,其它值表示获取失败。范例程序详见程序清单8.9。
程序清单8.9 获取精确日历时间范例程序
实际中,由于硬件性能的限制,往往并不能每纳秒更新一次纳秒计数值。不同平台实际纳秒计数值更新的快慢是不同的。例如,可能每隔2ms才更新一次纳秒计数值,使纳秒计数值每次增加2000000。
基于精确日历时间,可以完成一些需要精度高于秒的应用,例如,在用于运动员计时的秒表中,精度往往需要达到0.1 ~ 0.01秒。
8.3.2 设置系统时间
系统时间不准确时,或需要对系统时间进行初始设置时,可以通过接口函数重新设置系统时间的值,以便系统时间准确运行。根据不同的时间表示形式,有2种设置系统时间方式:使用细分时间,使用精确日历时间。相关函数的原型详见表8.4。
表8.4 设置系统时间接口函数(aw_time.h)
1. 使用细分时间设置系统时间
该函数用于使用细分时间的形式设置系统时间,其函数原型为:
其中,p_tm为指向细分时间(待设置的时间值)的指针。函数返回值为标准的错误号,返回AW_OK时表示设置成功,其它值表示设置失败。
当使用细分时间设置时间值时,细分时间的成员tm_wday, tm_yday无需用户设置,将在调用设置函数后自动更新。设置当前时间为2016-08-26 09:32:30的范例程序详见程序清单8.10。
程序清单8.10 使用细分时间设置系统时间范例程序
程序中,将时间设置为2016年8月26日09:32:30。并在while(1)主循环中,每隔1s打印一次当前的系统时间。
2. 使用精确日历时间设置系统时间
该函数用于使用精确日历时间的形式设置系统时间,其函数原型为:
其中,p_tv为指向精确日历时间(待设置的时间值)的指针。函数返回值为标准的错误号,返回AW_OK时表示设置成功,其它值表示设置失败。范例程序详见程序清单8.11。
程序清单8.11 使用精确日历时间设置系统时间范例程序
程序中,将精确日历时间的秒值设置为了1472203950,该值是从1970年1月1日0时0分0秒至2016年8月26日09时32分30秒的秒数。即将时间设置为2016年8月26日09时32分30秒。在while(1)主循环中,每隔1s按照细分时间的格式打印一次当前的系统时间,用以验证设置的结果。
通常情况下,不会这样设置时间值,均是采用细分时间的方式设置时间值,因为细分时间更加容易阅读和理解。但是,在一些应用场合,使用日历时间将是一种更优的选择,例如,需要通过远程传输时间值来更新本地的时间值,显然,日历时间的长度要远远小于细分时间的长度,这种情况下,通信过程中,传输日历时间比传输细分时间更节省通信数据量,可以节省一定的带宽。
8.4 系统节拍
系统节拍相当于系统的“心脏”,系统节拍的频率即为“心脏”跳动的频率,每次“跳动”,系统节拍计数器加1,并处理系统相关的事务。例如,在系统中,可以存在多个软件定时器(下节将详细介绍),则在每个系统节拍产生时,系统将自动检查所有的软件定时器,将它们的定时节拍数减1,当减至0时,表明定时器的定时时间到。在AWorks中,很多事物的处理都是基于系统节拍的,因而往往将系统节拍看作系统的“心脏”。
系统节拍相关的函数原型详见表8.5
表8.5 系统节拍接口(aw_system.h)
1. 获取系统节拍频率
一个系统节拍对应的实际时间与系统节拍的频率相关。系统节拍频率越高,系统相关的事务处理越频繁,实时性越好,但单位时间内,系统本身占用CPU的时间越长,对应的,用户能够使用CPU的时间也就越短。反之,系统节拍频率越低,系统相关的事物处理越缓慢,实时性越差,但单位时间内,系统本身占用CPU的时间越短,对应的,用户能够使用CPU的时间也就越长。因此,系统节拍的频率不能太高,也不能太低,需要设置为一个合理的值,通常在几Hz到几KHz 之间。
系统实际使用的节拍频率可以通过该函数获得,其函数原型为:
函数返回值即为系统节拍频率,例如,返回值为1000,表示节拍频率为1KHz,则每个节拍对应的时间为1ms。获取并打印当前系统节拍频率的范例程序详见程序清单8.12。
程序清单8.12 获取系统节拍频率的范例程序
2. 获取系统当前的节拍计数值
系统中存在一个系统节拍计数器,初始值为0,系统启动后,其值会在每个系统节拍加1。可以通过该函数在任意时刻获取当前系统节拍的计数值,其函数原型为:
函数返回值即为当前系统节拍计数器的值。aw_tick_t为一个无符号整数类型。其位数与具体平台相关,在32位系统中,其往往定义为32位。
系统节拍计数值往往可以用来计算一段程序运行的时间,如在程序运行前,使用该函数获取一个系统节拍计数值,在程序运行结束后,再使用该函数获取一个系统节拍计数值。它们的差值即为程序运行所消耗的系统节拍个数,再由系统节拍频率可以知道每个节拍对应的时间,从而得到程序运行所耗费的时间。范例程序详见程序清单8.13。
程序清单8.13 获取系统节拍计数值的范例程序
程序中,作为演示,测量的是一个执行1000000次空语句的for循环程序段的时间。t1与t0的差值即为for循环程序段耗费的系统节拍数,同时,系统节拍频率的倒数为每个节拍对应的时间(秒),程序中,将时间值扩大了1000倍,即将时间值的单位转换为了毫秒。
程序中,节拍差值和对应的时间都通过手动计算,实际中,为了方便用户使用,AWorks提供了相关操作对应的接口函数。
3. 计算两个时刻的节拍计数值的差值
该函数用于计算两个时刻的节拍计数值的差值,以计算某一程序段所耗费的时间节拍数,其函数原型为:
t0为某一程序段开始时刻的系统节拍计数值,t1为对应程序段结束时的系统节拍计数值。如优化程序清单8.13中计算节拍差值的语句,改由通过接口实现,范例程序详见程序清单8.14。
程序清单8.14 计算节拍差值的范例程序
4. 系统节拍个数转换为时间
在上面的例子中,将系统节拍个数转换为时间较为繁琐,需要获取系统节拍的频率,然后经过相关的换算。为了便于用户使用,提供了直接将系统节拍值转换为时间(单位:毫秒)的接口,其函数原型为:
其中,ticks为系统节拍个数,返回值为转换的时间结果(单位:毫秒)。如优化程序清单8.14中计算时间的语句,改由通过接口实现,范例程序详见程序清单8.15。
程序清单8.15 计算节拍值对应时间的范例程序
5. 时间转换为系统节拍个数
与将系统节拍个数转换为时间对应,AWorks提供了用于将时间(单位:毫秒)转换为系统节拍个数的接口,其函数原型为:
其中,ms是待转换的时间(单位:毫秒),返回值为转换的结果(系统节拍个数),即ms参数指定时间所对应的节拍数。例如,要获取500ms时间对应的节拍个数,范例程序详见程序清单8.16。
程序清单8.16 计算时间对应节拍个数的范例程序
8.5 软件定时器
当需要定时完成某件事情时,可以使用软件定时器,其可以提供毫秒级别的定时。当定时时间到时,自动通知用户,以便用户处理相关的事务。软件定时器相关的接口函数原型详见表8.6。
表8.6 软件定时器相关接口(aw_timer.h)
1. 定义软件定时器实例
每个软件定时器对应了一个事务处理,当定时时间到时,需要通知用户,在AWorks中,“通知”是通过回调机制实现的。即初始时,用户将一个函数与定时器绑定,后续当该定时器定时时间到时,将自动回调用户绑定的函数,即可达到将“定时时间到”的这一事件通知到用户。
在使用软件定时器前,必须定义一个软件定时器实例,软件定时器的类型为aw_timer_t,其在aw_timer.h文件中定义,具体类型的定义用户无需关心,仅需使用该类型定义软件定时器实例即可,即:
其地址即可作为软件定时器相关接口中p_timer参数的实参传递。
此外,系统中可以有多个软件定时器,每个定时器可以有不同的定时时间,对应不同的事务处理,例如:
2. 初始化软件定时器
该函数用于将指定的函数与软件定时器绑定(注册),保存在定时器实例中,当定时时间到,则通过函数指针p_func调用指定的函数,即注册回调函数机制。初始化函数的原型为:
其中,p_timer为指向使用aw_timer_t类型定义的软件定时器实例的指针,p_func指向本次注册的回调函数,当定时时间到时,系统将自动调用p_func指向的函数(注册回调函数),回调函数的类型为aw_pfuncvoid_t,该类型是AWorks中定义的函数指针类型,其具体定义(aw_types.h)如下:
由此可见,p_func指向的函数类型是无返回值,具有一个void*型参数的函数。
初始化函数的p_arg形参即为用户自定义的参数,在定时时间到调用回调函数时,会将此处设置的p_arg作为参数传递给回调函数,如果不使用此参数,则设置为NULL。
初始化函数的使用范例详见程序清单8.17。
程序清单8.17 初始化软件定时器范例程序
3. 启动软件定时器
完成定时器的初始化后,即可设置定时时间(单位:ms)并启动定时器,定时器开始工作,定时器定时的单位为系统节拍,启动定时器后,在每个系统节拍将软件定时器的节拍值减1,当减到0时,表明定时时间到,则自动调用初始化时注册的回调函数。启动软件定时器的函数原型为:
其中,p_timer指向软件定时器实例,ticks为定时的节拍数。如启动定时器,并定时500ms,则范例程序详见程序清单8.18
程序清单8.18 启动软件定时器范例程序
注意,默认情况下,启动软件定时器后,仅定时一次,即定时时间到后,调用用户注册的回调函数,整个定时过程结束。因此,上述程序仅能观察到500ms后LED点亮,后续不会再有其它任何现象。
如需再次定时,则必须再次启动软件定时器。特别地,若需要周期性的定时,则可以在回调函数执行结束时,再次启动软件定时器。例如,需要每隔500ms翻转一次LED,实现LED闪烁,则范例程序详见程序清单8.19。
程序清单8.19 软件定时器周期性定时范例程序
4. 关闭软件定时器
当不再需要定时服务时,可以关闭软件定时器,其函数原型为:
其中,p_timer指向软件定时器实例,用于指定需要关闭的软件定时器。例如,在程序清单8.19的基础上,若需要LED闪烁10s后自动停止闪烁,则可以在启动软件定时器10s后关闭软件定时器,范例程序详见程序清单8.20。
程序清单8.20 关闭软件定时器范例程序
上一篇:高通BLE Mesh网络解决方案
下一篇:嵌入式系统分析工具
推荐阅读最新更新时间:2024-05-07 18:02