1. 程式人生 > >ZYNQ-7000私有定時器中斷

ZYNQ-7000私有定時器中斷

 

轉自:https://blog.csdn.net/RZJMPB/article/details/50812579

本片文章將在ZYNQ的純PS裡實現私有定時器中斷。每個一秒中斷一次,在中斷函式裡計數加1,通過串列埠列印輸出。

*本文所使用的開發板是Miz702(相容zedboard) 
PC 開發環境版本:Vivado 2015.2 Xilinx SDK 2015.2*

中斷原理

中斷對於保證任務的實時性非常必要,在ZYNQ裡集成了中斷控制器GIC(Generic Interrupt Controller).GIC可以接受I/O外設中斷IOP和PL中斷,將這些中斷髮給CPU。 
中斷體系結構框圖圖下: 
這裡寫圖片描述

軟體中斷(SGI)

SGI通過寫ICDSGIR暫存器產生SGI.

共享中斷SPI

通過PS和PL內各種I/O和儲存器控制器產生。

私有中斷(PPI)

包含:全域性定時器,私有看門狗定時器,私有定時器以及來自PL的FIQ/IRQ。本文主要介紹PPI,其它的請參考官方手冊ug585_Zynq_7000_TRM.pdf。 
ZYNQ每個CPU連結5個私有外設中斷,所有中斷的觸發型別都是固定不變的。並且來自PL的快速中斷訊號FIQ和中斷訊號IRQ反向,然後送到中斷控制器因此儘管在ICDICFR1暫存器內反應的他們是低電平觸發,但是PS-PL介面中為高電平觸發。如圖所示: 
這裡寫圖片描述

私有定時器

zynq中每個ARM core都有自己的私有定時器,私有定時器的工作頻率為CPU的一半,比如Miz702或者zedboard的ARM工作頻率為666MHZ,則私有定時器的頻率為333MHz. 
私有定時器的特性如下: 
(1)32為計數器,達到零時產生一箇中斷 
(2)8位預分頻計數器,可以更好的控制中斷週期 
(3)可配置一次性或者自動重載入模式 
(4)定時器時間可以通過下式計算: 
定時時間 = [(預分頻器的值 + 1) (載入值 + 1)]/定時器頻率

搭建硬體系統工程:

配置ZYNQ PS

把ZYNQ配置為只保留UART1,然後點選OK如圖所示 
這裡寫圖片描述


點選Run Connection Automation,取消掉Apply Board Preset,如圖 
這裡寫圖片描述
最終的框圖很簡潔,如圖: 
這裡寫圖片描述

建立軟體工程

建立一個Hello World工程 
把Helloworld.c的程式碼修改如下:

#include <stdio.h>
#include "platform.h"
#include "xadcps.h"

#include "xil_types.h"
#include "Xscugic.h"
#include "Xil_exception.h"
#include "xscutimer.h"

//timer info
#define TIMER_DEVICE_ID     XPAR_XSCUTIMER_0_DEVICE_ID
#define INTC_DEVICE_ID      XPAR_SCUGIC_SINGLE_DEVICE_ID
#define TIMER_IRPT_INTR     XPAR_SCUTIMER_INTR

//#define TIMER_LOAD_VALUE  0x0FFFFFFF
#define TIMER_LOAD_VALUE    0x13D92D3F

static XAdcPs  XADCMonInst; //XADC
static XScuGic Intc; //GIC
static XScuTimer Timer;//timer

static void SetupInterruptSystem(XScuGic *GicInstancePtr,
        XScuTimer *TimerInstancePtr, u16 TimerIntrId);

static void TimerIntrHandler(void *CallBackRef);

int main()
{
     XScuTimer_Config *TMRConfigPtr;     //timer config
     printf("------------START-------------\n");
     init_platform();
     //
     //私有定時器初始化
     TMRConfigPtr = XScuTimer_LookupConfig(TIMER_DEVICE_ID);
     XScuTimer_CfgInitialize(&Timer, TMRConfigPtr,TMRConfigPtr->BaseAddr);
     XScuTimer_SelfTest(&Timer);
     //載入計數週期,私有定時器的時鐘為CPU的一般,為333MHZ,如果計數1S,載入值為1sx(333x1000x1000)(1/s)-1=0x13D92D3F
     XScuTimer_LoadTimer(&Timer, TIMER_LOAD_VALUE);
     //自動裝載
     XScuTimer_EnableAutoReload(&Timer);
     //啟動定時器
     XScuTimer_Start(&Timer);


     //set up the interrupts
     SetupInterruptSystem(&Intc,&Timer,TIMER_IRPT_INTR);

     while(1){
     }

     return 0;
}

void SetupInterruptSystem(XScuGic *GicInstancePtr,
        XScuTimer *TimerInstancePtr, u16 TimerIntrId)
{

        XScuGic_Config *IntcConfig; //GIC config
        Xil_ExceptionInit();
        //initialise the GIC
        IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
        XScuGic_CfgInitialize(GicInstancePtr, IntcConfig,
                        IntcConfig->CpuBaseAddress);
        //connect to the hardware
        Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
                    (Xil_ExceptionHandler)XScuGic_InterruptHandler,
                    GicInstancePtr);
        //set up the timer interrupt
        XScuGic_Connect(GicInstancePtr, TimerIntrId,
                        (Xil_ExceptionHandler)TimerIntrHandler,
                        (void *)TimerInstancePtr);
        //enable the interrupt for the Timer at GIC
        XScuGic_Enable(GicInstancePtr, TimerIntrId);
        //enable interrupt on the timer
        XScuTimer_EnableInterrupt(TimerInstancePtr);
        // Enable interrupts in the Processor.
        Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
    }

static void TimerIntrHandler(void *CallBackRef)
{

    static int sec = 0;   //計數
    XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;
    XScuTimer_ClearInterruptStatus(TimerInstancePtr);
    sec++;
    printf(" %d Second\n\r",sec);  //每秒列印輸出一次

}