基于ZYNQ-7000开发板的调试系列(2)

2. 基于PS调试流水灯

这一部分主要分为两个部分:

  1. PS通过EMIO控制PL端GPIO
  2. PS直接控制PS端GPIO

基于PS调试流水灯

该部分控制7个LED灯组成流水灯,其中A5、A6、A7、B8、T16是PL端的GPIO口;C19、G17是PS端的GPIO口。
其中,由A5、A6、A7、B8、C19组成一组流水灯;由T16、G17组成另外一组。

1. 创建Block

1. 创建一个ZYNQ Processing System

主要更改两个部分的内容:

  1. DDR Configuration 此处按照板卡的类型选择自己DDR的型号。基于ZYNQ-7000开发板的调试系列(2)_第1张图片
    [参考自SoC第一讲——Vivado的Block Design 的使用]
  2. MIO Configuration 此处由于需要使用MIO口,故而需要使能GPIO MIO端口基于ZYNQ-7000开发板的调试系列(2)_第2张图片

2. 创建一个AXI GPIO

这里为了实验需要允许了双通道的GPIO,正常一个GPIO通道即可完成5路GPIO口的控制。基于ZYNQ-7000开发板的调试系列(2)_第3张图片
然后让Vivado自动连接填补剩余模块即可。具体完成后就是如下的样子。基于ZYNQ-7000开发板的调试系列(2)_第4张图片

2. 约束文件编写

led.xdc

set_property IOSTANDARD LVCMOS33 [get_ports {gpio_rtl_0_tri_io[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpio_rtl_0_tri_io[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpio_rtl_0_tri_io[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpio_rtl_0_tri_io[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports gpio_rtl_1_tri_io]

set_property PACKAGE_PIN B8 [get_ports {gpio_rtl_0_tri_io[3]}]
set_property PACKAGE_PIN A6 [get_ports {gpio_rtl_0_tri_io[2]}]
set_property PACKAGE_PIN A7 [get_ports {gpio_rtl_0_tri_io[1]}]
set_property PACKAGE_PIN A5 [get_ports {gpio_rtl_0_tri_io[0]}]
set_property PACKAGE_PIN T16 [get_ports gpio_rtl_1_tri_io]

之后生成BitStream后导出硬件平台信息,由于这部分需要使用EMIO,需要包含bitstream库,之后可以征正常开始编写PS部分程序。基于ZYNQ-7000开发板的调试系列(2)_第5张图片

3. PS程序编写

通过Launch SDK打开Xilinx SDK。创建一个空的应用项目。由于没有使用串口,这一部分不可以使用HelloWorld模板。
先引入一部分必须引入的模块

#include "parameters.h" // 常数模块
#include "sleep.h" // 延时模块

1. PL部分GPIO口的控制

这一部分需要引入xgpio模块

#include "xgpio.h"

先实验一个基于PL部分的流水灯程序,这一部分主要是测试使用两个通道的GPIO口会有什么问题。测试下来,并没有什么问题。
用到的函数有

int XGpio_Initialize(
	XGpio* InstancePtr, 
	u16 DeviceId 
); //初始化
void XGpio_SetDataDirection(
	XGpio *InstancePtr, 
	unsigned Channel,
	u32 DirectionMask
); //设置GPIO口的读写方向
void XGpio_DiscreteWrite(
	XGpio * InstancePtr, 
	unsigned Channel, 
	u32 Data
); //设置GPIO口的值
int usleep(
	unsigned long useconds
); //设置延时时间

这些函数在官方文档以及官方例程里面给的比较详细,这里就无需说明。

针对PL口的流水灯的程序如下:

#include "xparameters.h"
#include "sleep.h"
#include "xgpio.h"

#define GPIO_DEVICE_ID XPAR_GPIO_0_DEVICE_ID
#define DELAY_LED 500000
#define LED_CHANNEL_1 1
#define LED_CHANNEL_2 2
#define LED0 0x01
#define LED1 0x02
#define LED2 0x04
#define LED3 0x08
#define LED4 0x01

XGpio gpio;
int main(){
	int Status;
	Status = XGpio_Initialize(&gpio, GPIO_DEVICE_ID);
	if(Status != XST_SUCCESS){
		return XST_FAILURE;
	}
	XGpio_SetDataDirection(&gpio, LED_CHANNEL_1, 0x00);
	XGpio_SetDataDirection(&gpio, LED_CHANNEL_2, 0x00);
	while(1){
		XGpio_DiscreteWrite(&gpio, LED_CHANNEL_1, (~LED0) | LED1 | LED2 | LED3);
		XGpio_DiscreteWrite(&gpio, LED_CHANNEL_2, LED4);
		usleep(DELAY_LED);
		XGpio_DiscreteWrite(&gpio, LED_CHANNEL_1, LED0 | (~LED1) | LED2 | LED3);
		XGpio_DiscreteWrite(&gpio, LED_CHANNEL_2, LED4);
		usleep(DELAY_LED);
		XGpio_DiscreteWrite(&gpio, LED_CHANNEL_1, LED0 | LED1 | (~LED2) | LED3);
		XGpio_DiscreteWrite(&gpio, LED_CHANNEL_2, LED4);
		usleep(DELAY_LED);
		XGpio_DiscreteWrite(&gpio, LED_CHANNEL_1, LED0 | LED1 | LED2 | (~LED3));
		XGpio_DiscreteWrite(&gpio, LED_CHANNEL_2, LED4);
		usleep(DELAY_LED);
		XGpio_DiscreteWrite(&gpio, LED_CHANNEL_1, LED0 | LED1 | LED2 | LED3);
		XGpio_DiscreteWrite(&gpio, LED_CHANNEL_2, ~LED4);
		usleep(DELAY_LED);
	}
	return 0;
}

2. PS部分GPIO口的控制

首先确定PS两个GPIO口对应的PS部分的编号。其中G17为MIO0,C19为MIO9。
实验一个基于PS部分的流水灯程序,这一部分主要是测试使用PS控制GPIO口是否有其他问题。测试下来,并没有什么问题。
用到的函数有:

XGpioPs_Config *XGpioPs_LookupConfig(
	u16 DeviceId
); //根据设备ID查看配置文件
s32 XGpioPs_CfgInitialize(
	XGpioPs *InstancePtr, 
	XGpioPs_Config *ConfigPtr,
	u32 EffectiveAddr
); //初始化
void XGpioPs_SetDirection(
	XGpioPs *InstancePtr, 
	u8 Bank,
	u32 Direction
); //设置GPIO口读写方向,这里和PL部分相反
void XGpioPs_SetOutputEnable(
	XGpioPs *InstancePtr, 
	u8 Bank, 
	u32 OpEnable
); //使能GPIO口输出
void XGpioPs_WritePin(
	XGpioPs *InstancePtr, 
	u32 Pin, 
	u32 Data
); //写入一个GPIO口的值,这里需要注意,只有最低位有效
#include "xparameters.h"
#include "sleep.h"
#include "xgpiops.h"

#define PS_GPIO_DEVICE_ID	XPAR_XGPIOPS_0_DEVICE_ID

#define DELAY_LED 500000

#define PS_GPIO_BANK			XGPIOPS_BANK0

#define PS_LED0 0x09
#define PS_LED1 0x00

static XGpioPs psGpio;

int main(){
	XGpioPs_Config *psConfigPtr;
	psConfigPtr = XGpioPs_LookupConfig(PS_GPIO_DEVICE_ID);
	if (psConfigPtr == NULL) {
		return XST_FAILURE;
	}
	XGpioPs_CfgInitialize(&psGpio, psConfigPtr, psConfigPtr->BaseAddr);
	XGpioPs_SetDirection(&psGpio, PS_GPIO_BANK, 0x201);
	XGpioPs_SetOutputEnable(&psGpio, PS_GPIO_BANK, 0x201);
	while(1){	
		XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
		XGpioPs_WritePin(&psGpio, PS_LED1, 0x00);
		usleep(DELAY_LED);
		XGpioPs_WritePin(&psGpio, PS_LED0, 0x00);
		XGpioPs_WritePin(&psGpio, PS_LED1, 0x01);
		usleep(DELAY_LED);
	}
	return 0;
}

3. 流水灯程序编写

有了之前的基础,这一部分比较简单。由于需要控制两个部分的流水灯,感觉意义缺缺,之后再中断中再继续这一个项目的编写。

#include "xparameters.h"
#include "sleep.h"
#include "xgpio.h"
#include "xgpiops.h"

#define PL_GPIO_DEVICE_ID 	XPAR_GPIO_0_DEVICE_ID
#define PS_GPIO_DEVICE_ID	XPAR_XGPIOPS_0_DEVICE_ID

#define DELAY_LED 500000

#define PL_LED_CHANNEL_1 		0x01
#define PL_LED_CHANNEL_2 		0x02
#define PS_GPIO_BANK			XGPIOPS_BANK0

#define PL_LED0 0x01
#define PL_LED1 0x02
#define PL_LED2 0x04
#define PL_LED3 0x08
#define PL_LED4 0x01
#define PS_LED0 0x09
#define PS_LED1 0x00

static XGpio plGpio;
static XGpioPs psGpio;

int main(){
	int Status;
	Status = XGpio_Initialize(&plGpio, PL_GPIO_DEVICE_ID);

	XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL_1, 0x00);
	XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL_2, 0x00);

	XGpioPs_Config *psConfigPtr;
	psConfigPtr = XGpioPs_LookupConfig(PS_GPIO_DEVICE_ID);
	if (psConfigPtr == NULL) {
		return XST_FAILURE;
	}
	XGpioPs_CfgInitialize(&psGpio, psConfigPtr, psConfigPtr->BaseAddr);
	XGpioPs_SetDirection(&psGpio, PS_GPIO_BANK, 0x201);
	XGpioPs_SetOutputEnable(&psGpio, PS_GPIO_BANK, 0x201);
	while(1){
		XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL_1, 0x0E);
		XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
		XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL_2, 0x00);
		XGpioPs_WritePin(&psGpio, PS_LED1, 0x01);
		usleep(DELAY_LED);
		XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL_1, 0x0D);
		XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
		XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL_2, 0x01);
		XGpioPs_WritePin(&psGpio, PS_LED1, 0x00);
		usleep(DELAY_LED);
		XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL_1, 0x0B);
		XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
		XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL_2, 0x00);
		XGpioPs_WritePin(&psGpio, PS_LED1, 0x01);
		usleep(DELAY_LED);
		XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL_1, 0x07);
		XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
		XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL_2, 0x01);
		XGpioPs_WritePin(&psGpio, PS_LED1, 0x00);
		usleep(DELAY_LED);
		XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL_1, 0x0F);
		XGpioPs_WritePin(&psGpio, PS_LED0, 0x00);
		XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL_2, 0x00);
		XGpioPs_WritePin(&psGpio, PS_LED1, 0x01);
		usleep(DELAY_LED);
		XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL_1, 0x0E);
		XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
		XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL_2, 0x01);
		XGpioPs_WritePin(&psGpio, PS_LED1, 0x00);
		usleep(DELAY_LED);
		XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL_1, 0x0D);
		XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
		XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL_2, 0x00);
		XGpioPs_WritePin(&psGpio, PS_LED1, 0x01);
		usleep(DELAY_LED);
		XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL_1, 0x0B);
		XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
		XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL_2, 0x01);
		XGpioPs_WritePin(&psGpio, PS_LED1, 0x00);
		usleep(DELAY_LED);
		XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL_1, 0x07);
		XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
		XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL_2, 0x00);
		XGpioPs_WritePin(&psGpio, PS_LED1, 0x01);
		usleep(DELAY_LED);
		XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL_1, 0x0F);
		XGpioPs_WritePin(&psGpio, PS_LED0, 0x00);
		XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL_2, 0x01);
		XGpioPs_WritePin(&psGpio, PS_LED1, 0x00);
		usleep(DELAY_LED);
	}
	return 0;
}

有一个神奇的地方,实际上应该是一个bug,我现在还没有弄明白为什么。
即如果将psGpio和plGpio的声明换为

static XGpio* plGpio;
static XGpioPs* psGpio;

后面将&psGpio换为psGpio,&plGpio换为plGpio。程序在初始化的位置就不知道跑到哪里去了。无论怎么尝试都是错误的,我一直以为这样的修改是等价的,不知道为什么会有这样的问题。

#include "xparameters.h"
#include "sleep.h"
#include "xgpio.h"
#include "xgpiops.h"

#define PL_GPIO_DEVICE_ID 	XPAR_GPIO_0_DEVICE_ID
#define PS_GPIO_DEVICE_ID	XPAR_XGPIOPS_0_DEVICE_ID

#define DELAY_LED 500000

#define PL_LED_CHANNEL_1 		0x01
#define PL_LED_CHANNEL_2 		0x02
#define PS_GPIO_BANK			XGPIOPS_BANK0

#define PL_LED0 0x01
#define PL_LED1 0x02
#define PL_LED2 0x04
#define PL_LED3 0x08
#define PL_LED4 0x01
#define PS_LED0 0x09
#define PS_LED1 0x00

static XGpio* plGpio;
static XGpioPs* psGpio;

int main(){
	int Status;
	Status = XGpio_Initialize(plGpio, PL_GPIO_DEVICE_ID);

	XGpio_SetDataDirection(plGpio, PL_LED_CHANNEL_1, 0x00);
	XGpio_SetDataDirection(plGpio, PL_LED_CHANNEL_2, 0x00);

	XGpioPs_Config *psConfigPtr;
	psConfigPtr = XGpioPs_LookupConfig(PS_GPIO_DEVICE_ID);
	if (psConfigPtr == NULL) {
		return XST_FAILURE;
	}
	XGpioPs_CfgInitialize(psGpio, psConfigPtr, psConfigPtr->BaseAddr);
	XGpioPs_SetDirection(psGpio, PS_GPIO_BANK, 0x201);
	XGpioPs_SetOutputEnable(psGpio, PS_GPIO_BANK, 0x201);
	while(1){
		XGpio_SetDataDirection(plGpio, PL_LED_CHANNEL_1, 0x0E);
		XGpioPs_WritePin(psGpio, PS_LED0, 0x01);
		XGpio_SetDataDirection(plGpio, PL_LED_CHANNEL_2, 0x00);
		XGpioPs_WritePin(psGpio, PS_LED1, 0x01);
		usleep(DELAY_LED);
		XGpio_SetDataDirection(plGpio, PL_LED_CHANNEL_1, 0x0D);
		XGpioPs_WritePin(psGpio, PS_LED0, 0x01);
		XGpio_SetDataDirection(plGpio, PL_LED_CHANNEL_2, 0x01);
		XGpioPs_WritePin(psGpio, PS_LED1, 0x00);
		usleep(DELAY_LED);
		XGpio_SetDataDirection(plGpio, PL_LED_CHANNEL_1, 0x0B);
		XGpioPs_WritePin(psGpio, PS_LED0, 0x01);
		XGpio_SetDataDirection(plGpio, PL_LED_CHANNEL_2, 0x00);
		XGpioPs_WritePin(psGpio, PS_LED1, 0x01);
		usleep(DELAY_LED);
		XGpio_SetDataDirection(plGpio, PL_LED_CHANNEL_1, 0x07);
		XGpioPs_WritePin(psGpio, PS_LED0, 0x01);
		XGpio_SetDataDirection(plGpio, PL_LED_CHANNEL_2, 0x01);
		XGpioPs_WritePin(psGpio, PS_LED1, 0x00);
		usleep(DELAY_LED);
		XGpio_SetDataDirection(plGpio, PL_LED_CHANNEL_1, 0x0F);
		XGpioPs_WritePin(psGpio, PS_LED0, 0x00);
		XGpio_SetDataDirection(plGpio, PL_LED_CHANNEL_2, 0x00);
		XGpioPs_WritePin(psGpio, PS_LED1, 0x01);
		usleep(DELAY_LED);
		XGpio_SetDataDirection(plGpio, PL_LED_CHANNEL_1, 0x0E);
		XGpioPs_WritePin(psGpio, PS_LED0, 0x01);
		XGpio_SetDataDirection(plGpio, PL_LED_CHANNEL_2, 0x01);
		XGpioPs_WritePin(psGpio, PS_LED1, 0x00);
		usleep(DELAY_LED);
		XGpio_SetDataDirection(plGpio, PL_LED_CHANNEL_1, 0x0D);
		XGpioPs_WritePin(psGpio, PS_LED0, 0x01);
		XGpio_SetDataDirection(plGpio, PL_LED_CHANNEL_2, 0x00);
		XGpioPs_WritePin(psGpio, PS_LED1, 0x01);
		usleep(DELAY_LED);
		XGpio_SetDataDirection(plGpio, PL_LED_CHANNEL_1, 0x0B);
		XGpioPs_WritePin(psGpio, PS_LED0, 0x01);
		XGpio_SetDataDirection(plGpio, PL_LED_CHANNEL_2, 0x01);
		XGpioPs_WritePin(psGpio, PS_LED1, 0x00);
		usleep(DELAY_LED);
		XGpio_SetDataDirection(plGpio, PL_LED_CHANNEL_1, 0x07);
		XGpioPs_WritePin(psGpio, PS_LED0, 0x01);
		XGpio_SetDataDirection(plGpio, PL_LED_CHANNEL_2, 0x00);
		XGpioPs_WritePin(psGpio, PS_LED1, 0x01);
		usleep(DELAY_LED);
		XGpio_SetDataDirection(plGpio, PL_LED_CHANNEL_1, 0x0F);
		XGpioPs_WritePin(psGpio, PS_LED0, 0x00);
		XGpio_SetDataDirection(plGpio, PL_LED_CHANNEL_2, 0x01);
		XGpioPs_WritePin(psGpio, PS_LED1, 0x00);
		usleep(DELAY_LED);


	}
	return 0;
}

无报错,无法正常运行。
以上,关于控制GPIO口的实验部分全部结束。

你可能感兴趣的:(FPGA)