嵌入式linux-----ARM裸机(8)-----按键和CPU的中断系统

1.什么是按键
(1)按键的物理特性
一般的按键都有4个引脚,这4个引脚成2对:其中一对是常开触点(不按则断开,按下则闭合);一对是常闭触点(不按时闭合,按下后断开)。一般用常开。
(2)按键的电学原理
硬件接法:SW1:GPH0_2 SW2:GPH0_3 SW3/4/5/6:GPH2_0/1/2/3。按钮没有按下时是高电平,按下时接地为低电平,电阻要选大一点,不然功耗太大了,太小的话电平会不稳。
在这里插入图片描述
在这里插入图片描述

(3)按键属于输入类设备。单纯的输入设备,如按键、触摸屏等;单纯的输出设备,如LCD;既能输入又能输出的,叫输入输出设备(IO),如串口。
(4)两种响应方式:轮询、中断。
2.轮询方式处理按键
以GPH0为例,GPH0CON(0xE020_0C00),GPH0DAT(0xE020_0C04)。GPH0CON中的[2]和[3]都设成输入模式0x0;同理GPH2的0/1/2/3也设为0x0。
在这里插入图片描述

先初始化GPIO模式为input,然后循环读取GPIO的电平值,判断有无按键按下。
此处用了led闪烁的代码为基础,在led.c中添加三种亮灯模式和一个灭灯的函数。
在这里插入图片描述

key.c代码如下,包括初始化和轮询检测函数。
在这里插入图片描述
在这里插入图片描述

Makefile中需加入key.o,把key.bin下面的除了led.o其他led都换成key。
在这里插入图片描述

3.串口输出和按键消抖
以之前的串口stdio的工程为基础来移植添加轮询方式按键处理。由于按键的物理特性影响,按键按下和弹起时,电平的变化不是瞬间完成的的,是一段不稳定的过程,这就叫抖动(抖动期内获取按键信息是不可靠的,要想办法消抖)。
消抖包括硬件消抖和软件消抖。硬件消抖就是减小抖动时间,是通过硬件添加电容等元件来实现的(电容可以平滑波形);软件消抖是发现一次按键按下/弹起后,不立即处理按键,而是延时一段时间(10~20ms,即消抖时间)后再次获取按键键值作为真实值。这里只做了一个按下的消抖。
Makefile里加上key.o。key.c新增部分消抖如下。
在这里插入图片描述

main.c修改如下,进行串口和按键的初始化、按键的轮询查看。
在这里插入图片描述

4.S5PV210的中断体系介绍
(1)中断
单核心CPU在微观角度是串行的,但是因为CPU很快,所以在宏观看来可以并行。因为单核CPU实际无法并行的,但是通过中断机制,可以实现假并行(宏观上的并行,微观上实际还是串行的)。
(2)SoC对中断的实现机制:异常向量表
异常向量表是CPU中某些特定地址的特定定义。如外部中断对应的异常向量地址为0x30000008,则发生外部中断后,CPU会硬件自动跳转到0x30000008地址去执行指令,软件需要做的就是把处理这个异常的代码的首地址填入这个异常向量地址。
异常向量表中各个向量的相对位置是固定的,但是他们的起始地址是不固定的,各种SoC可以不一样,而且复杂ARM中还可以让用户来软件设置这个异常向量表的基地址。扩展到所有架构的CPU中:所有架构(如51单片机、PIC单片机)的CPU实现中断都是通过异常向量表实现的,这个机制是不变的;但是不同CPU异常向量表的构造和位置是不同的。
下图为各种异常。
在这里插入图片描述

(3)异常和中断的区别和联系
针对SoC来说,发生复位、软中断、中断、快速中断、取指令异常、数据异常等,我们都统一叫异常。所以说:中断其实是异常的一种。
异常的定义就是突发事件,打断了CPU的正常常规业务,CPU不得不跳转到异常向量表中去执行异常处理程序;中断是异常的一种,一般特指SoC内的内部外设产生的打断SoC常规业务,或者外部中断(SoC的GPIO引脚传回来的中断)。
5.异常向量表的编程处理(3.key_interrupt_stdio)
(1)像内存一样去访问异常向量表
S5PV210的异常向量表可以改变(在CP15协处理器中),以适应操作系统的需求。但是目前系统刚启动时,此时DRAM尚未初始化,程序都在SRAM中运行。210在iRAM中设置了异常向量表,供暂时性使用。查210的iROM application note文档中iRAM的地址分配,可知,iRAM中的异常向量表起始地址为0xD0037400。知道了异常向量表的起始地址后,各个异常对应的入口就很好知道了。
在这里插入图片描述

在上节代码的基础上添加int.h和int.c。int.h作为头文件使用,而没有把所有需要定义的寄存器写在int.c中,是因为太多了。
在这里插入图片描述

(2)函数名的实质就是函数的首地址
函数名在C语言中的理解方法和变量名其实没区别。编译器会把这个函数的函数体对应的代码段和这个函数的函数名(实质是符号)对应起来,等我们在使用这个函数名符号时,编译器会将函数的函数体实际上做替换。因为函数体都不止4字节,而函数名这个符号只能对应1个地址,所以实际对应的是函数体那一个代码段的首地址。拿C语言中的语法来讲,函数名就是这个函数的函数指针。
在这里插入图片描述

总结:当我们将异常处理程序的首地址和异常向量表绑定起来后,异常处理初步阶段就完成了。到目前可以保证相应异常发生后,硬件自动跳转到对应异常向量表入口去执行时,可以执行到我们事先绑定的函数。
(3)中断处理要先在汇编中进行
在这里插入图片描述

中断处理要注意保护现场(中断从SVC模式来,则保存SVC模式下的必要寄存器的值)和恢复现场(中断处理完成后,准备返回SVC模式前,要将保存的SVC模式下的必要寄存器的值恢复回去,不然到了SVC模式后寄存器的值乱了,SVC模式下原来正在进行的常规任务就被你搞坏了)(如下图SVC和IRQ是共用r0-12寄存器的)

保存现场包括(如从SVC到IRQ):①设置IRQ栈;②保存LR;③保存R0~R12。中断返回时关键的2个寄存器是PC和CPSR。所以我们在进入IRQ模式时,应该将SVC模式下的下一句指令的地址(中断返回地址)和CPSR保存起来,将来恢复时才可以将中断返回地址给PC,将保存的CPSR给CPSR。中断返回地址就保存在LR中,而CPSR(自动)保存在(IRQ模式下的)SPSR中。
int.c中的IRQ和FIQ函数只考虑中断处理,不负责现场保护和恢复,负责保护和恢复的程序写在start.S中。进入IRQ模式后设置其下的栈,注意栈的其实地址是上边缘,因为ARM中默认是满减栈,即IRQ栈地址为0xD003_7F80。由于ARM有流水线,所以PC的值会比真正执行的代码+8,所以这里恢复的lr应该减4,即到刚刚执行的中断代码的下一个代码继续执行。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结:汇编保存现场和恢复现场。①保护现场关键是保存:中断处理程序的返回地址,r0-r12(cpsr是自动保存的)②恢复现场主要是恢复:r0-r12,pc,cpsr
6.S5PV210的向量中断控制器
(1)异常处理的2个阶段
可以将异常处理分为2个阶段来理解。第一个阶段是异常向量表跳转;第二个阶段就是进入了真正的异常处理程序irq_handler之后的部分。第二个阶段的目的是识别多个中断源中究竟哪一个发生了中断,然后调用相应的中断处理程序来处理这个中断。(210中有一百多种中断)
(2)S3C2440的第二阶段处理过程
①怎么找到具体是哪个中断(看被置一的编号):S3C2440的中断控制器中有一个寄存器(32位的),寄存器的每一个位对应一个中断源。为了解决支持更多中断源,2440又设计了一个子中断机制。在一级中断寄存器中有一些中断是共用的一个bit位,譬如AC97和WDT。对于共用中断,用子中断来区分究竟是哪一个发生了中断。首先给每个中断做了个编号,进入isr_handler之后先通过查阅中断源寄存器和子中断寄存器(中哪一位为1)确定中断的编号。
②怎么找到对应的isr的问题(用数组):用这个编号去isr数组中查阅得到isr地址。(isr数组是中断初始化时事先设定好的,就是把各个中断的isr的函数名(表示其函数所在地址)组成一个数组)
评价:2440的中断处理设计不是特别优秀:使用子中断搞成2级的很麻烦;计算中断编号很耗费时间,影响系统的实时性,即中断发生到响应的时间。
(3)S5PV210的第二阶段处理过程
①怎么找到具体是哪个中断(看被置一的编号):S5PV210中设计了4个中断寄存器,每个32位,每位对应一个中断源。(理论上210最多支持128个中断,实际支持不足128个,有些位是空的)。当中断发生时,在irq_handler中依次去查询4个中断源寄存器,看哪一个的哪一位被置1,则这个位对应的寄存器就发生了中断,即找到了中断编号。
②怎么找到对应的isr的问题(不用数组):210中支持的中断源多了很多,如果还使用2440的那一套来寻找isr地址就太慢了,太影响实时性了。210提供了很多寄存器来解决每个中断源对应isr的寻找问题,具体寻找过程和建立过程见下节,实现的效果是当发生相应中断时,硬件会自动的将相应isr推入一定的寄存器中,我们软件只要去这个寄存器中执行函数就行了。
(4)总结:第一阶段(异常向量表阶段)2440和210几乎是完全相同的。实际上几乎所有的CPU在第一阶段都是相同的。第二阶段就彼此不同了。各个SoC根据自己对实时性的要求,和支持的中断源的多少,各自发明了各自处理中断,找到中断编号,进一步找到对应isr地址的方式。
7.S5PV210中断处理的主要寄存器(n=0-3)
(1)VICnINTENABLE和VICnINTENCLEAR
VICnINTENABLE 对应interrupt enable负责相应的中断的使能,INTENCLEAR对应interrupt enable clear负责相应的中断的禁止。使能时只要在这个中断编号对应的VICnINTENABLE的相应bit位写1即可;如果想禁止某个中断源时,只要向VICnINTENCLEAR中相应的位写1即可。
注意:这里的设计一共有2种:有些CPU是中断使能和禁止是一个寄存器位,写1就使能写0就禁止,这就要使用读改写来操作;另一种就是使能和禁止分开为2个寄存器,要使能就写使能寄存器,要禁止就写禁止寄存器,这样的好处是我们使能操作时不需要读改写,直接把那个位写1即可(因为写0不会对其他位造成影响,其他位是1的还是1),禁止时也是,不需要读改写,210用的即是这种。

在这里插入图片描述
在这里插入图片描述

(2)VICnINTSELECT
设置各个中断的模式为irq还是fiq,一般都设置成irq。irq是普通中断,fiq是快速中断。快速中断用于对实时性要求很高的中断源。fiq在CPU设计时预先提供了一些机制保证fiq可以被快速处理,限制就是只能有一个中断源被设置为fiq。
在这里插入图片描述

CPU如何保证fiq比irq快?①fiq模式有专用的r8~r12,因此在fiq的isr中可以直接使用r8-r12而不用保存,这就能节省时间;②异常向量表中fiq是最后一个异常向量入口。因此fiq的isr不需要跳转,可以直接写在原地,这样就比其他异常少跳转一次,省了些时间。

在这里插入图片描述
在这里插入图片描述

(3)VICnIRQSTATUS和VICnFIQSTATUS
中断状态寄存器,是只读的。当发生了中断时,硬件会自动将该寄存器的对应位置为1,表示中断发生了。软件在处理中断第二阶段的第一阶段,就是靠查询这个寄存器来得到中断编号的。
在这里插入图片描述

(4)VICnVECTPRIORITY0~VICnVECTPRIORITY31
中断优先级设置寄存器,设置多个中断同时发生时先处理谁后处理谁的问题。一般来说高优先级的中断可以打断低优先级的中断,从而嵌套处理中断。当然了有些硬件/软件可以设置不支持中断嵌套。
(5)VICnVECTADDR0~VICnVECTADDR31、VICnADDR
这些寄存器和210中断处理第二阶段的第二阶段有关。VICnVECTADDR0-31这32个寄存器分别用来存放真正的各个中断对应的isr的函数地址。相当于每一个中断源都有一个VECTADDR寄存器,程序员在设置中断的时候,把这个中断的isr地址直接放入这个中断对应的VECTADDR寄存器即可。
VICnADDR这个寄存器是只需要读的,它里面的内容是由硬件自动设置的。当发生了相应中断时,硬件会自动识别中断编号,并且会自动找到这个中断的VECTADDR寄存器,然后将其读出复制到VICnADDR中,供我们使用。这样的设计避免了软件查找中断源和isr,节省了时间,提高了210的中断响应速度。
在这里插入图片描述
在这里插入图片描述

8.S5PV210中断处理的编程实践
(1)中断控制器初始化
第一阶段绑定异常向量表到异常处理程序;禁止所有中断源;选择所有中断类型为IRQ;清VICnADDR中的地址。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(2)中断的使能与禁止
先根据中断号判断这个中断属于VIC几,然后在用中断源减去这个VIC的偏移量,得到这个中断号在本VIC中的偏移量,然后1<<x位,写入相应的VIC的INTENABLE/INTENCLEAR寄存器即可。
在这里插入图片描述

在这里插入图片描述

(3)绑定自己实现的isr到VICnVECTADDR
首先要搞清楚2个寄存器的区别:VICnVECTADDR和VICnADDR。VICVECTADDR寄存器一共有4×32个,每个中断源都有一个VECTADDR寄存器,我们将自己为这个中断源写的isr地址丢到这个中断源对应的VECTADDR寄存器中即可。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(4)真正的中断处理程序如何获取isr
当发生中断时,硬件会自动把相应中断源的isr地址从VICnVECTADDR寄存器中推入VICnADDR寄存器中,所以我们第二阶段的第二阶段isr_handler中,只需要到相应的VICnADDR中去拿出isr地址,调用执行即可。
在这里插入图片描述

在这里插入图片描述

注:(4)和(5)的结合,就是210的硬件自动寻找isr的机制。
(6)总结,整个中断的工作分为2部分:
①是我们为中断响应而做的预备工作:
·初始化中断控制器
·绑定写好的isr到中断控制器
·相应中断的所有条件使能
②是当硬件产生中断后如何自动执行isr:(自动)
·第一步,经过异常向量表跳转入IRQ/FIQ的入口
·第二步,做中断现场保护(在start.S中),然后跳入isr_handler
· 第三步,在isr_handler中先去搞清楚是哪个VIC中断了,然后直接去这个VIC的ADDR寄存器中取isr来执行即可。
·第四步,isr执行完,中断现场恢复,直接返回继续做常规任务。
9.外部中断
(1)什么是外部中断?在数据手册哪里?
SoC支持的中断类型中有一类叫外部中断。内部中断就是指的中断源来自于SoC内部(一般是内部外设),譬如串口、定时器等部件产生的中断;外部中断是SoC外部的设备,通过外部中断对应的GPIO引脚产生的中断。如下1-31都是外部中断。
在这里插入图片描述

按键在SoC中就使用外部中断来实现。具体实现方法是:将按键电路接在外部中断的GPIO上,然后将GPIO配置为外部中断模式。按键电路的电压高低变化会触发GPIO对应的外部中断。

在这里插入图片描述
在这里插入图片描述

外部中断相关的介绍和寄存器都在数据手册2.2.60章节(属于GPIO部分)

(2)电平触发和边沿触发
外部中断包括:电平触发和边沿触发。电平触发的特点是,只要GPIO上的电平满足条件就会不停触发中断;边沿触发分为上升沿触发、下降沿触发和双边沿触发三种,边沿触只关心电平的变化。
分析按键的工作:如果我们关注的是按键按下和弹起这两个事件本身,那么应该用边沿触发来处理按键;如果我们关心的是按键按下/弹起的那一段时间,那么应该用电平触发。
(3)关键寄存器:CON、PEND、MASK
外部中断的主要配置寄存器有3个:EXT_CON、EXT_PEND、EXT_MASK。
EXT_CON配置外部中断的触发方式,即外部中断产生的条件。
在这里插入图片描述

EXT_PEND寄存器是中断挂起寄存器。这个寄存器中每一位对应一个外部中断,平时没有中断时值为0。当发生了中断后,硬件会自动置1(挂起状态),直到有空了去处理完这个中断后软件的代码才会将该位置0。
在这里插入图片描述

EXT_MASK寄存器就是各个外部中断的使能/禁止开关。

在这里插入图片描述

X210开发板的按键(涉及GPH0和GPH2)对应的EINT编号:EINT2、EINT3、EINT16、EINT17、EINT18、EINT19。

10.中断方式处理按键编程实践
(1)外部中断对应的GPIO模式设置(key.c)
先定义一下和外部中断相关的寄存器CON、PEND、MASK。
定义六个寄存器
在这里插入图片描述

定义访问寄存器

在这里插入图片描述
在这里插入图片描述

0xFF左移8位。
在这里插入图片描述

0xFFFF左移0位。
代码如下。
在这里插入图片描述

(2)中断触发模式设置(key.c)
在这里插入图片描述
在这里插入图片描述

上面两个按键分别设置为下降沿触发。即0010。(2表示二进制01)
在这里插入图片描述

(3)中断允许(外部中断允许)(key.c)
在这里插入图片描述
在这里插入图片描述

(4)清挂起(这里清挂起写1,虽然我也不知道为什么,朱老师说的)(key.c)
在这里插入图片描述
在这里插入图片描述

(5)中断处理程序isr编写(key.c)
如下为EINT2和EINT3的isr。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

如下为EINT16/17/18/19共用的isr。
在这里插入图片描述

(6)main.c如下
在这里插入图片描述
在这里插入图片描述

这里心跳模仿的是常规时钟。
(7)本来每一个.c文件都应该写一个.h文件供调用,这里直接把剩余所有的头文件声明到main.h,然后在main.c和key.c中调用main.h。(两个delay前新加了static就不用声明了,putc和getc报错就删)
在这里插入图片描述

(后来编译有错误,对比老师的代码发现之前几次的Makefile里的objs都少加了int.o key.o,而且在start.S中少加了一个.global IRQ_handle )

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值