SOPC的课程已经结束了,短短4天,要消化的内容还真不少。今天又开始了DDR2 SDRAM的课程,其实在我来北京之前他们已经开始SDRAM的课程了,想起我做SDRAM的时候,真的是好费劲,又没人指点,代码写了一大串,前仿真也做的差不多,后仿就不行了,有些东西不知道怎么解决,还涉及到时序约束这门听起来高大上的知识,所以那段时间把我纠结的不轻。看样子在我离开北京之前这边应该是一直讲SDRAM了,上课的是李凡老师,一个很和蔼,很低调的老师,据说已经做了三四十年的工程师了,也是国内顶尖的人物了。打算这几天好好再学一下SDRAM,毕竟这东西用处很广,前景也很明朗,估计消化掉SOPC的内容后就要再开始SDRAM的征程了,这样在北京的20天也就不虚此行了~ 今天我们讲定时器中断与串行口中断,相信学过单片的朋友都用过这东西,对这东西也并不陌生,其实无非就是我们以前是对单片机编程,今天是对我们上次自己搭建的那个硬件系统进行编程,即我们的NIOS II 处理器。 和上个实验一样,小墨同学先来跟大家学文档,从文档入手,做到每一条代码都有根有据吧,这次还是要用到这个文档 n2cpu_nii5v3,我会附在文章后面等会,供大家下载 我们先来看定时器这个核
学IP核总要先知道这个核是干什么的吧,这里就和大家一起来啃啃英文,有一点还是提醒大家,文档还是要看原版,即使你的英文不怎么好,也要试着去看,慢慢的就好了,我一般是喜欢开着有道词典一般看英文,遇到没见过的词鼠标一点就出来了,个人感觉比较方便,大家也可以试一下
我们先来总体看一下这个核,上面一段英文是说呢, 1、这个定时器核可以配置成32位或者64位的定时器,这个我们在SOPC builder里面已经配置好了 2、可以控制定时器的开始,停止与复位 3、有两种计数模式,一种是计数一次就停止,一种是持续计数。其实我们的定时器是先让我们设定一个初值给它,然后定时器从这个初值开始递减,第一种计数模式就是定时器从初值递减到0时就停止计数了,第二种模式呢就是仍然继续计数 4、第四行就告诉了我们这个计数器的计数方式,即递减式计数 5、然后后面那几行呢大概就是说有几种操作选项,例如我们可以开始或者关闭定时器中断啊,开启看门狗定时器啊,当定时器计满一次数的时候发出一个高脉冲信号啊之类的 大概就是这些内容了吧~
然后我们可以去看一下它的功能描述框图这一页,加深一些理解
大概意思就是说,这个定时器核里包含了这么多寄存器,什么状态寄存器,控制寄存器之类的,当我们开启寄存器的时候,count里会自动载入我们设定的初值,开始计数,当计满一次的时候会产生一个中断给CPU,知道大体意思就行,具体的到后面寄存器视图再仔细看他们的具体功能
下面我们需要看一下它的software files这一页,知道要用到哪些头文件
重点来了,我们主要的任务就是看这个定时器核的寄存器映射视图,这也就直接关系到我们代码的编写
上面一段话大体意思是说啊,我们如果使用了硬件抽象层系统库提供给NIOS II处理器的这样一个标准配置的话,我们就没必要去访问定时器这个核的寄存器了,一般来说这个寄存器映射图是准们给我们编写器件驱动用的
前面一段话我理解的不是很好,不敢在这里忽悠大家,但是我们的目的就是编写器件驱动嘛,所以我们还是可以使用的
下面就可以看到这个核为我们提供的几组寄存器了,大体浏览一下,主要是看下面的功能介绍
先来看这两个寄存器
状态寄存器的第零位呢是TO位,TO从字面上理解就是 timeout,即时间溢出嘛,就是说当我们计数器计到0 的时候TO位会自动被置1,如果我们想要置0的话需要在状态寄存器里手动清0,这也就解释了为什么我们后面的代码每次进入中断以后为什么要清TO位了
RUN寄存器可以通过上面的解释大概说,RUN寄存器是用来指示定时器当前状态的一个数据位,即当寄存器工作的时候他被设置为1,否则设置为0 ,而且这一位不受你往状态寄存器写数据的影响。
控制寄存器呢大概就是开启关闭中断,开始计数或者停止计数之类的,大家可以自己消化英文,下面的period_n寄存器无非就是我们设置一个初值给它,大家也可以自己看
好了,需要看的也就这些了,下面就是我们根据我们从文档中获得的知识去编写我们的代码了
首先就是定义头文件么
system.h即我们硬件系统生成时产生的,里面定义了我们各种模块的基址啊,位宽啊之类的,下面就是我们要操作的七段数码管的基址及相关信息,,有了基址我们才能访问到数码所在的PIO,才能给他赋相应的值
其他那些头文件无非就是定时器核的寄存器头文件和数据类型头文件,还有IO操作的的一些函数的头文件
代码部分其实很简单,先是设置定时器中断,每进入一次中断将TO位清零,这里用到IOWR这个函数就是IO头文件里面定义的一个函数之一,用来对状态寄存器进行写数据,然后就是数据自加1,由于我们在sopc builder里面设置的时候设置的定时器定时单位是ms,所以我们计数1000次也就是1S了
主函数部分就是上来调用函数对数码管的位选和片选端初始化,将定时器中断打开,这些都没什么,要注意一点就是中断函数的注册,即 alt_irq_register(TIMER_IRQ,TIMER_BASE,handle_Timer0_interrupts);
就是我们给我的定时器一个定义,即我们要访问的定时器的基址,和要命名的名字,只有注册了中断,我们才能进入中断函数
好了,我们可以暂时跳过调试这一环节,因为工程比较简单,我们可以直接下板
在下板的过程中大家可能会遇到这样下载失败的情况
No Nios II target connection paths were located. Check connections and that a Nios II .sof is downloaded.
注意要按顺序来做,一般是先下载硬件配置文件.SOF,再对软件进行全编译,然后再下载一般就可以解决了
下面我们再来看UART这个核
先来总体来看一下这个核,大体上是说啊,这个UART核可以通过Avalon总线,作为Avalon总线的一个从机,来实现 FPGA与外围器件之间的 通信,这个核服从RS232时序协议,提供了可手动配置的波特率啊,奇偶校验位啊,停止位啊之类的数据位,控制数据流向啊,允许设计者调用系统给出的必要的一些函数啊之类的
再大体看一下功能框图
和之前的差不多,核的内部也是一系列寄存器,时钟进来之后进入divisor ,进行波特率的配置,然后通过控制寄存器啊,状态寄存器啊控制数据的读写功能,大体上是这个意思,具体还要看后面的寄存器映射视图
太过详细的东西这里不做介绍,留给大家慢慢消化,主要是来看一下它的寄存器映射视图
这里有6组寄存器,包括什么读写寄存器,控制寄存器,状态寄存器,波特率设置寄存器之类的,具体功能大同小异,简单来看一下
先来看一下读寄存器吧
大体上是说啊,这个读寄存器可以保存住来自RXD口输入过来的数据,当一个新的字符完全进入这个寄存器之后啊,那么状态寄存器的rrdy位将会自动置1,当读寄存器中的数据被读出的时候,rrdy位自动置0,如果当读寄存器中的数值还没有被读完,又有新字符输入的话,那么就会发生错误,状态寄存器的的roe位自动置1,新的数据将会覆盖原有的数据,对读寄存器写数据是没有影响的,大体上就是这个意思了
状态寄存器位数太多,这里就不一一讲解,大体上是说,当发生各种错误,例如什么奇偶校验位错误,停止位错误之类的,接收发送错误之类的,还有指示读写寄存器状态的,当有数据的时候置1,空的时候置0之类的,大家还是自己去理解
控制寄存器主要是控制中断,包括这种中断,这里我们只需要用到发送中断和接收中断,其他的大家可以自己看一下
代码部分呢主先来看一下初始化部分
由于我们设置的NIOS II处理器是32位的,因此我们的寄存器偏移地址根据基址也就知道了,即32/8 = 4,
先对控制寄存器操作,打开就收发送使能端,control寄存器是第5个寄存器,偏移为12
IOWR_16DIRECT(UART_BASE,12,0xc0);
状态寄存器清零,状态寄存器偏移为8
IOWR_16DIRECT(UART_BASE,8, 0x0);
注册中断函数
alt_irq_register(UART_IRQ,UART_BASE,Uart_rx_ISR);
接收中断部分主要就是将状态寄存器的数值读回,判断状态寄存器的rrdy位的状态,如果是1的话那么表明数据接收完毕,可以读了,然后将数据保存起来,将接收数据位置1表明我们已经收到数据了
|