LV04-06-中断-05-IMX6ULL按键中断实例
本文主要是中断——IMX6ULL按键中断的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
Windows版本 | windows11 |
Ubuntu版本 | Ubuntu16.04的64位版本 |
VMware® Workstation 16 Pro | 16.2.3 build-19376536 |
终端软件 | MobaXterm(Professional Edition v23.0 Build 5042 (license)) |
Linux开发板 | 正点原子 i.MX6ULL Linux 阿尔法开发板 |
uboot | NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03) |
linux内核 | linux-4.15(NXP官方提供) |
Win32DiskImager | Win32DiskImager v1.0 |
点击查看本文参考资料
分类 | 网址 | 说明 |
官方网站 | https://www.arm.com/ | ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档 |
https://www.nxp.com.cn/ | NXP官方网站 | |
https://www.nxpic.org.cn/ | NXP 官方社区 | |
https://u-boot.readthedocs.io/en/latest/ | u-boot官网 | |
https://www.kernel.org/ | linux内核官网 |
点击查看相关文件下载
分类 | 网址 | 说明 |
NXP | https://github.com/nxp-imx | NXP imx开发资源GitHub组织,里边会有u-boot和linux内核的仓库 |
https://elixir.bootlin.com/linux/latest/source | 在线阅读linux kernel源码 | |
nxp-imx/linux-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga | NXP linux内核仓库tags中的rel_imx_4.1.15_2.1.0_ga | |
nxp-imx/uboot-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga | NXP u-boot仓库tags中的rel_imx_4.1.15_2.1.0_ga | |
I.MX6ULL | i.MX 6ULL Applications Processors for Industrial Products | I.MX6ULL 芯片手册(datasheet,可以在线查看) |
i.MX 6ULL Applications ProcessorReference Manual | I.MX6ULL 参考手册(下载后才能查看,需要登录NXP官网) | |
ARM | Cortex-A7 MPCore Technical Reference Manual | Cortex-A7 MPCore技术参考手册 |
ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition | ARM架构参考手册ARMv7-A和ARMv7-R版 | |
Arm Generic Interrupt Controller Architecture Specification- version 3 and version 4 | Arm通用中断控制器架构规范-版本3和版本4 | |
ARM Generic Interrupt Controller Architecture Specification - Version 2.0 | Arm通用中断控制器架构规范-版本2.0 | |
ARM Cortex-A Series Programmer's Guide for ARMv7-A | Cortex-A系列ARMv7-A编程指南 |
一、硬件连接
我们用到的就是ALPHA开发板上的按键,电路原理图如下:

按键 KEY0 是连接到 I.MX6U 的 UART1_CTS 这个 IO 上的 ,我们查一下这个IO是哪个,这里其实我没看懂,但是根据教程描述,这个引脚应该是这个:

这个引脚的默认功能是UART1_CTS_B,但是可以复用为GPIO1_IO18,这里就相当于接在了GPIO1_IO18上边。
二、中断号怎么确定?
接下来就是怎么确认中断号?我们前边知道了按键接在了GPIO1_IO18上边,我们可以查看《I.MX6UL参考手册》的3.2 Cortex A7 interrupts一节,找到这个GPIO管脚对应的中断号:

可以看到GPIO1的0-15管脚使用的是66,16-31使用的是67,这里只是IRQ的编号,对应到 GIC 的 SPI中断号需要在此编号基础上加上 32,所以这里的按键中断号实际为99(67+32)。这个其实在我们之前移植的SDK包里边就有定义,我们打开 MCIMX6Y2.h 文件,有如下内容:

这里其实已经为我们定义好了中断号的枚举类型。上边我们知道多个GPIO引脚都会产生这个中断号,所以我们需要注意:当发生 GIC 99 号中断时,表示发生了 GPIO1 中 interrupt 0~15,需要进一步细分出是 GPIO1 里的哪一个中断。
三、软件设计
1. start.s
完整的可以看这里:project/start.S · qidaink/imx6ull-bare-prj - 码云 - 开源中国 (gitee.com),这里只写一部分重要的内容:
1 | /* IRQ中断!重点!!!!! */ |
IRQ 中断服务函数主要的工作就是区分当前发生的什么中断,也就是获取发生中断的中断号。然后针对不同的外部中断做出不同的处理。
1.1 保存现场
1 | push {lr} /* 保存lr地址 */ |
1.2 获取当前中断号
1 | mrc p15, 4, r1, c15, c0, 0 /* 从CP15的C0寄存器内的值到R1寄存器中 |
经过这一步,中断号被保存到了 r0 寄存器中。
1.3 调用中断处理函数
1 | push {r0, r1} /* 保存r0,r1 */ |
这里先讲R0和R1中的数据入栈,随后调用了函数 system_irqhandler,函数 system_irqhandler 是一个 C 语言函数,此函数有一个参数,这个参数就是中断号,所以我们需要传递一个参数。汇编中调用 C 函数如何实现参数传递呢?根据 ATPCS(ARM-Thumb Procedure Call Standard)定义的函数参数传递规则,在汇编调用 C 函数的时候建议形参不要超过 4 个, 形参可以由 r0~r3 这四个寄存器来传递,如果形参大于 4 个, 那么大于 4 个的部分要使用堆栈进行传递。 所以给 r0 寄存器写入中断号就可以了函数 system_irqhandler 的参数传递,前边已经向 r0 寄存器写入了中断号了。中断的真正处理过程其实是在函数 system_irqhandler 中完成,稍后需要编写函数 system_irqhandler。
1.4 中断处理完成
1 | pop {lr} /* 执行完C语言中断服务函数,lr出栈 */ |
最后向 GICC_EOIR 寄存器写入刚刚处理完成的中断号, 当一个中断处理完成以后必须向 GICC_EOIR 寄存器写入其中断号表示中断处理完成。
1.5 恢复现场
1 | pop {r0} |
1.6 跳回原来的地方运行
1 | subs pc, lr, #4 /* 将lr-4赋给pc */ |
中断处理完成以后就要重新返回到曾经被中断打断的地方运行,这里为什么要将lr-4 然后赋给 pc 呢?而不是直接将 lr 赋值给 pc? ARM 的指令是三级流水线:取指、译指、执行, pc 指向的是正在取值的地址,这就是很多书上说的 pc=当前执行指令地址+8。比如下面代码示例:
1 | 0X2000 MOV R1, R0 ;执行 |
上面示例代码中,左侧一列是地址,中间是指令,最右边是流水线。当前正在执行 0X2000地址处的指令“MOV R1, R0”,但是 PC 里面已经保存了 0X2008 地址处的指令“MOV R4, R5”。假设此时发生了中断,中断发生的时候保存在 lr 中的是 pc 的值,也就是地址 0X2008。当中断处理完成以后肯定需要回到被中断点接着执行,如果直接跳转到 lr 里面保存的地址处(0X2008)开始运行,那么就有一个指令没有执行,那就是地址 0X2004 处的指令“MOV R2, R3”,显然这是一个很严重的错误!所以就需要将 lr-4 赋值给 pc,也就是 pc=0X2004,从指令“MOV R2,R3”开始执行。
2. 通用中断驱动文件
bsp/interrupt/bsp_interrupt.c · qidaink/imx6ull-bare-prj - 码云 - 开源中国 (gitee.com)
bsp/interrupt/bsp_interrupt.h · qidaink/imx6ull-bare-prj - 码云 - 开源中国 (gitee.com)
3. GPIO驱动文件
- bsp/gpio/bsp_gpio.c · qidaink/imx6ull-bare-prj - 码云 - 开源中国 (gitee.com)
- bsp/gpio/bsp_gpio.h · qidaink/imx6ull-bare-prj - 码云 - 开源中国 (gitee.com)
4. 按键中断驱动文件
- bsp/exit/bsp_exit.c · qidaink/imx6ull-bare-prj - 码云 - 开源中国 (gitee.com)
- bsp/exit/bsp_exit.h · qidaink/imx6ull-bare-prj - 码云 - 开源中国 (gitee.com)
5. 完整代码
可以看这里:feat:GPIO中断实验 · 1a0fdf1 · qidaink/imx6ull-bare-prj - Gitee.com