聲明:本文為原創作品,著作權歸本博文作者
所有,如需轉載,請註明出處
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; }
定時器的內容就講完了,由於時間匆忙,可能其中有錯誤的地方,大家看到了給我留言,我確定後將加以修改,謝謝大家!