1、前言
在操作系统中,常常需要延时或周期性的操作,比如任务的延时调度、周期性的触发等。基于对时钟精确性的要求,每个运行的cpu平台都会提供相应的硬件定时机制。
2、基本概念
目前cpu提供的定时机制主要归结为以下两类:倒计数模式、正计数模式
系统tick本质就是基于cpu的硬件定时机制设置一个基础硬件定时器。而对于操作系统来讲,tick提供了系统调度需要的最小基本定时单元。例如在任务中需要进行延时操作,可以以tick为单位,每一个tick所占用的具体时间单元是用户可配置的。一般设置每秒100个tick,则每个tick代表10ms,系统中每延时一个tick单元,代表延时10ms。
3、内核系统时钟的基本配置
Rhino中使用tick时,需要进行两项基本配置,一项是tick周期配置,另一项是tick处理函数配置。这两项配置也是操作系统在不同系统移植中的必处理选项。
对于tick周期配置,一般用k_config.h的宏RHINO_CONFIG_TICKS_PER_SECOND来配置,用户可以通过该宏来定义每秒tick数。
系统的tick处理函数指的是每次tick周期触发时,操作系统需要进行的处理。Rhino提供了一个统一的tick函数入口krhino_tick_proc(),一般将此函数加入tick定时中断处理函数。
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/RHINO_CONFIG_TICKS_PER_SECOND); /*配置定时器触发周期*/
void SysTick_Handler(void) /*中断钩子函数*/
{
HAL_IncTick();
krhnio_intrpt_enter();
krhino_tick_proc();
krhnio_intrpt_exit();
}
4、系统时钟的调度处理
系统tick的调度处理对象主要是针对任务的。目前主要包括两个方面:延时任务处理和Round-Robin调度触发。
被**延时调度**的任务都会被放入一个延时队列g_tick_head中。任务被延时调度的原因主要有以下两种。
任务主动延时:通过调用krhino_task_sleep(tick_t ticks)函数可以暂时释放当前任务的cpu资源,将当前任务临时加入g_tick_head队列,并切换到其他任务。此函数会将task延时ticks时间,当ticks时间结束,ticks处理需要将此任务重新拉去放回ready队列。
**等待资源暂时释放**:任务需要等待资源如信号量、互斥锁等,如果暂时无法获取此共享资源,则操作系统会将该任务放入该资源的阻塞队列。如果设置了等待超时时间,则该任务就会被放入g_tick_head队列。如果在设置的超时时间内还未获取到共享资源,则tick处理会将该任务从g_tick_head队列中取出,并将任务状态设置为超时,超时的状态会通过接口返回给调用者。如果在超时时间内获取到共享资源,则在获取到资源的流程里,将该任务从g_tick_head队列中直接删除,并设置回到正常状态。
**Round-Robin调度**是一种基于时间片的调度,通过设置任务的最大占用时间片上限,实现同优先级的任务间CPU资源共享。任务能够基于Round-Robin调度,需要有两个前提条件:
①RR调用宏RHINO_CONFIG_SCHED_RR打开;
②任务创建时的调度策略需要设置为KSCHED_RR;
任务创建时,需要设置最大tick数,一旦开启了宏RHINO_CONFIG_SCHED_RR,默认设置时长为RHINO_CONFIG_TIME_SLICE_DEFAULT。
Round-Robin只能针对当前同优先级的任务进行cpu资源切换。
5、tick模块使用
tick模块性质属于操作系统内部模块,用户不需要初始化和配置。在芯片移植过程中,需要设置tick周期以及挂载tick的中断处理钩子函数。此外tick提供了几个维护测试接口用来获取基本的tick信息。具体函数格式及简单说明如下:
函数名:sys_time_t krhino_sys_tick_get(void)
此接口返回从tick处理开始执行,到当前为止程序所运行的tick计数。
函数名:sys_time_t krhino_sys_time_get(void)
此接口返回从tick处理开始执行,到当前为止程序所运行的ms数。
函数名:tick_t krhino_ms_to_ticks(sys_time_t ms)
此接口将当前ms数转换为tick数。
函数名:sys_time_t krhino_ticks_to_ms(tick_t ticks)
此接口当前的ticks数转换为ms值。