《电子技术应用》
您所在的位置:首页 > 可编程逻辑 > 业界动态 > 从小白到专家:网友亲身实践教您上手 ZYNQ 开发 | Zynq 常用外设快速上手指南

从小白到专家:网友亲身实践教您上手 ZYNQ 开发 | Zynq 常用外设快速上手指南

2019-09-15
关键词: ZYNQ开发 Zynq

  ZYNQ有专用的DDR Controller接口,如果外部硬件连接了DDR器件,于是在ZYNQ Processing System中正确配置了相应的信号和参数后,DDR就可以成为ZYNQ的内存,在SDK中可以直接使用memcpy、memset以及类似的函数对于Memory空间进行操作。

  Step1:查看ZYBO的原理图,找到相应的配置。ZYBO原理图中与DDR相关的部分如下图所示。

640.webp.jpg

  于是得到两个信息,第一个所使用的芯片是MT41J128M16JT-125,第二个是两片DDR3颗粒是通过位拼接完成的,也就是数据位宽为32bit。

  Step2:在Block Design中对DDR部分的参数进行配置。

640.webp (1).jpg

  Step3:完成Block Design设计,产生Bitstream,导入SDK。

640.webp (2).jpg

  Step4:在SDK中编写Memory测试代码。

  #include <stdio.h>

  #include <stdlib.h>

  #include "platform.h"

  #include "xil_printf.h"

  #include "xil_types.h"

  #include "xil_io.h"

  int main()

  {

  u32 test_src[100];

  int i;

  int readback;

  init_platform();

  u32 *result = (u32*) malloc(sizeof(u32) * 100);

  if (result) {

  memset(result, 0, sizeof(u32) * 100);

  } else {

  return 0;

  }

  for(i=0;i<99;i++)

  {

  test_src[i]=i;

  }

  memcpy(result,test_src,100 * sizeof(u32));

  for(i=0;i<100;i++)

  {

  readback = Xil_In32(result+i);

  其中特别需要学习的就是malloc与memcpy的使用方法。

  贰

  ZYNQ 中 MIO/EMIO GPIO 的使用

  参考工程见“ZYBO_Memory_GPIO_Interrupt_demo.xpr”。

  MIO是PS端的外部引脚,共有54个;EMIO是PL端的外部引脚,共有64个。ZYNQ支持通过配置将PS的控制器信号通过EMIO输出,例如PS自带的UART Controller,如果正常选择引脚只能选择MIO引脚输出,但是通过设置可以选择连接到EMIO引脚。同时EMIO引脚也可以作为PS端的扩展引脚,即经过扩展PS一共可以控制118个引脚。

  该例程演示将4个EMIO设置为PS的扩展引脚,这4个EMIO连接着LED。于是,与“将用户逻辑设计封装成IP”中的实验相比,同样是控制外部4个LED,就不需要另外设计一个逻辑模块,并封装成IP作为PS的外设了,可以直接通过SDK的程序进行控制。

  注意

  1. 用于扩展GPIO的EMIO和用于扩展外设的EMIO是完全独立的,GPIO的EMIO共有64个,由2个bank组成,如下图所示。

    640.webp (3).jpg

  2. EMIO的内部排序按照EMIO54、EMIO55... ... EMIO117,以此类推。有了EMIO的编号之后就与内部控制EMIO的寄存器一一对应;而EMIO在外部与外部引脚的对应关系又是可以通过管脚约束进行更改的。于是可以得出:不能通过EMIO的外部引脚的关系确定其内部寄存器的地址。工具对于EMIO GPIO的连接关系是按照从EMIO54开始依次向上排列。

  Step1:在Block Design中加入ZYNQ7 Processing System,在ZYNQ7 Processing System配置中添加EMIO GPIO,如下图所示。通过设置EMIO GPIO Width来选择扩展EMIO GPIO的个数,此时就完成了与内部寄存器之间的对应关系,规则就是从EMIO54开始向上排列。

640.webp (4).jpg

  Step2:将ZYNQ的EMIO连接到外部引脚。右击生成的GPIO信号,点击Make External。

640.webp (5).jpg

  Step3:约束EMIO与外部引脚Pad的对应关系以及EMIO的电平标准。

  方法有两种:

  第一种是通过XDC约束文件进行约束,需要先将Block Design生成HDL Wrapper,这样才能知道其引脚名称。

  set_property IOSTANDARD LVCMOS33 [get_ports {gpio_0_tri_io[3]}]]

  set_property IOSTANDARD LVCMOS33 [get_ports {gpio_0_tri_io[2]}]

  set_property IOSTANDARD LVCMOS33 [get_ports {gpio_0_tri_io[1]}]

  set_property IOSTANDARD LVCMOS33 [get_ports {gpio_0_tri_io[0]}]

  set_property PACKAGE_PIN D18 [get_ports {gpio_0_tri_io[3]}]

  set_property PACKAGE_PIN G14 [get_ports {gpio_0_tri_io[2]}]

  set_property PACKAGE_PIN M15 [get_ports {gpio_0_tri_io[1]}]

  set_property PACKAGE_PIN M14 [get_ports {gpio_0_tri_io[0]}]

  第二种方法就是Open Elaborated Design,在GUI中设置电平和引脚。

640.webp (6).jpg

  Step4:完成Block Design的综合、实现、生成Bitstream并导入SDK。

  Step5:SDK中完成代码的编写,EMIO的代码编写需要包含的库文件是"xgpiops.h"。

  #include "xgpiops.h"

  static XGpioPs emio;

  #define EMIO_54   54

  #define EMIO_55   55

  #define EMIO_56   56

  #define EMIO_57   57

  int main()

  {

  //定义GPIOPS型指针,用于初始化时绑定硬件

  XGpioPs_Config *ConfigPtrPS;

  init_platform();

  //初始化GPIOPS,将ConfigPtrPS与硬件绑定

  ConfigPtrPS = XGpioPs_LookupConfig(0);

  XGpioPs_CfgInitialize(&emio, ConfigPtrPS,

  ConfigPtrPS->BaseAddr);

  //设置EMIO的方向,并使能EMIO

  XGpioPs_SetDirectionPin(&emio, EMIO_54, 1);

  XGpioPs_SetOutputEnablePin(&emio, EMIO_54, 1);

  XGpioPs_SetDirectionPin(&emio, EMIO_55, 1);

  XGpioPs_SetOutputEnablePin(&emio, EMIO_55, 1);

  XGpioPs_SetDirectionPin(&emio, EMIO_56, 1);

  XGpioPs_SetOutputEnablePin(&emio, EMIO_56, 1);

  XGpioPs_SetDirectionPin(&emio, EMIO_57, 1);

  XGpioPs_SetOutputEnablePin(&emio, EMIO_57, 1);

  while(1)

  {

  // 向EMIO写入数据,即驱动EMIO引脚

  XGpioPs_WritePin(&emio, EMIO_54, 0x0);

  XGpioPs_WritePin(&emio, EMIO_55, 0x0);

  XGpioPs_WritePin(&emio, EMIO_56, 0x0);

  XGpioPs_WritePin(&emio, EMIO_57, 0x0);

  usleep(200000);

  XGpioPs_WritePin(&emio, EMIO_54, 0x1);

  XGpioPs_WritePin(&emio, EMIO_55, 0x1);

  XGpioPs_WritePin(&emio, EMIO_56, 0x1);

  XGpioPs_WritePin(&emio, EMIO_57, 0x1);

  usleep(200000);

  }

  cleanup_platform();

  return 0;

  }

  Step6:如果需要将EMIO作为输入端口,只需要将IO的方向设置为input。对于IO,作为输出的时候需要Enable,但是作为输入是永远使能的,不需要额外的Enable。具体代码如下图所示。

640.webp (7).jpg

  补充说明:

  MIO和EMIO都属于PS的GPIO,用于指示的变量类型为XGpioPs;而使用AXI_GPIO外设的GPIO,由于是属于PL的,所以指示这些IO的变量类型为XGpio。

  MIO和EMIO的控制对于SDK是完全相同的,其地址偏移量也是排在一起的,MIO从0排到53,EMIO接着从54开始。示例代码中显示的是EMIO作为输出和MIO作为输入,只需要将引脚编号的宏定义改为需要的MIO或者EMIO编号即可使用。

  在硬件配置时MIO的配置方法与EMIO有所不同,EMIO的配置如“Step1”所示。而MIO由于不像EMIO,外部管脚是确定的,所以可以在ZYNQ7 Processing System配置时同时完成属性以及电平的设置,如下图所示。

640.webp (8).jpg

  叁

  ZYNQ 中 Interrupt 使用

  参考工程见“ZYBO_Memory_GPIO_Interrupt_demo.xpr”。

  ZYNQ中的中断管理是通过Generic Interrupt Controller(GIC)完成的。

  任何的中断功能都需要两步,第一步是配置相应的中断,第二步是设置中断触发之后的服务函数。

  配置相应中断分以下几个步骤:

  1.   使能相应的功能,例如GPIO中断需要首先使能和配置GPIO;Timer中断需要首先使能和配置Timer;

  2. 640.webp (9).jpg

  2.  初始化并配置使能GIC,还要使能异常处理。第1步中的操作对于每个中断源来说都不相同,但是这一步的配置对于不同中断源而言是类似的。不同之处在于有一个参数:中断ID,即例子中的52是变化的,52是GPIO的中断号,其他中端需要使用不同的ID。该值可以在UG585中断的相关章节查询到,如下图所示。

640.webp (10).jpg

640.webp (11).jpg

  另一个区别就是XScuGic_Connect时的服务子函数不同。

  3.  编写中断服务函数,需要注意的是进入服务函数后首先需要禁止中断,保证在处理中断时不会再次因触发中断而程序跳转;另外就是需要清除中断标志位,否则会不断触发中断。

640.webp (12).jpg

  ZYNQ CPU内部任何有定时器,在Vivado的ZYNQ配置中无需任何操作就可以在SDK中直接使用。与GPIO中断类似,Timer的中断也包含相同的几步操作,下面给出各个阶段的代码片段,完整代码请点击“阅读原文”,登录论坛获取。

  1.   初始化Timer。

    640.webp (13).jpg

  2.  初始化GIC,设置中断服务函数入口。

640.webp (14).jpg

  3.  配置Timer工作模式,导入计数初值,使能中断。

640.webp (15).jpg

  4.  编写中断服务函数,进入中断后首先Disable中断,清楚中断标志位;然后进行中断处理;退出中断服务函数前重新使能中断。

640.webp (16).jpg

本站内容除特别声明的原创文章之外,转载内容只为传递更多信息,并不代表本网站赞同其观点。转载的所有的文章、图片、音/视频文件等资料的版权归版权所有权人所有。本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如涉及作品内容、版权和其它问题,请及时通过电子邮件或电话通知我们,以便迅速采取适当措施,避免给双方造成不必要的经济损失。联系电话:010-82306118;邮箱:aet@chinaaet.com。