ZYNQ7010常用基礎外設綜合應用詳講

本文設計了一個ZYNQ7010的裸跑工程,包含了1路定時器中斷、2路PL中斷、自制IP(PS與PL寄存器交互)、EMIO和MIO功能,方便裁剪。

 

詳講工程設計步驟和代碼,並給出一些值得注意的細節和Trick。

 

在ZYNQ7010中通過IP核的方式實現PS與PL的協同工作。
        (1).在“Create Block Design”中添加IP:ZYNQ7 Processing System,設置Peripheral I/O Pins中的UART1、DDR Configuration中的DDR和Interrupt中的PL-PS Interrupt Ports。

如下圖:

        (2).添加IP:AXI GPIO,設置如下:

        (3).添加IP:concat,設置如下:

        (4).自制IP核:Tool->Create and Package IP->Create AXI4 Peripheral->命名example_button_ip->Number of Registers=2-> Add IP to repository

    IP Catalog->example_button_ip->Edit in IP Package

        在定層文件中加入接口並在子模塊中例化接口:

        在子模塊中:

然後添加代碼:

再“run implementation”

雙擊“IP-XAT”下的“component.xml”

Ports and Interfaces->merge changes from ports and interfaces Wizard

如果在IP設計中添加了別的.v文件,則在此處還要處理File Groups,直至Packaging Steps中除了Review and Package項外全爲對號。

Review and Package->Re-Package IP

        (5).回到原工程,添加剛纔自制的IP->Run Connection Automation->Run Block Automation

Concat的dout連接ZYNQ7的IRQ_F2P。對concat的In0、In1和自制IP的button和led右鍵make external。最後對引腳重命名後Regenerate Layout。至此IP設計完成,如下圖:

        (6).Generate output products->Create HDL wrapper,檢查頂層文件引腳是否齊全:

        (7).在引腳約束文件中約束這些引腳:

        (8).run implemtation->generate Bitstream->export hardwar->launch SDK->file->Application Project->Hello world

        (9).SDK中的代碼如下,給出了較爲詳細的註釋

//hello world.c

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

int main()
{
    init_platform();

    init_key();//初始化按鍵
    init_timer();//初始化定時器
    init_led();//初始化PLgpio用於led顯示
    init_PSgpio();//初始化PSgpio用於led顯示

    while(1)
    {
    	PSgpio_task();//PSgpio控制的led亮滅
    }

    cleanup_platform();
    return 0;
}
//led.c
#include "xgpio.h"

#define led_ID  XPAR_AXI_GPIO_0_DEVICE_ID
//添加ip核axi gpio後會在xparameters.h中自動定義XPAR_AXI_GPIO_0_DEVICE_ID

XGpio   LED;//GPIO的結構體

void init_led()
{
	int status;
	// 初始化按鍵
	status = XGpio_Initialize(&LED, led_ID);//根據外設ID,找到其寄存器地址
	if(status != XST_SUCCESS) return XST_FAILURE;

	XGpio_SetDataDirection(&LED, 1, 0x00);//設置LED IO的方向爲輸出
	XGpio_DiscreteWrite(&LED,1,0x03);//設置LED 燈熄滅
}

void open_led()
{
	 XGpio_DiscreteWrite(&LED,1,0x00);//設置LED 燈亮
}

void close_led()
{
	 XGpio_DiscreteWrite(&LED,1,0x03);//設置LED 燈熄滅
}
//timer.c
#include <stdio.h>
#include "xadcps.h"
#include "xil_types.h"
#include "Xscugic.h"
#include "Xil_exception.h"
#include "xscutimer.h"

#define TIMER_DEVICE_ID     XPAR_XSCUTIMER_0_DEVICE_ID//定時器外設ID
#define INTC_DEVICE_ID      XPAR_PS7_SCUGIC_0_DEVICE_ID//中斷外設ID
#define TIMER_IRPT_INTR     XPAR_SCUTIMER_INTR//定時器中斷源
#define TIMER_LOAD_VALUE    0x13D92D3F

extern XScuGic INTCInst; //一個工程裏的XScuGic要使用同一個,兩個GPIO中斷和定時器中斷使用同一個
static XScuTimer Timer;//timer
static void SetupInterruptSystem(XScuGic *GicInstancePtr,
        XScuTimer *TimerInstancePtr, u16 TimerIntrId);
static void TimerIntrHandler(void *CallBackRef);

void init_timer()
{
     XScuTimer_Config *TMRConfigPtr;     //timer config
     TMRConfigPtr = XScuTimer_LookupConfig(TIMER_DEVICE_ID);//根據外設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(&INTCInst,&Timer,TIMER_IRPT_INTR);
}

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);

	XScuGic_Enable(GicInstancePtr, TimerIntrId);//enable the interrupt for the Timer at GIC
	XScuTimer_EnableInterrupt(TimerInstancePtr);//enable interrupt on the timer
	Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);// Enable interrupts in the Processor.
}

static void TimerIntrHandler(void *CallBackRef)
{
    static int sec = 0;   //計數
    XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;
    XScuTimer_ClearInterruptStatus(TimerInstancePtr);
    printf("sec = %d\n\r",sec++);  //每秒打印輸出一次
}
//button_ip.c

#include "example_button_ip.h"//定義了PL中 寄存器的偏移地址、寫寄存器函數和讀寄存器函數

#define button_ip_BASEADDR       0x43C00000//ip核地址可以在vivado的address editor中找到

void button_IP_task()
{
	EXAMPLE_BUTTON_IP_mWriteReg (button_ip_BASEADDR,
			8, EXAMPLE_BUTTON_IP_mReadReg (button_ip_BASEADDR, 0));
	//將寄存器0的內容寫給寄存器2
}
//key.c
#include <stdio.h>
#include "xscugic.h"
#include "xil_exception.h"


#define INT_CFG0_OFFSET 0x00000C00

// Parameter definitions
#define SW1_INT_ID              61//PL中斷源優先級61
#define SW2_INT_ID              62//PL中斷源優先級62

#define INTC_DEVICE_ID          XPAR_PS7_SCUGIC_0_DEVICE_ID
#define INT_TYPE_RISING_EDGE    0x03
#define INT_TYPE_HIGHLEVEL      0x01
#define INT_TYPE_MASK           0x03

XScuGic INTCInst;//一個工程裏的XScuGic要使用同一個,兩個GPIO中斷和定時器中斷使用同一個

static void SW1_intr_Handler(void *param);
static void SW2_intr_Handler(void *param);

static int IntcInitFunction(u16 DeviceId);

static void SW1_intr_Handler(void *param)
{
    int sw_id = (int)param;
    printf("SW%d int\n\r", sw_id);
    open_led();
    button_IP_task();
}
static void SW2_intr_Handler(void *param)
{
    int sw_id = (int)param;
    printf("SW%d int\n\r", sw_id);
    close_led();
}


void IntcTypeSetup(XScuGic *InstancePtr, int intId, int intType)
{
    int mask;

    intType &= INT_TYPE_MASK;
    mask = XScuGic_DistReadReg(InstancePtr, INT_CFG0_OFFSET + (intId/16)*4);
    mask &= ~(INT_TYPE_MASK << (intId%16)*2);
    mask |= intType << ((intId%16)*2);
    XScuGic_DistWriteReg(InstancePtr, INT_CFG0_OFFSET + (intId/16)*4, mask);
}

int IntcInitFunction(u16 DeviceId)
{
    XScuGic_Config *IntcConfig;
    int status;

    // Interrupt controller initialisation
    IntcConfig = XScuGic_LookupConfig(DeviceId);
    status = XScuGic_CfgInitialize(&INTCInst, IntcConfig, IntcConfig->CpuBaseAddress);
    if(status != XST_SUCCESS) return XST_FAILURE;

    // Call to interrupt setup
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
                                 (Xil_ExceptionHandler)XScuGic_InterruptHandler,
                                 &INTCInst);
    Xil_ExceptionEnable();

    // Connect SW1~SW3 interrupt to handler
    status = XScuGic_Connect(&INTCInst,
                             SW1_INT_ID,
                             (Xil_ExceptionHandler)SW1_intr_Handler,
                             (void *)1);
    if(status != XST_SUCCESS) return XST_FAILURE;

    status = XScuGic_Connect(&INTCInst,
                             SW2_INT_ID,
                             (Xil_ExceptionHandler)SW2_intr_Handler,
                             (void *)2);
    if(status != XST_SUCCESS) return XST_FAILURE;


    // Set interrupt type of SW1~SW3 to rising edge
    IntcTypeSetup(&INTCInst, SW1_INT_ID, INT_TYPE_RISING_EDGE);
    IntcTypeSetup(&INTCInst, SW2_INT_ID, INT_TYPE_RISING_EDGE);


    // Enable SW1~SW3 interrupts in the controller
    XScuGic_Enable(&INTCInst, SW1_INT_ID);
    XScuGic_Enable(&INTCInst, SW2_INT_ID);


    return XST_SUCCESS;
}

void init_key()
{
    IntcInitFunction(INTC_DEVICE_ID);
}
//PSgpio.c
#include "xgpiops.h"
#include "sleep.h"

static XGpioPs psGpioInstancePtr;
static XGpioPs_Config* GpioConfigPtr;
static int iPinNumber= 0; //LED0連接的是MIO0
static u32 uPinDirection = 0x1; //1表示輸出,0表示輸入
static int xStatus;

void init_PSgpio()
{

	//--MIO的初始化
	GpioConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
	if(GpioConfigPtr == NULL)
	return XST_FAILURE;
	xStatus = XGpioPs_CfgInitialize(&psGpioInstancePtr,GpioConfigPtr, GpioConfigPtr->BaseAddr);
	if(XST_SUCCESS != xStatus)
	print(" PS GPIO INIT FAILED \n\r");
	//--MIO的輸入輸出操作
		 XGpioPs_SetDirectionPin(&psGpioInstancePtr, iPinNumber,uPinDirection);//配置MIO輸出方向
	XGpioPs_SetOutputEnablePin(&psGpioInstancePtr, iPinNumber,1);//配置MIO的第7位輸出

}

void PSgpio_task()
{
	XGpioPs_WritePin(&psGpioInstancePtr, iPinNumber, 1);//點亮MIO的第0位輸出1
	usleep(500000); //延時
	XGpioPs_WritePin(&psGpioInstancePtr, iPinNumber, 0);//熄滅MIO的第0位輸出0
	usleep(500000); //延時
}

細節:

        (1). 時刻保存工程,在自制IP核工程關閉之後偶爾會把原工程也關掉。

        (2). 管腳約束的引腳定義要去

        (3). 在SDK中下載bitstream時要注意文件別下錯了

        (4). 函數默認是extern的,不想被別的.c調用或爲了避免命名衝突,要對變量和函數使用static聲明

        (5). 在vivado中更新硬件export hardware(.hdf文件)之後,關聯的SDK工程會自動更新工程,如果SDK沒自動更新硬件,可以手動在SDK的hw_platform文件夾上右鍵”change hardware platform specification”

        (6). 更新.bd之後重新生成頂層和底層文件之後要仔細檢查頂層文件是否正確,尤其是管腳有沒有更正,如果文件不對,可以手動delete掉頂層wrapper文件,再重新create一個,對於底層文件可以直接按按鈕“reload”生成。此處如果沒更新正確的話,SDK則檢測不到IP核。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章