嵌入式Linux(七)GPIO中断

1. STM32的中断回顾

1.1 中断向量表

  我们写的中断服务函数,在中断触发时会调用,实现这个调用就需要中断向量表。中断向量表保存的是中断服务程序的入口地址
  STM32代码是下载到0x8000000开始的区域,这就和中断向量表应该在的0x00000000不一样,那么就需要进行中断向量偏移。包括我用的I.MX6ULL,代码连接到的地址是0x87800000,所以也需要进行中断向量偏移。

1.2 NVIC中断控制器

  STM32是Cortex-M内核,中断控制器为NVIC。I.MX6ULL是Cotex-A内核,中断控制器叫做GIC

1.3 使能中断,编写中断服务函数

2. Cortex-A7中断系统

2.1 中断向量表

  A7内核有8个异常中断(实际上只有七个),中断向量表如下,其中主要用到的是IRQ外部中断:
嵌入式Linux(七)GPIO中断_第1张图片
  A7需要自己写中断向量表,另外需要设置中断向量偏移。

2.2 GIC中断控制器

2.2.1 GIC总览

  I.MX6ULL是ARMv7-A架构,使用的是GIC V2版本。GIC接到外部中断信号后会报给ARM内核。原理示意如下图。上报主要有四个信号,主要关注IRQ信号。GIC将众多中断源分为:
  SPI共享中断:所有核心共享的中断,所有外部中断都属于SPI中断,所有的核心都能够处理。
  PPI私有中断:每个核独有的中断,需要指定核心处理。
  SGI软件中断:像寄存器GICD_SGIR写入数据触发,可以用于多核通信。
嵌入式Linux(七)GPIO中断_第2张图片

2.2.2 中断ID

  一个CPU最多支持1020个中断ID,ID0-1019,其中包含了SPI,PPI,SGI。其中ID0-ID15分配给SGI,ID16-ID31分配给PPI,ID32-ID1019分配给SPI。I.MX6ULL一共有128个SPI的中断ID,加上32个SGI/PPI的ID,一共有160个中断ID。在参考手册第3.2节可以查看每个中断ID对应的中断源和描述。

2.2.3 中断服务函数

  主要包含IRQ中断服务函数(汇编文件里面)和IRQ中断服务函数里查找并运行的外设中断服务函数(bsp里面)

3. 按键外部中断实验

  KEY0使用UART1_CTS这个IO。

3.1 修改start.s

3.1.1 复位中断服务函数

  ①在_start后面添加中断向量表
  ②编写复位中断服务函数:1)关闭I & D cache和MMU;2)设置处理器9种工作模式下对应的sp指针;3)清除bss段;4)跳到main.c。
  ③CP15协处理器。主要包含MRC和MCR指令,MRC将cp15的寄存器数据读入到ARM寄存器,MCR相反。MCR指令格式为:

MCR{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2>
/*
cond为条件码,忽略表示无条件执行
opc1为协处理器要执行的操作码
Rt为ARM源寄存器
CRn为cp15的目标寄存器
CRm为协处理器种附加的目标寄存器或者源操作数寄存器,不需要就设为0
opc2为可选操作码,不要就设为0
*/

/*MRC指令格式一样,不过Rt是目标寄存器,CRn为源寄存器。*/

  1)关闭I,D Cache和MMU:CP15中c1寄存器设置CRn=c1,opc1=0,CRm=c0,opc2=0,这时候c1就被选为SCTLR寄存器(系统控制寄存器)。该寄存器bit12为I Cache使能位,bit11为分支预测,bit2为D Cache使能位,bit0为MMU使能位。
在这里插入图片描述
  2)中断向量偏移:c12寄存器CRn=c12,opc1=0,CRm=c0,opc2=0时为VBAR寄存器(向量表基地址寄存器)。由于代码链接起始地址为0x87800000,所以需要将该地址设置为VBAR也就是中断向量表起始地址。

3.1.2 IRQ_Handler中断服务函数

  首先需要了解GIC架构分为Distributor(分发器端-0x1000-0x1FFF),CPU Interface(CPU接口端-0x2000-0x3FFF)。在得到GIC基地址之后就可以根据这些偏移量访问GIC的各个寄存器。
  cp15协处理器的c15寄存器,可以作为CBAR寄存器,通过如下命令获得GIC基地址:

MRC p15, 4, r1, c15, c0, 0

  然后调用c语言函数system_irqhandler,处理中断。处理完成后将GICC_IAR的值写入GICC_EOIR寄存器,表示中断完成。

关于为什么中断完成后要将lr-4赋给pc:
嵌入式Linux(七)GPIO中断_第3张图片

3.2 编写通用中断驱动

3.2.1 通用中断处理流程

  前面IRQ_Handler调用了system_irqhandler函数,所以需要进行编写。I.MX6ULL的160个中断源需要160个中断处理函数,这里建立一个数组,将中断处理函数作为数组元素,中断ID作为数组下标,由system_irqhandler进行调用。进行int.c和int.h的编写,int.h如下。
嵌入式Linux(七)GPIO中断_第4张图片

3.2.2 修改通用GPIO驱动

  ①首先要设置GPIO的中断触发方式,上升沿,下降沿,高低电平,边沿触发5种。由GPIO_ICR1或ICR2寄存器设置。对于KEY0触发的中断,由于按键按下时由1变0,所以需要设置为下降沿触发。
  ②使能GPIO对应的中断,设置GPIO_IMR寄存器,为1使能。
  ③处理完中断后需要清除中断标志位,也就是清除GPIO_ISR寄存器相应的位,该寄存器写1清零。

3.2.3 按键中断驱动编写

调用通用GPIO驱动。
GIC配置:
  ①使能相应的中断ID,GPIO_18对应的是67+32。(32是PPI和SGI占用的,在MCIMX6Y2.h中可以查到)
  ②设置中断优先级。
  ③注册该中断ID对应的中断服务函数。
注意点:确保中断向量表处于起始位置也即是0x87800000
嵌入式Linux(七)GPIO中断_第5张图片
KEY0外部中断控制BEEP:
嵌入式Linux(七)GPIO中断_第6张图片

你可能感兴趣的:(嵌入式,linux,驱动开发)