RT-Thread 的空闲线程和阻塞延时

RTOS中的延时叫阻塞延时,即线程需要延时的时候,线程会放弃CPU的使用权,CPU可以去干其他事情,当线程延时时间到,重新获取CPU使用权,线程继续运行,这样就充分利用了CPU的使用权,而不是刚等着。

  当线程需要延时,CPU进入阻塞状态,那CPU又去干什么事情了?如果没有其它线程可以运行,RTOS都会为CPU创建一个空闲线程,这个时候CPU就运行空闲线程。在RT-Thread中,空闲线程是系统在初始化的时候创建的优先级最低的线程。空闲线程主体主要做一些系统内存的清理工作。在实际应用中,当系统进入空闲线程的时候,可在空闲线程中让单片机进入休眠或者低功耗等操作。

1、实现空闲线程

1.1定义空闲线程的栈

  空闲线程的栈在idle.c文件中定义,

#include <rtthread.h>
#include <rthw.h>
#define IDLE_THREAD_STACK_SIZE 512
ALIGN(RT_ALIGN_SIZE)
static rt_uint8_t rt_thread_stack[IDLE_THREAD_STACK_SIZE];

空闲线程的栈是一个定义好的数组,大小由IDLE_THREAD_STACK_SIZE 这个宏控制,默认为512,即128个字。

1.2空闲线程的线程控制块

/* 空闲线程的线程控制块 */
 struct rt_thread idle;

2、实现阻塞延时

阻塞延时的阻塞是指线程调用该延时函数后,线程会被剥离CPU使用权,然后进入阻塞状态,直到延时结束,线程重新获取CPU使用权才可以继续运行,在线程阻塞这段时间,CPU可以去执行其他的线程,如果其他的线程也在延时状态,那么CPU就将运行空闲线程。阻塞延时函数在thread.c中定义。

void rt_thread_delay(rt_tick_t tick)
{ 
 struct rt_thread *thread; 

 /* 获取当前线程的线程控制块 */
 thread = rt_current_thread; (1) 

 /* 设置延时时间 */
 thread->remaining_tick = tick; (2)

 /* 进行系统调度 */
 rt_schedule(); (3)
 }

(1)获取当前线程的线程控制块。rt_current_thread 是一个在scheduler.c中定义的全局变量,用于指向当前正在运行的线程的线程控制块。

(2)remaining_tick 是线程控制块的一个成员,用于记录线程需要延时的时间,单位为SysTick 的中断周期。

3、SysTick_Handler中断服务函数

在系统调度函数rt_schedule()中,会判断每个线程的线程控制块中的延时成员remaining_tick的值是否为0,如果为0,就要将对应的线程就绪,如果不为0,就继续延时。如果一个线程要延时,一开始remaining_tick 肯定不为0,当remaining_tick为0就延时结束,那么remaining_tick是以什么周期在递减?在哪里递减?在RT-Thread中,这个周期由SysTick中断提供,操作系统里面最小的时间单位就是SysTick的中断周期,我们称之为一个tick,SysTick中断服务函数

/* 关中断 */
rt_hw_interrupt_disable(); (1) 

/* SysTick 中断频率设置 */
SysTick_Config( SystemCoreClock / RT_TICK_PER_SECOND ); (2) 

void SysTick_Handler(void) (3)
{ 
    /* 进入中断 */
     rt_interrupt_enter(); (3)-1
    /* 时基更新 */
     rt_tick_increase(); (3)-2
     /* 离开中断 */
     rt_interrupt_leave(); (3)-3
 }

(1)关中断。在程序开始的时候把中断关掉是一个好习惯,等系统初始化完毕,线程创建完毕,启动系统调度的时候会重新打开中断。

(2)初始化SysTick,调用固件库函数SysTick_Config来实现,配置中断周期为10ms,中断优先级为最低(无论中断优先级分组怎么分都是最低,因为这里把表示SysTick中断优先级的四个位全部配置为1,即15,在Cortex-M内核中,优先级越低,逻辑优先级最低),RT_TICK_PER_SECOND是一个在rtconfig.h中定义的宏,目前等于100。 

(3)更新系统时基,该函数在clock.c中实现。

进入和离开中断,这两个函数在irq.c 中实现。

SysTick 初始化函数(在core_cm3.h 中定义)
 
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
 {
  /* 非法的重装载寄存器值 */
 if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
  { 
     return (1UL);

   } 
 /* 设置重装载寄存器的值 */
 SysTick->LOAD = (uint32_t)(ticks - 1UL);
 /* 设置 SysTick 的中断优先级 */
 NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL);
 /* 加载 SysTick 计数器值 */
 SysTick->VAL = 0UL;
 /* 设置系统定时器的时钟源为 AHBCLK
 使能 SysTick 定时器中断
 使能 SysTick 定时器 */
 SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
 SysTick_CTRL_TICKINT_Msk |
 SysTick_CTRL_ENABLE_Msk;
 return (0UL);
 }

 3.1系统时基更新函数

#include <rtthread.h>
#include <rthw.h>

static rt_tick_t rt_tick = 0; /* 系统时基计数器 */ (1) 
extern rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];


void rt_tick_increase(void) 
{
    rt_ubase_t i;
    struct rt_thread *thread;
    rt_tick ++; (2)

     /* 扫描就绪列表中所有线程的 remaining_tick,如果不为 0,则减 1 */
     for(i=0; i<RT_THREAD_PRIORITY_MAX; i++) (3)
     {
       thread = rt_list_entry( rt_thread_priority_table[i].next,
                    struct rt_thread,
tlist);
if(thread->remaining_tick > 0)   {   thread->remaining_tick --;   } } /* 系统调度 */ rt_schedule(); (4) }

(1)系统时基计数器,是一个全局变量,用来记录产生了多少次SysTick 中断。

(2)系统时基计数器加1操作

(3)扫描就绪列表中的remaining_tick,如果不为0,则减 1。

(4)进行系统调度

进入和离开中断函数

#include <rtthread.h>
#include <rthw.h>
 /* 中断计数器 */
volatile rt_uint8_t rt_interrupt_nest; (1)  

/** * 当 BSP 文件的中断服务函数进入时会调用该函数 * * @note 请不要在应用程序中调用该函数 * * @see rt_interrupt_leave */ void rt_interrupt_enter(void) (2) {   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) (3) {   rt_base_t level;   /* 关中断 */   level = rt_hw_interrupt_disable();   /* 中断计数器-- */   rt_interrupt_nest --;   /* 开中断 */   rt_hw_interrupt_enable(level); }

(1)中断计数器,是一个全局变量,用来记录中断嵌套次数。

(2)进入中断函数,中断计数器rt_interrupt_nest 加1 操作。当BSP文件的中断服务函数进入时会调用该函数,应用程序不能调用,切记。

(3)离开中断函数,中断计数器rt_interrupt_nest 减1操作。当BSP文件的中断服务函数进入时会调用该函数,应用程序不能调用,切记。

原文地址:https://www.cnblogs.com/tansuoxinweilai/p/14964973.html