基于TI-RTOS的CC2650DK开发(15)---Clock(时钟)

judy的头像

时间服务

时间服务概览

• 在 SYS/BIOS和XDCtools中,有几个模块涉及计时和时钟相关服务:
ti.sysbios.knl.Clock模块:负责内核用于保持时间轨道的周期性系统tick。所有SYS/BIOS APIs期望一个timeout参数来中断根据时间ticks所设置的timeout。时钟模块用于调度那些在时钟ticks中指定的内部运行的函数。默认情况下,时钟模块使用硬件抽象层。计时器模块则获取基于硬件的tick。另外,时钟模块可配置为使用应用程序提供的tick源,详见5.2节(时钟模块代替了早期DSP/BIOS版本中的CLK和PRD)。

• ti.sysbios.hal.Timer模块:提供使用计时器外设的标准接口。它隐藏了计时器外设的所有目标/设备的具体特征。计时器的目标/设备具体属性由 ti.sysbios.family.xxx.Timer模块支持(例如,ti.sysbios.family.c64.Timer)。你可以使用计时器模块来在计时器到期时调用一个tickFxn。详见5.3节和8.3节。

• The ti.sysbios.hal.Seconds模块:提供了一种手段用于维护当前时间和日期,定义为自1970年(Unix时代)以来的秒数。此模块生成一个自定义的time()函数来调用Seconds_get(),重写了C标准库的time()函数。详见5.4节。

• xdc.runtime.Timestamp模块:提供简单的时间戳服务用于基准代码和添加时间戳日志。此模块使用了SYS/BIOS中特定目标/设备的TimestampProvider以控制时间戳的实现,详见5.5节。

参见video introducing Timers and Clocks进行概览。

时钟

ti.sysbios.knl.Clock模块负责内核用于保持时间轨道的周期性系统tick。所有SYS/BIOS APIs期望一个timeout参数来中断根据时间ticks所设置的timeout。

时钟模块默认情况下使用 ti.sysbios.hal.Timer模块所创建的计时器来产生系统tick---基于所调用Clock_tick()的周期。更多关于计时器模块信息请参考5.3节。

时钟模块可被配置为不使用计时器,以下两种静态声明都可以:

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.tickPeriod来创建计时器。Clock.timerId可被改变,它使得时钟可使用不同的计时器。

• 如果tickSource是Clock.tickSource_USER,那么你的应用必须从用户中断调用Clock_tick(),并设置tickPeriod为以微秒为单位的用户中断的近似频率。

• 如果tickSource为Clock.tickSource_NULL,你无法调用任何带timeout值的SYS/BIOS APIs,且不能调用任何时钟APIs。你仍可使用Task模块,但不能调用需要timeout的APIs,例如Task_sleep()。此配置中的Clock.tickPeriod值无效。

Clock_getTicks()获取自启动以来,时钟ticks已经发生的次数。此值在到达32bit所能存储的最大值后会重新变为0。

Clock模块提供了APIs对tick进行开始、停止和重配置操作。这些APIs允许你在运行时改变频率。此三个APIs是不可重入的,所以无需gates进行保护。

• Clock_tickStop()通过调用Timer_stop()停止计时器用于生成时钟tick。

• Clock_tickReconfig()在内部调用Timer_setPeriodMicroseconds()重配置计时器。如果计时器无法在当前CPU频率下支持Clock.tickPeriod,则Clock_tickReconfig()失败。

• Clock_tickStart()通过调用Timer_start()重启计时器以生成时钟tick。

时钟模块可让你创建时钟对象实例,当时钟ticks中指定的timeout值到期,关联函数运行。

所有时钟函数在Swi上下文中运行。也就是时钟模块自动创建一个Swi并在Swi内运行时钟函数。可以通过配置Clock.swiPriority来改变时钟所使用的Swi的优先级。

你可以使用Clock_create()动态创建时钟实例。时钟实例可以是单次触发或连续触发的。你可以在时钟创建或启动后通过调用Clock_start()来开始一个时钟实例。这由配置参数startFlag来进行控制。Clock_create()仅能在Task或main()函数上下文中被调用。

Clock_create()需要一个函数和一个非0 timeout值作为参数。当timeout到期,函数被调用。timeout值用于计算第一个到期时间。对于单次触发时钟实例来说,timeout值用于计算单个到期时间,period为0。对于定期时钟实例来说,timeout值用于计算第一次到期时间,period值(参数的一部分)用于第一次触发之后。

时钟实例(单次时钟和周期性时钟)可通过调用Clock_start()和Clock_stop()来开始和重启。注意当Clock_tickStop()停止了用于产生时钟tick的计时器时,Clock_stop()仅停止时钟对象的一个实例。当你调用Clock_start()时,到期值将重新计算。启动和停止时钟实例的APIs---Clock_start()和Clock_stop()可以在除了main()开始前的程序启动之外的任何上下文中被调用。

时钟模块提供了Clock_setPeriod()、Clock_setTimeout()和Clock_setFunc() APIs来更改停止后的时钟实例属性。

运行时实例:这个C实例演示了如何创建一个时钟实例。此实例是动态创建(重复运行)并自动开始的。它每隔5个ticks运行一次myHandler函数。一个用户参数(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");
}

配置实例:此例通过配置脚本创建一个时钟实例,属性和前面的例子一样。

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);

运行时实例:此实例使用一些时钟APIs来打印一个Task休眠了多久。

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));

运行时实例:此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);

以下非翻译内容:

时钟还是经常用到的,下面来做几个例子,熟悉时钟的使用。

实例1:

之前讲LED时曾做过一个跑马灯,使用的是CPUdelay()来控制时间,当时也曾提到过,这种方法并不适合于实际开发。那么,今天就用时钟来实现跑马灯,这应算是正常的方法了。代码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "Board.h"

/* Pin driver handle */
static PIN_Handle ledPinHandle;
static PIN_State ledPinState;

PIN_Config ledPinTable[] =
{
    Board_LED1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_DRVSTR_MAX,
    Board_LED2 | PIN_GPIO_OUTPUT_EN | PIN_DRVSTR_MAX,
    Board_LED3 | PIN_GPIO_OUTPUT_EN | PIN_DRVSTR_MAX,
    Board_LED4 | PIN_GPIO_OUTPUT_EN | PIN_DRVSTR_MAX,
    PIN_TERMINATE
};

Void clkFxn(UArg arg0)
{
    static uint8_t i = 0;
    PIN_setOutputValue(ledPinHandle, ledPinTable[i] & 0xFF, 0);
    i = (i + 1) % 4;
    PIN_setOutputValue(ledPinHandle, ledPinTable[i] & 0xFF, 1);
}

int main(void)
{
    Clock_Params clockParams;
    Board_initGeneral();
    /* Open LED pins */
    ledPinHandle = PIN_open(&ledPinState, ledPinTable);
    if(!ledPinHandle)
    {
        System_abort("Error initializing board LED pins\n");
    }

    Clock_Handle myClock;
    Error_Block eb;
    Error_init(&eb);
    Clock_Params_init(&clockParams);
    clockParams.period = 10000;
    clockParams.startFlag = TRUE;
    myClock = Clock_create(clkFxn, 5, &clockParams, &eb);
    if (myClock == NULL)
    {
        System_abort("Clock create failed");
    }

    BIOS_start();
    return (0);
}

实例2:

CC2650自带一个时钟实例,可以CCS中选择【View】-->【Resource Explorer】菜单下载得到。代码如下:

#include 
#include 
#include 
#include 
#include "Board.h"

Void clk0Fxn(UArg arg0);
Void clk1Fxn(UArg arg0);

Clock_Struct clk0Struct, clk1Struct;
Clock_Handle clk2Handle;

Int main()
{
    /* Construct BIOS Objects */
    Clock_Params clkParams;

    /* Call board init functions */
    Board_initGeneral();

    Clock_Params_init(&clkParams);
    clkParams.period = 5000 / Clock_tickPeriod;
    //设置此标志表示clk0为自动启动
    clkParams.startFlag = TRUE;

    /* 创建多周期时钟*/
    Clock_construct(&clk0Struct, (Clock_FuncPtr)clk0Fxn,
                    5000 / Clock_tickPeriod, &clkParams);

    clkParams.period = 0;
    //设置此标志表示clk1为手动启动
    clkParams.startFlag = FALSE;
    /* 创建单周期时钟*/
    Clock_construct(&clk1Struct, (Clock_FuncPtr)clk1Fxn,
                    11000 / Clock_tickPeriod, &clkParams);
    clk2Handle = Clock_handle(&clk1Struct);
    //手动启动clk1
    Clock_start(clk2Handle);

    BIOS_start();    /* does not return */
    return(0);
}

Void clk0Fxn(UArg arg0)
{
    UInt32 time;
    time = Clock_getTicks();
    System_printf("System time in clk0Fxn = %lu\n", (ULong)time);
}

Void clk1Fxn(UArg arg0)
{
    UInt32 time;
    time = Clock_getTicks();
    System_printf("System time in clk1Fxn = %lu\n", (ULong)time);
    System_printf("Calling BIOS_exit() from clk1Fxn\n");
    BIOS_exit(0);
}

运行结果如下:

System time in clk0Fxn = 506
System time in clk0Fxn = 1010
System time in clk1Fxn = 1104
Calling BIOS_exit() from clk1Fxn

此例演示了如何在运行时创建单周期时钟和多周期时钟。它使用的是Clock_construct()函数进行创建,和第一个实例有所不同。并且,此例还演示了如何手动开始一个时钟,还是非常具有参考意义的。

出处: abatei的专栏

主图: