【连载】【FPGA黑金开发板】NIOS II那些事儿外部中断实验(五)

 声明:本文为原创作品,版权归本博文作者所有,如需转载,请注明出处http://www.cnblogs.com/kingst/

2010032423274411    

简介

      这一节,我们通过来讲解一下NIOS II的硬件中断的内容,同时借助这节内容我们也要介绍NIOS II IDE在线调试的方法和技巧。首先来点理论知识,介绍一下与硬件中断相关的内容,让大家对NIOS II 的硬件中断有一个概括性的了解。

      ISR(Interrupt Service Routine)中断服务函数是为硬件中断服务的子程序。NIOS II处理器支持32个硬件中断,每一个使能了的硬件中断都应该有一个ISR与之对应。中断发生时,硬件中断处理器会根据检测到的有效中断级别,调用相应的ISR为其进行中断服务。

要完成硬件中断工作,我们需要做两件事:

      第一, 注册中断函数ISR,它的函数原型如下所示:

      int alt_irq_register(alt_u32 id, void* context, void(*handler) (void*,alt_u32));

      id:中断优先级,即所注册的ISR是为哪个中断优先级的中断服务的。中断优先级在SOPC Builder中分配的,在第一节中我们提起过,不知道大家是否记得,我们来回忆一下,如下图所示,通过这一步我们来完成中断的自动分配

clip_image002

分配好的IRQ如下图所示,当然,IRQ可根据自己的特殊要求进行手工修改,只要不重复就没问题。

clip_image004

而在NIOS II IDE软件中体现在system.h文件中,之前我们也提到过,如下图所示,看到了吧,JTAG_UART_IRQ为0,与上图的中断号正好相同,这可不是巧合。我已经说过了,system.h文件是根据软核来生成的,所以JTAG_UART_IRQ等于0也是可想而知的了。

clip_image006

      接下来我们继续说剩下两个参数,

      context,为所注册的ISR传递参数,可以是NULL;

      handler,中断服务函数ISR的指针。

      再来说返回值,返回值是0时,表示中断注册成功;返回为负数,表明中断注册失败。

      这里面有一个需要注册的地方,如果handler不是NULL,则该优先级中断在注册成功后将自动使能,也即是说,只要我们在handler处有相应的ISR,我们就不需要再进行使能处理了。说完第一,我们来说说第二。

      第二, 编写ISR函数,这个函数有我们自己来写,而不是HAL系统提供的。它跟一般的函数定义没什么区别,只是对ISR的函数原型有特定的要求:

      void ISR_handler(void* context, alt_u32 id);

      context: 传给ISR的形参,可以是NULL;

      id: 中断优先级。

      OK,只要这两步我们就可以完成中断函数的处理了。废话少说,我们来点实际的吧,跟我来。

硬件开发

      下面,我们就利用一个外部按键来验证一下中断函数的处理过程。

      首先,我们要构造一个给外部按键用的PIO模块。这部分内容之前已经详细的讲过了,在此简述略过吧。

      打开Quartus II软件,然后双击KERNEL,进入SOPC BUILDER。进入后,我们建立一个PIO模块,在建立过程中有一个地方有所不同,我们来看一下,如下图所示,红圈1处,我们输入1,因为我们只需要一个按键(我们的黑金开发板中一共有5个按键);红圈2处选择Input ports only,也就是作为输入。完成后,点击Next,

clip_image008

点击后,如下图所示,我们将红圈2(General IRQ)处选中,其他不变。在这多说两句,这里的中断分为两种,一种是电平(Level)中断,也就是高电平/低电平中断,还有一种就是沿中断,包括上升沿、下降沿。做过单片机的人应该都很熟悉,如果你想要实现沿中断,需要把红圈1(Synchronously capture)处选中,下面包括3种方式,大家可以根据自己的要求选择。完成上面工作以后,点击Finish,完成PIO构建。

clip_image010

      下面需要对名字需要进行修改,我将其命名为KEY,如下图红圈1处所示。大家再看一下红圈2处,大家可以发现中断号有两个0,一个是jtag_uart的,一个是我们新建立的KEY的,这回大家明白了,为什么我们需要自动分配中断号了吧,出现了相同的中断优先级了。

clip_image012

接下来,我们就对中断自动分配一下。如下图所示,大家可以看到红圈2处发生变化了吧,KEY的中断级别变化了,变成了1。自动中断分配是自上由下按顺序分配的。

clip_image014

完成上面的构建以后开始编译了,又需要漫长的等待了……(其实也不是很长,也就一两分钟吧,根据电脑配置而定)

      编译好了以后,回到Quartus界面,需要进行整理,添加KEY的输入管脚,我这里就简要略过了。看一下整好要以后的样子,如下图所示,我我将其命名为KEY[4],这个对应的是我的黑金开发板的中间的那个按键。下图中有一个地方需要注意,由于电平中断时,NIOS只对高电平敏感,所以如果想实现低电平敏感需要加一个非门,

clip_image016

非门的加入方式跟加入input管脚是一样的,双击空白处出现下图,然后在红圈处输入not,点击OK即可。

clip_image018

然后,分配引脚,编译,又是等待……

编译好以后,咱们再来看看用了多少资源,如下图所示,还是66%,

clip_image020

咱们进行对以一下,在上一节我们占用资源如下图所示

clip_image022

对比以后发现,PIO模块占用资源是相当的少。

      编译好以后,大家可以选择是通过AS模式或JTAG模式下载都可以,不过记得JTAG模式掉电会丢失,记得上电以后重新烧写。

      硬件部分就讲完了,下面我们来讲软件编程部分的内容。

软件开发

      打开NIOS II 9.0 IDE软件,打开后,先编译一次,快捷键为CTRL+b(本人比较喜欢快捷键,方便快捷么,呵呵)。编译好以后,我们来看看system.h有什么变化。大家可以看到,system.h中多出来了下面内容,这部分内容就是有关KEY的,其中红圈1处是基地址,红圈2处室中断号,这些都是我们在下面要用到的,大家先有个印象。

clip_image024

接下来,我们就要开始写程序了,首先需要在sopc.h中添加内容,如下图所示,跟上一节的LED是一样的,在此就不做解释了。

clip_image026

修改好以后,我们对main.c函数进行更改,整体程序如下图所示,这个程序没有对按键进行防抖处理,只是为了展示外部中断处理的操作过程,如想作为项目中应用必须加入按键防抖处理,在此不具体说明。这个函数通过外部按键来产生中断,因为我们设置的是低电平产生中断,所以当按键按下时,就会产生了一个低电平,这时就会进入中断函数。在中断函数中,我们对key_flag进行取反。而在主函数中,我们不断地进行查询,当key_flag为1时,LED->DATA置1,也就是让外部发光二极管亮;当key_flag为0时,LED->DATA置0,这时,发光二极管不亮。

      接下来我们具体讲解一下每一个函数。

clip_image028

      首先编译一个初始化程序,如下图所示,一共有两条语句,我们先说红圈1处的语句,这个语句的目的是,使能中断位,上一节我已经讲过了,PIO模块对应的结构体中的INTERRUPT_MASK是中断控制寄存器的内存映射,当该位置1时,允许中断,否则,禁止中断。再说红圈2处的语句,我们前面已经见过这个语句了,用它来完成中断的注册,KEY_IRQ来自system.h,ISR_key是ISR函数。用return返回为了在主函数中判断注册是否成功,如果成功返回0,非0表示注册失败。

clip_image030

      下面来看看ISR_key函数,如下图所示,只有一条语句,其中,key_flag是一个全局变量,这条语句的意思,没进一次中断,就将key_flag取反。

clip_image032

      下面是主函数,红圈1处部分是判断初始化是否成功,如果init_key()函数返回值是0,说明注册成功,打印register successfully!\n,可以再观察栏中看到它,否则打印Error: register failure!\n。红圈2处是判断标志位key_flag,如果它为1,则对LED->DATA 置1,也就是让让对应的LED亮,否则LED不亮。

clip_image034

这个程序都很简单,有利于我们来了解中断的处理过程。

      现在借着讲中断的机会,给大家介绍一下NIOS II IDE中如何进行在线调试(DEBUG)。如下图示,在左边框的hello_world点击右键,选择Debug As->Nios II Hardware,或者快捷键F11(前提:已将仿真器与JTAG口相连,并且电源通电),我们就进入了DEBUG界面

clip_image036

      我们来简单介绍一下DEBUG界面下的几个功能栏,代码区不用说了,显示我们的源代码,观察区包括主要用到两个,一个是控制台console,另一个是Memory。Memory在正常模式下是不能使用的,也就是说只有在Debug模式下,我们才能通过Memory来观察内存中数值的改变。变量区主要是用来观察变量值,它的存在有助于我们判断数值的变化是否与我们预想的一样。还有一部分使我们在调试过程中经常用的功能键。包括开始,停止,还有就是三种模式的单步调试功能键。其他区域也有一定的功能,大家可以自己研究一下,对大家调试程序有一定的益处。

clip_image038

      简单介绍了一些功能以后,我们来调试一下我们的中断程序。对于调试中断程序,如何判断中断是否进入,我们有一个小的技巧,简单而有效。

      首先,我们在中断函数处设定一个断点,设置断点的方法是在你需要设置断点的地方,即红圈处,双击右键即可。

clip_image040

      记下来,让程序全速跑起来,按红圈处的按钮。程序跑起来以后,按开发板上的的外部按键(中间的那个按键),如果中断没有问题,程序就会停在我们设置的中断处,这说明,中断进入成功。这个方法即使用有简单,对调试中断这类程序很有好处。

clip_image042

对于如何运用单步调试之类的功能,只要玩过单片机的,都应该会的,我就不多说了。

总结

      最后,再简单总结一下中断需要注意的地方,大家知道中断程序主要处理一些实时性比较强的操作,因此不能在中断程序中进行等待或者阻塞性的操作。所以,尽量保持中断服务程序精简,不要在中断程序中处理复杂的处理,比如浮点型操作,printf函数等。

      这一节就到此为止,由于匆忙,内容可能会出现有问题的地方,希望大家指出,我将进行修改。

原文地址:https://www.cnblogs.com/kingst/p/1700812.html