RT-Thread--中断管理

Cortex-M CPU架构基础

 寄存器简介

  • Cortex-M 系列 CPU 的寄存器组里有 R0\~R15 共 16 个通用寄存器组和若干特殊功能寄存器,如下图所示。
  • 通用寄存器组里的 R13 作为堆栈指针寄存器 (Stack Pointer,SP);R14 作为连接寄存器 (Link Register,LR),用于在调用子程序时,存储返回地址;R15 作为程序计数器 (Program Counter,PC),其中堆栈指针寄存器可以是主堆栈指针(MSP),也可以是进程堆栈指针(PSP)。

  • 特殊功能寄存器包括程序状态字寄存器组(PSRs)、中断屏蔽寄存器组(PRIMASK, FAULTMASK, BASEPRI)、控制寄存器(CONTROL),可以通过 MSR/MRS 指令来访问特殊功能寄存器,例如:
  • MRS R0, CONTROL ; 读取 CONTROL 到 R0 中
    MSR CONTROL, R0 ; 写入 R0 到 CONTROL 寄存器中
  • 程序状态字寄存器里保存算术与逻辑标志,例如负数标志,零结果标志,溢出标志等等。中断屏蔽寄存器组控制 Cortex-M 的中断除能。控制寄存器用来定义特权级别和当前使用哪个堆栈指针。

  • 如果是具有浮点单元的 Cortex-M4 或者 Cortex-M7,控制寄存器也用来指示浮点单元当前是否在使用,浮点单元包含了 32 个浮点通用寄存器 S0\~S31 和特殊 FPSCR 寄存器(Floating point status and control register)。

操作模式和特权模式

  • Cortex-M 引入了操作模式和特权级别的概念,分别为线程模式和处理模式,如果进入异常或中断处理则进入处理模式,其他情况则为线程模式。

  • Cortex-M 有两个运行级别,分别为特权级和用户级,线程模式可以工作在特权级或者用户级,而处理模式总工作在特权级,可通过 CONTROL 特殊寄存器控制;
  • Cortex-M 的堆栈寄存器 SP 对应两个物理寄存器 MSP 和 PSP,MSP 为主堆栈,PSP 为进程堆栈,处理模式总是使用 MSP 作为堆栈,线程模式可以选择使用 MSP 或 PSP 作为堆栈,同样通过 CONTROL 特殊寄存器控制。复位后,Cortex-M 默认进入线程模式、特权级、使用 MSP 堆栈。

嵌套向量中断控制器

  •  Cortex-M 中断控制器名为 NVIC(嵌套向量中断控制器),支持中断嵌套功能。当一个中断触发并且系统进行响应时,处理器硬件会将当前运行位置的上下文寄存器自动压入中断栈中,这部分的寄存器包括 PSR、PC、LR、R12、R3-R0 寄存器。

  • 当系统正在服务一个中断时,如果有一个更高优先级的中断触发,那么处理器同样会打断当前运行的中断服务程序,然后把这个中断服务程序上下文的 PSR、PC、LR、R12、R3-R0 寄存器自动保存到中断栈中。

PendSV系统调用

  • PendSV 也称为可悬起的系统调用,它是一种异常,可以像普通的中断一样被挂起,它是专门用来辅助操作系统进行上下文切换的;
  • PendSV 异常会被初始化为最低优先级的异常。每次需要进行上下文切换的时候,会手动触发 PendSV 异常,在 PendSV 异常处理函数中进行上下文切换;

RT-Thread中断工作机制

 中断向量表

  •  中断向量表是所有中断处理程序的入口,如下图所示是 Cortex-M 系列的中断处理过程:把一个函数(用户中断服务程序)同一个虚拟中断向量表中的中断向量联系在一起。当中断向量对应中断发生的时候,被挂接的用户中断服务程序就会被调用执行。

  • 在 Cortex-M 内核上,所有中断都采用中断向量表的方式进行处理,即当一个中断触发时,处理器将直接判定是哪个中断源,然后直接跳转到相应的固定位置进行处理,每个中断服务程序必须排列在一起放在统一的地址上(这个地址必须要设置到 NVIC 的中断向量偏移寄存器中)。中断向量表一般由一个数组定义或在起始代码中给出,默认采用起始代码给出:
    __Vectors       DCD     __initial_sp               ; Top of Stack
                    DCD     Reset_Handler              ; Reset Handler
                    DCD     NMI_Handler                ; NMI Handler
                    DCD     HardFault_Handler          ; Hard Fault Handler
                    DCD     MemManage_Handler          ; MPU Fault Handler
                    DCD     BusFault_Handler           ; Bus Fault Handler
                    DCD     UsageFault_Handler         ; Usage Fault Handler
                    DCD     0                          ; Reserved
                    DCD     0                          ; Reserved
                    DCD     0                          ; Reserved
                    DCD     0                          ; Reserved
                    DCD     SVC_Handler                ; SVCall Handler
                    DCD     DebugMon_Handler           ; Debug Monitor Handler
                    DCD     0                          ; Reserved
                    DCD     PendSV_Handler             ; PendSV Handler
                    DCD     SysTick_Handler            ; SysTick Handler
    
    .......
    
    
    __Vectors_End
    
    __Vectors_Size  EQU  __Vectors_End - __Vectors
    
                    AREA    |.text|, CODE, READONLY
    
    ; Reset handler
    Reset_Handler    PROC
                     EXPORT  Reset_Handler             [WEAK]
            IMPORT  SystemInit
            IMPORT  __main
    
                     LDR     R0, =SystemInit
                     BLX     R0
                     LDR     R0, =__main
                     BX      R0
                     ENDP
    
    ; Dummy Exception Handlers (infinite loops which can be modified)
    
    NMI_Handler     PROC
                    EXPORT  NMI_Handler                [WEAK]
                    B       .
                    ENDP
    HardFault_Handler\
                    PROC
                    EXPORT  HardFault_Handler          [WEAK]
                    B       .
                    ENDP
    MemManage_Handler\
                    PROC
                    EXPORT  MemManage_Handler          [WEAK]
                    B       .
                    ENDP
    BusFault_Handler\
                    PROC
                    EXPORT  BusFault_Handler           [WEAK]
                    B       .
                    ENDP
    UsageFault_Handler\
                    PROC
                    EXPORT  UsageFault_Handler         [WEAK]
                    B       .
                    ENDP
    SVC_Handler     PROC
                    EXPORT  SVC_Handler                [WEAK]
                    B       .
                    ENDP
    DebugMon_Handler\
                    PROC
                    EXPORT  DebugMon_Handler           [WEAK]
                    B       .
                    ENDP
    PendSV_Handler  PROC
                    EXPORT  PendSV_Handler             [WEAK]
                    B       .
                    ENDP
    SysTick_Handler PROC
                    EXPORT  SysTick_Handler            [WEAK]
                    B       .
                    ENDP

中断处理过程

  • void SysTick_Handler(void)
    {
        /* enter interrupt */
        rt_interrupt_enter();
    
        rt_tick_increase();
    
        /* leave interrupt */
        rt_interrupt_leave();
    }
  •  RT-Thread 中断管理中,将中断处理程序分为中断前导程序、用户中断服务程序、中断后续程序三部分;

中断前导程序
  • 保存 CPU 中断现场,这部分跟 CPU 架构相关,不同 CPU 架构的实现方式有差异,对于 Cortex-M 来说,该工作由硬件自动完成,处理器硬件会将当前运行部分的上下文寄存器自动压入中断栈中,这部分的寄存器包括 PSR、PC、LR、R12、R3-R0 寄存器。
  • 通知内核进入中断状态,调用 rt_interrupt_enter() 函数,作用是把全局变量 rt_interrupt_nest 加 1,用它来记录中断嵌套的层数;
  • /**
     * This function will be invoked by BSP, when enter interrupt service routine
     *
     * @note please don't invoke this routine in application
     *
     * @see rt_interrupt_leave
     */
    void rt_interrupt_enter(void)
    {
        rt_base_t level;
    
        RT_DEBUG_LOG(RT_DEBUG_IRQ, ("irq coming..., irq nest:%d\n",
                                    rt_interrupt_nest));
    
        level = rt_hw_interrupt_disable();
        rt_interrupt_nest ++;
        RT_OBJECT_HOOK_CALL(rt_interrupt_enter_hook,());
        rt_hw_interrupt_enable(level);
    }
    RTM_EXPORT(rt_interrupt_enter);
  • 用户中断服务程序
  • 中断服务程序分为两种情况:
    1. 不进行线程切换,这种情况下用户中断服务程序和中断后续程序运行完毕后退出中断模式,返回被中断的线程‘’
    2. 中断处理过程中需要进行线程切换,这种情况会调用 rt_hw_context_switch_interrupt() 函数进行上下文切换,该函数跟 CPU 架构相关,不同 CPU 架构的实现方式有差异,如下图所示:

  • 在 Cortex-M 架构中,rt_hw_context_switch_interrupt() 的函数实现流程如下图所示,它将设置需要切换的线程 rt_interrupt_to_thread 变量,然后触发 PendSV 异常(PendSV 异常是专门用来辅助上下文切换的,且被初始化为最低优先级的异常)。PendSV 异常被触发后,不会立即进行 PendSV 异常中断处理程序,因为此时还在中断处理中,只有当中断后续程序运行完毕,真正退出中断处理后,才进入 PendSV 异常中断处理程序。
中断后续程序
  • 中断后续程序主要完成的工作是:
    1.  通知内核离开中断状态,通过调用 rt_interrupt_leave() 函数,将全局变量 rt_interrupt_nest 减 1;
    2. /**
       * This function will be invoked by BSP, when leave interrupt service routine
       *
       * @note please don't invoke this routine in application
       *
       * @see rt_interrupt_enter
       */
      void rt_interrupt_leave(void)
      {
          rt_base_t level;
      
          RT_DEBUG_LOG(RT_DEBUG_IRQ, ("irq leave, irq nest:%d\n",
                                      rt_interrupt_nest));
      
          level = rt_hw_interrupt_disable();
          rt_interrupt_nest --;
          RT_OBJECT_HOOK_CALL(rt_interrupt_leave_hook,());
          rt_hw_interrupt_enable(level);
      }
      RTM_EXPORT(rt_interrupt_leave);
    3. 恢复中断前的 CPU 上下文,如果在中断处理过程中未进行线程切换,那么恢复 from 线程的 CPU 上下文,如果在中断中进行了线程切换,那么恢复 to 线程的 CPU 上下文。这部分实现跟 CPU 架构相关;

中断嵌套

  • 在允许中断嵌套的情况下,在执行中断服务程序的过程中,如果出现高优先级的中断,当前中断服务程序的执行将被打断,以执行高优先级中断的中断服务程序,当高优先级中断的处理完成后,被打断的中断服务程序才又得到继续执行,如果需要进行线程调度,线程的上下文切换将在所有中断处理程序都运行结束时才发生,如下图所示。

中断栈

  •  在中断处理过程中,在系统响应中断前,软件代码(或处理器)需要把当前线程的上下文保存下来(通常保存在当前线程的线程栈中),再调用中断服务程序进行中断响应、处理;
  • 在进行中断处理时,中断处理函数中很可能会有自己的局部变量,这些都需要相应的栈空间来保存,所以中断响应依然需要一个栈空间来做为上下文,运行中断处理函数。中断栈可以保存在打断线程的栈中,当从中断中退出时,返回相应的线程继续执行;
  • RT-Thread 采用的方式是提供独立的中断栈,即中断发生时,中断的前期处理程序会将用户的栈指针更换到系统事先留出的中断栈空间中,等中断退出时再恢复用户的栈指针。这样中断就不会占用线程的栈空间,从而提高了内存空间的利用率,且随着线程的增加,这种减少内存占用的效果也越明显。
  • 在 Cortex-M 处理器内核里有两个堆栈指针,一个是主堆栈指针(MSP),是默认的堆栈指针,在运行第一个线程之前和在中断和异常服务程序里使用;另一个是线程堆栈指针(PSP),在线程里使用。在中断和异常服务程序退出时,修改 LR 寄存器的第 2 位的值为 1,线程的 SP 就由 MSP 切换到 PSP。

中断的底半处理

  •  对于另外一些中断,中断服务程序在取得硬件状态或数据以后,还需要进行一系列更耗时的处理过程,通常需要将该中断分割为两部分,即上半部分(Top Half)和底半部分(Bottom Half)。在上半部分中,取得硬件状态和数据后,打开被屏蔽的中断,给相关线程发送一条通知(可以是 RT-Thread 所提供的信号量、事件、邮箱或消息队列等方式),然后结束中断服务程序;而接下来,相关的线程在接收到通知后,接着对状态或数据进行进一步的处理,这一过程称之为底半处理。
  • 我们以一个虚拟的网络设备接收网络数据包作为范例,如下代码,并假设接收到数据报文后,系统对报文的分析、处理是一个相对耗时的,比外部中断源信号重要性小许多的,而且在不屏蔽中断源信号情况下也能处理的过程;
  • 创建了一个 nwt 线程,这个线程在启动运行后,将阻塞在 nw_bh_sem 信号上,一旦这个信号量被释放,将执行接下来的 nw_packet_parser 过程,开始 Bottom Half 的事件处理。
  • /*
     * 程序清单:中断底半处理例子
     */
    
    /* 用于唤醒线程的信号量 */
    rt_sem_t nw_bh_sem;
    
    /* 数据读取、分析的线程 */
    void demo_nw_thread(void *param)
    {
        /* 首先对设备进行必要的初始化工作 */
        device_init_setting();
    
        /*.. 其他的一些操作..*/
    
        /* 创建一个 semaphore 来响应 Bottom Half 的事件 */
        nw_bh_sem = rt_sem_create("bh_sem", 0, RT_IPC_FLAG_FIFO);
    
        while(1)
        {
            /* 最后,让 demo_nw_thread 等待在 nw_bh_sem 上 */
            rt_sem_take(nw_bh_sem, RT_WAITING_FOREVER);
    
            /* 接收到 semaphore 信号后,开始真正的 Bottom Half 处理过程 */
            nw_packet_parser (packet_buffer);
            nw_packet_process(packet_buffer);
        }
    }
    
    int main(void)
    {
        rt_thread_t thread;
    
        /* 创建处理线程 */
        thread = rt_thread_create("nwt",demo_nw_thread, RT_NULL, 1024, 20, 5);
    
        if (thread != RT_NULL)
            rt_thread_startup(thread);
    }
    
    void demo_nw_isr(int vector, void *param)
    {
        /* 当 network 设备接收到数据后,陷入中断异常,开始执行此 ISR */
        /* 开始 Top Half 部分的处理,如读取硬件设备的状态以判断发生了何种中断 */
        nw_device_status_read();
    
        /*.. 其他一些数据操作等..*/
    
        /* 释放 nw_bh_sem,发送信号给 demo_nw_thread,准备开始 Bottom Half */
        rt_sem_release(nw_bh_sem);
    
        /* 然后退出中断的 Top Half 部分,结束 device 的 ISR */
    }

     

  • 从上面例子的代码片段可以看出,中断服务程序通过对一个信号量对象的等待和释放,来完成中断 Bottom Half 的起始和终结。由于将中断处理划分为 Top 和 Bottom 两个部分后,使得中断处理过程变为异步过程。这部分系统开销需要用户在使用 RT-Thread 时,必须认真考虑中断服务的处理时间是否大于给 Bottom Half 发送通知并处理的时间。

RT-Thread中断管理接口

  • 为了把操作系统和系统底层的异常、中断硬件隔离开来,RT-Thread 把中断和异常封装为一组抽象接口;

 中断服务程序挂接

  •  系统把用户的中断服务程序 (handler) 和指定的中断号关联起来,可调用如下的接口挂载一个新的中断服务程序:
  • /**
     * 把中断服务程序和指定的中断号关联起来
     *
     * @param vector 是挂载的中断号
     * @param handler 新挂载的中断服务程序
     * @param param 会作为参数传递给中断服务程序
     * @param name 中断的名称
     * @return 挂载这个中断服务程序之前挂载的中断服务程序的句柄
     */
    rt_isr_handler_t rt_hw_interrupt_install(int              vector,
                                             rt_isr_handler_t handler,
                                             void            *param,
                                             const char      *name);

     

  • 这个 API 并不会出现在每一个移植分支中,例如通常 Cortex-M0/M3/M4 的移植分支中就没有这个 API

中断源管理

  •  通常在 ISR 准备处理某个中断信号之前,我们需要先屏蔽该中断源,在 ISR 处理完状态或数据以后,及时的打开之前被屏蔽的中断源,屏蔽中断源可以保证在接下来的处理过程中硬件状态或者数据不会受到干扰;
  • // ector    要屏蔽的中断号
    void rt_hw_interrupt_mask(int vector);

     

  • 这个 API 并不会出现在每一个移植分支中,例如通常 Cortex-M0/M3/M4 的移植分支中就没有这个 API。

  • 为了尽可能的不丢失硬件中断信号,可调用下面的函数接口打开被屏蔽的中断源:
  • //  vector   要打开屏蔽的中断号
    void rt_hw_interrupt_umask(int vector);

     

  • 这个 API 并不会出现在每一个移植分支中,例如通常 Cortex-M0/M3/M4 的移植分支中就没有这个 API。

全局中断开关

  •  局中断开关也称为中断锁,是禁止多线程访问临界区最简单的一种方式,即通过关闭中断的方式,来保证当前线程不会被其他事件打断(因为整个系统已经不再响应那些可以触发线程重新调度的外部事件),也就是当前线程不会被抢占,除非这个线程主动放弃了处理器控制权。当需要关闭整个系统的中断时,可调用下面的函数接口:
  • rt_base_t rt_hw_interrupt_disable(void);

     

  • 恢复中断也称开中断。rt_hw_interrupt_enable()这个函数用于 “使能” 中断,它恢复了调用 rt_hw_interrupt_disable()函数前的中断状态。

  • void rt_hw_interrupt_enable(rt_base_t level);

     

  • 使用中断锁来操作临界区的方法可以应用于任何场合,且其他几类同步方式都是依赖于中断锁而实现的,可以说中断锁是最强大的和最高效的同步方法。只是使用中断锁最主要的问题在于,在中断关闭期间系统将不再响应任何中断,也就不能响应外部的事件。所以中断锁对系统的实时性影响非常巨大,当使用不当的时候会导致系统完全无实时性可言;而使用得当,则会变成一种快速、高效的同步方式。

  • 例如,为了保证一行代码(例如赋值)的互斥运行,最快速的方法是使用中断锁而不是信号量或互斥量:
  •   /* 关闭中断 */
        level = rt_hw_interrupt_disable();
        a = a + value;
        /* 恢复中断 */
        rt_hw_interrupt_enable(level);

     

  • 在使用中断锁时,需要确保关闭中断的时间非常短,例如上面代码中的 a = a + value; 也可换成另外一种方式,例如使用信号量:.

  • rt_sem_take 、rt_sem_release 的实现中,已经存在使用中断锁保护信号量内部变量的行为,所以对于简单如 a = a + value; 的操作,使用中断锁将更为简洁快速。
  •     /* 获得信号量锁 */
        rt_sem_take(sem_lock, RT_WAITING_FOREVER);
        a = a + value;
        /* 释放信号量锁 */
        rt_sem_release(sem_lock);

     

  • 函数 rt_base_t rt_hw_interrupt_disable(void) 和函数 void rt_hw_interrupt_enable(rt_base_t level) 一般需要配对使用,从而保证正确的中断状态。

  • 在 RT-Thread 中,开关全局中断的 API 支持多级嵌套使用:
  • #include <rthw.h>
    
    void global_interrupt_demo(void)
    {
        rt_base_t level0;
        rt_base_t level1;
    
        /* 第一次关闭全局中断,关闭之前的全局中断状态可能是打开的,也可能是关闭的 */
        level0 = rt_hw_interrupt_disable();
        /* 第二次关闭全局中断,关闭之前的全局中断是关闭的,关闭之后全局中断还是关闭的 */
        level1 = rt_hw_interrupt_disable();
    
        do_something();
    
        /* 恢复全局中断到第二次关闭之前的状态,所以本次 enable 之后全局中断还是关闭的 */
        rt_hw_interrupt_enable(level1);
        /* 恢复全局中断到第一次关闭之前的状态,这时候的全局中断状态可能是打开的,也可能是关闭的 */
        rt_hw_interrupt_enable(level0);
    }

中断通知

  •  当整个系统被中断打断,进入中断处理函数时,需要通知内核当前已经进入到中断状态。可使用接口:
  • void rt_interrupt_enter(void);
    void rt_interrupt_leave(void);

     

  • 每当进入中断时,可以调用 rt_interrupt_enter() 函数,用于通知内核,当前已经进入了中断状态,并增加中断嵌套深度(执行 rt_interrupt_nest++);

  • 当退出中断时,可以调用 rt_interrupt_leave() 函数,用于通知内核,当前已经离开了中断状态,并减少中断嵌套深度(执行 rt_interrupt_nest --)。注意不要在应用程序中调用这两个接口函数;

 

  • 使用 rt_interrupt_enter/leave() 的作用是,在中断服务程序中,如果调用了内核相关的函数(如释放信号量等操作),则可以通过判断当前中断状态,让内核及时调整相应的行为。例如:在中断中释放了一个信号量,唤醒了某线程,但通过判断发现当前系统处于中断上下文环境中,那么在进行线程切换时应该采取中断中线程切换的策略,而不是立即进行切换。
  • 在上层应用中,在内核需要知道当前已经进入到中断状态或当前嵌套的中断深度时,可调用 rt_interrupt_get_nest() 接口:
  • /**
     * This function will return the nest of interrupt.
     *
     * User application can invoke this function to get whether current
     * context is interrupt context.
     *
     * @return the number of nested interrupts.
     */
    rt_uint8_t rt_interrupt_get_nest(void)
    {
        return rt_interrupt_nest;
    }

中断与轮训

  •  当驱动外设工作时,其编程模式到底采用中断模式触发还是轮询模式触发往往是驱动开发人员首先要考虑的问题,并且这个问题在实时操作系统与分时操作系统中差异还非常大。因为轮询模式本身采用顺序执行的方式:查询到相应的事件然后进行对应的处理。所以轮询模式从实现上来说,相对简单清晰。
  • 例如往串口中写入数据,仅当串口控制器写完一个数据时,程序代码才写入下一个数据(否则这个数据丢弃掉)。相应的代码可以是这样的:
  • /* 轮询模式向串口写入数据 */
        while (size)
        {
            /* 判断 UART 外设中数据是否发送完毕 */
            while (!(uart->uart_device->SR & USART_FLAG_TXE));
            /* 当所有数据发送完毕后,才发送下一个数据 */
            uart->uart_device->DR = (*ptr & 0x1FF);
    
            ++ptr; --size;
        }
  • 通常情况下,实时系统中更多采用的是中断模式来驱动外设。当数据达到时,由中断唤醒相关的处理线程,再继续进行后续的动作。例如一些携带 FIFO(包含一定数据量的先进先出队列)的串口外设,其写入过程可以是这样的,如下图所示:

  • 线程先向串口的 FIFO 中写入数据,当 FIFO 满时,线程主动挂起。串口控制器持续地从 FIFO 中取出数据并以配置的波特率(例如 115200bps)发送出去。当 FIFO 中所有数据都发送完成时,将向处理器触发一个中断;当中断服务程序得到执行时,可以唤醒这个线程。这里举例的是 FIFO 类型的设备,在现实中也有 DMA 类型的设备,原理类似。

 

  • 发送数据量越小,发送速度越快,对于数据吞吐量的影响也将越大。归根结底,取决于系统中产生中断的频度如何。当一个实时系统想要提升数据吞吐量时,可以考虑的几种方式:.
    1. 增加每次数据量发送的长度,每次尽量让外设尽量多地发送数据;

    2. 必要情况下更改中断模式为轮询模式。同时为了解决轮询方式一直抢占处理机,其他低优先级线程得不到运行的情况,可以把轮询线程的优先级适当降低。

全局终端开关使用示例

  •  在多线程访问同一个变量时,使用开关全局中断对该变量进行保护:
  • #include <rthw.h>
    #include <rtthread.h>
    
    #define THREAD_PRIORITY      20
    #define THREAD_STACK_SIZE    512
    #define THREAD_TIMESLICE     5
    
    /* 同时访问的全局变量 */
    static rt_uint32_t cnt;
    void thread_entry(void *parameter)
    {
        rt_uint32_t no;
        rt_uint32_t level;
    
        no = (rt_uint32_t) parameter;
        while (1)
        {
            /* 关闭全局中断 */
            level = rt_hw_interrupt_disable();
            cnt += no;
            /* 恢复全局中断 */
            rt_hw_interrupt_enable(level);
    
            rt_kprintf("protect thread[%d]'s counter is %d\n", no, cnt);
            rt_thread_mdelay(no * 10);
        }
    }
    
    /* 用户应用程序入口 */
    int interrupt_sample(void)
    {
        rt_thread_t thread;
    
        /* 创建 t1 线程 */
        thread = rt_thread_create("thread1", thread_entry, (void *)10,
                                  THREAD_STACK_SIZE,
                                  THREAD_PRIORITY, THREAD_TIMESLICE);
        if (thread != RT_NULL)
            rt_thread_startup(thread);
    
    
        /* 创建 t2 线程 */
        thread = rt_thread_create("thread2", thread_entry, (void *)20,
                                  THREAD_STACK_SIZE,
                                  THREAD_PRIORITY, THREAD_TIMESLICE);
        if (thread != RT_NULL)
            rt_thread_startup(thread);
    
        return 0;
    }
    
    /* 导出到 msh 命令列表中 */
    MSH_CMD_EXPORT(interrupt_sample, interrupt sample);

    运行结果:

  •  \ | /
    - RT -     Thread Operating System
     / | \     3.1.0 build Aug 27 2018
     2006 - 2018 Copyright by rt-thread team
    msh >interrupt_sample
    msh >protect thread[10]'s counter is 10
    protect thread[20]'s counter is 30
    protect thread[10]'s counter is 40
    protect thread[20]'s counter is 60
    protect thread[10]'s counter is 70
    protect thread[10]'s counter is 80
    protect thread[20]'s counter is 100
    protect thread[10]'s counter is 110
    protect thread[10]'s counter is 120
    protect thread[20]'s counter is 140

     

  • 由于关闭全局中断会导致整个系统不能响应中断,所以在使用关闭全局中断做为互斥访问临界区的手段时,必须需要保证关闭全局中断的时间非常短,例如运行数条机器指令的时间。

参考

  • 《RT-Thread 编程指南》

 

转载于:https://www.cnblogs.com/icefree/p/10828648.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RT-Thread是一个开源的嵌入式实时操作系统,支持多种不同的处理器架构。nRF52840是一款由Nordic Semiconductor开发的低功耗蓝牙微控制器芯片。 RT-Thread nRF52840是指RT-Thread在nRF52840芯片上的移植和运行。nRF52840具有强大的处理能力和低功耗特性,适用于物联网和无线通信应用。RT-Thread在nRF52840上的移植意味着RT-Thread的实时操作系统可以在该芯片上运行,并能够充分发挥其性能和功能。 通过将RT-Thread移植到nRF52840芯片上,可以实现以下功能: 1. 实时操作系统支持:RT-Thread是一个实时操作系统,具有任务调度、中断处理和内存管理等功能,可以实现快速响应和实时的系统行为。 2. 多任务支持:RT-Thread支持多任务并发执行,可以同时运行多个任务,并且可以根据优先级和时间片轮转算法来进行任务调度。 3. 低功耗支持:nRF52840芯片具有低功耗特性,结合RT-Thread的低功耗管理功能,可以实现优化的能源管理和延长设备的电池寿命。 4. 物联网支持:nRF52840芯片支持蓝牙低功耗(BLE)和IEEE 802.15.4等无线通信协议,与RT-Thread的网络协议栈配合使用,可以实现智能家居、传感器网络和物联网设备等应用。 5. 外设驱动支持:nRF52840芯片具有丰富的外设接口,如UART、SPI、I2C和GPIO等,RT-Thread提供了相应的驱动程序和中间件,可以方便地使用这些外设接口。 总之,RT-Thread在nRF52840芯片上的移植使得开发者可以利用RT-Thread的丰富功能来开发基于该芯片的嵌入式应用程序,实现实时、低功耗和物联网支持的应用场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值