一、中断的概念
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);