黑金动力社区:http://www.heijin.org
声明:本文为原创作品,版权归本博文作者所有,如需转载,请注明出处
这一节,我将给大家讲解第一个与硬件有关的程序,虽然内容简单,却极具代表性。我将采用一种寄存器的操作方式,让大家感受到开发NIOS跟单片机一样的简单,看透NIOS II开发的本质,尽量避免使用NIOS II IDE提供的API,这样做有很多好处。首先,有单片机开发经验的人很熟悉这种操作方式,其次,可以了解到NIOS的本质,真正的去开发它,而不是仅仅用它的API来写一些应用程序。
做过单片机试验的人一定对LED实验记忆犹新,因为它是硬件试验部分的第一课,通过这个简单的实验,可以让你对单片机的操作有一个感官上的了解,可以说意义不同寻常。这一节,我也通过LED实验来带大家进入NIOS II的开发世界,感受NIOS的魅力所在,下面我们开始吧。
第一步,我们要在软核中加入PIO模块。打开我们第一节建的Quartus工程,然后双击KERNEL,如下图红圈所示
点击后进入了SOPC BUILDER界面,如下图所示
点击下图所示红圈处PIO(Parallel I/O)
点击后,如下图所示,红圈1处是你需要的PIO口的宽度,即你需要几个IO口,这里面我设置为4,即我要控制4个LED,红圈2是选择输出方式,我选择为输出(Output)。
接下来,点击Finish,完成PIO模块的构建,然后将其改名为PIO_LED,如下图所示
接下来,需要自动分配一下地址,第一节我们已经讲过,如下图所示
接下来,我们就要开始编译了,点击Generate,需要保存一下,点击save,开始编译。
经过一段耐心的等待,编译成功,如下图所示
完成了上面的工作,点击Exit,会出现下面的界面,询问是否需要对KERNEL进行更新,点击“是(Y)”。
然后,会出现下面界面,点击OK
点击后,会出现下面情况,连线错位,这就需要我们手工来整理一下,将相应的管脚相连接。
整理还以后,如下图所示,可以看出,还有PIO_LED没有管脚相连,我们来建立一个输出管脚,并重命名
最后的样子大家可以看到,如下图所示,我们将将其命名为LED[3..0],这是quartus中总线的命名方式,大家要注意。
一切准备好了,我们需要给他进行引脚分配,按下图所示
点击Tcl scripts,如下图所示,选中kingst.tcl,点击Run,完成管脚分配。
下面,我们开始编译了,又是一次漫长的等待后,看到了下图红圈所示,说明编译成功。
我们再来看看我们占用了多少资源,如下图所示,还是66%,说明一个PIO模块占用资源很少的。
到此为止,我们在软核中添加PIO模块已经完成,接下来,我们进行的就是在NIOS II IDE中开发软件部分。
首先打开NIOS II 9.0 IDE,打开后,第一件事就是重新编译整个工程,按快捷键Ctrl+b。又一次漫长的等待之后,我们打开system.h文件,大家能看到有什么变化么,可能有人发现了,system.h文件对比之前的多了下面内容,这部分内容就是我们在软核中新构建的PIO_LED模块,我们将要用到的是PIO_LED_BASE,它是PIO_LED所有寄存器的首地址。
下面,我们就来运用这个首地址来进行编程,完成对4个LED的控制。
第一步,我需要建立一个放置头文件的文件夹,我将其命名为inc,方法如下图所示,鼠标在hello_world上点击右键,然后点击New中的Source Folder,这样就建立了一个文件夹,
然后会出现下图,在红圈处输入inc,点击Finish,完成文件夹建立。
大家可以看出,在左边工程目录栏中多出了一个inc文件夹,如下图所示
接下来,我要在inc中建立一个头文件,方法如下图所示,在inc上点击右键,然后点击New中的Header File,红圈处所示
点击以后,出现下图,在红圈处输入sopc.h,点击Finish,完成头文件的构建。
完成上面的工作以后,代码栏处,可以看到下图,现在我们就对其进行编辑
修改后的代码如下图所示,
大家可能看到上面的代码不明白什么意思,这很正常,第一次接触NIOS的人不是很了解这个东西,我现在就给大家详细的讲解一下。
首先我们看红圈2处的结构体,在这里,我们定义了一个结构体,将其命名为PIO_STR,这个结构体的结构是有说法的,它是根据芯片手册来写的。我现在给大家截个图,这个图来自《n2cpu_Embedded Peripherals.pdf》,版本是下图所示
其中9-5页和9-6也中有这么一个表格,如下图所示
这个表格就是PIO Core寄存器映射,我的结构体就是根据它来写的,名字不重要,重要的是它的顺序,也就是偏移量(offset)。第一项是数据data,第二项是IO口方向,第三项是中断控制位,第四项是边沿控制位。他们每一项都有n位,这个n就是我们在软核构建中的宽度(width),如下图所示的红框1,我们之前设置为4,那么n等于4。
接下来,我们来讲红框3中的代码,这部分代码中你一定记得PIO_LED_BASE,对,这就是我之前在system.h中特意强调的,PIO_LED的基地址。那我红圈3中的代码意图就很明显了,定义了一个宏,命名为LED,它是指向PIO_LED_BASE的结构体指针,这个结构体就是PIO_STR(如果大家对结构体指针不理解,那你就得回去看C语言书了,我在这就不具体说了)。红圈1的代码也很简单,是为了控制红圈3的定义的,这样做是为了增强代码的严谨性和可控制性。当你没有定义PIO_LED_BASE时,你就可以将红圈1中的宏定义#define _LED去掉,不多说了,再多说就挨砖块了,呵呵。
大家理解了sopc.h代码以后,我们就可以进行C代码编程了。忘了说了,在NIOS II IDE中一定要记得修改以后保存,它很幼稚的,编译前不会提醒你去保存的,如果你忘记了保存,相当于你没有修改。好了,下面我们进行C代码编程了。
为了规范化程序,我需要对程序做一些调整,以便以后的讲解。首先建立两个文件夹,分别命名为driver,main。建好以后,如下图所示,
然后将hello_world.c名字改为main.c,并将它放到main文件夹中。改好以后,如下图所示
接下来,我们来修改main.c中的程序,我先介绍一下我这段代码的目的,这段代码就是控制我的黑金开发板的4个LED,让他们按顺序闪烁,就是大家习惯说的流水灯。下图就是我们要控制的4个LED,当FPGA将相应的管脚置1时,LED就会亮,当置0,LED就不会亮了。
修改好的程序如下图所示,首先说红框1,有些人可能没用过这种方式,这是一种相对路径的方式调用头文件,好处就是可移植性好,不管工程放到其他什么地方,这个头文件和c文件的相对位置都不会变,也就不需要对这个地方进行修改。再说红框2处的代码,关键是LED->DATA = 1<<i;LED大家应该记得,LED是我们在sopc.h中定义的宏,是结构体指针,那么LED->DATA是什么意思大家就应该很清楚了,是它的结构体中DATA的内容(不是地址)。LED->DATA = 1<<i;就是对LED->DATA的四个位循环置1。所以,4个LED就会按顺序闪烁了。usleep是微妙级延时,我们在这里每次延时0.5ms。
讲到这里,第一个程序就讲完了,下面需要做的就是编译,下载程序到FPGA,验证是否正确,我就不详细说了。
接下来,我们总结一下这节内容。我们来对比一下寄存器操作方式与API之间有什么联系和不同,上面的程序,如果用NIOS II IDE提供的API来写,那么如下图所示
IOWR_ALTERA_AVALON_PIO是一个宏,在altera_avalon_pio_regs.h中,其定义如下
(大家可以按住ctrl键后,用鼠标点击进入定义所在的位置),大家可以看到,它是一个IOWR的宏,而IOWR的具体写法我就在此不详细说了(大家感兴趣的可以去NIOS的源码),反正就是对硬件地址的控制。我的做法就是绕过这个大圈子,直接去控制它的寄存器。
大家可能可能有点纳闷,我们的机构体中定义了四个变量,但只用了DATA一个,在这说明一下原因,首先是DIRECTION。这个是IO的方向,就是说是输入还是输出,或者是双向的。因为在我们构建PIO模块的构成中有了一个选项,如下图所示,红圈2,我们选择了输出(Output ports only),也就是我们在底层就已经固定了它的方向,所以在软件中就不需要在定义了。还有两个变量是涉及到中断时才会用到,所以在这个程序中也没有用到。
这一节是以后内容的基础,希望大家好好的理解,尤其是sopc.h中定义的那个结构体,大家一定要理解清楚。以后,还会涉及到UART,SPI等,都需要建立结构体,这个都是触类旁通,举一反三的东西,弄清楚一个,其他的都好理解了。
我要提醒大家一句,我虽然提倡大家用操作寄存器方式编程,但并不希望所有的程序都按这种方式来写,比如说对flash的操作,我们就可以使用API来写,因为flash的操作相对复杂,而利用API可以很简单的几个语句就能完成,没必要自己来写。
好了,今天我们就讲到这里,下一节我们将讲一下NIOS如何在线调试,以及调试中的一些小技巧,希望大家耐心等待……
文章评论(0条评论)
登录后参与讨论