一、前言

  Xlinx的ZYNQ系列SOC集成了APU、各种专用外设资源和传统的FPGA逻辑,为ARM+FPGA的应用提供助力,降低功耗和硬件设计难度的同时极大提高两者间传输的带宽。之前在研究生课题中使用过ZYNQ搭建环路系统对算法进行板级验证,但并没有深入使用和理解这个异构平台,今天算是对入门的总结。一款SOC的入门必然是GPIO的使用,而中断则是MCU能保证实时性的必杀武器。硬件调试难度高一直是FPGA的痛点,集成ARM的FPGA更是如此,cross-trigger调试有效地解决了这一问题,所以它也作为入门ZYNQ的必要技能。

二、硬件系统搭建

  ZYNQ的三种GPIO分别是MIO、EMIO和AXI-GPIO。PS部分直接连接到芯片引脚的IO叫MIO,经过FPGA再连接到引脚的是EMIO。EMIO可以通过硬件约束指定不同的端口号和电压标准,提高了ARM IO的灵活性。而AXI-GPIO相当于是对ARM IO的补充,通过调用AXI-GPIO IP核与外部通信。以下通过一个实例来说明三种IO的使用方式。

系统功能:使用一个MIO使连接其上的LED闪烁,使用8个EMIO同样与LED连接构成流水灯效果,另外再调用一个5bit位宽的AXI-GPIO IP核以终端模式响应电路板上5个按键。

平台:米联客 MIZ702N (ZYNQ-7020)

  配置ZYNQ IP,使能MIO和EMIO,配置EMIO位宽是8bit。

  使能Cross Trigger和共享中断。

  之后添加AXI-GPIO IP Core,配置位宽并使能其中断功能:

  运行Run Automatic Connection最终block design系统结构:

  这里使用ILA抓取AXI-GPIO的中断信号。

三、软件编程与AXI-GPIO中断模式解析

  Implementation,export hardware with bitstream, launch SDK. BSP中自带了硬件系统所使用到的IP的一些示例代码和文档,为入门提供了很好的帮助。

  为了方便复用,对Xilinx提供的API做进一步封装,生成gpiops.h gpiops.c gpio.h gpio.c和gic.h文件。接下来重点讲述GIC相关的代码原理。若要使用中断系统,首先要初始化GIC,和其他IP一样包括查找配置和初始赋值两个步骤,分别由LookupConfig和CfgInitialize两个函数完成。后者实际上初始化了中断处理句柄使其指向了一个空结构。要理解内部原理,需要弄清楚XScuGic的数据结构。

  其中Handler实际上是一个函数指针类型,用于定义中断产生时的回调函数。而CallBackRef用于传入InstancePtr,即Gic Instance Pointer。GIC初始化完,要将GIC与中断ID和自定义中断回调函数绑定。

  内部的核心代码依然和初始化时一致,只不过换成了输入参数:

  下一步该使能中断了,一方面是使用GIC对GPIO中断ID的响应,另一方面是使能AXI-GPIO的中断信号。最后是系统对异常的处理函数,这里将其封装在exception_enable中:

  总结来看,中断系统建立的步骤为:

1 初始化GIC

2 连接GIC与中断ID和回调函数

3 使能中断

4 使能异常处理

  那么为什么完成上述操作后,中断事件发生会立即执行自定义中断回调函数GpioHandler呢?CPU会将中断向量表存储在特定的寄存器中,读取该寄存器可以获取中断向量表内容,里边存放着各个中断ID对应的中断函数入口地址。跳转指令则有中断控制器完成。

接下来是各个文件的软件代码:

 /*
* main.c
*
* Created on: 2020年2月22日
* Author: s
*/ #include "xparameters.h" #include "xstatus.h"
#include <xil_printf.h>
#include "sleep.h" #include "gpiops.h"
#include "gpio.h"
#include "gic.h" XGpioPs GpioPs; /* The driver instance for GPIO Device. */
XGpio Gpio;
XScuGic Intc; /* The Instance of the Interrupt Controller Driver */ #define printf xil_printf /* Smalller foot-print printf */ #define LOOP_NUM 8 static u32 MIO_OUT_PIN_INDEX =; /* LED button */
static u32 EMIO_OUT_PIN_BASE_INDEX = ;
volatile u32 IntrFlag; /* Interrupt Handler Flag */ void GpioHandler(void *CallbackRef);
int setupIntSystem(XScuGic *IntcInstancePtr,XGpio *gpioInstancePtr
,u32 IntrId); int main()
{
int Status;
u8 i=;
u32 sys_led_out=0x1; Status = gpiops_initialize(&GpioPs,GPIOPS_DEVICE_ID);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
} Status = gpio_initialize(&Gpio,GPIO_DEVICE_ID);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
} /*
* Set the direction for the pin to be output and
* Enable the Output enable for the LED Pin.
*/
gpiops_setOutput(&GpioPs,MIO_OUT_PIN_INDEX); for(i=;i<LOOP_NUM;i++){
gpiops_setOutput(&GpioPs,EMIO_OUT_PIN_BASE_INDEX+i);
} gpio_setDirect(&Gpio, ,GPIO_CHANNEL1); Status = setupIntSystem(&Intc,&Gpio,INTC_GPIO_INTERRUPT_ID);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
} printf("Initialization finish.\n"); while(){ for(i=;i<LOOP_NUM;i++){
/* Set the GPIO output to be low. */
gpiops_outputValue(&GpioPs, EMIO_OUT_PIN_BASE_INDEX+i, 0x1);
usleep(*);
gpiops_outputValue(&GpioPs, EMIO_OUT_PIN_BASE_INDEX+i, 0x0);
} gpiops_outputValue(&GpioPs, MIO_OUT_PIN_INDEX, sys_led_out);
sys_led_out = sys_led_out == 0x0 ? 0x1 : 0x0;
}
return ;
} int setupIntSystem(XScuGic *IntcInstancePtr,XGpio *gpioInstancePtr
,u32 IntrId)
{
int Result;
/*
* Initialize the interrupt controller driver so that it is ready to
* use.
*/ Result = gic_initialize(&Intc,INTC_DEVICE_ID);
if (Result != XST_SUCCESS) {
return XST_FAILURE;
} XScuGic_SetPriorityTriggerType(IntcInstancePtr, IntrId,
0xA0, 0x3); /*
* Connect the interrupt handler that will be called when an
* interrupt occurs for the device.
*/
Result = XScuGic_Connect(IntcInstancePtr, IntrId,
(Xil_ExceptionHandler)GpioHandler, gpioInstancePtr);
if (Result != XST_SUCCESS) {
return Result;
} /* Enable the interrupt for the GPIO device.*/
XScuGic_Enable(IntcInstancePtr, IntrId); /*
* Enable the GPIO channel interrupts so that push button can be
* detected and enable interrupts for the GPIO device
*/
XGpio_InterruptEnable(gpioInstancePtr,GPIO_CHANNEL1);
XGpio_InterruptGlobalEnable(gpioInstancePtr); /*
* Initialize the exception table and register the interrupt
* controller handler with the exception table
*/
exception_enable(&Intc); IntrFlag = ; return XST_SUCCESS;
} void GpioHandler(void *CallbackRef)
{
XGpio *GpioPtr = (XGpio *)CallbackRef;
u32 gpio_inputValue; /* Clear the Interrupt */
XGpio_InterruptClear(GpioPtr, GPIO_CHANNEL1);
printf("Input interrupt routine.\n"); //IntrFlag = 1;
gpio_inputValue = gpio_readValue(GpioPtr, );
switch(gpio_inputValue)
{
case :
printf("button up\n");
break;
case :
printf("button center\n");
break;
case :
printf("button left\n");
break;
case :
printf("button right\n");
break;
case :
print("button down\n");
break;
} }

main.c

 /*
* gpio.h
*
* Created on: 2020年2月23日
* Author: s
*/ #ifndef SRC_GPIO_H_
#define SRC_GPIO_H_ #include "xgpio.h" #define GPIO_DEVICE_ID XPAR_GPIO_0_DEVICE_ID
#define INTC_GPIO_INTERRUPT_ID XPAR_FABRIC_AXI_GPIO_0_IP2INTC_IRPT_INTR
#define GPIO_CHANNEL1 0x1F int gpio_initialize(XGpio * InstancePtr, u16 DeviceId);
void gpio_setDirect(XGpio *InstancePtr, unsigned Channel,
u32 DirectionMask);
void gpio_outputValue(XGpio * InstancePtr, unsigned Channel, u32 Data);
u32 gpio_readValue(XGpio * InstancePtr, unsigned Channel);
#endif /* SRC_GPIO_H_ */

gpio.h

/*
* gpio.c
*
* Created on: 2020年2月23日
* Author: s
*/ #include "gpio.h" int gpio_initialize(XGpio * InstancePtr, u16 DeviceId)
{
return XGpio_Initialize(InstancePtr,DeviceId);
} void gpio_setDirect(XGpio *InstancePtr, unsigned Channel,
u32 DirectionMask)
{
XGpio_SetDataDirection(InstancePtr, Channel,
DirectionMask);
} void gpio_outputValue(XGpio * InstancePtr, unsigned Channel, u32 Data)
{
XGpio_DiscreteWrite(InstancePtr, Channel, Data);
} u32 gpio_readValue(XGpio * InstancePtr, unsigned Channel)
{
return XGpio_DiscreteRead(InstancePtr, Channel);
}

gpio.c

 /*
* gpiops.c
*
* Created on: 2020年2月23日
* Author: s
*/ #include "gpiops.h" int gpiops_initialize(XGpioPs *InstancePtr,u16 DeviceId)
{
XGpioPs_Config *ConfigPtr; ConfigPtr = XGpioPs_LookupConfig(DeviceId);
return XGpioPs_CfgInitialize(InstancePtr, ConfigPtr,
ConfigPtr->BaseAddr);
} void gpiops_setOutput (XGpioPs *InstancePtr,u32 Pin)
{
XGpioPs_SetDirectionPin(InstancePtr, Pin, );
XGpioPs_SetOutputEnablePin(InstancePtr, Pin, );
} void gpiops_setInput(XGpioPs *InstancePtr,u32 Pin)
{
XGpioPs_SetDirectionPin(InstancePtr, Pin, );
} void gpiops_outputValue(XGpioPs *InstancePtr,u32 Pin,u32 Data)
{
XGpioPs_WritePin(InstancePtr, Pin, Data);
} u32 gpiops_readValue(XGpioPs *InstancePtr,u32 Pin)
{
/* Read the state of the data so that it can be verified. */
return XGpioPs_ReadPin(InstancePtr, Pin);
}

gpiops.c

 /*
* gpio.h
*
* Created on: 2020年2月23日
* Author: s
*/ #ifndef SRC_GPIOPS_H_
#define SRC_GPIOPS_H_ #include "xgpiops.h" #define GPIOPS_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID int gpiops_initialize(XGpioPs *InstancePtr,u16 DeviceId);
void gpiops_setOutput (XGpioPs *InstancePtr,u32 Pin);
void gpiops_setInput(XGpioPs *InstancePtr,u32 Pin);
void gpiops_outputValue(XGpioPs *InstancePtr,u32 Pin,u32 Data);
u32 gpiops_readValue(XGpioPs *InstancePtr,u32 Pin); #endif /* SRC_GPIOPS_H_ */

gpiops.h

 /*
* gic.h
*
* Created on: 2020年2月23日
* Author: s
*/ #ifndef SRC_GIC_H_
#define SRC_GIC_H_ #include "xscugic.h" #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID s32 gic_initialize(XScuGic *InstancePtr,u16 DeviceId)
{
XScuGic_Config *IntcConfig; IntcConfig = XScuGic_LookupConfig(DeviceId);
if (NULL == IntcConfig) {
return XST_FAILURE;
} return XScuGic_CfgInitialize(InstancePtr, IntcConfig,
IntcConfig->CpuBaseAddress);
} void exception_enable(void *Data)
{
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler, Data); /* Enable non-critical exceptions */
Xil_ExceptionEnable();
} #endif /* SRC_GIC_H_ */

gic.h

四、交叉触发调试

  右键工程文件夹->Run As/Debug As分别用于代码下载和调试。SDK基于GDB提供了强大的调试能力,支持断点运行,可查看内部寄存器、地址数值以及汇编代码等。Debug As ->Debug Configuartion,双击System Debugger新建ELF文件。勾选Reset entire system和Program FPGA,因为ELF只是软件,硬件信息存储在bitstream中。最重要的是勾选enable cross-triggering。

  点击enable cross-triggering右侧的按钮,按照如下操作使能Processor to Fabric Trigger.

  再次create使能Fabric to Processor Trigger:

  最后点击Debug下载软硬件代码并进入调试界面。

  1 首先尝试PS触发PL调试:

  指定中断回调函数起始位置一个断点。然后进入VIVADO,打开Hardware Manager连接硬件。注意此时触发模式选择IN_ONLY。此时不用设置ILA抓取信号的触发条件,因为触发由PS端的PC控制。点击Run Trigger等待触发条件。这时回到SDK点击Resume按钮使代码开始运行。按下任意按键产生中断,此时软件代码运行到断点处停止,ILA随即抓取中断信号。

  2 尝试PL触发PS调试:

  这回在VIVADO中设置触发模式为OR_TRIG_IN,并启动触发条件为上升沿触发。按下按键,C运行到满足ILA触发条件时C代码立即停止,故PL控制了PS端的程序运行。

  可以看到此时程序进入IRQHandler。

  串口终端也打印进入中断函数的信息,正确响应中断。到此示例结束。本文虽是对ZYNQ入门的整理,但涉及到的东西很多,包括GPIO应用、中断系统建立和相应机制、调用AXI总线IP核、软件设计以及软硬件交叉触发调试流程。

ZYNQ入门实例——三种GPIO应用、中断系统及软硬件交叉触发调试的更多相关文章

  1. JdbcTemplae使用入门&&Spring三种连接池配置&&Spring配置文件引用外部properties文件

    JdbcTemplate的使用 Spring为了各种支持的持久化技术,都提供了简单操作的模版和回调. JdbcTemplate 简化 JDBC 操作HibernateTemplate 简化 Hiber ...

  2. 第五篇 CSS入门 明白 三种嵌套形式,三种常用控制器

    CSS入门 css是 层叠式样式表   css的作用是什么呢?举个抽象的例子啊,HTML是人,CSS则是衣服...   css给html穿上衣服,有三种形式: 内嵌.内联.外联. 这三种形式,优先级为 ...

  3. springboot与dubbo整合入门(三种方式)

    Springboot与Dubbo整合三种方式详解 整合环境: jdk:8.0 dubbo:2.6.2 springboot:2.1.5 项目结构: 1.搭建项目环境: (1)创建父项目与三个子项目,创 ...

  4. 【CC2530入门教程-03】CC2530的中断系统及外部中断应用

    第3课  CC2530的中断系统及外部中断应用 广东职业技术学院  欧浩源 一.中断相关的基础概念  内核与外设之间的主要交互方式有两种:轮询和中断. 轮询的方式貌似公平,但实际工作效率很低,且不能及 ...

  5. [Linux]三种方案在Windows系统下安装ubuntu双系统(转)

    在学习linux的过程中,ubuntu无疑是初学者的最佳选择. 下面来列举给Windows系统安装ubuntu双系统的三种方法. 一.虚拟机安装(不推荐) 使用工具:Vmware 如果不是因为迫不得已 ...

  6. 三种Dataase Mapping的系统架构

    ORM - O/R M - Object/Relational Mapping: A technique/idea used to map objects and thier individual r ...

  7. 三种方案在Windows系统下安装ubuntu双系统

    一.虚拟机安装(不推荐) 使用工具:Vmware 如果不是因为迫不得已,比如Mac OS对硬件不兼容,Federa安装频繁出错,各种驱动不全等等,不推荐使用虚拟机安装. 个人感觉这是一种对操作系统的亵 ...

  8. ZYNQ入门实例——定时器中断与程序固化

    一.前言 APU系统中CPU以串行执行代码的方式完成操作,软件方式很难做到精准计时,因此调用内部定时器硬件完成计时是更好的选择.本文以定时器中断方式控制LED周期性闪烁为例学习私有定时器的使用.同时学 ...

  9. 登陆验证系统实例-三种(cookie,session,auth)

    登陆验证 因为http协议是无状态协议,但是我们有时候需要这个状态,这个状态就是标识 前端提交from表单,后端获取对应输入值,与数据库对比,由此对象设置一个标识,该对象 在别的视图的时候,有此标识, ...

随机推荐

  1. python小功能记录

    本博客会不断完善,记录python小功能. 1. 合并两个字典 # in Python 3.5+ >>> x = {'a': 1, 'b': 2} >>> y = ...

  2. Redis常用命令详细介绍

    一.字符串 字符串键是Redis最基本的键值对类型,将一个单独的键和一个单独的值关联起来.通过字符串键,不仅可以存储和读取字符串,如果输入能被解释为整数和浮点数,还能执行自增或自减操作. 1.SET: ...

  3. java项目Jenkins部署

    假设背景: Nginx跳板机服务器:192.168.10.1 Tomcat应用服务器:192.168.10.3 端口:10083 应用名称:appXXX 1.配置跳板机的转发路径 如:192.168. ...

  4. Oracle GoldenGate Best Practices: Active-Active Configuration with DML Auto CDR

    Executive Overview This document is an introduction to Oracle GoldenGate (DIPC remote agent)’s best ...

  5. 痞子衡嵌入式:ARM Cortex-M内核那些事(3.2)- 安全模块看特性(M23/33/35P)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是ARM Cortex-M功能模块,不过侧重点是三款安全特性处理器. ARM Cortex-M处理器家族发展至今(2020),已有8代产品 ...

  6. SpingMvc复杂参数传收总结

    上一篇文章[javaWeb传收参数方式总结]总结了简单传收参数,这一篇讲如何传收复杂参数,比如Long[] .User(bean里面包含List).User[].List.List<Map< ...

  7. Day9-Python3基础-多线程、多进程

    1.进程.与线程区别 2.python GIL全局解释器锁 3.线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生产者消费者模型 Que ...

  8. Java之路——初识Eclipse

    零.大纲 一.前言 二.获取Eclipse 三.运行Eclipse 四.创建及运行第一个Java Project 五.界面介绍 六.如何调试 七.获取插件 八.Eclipse 快捷键 九.总结 一.前 ...

  9. C# 定时器导致的内存泄露问题

    C# 中有三种定时器,System.Windows.Forms 中的定时器和 System.Timers.Timer 的工作方式是完全一样的,所以,这里我们仅讨论 System.Timers.Time ...

  10. time 模块 和 random 模块常用方法讲解

    import timeprint(help(time))print(time.time())#时间戳 1573991312.5361328print(time.perf_counter())#计算CP ...