天天看點

【連載】【FPGA黑金開發闆】NIOS II那些事兒--定時器實驗(十一)

聲明:本文為原創作品,版權歸本博文作者所有,如需轉載,請注明出處http://www.cnblogs.com/kingst/

【連載】【FPGA黑金開發闆】NIOS II那些事兒--定時器實驗(十一)

簡介

      這一節,我們來講講有關定時器的内容。定時器,顧名思義,與時間有關系的一個器件,是用于對時鐘周期進行計數并産生周期性中斷信号的硬體外圍裝置。

      用過單片機的人對定時器一定很熟悉,它主要用來處理周期性的事件,比如設定AD的采樣頻率,通過定時器産生周期性的定時器中等等。我發現,在很多資料中,都是介紹如何實作系統時鐘,Timestamp,或者是看門狗的功能,卻沒有真正的介紹如何真正的去使用定時器本身具備的功能,這樣很容易誤導大家。有的人可能在學習這部分内容的時候遇到這樣的問題,一個軟核隻能定義一個系統時鐘,那如果我們想用到兩個定時器怎麼辦呢?沒辦法,這種方式行不通,這就是系統時鐘方式的弊端。為了解決這個問題,我們就要撇開系統時鐘,真正的去了解NIOS本身定時器所具備的功能,它是可以像單片機的定時器一樣來操作的。下面我們開始吧。

硬體開發

      首先我們需要在NIOS軟核中建構timer子產品,如下圖所示

【連載】【FPGA黑金開發闆】NIOS II那些事兒--定時器實驗(十一)

點選進入後,如下圖所示,紅圈1處是用于預設硬體生成後的定時器周期, 也就是說這是個初始值,我們可以通過軟體來改變定時器的周期。紅圈2處是定時器計數器的大小,它分為32位和64位兩種,需要根據你的定時器的周期來選擇,我們在這裡選擇32位。紅圈3處是定時器的幾種預設方式,是為了實作不同的功能,我們在這裡選擇Full-featured,就是全功能。選擇了這個選項,我們就可以修改周期,可以控制停止開始位。選擇好以後,點選Finish完成設定。

【連載】【FPGA黑金開發闆】NIOS II那些事兒--定時器實驗(十一)

我們在這裡建立兩個定時器,另一個方法相同。建立好以後,如下圖所示

【連載】【FPGA黑金開發闆】NIOS II那些事兒--定時器實驗(十一)

我們這個定時器試驗需要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頁。

【連載】【FPGA黑金開發闆】NIOS II那些事兒--定時器實驗(十一)

通過這個表格,我們可以看到,定時器有狀态寄存器,控制寄存器,定時器周期的高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:  [email protected]  
* =================================================================
 */



/*------------------------------------------------------------------
 *  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實作的,當然我們也可以通過我以前提供的方法實作,建立一個結構體,直接對寄存器進行指派,這樣的方法我更喜歡,清晰而且結構完全自己控制。下面提供給大家一個這個結構體,實作方法大家自己實作吧,很簡單,一句一句替換就可以了。建立結構體的内容根據下面的表格決定。

【連載】【FPGA黑金開發闆】NIOS II那些事兒--定時器實驗(十一)
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選項框中選擇你要的定時器。選擇好以後,需要重新編譯才行。

【連載】【FPGA黑金開發闆】NIOS II那些事兒--定時器實驗(十一)
/*
 * ==============================================================
*       Filename:  main.c
*   Description:  
*        Version:  1.0.0
 *        Created:  2010.4.16
 *       Revision:  none
 *       Compiler:  Nios II 9.0 IDE
*         Author:  馬瑞 (AVIC)
 *			Email:  [email protected]  
* ==============================================================
 */


/*----------------------------------------------------------------
 *  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;        
}      

    定時器的内容就講完了,由于時間匆忙,可能其中有錯誤的地方,大家看到了給我留言,我确定後将加以修改,謝謝大家!

【連載】【FPGA黑金開發闆】NIOS II那些事兒--定時器實驗(十一)