【rtthread内核系列】第二篇:中断

一、中断的概念

1.1 rtthread中断处理过程

rtthread将中断分为三个过程:前导程序、中断服务程序、后续程序。

  • 前导程序:保存cpu中断现场,m3核该部分由硬件完成,关于保存现场的寄存器有 PSR、 PC、 LR、 R12、 R3-R0。之后通知内核处理中断。
  • 中断服务程序:在中断触发后要执行的用户操作。
  • 后续程序:通知内核中断处理完成,恢复cpu现场。

1.2 中断栈

rtthread会在中断的前期处理中切换栈指针到预先留出的中断栈空间,等中断退出时恢复用户的栈指针。

1.3 中断低半部处理

低半部处理用于处理耗时长的操作,举一种使用信号量完成低半部的例子:线程demo_nw_thread阻塞在等待nw_bh_sem,在中断服务程序中释放nw_bh_sem信号让demo_nw_thread线程处理中断的低半部。

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 */
}

二、中断api

要使用中断,首先需要装载一个中断,当中断触发时,就会进入中断处理函数处理。

  • 可以通过rt_hw_interrupt_mask屏蔽指定中断号的中断,避免再次触发的中断对本次中断处理的影响。
  • rtthread提供全局中断的打开和关闭api,这是rtthread线程同步的基础,利用全局中断的打开和关闭可以实现对临界区的保护。
//装载中断
/*
vector:中断号
handle:中断服务程序
param:中断服务程序的参数
name:中断的名称
*/
rt_isr_handler_t rt_hw_interrupt_install(int vector,
                                                rt_isr_handler_t handler,
                                                void *param,
                                                char *name);
         
//屏蔽中断:可避免再次触发的中断对正在处理的中断的影响
/*
vector:中断号
*/
void rt_hw_interrupt_mask(int vector);

//取消屏蔽中断
/*
vector:中断号
*/
void rt_hw_interrupt_umask(int vector);

//关闭全局中断
/*
返回函数执行前的中断状态
*/
rt_base_t rt_hw_interrupt_disable(void);

//打开全局中断,与关闭全局中断配合使用,保护临界区的效率比互斥锁高,对实时性影响较大。
/*
level:中断状态
*/
void rt_hw_interrupt_enable(rt_base_t level);

//中断通知:修改中断深度 rt_interrupt_nest
void rt_interrupt_enter(void);
void rt_interrupt_leave(void);

三、中断与轮询

轮询模式采用顺序执行的方式:查询到相应的事件对其进行处理。这种方式在实时操作系统中存在的问题是低优先级的线程得不到cpu的使用权,所以通常情况下,RTOS使用中断来驱动外设。
对于低速的情况,中断模式非常好,cpu可以在等待数据到来前处理其它任务。但对于高速设备,由于操作系统切换上下文有8us的事件,如果处理一个25us的任务,数据带宽为8/(25+8)=75.8%,采用轮询模式不存在上下文切换的问题,数据带宽为100%。

四、示例

/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-08-24     yangjie      the first version
 */

/* 程序清单:关闭中断进行全局变量的访问 */
#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;

    /* 创建thread1线程 */
    thread = rt_thread_create("thread1", thread_entry, (void *)10,
                              THREAD_STACK_SIZE,
                              THREAD_PRIORITY, THREAD_TIMESLICE);
    if (thread != RT_NULL)
        rt_thread_startup(thread);

    /* 创建thread2线程 */
    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);

猜你喜欢

转载自blog.csdn.net/weixin_43810563/article/details/116721983