給NIOS II CPU添加一顆澎湃的心——sysclk的使用


給NIOS II CPU添加一顆澎湃的心——系統時鍾的使用

 

本實驗介紹如何在Qsys中添加一個定時器作為NIOS II的心跳定時器,並在NIOS II中軟件編程使用該定時器。

將上一個實驗watchdog工程復制、粘貼並重命名為08_sysclk_timer。在Quartus II中打開該工程,然后點擊Qsys快捷圖標打開Qsys組件,如下圖所示:

在Qsys中,打開工程目錄(08_sysclk_timer)下的mysystem.qsys工程。

打開后,在左側的IP列表中輸入"timer",然后雙擊Interval Timer打開參數配置界面:

 

設置Period 為20,單位(Units)為ms,勾選上Fixed period。其他保持不變,點擊finish,則Timer被加入到了我們之前的已有的系統中。

 

修改timer的名字為sys_clk按照上一節看門狗的連線內容將所有信號連接到對應總線上。

添加的過程中下面信息欄會有報錯,暫時不用理會,等我們后面的操作完成后,報錯會自動消失。

然后在菜單欄中點擊【System】->【Assign Base Addresses】自動分配基地址。如下圖所示:

這個時候,應該所有錯誤都消失了。接着我們在菜單欄中點擊【Generate】->【Generate HDL】來生成Quartus II軟件可用的設計文件,如下圖所示:

生成完成之后,我們點擊finish即可關閉Qsys系統,同時Quartus II軟件彈出提示對話框,點擊OK以關閉。

因為本實驗中我們依然沒有添加需要引出到外部引腳的IP,引出在Quartus II中對系統的例化文件不需要進行任何修改,直接點擊"Start Compilation"(或者使用Ctrl + L組合鍵)對工程進行全編譯即可。

 

打開NIOS II IDE開發軟件。創建NIOS II軟件工程和板級支持包工程,選擇sopcinfo文件為"E:\easy_sopc\NiosOnlyExp\08_sysclk_timer\mysystem.sopcinfo"(自己根據具體路徑修改,注意切換路徑)。創建工程名字為sys_clk_test,選擇Hello World模版工程,然后點擊Finish。如下圖所示:

 

選中bsp工程(sys_clk_test_bsp),進入BSP Editor。

  1. 設置sys_clk_timer為"sys_clk",
  2. 設置timestamp_timer為none,
  3. 設置stdin、stdout、stderr、為uart_0。

其他不變,點擊generate,然后點擊exit退出。

 

復制上一個工程中軟件工程中的【hardware】文件夾到本工程下的【sys_clk_test】文件夾下,最好直接在電腦的文件管理器中進行(對Eclipse使用非常熟練的,請盡情使用拖拽添加的方式)。復制完成后,選中【sys_clk_test】工程,單擊右鍵,選擇【Refresh】,就可以在工程中看到【hardware】文件夾了。

接着我們添加【hardware】下的【inc】路徑到工程頭文件包含路徑中來。我們選中【sys_clk_test】工程,按下鍵盤組合鍵"ATRL + Enter"鍵打開【Properties】設置界面,選擇Nios II Application Properties下的Nios II Application Paths,在右側的Application include directories下,點擊Add按鈕,添加hardware/inc到包含路徑中。然后在彈出的對話框中點擊Yes,即可將此路徑添加為我們的頭文件包含路徑。如果用戶之后自己有其他的頭文件路徑需要添加,也是按照這種方法進行。

接着我們查看下Project References中是否勾選了sys_clk_test_bsp工程,如果沒有勾選的話,當工程關閉了重新打開時,工程有可能會報各種無法理解的錯誤(當一個workspace中有多個應用工程時)。這里我們需要確認這個選項被勾選上了。

然后我們再在C/C++ General下的Indexer下,勾選按照下圖中所示進行勾選。通過這樣勾選后,一般工程不管怎么折騰,都不會出現xxxx 'xxxx' could not be resolved的報錯了。

 

我們可以選擇將系統創建時默認的main函數所在文件hello_world.c文件改名為main.c,修改時只需要選中hello_world.c文件,單擊右鍵,選擇rename,然后改為main.c即可。

 

最后我們編寫main文件函數如下所示:

/******************************************************************

* 名:main.c

* 能:利用系統時鍾服務產生1s的周期性事件,並借此控制LED閃爍,

*

* 明:觀察LED閃爍效果。

******************************************************************/

#include <stdio.h>

#include "system.h" //包含基本的硬件描述信息

#include "led.h"

#include "sys/alt_alarm.h" //系統時鍾服務頭文件

#include "altera_avalon_pio_regs.h" //包含基本的IO寄存器信息

#include "alt_types.h" //Altera定義的數據類型

 

 

static alt_alarm alarm; //按調用API函數規定定義的變量

 

alt_u8 led_data;

 

LED_HANDLE hLED;

 

/******************************************************************

* 稱:my_alarm_callback

* 能:按調用規定系統時鍾回調函數,在該函數中實現用戶功能

* 入口參數:context,系統傳給回調函數的參數

* 出口參數:返回下一次的系統時鍾服務的周期值

******************************************************************/

alt_u32 my_alarm_callback(void* context) {

if (led_data == 0x0f) {

led_data = 0x00;

} else {

led_data = 0x0f;

}

LED_WriteData(hLED, led_data); //一次性控制所有LED的亮滅

return alt_ticks_per_second();

}

 

int main() {

//初始化LED

hLED = LED_Init(PIO_LED_BASE);

 

if (!hLED) {

// printf("Failed to init LED\n");

}

 

//熄滅所有LED

LED_Off(hLED, LED0 | LED1 | LED2 | LED3);

 

printf("test alarm...\n"); //打印提示信息

printf("alt_ticks_per_second() is %ld", alt_ticks_per_second());

 

//啟動系統時鍾服務

if (alt_alarm_start(&alarm, alt_ticks_per_second()

, my_alarm_callback, NULL) < 0) {

printf("No system clock available\n");

}

while (1)

; //等待時鍾事件發生

return 0;

}

這樣我們的軟件就編寫完成了,接着我們點擊鍵盤的組合鍵"CTRL + B"(或者依次點擊【Project】【Build All】)來編譯整個工程。

接着我們在Quartus II中打開配置下載窗口將sof文件下載到FPGA中,然后在Eclipse中點擊【Run】->【Run Configuration】,在彈出的界面中,雙擊左側的【Nios II Hardware】新建一個下載設置,將name更改為sys_clk_test,與工程名保持一致。右側Project Name選擇sys_clk_test,如下圖所示:

運行后在芯航線FPGA開發板上可以看到,4個LED每隔1秒閃爍一次。

 

補充閱讀:

不常用操作系統的工程師很少碰到alarm這個概念,在他們心目中 alarm實質上就是一個簡單的定時器周期中斷事件。在操作系統中,alarm卻是一個常用的概念,比如,當一個進程需要等待某個事件發生又不想永遠等待下去時.該進程會設置一個超時(timeout)值。當這個超時值到達時,操作系統會向進程發送一個alarm(警告信號),提醒進程不要再等待。在Nios II中,alarm也可以同樣理解為是打斷正在執行的流程,提醒系統不要等待的信號。

 

如何初始化系統時鍾服務?按照一般思路,先初始化定時器,再編寫定時器中斷服務程序。但是,在HAL層上進行應用程序開發不必如此,因為與硬件細節相關的初始化過程都由系統替用戶完成了。用戶在使用某項服務前,只需要開啟該項服務即可。

 

使用HAL提供的系統時鍾服務大致分為3個步驟:

  1. 調用alt_alarm_start()開啟系統時鍾服務,該函數位於BSP工程下的HAL\src 文件夾下的 alt_alarm_start. c 中。
  2. 按照格式要求編寫回調函數。該回調函數將實現用戶的功能。
  3. 調用alt_alarm_stop()關閉系統時鍾服務。該函數位於BSP工程下的HAL\src 文件夾下的 alt_tick. c中。

通過接口函數訪問定時器對象中的變量,為了達到隱藏信息的目的,Altera公司提 供了一個接口函數alt_ticks_per_second()供用戶獲得一個設定alarm服務周期為1 s的變量值。

系統時鍾服務API函數分析:

    開啟系統時鍾服務函數:alt_alarm__start()

函數原型:

int alt_alarm_start (

alt_alarm * alarm

alt_u32 nticks

alt_u32 (* callback) (void* context),

void* context)

函數功能:啟動系統時鍾服務。

輸入參數:

alarm——一個指向alt_alarm結構體類型的指針變量,用戶需要為每個系統時鍾服務創建一個alt_alarm類型變量。系統由該變量對系統時鍾服務進行維護。例如,用戶需要兩個系統時鍾服務,則需要定義alt_alarm alarm_1和alt_alarm alarm_2。

nticks——指示每隔nticks執行一次回調函數。如果定時器預設的中斷周期 為1 ms,則每個tick的時間間隔為1 ms。如果用戶希望獲得1 s—次的服務, 則nticks的輸人值為1 s/1 ms=1000。

Callback——用戶回調函數指針。

Context—-傳給回調函數的參數,一般為寄存器的地址,也可以是其它參數。 如果不需要傳遞參數則此處填入NULL。

②    用戶回調函數:my_alarm_callback()

函數原型:

alt_u32 my_alarm_callback (void * context)

輸人參數:

context —— alt_alarm_start()函數傳人的參數,函數名可以自己定義,但是函數原型一定要按上述格式書寫。

注意:不要在回調函數中實現復雜的功能,因為回調函數實際是定時器中斷服務函數的

一部分。

③停止系統時鍾服務函數alt_alarm_stop()

函數原型:

void alt_alarm_stop ( alt_alarm * alarm )

輸入參數:

alarm—— —個指向ah_alarm結構體類型的指針變量,用戶希望停止哪一個 系統時鍾服務,即把該時鍾服務對應的alarm變童傳給alt_alarm_stop()。 至此,通過HAL API函數使用系統時鍾服務的方法已總結完畢,其它API函數本文不再敘述,讀者可參閱Altera公司提供的API函數手冊。

最后留一個問題供讀者思考,如何開啟 3個系統時鍾服務,使得3個LED分別以2 Hz、l Hz和0.5 Hz的頻率閃爍?

 

如有更多問題,歡迎加入芯航線 FPGA 技術支持群交流學習:472607506

小梅哥

芯航線電子工作室


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM