一般来说ZYNQ可以有三种方式实现流水灯:MIO,EMIO,AXI GPIO。但是由于赛灵思的ZYNQ开发板MIO资源没有LED,所以实现流水灯只能EMIO或者AXI GPIO。这里使用AXI GPIO实现流水灯,为了更多的使用板子资源,在流水灯的基础上增加了按键,中断,定时器,串口等功能可以实现流水灯的方向的切换,精准定时等功能。同时也会给出AXI GPIO和EMIO实现流水灯的代码。
创建一个工程,添加ZYNQ IP核
点击自动连线
完成后双击打开,配置EMIO
配置EMIO位宽为2
然后添加AXI GPIO
点击自动连线
左边全勾上,接口选择led
然后点击ZYNQ核上的GPIO,刚刚才配置的,设置将引脚扩展处来,为后面PL分配引脚做准备。
然后验证当前设计,出现如下弹窗,说明设计结果正确。
在 Sources 窗口中,选中 Design Sources 下的 .bd文件生成HDL 模块
然后进行分析与综合
分配引脚为刚刚我们设置的两个EMIO
按键原理图
设置按键引脚和电源值
保存引脚约束文件
然后生成字节流,因为用到了PL部分
将硬件导出
启动vitis进入软件编程部分
这里工程的建立就不说了,具体的可以参考前面的
主要是初始化LED和设置LED的输入输出方向
int led_init(void)
{
int Status;
//初始化GPIO
Status = XGpio_Initialize(&Axi_Gpio, GPIO_DEVICE_ID);
if (Status != XST_SUCCESS) //判断是否初始化成功
{
return XST_FAILURE;
}
//设置GPIO输入/输出模型 0:输出 1:输入
XGpio_SetDataDirection(&Axi_Gpio, led_channel, 0x00);
return XST_SUCCESS;
}
主要是初始化Key和设置Key的输入输出方向
int key_init(void)
{
int Status;
//查找器件的ID,查找器件的配置信息
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
if (ConfigPtr == NULL)
{
return XST_FAILURE;
}
//初始化GPIO驱动
Status = XGpioPs_CfgInitialize(&Key_Gpio, ConfigPtr,ConfigPtr->BaseAddr);
if (Status != XST_SUCCESS) //判断是否初始化成功
{
return XST_FAILURE;
}
//设置输出使能(最后一个参数:0表示关闭使能,1表示打开)
XGpioPs_SetOutputEnablePin(&Key_Gpio, EMIO_KEY0, 1);
XGpioPs_SetOutputEnablePin(&Key_Gpio, EMIO_KEY1, 1);
//把GPIO的方向设置为输入(最后一个参数:0表示输入,1表示输出)
XGpioPs_SetDirectionPin(&Key_Gpio, EMIO_KEY0, 0);
XGpioPs_SetDirectionPin(&Key_Gpio, EMIO_KEY1, 0);
return XST_SUCCESS;
}
初始化定时器并且设置定时器的周期和模式。定时器周期计算主要是要主要私有定时器的时钟频率为CPU工作频率的一半。
int timer_init(void)
{
int Status;
//私有定时器初始化
XScuTimer_Config *timer_cfg_ptr;
timer_cfg_ptr = XScuTimer_LookupConfig(TIMER_DEVICE_ID);
if (timer_cfg_ptr == NULL)
{
return XST_FAILURE;
}
Status = XScuTimer_CfgInitialize(&Timer, timer_cfg_ptr,timer_cfg_ptr->BaseAddr);
if (Status != XST_SUCCESS) //判断是否初始化成功
{
return XST_FAILURE;
}
XScuTimer_LoadTimer(&Timer, TIMER_LOAD_VALUE); // 加载计数周期
XScuTimer_EnableAutoReload(&Timer); // 设置自动装载模式
return XST_SUCCESS;
}
中断处理固定模式:
①初始化中断控制器
②初始化CPU异常功能、向CPU注册异常处理回调函数、使能异常处理功能
③使能中断控制器、设置中断的类型、使能对应引脚的中断
④设置中断的回调函数(用户自己设置)
int interrupt_init()
{
int Status;
//初始化中断控制器
XScuGic_Config *gic_cfg; //中断控制器配置信息
gic_cfg = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (gic_cfg == NULL)
{
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(&Intc,gic_cfg,gic_cfg->CpuBaseAddress);
if (Status != XST_SUCCESS) //判断是否初始化成功
{
return XST_FAILURE;
}
//初始化异常处理
Xil_ExceptionInit();
//CPU中断异常注册
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&Intc);
//使能处理器中断
Xil_ExceptionEnable();;
//设置定时器中断: timer_intr_handler 中断处理函数
XScuGic_Connect(&Intc, TIMER_IRPT_INTR,(Xil_ExceptionHandler)timer_intr_handler, &Timer);
XScuGic_Enable(&Intc,TIMER_IRPT_INTR);//使能 GIC 中的定时器中断
XScuTimer_EnableInterrupt(&Timer); //使能定时器中断
return XST_SUCCESS;
}
EMIO代码:
#include "stdio.h"
#include "xparameters.h"
#include "xgpiops.h"
#include "sleep.h"
#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
//EMIO引脚从54开始算起
#define EMIO_LED0 54
#define EMIO_LED1 55
#define EMIO_LED2 56
#define EMIO_LED3 57
#define EMIO_KEY0 58
#define EMIO_KEY1 59
int key_scan();
XGpioPs_Config * ConfigPtr;
XGpioPs Gpio; /* The driver instance for GPIO Device. */
int main()
{
int key_value;
//查找器件的ID,查找器件的配置信息
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
//初始化GPIO驱动
XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr);
//LED:
//把GPIO的方向设置为输出(最后一个参数:0表示输入,1表示输出)
XGpioPs_SetDirectionPin(&Gpio, EMIO_LED0, 1);
XGpioPs_SetDirectionPin(&Gpio, EMIO_LED1, 1);
XGpioPs_SetDirectionPin(&Gpio, EMIO_LED2, 1);
XGpioPs_SetDirectionPin(&Gpio, EMIO_LED3, 1);
//设置输出使能(最后一个参数:0表示关闭使能,1表示打开)
XGpioPs_SetOutputEnablePin(&Gpio, EMIO_LED0, 1);
XGpioPs_SetOutputEnablePin(&Gpio, EMIO_LED1, 1);
XGpioPs_SetOutputEnablePin(&Gpio, EMIO_LED2, 1);
XGpioPs_SetOutputEnablePin(&Gpio, EMIO_LED3, 1);
//KEY:
//把GPIO的方向设置为输入(最后一个参数:0表示输入,1表示输出)
XGpioPs_SetDirectionPin(&Gpio, EMIO_KEY0, 0);
XGpioPs_SetDirectionPin(&Gpio, EMIO_KEY1, 0);
while(1)
{
key_value = key_scan();
if(key_value ==1)
{
//将按键的状态写入LED
XGpioPs_WritePin(&Gpio, EMIO_LED1,1);
}
else if(key_value ==2)
{
XGpioPs_WritePin(&Gpio, EMIO_LED1,0);
}
}
return 0;
}
int key_scan()
{
int val=0;
if(XGpioPs_ReadPin(&Gpio, EMIO_KEY0) == 1)
val = 1;
if(XGpioPs_ReadPin(&Gpio, EMIO_KEY1) == 1)
val = 2;
return val;
}
AXI GPIO代码:
#include "xparameters.h"
#include "xgpiops.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "xplatform_info.h"
#include
#include "xgpio.h"
#include "xscutimer.h"
#include "xil_exception.h"
#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID //GPIO ID
#define TIMER_DEVICE_ID XPAR_XSCUTIMER_0_DEVICE_ID //定时器 ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID //中断控制器 ID
#define TIMER_IRPT_INTR XPAR_SCUTIMER_INTR //定时器中断 ID
static XScuGic Intc; //中断驱动程序实例
static XScuTimer Timer; //定时器驱动程序实例
#define TIMER_LOAD_VALUE 0x1EFE920 //定时器装载值 100ms中断 1EFE920
//EMIO KEY
static XGpioPs Key_Gpio; //gpio ps 驱动实例
static XGpioPs_Config * ConfigPtr;
#define EMIO_KEY0 54
#define EMIO_KEY1 55
//AXI GPIO
#define led_channel 1 //LED灯引脚
static XGpio Axi_Gpio; //GPIO设备的驱动实例
static int key_val,key_down,key_up,key_old; //按键处理
static int delay_cnt=0; //用于精准延时
int led_init(void)
{
int Status;
//初始化GPIO
Status = XGpio_Initialize(&Axi_Gpio, GPIO_DEVICE_ID);
if (Status != XST_SUCCESS) //判断是否初始化成功
{
return XST_FAILURE;
}
//设置GPIO输入/输出模型 0:输出 1:输入
XGpio_SetDataDirection(&Axi_Gpio, led_channel, 0x00);
return XST_SUCCESS;
}
int key_init(void)
{
int Status;
//查找器件的ID,查找器件的配置信息
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
if (ConfigPtr == NULL)
{
return XST_FAILURE;
}
//初始化GPIO驱动
Status = XGpioPs_CfgInitialize(&Key_Gpio, ConfigPtr,ConfigPtr->BaseAddr);
if (Status != XST_SUCCESS) //判断是否初始化成功
{
return XST_FAILURE;
}
//设置输出使能(最后一个参数:0表示关闭使能,1表示打开)
XGpioPs_SetOutputEnablePin(&Key_Gpio, EMIO_KEY0, 1);
XGpioPs_SetOutputEnablePin(&Key_Gpio, EMIO_KEY1, 1);
//把GPIO的方向设置为输入(最后一个参数:0表示输入,1表示输出)
XGpioPs_SetDirectionPin(&Key_Gpio, EMIO_KEY0, 0);
XGpioPs_SetDirectionPin(&Key_Gpio, EMIO_KEY1, 0);
return XST_SUCCESS;
}
int key_scan()
{
int val=0;
if(XGpioPs_ReadPin(&Key_Gpio, EMIO_KEY0) == 1)
val = 1;
if(XGpioPs_ReadPin(&Key_Gpio, EMIO_KEY1) == 1)
val = 2;
return val;
}
//中断处理函数 100ms
void timer_intr_handler(void *CallBackRef)
{
XScuTimer *timer_ptr = (XScuTimer *) CallBackRef;
delay_cnt += 1;
//处理按键
key_val = key_scan();
key_down = key_val & (key_old^key_val);
key_up = ~key_val & (key_old^key_val);
key_old = key_val;
//清除定时器中断标志
XScuTimer_ClearInterruptStatus(timer_ptr);
}
int interrupt_init()
{
int Status;
//初始化中断控制器
XScuGic_Config *gic_cfg; //中断控制器配置信息
gic_cfg = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (gic_cfg == NULL)
{
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(&Intc,gic_cfg,gic_cfg->CpuBaseAddress);
if (Status != XST_SUCCESS) //判断是否初始化成功
{
return XST_FAILURE;
}
//初始化异常处理
Xil_ExceptionInit();
//CPU中断异常注册
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&Intc);
//使能处理器中断
Xil_ExceptionEnable();;
//设置定时器中断: timer_intr_handler 中断处理函数
XScuGic_Connect(&Intc, TIMER_IRPT_INTR,(Xil_ExceptionHandler)timer_intr_handler, &Timer);
XScuGic_Enable(&Intc,TIMER_IRPT_INTR);//使能 GIC 中的定时器中断
XScuTimer_EnableInterrupt(&Timer); //使能定时器中断
return XST_SUCCESS;
}
int timer_init(void)
{
int Status;
//私有定时器初始化
XScuTimer_Config *timer_cfg_ptr;
timer_cfg_ptr = XScuTimer_LookupConfig(TIMER_DEVICE_ID);
if (timer_cfg_ptr == NULL)
{
return XST_FAILURE;
}
Status = XScuTimer_CfgInitialize(&Timer, timer_cfg_ptr,timer_cfg_ptr->BaseAddr);
if (Status != XST_SUCCESS) //判断是否初始化成功
{
return XST_FAILURE;
}
XScuTimer_LoadTimer(&Timer, TIMER_LOAD_VALUE); // 加载计数周期
XScuTimer_EnableAutoReload(&Timer); // 设置自动装载模式
return XST_SUCCESS;
}
int main(void)
{
xil_printf("My test!\r\n");
//led初始化
int Status,key;
int lval = 0x1,rval = 0x1;
Status = led_init();
if (Status != XST_SUCCESS)
{
xil_printf("LEDs init failed!\r\n");
}
//key初始化
Status = key_init();
if (Status != XST_SUCCESS)
{
xil_printf("Key init failed!\r\n");
}
//定时器初始化
Status = timer_init();
if (Status != XST_SUCCESS)
{
xil_printf("timer init failed!\r\n");
}
//中断初始化
Status = interrupt_init(); //一定要先定时器初始化后再中断初始化
if (Status != XST_SUCCESS)
{
xil_printf("interrupt init failed!\r\n");
}
XScuTimer_Start(&Timer); //启动定时器
while(1)
{
if(key_down ==1) //右移
{
key = 1;
}
else if(key_down ==2)
{
key = 2;
}
if(key == 1)
{
if(delay_cnt >= 10) //1s
{
//将按键的状态写入LED
XGpio_DiscreteWrite(&Axi_Gpio, led_channel, rval);
rval = rval >>1;
if(rval < 0x1)
{
rval = 0x8;
}
delay_cnt = 0;
}
}
else if(key == 2)
{
if(delay_cnt >= 10)
{
//将按键的状态写入LED
XGpio_DiscreteWrite(&Axi_Gpio, led_channel, lval);
lval = lval <<1;
if(lval > 0x8)
{
lval = 0x1;
}
delay_cnt = 0;
}
}
}
return 0;
}