RTOS 中的延时叫阻塞延时,即线程需要延时的时候,线程会放弃 CPU 的使用权,CPU 可以去干其它的事情,当线程延时时间到,重新获取 CPU 使用权,线程继续运行,这样就充分地利用了 CPU 的资源,而不是干等着。如果没有其它线程可以运行,RTOS 都会为 CPU 创建一个空闲线程,这个时候 CPU 就运行空闲线程,且空闲线程的优先级最低。
空闲线程
在idle.c文件下
定义空闲线程栈
定义空闲线程控制块
定义空闲线程函数
空闲线程初始化
idle.c
#define IDLE_THREAD_STACK_SIZE 512
ALIGN(RT_ALIGN_SIZE)
static rt_uint8_t rt_thread_stack[IDLE_THREAD_STACK_SIZE];
struct rt_thread idle;
rt_ubase_t rt_idletask_ctr = 0;
void rt_thread_idle_entry(void *parameter)
{
parameter = parameter;
while (1)
{
rt_idletask_ctr ++;
}
}
/**
* @ingroup SystemInit
*
* 初始化空闲线程,启动空闲线程
*
* @NOTE 当系统初始化的时候该函数必须被调用
*/
void rt_thread_idle_init(void)
{
/* 初始化线程 */
rt_thread_init(&idle,
"idle",
rt_thread_idle_entry,
RT_NULL,
&rt_thread_stack[0],
sizeof(rt_thread_stack));
/* 将线程插入到就绪列表 */
rt_list_insert_before( &(rt_thread_priority_table[RT_THREAD_PRIORITY_MAX-1]),&(idle.tlist) );
}
阻塞延时
阻塞延时函数
thread.c
void rt_thread_delay(rt_tick_t tick)
{
struct rt_thread *thread;
/* 获取当前线程的线程控制块 */
thread = rt_current_thread;
/* 设置延时时间 */
thread->remaining_tick = tick;
/* 进行系统调度 */
rt_schedule();
}
remaining_tick 是线程控制块的一个成员,用于记录线程需要延时的时间,单位为 SysTick 的中断周期。比如我们本书当中 SysTick 的中断周期为 10ms,调用 rt_thread_delay(2)则完成 2*10ms 的延时。
remaining_tick 定义
/*
*************************************************************************
* 线程结构体
*************************************************************************
*/
struct rt_thread
{
/* rt 对象 */
char name[RT_NAME_MAX]; /* 对象的名字 */
rt_uint8_t type; /* 对象类型 */
rt_uint8_t flags; /* 对象的状态 */
rt_list_t list; /* 对象的列表节点 */
rt_list_t tlist; /* 线程链表节点 */
void *sp; /* 线程栈指针 */
void *entry; /* 线程入口地址 */
void *parameter; /* 线程形参 */
void *stack_addr; /* 线程起始地址 */
rt_uint32_t stack_size; /* 线程栈大小,单位为字节 */
rt_ubase_t remaining_tick; /* 用于实现阻塞延时 */
};
typedef struct rt_thread *rt_thread_t;
系统调度
/* 系统调度 */
void rt_schedule(void)
{
struct rt_thread *to_thread;
struct rt_thread *from_thread;
#if 0
/* 两个线程轮流切换 */
if( rt_current_thread == rt_list_entry( rt_thread_priority_table[0].next,
struct rt_thread,
tlist) )
{
from_thread = rt_current_thread;
to_thread = rt_list_entry( rt_thread_priority_table[1].next,
struct rt_thread,
tlist);
rt_current_thread = to_thread;
}
else
{
from_thread = rt_current_thread;
to_thread = rt_list_entry( rt_thread_priority_table[0].next,
struct rt_thread,
tlist);
rt_current_thread = to_thread;
}
#else
/* 如果当前线程是空闲线程,那么就去尝试执行线程1或者线程2,
看看他们的延时时间是否结束,如果线程的延时时间均没有到期,
那就返回继续执行空闲线程 */
if( rt_current_thread == &idle )
{
if(rt_flag1_thread.remaining_tick == 0)
{
from_thread = rt_current_thread;
to_thread = &rt_flag1_thread;
rt_current_thread = to_thread;
}
else if(rt_flag2_thread.remaining_tick == 0)
{
from_thread = rt_current_thread;
to_thread = &rt_flag2_thread;
rt_current_thread = to_thread;
}
else
{
return; /* 线程延时均没有到期则返回,继续执行空闲线程 */
}
}
else
{
/*如果当前线程是线程1或者线程2的话,检查下另外一个线程,如果另外的线程不在延时中,就切换到该线程
否则,判断下当前线程是否应该进入延时状态,如果是的话,就切换到空闲线程。否则就不进行任何切换 */
if(rt_current_thread == &rt_flag1_thread)
{
if(rt_flag2_thread.remaining_tick == 0)
{
from_thread = rt_current_thread;
to_thread = &rt_flag2_thread;
rt_current_thread = to_thread;
}
else if(rt_current_thread->remaining_tick != 0)
{
from_thread = rt_current_thread;
to_thread = &idle;
rt_current_thread = to_thread;
}
else
{
return; /* 返回,不进行切换,因为两个线程都处于延时中 */
}
}
else if(rt_current_thread == &rt_flag2_thread)
{
if(rt_flag1_thread.remaining_tick == 0)
{
from_thread = rt_current_thread;
to_thread = &rt_flag1_thread;
rt_current_thread = to_thread;
}
else if(rt_current_thread->remaining_tick != 0)
{
from_thread = rt_current_thread;
to_thread = &idle;
rt_current_thread = to_thread;
}
else
{
return; /* 返回,不进行切换,因为两个线程都处于延时中 */
}
}
}
#endif
/* 产生上下文切换 */
rt_hw_context_switch((rt_uint32_t)&from_thread->sp,(rt_uint32_t)&to_thread->sp);
}
SysTick_Handler 中断服务函数
/* 关中断 */
rt_hw_interrupt_disable();
/* SysTick中断频率设置 */
SysTick_Config( SystemCoreClock / RT_TICK_PER_SECOND );
void SysTick_Handler(void)
{
/* 进入中断 */
rt_interrupt_enter();
rt_tick_increase();
/* 离开中断 */
rt_interrupt_leave();
}
clock.c
static rt_tick_t rt_tick = 0;
void rt_tick_increase(void)
{
rt_ubase_t i;
struct rt_thread *thread;
rt_tick ++;
/* 扫描就绪列表中所有线程的remaining_tick,如果不为0,则减1 */
for(i=0; i
{
thread = rt_list_entry( rt_thread_priority_table.next,
struct rt_thread,
tlist);
if(thread->remaining_tick > 0)
{
thread->remaining_tick --;
}
}
/* 系统调度 */
rt_schedule();
}
irq.c
/* 中断计数器 */
volatile rt_uint8_t rt_interrupt_nest;
/**
* 当BSP文件的中断服务函数进入时会调用该函数
*
* @note 请不要在应用程序中调用该函数
*
* @SEE rt_interrupt_leave
*/
void rt_interrupt_enter(void)
{
rt_base_t level;
/* 关中断 */
level = rt_hw_interrupt_disable();
/* 中断计数器++ */
rt_interrupt_nest ++;
/* 开中断 */
rt_hw_interrupt_enable(level);
}
/**
* 当BSP文件的中断服务函数离开时会调用该函数
*
* @note 请不要在应用程序中调用该函数
*
* @see rt_interrupt_enter
*/
void rt_interrupt_leave(void)
{
rt_base_t level;
/* 关中断 */
level = rt_hw_interrupt_disable();
/* 中断计数器-- */
rt_interrupt_nest --;
/* 开中断 */
rt_hw_interrupt_enable(level);
}
rt_interrupt_nest中断计数器,是一个全局变量,用了记录中断嵌套次数。当 BSP 文件的中断服务函数进入时会调用该函数,应用程序不能调用,切记。
main函数
/*
*************************************************************************
* 全局变量
*************************************************************************
*/
rt_uint8_t flag1;
rt_uint8_t flag2;
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;
ALIGN(RT_ALIGN_SIZE)
/* 定义线程栈 */
rt_uint8_t rt_flag1_thread_stack[512];
rt_uint8_t rt_flag2_thread_stack[512];
/* 线程声明 */
void flag1_thread_entry(void *p_arg);
void flag2_thread_entry(void *p_arg);
/*
*************************************************************************
* 函数声明
*************************************************************************
*/
void delay(uint32_t count);
/************************************************************************
* @Brief main函数
* @param 无
* @retval 无
*
* @attention
***********************************************************************
*/
int main(void)
{
/* 硬件初始化 */
/* 将硬件相关的初始化放在这里,如果是软件仿真则没有相关初始化代码 */
/* 关中断 */
rt_hw_interrupt_disable();
/* SysTick中断频率设置 */
SysTick_Config( SystemCoreClock / RT_TICK_PER_SECOND );
/* 调度器初始化 */
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) ); /* 线程栈大小,单位为字节 */
/* 将线程插入到就绪列表 */
rt_list_insert_before( &(rt_thread_priority_table[0]),&(rt_flag1_thread.tlist) );
/* 初始化线程 */
rt_thread_init( &rt_flag2_thread, /* 线程控制块 */
"rt_flag2_thread", /* 线程名字,字符串形式 */
flag2_thread_entry, /* 线程入口地址 */
RT_NULL, /* 线程形参 */
&rt_flag2_thread_stack[0], /* 线程栈起始地址 */
sizeof(rt_flag2_thread_stack) ); /* 线程栈大小,单位为字节 */
/* 将线程插入到就绪列表 */
rt_list_insert_before( &(rt_thread_priority_table[1]),&(rt_flag2_thread.tlist) );
/* 启动系统调度器 */
rt_system_scheduler_start();
}
/*
*************************************************************************
* 函数实现
*************************************************************************
*/
/* 软件延时 */
void delay (uint32_t count)
{
for(; count!=0; count--);
}
/* 线程1 */
void flag1_thread_entry( void *p_arg )
{
for( ;; )
{
#if 0
flag1 = 1;
delay( 100 );
flag1 = 0;
delay( 100 );
/* 线程切换,这里是手动切换 */
rt_schedule();
#else
flag1 = 1;
rt_thread_delay(2);
flag1 = 0;
rt_thread_delay(2);
#endif
}
}
/* 线程2 */
void flag2_thread_entry( void *p_arg )
{
for( ;; )
{
#if 0
flag2 = 1;
delay( 100 );
flag2 = 0;
delay( 100 );
/* 线程切换,这里是手动切换 */
rt_schedule();
#else
flag2 = 1;
rt_thread_delay(2);
flag2 = 0;
rt_thread_delay(2);
#endif
}
}
void SysTick_Handler(void)
{
/* 进入中断 */
rt_interrupt_enter();
rt_tick_increase();
/* 离开中断 */
rt_interrupt_leave();
}
原作者:橘长_
|