RT-Thread学习笔记【时钟管理】
时钟
操作系统中最⼩时间单位是时钟节拍
时钟节拍可以让系统处理包括但不仅限于:线程延时、线程时间⽚轮转调度、定时器超时等事件
时钟节拍
时钟节拍(OS Tick)是特定的周期性中断,可以看作是系统⼼跳
中断之间的时间间隔取决于不同的应⽤,⼀般是1ms-100ms
注意:时钟节拍率越快,系统的额外开销(耗电量)越⼤
系统时间:从系统启动开始计数的时钟节拍
RTT中时钟节拍的长度可以根据RT_TICK_PER_SECOND的定义来调整
公式:
1
T=RT_TICK_PER_SECOND
单位:秒(s)
timeout on t2 timer时钟节拍使内核可以将线程延时若⼲个整数时钟节拍并在线程等待事件发⽣时提供等待超时的依据
频率越快,内核函数介⼊系统运⾏的⼏率越⼤,内核占⽤的处理器时间越长,系统的符合就变⼤;频率越⼩,时间处理的精度⼜不够
在stm32上⼀般设置系统时钟节拍频率为100Hz,即每个滴答的时间是10ms
时钟节拍的实现
时钟节拍由配置为中断触发模式的硬件定时器产⽣,当中断到来时,将调⽤⼀次void rt_tick_increase(void)函数,通知操作系统已经过去⼀个系统时钟,不同硬件定时器中断实现都不同
stm32定时器的实现如下:
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
RT_ASSERT(HAL_RCC_OscConfig(&RCC_OscInitStruct)== HAL_OK);
/
**Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
RT_ASSERT(HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2)== HAL_OK);
/**配置Systick分频
*/
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/ RT_TICK_PER_SECOND);
/**配置Systick时钟源
*/
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* 配置Systick中断 */
HAL_NVIC_SetPriority(SysTick_IRQn,15,0);
}
利⽤arm核的systick定时器中断驱动系统时钟,系统时钟⾃增时检查定时器时钟
/* 进⼊中断 */
rt_interrupt_enter();
HAL_IncTick();//调⽤HAL库函数
rt_tick_increase();//系统时钟++
/
* 退出中断 */
rt_interrupt_leave();
}
void rt_tick_increase(void)
{
struct rt_thread *thread;
/* 全局变量rt_tick++ */
++ rt_tick;
/* 检查时间⽚是否⽤完 */
thread =rt_thread_self();
-- thread->remaining_tick;//剩余时间--
if(thread->remaining_tick ==0)//如果剩余时间为0
{
/* 重新赋初值 */
thread->remaining_tick = thread->init_tick;
/* 挂起线程 */
rt_thread_yield();
}
/* 检查定时器是否超时 */
rt_timer_check();
}
rt_tick的值表⽰系统从启动开始总共经过的时钟节拍数,即系统时间
rt_timer_check()⽤于检查系统硬件定时器链表,如果有定时器超时将调⽤相应的超时函数,且**所有定时器超时后都会从定时器链表中被移除**,特别地,周期性定时器会在它重启时被加⼊定时器链表
获取时钟节拍
使⽤rt_tick_get获取当前rt_tick的值,即 当前的时钟节拍值
此接⼝可⽤于记录系统运⾏时间或测量某任务运⾏的时间
rt_tick_t rt_tick_get(void)
{
/* 返回当前时钟节拍值 */
return rt_tick;
}
定时器
定时器:从指定的时刻开始,经过指定时间后触发⼀个事件
1. 硬件定时器:MCU本⾝提供的物理定时器,精度较⾼,⼀般以中断触发,也能连接其他外设输出特定信号
2. 软件定时器:操作系统提供的系统接⼝,构建在硬件定时器基础上,使系统能提供不限数⽬的定时器服务
RTT软件定时器基于OS TIck提供,它的定时数值必须是OS Tick的整数倍
定时器基本类型和⼯作模式
1. 单次触发定时器
启动后只触发⼀次定时器事件,然后定时器⾃动停⽌
2. 周期触发定时器
周期性触发定时器事件,直到⽤户停⽌,否则将永远持续执⾏
3. HARD_TIMER模式(RTT定时器默认模式)
定时器超时函数在中断上下⽂环境(默认为系统时钟中断上下⽂环境)中执⾏
HARD_TIMER模式下超时函数的要求与中断服务函数的要求相同:执⾏时间尽量短、执⾏时不应导致当前上下⽂挂起或等待,不应在超时函数中申请或释放动态内存等
⼈话:系统时钟中断时运⾏中断服务函数
4. SOFT_TIMER
可通过宏定义RT_USING_TIMER_SOFT控制是否启⽤SOFT_TIMER模式
该模式被启⽤后,系统在初始化时创建⼀个timer线程,然后SOFT_TIMER模式的定时器超时函数会在timer线程的上下⽂环境中执⾏
⼈话:创建⼀个中断服务函数的线程,有定时器中断时跳到中断服务函数的线程运⾏
初始化/创建定时器时,使⽤参数RT_TIMER_FLAG_HARD_TIMER和RT_TIMER_FLAG_SOFT_TIMER来确定使⽤哪种模式
定时器⼯作⽅式
两个重要全局变量
系统时间rt_tick:当硬件定时器中断来临时,⾃增1
定时器链表rt_timer_list:系统新创建并激活的定时器会按照超时时间排序插⼊到此链表
定时器控制块
定时器控制块是操作系统⽤于管理定时器的数据结构
如下所⽰:
struct rt_timer
{
struct rt_object parent;/**< 继承内核对象的属性 */
rt_list_t row[RT_TIMER_SKIP_LIST_LEVEL];//定时器链表节点
void(*timeout_func)(void*parameter);/**< 定时器超时调⽤的函数(定时器中断服务函数) */
void*parameter;/**< 超时函数的参数 */
rt_tick_t init_tick;/**< 定时器初始超时节拍数 */
rt_tick_t timeout_tick;/**< 定时器实际超时节拍数 */
};
typedef struct rt_timer *rt_timer_t;//定时器内核对象继承⾃内核对象控制块
//内核控制块
struct rt_object
{
char name[RT_NAME_MAX];//内核对象名称
rt_uint8_t type;//内核对象类型
rt_uint8_t flag;//内核对象参数
#ifdef RT_USING_MODULE
void*module_id;//应⽤模块的id
#endif
rt_list_t list;//内核对象管理链表
};
typedef struct rt_object *rt_object_t;//将内核对象指针封装成“内核对象类”
定时器控制块被封装为struct rt_timer,再形成定时器内核对象rt_timer_t,链接到内核对象容器中进⾏管理(继承来的list成员可以将其加⼊内核对象容器链表)
调⽤list成员可以把⼀个激活(已启动)的定时器链接到rt_timer_list链表中
定时器跳表(Skip List)算法
系统新创建并激活的定时器都会按照以超时时间排序的⽅式插⼊rt_timer_list链表中,即 rt_timer_list是⼀个有序链表,可以使⽤跳表算法来加快查链表元素的速度
跳表:⼀种基于并联链表的数据结构,在链表基础上增加了“跳跃”功能,实现插⼊、删除、查的时间
复杂度均为O(log n)
跳表算法采⽤类似⼆叉搜索树的⽅式:从有序链表中提出⼀些节点形成新的链表,这个链表作为对原链表的索引
定时器调表从最上层索引开始,依次判断,如果满⾜条件,则进⼊下⼀层索引,依次跳转直到到最底层的原始链表,减少⽐较次数、提升查效率,换句话说,这是⼀种“空间换时间”的算法
在RTT中通过宏定义RT_TIMER_SKIP_LIST_LEVEL来配置调表层数,默认为1,每增加1,表⽰在原链表基础上增加1级索引
定时器管理
定时器管理系统初始化
系统启动时初始化定时器管理系统,接⼝如下:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论