本章描述的是时间模块
Overview of Timing Services
SYS/BIOS和XDCtools中的几个模块涉及到计时和时钟相关的服务:
- ti.sysbios.knl.Clock模块负责内核用来记录时间的周期性系统滴答。所有期望超时参数的SYS/BIOS APIs都将超时解释为时钟滴答数。Clock模块用于按照时钟滴答声中指定的时间间隔来调度函数。默认情况下,时钟模块使用hal.Timer模块来获得一个基于硬件的滴答。另外,时钟模块也可以配置为使用应用程序提供的时钟源。Clock模块替代了早期版本的DSP/BIOS中的CLK和PRD模块
- ti.sysbios.hal.Timer模块为使用定时器外设提供了一个标准接口。它隐藏定时器外设的任何目标/设备特定特征。ti.sysbios.family.xxx.Timer模块(例如ti.sysbios.family.c64.Timer)支持定时器的目标/设备特定属性。可以使用Timer模块来选择一个计数器,该计时器在计时到期时调用tickFxn。
- ti.sysbios.hal.Seconds模块提供了一种维护当前时间和日期的方法,该方法由1970年(Unix纪元)以来的秒数来定义。该模块生成一个自定义的time()函数,该函数调用Seconds_get(),覆盖C标准库的time()函数。
- xdc.runtime.Timestamp模块提供了简单的时间戳服务,用于对代码进行基准测试和向日志添加时间戳。这个模块在SYS/BIOS中使用目标/设备特定的TimestampProvider来控制如何实现时间戳
Clock
ti.sysbios.knl.Clock模块负责用来记录时间的周期性系统滴答。所有期望超时参数的SYS/BIOS API都将解释为时钟滴答数
在默认情况下,Clock模块,使用ti.sysbios.hal.Timer模块创建一个计时器来生成系统时钟,这基本上是低Clock_tick()的周期性调用
可以使用下列的任一配置语句配置Clock模块不使用定时器:
ti.sysbios.knl.Clock.tickSource = Clock.TickSource_USER
or
ti.sysbios.knl.Clock.tickSource = Clock.TickSource_NULL
系统的tick周期由Clock.tickPeriod配置参数设置。这是以微秒为单位。
Clock_tick()和tick周期的用法如下:
- 如果tickSource是Clock.TickSource_TIMER(默认),时钟使用ti.sysbios.hal.Timer来创建一个计时器来生成系统tick,它基本是对Clock_tick()的周期性调用。Clock使用Clock.tickPeriod来创建timer。Clock.timerId可以被修改使时钟使用不同的计时器。
- 如果tickSource是Clock.TickSource_USER,然后应用程序必须从用户中断调用Clock_tick(),并将tickPeriod设置为用户中断的的大约频率(以微秒为单位)
- 如果tickSource是Clock.TickSource_NULL,不能调用任何带有超时值的SYS/BIOS API,也不能调用任何时钟API。可以使用Task模块,但不能调用需要超时的API,例如,Task_sleep()。Clock.tickPeriod值在这个配置中不是有效的。
Clock_getTicks()获取自启动以来发生的时钟滴答数。返回的值在达到可以存储在32位中的最大值返回为零。
Clock模块提供了启动、停止和重新配置时钟的API。这些APIs允许在运行时更新频率。这三个APIs都是不可重入的,需要使用gate来保护它们。
- Clock_tickStop()通过调用Timer_stop停止用于生成时钟的计时器。
- Clock_tickReconfig()在内部调用Timer_setPeriodMicroseconds()来重新配置计时器。在当前的CPU频率下,如果定时器不支持Clock.tickPeriod,Clock_tickReconfig()将失败
- Clock_tickStart()通过调用Timer_start()重新启动用于生成时钟tick的定时器
Clock模块允许创建Clock对象实例,这些实例引用在Clock计时中指定的超时值到期时运行的函数
所有的Clock函数运行在Swi上下文中。也就是说,Clock模块自动创建一个Swi供其使用,并且在该Swi中运行Clock函数。时钟使用的Swi优先级可以通过配置Clock.swiPriority来更改。
可以使用Clock_create()动态地创建clock句柄。Clock实例可以是"一次性的"或连续的。可以在创建时钟实例时启动它,或者稍后通过调用Clock_start()启动它。这是由startFlag配置参数控制的。Clock_create()只能从Task或main()函数的上下文调用。
Clock_create()需要一个函数和一个非零超时值作为参数。当超时时调用该函数。超时值用于计算第一次过期时间。对于一次性时钟实例,用于计算单个过期时间的超时值,且周期为零。对于周期时钟实例,超时值被用于计算第一次过期时间;周期值(参数的第一部分)在第一次过期之后使用。
表:一次性和连续时钟的时间线
可以通过调用Clock_start()和Clock_stop()来停止和重启时钟实例(一次性的和周期性的)。注意,虽然Clock_tickStop()停止用于生成时钟tick的计时器,但Clock_stop()只停止时钟对象的一个实例。当调用Clock_start()函数时,将重新计算过期值。启动或停止时钟实例的API—Clock_start()和Clock_stop()—可以在任何上下文中调用,除非在main()开始之前启动程序。
Clock模块提供了Clock_setPeriod(),Clock_setTimeout()和Clock_setFunc() API来修改已经停止的时钟实例的时钟实例属性
Runtime example: 这个C语言示例展示了如何创建一个时钟实例。这个实例是动态的(重复运行)并自动启动。它每5个节拍进行一次myHander函数。将用户参数(UArg)传递给函数
Clock_Params clockParams;
Clock_Handle myClock;
Error_Block eb;
Error_init(&eb);
Clock_Params_init(&clockParams);
clockParams.period = 5;
clockParams.startFlag = TRUE;
clockParams.arg = (UArg)0x5555;
myClock = Clock_create(myHandler1, 5, &clockParams, &eb);
if (myClock == NULL) {
System_abort("Clock create failed");
}
Configuration example: 这个示例配置脚本创建一个时钟实例,其属性与前面的示例相同。
var Clock = xdc.useModule('ti.sysbios.knl.Clock');
var clockParams = new Clock.Params();
clockParams.period = 5;
clockParams.startFlag = true;
clockParams.arg = (UArg)0x5555;
Program.global.clockInst1 = Clock.create("&myHandler1", 5, clockParams);
Runtime example: 这个C示例使用一些时钟APIs来打印关于Task sleeps多长时间的消息
UInt32 time1, time2;
. . .
System_printf("task going to sleep for 10 ticks... \n");
time1 = Clock_getTicks();
Task_sleep(10);
time2 = Clock_getTicks();
System_printf("...awake! Delta time is: %lu\n", (ULong) (time2 - time1));
Runtime example: 这个C语言示例使用了一些时钟APIs来降低时钟模块的频率
BIOS_getCpuFreq(&cpuFreq);
cpuFreq.lo = cpuFreq.lo / 2;
BIOS_setCpuFreq(&cpuFreq);
key = Hwi_disable();
Clock_tickStop();
Clock_tickReconfig();
Clock_tickStart();
Hwi_restore(key);
Timer 模块
ti.sysbios.hal.Timer模块为使用定时器外设提供了一个标准的外设。
还可以使用此模块创建一个定时器(即,标记一个定时器使用),并将其配置为在计时器到期时调用tickFxn。只有在不需要对定时器外设进行任何自定义配置时才使用此模块
定时器可以配置为一次性或连续模式定时器。周期可以指定为计时器计数或微秒
Seconds 模块
ti.sysbios.hal.Seconds 模块提供了一种方法来设置和获取从1月1日00:00:00 GMT 1970(Unix)开始经过的秒数。Seconds模块通过特定于Seconds 委托(如果可用)来维护时间
如果某个设备特定的Seconds模块不可用,则ti.sysbios.hal.SecondsClock模块被用作Seconds委托。SecondsClock在内部使用Clock模块周期性地增加秒计数。例如,这些配置命令会导致Seconds模块使用ti.sysbios.hal.SecondsClock模块
Seconds = xdc.useModule('ti.sysbios.hal.Seconds');
var SecondsProxy = xdc.useModule('ti.sysbios.hal.SecondsClock');
Seconds模块的APIs是:
Void Seconds_set(UInt32 seconds);
UInt32 Seconds_get(Void);
应用程序应该调用Seconds_set()来初始化秒计数。如果需要,可以再次调用Seconds_set()来更新或重置秒计数。应用程序必须在调用Seconds_get()之前至少调用一次Seconds_set()。否则,由Seconds_get()返回的结果是无意义的。Seconds_set()函数是不可重入的。
Seconds模块包含一个time()函数,该函数调用Seconds_get()。这将覆盖C标准库time()函数。可以将这个time()函数与C标准头文件time.h中的其他时间函数结合使用,以可读格式查看当前日期和时间。
例子: 这个例子初始化Seconds模块,设置日期,获取当前日期,并以人们刻度的格式显示当前的时间和日期:
#include <time.h>
#include <ti/sysbios/hal/Seconds.h>
UInt32 t;
time_t t1;
struct tm *ltm;
char *curTime;
/* set to today’s date in seconds since Jan 1, 1970 */
Seconds_set(1412800000); /* Wed, 08 Oct 2014 20:26:40 GMT */
/* retrieve current time relative to Jan 1, 1970 */
t = Seconds_get();
/* Use overridden time() function to get the current time.
* Use standard C RTS library functions with return from time().
* Assumes Seconds_set() has been called as above */
t1 = time(NULL);
ltm = localtime(&t1);
curTime = asctime(ltm);
System_printf("Time(GMT): %s\n", curTime);
Timestamp 模块
顾名思义,xdc.runtime.Timestamp模块,提供时间戳服务。时间戳模块可用于对代码进行基准测试,并向日志中添加时间戳。对时间戳模块函数的调用被转发到特定于平台的TimestampPrivider实现。
Timestamp模块的包路径是xdc.runtime.Timestamp,所以SYS/BIOS应用程序应该包含以下#include语句:
#include <xdc/runtime/Timestamp.h>
配置文件(*.cfg)应该启用该模块,如下所示:
var BIOS = xdc.useModule('xdc.runtime.Timestamp');
以下的时间戳模块API在SYS/BIOS应用程序中很有用:
- Timestamp_get32() — 返回一个32位时间戳
- Timestamp_get64() — 如果目标支持64位解析,返回一个64位时间戳
- Timestamp_getFreq() — 获取时间戳计时器的频率(Hz),可以使用此函数将时间戳值转换为实时单位
如果想要一个与平台无关的Timestamp版本,可以使用TimestampStd模块,该模块使用ANSI C clock()函数
特定于平台的TimestampProvider模块位于ti.sysbios.family包中。例如,ti.sysbios.family.msp430.TimestampProvider和ti.sysbios.family.arm.m3.TimestampProvider。大部分TimestampProvider模块都有可用于控制硬件时钟源的配置参数,以及在时间戳计数器溢出时的行为
下面的例子计算了将Timestamp增量与CPU周期关联起来所需的因子:
Types_FreqHz freq1; /* Timestamp frequency */
Types_FreqHz freq2; /* BIOS frequency */
Float factor; /* Clock ratio cpu/timestamp */
Timestamp_getFreq(&freq1);
BIOS_getCpuFreq(&freq2);
factor = (Float)freq2.lo / freq1.lo;
System_printf("%lu\t%lu\t%lu\t Timestamp Freq, BIOS Freq, Factor\n",
freq1.lo, freq2.lo, (UInt32) factor);
参考文献:
- 《TI-RTOS Kernel (SYS/BIOS) User’s Guide》
|