zoukankan      html  css  js  c++  java
  • 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文件的中断服务函数进入时会调用该函数,应用程序不能调用,切记。

  • 相关阅读:
    Nginx下配置SSL安全协议
    Java嵌套类
    python命名空间与作用域
    Effective Java 4:类和接口
    VIM快捷键(转载)
    Eclipse快捷键
    Effective Java 3:对于所有对象都通用的方法
    Effective Java 1:创建和销毁对象-续
    Effective Java 1:创建和销毁对象
    python文件操作(转载)
  • 原文地址:https://www.cnblogs.com/tansuoxinweilai/p/14964973.html
Copyright © 2011-2022 走看看