中断在嵌入式里面是很常见的一个功能了。通过这个功能,可以让CPU减轻很多负担,不用不断的查询设备的状态。提高了CPU的效率。

中断的大体过程如下:

中断源检测中断信号产生,然后将中断信号发送给中断控制器,中断控制器判断该中断是否被屏蔽,从而决定该中断信号是否要发送给CPU。中断信号发送给CPU后,CPU对中断进行处理,也就是调用中断函数。上述过程,基本上是嵌入式的通过中断处理过程,只是不同的嵌入式在这三部分配置有区别而已。

S3C6410共有64个中断源。

上图是S3C6410的中断控制器,这里就关心红色框部分。这两个是中断控制器,分别管理各自的32个中断。 如下图:

上图红框中的就是今天我们需要关心的中断部分(外部中断)。

S3C6410共有127个外部中断,被分成10组,分组及引脚对应情况如下:

分组对应中断号如下:

从上图我们知道,并不是每一个外部中断引脚都分配了中断号,因此,在中断服务程序中,为了知道具体是哪一个中断,还需要去查询寄存器以知道是哪一个中断产生。

学习过2440的朋友可能知道,2440的中断的处理采用的是非向量方式,就是当中断产生时,都跳转到中断异常去,然后这个中断异常中,编写程序,判断是哪一个中断产生,然后去执行对应的中断处理程序。如下图:

为了向下兼容,S3C6410中断处理有向量模式和非向量方式。在向量模式中,提前设定每个中断对应的入口地址,这样当中断产生的时候,就不用跳转到中断异常去了,直接跳转到对应的中断程序去了。这样中断处理的效率就提高了。向量中断模式如下图:

明显看出,中断向量方式的效率要高。因此,在学习ok6410开发板的中断时,我职无旁贷的选择了向量中断模式。

综上,可以总结出S3C6410的外部中断程序设计基本步骤如下:

1、  设置外部管脚为中断;

2、  设置中断触发方式;

3、  取消中断屏蔽,使外部中断不屏蔽;

4、  设置中断滤波(可不设置,这里忽略);

5、  使能外部中断;

6、  设置中断号的入口地址;

7、  设置中断号的中断选择,是irq还是fiq,默认为是irq;

8、  开启向量中断方式并打开全局中断;

9、  编写中断处理函数,中断函数前和后要使用嵌入汇编,保存环境和恢复环境。中断处理后,要清除中断挂起位和中断执行地址。

接下来我们就以OK6410开发板为载体来逐步的分析外部中断的实现步骤。OK6410开发板上共有6个用户按键,我们接下来就通过这6个按键完成外部中断,实现按键控制led和蜂鸣器的运行情况。

要想实现上述的控制功能,就得知道这些按键、led和蜂鸣器对应的引脚情况,这些可以通过查看OK6410开发板原理图得到。原理图部分截图如下:

用户按键与芯片部分

led

     

蜂鸣器

    

从截图上可以看出来按键对应的引脚是GPN0~5、led对应的引脚是GPM0~3、蜂鸣器对应的引脚是GPF15。需要用到的引脚已经确认,下面开始按步骤配置外部中断。

1.配置外部引脚位中断

通过查看s3c6410手册GPIO章节,我们找到GPN相关配置寄存器如下:

这三个寄存器我们只需设置GPNCON,详细说明如下:

从上图可知,要想将开发板用户按键设置成外部中断,只需要将GPNCON寄存器的GPN0~5这几个位设为10即可。实现代码如下:

#define  GPNCON *((volatile unsigned long*)0x7f008830)           /* GPN控制寄存器 */
#define  GPNDAT *((volatile unsigned long*)0x7f008834)           /* GPN数据寄存器 此处未使用*/
#define  GPNPUD *((volatile unsigned long*)0x7f008838)           /* GPN上下拉配置寄存器 此处未使用*//* 按键初始化
*  设置按键对应引脚为外部中断模式
*/
void button_init(void)
{/* 方式1  移位GPNCON = (0b10 << 0) | (0b10 << 2) | (0b10 << 4) | (0b10 << 6) | (0b10 << 8) | (0b10 << 10);    *//* 方式2 逻辑运算 */GPNCON &= (~0x00000aaa);   GPNCON |= 0x00000aaa;
}

2.设置中断触发方式

通过原理图我们发现,按键部分是作了上拉处理的,所以这里设定外部中断触发方式为下降沿触发。接着在s3c6410手册的GPIO章节中找到外部中断相关寄存器说明部分,如下:

由GPNCON寄存器我们知道GPN0~5对应的是外部中断分组0 ,所以这里我们只需要配置EINT0CON0寄存器即可,EINT0CON0说明如下:

红框中的位就是我们需要设置,从图中可知,只需将对应为设置位010即可,代码如下:

/*interrupt registes*/
#define EXT_INT_0_CON       *((volatile unsigned int *)0x7f008900)        /* 外部中断0~27配置寄存器 */EXT_INT_0_CON &= ~(0x00000222);   EXT_INT_0_CON |= 0x00000222;                                   /* 配置为下降沿触发 */

3.取消中断屏蔽,使外部中断不屏蔽

既然想要取消中断屏蔽,那就得配置中断屏蔽寄存器,该寄存器在s3c6410手册的GPIO章节中找到外部中断相关寄存器说明部分。因为这里是外部中断分组0的0~5,所以只需配置EINT0MASK寄存器(0x7f008920)的bit0~5,如下红框部分:

从上图可以知道,需要将EINT0MASK寄存器的bit0~5配置为0,代码如下:

#define EXT_INT_0_MASK      *((volatile unsigned int *)0x7f008920)        /* 外部中断0~27屏蔽寄存器 */  EXT_INT_0_MASK &= 0xffffffc0;                                   /* 取消屏蔽外部中断 */

4.设置中断滤波

滤波主要是消除毛刺干扰,需要配置滤波寄存器,如下图:

代码如下“

#define EXT_INT_0_FLTCON0   *((volatile unsigned int *)0x7f008910)        /* 外部中断组0滤波寄存器 */ EXT_INT_0_FLTCON0 = (0xff) | (0xff << 8) | (0xff << 16);        /* 设置外部中断0~5的滤波*/

5.使能外部中断

要想使能外部中断,就得配置中断使能寄存器VICxINTENABLE,又因为这里是外部中断分组0的0~5,根据s3c6410手册中断章节中断源介绍部分可知,这里只需要配置VIC0INTENABLE,如下红框:

中断使能寄存器VIC0INTENABLE地址及说明如下:

从上图可知,只需将中断使能寄存器VIC0INTENABLE的bit0~1设置为1即可。配置代码如下:

#define VIC0INTENABLE       *((volatile unsigned int *)0x71200010)        /* 中断使能寄存器 */VIC0INTENABLE &= ~(0x00000003);                                /* 使能外部中断*/  VIC0INTENABLE |= (0x00000003);

6.设置中断号的入口地址

要想设置中断的入口地址,就得找到中断向量地址寄存器。通过上面的讲解可以知道外部中断组0的0~5中断由中断源0、1产生,隶属于VIC0,所以这里的中断向量地址寄存器就是VIC0VECRADDR0和VIC0VECRADDR1,地址如下:

中断向量地址寄存器说明如下:

所以,这里只需要将自定义的中断处理函数的地址赋给VIC0VECRADDR0和VIC0VECRADDR1即可,代码如下:

#define EINT0_3_VECTADDR    *((volatile unsigned int *)0x71200100)        /* 外部中断0~3向量地址寄存器 */
#define EINT4_7_VECTADDR    *((volatile unsigned int *)0x71200104)        /* 外部中断4~7向量地址寄存器 */EINT0_3_VECTADDR = (int)INT_TINT0_ISR;                         /* 中断产生时,CPU就会自动的将VIC0VECTADDR0的值赋给VIC0ADDRESS并跳转到这个地址去执 */
EINT4_7_VECTADDR = (int)INT_EINT1_ISR;                         /* INT_TINT0_ISR  INT_TINT1_ISR即为中断处理函数 */          

7.设置中断号的中断选择,是irq还是fiq,默认为是irq

既然这里说了默认是irq,那我们就是使用默认的中断模式,不去进行设置。如果想要更改,配置中断选择寄存器即可,如下:

8.开启向量中断方式并打开全局中断

开启向量中断方式需要操作系统控制协处理器(P15),而打开全局中断需要操作状态寄存器(CPSR)。别问我是怎么知道的,我不会告诉你是uboot告诉我这么做的。

开启向量中断方式:首先从arm11内核技术参考手册中找到系统控制协处理器章节(3.2节),找到与开启向量中断方式相关的协处理器控制寄存器,如下:

根据协处理器控制寄存器介绍得知,只需将控制寄存器的bit24置1就行了,代码如下:

/* 开启向量中断方式 */
__asm__("mrc p15,0,r0,c1,c0,0\n""orr r0,r0,#(1<<24)\n""mcr p15,0,r0,c1,c0,0\n"          : :);

开全局中断:从arm架构参考手册中找到程序状态寄存器相关章节,如下:

红框中这两个位就是关于开启中断的操作为,具体说明如下:

arm11内核技术参考手册中也有相关的介绍,如下:

设置代码如下:

    /*打开全局中断(开总中断)*/__asm__("mrs r0,cpsr\n""bic r0, r0, #0x80\n""msr cpsr_c, r0\n"            : :
);

至此,有关外部中断的初始化就完成,下面就可以进行中断服务函数的设计了。

9.编写中断处理函数,中断函数前和后要使用嵌入汇编,保存环境和恢复环境。中断处理后,要清除中断挂起位和中断执行地址

保存环境:这里需要嵌入汇编,代码如下:

/* 保存坏境 */  __asm__("sub lr, lr, #4\n"  "stmfd sp!, {r0-r12, lr}\n"       : :
);

这是一个固定的代码,目的是为了保存环境,将r0-r12,lr寄存器的值给压入栈中。这里有sub lr,lr,#4。将lr的值减去4。这个原因就要从ARM的流水线说起了。ARM采用流水线,取址,译码,执行。所以pc的值永远是当前执行指令的地址+8。中断跳转的时候,会将pc的值给lr,pc的值为当前执行程序地址+8,lr的值就是pc的值。而返回的地址应该是执行阶段的下一条地址,也就是当前执行程序地址+4,所以直接返回lr的值就不对了,应该返回lr-4的值。

恢复环境:这里也需要嵌入汇编代码,如下:

    __asm__( "ldmfd sp!, {r0-r12, pc}^ \n"       : :
);

这段代码也是固定的,目的是为了中断执行完后,在将这些值返回给r0-r12,pc寄存器。这样r0-r12寄存器的内容就恢复了,同时pc得到返回地址,就返回到中断前的程序地址去了。

有关于保存环境和恢复环境的代码。参考arm架构参考手册的A2,6章节,如下:

中断处理代码主要实现按键控制led和蜂鸣器运行状态,按下按键s2,点亮led1,再按一次s2,关闭led1;依次类推,详见代码:

/* 外部中断0~3处理函数 */ if((EXT_INT_0_PEND & (1<<0)) == (1 << 0))led_Toggle(1);if((EXT_INT_0_PEND & (1<<1)) == (1 << 1))led_Toggle(2);if((EXT_INT_0_PEND & (1<<2)) == (1 << 2))led_Toggle(3);if((EXT_INT_0_PEND & (1<<3)) == (1 << 3))
          led_Toggle(4);

/* 外部中断4~7处理函数 */ if((EXT_INT_0_PEND & (1<<4)) == (1 << 4))     beep_Toggle();if((EXT_INT_0_PEND & (1<<5)) == (1 << 5)){     led_Toggle(0);     beep_Toggle();}

中断处理完后,需要将中断挂起位给清零。这里,很简单的将所有中断位都给清零。然后再将中断执行地址给清0。

中断位挂起寄存器如下:

中断向量0地址寄存器如下:

代码如下:

    /* 清除中断 */EXT_INT_0_PEND = ~0x0; VIC0ADDRESS = 0; 

这样,也就完成了S3C6410的外部中断程序设计了。但是,此时的代码下载至Ok6410开发板并不能正常工作,那是因为中断处理函数的代码是用c代码写的,c代码需要什么?需要栈啊。不然怎么时间环境保护和恢复环境了。可能有人会问之前不是设置过栈吗?怎么这里还需要设置了?那是因为之前设置的栈是SVC模式下的栈,而不是irq模式下的栈。不同模式,有自己的备份寄存器,其中,栈SP是每个模式都有自己的。所以需要设置下irq模式下的栈。代码也是比较简单的。在之前的设置栈的汇编代码中,将模式切换为irq模式,再设置sp。

代码如下:

@栈初始化 64M内存用于栈
init_stack:msr cpsr_c, #0xd2ldr sp, =0x53000000    @ 初始化r13_irqmsr cpsr_c, #0xd3ldr sp, =0x54000000    @ 将0x54000000写入sp寄存器中,栈大小64M    0x5400000000-0x500000000mov pc, lr               @ 返回调用处继续往下执行

这样再将代码编译后下载至开发板里就可以正常工作了。

代码下载地址:OK6410实现外部中断控制led与蜂鸣器

5、  设置中断号的中断选择,是irq还是fiq,默认为是irq;

OK6410开发板学习之外部中断(按键点亮led和蜂鸣器)相关推荐

  1. OK6410开发板学习之一步一步实现精简BootLoader(BL1部分)

    众所周知,ok6410开发板是一块基于s3c6410芯片的开发板,板载资源丰富.s3c6410是三星电子生产的基于arm11内核的芯片.本文旨在总结一下bootloader操作步骤,用于以后复习.查找 ...

  2. OK6410开发板学习之安装linux系统至开发板

    说来惭愧,OK6410开发板自从买来就一直在躺灰,当时连教程也花了1千多大洋了,近来感觉前途迷茫,遂决定将其拿出来研究研究,看是否能够给我新出路. 废话不多说,先上一张OK6410开发板大图: 不要问 ...

  3. OK6410开发板Uboot学习总结----(三)从SD卡启动分析

    前面讲了Uboot启动流程和如何修改调试串口,相信大家对Uboot已经有了初步的了解,今天来进行更深一点的分析.上篇文章 OK6410开发板Uboot学习总结----(二)修改调试打印串口 遗留一个问 ...

  4. OK6410开发板Uboot学习总结----(一)Uboot启动分析

    OK6410开发板的Uboot是在1.1.6版本上进行移植的,为了便于分析,创建一个Source Insight工程,把源码下board目录里跟samsung相关的文件.cpu目录下s3c64xx文件 ...

  5. 基于OK6410开发板Uboot源码简单分析

    2018-04-07 OK6410开发板是基于三星S3C6410芯片设计的一款开发板,资源比较丰富,可是想要使用这些资源就需要编写相应的启动引导程序,即BootLoader.当然,想要自己凭空写出Bo ...

  6. 【STM32 .Net MF开发板学习-02】GPIO测试

    前段时间我借用市面上现成的Corex-M3开发板,打造了最低价的.Net Micro Framework开发板(参见<免费发放firmwave,打造史上最低价.Net MF开发板>),在此 ...

  7. 阿里云HaaS100物联网开发板学习笔记(四)轻应用初步--用javascript连接阿里云物联网平台

    摘要:本篇文章讲解如何使用JavaScript"轻应用"连接阿里云物联网平台并上报一个数据.仍然延续前几篇文章的结构,从安装软件环境开始讲,以使零基础的同学看了本篇文章之后,也能够 ...

  8. linux 控制虚拟串口,linux虚拟串口控制器实现-适用于无开发板学习tty driver-好向圈...

    在前面几章,我们介绍了tty子系统的框架.数据结构.tty驱动的注册与注销等内容,本章我们借助 之前学习的内容开发一个虚拟的串口控制器驱动,以便我们理解开发串口驱动的步骤及驱动开发实践. 本次实现的虚 ...

  9. linux开发板led怎么亮,TQ2440开发板上实现按键点亮LED(转载)

    //声明:该文章是在网上搜到的,对作者表示感谢. 文章记录了作者在TQ2440开发板上实现按键点亮LED驱动开发的详细过程,还记录了一些容易出现的错误,以及怎么解决这些错误. 一.驱动开发流程 二.驱 ...

最新文章

  1. 基于MM32F3273的MicroPython实验电路板 - 工作并不是正常
  2. 如何禁止用户名,密码自动填充
  3. C#父类与子类(多态性)
  4. 1.计算机语言发展史
  5. identity_insert 如何改为on_十分钟教你如何快速提高Laya构建速度,还不快来康康?...
  6. Red Hat Enterprise Linux (RHEL) 6.4 DVD ISO 迅雷下载地址
  7. CDM是什么?和CDP有什么区别?
  8. 第01将-Mysql体系结构与存储引擎
  9. 字符编码(ucs2 ucs4 utf)
  10. 获取选择的当前天、周、月、年的时间段
  11. ABP框架----写一个WebAPI
  12. 游戏策划——游戏的分类
  13. 移动硬盘无法退出终极解决方法
  14. 对比自监督学习综述 - A Survey of Contrastive Self-Supervised Learning
  15. 2020-03-10
  16. 数据结构 Hash,哈希冲突,哈希聚集,BloomFilter,分布式一致性hash
  17. UE4-制作真实场景三维地形
  18. Window10家庭版启用远程桌面功能
  19. 服务器变卡的原因有哪些
  20. Http请求之GET,POST,PUT,DELETE方法详解

热门文章

  1. mysql添加字段自动记录加入时间、更新时间
  2. java 一切object_javaObject类
  3. linux数据库监听配了两个,Oracle数据库和监听随LINUX系统启动
  4. c++语言常用语句,c++语言词汇.doc
  5. c通过位运算求绝对值_初中数学归类总结(四)有理数的乘除乘方及混合运算...
  6. ##MySql数据库的增删改查方法
  7. 2.js深入(以通俗易懂的语言解释JavaScript)
  8. CF935D Fafa and Ancient Alphabet 概率dp(递推)
  9. [BZOJ5286][洛谷P4425][HNOI2018]转盘(线段树)
  10. strip(),replace()和re.sub()用法