创作目的:熟悉ALINX的XC7Z100开发板,熟悉ARM架构,理解ZYNQ的ARM+FPGA即PS+PL结构,熟练掌握vivado端的硬件设计以及sdk端的软件设计,实现软硬件协同工作测试板卡的功能。
1、准备工作
硬件环境:vivado 2017.4
软件环境:xilinx sdk 2017.4
2、硬件设计
创建工程,选择型号,添加ZYNQ的PS模块这几步就不再赘述了,直接配置ZYNQ的PS模块。
勾选GPIO的MIO和EMIO,将BANK1的电平设置为1.8V
将EMIO的位宽设置为4,实验中使用的4个LED
更换DDR型号为自己板卡对应的DDR型号,这一步必须选择正确型号,否则后续可能出现错误。
点击OK之后,将GPIO口引出,在导出硬件时会将硬件的引脚导出到软件的底层设备中,根据导出的库函数和宏定义参数编程硬件的功能。
将GPIO引出的部分修改名称为led
时钟配置,将PL端的时钟设置为100MHz给M_AXI_GP0供电
右键bd文件,点击Generate Output Products,Generate。
右键bd文件,点击Create HDL Wrapper,点击生成的顶层文件,可以看到我们配置的4位led在顶层设计中。
添加约束文件 ,前三行
生成比特流文件
点击左侧导航栏的PROGRAM AND DEBUG下的Generate Bitstream,生成完毕后cancel
导出硬件,包含比特流文件
至此,硬件设计部分已经完成。
3、软件设计
打开sdk
新建APP工程
finish,在新建的app工程中新建一个C文件用来实现软件控制硬件的工作。名称:led.c 记得加.c要不然会变成文本文件。
新建的C文件一片空白,我们该如何编程呢?点击ps_led_bsp目录下的system.mss,找到ps7_gpio,Import Examples,导入一个示例代码
可以看到生成了一个示例工程,可以直接下载该工程到板子上,但是我们如果有自己的需求的话,最好根据自己的需求和例程中代码编写自己的C文件。分析一下这个代码:我们要使用led,首先需要初始化这个IO吧,结合示例代码可知XGpioPs_CfgInitialize函数适用于初始化PS端的GPIO的配置函数,根据这个函数的参数定义其他需要的变量。初始化完成之后,既然是IO,那肯定有输入输出吧,设置输入还是输出,结合示例代码可知XGpioPs_SetDirectionPin函数用于实现该功能,再根据他里面的参数具体分析,或者打开他的源代码,看看里面是怎么实现的。这里有必要说一下这个引脚号为什么是54,这个54当时我也不是很理解,但是仔细一想并查看xgpiops.h中的宏定义可知,0~53是MIO的54个引脚,54~117是EMIO的64个引脚,我们这里使用的EMIO,它默认第一个引脚是我们使用的引脚,还记得我们在硬件设计中定义了EMIO的位宽为4吗?读者可以试一下55,56,57这三个引脚,分别对应了led2,led3,led4。虽然不知道这个对应关系是怎么实现的,但是我们知道可以从头部开始。
#include "xgpiops.h"
#define LED_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
XGpioPs Gpio;
int main(void){
int Status;
XGpioPs_Config *ConfigPtr;
/* Initialize the GPIO driver. */
ConfigPtr = XGpioPs_LookupConfig(LED_DEVICE_ID);
Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr,
ConfigPtr->BaseAddr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
XGpioPs_SetDirectionPin(&Gpio, 54, 1); // 设置引脚输出方向为 output
XGpioPs_SetOutputEnablePin(&Gpio, 54, 1); // 输出使能
/* Set the GPIO output to be high. */
XGpioPs_WritePin(&Gpio, 54, 0x1); // 点亮第一个led
}
上述代码 实现了点亮第一个led灯的效果,其他流水灯,闪烁灯均可在C文件中修改代码实现相应的功能。
下载代码到板子上,即可看到效果。勾选复位整个系统和编程到FPGA上。run!
测试结果如下所示:led1亮。实验完成,可以根据自己需求,在sdk中修改led.c文件的代码实现不同的led灯效果。学习最重要的是举一反三,不是学什么就只会什么。
补充一下后面的测试案例:
1、led闪烁
2、led流水灯
代码如下:
#include "xgpiops.h"
#define LED_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
XGpioPs Gpio;
#define LED_DELAY 50000000 // 100MHz 亮灭一次代表1s
#define led1 54
#define led2 55
#define led3 56
#define led4 57
int main(void){
int led_value = 0x1;
int delay;
int Status;
XGpioPs_Config *ConfigPtr;
/* Initialize the GPIO driver. */
ConfigPtr = XGpioPs_LookupConfig(LED_DEVICE_ID);
Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr,
ConfigPtr->BaseAddr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
XGpioPs_SetDirectionPin(&Gpio, led1, 1); // 设置引脚输出方向为 output
XGpioPs_SetOutputEnablePin(&Gpio, led1, 1); // 输出使能
XGpioPs_SetDirectionPin(&Gpio, led2, 1); // 设置引脚输出方向为 output
XGpioPs_SetOutputEnablePin(&Gpio, led2, 1); // 输出使能
XGpioPs_SetDirectionPin(&Gpio, led3, 1); // 设置引脚输出方向为 output
XGpioPs_SetOutputEnablePin(&Gpio, led3, 1); // 输出使能
XGpioPs_SetDirectionPin(&Gpio, led4, 1); // 设置引脚输出方向为 output
XGpioPs_SetOutputEnablePin(&Gpio, led4, 1); // 输出使能
//XGpioPs_Write(&Gpio,2,0xf); // 设置四个led全亮
/* // led闪烁 频率是1s亮灭一次
while (1) {
for (delay=0;delay<LED_DELAY;delay++);
XGpioPs_WritePin(&Gpio, led1, 0x0);
XGpioPs_WritePin(&Gpio, led2, 0x0);
XGpioPs_WritePin(&Gpio, led3, 0x0);
XGpioPs_WritePin(&Gpio, led4, 0x0);
for (delay=0;delay<LED_DELAY;delay++);
XGpioPs_WritePin(&Gpio, led1, 0x1);
XGpioPs_WritePin(&Gpio, led2, 0x1);
XGpioPs_WritePin(&Gpio, led3, 0x1);
XGpioPs_WritePin(&Gpio, led4, 0x1);
}
*/
// led流水灯 频率是每0.5s流动一次
while (1){
if (led_value > 0x8)
led_value = 0x1;
for (delay=0;delay<LED_DELAY;delay++);
XGpioPs_Write(&Gpio,2,led_value);
led_value <<= 1;
}
}
在我的板卡上已经验证过了以上两个功能。读者可自行测试。