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核。

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Zynq 7010开发手册是一本指导用户如何开发和使用Zynq 7010芯片的技术文档。Zynq 7010是一款由赛灵思(Xilinx)公司开发的片上系统(SoC),集成了一个ARM处理器和FPGA可编程逻辑,可以同时运行软件和硬件。 在Zynq 7010开发手册中,首先会介绍Zynq 7010芯片的基本架构和性能特点。用户可以了解芯片的主要组成部分,包括处理系统(PS)和可编程逻辑(PL)。同时,手册还会详细介绍芯片的管脚布局和外部接口,以及供电和时钟管理等相关信息。 接下来,Zynq 7010开发手册会向用户介绍如何使用软件开发环境来编写和调试基于ARM处理器的应用程序。手册中会涵盖使用赛灵思提供的工具和软件库来开发和调试C/C++代码,以及如何使用操作系统(如Linux)来构建嵌入式系统。 除了软件开发,Zynq 7010开发手册还会详细介绍如何使用FPGA可编程逻辑来实现硬件设计。用户可以了解如何使用赛灵思提供的设计工具来实现和验证FPGA逻辑,以及如何与ARM处理器进行通信和协同工作。 在手册的后面,还会有一些示例项目和实验,以帮助用户更好地理解和运用Zynq 7010芯片。这些示例项目包括分别基于软件和硬件的应用案例,用户可以参考这些案例来快速上手开发和调试自己的应用。 总体而言,Zynq 7010开发手册是一本全面而详细的技术文档,涵盖了Zynq 7010芯片的各个方面。它旨在帮助开发者快速了解、学习和开发基于Zynq 7010应用程序和硬件设计。无论是初学者还是有经验的开发者,都可以从中获得很多有用的信息和指导。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值