良许Linux教程网 干货合集 控制IRQ和FIQ中断的编译器内部函数 – 基于Keil MDK

控制IRQ和FIQ中断的编译器内部函数 – 基于Keil MDK

本文探讨了一些与底层相关的内容。

1.引言

在本文中,我们将探讨编译器内部函数__disable_irq、__enable_irq、__disable_fiq和__enable_fiq,这些函数用于控制IRQ和FIQ中断。

需要注意的是,只有在处理器处于特权模式时才能使用这些内部函数。因为这些函数需要修改CPSR和SPSR寄存器(适用于ARM7、ARM9等)或PRIMASK和FAULTMASK寄存器(适用于Cortex-M3、M4等),而这些寄存器只能在特权模式下访问。

这些内部函数适用于所有架构的处理器,不论是ARM状态还是Thumb状态。对于ARMv6(ARM11)或更新的架构,编译器会用CPS指令来替代这些函数。

如果使用的是ARMv4或者ARMv5架构并且处于ARM状态,编译器会将这些函数用MRS和MSR指令代替。一般情况下ARM7属于ARMv4架构,ARM9属于ARMv5架构。

如果使用的是ARMv4或者ARMv5架构并且处于Thumb状态或编译器使能-compatible参数,则编译器会调用一个辅助函数比如__ARM_disable_irq来控制中断。

2.__enable_fiq使能FIQ中断

通常是通过清除寄存器CPSR中的F位来实现的。

注意FIQ中断一般只存在于ARMv4和ARMv5架构中(即ARM7和ARM9),ARMv6架构的处理器不支持此函数。

对于ARMv7架构的处理器(Cortex-M3),这个函数清除FAULTMASK寄存器的值。

语法:void __enable_fiq(void)

限制:只能在特权级别下使用,用户模式下无效。

3.__disable_fiq禁用FIQ中断

通常是通过置一CPSR的F位来实现的。

注意FIQ中断一般只存在于ARMv4和ARMv5架构中(即ARM7和ARM9),ARMv6架构的处理器不支持此函数。

对于ARMv7架构的处理器(Cortex-M3),这个函数置位FAULTMASK寄存器,这意味着此后只有NMI可以响应,所有其它的异常,包括中断和 Fault都不能响应。

语法:__disable_fiq有两个版本。

一个是返回值为空的void __disable_fiq(void)

另一个返回值为整形值的int __disable_fiq(void)

用法:int __disable_fiq(void),禁止FIQ中断(ARMv4和ARMv5)或禁用除NMI之外的所有中断(ARMv7)。

在禁用中断前,将中断使能状态返回。

void __disable_fiq(void),禁用FIQ中断(ARMv4和ARMv5)或禁用除NMI之外的所有中断(ARMv7)。

限制:只能在特权级别下使用,用户模式下无效。

如果编译器参数设置为-cpu=7,则不支持int __disable_fiq(void)函数,这是因为通用ARMv7架构和ARMv7 R及ARMv7 M-profiles架构的异常处理模式不同所导致的。

这意味着如果编译器参数设置为-cpu=7,编译器不能为int __disable_fiq(void)函数产生所有ARMv7架构通用的指令序列,此时只能使用void __disable_fiq(void)。

举例:

void func(void)
{
  int was_masked = __disable_irq();
  /*其它处理*/
  if(!was_masked)
  {
    __enable_irq();
  }
}

为什么例子中要使用变量was_masked获取之前的中断使能信息,并且在使能中断时还要先判断这个变量?

直接使用__disable_fiq()和__enable_fiq()函数不是更简单吗?

这是因为如果之前系统的中断已经是关闭的,当你直接使用__enable_fiq()函数就会无条件打开中断,这样可能是很危险的。所以在打开中断前,要检查之前中断是不是已经是禁止状态,如果是的话就不要使能中断。

4.__enable_irq使能IRQ中断

对于ARMv4和ARMv5架构(ARM7和ARM9),编译器插入下列指令清除CPSR寄存器的I位。

int disable_irq(void)
{
  return __disable_irq();
}

对于ARMv6(ARM11)和ARMv7(Cortex-M3等)指令,编译器插入下列指令使能中断:

CPSIE  I

比如Cortex-M3架构处理器,该指令清除PRIMASK寄存器,使能中断。

语法:void __enable_irq(void)

限制:只能在特权级别下使用,用户模式下无效。

5. __disable_irq禁止IRQ中断

对于ARMv4和ARMv5架构(ARM7和ARM9),编译器插入下列指令置位CPSR寄存器的I位。

MRS  r0, CPSR
ORR  r0, r0, #0x80
MSR  CPSR_c, r0

对于ARMv6(ARM11)和ARMv7(Cortex-M3等)指令,编译器插入下列指令禁用中断:

CPSID  I

比如Cortex-M3架构处理器,该指令置位PRIMASK寄存器,表示禁止中断和可屏蔽的异常,只剩下NMI和硬Fault可以响应。

__disable_irq函数有两种形式,返回值为空的void __disable_irq(void)和返回值为整形数的int __disable_irq(void)。

前者直接禁用中断,后者在禁用中断前,将中断使能状态返回。

举例:

void func(void)
{
  int was_masked = __disable_irq();
  /*其它处理*/
  if(!was_masked)
  {
    __enable_irq();
  }
}

为什么例子中要使用变量was_masked获取之前的中断使能信息,并且在使能中断时还要先判断这个变量?

直接使用__disable_irq()和__enable_irq()函数不是更简单吗?

这是因为如果之前系统的中断已经是关闭的,当你直接使用__enable_irq()函数就会无条件打开中断,这样可能是很危险的。

所以在打开中断前,要检查之前中断是不是已经是禁止状态,如果是的话就不要使能中断。

限制:只能在特权级别下使用,用户模式下无效。

如果编译器参数设置为-cpu=7,则不支持int __disable_irq(void)函数,这是因为通用ARMv7架构和ARMv7 R及ARMv7 M-profiles架构的异常处理模式不同所导致的。

这意味着如果编译器参数设置为-cpu=7,编译器不能为int __disable_irq(void)函数产生所有ARMv7架构通用的指令序列.

此时只能使用void __disable_irq(void)。

我们再从汇编层面上看一下返回整形数的__disable_irq:

int disable_irq(void)
{
  return __disable_irq();
}

在-cpu=Cortex-M3时,Keil MDK编译器产生的汇编代码为:

MRS    r0, PRIMASK
AND    r0, r0, #1
CPSID  i
BX     lr

6.这些函数有什么用处?

  • 保护共享资源
  • 禁止中断嵌套

保护共享资源很好理解,但禁止中断嵌套可能很多人不理解。

中断嵌套可以提高系统响应时间,为什么要禁用掉?

虽然中断嵌套能提高响应时间,但绝大多数的应用并不需要如此高的响应时间;

更重要的是,中断嵌套增加了程序运行的不确定性。所以我建议在不需要极致的响应时间使,禁止中断嵌套。

方法也很简单,在进入中断服务函数后和退出中断服务函数前中调用本文讲的这些中断控制函数即可。

7.移植性

与编译器特性相关,不具备移植性,建议使用前先用宏进行封装。

以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !

137e00002230ad9f26e78-265x300
本文由 良许Linux教程网 发布,可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
良许

作者: 良许

良许,世界500强企业Linux开发工程师,公众号【良许Linux】的作者,全网拥有超30W粉丝。个人标签:创业者,CSDN学院讲师,副业达人,流量玩家,摄影爱好者。
上一篇
下一篇

发表评论

联系我们

联系我们

公众号:良许Linux

在线咨询: QQ交谈

邮箱: yychuyu@163.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部