1- 实现基于定时器的线程阻塞,每个线程内部都内置一个定时器,定时器启动后,线程将放弃 CPU 的使用权。
2- 当同一个优先级下有两个或两个以上线程的时候,线程支持时间片功能,即我们可以指定线程持续运行一次的时间,单位为 tick。
定时器设计
调度器每次调度时都会查看就绪列表,挑选出就绪列表中优先级最高线程执行。
定时器的设计原理是,定时器启动后会把线程移除就绪列表,放入定时器列表。于是调度器扫描就绪列表时找不到线程,故不会执行线程。
SysTick 中断发生时,系统会扫描定时器列表,如果定时结束,会把线程移除定时器列表,放回就绪列表中。
时间片设计1
当线程优先级相同时,我们可以设定相同优先级的运行时间。当运行时间到时,就把该线程从就绪列表中移除,然后放入就绪列表当前优先级的尾部。调度器会从就绪列表当前优先级的头部挑选线程执行
1. 定时器代码实现
定时器模块的两个操作块:定时器控制块与定时器列表。
定时器控制块是一个结构体,记录了定时器的内部属性,比如定时器节点(是链表节点,好比一个钩子,可以钩在定时器列表上),超时函数指针(定时结束后执行的函数),定时时间
定时器控制块(每个线程都会内置定时器,会在线程初始化时将定时器初始化):
/* 定时器结构体 */
struct rt_timer
{
struct rt_object parent; /* 从rt_object继承 */
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;
注:init_tick 是定时器延时的相对时间,即从定时器启动时开始计算的时间。
timeout_tick 是从操作系统的时钟启动时开始计算的定时器延时时间。
比如说:系统定义了一个全局的系统时基计数器 rt_tick(在 clock.c 中定义),每产生一次系统时基中断(即 SysTick 中断)时,rt_tick 计数加一。假设线程要延时 10 个 tick,即 init_tick 等于 10,此时 rt_tick 等于 2,那么 timeout_tick 就等于 10 加 2 等于 12,当 rt_tick 递增到 12 的时候,线程延时到期,这个就是 timeout_tick 的实际含义。
定时器列表
static rt_list_t rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL];
用于挂载延时线程
定时器函数实现
定时器提供给外部的的接口函数:
-
系统定时器列表初始化2 -
==void rt_system_timer_init(void)==
-
定时器初始化 3- 在线程初始化时调用
void rt_timer_init(rt_timer_t timer, const char *name, void (*timeout)(void *parameter), void *parameter, rt_tick_t time, rt_uint8_t flag)
-
定时器启动 4- **==rt_timer_start==**
- 定时器模块的核心,负责把定时器按照延时做升序排列插入定时器列表
-
定时器停止 5 -
==** rt_err_t rt_timer_stop(rt_timer_t timer)**==
-
定时器控制函数 -
==**rt_timer_control**==
- 用于设置定时器的内部属性
- 通常在用户指定的线程函数内部调用,主要设置定时器的延时时间
-
定时器扫描函数6 - **
==void rt_timer_check(void)==
**- 扫描定时器列表,超时时间发生时,就调用对应的超时函数
- 在 SysTick 的中断函数中被调用
系统定时器列表初始化
遍历系统定时器列表,使其内部节点自指
/* 系统定时器列表初始化 */
void rt_system_timer_init(void)
{
int i;
for(i = 0; i < sizeof(rt_timer_list) / sizeof(rt_timer_list[0]); i++)
{
rt_list_init(rt_timer_list + i);
}
}
定时器初始化
- 设置定时器初始状态 - 非激活态
- 设置超时函数
/* 内部函数 - 初始化内置定时器 */
static void _rt_timer_init(rt_timer_t timer,
void (*timeout)(void *parameter),
void *parameter,
rt_tick_t time,
rt_uint8_t flag)
{
int i;
/* 定时器对象初始化,将其插入对象容器 */
rt_object_init((rt_object_t)timer,RT_Object_Class_Timer,name);
/* 设置标志 */
timer->parent.flag = flag;
/* 先设置为非激活状态 */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
timer->timeout_func = timeout;
timer->parameter = parameter;
/* 初始化定时器实际超时的系统节拍数 */
timer->timeout_tick = 0;
/* 初始化定时器需要超时的节拍数 */
timer->init_tick = time;
/* 初始化定时器的内置节点 */
for(i = 0 ; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
{
rt_list_init(&(timer->row[i]));
}
}
定时器停止
- 只有处于激活状态的定时器才可以停止
- 将定时器从定时器列表中移除 - 调用封装函数
**==_rt_timer_remove==**
rt_err_t rt_timer_stop(rt_timer_t timer)
{
register rt_base_t level;
/* 只有处于激活状态的定时器才可以被停止,否则退出错误码返回 */
if(!(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED))
return -RT_ERROR;
/* 关中断 */
level = rt_hw_interrupt_disable();
_rt_timer_remove(timer);
rt_hw_interrupt_enable(level);
/* 改变定时器的状态为非激活状态 */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
return RT_EOK;
}
static void _rt_timer_remove(rt_timer_t timer)
{
int i;
for(i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
{
rt_list_remove(&(timer->row[i]));
}
}
定时器启动
- 将定时器按照延时时间做升序排列插入系统定时器列表
rt_err_t rt_timer_start(rt_timer_t timer)
{
unsigned int row_lvl = 0;
rt_list_t *timer_list;
register rt_base_t level;
rt_list_t *row_head[RT_TIMER_SKIP_LIST_LEVEL];
unsigned int tst_nr;
static unsigned int random_nr;
/* 关中断 */
level = rt_hw_interrupt_disable();
/* 将定时器从系统定时器列表移除 */
_rt_timer_remove(timer);
/* 将定时器状态设置为非激活态 */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
/* 获取 timeout tick,
最大的 timeout tick 不能大于 RT_TICK_MAX/2 */
timer->timeout_tick = rt_tick_get() + timer->init_tick;
rt_hw_interrupt_enable(level);
/* 将定时器插入到定时器列表 */
/* 获取定时器列表根节点地址, rt_timer_list 是一个全局变量 */
timer_list = rt_timer_list;
/* 获取系统定时器列表第一根链表的根节点地址 */
row_head[0] = &timer_list[0];
for(row_lvl = 0; row_lvl < RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
{
for(; row_head[row_lvl] != timer_list[row_lvl].prev; row_head[row_lvl] = row_head[row_lvl]->next)
{
struct rt_timer *t;
/* 获取定时器列表节点地址 */
rt_list_t *p = row_head[row_lvl]->next;
/* 根据节点地址获取父结构的指针 */
t = rt_list_entry(p, /* 节点地址 */
struct rt_timer, /* 节点所在父结构的数据类型 */
row[row_lvl]); /* 节点在父结构中叫什么,即名字 */
/* 两个定时器的超时时间相同,则继续在定时器列表中寻找下一个节点 */
if( (t->timeout_tick - timer->timeout_tick) == 0)
{
continue;
}
else if(t->timeout_tick - timer->timeout_tick < RT_TICK_MAX /2)
{
break;
}
}
if (row_lvl != RT_TIMER_SKIP_LIST_LEVEL - 1)
{
row_head[row_lvl + 1] = row_head[row_lvl] + 1;
}
}
/* random_nr 是一个静态变量,用于记录启动了多少个定时器 */
random_nr++;
tst_nr = random_nr;
/* 将定时器插入到系统定时器列表 */
rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - 1],
&(timer->row[RT_TIMER_SKIP_LIST_LEVEL - 1])
);
/* 设置定时器标志位为激活态 */
timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED;
/* 开中断 */
rt_hw_interrupt_enable(level);
return -RT_EOK;
}
定时器扫描函数
/*
* 用于扫描系统定时器列表,当有超时时间发生时,就调用对应的超时函数
*
* 该函数会在SysTick的中断函数中被调用
*
*
*/
void rt_timer_check(void)
{
struct rt_timer *t;
rt_tick_t current_tick;
register rt_base_t level;
/* 获取系统时基计数器 rt_tick 的值 */
current_tick = rt_tick_get();
/* 关中断 */
level = rt_hw_interrupt_disable();
/* 扫描定时器 */
while(!rt_list_isempty( &rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]))
{
/* 获取第一个节点定时器的地址 */
t = rt_list_entry(rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next,
struct rt_timer,
row[RT_TIMER_SKIP_LIST_LEVEL - 1]);
if( (current_tick - t->timeout_tick) < RT_TICK_MAX / 2)
{
/* 先将定时器从系统定时器列表移除 */
_rt_timer_remove(t);
/* 调用超时函数 */
t->timeout_func(t->parameter);
/* 重新获取rt_tick */
current_tick = rt_tick_get();
/* 周期定时器 */
if( (t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
(t->parent.flag & RT_TIMER_FLAG_ACTIVATED) )
{
/* 启动定时器 */
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_timer_start(t);
}
else
{
/* 停止计时器 */
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
}
}
else
break;
}
/* 开中断 */
rt_hw_interrupt_enable(level);
}
2. 线程代码改动
定时器向线程提供接口函数,支持线程以下功能:
-
线程初始化时同时初始化定时器7
- 在初始化函数中调用定时器初始化函数
-
线程延时8
- 将线程移出就绪列表 (调度器提供的接口)
- 设置定时器属性 - 设置延时时间
- 启动定时器 - 将定时器以延时时间升序排列的方式挂载到系统定时器列表
- 执行新的系统调度(此时调度器已经找不到原来的线程,会执行新的线程)
-
超时函数9
- 将挂起的线程再插入到就绪列表中,恢复线程的可执行状态
线程初始化
- 线程初始化的同时初始化定时器
/* 初始化线程定时器 */
rt_timer_init(&(thread->thread_timer), /* 静态定时器对象 */
thread->rt_object.name, /* 定时器的名字,直接使用的是线程的名字 */
rt_thread_timeout, /* 超时函数 */
thread, /* 超时函数形参 */
0, /* 延时时间 */
RT_TIMER_FLAG_ONE_SHOT); /* 定时器的标志 */
线程延时
/* 让线程睡眠一段时间
*
* 调用定时器提供的接口
*
*/
rt_err_t rt_thread_sleep(rt_tick_t tick)
{
register rt_base_t temp;
struct rt_thread *thread;
/* 关中断 */
temp = rt_hw_interrupt_disable();
/* 获取当前线程的线程控制块 */
thread = rt_current_thread;
/* 挂起线程,操作就绪列表 */
rt_thread_suspend(thread);
/* 设置线程定时器的超时时间 - 操作定时器 */
rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &tick);
/* 启动定时器 */
rt_timer_start(&(thread->thread_timer));
/* 开中断 */
rt_hw_interrupt_enable(temp);
/* 执行系统调度 */
rt_schedule();
return RT_EOK;
}
static rt_err_t rt_thread_suspend(rt_thread_t thread)
{
register rt_base_t temp;
/* 只有就绪的线程才能被挂起,否则退出返回错误码 */
if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_READY){
return -RT_ERROR;
}
temp = rt_hw_interrupt_disable();
/* 改变线程状态 */
thread->stat = RT_THREAD_SUSPEND;
/* 将线程从就绪列表删除 */
rt_schedule_remove_thread(thread);
/* 停止线程定时器 */
rt_timer_stop(&(thread->thread_timer));
/* 开中断 */
rt_hw_interrupt_enable(temp);
return RT_EOK;
}
超时函数
- 将线程从悬挂列表中删除
- 将线程插入到就绪列表
/**
* 线程超时函数
* 当线程延时到期或者等待的资源可用或者超时时,该函数会被调用
*
* @param parameter 超时函数的形参
*/
void rt_thread_timeout(void *parameter)
{
struct rt_thread *thread;
thread = (struct rt_thread *)parameter;
/* 设置错误码为超时 */
thread->error = -RT_ETIMEOUT;
/* 将线程从挂起列表中删除 */
rt_list_remove(&(thread->tlist));
/* 将线程插入到就绪列表 */
rt_schedule_insert_thread(thread);
/* 系统调度 */
rt_schedule();
}
支持时间片
时间片设计1
rt_ubase_t init_tick; /* 剩余时间片 */
rt_ubase_t remaining_tick; /* 用于实现阻塞延时 */
修改系统时基更新函数
void rt_tick_increase(void)
{
/* 获取当前线程 */
struct rt_thread *thread;
/* 系统时基计数器加 1 操作,rt_tick 是一个全局变量 */
rt_tick++;
/* 获取当前线程控制块 */
thread = rt_thread_self();
/* 时间片递减 */
--thread->remaining_tick;
if(thread->remaining_tick == 0)
{
/* 重置时间片 */
thread->remaining_tick = thread->init_tick;
/* 让出处理器 */
rt_thread_yield();
}
/* 扫描系统定时器列表 */
rt_timer_check();
}
让出处理器:
rt_err_t rt_thread_yield(void)
{
register rt_base_t level;
struct rt_thread *thread;
/* 关中断 */
level = rt_hw_interrupt_disable();
thread = rt_current_thread;
/* 如果线程在就绪态,且同一个优先级下不止一个线程 */
if( ((thread->stat) & RT_THREAD_STAT_MASK ) == RT_THREAD_READY &&
thread->tlist.next != thread->tlist.prev)
{
/* 将时间片耗完的线程从就绪列表移除 */
rt_list_remove(&(thread->tlist));
/* 将线程插入到该优先级下的列表的尾部 */
rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]),
&(thread->tlist));
}
/* 开中断 */
rt_hw_interrupt_enable(level);
/* 执行调度 */
rt_schedule();
return RT_EOK;
}
main 函数代码:
#include <rtthread.h>
#include <rthw.h>
#include "ARMCM3.h"
#include <rtconfig.h>
#include <core_cm3.h>
/*
*************************************************************************
* 全局变量
*************************************************************************
*/
rt_uint8_t flag1;
rt_uint8_t flag2;
rt_uint8_t flag3;
extern rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];
/*
*************************************************************************
* 线程控制块& STACK &线程声明
*************************************************************************
*/
/* 定义线程控制块 */
struct rt_thread rt_flag1_thread;
struct rt_thread rt_flag2_thread;
struct rt_thread rt_flag3_thread;
ALIGN(RT_ALIGN_SIZE)
/* 定义线程栈 */
rt_uint8_t rt_flag1_thread_stack[512];
rt_uint8_t rt_flag2_thread_stack[512];
rt_uint8_t rt_flag3_thread_stack[512];
/* 线程声明 */
void flag1_thread_entry(void *p_arg);
void flag2_thread_entry(void *p_arg);
void flag3_thread_entry(void *p_arg);
/*
*************************************************************************
* 函数声明
*************************************************************************
*/
void delay(rt_uint32_t count);
int main(void)
{
/* 关中断 */
rt_hw_interrupt_disable();
/* SysTick中断频率设置 */
SysTick_Config( SystemCoreClock / RT_TICK_PER_SECOND ); // 25MHz / 100
/* 系统定时器列表初始化 */
rt_system_timer_init();
/* 调度器初始化 */
rt_system_scheduler_init();
/* 初始化空闲线程 */
rt_thread_idle_init();
/* 初始化线程 */
rt_thread_init( &rt_flag1_thread, /* 线程控制块 */
"rt_flag1_thread", /* 线程名字,字符串形式 */
flag1_thread_entry, /* 线程入口地址 */
RT_NULL, /* 线程形参 */
&rt_flag1_thread_stack[0], /* 线程栈起始地址 */
sizeof(rt_flag1_thread_stack), /* 线程栈大小,单位为字节 */
2, /* 优先级 */
4); /* 时间片 */
/* 将线程1插入就绪列表 */
//rt_list_insert_before( &(rt_thread_priority_table[0]),&(rt_flag1_thread.tlist) );
rt_thread_startup(&rt_flag1_thread);
/* 初始化线程 */
rt_thread_init( &rt_flag2_thread, /* 线程控制块 */
"rt_flag2_thread", /* 线程名字,字符串形式 */
flag2_thread_entry, /* 线程入口地址 */
RT_NULL, /* 线程形参 */
&rt_flag2_thread_stack[0], /* 线程栈起始地址 */
sizeof(rt_flag2_thread_stack), /* 线程栈大小,单位为字节 */
3, /* 优先级 */
2); /* 时间片 */
/* 将线程2插入就绪列表 */
//rt_list_insert_before( &(rt_thread_priority_table[1]),&(rt_flag2_thread.tlist) );
rt_thread_startup(&rt_flag2_thread);
/* 初始化线程 */
rt_thread_init( &rt_flag3_thread, /* 线程控制块 */
"rt_flag3_thread", /* 线程名字,字符串形式 */
flag3_thread_entry, /* 线程入口地址 */
RT_NULL, /* 线程形参 */
&rt_flag3_thread_stack[0], /* 线程栈起始地址 */
sizeof(rt_flag3_thread_stack), /* 线程栈大小,单位为字节 */
3, /* 优先级 */
3); /* 时间片 */
/* 将线程3插入就绪列表 */
rt_thread_startup(&rt_flag3_thread);
/* 启动系统调度器 */
rt_system_scheduler_start();
}
/*
*************************************************************************
* 函数实现
*************************************************************************
*/
void delay (rt_uint32_t count)
{
for(; count!=0; count--);
}
void flag1_thread_entry( void *p_arg )
{
for ( ;; )
{
flag1 = 1;
rt_thread_delay(3); //阻塞延时
flag1 = 0;
rt_thread_delay(3);
}
}
void flag2_thread_entry( void *p_arg )
{
for ( ;; )
{
flag2 = 1;
delay(100); //软件延时
flag2 = 0;
delay(100);
}
}
void flag3_thread_entry( void *p_arg )
{
for ( ;; )
{
flag3 = 1;
// rt_thread_delay(3);
delay( 100 );
flag3 = 0;
// rt_thread_delay(3);
delay( 100 );
}
}
void SysTick_Handler(void)
{
/* 进入中断 */
rt_interrupt_enter();
rt_tick_increase();
/* 离开中断 */
rt_interrupt_leave();
}
实验现象:
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于