ARM Cortex-M内核软件中断触发方式
1. 背景
在嵌入式开发过程中,如果不是参与操作系统调度开发的,其实应该是较少使用软件中断的,或者在常规的驱动开发过程中,应该对使用软件中断应该是感觉到意外,因为明明可以直接执行相应的程序,为什么还要使用中断去执行。在正常的嵌入式开发过程中,确实很难会接触软件中断这一概念,更多的可能是用计时器来进行定时触发一些动作,或者也有可能借助外部中断软件触发的方式来做软中断,那本篇博客就简单探讨一下软件中断的作用和在STM32中有那些实现方式。
2. 软件中断之于操作系统
对于操作系统而言,最为核心的部分当然就是支持并发性,通过对task的调度,分时复用CPU资源来实现并发,操作系统将在Systick中断中发起对task的调度,但是并非是Systick一到,就应该立刻发起调度,因为这时如果,当前正处于cpu内核态,调度完线程,就会返回用户态,所以操作系统中,systick中是向一个低优先级的软件中断挂起一个请求,等到cpu回到用户态时,就会立刻进行task调度。操作系统中的软件中断,或者也可以称之为系统服务调用。比如8086汇编中的int 0x80,arm7中的swi,这里的swi其实就是software interrupt,arm7的swi和svc的地位其实是相同的,而且机器码也相同。这里的软件中断是用户为了向系统提出操作对应的硬件请求,而硬件的操作部分是位于系统内核态,所以用户就可以方便再用户态,调用所需要的硬件功能,而不必受限于当前的cpu权限,操作系统内核也无需担心用户误操作而引起系统瘫痪。
3. Cortex-M的软件中断
在Cortex-M系列中svc和swi不再一样,svc的被调用后,就需要cpu立刻执行,如果svc不能得到相应就会产生一个hardfault,所以像在svc中或者nmi中调用svc都会产生hardfault。swi,是一个和其他外设中断一样的地位,我们可以通过一些空闲的中断号或者是PendSV中断来是实现软件中断。
当然也有一些其他的软件中断实现方式,常见的软件触发中断方式如下。
-
定时器方式触发,在STM32开发中,我们可以开启一个定时器,计时的count可以设置为0,也就是会立刻触发更新中断,然后在中断中关闭这个定时器,通过这种方式,我们也可以实现软件中断。
-
外部中断软件触发,外部中断在配置和初始化后,也是可以手动触发的,在HAL库中,就有对应的接口函数可以做到,当然EXTI中也有寄存器SWIER可软件触发。
-
在空闲中断号中设置软件中断,在Cortex-M系列内核中中断资源号应该有256,除去内核占用的16个中断号,按理应该还有240个中断资源号,但是并不是所有的芯片都实现这240个中断,在stm32f103c8t6中,仅有0-42中断号可用,43以上的中断号,并未实现在这mcu中。如果有空闲的中断号,或者不使用的外设中断号,我们可用将之作为软件中断,像普通中断一样,把这个中断设置优先级和使能中断后,我们可用通过向NVIC->STIR中写入中断号,来主动触发软件中断。
-
使用SVC或者PendSV
- 在操作系统中,SVC和PendSV往往是被操作系统给占用,用于调度task,所以不推荐在有操作系统的项目中使用这两个中断资源
- 在裸机开发中,这两个中断资源往往是闲置的,非常推荐使用这两个中断资源,使用SVC中断,相当于使用临界区,而且通过汇编语句对CPU的合理设置我们可以将系统调用函数做出有返回值和入参的函数,这就使得SVC的可用性大大加强,PendSV,同样也是Cortex内核的保留中断号,但是他的优先级可自行设置,地位相当于普通软件中断,但是相比于软件中断,PendSV的中断号是固定的,这在项目之间进行移植带来了许多便利。
-
普通外设中断手动触发,其实也是可以通过NVIC->STIR来模拟外设触发中断,执行相应的中断程序。
4. 总结
在通用操作系统中,软件中断作为用户态和内核态的桥梁,是用户调用系统服务的接口。而在嵌入式领域,在有操作系统的项目中软件中断的使用较少,由于有多线程并发操作,和互斥锁及信号量的使用,使得对于并发和全局资源的互斥访问,有更为方便的途径。在裸机项目开发中,软件中断的使用,可以看作中断并发的延申,以及中断之间以及主任务调配资源。这使得软件中断的使用有了一些其他的意义,我们甚至可以参照操作系统的方式使用软件中断,来调配系统任务,实现一个伪操作系统的概念。