學(xué)習(xí)內(nèi)容
本文首先介紹了ZYNQ的定時(shí)器的相關(guān)內(nèi)容,并學(xué)習(xí)使用ZYNQ芯片中的定時(shí)器進(jìn)行操作測(cè)試。
開(kāi)發(fā)環(huán)境
vivado 18.3&SDK,PYNQ-Z2開(kāi)發(fā)板。
定時(shí)器簡(jiǎn)介
介紹
ZYNQ有兩個(gè)Cortex-A9處理器,每個(gè)Cortex-A9處理器都有自己的專用32位計(jì)時(shí)器和32位看門(mén)狗計(jì)時(shí)器。兩個(gè)都處理器共享一個(gè)全局64位計(jì)時(shí)器。這些計(jì)時(shí)器始終以1/2的CPU頻率計(jì)時(shí)(CPU_3x2x)。在系統(tǒng)層級(jí),有一個(gè)24位看門(mén)狗定時(shí)器和兩個(gè)16位三重定時(shí)器/計(jì)數(shù)器。系統(tǒng)看門(mén)狗定時(shí)器的時(shí)鐘頻率為CPU頻率(CPU_1x)的1/4或1/6,時(shí)鐘也可以由來(lái)自MIO引腳或PL的外部信號(hào)。兩個(gè)三重計(jì)時(shí)器/計(jì)數(shù)器時(shí)鐘頻率為在CPU頻率(CPU_1x)的1/4或1/6,可以用于計(jì)算來(lái)自MIO引腳或來(lái)自PL的脈沖寬度。
系統(tǒng)框圖
下圖為ZYNQ內(nèi)部的定時(shí)器的系統(tǒng)框圖。
在圖中顯示,定時(shí)器部分包括系統(tǒng)看門(mén)狗、CPU內(nèi)部的看門(mén)狗定時(shí)器,CPU私有的定時(shí)器,全局定時(shí)器,三重定時(shí)器。其中所有的定時(shí)器都可以觸發(fā)中斷控制器進(jìn)行中斷控制的操作,看門(mén)狗定時(shí)器(無(wú)論是系統(tǒng) 的還是CPU內(nèi)部的)都可以對(duì)系統(tǒng)進(jìn)行一個(gè)復(fù)位操作。
CPU私有定時(shí)器和看門(mén)狗定時(shí)器
關(guān)于CPU私有定時(shí)器和看門(mén)狗定時(shí)器均具有以下功能:
- 32位計(jì)數(shù)器,在達(dá)到零時(shí)會(huì)產(chǎn)生中斷
- 八位預(yù)分頻器,可以更好地控制中斷周期
- 可配置的單次或自動(dòng)重裝模式
- 計(jì)數(shù)器的可配置起始值
- 定時(shí)器和看門(mén)狗復(fù)位信號(hào)發(fā)送到PS復(fù)位子系統(tǒng)
- 所有私有計(jì)時(shí)器和看門(mén)狗計(jì)時(shí)器始終以CPU頻率(CPU_3x2x)的1/2計(jì)時(shí)。
下圖為CPU私有定時(shí)器和看門(mén)狗定時(shí)器寄存器表:
全局定時(shí)器(GT)
全局定時(shí)器是具有自動(dòng)遞增功能的64位遞增計(jì)數(shù)器。全局計(jì)時(shí)器在與專用計(jì)時(shí)器相同的地址空間中映射到內(nèi)存中。全局計(jì)時(shí)器僅在復(fù)位時(shí)以安全狀態(tài)訪問(wèn)。 所有Cortex-A9處理器均可訪問(wèn)全局計(jì)時(shí)器。每個(gè)Cortex-A9處理器都有一個(gè)64位比較器,用于在全局計(jì)時(shí)器達(dá)到比較器值時(shí)聲明一個(gè)私有中斷。全局定時(shí)器始終以CPU頻率(CPU_3x2x)的1/2計(jì)時(shí)。全局定時(shí)器(GT)寄存器功能表如下圖:
系統(tǒng)看門(mén)狗定時(shí)器(SWDT)
除了兩個(gè)CPU私有看門(mén)狗定時(shí)器之外,還有一個(gè)系統(tǒng)看門(mén)狗定時(shí)器(SWDT),可以在系統(tǒng)發(fā)生故障時(shí),如PS PLL鎖相失敗,此時(shí)看門(mén)狗可以產(chǎn)生一個(gè)復(fù)位信號(hào)讓程序重啟,從而保證系統(tǒng)正常運(yùn)行。與CPU私有看門(mén)狗定時(shí)器不同的是,SWDT的時(shí)鐘可以來(lái)自于外部設(shè)備或者PL端,用于提供一個(gè)復(fù)位信號(hào)輸出到PL端口或者外部設(shè)備。
特征
SWDT的主要功能如下:
- 內(nèi)部24位計(jì)數(shù)器。
- 可選時(shí)鐘輸入,時(shí)鐘信號(hào)可以來(lái)自:1. 內(nèi)部PS總線時(shí)鐘(CPU_1x);2. 內(nèi)部時(shí)鐘(來(lái)自PL);3. 外部時(shí)鐘(來(lái)自MIO)。
- 計(jì)時(shí)超時(shí)時(shí),可以進(jìn)行系統(tǒng)中斷(PS)和系統(tǒng)重置(PS,PL,MIO)。
- 可編程超時(shí)時(shí)間:1. 超時(shí)范圍32,760至68,719,476,736個(gè)時(shí)鐘周期(在100 MHz時(shí)可配置范圍為330 µs至687.2s)。
- 超時(shí)時(shí),可編程的輸出信號(hào)持續(xù)時(shí)間:系統(tǒng)中斷脈沖(4、8、16或32個(gè)時(shí)鐘周期(CPU_1x時(shí)鐘))。
系統(tǒng)看門(mén)狗定時(shí)器寄存器功能表如圖:
編程指南
系統(tǒng)看門(mén)狗定時(shí)器使能順序如下:
- 使用slcr.WDT_CLK_SEL [SEL]位選擇時(shí)鐘輸入源 :確保禁用SWDT(swdt.MODE [WDEN] = 0),并且將時(shí)鐘輸入源設(shè)置為選定的正在運(yùn)行,然后繼續(xù)此步驟。更改時(shí)鐘輸入源時(shí),啟用SWDT會(huì)導(dǎo)致無(wú)法預(yù)測(cè)的行為。時(shí)鐘輸入源更改為非運(yùn)行時(shí)鐘導(dǎo)致APB訪問(wèn)掛起。
- 設(shè)置超時(shí)時(shí)間(計(jì)數(shù)器控制寄存器) :swdt.CONTROL [CKEY]字段必須為0x248,才能寫(xiě)入此寄存器。
- 啟用計(jì)數(shù)器;使能輸出脈沖 ;設(shè)置輸出脈沖長(zhǎng)度(零模式寄存器):swdt.MODE [ZKEY]字段必須為0xABC,才能寫(xiě)入此寄存器。確保IRQLN符合指定的最小值。
- 要使用其他設(shè)置運(yùn)行SWDT,請(qǐng)首先禁用定時(shí)器(swdt.MODE [ZKEY]位)。然后重復(fù)上述步驟。
三重計(jì)時(shí)器(TTC)
TTC包含三個(gè)獨(dú)立的計(jì)時(shí)器/計(jì)數(shù)器。PS端中有兩個(gè)TTC模塊,總共六個(gè)計(jì)時(shí)器/計(jì)數(shù)器??梢允褂?nic301_addr_region_ctrl_registers.security_apb [ttc1_apb]寄存器位將TTC 1控制器配置為安全或非安全模式。TTC控制器中的三個(gè)計(jì)時(shí)器具有相同的安全狀態(tài)。
特征
每個(gè)三重計(jì)時(shí)器/計(jì)數(shù)器都具有以下特征:
- 三個(gè)獨(dú)立的16位預(yù)分頻器和16位向上/向下計(jì)數(shù)器。
- 可選時(shí)鐘輸入,來(lái)自:1. 內(nèi)部PS總線時(shí)鐘(CPU_1x);2. 內(nèi)部時(shí)鐘(來(lái)自PL);3. 外部時(shí)鐘(來(lái)自MIO)。•每個(gè)計(jì)數(shù)器有一個(gè)中斷。•可以產(chǎn)生溢出中斷,定時(shí)中斷或計(jì)數(shù)中斷,可編程初始值。•可以生成通過(guò)MIO到PL的波形輸出(例如PWM)。
三重計(jì)時(shí)器/計(jì)數(shù)器寄存器功能表如圖:
計(jì)數(shù)器編程啟用順序
- 選擇時(shí)鐘輸入源,設(shè)置預(yù)分頻值。(slcr.MIO_MUX_SEL寄存器,TTC時(shí)鐘控制)在繼續(xù)執(zhí)行此操作之前,請(qǐng)確保已禁用TTC(ttc.Counter_Control_x [DIS] = 1)。
- 設(shè)置間隔值(間隔寄存器)。 此步僅適用于間隔模式。(可選不配置)
- 設(shè)置匹配值(匹配寄存器)。 如果要啟用匹配,可以配置此操作。(可選不配置)
- **使能中斷(中斷使能寄存器)。**如果要啟用中斷,可以配置此操作。(可選不配置)
- 啟用/禁用波形輸出,啟用/禁用匹配,設(shè)置計(jì)數(shù)方向,設(shè)置模式,使能計(jì)數(shù)器(TTC計(jì)數(shù)器控制寄存器)。 此步驟完成后將啟動(dòng)計(jì)數(shù)器。
計(jì)數(shù)器編程停止順序
- 讀回計(jì)數(shù)器控制寄存器的值。
- 將DIS位設(shè)置為1,同時(shí)保留其他位。
- 寫(xiě)回計(jì)數(shù)器控制寄存器。
計(jì)數(shù)器編程重啟順序
- 讀回計(jì)數(shù)器控制寄存器的值。
- 將RST位設(shè)置為1,同時(shí)保留其他位。
- 寫(xiě)回計(jì)數(shù)器控制寄存器。
事件計(jì)時(shí)器啟用序列
- 選擇外部脈沖源(slcr.MIO_MUX_SEL寄存器)。選定外部待測(cè)脈沖。
- 設(shè)置溢出處理,選擇外部脈沖電平,啟用事件計(jì)時(shí)器(事件控制計(jì)時(shí)器寄存器)。此步驟開(kāi)始測(cè)量外部所選電平(高或低)的寬度脈沖。
- 使能中斷(中斷使能寄存器)。如果要啟用中斷,可以配置此操作。(可選不配置)
- 讀取測(cè)量的寬度(事件寄存器)。
中斷清除和應(yīng)答序列
- 讀取中斷寄存器:讀取時(shí)清除中斷寄存器中的所有位。
系統(tǒng)框圖
本次工程系統(tǒng)框圖如下圖所示。
調(diào)用UART、TIMER、GPIO資源進(jìn)行開(kāi)發(fā)設(shè)計(jì),UART串口引腳直接連接到MIO,GPIO引腳連接到PL端的EMIO引腳,由ARM核進(jìn)行配置定時(shí)器中斷操作,實(shí)現(xiàn)定時(shí)中斷進(jìn)行流水燈操作。
硬件平臺(tái)搭建
新建工程,創(chuàng)建 block design。添加ZYNQ7 ip,根據(jù)本次工程需要對(duì)IP進(jìn)行配置。勾選本次工程使用的資源。
硬件系統(tǒng)構(gòu)建完成如下:
然后我們進(jìn)行g(shù)enerate output product 然后生成HDL封裝。這里用到了UART和GPIO,所以需要進(jìn)行管腳分配。這里使用的是PYNQZ2開(kāi)發(fā)板,該板卡可直接引用以下約束文件:
#LEDs
set_property -dict { PACKAGE_PIN R14 IOSTANDARD LVCMOS33 } [get_ports { EMIO_LED_tri_io[0] }]; #IO_L6N_T0_VREF_34 Sch=led[0]
set_property -dict { PACKAGE_PIN P14 IOSTANDARD LVCMOS33 } [get_ports { EMIO_LED_tri_io[1] }]; #IO_L6P_T0_34 Sch=led[1]
set_property -dict { PACKAGE_PIN N16 IOSTANDARD LVCMOS33 } [get_ports { EMIO_LED_tri_io[2] }]; #IO_L21N_T3_DQS_AD14N_35 Sch=led[2]
set_property -dict { PACKAGE_PIN M14 IOSTANDARD LVCMOS33 } [get_ports { EMIO_LED_tri_io[3] }]; #IO_L23P_T3_35 Sch=led[3]
完成約束后,進(jìn)行綜合和布局布線,生成bit流,然后點(diǎn)擊導(dǎo)出硬件資源(包含bit流文件),接著launch SDK。
SDK軟件部分
打開(kāi)SDK后,新建application project。在system.mss中可以打開(kāi)相關(guān)參考文檔輔助設(shè)計(jì)。
可以選擇timer中斷的例程進(jìn)行參考設(shè)計(jì),導(dǎo)入uart_intr_example例程模板,
在main.c中輸入以下代碼:
#include
#include "xparameters.h"
#include "xscutimer.h"
#include "xscugic.h"
#include "xgpiops.h"
#include "xil_exception.h"
#include "xil_printf.h"
//聲明定義
#define GPIO_ID XPAR_PS7_GPIO_0_DEVICE_ID
#define TIMER_ID XPAR_PS7_SCUTIMER_0_DEVICE_ID
#define INTR_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define TIMER_IRPT_INTR XPAR_SCUTIMER_INTR
#define LOAD_VALUE 0X9AF8D9F//0.5s流水 系統(tǒng)時(shí)鐘650M 定時(shí)器時(shí)鐘325M 周期3ns
#define LED0 54
#define LED1 55
#define LED2 56
#define LED3 57
//聲明示例結(jié)構(gòu)體
XGpioPs GpioPs;
XScuTimer Timer;
XScuGic ScuGic;
//函數(shù)定義
void Emio_init();
void Timer_init();
void Timer_intr_init(XScuGic *intr, XScuTimer *time);
void Timer_IntrHandler(void *CallBackRef);
int main(){
//EMIO初始化
Emio_init();
//初始化定時(shí)器
Timer_init();
//初始化中斷
Timer_intr_init(&ScuGic,&Timer);
//啟動(dòng)定時(shí)器
XScuTimer_Start(&Timer);
while(1);
return0;
}
void Emio_init(){
XGpioPs_Config *XGpioPs_Cfg;
XGpioPs_Cfg = XGpioPs_LookupConfig(GPIO_ID);
XGpioPs_CfgInitialize(&GpioPs,XGpioPs_Cfg,XGpioPs_Cfg->BaseAddr);
XGpioPs_SetDirectionPin(&GpioPs,LED0,0x01);
XGpioPs_SetOutputEnablePin(&GpioPs,LED0,0x01);
XGpioPs_SetDirectionPin(&GpioPs,LED1,0x01);
XGpioPs_SetOutputEnablePin(&GpioPs,LED1,0x01);
XGpioPs_SetDirectionPin(&GpioPs,LED2,0x01);
XGpioPs_SetOutputEnablePin(&GpioPs,LED2,0x01);
XGpioPs_SetDirectionPin(&GpioPs,LED3,0x01);
XGpioPs_SetOutputEnablePin(&GpioPs,LED3,0x01);
}
void Timer_init(){
int Status;
XScuTimer_Config *ConfigPtr;
//初始化定時(shí)器
ConfigPtr = XScuTimer_LookupConfig(TIMER_ID);
XScuTimer_CfgInitialize(&Timer, ConfigPtr,ConfigPtr->BaseAddr);
//定時(shí)器自檢測(cè)
Status = XScuTimer_SelfTest(&Timer);
if (Status != XST_SUCCESS) {
printf("timer selftest failed!\n");
}
printf("timer selftest success!\n");
//裝載初值
XScuTimer_LoadTimer(&Timer, LOAD_VALUE);
//使能自動(dòng)裝載模式
XScuTimer_EnableAutoReload(&Timer);
}
void Timer_intr_init(XScuGic *intr, XScuTimer *time){
XScuGic_Config *IntcConfig;
//初始化定時(shí)器中斷
IntcConfig = XScuGic_LookupConfig(INTR_DEVICE_ID);
XScuGic_CfgInitialize(intr,IntcConfig,IntcConfig->CpuBaseAddress);
//初始化中斷異常函數(shù)
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
intr);
Xil_ExceptionEnable();
//設(shè)置中斷服務(wù)函數(shù)
XScuGic_Connect(intr, TIMER_IRPT_INTR,
(Xil_ExceptionHandler)Timer_IntrHandler,
(void *)time);
//使能中斷控制器
XScuGic_Enable(intr, TIMER_IRPT_INTR);
//使能定時(shí)器
XScuTimer_EnableInterrupt(time);
}
//中斷處理函數(shù)
void Timer_IntrHandler(void *CallBackRef){
staticchar led_status=0x01;
XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;
if(XScuTimer_IsExpired(TimerInstancePtr)){
led_status = led_status<<1;
if(led_status == 0X10)
led_status =0x01;
XGpioPs_WritePin(&GpioPs,LED0,led_status);
XGpioPs_WritePin(&GpioPs,LED1,led_status>>1);
XGpioPs_WritePin(&GpioPs,LED2,led_status>>2);
XGpioPs_WritePin(&GpioPs,LED3,led_status>>3);
XScuTimer_ClearInterruptStatus(TimerInstancePtr);
}
}
部分代碼講解
在主函數(shù)中引用了Emio_init();
、Timer_init();
、Timer_intr_init(&ScuGic,&Timer);
分別完成初始化GPIO操作,初始化定時(shí)器,初始化中斷定時(shí)器等操作。最后用XScuTimer_Start(&Timer);
啟動(dòng)定時(shí)器。
對(duì)于定時(shí)器初始化設(shè)置主要要對(duì)計(jì)數(shù)模式進(jìn)行設(shè)置,對(duì)初始值進(jìn)行裝載配置 也就是調(diào)用下面的函數(shù):
//裝載初值
XScuTimer_LoadTimer(&Timer, LOAD_VALUE);
//使能自動(dòng)裝載模式
XScuTimer_EnableAutoReload(&Timer);
這里L(fēng)OAD_VALUE表示需要計(jì)時(shí)或者計(jì)數(shù)的次數(shù),因?yàn)檫@里的代碼實(shí)現(xiàn)的是led的0.5s定時(shí)中斷,系統(tǒng)CPU時(shí)鐘為650M,所以定時(shí)器的時(shí)鐘頻率為325M,也就是周期為3.07ns,計(jì)算0.5s下的計(jì)數(shù)次數(shù)得到這里需要裝載的初值。(也即為0X9AF8D9F)
Reference
- UG585
- 正點(diǎn)原子ZYNQ開(kāi)發(fā)視頻