前言:
参考官网demo路径:在你Vitis安装路径下面能直接找到官网demo源码。
....../Xilinx/Vitis/2021.1/data/embeddedsw/XilinxProcessorIPLib/drivers/gpio_v3_9/examples
但是要注意外设驱动的版本号。如工程生成导入的gpio驱动版本是v3_9,即使文件夹有最新的v4_7的版本,但是不能使用,因为版本不对应。
题外:虽然可以在Vitis的Platform工程中的Platform.spr导入外设demo进来,但是有时候Platform.spr显示的demo不全,故自己找到对应的demo文件夹是最好。 ?
1、硬件
芯片型号:Zynq UltraScale+ XCZU4EV-SFVC784-1-I?
由硬件原理图得知:KEY接在MIO26,按键按下是电平;LED接在MIO40。
?
?
?
2、软件环境
Ubuntu20.04 + Vitis2021.1 + Vivado2021.1
?3、Vivado配置
KEY所在引脚是MIO26,LED的所在的引脚是MOI40 ,那么在GPIO外设这项选中GPIO1 MIO该项,该项意思是选中MIO26~MIO51这个范围。下拉还能设置其他的功能,如速度,上下拉,输出输入等。
4、Vitis编程
? ? ? ? 实现目标:中断检测按键所在MIO26的电平变化,当电平变化是下降沿的时候触发中断处理函数。在中断处理函数中控制LED的当前输出状态的反电平,从而实现控制LED亮灭。
/*
* main.c
*
* Created on: Aug 6, 2021
* Author: sudaroot
*/
#include "stdio.h"
#include "xgpiops.h"
#include "xscugic.h"
#include "xparameters.h"
#include "sleep.h"
#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID /* GPIO外设设备号 */
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define GPIO_INTERRUPT_ID XPAR_XGPIOPS_0_INTR
#define KEY_MIO_NUM 26 /* 按键KEY所在IO号 */
#define LED_MIO_NUM 40 /* LED灯所在IO号 */
static int GPIO_MIO_InputOutput_Init(XGpioPs *GpioPtr);
static int SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio, u16 GpioIntrId);
static void IntrHandler(void *CallBackRef, u32 Bank, u32 Status);
XGpioPs Gpio; /* GPIO驱动结构体实例 */
XScuGic Intc; /* The Instance of the Interrupt Controller Driver */
int main(void)
{
xil_printf("Hello Sudaroot, GPIO_MIO_INT_TEST\r\n");
GPIO_MIO_InputOutput_Init(&Gpio);
SetupInterruptSystem(&Intc, &Gpio, GPIO_INTERRUPT_ID);
while(1)
{
sleep(1);
}
}
static int GPIO_MIO_InputOutput_Init(XGpioPs *GpioPtr)
{
int Status;
XGpioPs_Config *ConfigPtr;
/* 初始化GPIO外设 */
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
if (ConfigPtr == NULL) {
return XST_FAILURE;
}
Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/* 配置KEY IO为输入模式。 */
XGpioPs_SetDirectionPin(&Gpio, KEY_MIO_NUM, 0x0);
/* 配置LED IO为输出模式,然后使能输出。 */
XGpioPs_SetDirectionPin(&Gpio, LED_MIO_NUM, 1);
XGpioPs_SetOutputEnablePin(&Gpio, LED_MIO_NUM, 1);
return XST_SUCCESS;
}
static int SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio, u16 GpioIntrId)
{
int Status;
XScuGic_Config *IntcConfig; /* Instance of the interrupt controller */
// 异常处理初始化
Xil_ExceptionInit();
// 初始化中断
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (NULL == IntcConfig) {
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(GicInstancePtr, IntcConfig,
IntcConfig->CpuBaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
// 将中断控制器中断处理程序连接到处理器中的硬件中断处理逻辑。
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
GicInstancePtr);
// GPIO外设中断连接到系统默认处理函数
Status = XScuGic_Connect(GicInstancePtr, GpioIntrId,
(Xil_ExceptionHandler)XGpioPs_IntrHandler,
(void *)Gpio);
if (Status != XST_SUCCESS) {
return Status;
}
// 使能下降沿触发中断
XGpioPs_SetIntrTypePin(Gpio, KEY_MIO_NUM, XGPIOPS_IRQ_TYPE_EDGE_FALLING);
// GPIO中断处理回调函数
XGpioPs_SetCallbackHandler(Gpio, (void *)Gpio, IntrHandler);
// 使能引脚中断
XGpioPs_IntrEnablePin(Gpio, KEY_MIO_NUM);
// 使能GPIO外设中断
XScuGic_Enable(GicInstancePtr, GpioIntrId);
// 使能全局中断
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
return XST_SUCCESS;
}
static void IntrHandler(void *CallBackRef, u32 Bank, u32 Status)
{
u32 led_pin = 0;
XGpioPs *Gpio = (XGpioPs *)CallBackRef;
if(XGpioPs_ReadPin(Gpio, KEY_MIO_NUM) == 0)
{
printf("IntrKey\r\n");
XGpioPs_IntrClearPin(Gpio, KEY_MIO_NUM);
led_pin = XGpioPs_ReadPin(Gpio, LED_MIO_NUM);
XGpioPs_WritePin(Gpio, LED_MIO_NUM, !led_pin);
}
}
5、相关API说明
1、XGpioPs_SetIntrTypePin()和?XGpioPs_IntrEnablePin()及XGpioPs_IntrClearPin()这3个函数是设置单个GPIO的中断触发类型和使能中断及清除中断标志位的。而XGpioPs_SetIntrType()和 XGpioPs_IntrEnable()及 XGpioPs_IntrClear()则是设置一个bank上的GPIO的中断相关的。但是每个bank与gpio引脚号的关系需要看手册,一一对应,易出错。
2、下面看看这三个函数的关系:? Xil_ExceptionRegisterHandler、XScuGic_Connect、XGpioPs_SetCallbackHandler。
// 将中断控制器中断处理程序连接到处理器中的硬件中断处理逻辑。
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
?? ??? ??? ??? ?(Xil_ExceptionHandler)XScuGic_InterruptHandler,
?? ??? ??? ??? ?GicInstancePtr);
// GPIO外设中断连接到系统默认处理函数
Status = XScuGic_Connect(GicInstancePtr, GpioIntrId,
?? ??? ??? ??? ?(Xil_ExceptionHandler)XGpioPs_IntrHandler,
?? ??? ??? ??? ?(void *)Gpio);
// 某个引脚中断处理回调函数
XGpioPs_SetCallbackHandler(Gpio, (void *)Gpio, IntrHandler);
Xil_ExceptionRegisterHandler这是系统设置外设中断响应函数,XScuGic_InterruptHandler()然后在中断向量表中找到对应的外设中断处理函数。
主要看XScuGic_Connect()中的参数XGpioPs_IntrHandler函数指针,这个XGpioPs_IntrHandler函数是库函数,所以的GPIO中断xilinx都希望通过该函数在跳转到用户的中断回调函数IntrHandler()。
所以这个函数XGpioPs_SetCallbackHandler主要就是设置用户的中断回调函数。
但是这样会发现一个效率问题,如果频繁发生中断,那么函数套的太多了。故可以在XScuGic_Connect()直接设置用户的中断回调函数IntrHandler()提高效率,减少一层回调。
6、效果:
?
全篇完。
本人是一个嵌入式未入门小白,博客仅仅代表我个人主观见解,记录成长笔记。 笔记是以最简单的方式,只展示最核心的原理。 若有与 大神大大 见解有歧义,我绝对坚信 大神大大 见解是对的,我的是错的。 若无积分等无法下载源码,可私聊私发。 感谢~!
?
|