S3C6410中断控制,基于OK6410A裸机按键中断程序(1)
近来为了学习Linux嵌入式系统的移植,买了块OK6410A的开发板,当然从裸机程序的开发开始了,然后不可避免遇到了按键中断这样的程序,下面按照思考的过程,写下总结吧!
首先,弄清楚硬件连接,通过查看原理图,得到了这样的硬件连接
KEYINT1-GPN0-EINT_G0_0
KEYINT2-GPN1-EINT_G0_1
KEYINT3-GPN2-EINT_G0_2
KEYINT4-GPN3-EINT_G0_3
KEYINT5-GPN4-EINT_G0_4
KEYINT6-GPN5-EINT_G0_5,然后就去S3C6410datasheet寻找对应的端口说明吧,一看不知道,看了吓一跳,三星这个英文 文档写的,感觉忒不严谨了,有叫“External Interrupt Group 1”的,有叫“Ext. Interrupt Group 8”的,还有“Ext. Interrupt Group”的,其实他们都是并列关系,外部中断的9个Group,你说为啥不统一写成“Ext. Interrupt Group0、Ext. Interrupt Group1”这样呢,非要一会儿缩写一会儿把0都省去了,害得我前后看了几遍GPIO部分。
既然端口对应上了外部中断EINT,决定去统一的整理下S3C6410的中断机制。
1、外部中断源控制器EINT,他一共被分为9个组,每组里面对应不同的IO管脚,S3C6410共有127个外部中断,其外接I/O引脚及分组如下:
外部中断组0 EINT_G0 GPN0---GPN15 、GPL8---GPL14、GPM0---GPM4
外部中断组1 EINT_G1 GPA0---GPA7、GPB0---GPB6
外部中断组2 EINT_G2 GPC0---GPC7
外部中断组3 EINT_G3 GPD0---GPD5
外部中断组4 EINT_G4 GPF0---GPF14
外部中断组5 EINT_G5 GPG0---GPG7
外部中断组6 EINT_G6 GPH0---GPH9
外部中断组7 EINT_G7 GPO0---GPO15
外部中断组8 EINT_G8 GPP0---GPP14
外部中断组9 EINT_G9 GPQ0---GPQ9
以上127个引脚每个引脚都可以产生一个外部中断
2、ARM的总中断控制器VIC,他由两个VIC组成,联合起来控制了64个中断源,每个控制32个,因此我们发现VIC的控制寄存器都是对应的有VIC0就有VIC1,其中我们找到了与外部中断有关的127个外部中断在VIC里的中断号的对应关系:
NO. 中断源 说明明 GROUP
0 INT_EINT0 外部中断组0 (EINT_G0)引脚号0-3 VIC0
1 INT_EINT1 外部中断组0 (EINT_G0)引脚号4-11 VIC0
32 INT_EINT2 外部中断组0 (EINT_G0)引脚号12-19 VIC1
33 INT_EINT3 外部中断组0 (EINT_G0)引脚号20-27 VIC1
53 INT_EINT4 外部中断组1-9 (EINT_G1~9) VIC1
我们发现属于外部中断组0的27个中断占用了VIC里的4个中断号,外部中断组1-9只占用了1个中断号,世界就是这么的不公平,努力吧!
3、S3C6410的中断流程,说到这里,大家是不是觉得有点乱(如果使用过STM32应该不会乱,因为STM32的中断也是两级控制的),我们总结下具体的中断流程:
外设(GPIO)——》EINT——》VIC——?,因此对应的我们编程序也就是
外设配置——》EINT配置——VIC配置——?,这是我们现在所能想到的,大家也觉得分析的不错吧,可惜错误也再此埋下了伏笔,即是问号处,我们缺失了一个配置,这个配置有的启动文件里已经帮你写了,所以好多教程也没有怎么提。此处不表,最后会说明。
S3C6410中断控制,基于OK6410A裸机按键中断程序(2)
前面大概讲了一下外部中断的流程,现在我就开始按照此流程,结合具体的硬件进行整理和编程了。下面讲到具体的寄存器都按照OK6410开发板对应的GPN讲解。
1、GPIO的配置
相信你已经开始写中断程序了,基础的IO控制LED的程序肯定已经写过了,那一定知道控制GPIO一般就如下几个寄存器:
GPNCON——端口控制寄存器、可读可写
GPNDAT——端口数据寄存器、可读可写
GPNPUD——端口上下拉寄存器、可读可写,对于按键中断,显然我们只需要用到GPNCON,将端口设置为中断模式即可,按照下图,将对应的位设置为10即可。
rGPNCON &= ~((0x03<<0)|(0x03<<2)|(0x03<<4)|(0x03<<6)|(0x03<<8)|(0x03<<10));//把两位都变为00,也可以写成~(0xfff<<0)
rGPNCON |= ((0x02<<0)|(0x02<<2)|(0x02<<4)|(0x02<<6)|(0x02<<8)|(0x02<<10));//把前一位变为1,也可写成0xaaa<<0
此处为了更清楚的表明设置的过程,设置的稍有复杂,大家可以直接简化为注释后面的,同时为了不对其他端口造成影响,严格采用了对应的与或操作。
2、EINT配置,对应前面描述的中断知识,本按键GPN0~5只涉及到了GROUP0的中断EINT即EINT_G0,具体说是EINT_G0_0~5。
关于EINT的配置,我们先总结一下可能涉及到的寄存器:
总结下来就是四类,EINT控制寄存器,滤波控制寄存器,中断屏蔽寄存器,中断挂载寄存器,当然还有关于优先级等寄存器,如PRIORTY,SERVICE,SERVICEPEND这些,比较高级的功能,我也没有深入,此处暂不表。
此处说一下常用的这三类,(1)EINT控制寄存器,用来控制外部中断的触发方式,比如此处可以设定为低电平触发。
//设置外部中断EINT触发方式低电平触发为x000,最高位忽略,默认为x000,两个共用一个
rEINT0CON0 &= ~((0x07<<0)|(0x07<<4)|(0x07<<8));//把三位都变为0,也可以写为~(0x777<<0)
(2)中断屏蔽寄存器EIN0MASK,可读可写,写1屏蔽中断,写0使能中断。此时,我们首先肯定要使能按键中断口。
//设置中断使能,即将中断屏蔽寄存器置0,
rEINT0MASK &= ~((0x01<<0)|(0x01<<1)|(0x01<<2)|(0x01<<3)|(0x01<<4)|(0x01<<5));//把一位变为0,也可以写成~(0x3f<<0)
(3)中断挂载寄存器EINT0PEND,可读可写,读1表示发生中断,读0表示未发生中断,写1表示清除外部中断。
//清楚外部中断挂载,即外部中断挂载寄存器写1,清楚中断
rEINT0PEND |= ((0x01<<0)|(0x01<<1)|(0x01<<2)|(0x01<<3)|(0x01<<4)|(0x01<<5));//把一位变为1,也可以写成(0x3f<<0)
S3C6410中断控制,基于OK6410A裸机按键中断程序(3)
3、VIC的配置,对应前面讲的对应前面描述的中断知识,本按键GPN0~5只涉及到了GROUP0的中断EINT即EINT_G0,具体说是EINT_G0_0~5。再次对应到VIC,我们看到其中GPN0~3对应了EINT_G0_0~3,对应了INT_EINT0;GPN4~5对应了EINT_G0_4~5,对应了INT_EINT1,这两者都是在VIC0中,分属NO0和NO1。
然后关于VIC我们总结下相应的寄存器:此处仅以VIC0为例,选出了常用的一些寄存器,其他类似于中断优先级,软中断等等,还没有研究,需要共同学习。
(1)VIC0INTSELECT——中断选择寄存器,可读可写,0-选择IRQ,1-选择FIQ,FIQ更快,一般选IRQ,32位分别对应每一个中断源;
(2)VIC0INTENABLE与VIC0INTENCLEAR,这两者是一对的,其中VIC0INTENABLE——系统中断使能寄存器,可读可写,0-中断禁止,1-中断使能,一般只能用来使能中断,不能用来写0禁止中断,禁止中断使用VIC0INTENCLEAR——系统中断清除,可写,写0无效,写1-禁止中断,大家注意配合使用,32位分别对应每一个中断源;
(3)VIC0VECTADDR[0~31] ——矢量地址寄存器,即对应的中断服务程序的入口地址,一共32个地址,对应VIC0的32个中断源。可读可写;
(4)VIC0ADDRESS——又一个矢量地址寄存器,糊涂了吧,记住它的功能就行,通过向其写入任意值,就可以清除中断服务标志,类似于外部中断EINT中的EINT0PEND了,我们可以通过它在中断服务程序中清除系统中断标志,32位分别对应每一个中断源;
介绍完了这些VIC对应的寄存器,现在开始说说其配置的流程:
(1) 首先肯定是需要设定中断的类型IRQ还是FIQ,配置之前呢,先把对应的中断关了,因此,先需要禁止对应的系统中断。
//关闭对应的中断,对应位写1关闭系统中断,只能写32位,同rVIC1INTENCLEAR一起与rVIC0INTENABLE/rVIC1INTENABLE配套
rVIC0INTENCLEAR |= (0x01<<0|(0x01<<1));//外部中断0_0~5属于系统中断INT_EINT0/INT_EINT1
(2)那其次就是上面说的设定中断的类型IRQ还是FIQ了。
//设置系统中断类型,对应位写0-IRQ,1-FIQ,只能写32位,同rVIC1INTSELECT一起
rVIC0INTSELECT &= ~((0x01<<0)|(0x01<<1));//外部中断0_0~5属于系统中断INT_EINT0/INT_EINT1
(3)然后呢清除系统中断标志
/清除系统中断,对应位写0或者1都行,只能写32位,同rVIC1ADDRESS一起
rVIC0ADDRESS &= ~((0x01<<0)|(0x01<<1));//外部中断0_0~5属于系统中断INT_EINT0/INT_EINT1
(4)设定中断服务程序的入口地址
//写中断处理程序地址,rVIC0VECTADDR
VIC0VECTADDR[0] = (unsigned)Key1_handler;//unsigned说明是32位地址
VIC0VECTADDR[1] = (unsigned)Key2_handler;//unsigned说明是32位地址,这里需要说明,这边是32个32位的数组对应32个中断源的处理地址,因此宏定义的时候需要注意,此处宏定义跟其他寄存器定义有区别。
#define VIC0VECTADDR (( unsigned *)(0x71200100))
#define VIC1VECTADDR (( unsigned *)(0x71300100))
(5)使能系统中断
//使能系统中断,对应位写1使能系统中断,只能写32位,同rVIC1INTENABLE一起与rVIC0INTENCLEAR/rVIC1INTENCLEAR配套
rVIC0INTENABLE |= (0x01<<0|(0x01<<1));//外部中断0_0~5属于系统中断INT_EINT0/INT_EINT1
、GPIO的配置
这样一个简单的系统中断VIC配置就算完成了。
4、接下来就是中断服务程序了,这个大家都弄腻了,需要注意的就是清除中断标志,从上面可以看出,这里需要清除两个中断标志,分别是外部中断EINT和系统中断VIC的标志,例如rVIC0ADDRESS &= ~(0x01<<0);//清除系统中断INT_EINT0
以及rEINT0PEND |= (0x01<<0);//清除外部中断0_0
例外可以通过读rEINT0PEND的某一位,知道具体是外部中断EINT_G0的哪一个口发生中断。
大家是不是觉得这样就OK了呢,我也是这么想的,然后就下载程序进行了在线调试,可惜啥效果没有,然后又认真的把每一个寄存器又都检查了一遍,还是没有发现错误,疑惑不解啊。这就又回到了一开始埋下的伏笔那,具体见下一节。
S3C6410中断控制,基于OK6410A裸机按键中断程序(4)--------无法进中断
经过前面的准备,0K6410的按键中断基本搞定了,但是还有一个地方被我们大家遗漏了,于是我跟众多网友一样,停滞在这里,调不通按键中断了,没办法啊,中断就是不响应。
于是又回到了一开始埋下的伏笔那,S3C6410的中断响应流程那,外设(GPIO)——》EINT——》VIC——?,因此对应的我们编程序也就是外设配置——》EINT配置——VIC配置——?,这个问号处,是被我们忽视的地方。(此处各种上网查找,也有好多网友给出了正确答案,这里不一一致谢了哈,反正是各种实验哦)
下面是S3C6410应用笔记(apnv1.0)中断部分的截图,通过图我们发现,第一部分,我们没有做啊,即使能VIC接口(enable vic port)。当然还有另外一个方法,不太常用,一般都用这个调用VIC PORT。
按照笔记,使能VIC PORT,这里给出了一段汇编代码,尼玛,汇编不太会啊,这个放在哪儿呢,如果放在C程序中,需要内嵌汇编,使用asm又报错,没能解决,参考网上,有人放在了启动代码中,果断复制到启动代码中。
;------------------------------------ ; Enable VIC Port ;------------------------------------ mrc p15,0,r0,c1,c0,0 orr r0,r0,#(1<<24) mcr p15,0,r0,c1,c0,0
到了这里,好多网友都运行成功了,可是我这里还是没有运行成功,这又让我很郁闷,最终在网上众多博客中,找到了一篇http://blog.csdn.net/tankai19880619/article/details/8310050
,他里面另外还有一段汇编,使能IRQ,我把这段汇编copy到启动代码中,经过尝试,成功了,哈哈,虽然他博客中写的他自己没有调试成功。这个哥们的博客http://blog.csdn.net/yin138/article/details/6738917中也有类似的这段代码。
;------------------------------------ ; Enable IRQ ;------------------------------------ mrs r0,cpsr bic r0,r0,#0x80 msr cpsr_c,r0
就这样,最终按键中断的功能得以实现,真是一波三折啊。最终汇总一下整个流程:
外设(GPIO)——》EINT——》VIC——协处理器,因此对应的我们编程流程也就是
外设配置——》EINT配置——VIC配置——使能VIC PORT和使能IRQ(这里是我们不能进中断的很大原因,好多人都跟我一样死在这里了)。
由于对汇编不是很了解,以上有不当之处,请指正和谅解。
关于mcr与mrc参考 http://blog.csdn.net/chepwavege/article/details/7082808
关于msr与mrs参考 http://freesoftman.iteye.com/blog/1827249