【连载】【FPGA黑金开发板】NIOS II那些事儿--定时器实验(十一)
声明:本文为原创作品,版权归本博文作者所有,如需转载,请注明出处http://www.cnblogs.com/kingst/
简介
这一节,我们来讲讲有关定时器的内容。定时器,顾名思义,与时间有关系的一个器件,是用于对时钟周期进行计数并产生周期性中断信号的硬件外围设备。
用过单片机的人对定时器一定很熟悉,它主要用来处理周期性的事件,比如设置AD的采样频率,通过定时器产生周期性的定时器中等等。我发现,在很多资料中,都是介绍如何实现系统时钟,Timestamp,或者是看门狗的功能,却没有真正的介绍如何真正的去使用定时器本身具备的功能,这样很容易误导大家。有的人可能在学习这部分内容的时候遇到这样的问题,一个软核只能定义一个系统时钟,那如果我们想用到两个定时器怎么办呢?没办法,这种方式行不通,这就是系统时钟方式的弊端。为了解决这个问题,我们就要撇开系统时钟,真正的去了解NIOS本身定时器所具备的功能,它是可以像单片机的定时器一样来操作的。下面我们开始吧。
硬件开发
首先我们需要在NIOS软核中构建timer模块,如下图所示
点击进入后,如下图所示,红圈1处是用于预设硬件生成后的定时器周期, 也就是说这是个初始值,我们可以通过软件来改变定时器的周期。红圈2处是定时器计数器的大小,它分为32位和64位两种,需要根据你的定时器的周期来选择,我们在这里选择32位。红圈3处是定时器的几种预设方式,是为了实现不同的功能,我们在这里选择Full-featured,就是全功能。选择了这个选项,我们就可以修改周期,可以控制停止开始位。选择好以后,点击Finish完成设置。
我们在这里建立两个定时器,另一个方法相同。建立好以后,如下图所示
我们这个定时器试验需要4个LED来配合,所以还需要建立一个PIO模块,这里不具体讲了,前面已经讲过了。接下来,自动配置地址,自动配置中断,跟以前的都一样,下来就是编译,等待,编译,等待…
硬件部分就OK了,接下来我们开始软件开发了。
软件开发
打开NIOS 9.0 IDE,首先编译一下,快捷键Ctrl+b。编译完以后,我们可以去看看system.h文件,应该出现了下面内容
#define TIMER1_NAME "/dev/timer1" #define TIMER1_TYPE "altera_avalon_timer" #define TIMER1_BASE 0x00201000 …… #define TIMER2_NAME "/dev/timer2" #define TIMER2_TYPE "altera_avalon_timer" #define TIMER2_BASE 0x00201000 ……
大家在开发NIOS软件的时候,要注意一定要先看systetm.h文件,首先确定硬件已经正确加载,而且名字跟你设定的是一样的,避免在这个地方出现问题。
下面,我们来看看定时器的寄存器,这面的表格来自《n2cpu_Embedded Peripherals.pdf》的第24-6页。
通过这个表格,我们可以看到,定时器有状态寄存器,控制寄存器,定时器周期的高16位,低16位,还有snap的高16位,低16位,我们用到的是前3个寄存器,snap寄存器还没有弄到。
我们下面的实验是通过定时器1来控制4个LED的闪烁,而定时器2是用来改变定时器1的定时器周期,这样,我们就可以看到,4个LED的闪烁频率在不断的变化。
下面我们来看看源代码
/* * ================================================================= * Filename: main.c * Description: 定时器试验 * Version: 1.0.0 * Created: 2010.4.16 * Revision: none * Compiler: Nios II 9.0 IDE * Author: 马瑞 (AVIC) * Email: avic633@gmail.com * ================================================================= */ /*------------------------------------------------------------------ * Include *-----------------------------------------------------------------*/ #include <stdio.h> #include <sys/unistd.h> #include <io.h> #include <string.h> #include "system.h" #include "altera_avalon_pio_regs.h" #include "altera_avalon_timer_regs.h" #include "alt_types.h" #include "sys/alt_irq.h" #include "../inc/sopc.h" /*----------------------------------------------------------------- * Variable *-----------------------------------------------------------------*/ static void timer_init(void); //初始化中断 int i = 0,j = 0,flag; alt_u32 timer_prd[4] = {5000000, 10000000, 50000000, 100000000};//定时器的时钟数
//定时器的定时时间的计算方法是:定时器的时钟数/定时器的时钟周期
//我用的系统时钟是100MHz,所以,上面的四个的定时时间就为{0.05, 0.1, 0.5, 1} /* * === FUNCTION =================================================== * Name: main * Description: * ================================================================= */ int main(void) { //初始化Timer timer_init(); while(1); return 0; } /* * === FUNCTION =================================================== * Name: ISR_timer * Description: * ================================================================= */ static void ISR_timer1(void *context, alt_u32 id) { //控制流水灯闪烁,一共四个LED LED->DATA = 1<<i; i++; if(i == 4) i = 0; //清除Timer中断标志寄存器 IOWR_ALTERA_AVALON_TIMER_STATUS(TIMER1_BASE, 0x00); } /* * === FUNCTION =================================================== * Name: ISR_timer2 * Description: 通过定时器2来改变定时器1的周期,改变后需要重新启动定时器 * ================================================================= */ static void ISR_timer2(void *context, alt_u32 id) { //改变定时器1的周期 IOWR_ALTERA_AVALON_TIMER_PERIODL(TIMER1_BASE, timer_prd[j]); IOWR_ALTERA_AVALON_TIMER_PERIODH(TIMER1_BASE, timer_prd[j] >> 16); //重新启动定时器 IOWR_ALTERA_AVALON_TIMER_CONTROL(TIMER1_BASE, 0x07); //闪烁频率先高后低然后又变高 if(j == 0) flag = 0; if(j == 3) flag = 1; if(flag == 0){ j++; } else{ j--; } //清除中断标志位 IOWR_ALTERA_AVALON_TIMER_STATUS(TIMER2_BASE, 0); } /* * === FUNCTION =================================================== * Name: timer_init * Description: 定时器初始化 * ================================================================= */ void timer_init(void) //初始化中断 { //清除Timer1中断标志寄存器 IOWR_ALTERA_AVALON_TIMER_STATUS(TIMER1_BASE, 0x00); //设置Timer1周期,这里输入的是时钟周期数 IOWR_ALTERA_AVALON_TIMER_PERIODL(TIMER1_BASE,100000000); IOWR_ALTERA_AVALON_TIMER_PERIODH(TIMER1_BASE, 100000000 >> 16); //允许Timer1中断 IOWR_ALTERA_AVALON_TIMER_CONTROL(TIMER1_BASE, 0x07); //注册Timer1中断 alt_irq_register(TIMER1_IRQ, (void *)TIMER1_BASE, ISR_timer1); //清除Timer2中断标志寄存器 IOWR_ALTERA_AVALON_TIMER_STATUS(TIMER2_BASE, 0x00); //设置Timer2周期 IOWR_ALTERA_AVALON_TIMER_PERIODL(TIMER2_BASE,500000000); IOWR_ALTERA_AVALON_TIMER_PERIODH(TIMER2_BASE, 500000000 >> 16); //允许Timer2中断 IOWR_ALTERA_AVALON_TIMER_CONTROL(TIMER2_BASE, 0x07); //注册Timer2中断 alt_irq_register(TIMER2_IRQ, (void *)TIMER2_BASE, ISR_timer2); }
上面的方式是通过HAL提供的API实现的,当然我们也可以通过我以前提供的方法实现,建立一个结构体,直接对寄存器进行赋值,这样的方法我更喜欢,清晰而且结构完全自己控制。下面提供给大家一个这个结构体,实现方法大家自己实现吧,很简单,一句一句替换就可以了。建立结构体的内容根据下面的表格决定。
typedef struct{ union{ struct{ volatile unsigned long int TO :1; volatile unsigned long int RUN :1; volatile unsigned long int NC :30; }BITS; volatile unsigned long int WORD; }STATUS; union{ struct{ volatile unsigned long int ITO :1; volatile unsigned long int CONT :1; volatile unsigned long int START:1; volatile unsigned long int STOP :1; volatile unsigned long int NC :28; }BITS; volatile unsigned long int WORD; }CONTROL; volatile unsigned long int PERIODL; volatile unsigned long int PERIODH; volatile unsigned long int SNAPL; volatile unsigned long int SNAPH; }TIMER;
有了这个结构体就可以按照我之前给大家讲的方法实现定时器功能了。到此,我说的定时器的方法就讲完了,同时用两个定时器的问题也能够得以解决了。
下面,我给大家一个有关定时器系统时钟的例程作为参考,但我不建议大家使用,原因我已经说过了。这个函数实现的是每隔一秒点亮一个LED,一共4个LED。首先需要软件设置一下,如下图所示,进入系统库属性,在System clock timer选项框中选择你要的定时器。选择好以后,需要重新编译才行。
/* * ============================================================== * Filename: main.c * Description: * Version: 1.0.0 * Created: 2010.4.16 * Revision: none * Compiler: Nios II 9.0 IDE * Author: 马瑞 (AVIC) * Email: avic633@gmail.com * ============================================================== */ /*---------------------------------------------------------------- * Include *---------------------------------------------------------------*/ #include <stdio.h> #include <stdlib.h> #include "system.h" #include "altera_avalon_pio_regs.h" #include "alt_types.h" #include "sys/alt_irq.h" #include "../inc/sopc.h" #include "sys/alt_alarm.h" /*----------------------------------------------------------------- * Function prototypes *-----------------------------------------------------------------*/ alt_u32 my_alarm_callback(void *context); /*----------------------------------------------------------------- * Variable *-----------------------------------------------------------------*/ unsigned int i = 0; unsigned int alarm_flag; /*----------------------------------------------------------------- * Define *-----------------------------------------------------------------*/ #define INTEVAL_TICK 1 #define DEBUG /* * === FUNCTION ================================================== * Name: main * Description: * ================================================================= */ int main(void) { //调用API函数的变量 alt_alarm alarm; //启动系统时钟服务 if(alt_alarm_start(&alarm,INTEVAL_TICK,my_alarm_callback,NULL) < 0){ #ifdef DEBUG printf("Error: No system clock available\n"); #endif exit(0); } else{ #ifdef DEBUG printf("Success: System clock available\n"); #endif } while(1){ if(alarm_flag != 0){ LED->DATA = 1<<i; i++; if(i == 4) i = 0; alarm_flag = 0; } }; return 0; } /* === FUNCTION =================================================== * Name: my_alarm_callback * Description: * ================================================================= */ alt_u32 my_alarm_callback(void *context) { alarm_flag = 1; //INTEVAL_TICK的值决定下次进入定时器中断函数的时间 return INTEVAL_TICK; }
定时器的内容就讲完了,由于时间匆忙,可能其中有错误的地方,大家看到了给我留言,我确定后将加以修改,谢谢大家!