首页 理论教育 ARM嵌入式C语言编程技巧:掌握C中断技术

ARM嵌入式C语言编程技巧:掌握C中断技术

时间:2023-10-19 理论教育 版权反馈
【摘要】:在计算机系统中,中断是指异步事件引起的CPU停止当前的执行流而跳转到特定的异步事件的处理程序[通常称为中断服务程序]。与硬件中断不同,软件中断的发生是因为执行了软件中断指令。第一层含义是快速响应,也就是说CPU要尽快地响应中断请求。

ARM嵌入式C语言编程技巧:掌握C中断技术

在计算机系统中,中断是指异步事件引起的CPU停止当前的执行流而跳转到特定的异步事件的处理程序[通常称为中断服务程序(ISR)]。从中断产生的原因看,可以将中断分为硬件中断、软件中断和异常3种。

(1)硬件中断(Hardware Interrupt)是指CPU以外的硬件设备引起的异步事件,比如键盘产生击键、串口接收到新数据等。需要说明的是,现在大多数嵌入式处理器中往往集成了多个外围设备(比如定时器、串口控制器、DMA控制器等),这些片内设备产生的中断称为内部硬件中断;为了适应应用的需要,这些嵌入式处理器往往会在芯片外部提供一些可以接收片外设备中断请求的中断线。比如如果需要在片外扩展一个USB Host控制器,该控制器就需要使用一根外部中断线以通知CPU USB Host的中断请求,这些中断称为外部硬件中断。

(2)软件中断(Software Interrupt)又叫软陷。与硬件中断不同,软件中断的发生是因为执行了软件中断指令。如80x86的int指令、68000的trap指令、ARM的SWI指令。软件中断指令一般用于操作系统的系统调用入口。

(3)异常(Exception)是指CPU内部在运行过程中引起的事件,比如指令预取中止、数据预取中止、未定义指令等,异常发生后一般由操作系统接管。

虽然中断产生的原因不同,但是中断响应的过程基本上是相同的,而且大多数处理器在响应中断时的操作也基本相同,比如在响应中断时硬件会自动关中断,以防中断响应时发生中断嵌套,如果程序员希望能够支持中断嵌套就必须在中断服务程序中显示地打开中断;又比如几乎所有的处理器在响应中断时,处理器硬件都要保存返回地址和当前的程序状态字,有些处理器将这些内容直接压栈,有些处理器将这些内容保存在相关的寄存器中。

中断的处理过程一般由硬件、软件两部分共同完成。由硬件(以ARM处理器为例)实现的部分有:

(1)复制CPSR到SPSR_<mode>,此处的SPSR_<mode>指的是所进入的异常模式。

(2)设置正确的CPSR位。

(3)切换到ARM状态。

(4)切换到异常模式,禁止中断。

(5)将返回地址保存在LR_<mode>,设置PC到异常向量地址,此处的LR_<mode>指的是所进入的异常模式。

由软件(中断服务程序)实现的部分有:

(1)把SPCR和LR(保存了PC的值)压栈。

(2)保存中断服务程序中使用的寄存器到堆栈中。

(3)用户服务程序可以打开中断,以接受中断嵌套。

(4)中断服务程序处理完中断后,从堆栈中恢复保存的寄存器。

(5)从堆栈中弹出SPSR和PC,从而恢复原来的执行流程。

1.C语言中的中断处理

标准C语言中不包含中断,许多编译开发商在标准C语言中增加了对中断的支持,提供新的关键字用于标示中断服务程序,类似于__interrupt、#program interrupt,在ARM编译器中也增加了__irq这个关键字。当一个函数被定义为ISR时,编译器会自动为该函数增加中断服务程序所需要的中断现场入栈和出栈代码(最主要是程序状态字PSR入栈和出栈,这一点和普通的函数调用不同)。

为了便于使用高级语言直接编写异常处理函数,ARM编译器对此作了特定的扩展,可以使用函数声明关键字__irq,编译出来的函数就可满足异常响应对现场保护和恢复的需要,并且自动对LR进行减4的处理,符合IRQ和FIQ中断处理要求。编译器在处理用__irq关键字声明的函数时将:①保存ATPCS规定的被破坏的寄存器;②保存其他中断服务程序中用到的寄存器;③将(LR-4)赋予PC,实现中断服务程序的返回,并且恢复CPSR的内容。

示例代码如下:

2.中断处理的一般原则

中断处理的本质特征是它的异步性,中断可以在任何时候发生,而CPU以及操作系统必须能够在最短的时间内响应。总的来说,中断处理的基本原则只有两点:快速、保护。(www.xing528.com)

“快速”包括两层含义。第一层含义是快速响应,也就是说CPU要尽快地响应中断请求。对于外部中断,通常情况下CPU在执行完当前指令后会对中断信号进行采样,如果有中断请求且允许中断,CPU将进入中断响应。但是依然有下列几种情况可能造成中断响应的延迟甚至丢失:

(1)虽然现在RISC处理器的大多数指令可以在一个周期内完成,但是依然存在一些特殊的指令必须在多个周期内才能完成,而在这些指令运行期间CPU是不接受中断请求的。比如ARM指令集中的LDM和STM两类指令,这些指令是多装载和多存储指令,它们的执行时间取决于程序员希望通过一条指令保存多少数据,在最坏的情况下可能需要十几个周期才能完成。

(2)几乎所有的处理器在响应中断期间是关中断的,也就是说当CPU响应某个中断请求时,硬件会自动地将程序状态字中的中断使能位清除(ARM处理器刚好相反,在响应中断时硬件会自动在CPSR中设置一位禁止中断位)。

(3)对于一些重要的全局变量或者全局数据结构以及其他临界资源的访问,必须采取相应的保护措施。对于无操作系统的系统,一般采用关中断的方法实现对临界资源的互斥访问;对于有操作系统的系统,可以有多种方法实现临界资源的互斥,比如采用信号量、关闭调度器以及关中断。总之,在软件系统中往往需要通过关中断的方法来实现对临界资源的互斥保护,在这种情况下会造成中断响应的延迟。

针对上面分析的原因,为了能够加快中断的响应速度,可以采取的措施如下:

(1)尽量避免在程序中直接使用关中断的方法。

(2)正如前面分析的,中断服务程序在默认情况下是不支持嵌套的,因此要加快中断响应就必须加快中断处理的速度。为了加快中断处理的速度,程序员应该注意:

①中断服务程序只处理最基本的硬件操作,其他的处理内容可以设法放在中断服务程序之外完成。

②中断服务程序中应该避免调用耗时的函数,比如printf(char∗lpFormat String,…)函数(在ARM平台上由于半主机机制,该函数的速度更慢)。

浮点运算由于性能和可能存在的重入问题以及其他的耗时操作都不应该在中断服务程序中使用。

④在有操作系统的情况下,要非常小心那些有可能引起挂起的系统调用。

⑤由于函数调用本身的压栈和退栈开销以及可能存在的函数重入风险,在中断服务程序中应该尽可能避免不必要的函数调用。

上面讨论了中断处理的第一个原则“快速”,接下来讨论第二个原则“保护”。因为中断随时都会发生,因此对于全局数据结构和其他临界资源需要进行必要的互斥保护。当然,如果中断服务程序不访问这些全局数据结构和临界资源,就可以不要这些额外的保护。

(1)没有操作系统情况下的中断处理。

对于没有操作系统的应用,由于不存在多个任务的并发执行,中断服务程序需要考虑的因素比较单纯。中断服务程序不需要通知内核中断的发生,也不需要在中断处理结束前调用内核提供的调度器,另外由于没有多任务的并发运行,因此中断引起的函数重入问题以及其他关于临界资源的互斥保护问题等都不需要考虑。需要做的就是:确保中断处理尽快完成,以及确保没有在中断服务程序中调用不可重入函数以及其他临界资源。

(2)有操作系统情况下的中断处理。

在有操作系统作为底层软件平台的系统中中断处理的问题会相对复杂。任何一个嵌入式系统内核都必须完成3项最基本的工作:

(1)任务管理——负责多任务的环境维护、任务调度等;

(2)任务间通信——完成任务间的数据通信、同步与互斥,包括信号量、事件标志、邮箱等;

(3)中断管理将接管系统的所有中断事件,并由内核根据中断事件完成相应的任务切换等工作。

首先与所有的系统一样,操作系统需要构建中断向量表。与x86处理器不同的是,ARM处理器的中断向量表(异常向量表)中存放的不是中断发生后需要跳转的地址,而是在中断向量表中存放发生中断后需要执行的第一条指令。因此,一般来说在ARM的中断向量表中存放的都是跳转指令,这样当中断发生时,CPU将在相应的位置取到该跳转指令,并将程序的执行流程转向真正的中断服务程序。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈