使用小梅哥zynq7020开发板,参考UG585和小梅哥裸机教程及示例代码,实现寄存器方式GPIO点灯记录:
先放一张UG585里面提供的GPIO架构图:

左边就是与GPIO有关的各个寄存器,INT开头的和中断相关,下面的就是读写寄存器、读写方向控制寄存器、输出使能寄存器,MIO Banks被复用到GPIO,GPIO输出三态门(我的理解这个三态门和STM32里面GPIO外设应该是一样道理)
与STM32类似,寄存器控制GPIO写/输出:
1、Vivado部分:block design选择Zynq IP硬核,IO Peripheral中选择GPIO,选几个都行,之后布线布局综合生产xsa
2、Vitis部分:参考UG585 Chapter14.3.2
1、Set the direction as output: Write 0x0000_0400 to the gpio.DIRM_0 register.
2、Set the output enable: Write 0x0000_0400 to the gpio.OEN_0 register. The output enable has significance only when the GPIO pin is configured as an output.
3、GPIO写数:可以使用DATA_0 register或者MASK_DATA_x_MSW/LSW,两个寄存器,我用的是MASK_DATA_x_LSW(小梅哥zynq 7020 板子PS_LED对应管脚是MIO7),处于Bank0,对应Bank0寄存器所在地址XGPIOPS_DATA_LSW_OFFSET。
注意这个寄存器对应Bank0的0~15的GPIO Pin,MSW的寄存器对应控制Bank0中的16~31的GPIO管脚。MASK_DATA_x_MSW高16位用于使能16个GPIO Pin脚中是否可以输出,低16位对应要输出的值,例如Pin 7应该在第23位(高16位+7位对应Pin7)写入0,低位的第7位写入想要输出的值,从而进行GPIO输出,对应复用MIO 7(LED Pin)的GPIO程序如下,这个程序写的不如小梅哥没有对寄存器其他位的值进行保护,纯自己好玩,仅供参考:
这里用uint32t和u32都可以。Xil_Out/Xil_In是Xilinx官方提供的读写寄存器的函数,在xil_io.h里面。
#include "xil_types.h"
#include "xil_io.h"
#include <stdio.h>
#define GPIO_BASEADDR 0xE000A000
#define XGPIOPS_DIRM_OFFSET 0xE000A204
#define XGPIOPS_OUTEN_OFFSET 0xE000A208
#define XGPIOPS_DATA_LSW_OFFSET 0xE000A000
int main()
{
uint32_t reg_val = 0;
uint32_t data_in = 0;
uint32_t data_out = 0;
reg_val = Xil_In32(XGPIOPS_DIRM_OFFSET);
data_in = reg_val|(0x1 << 7);
Xil_Out32(XGPIOPS_DIRM_OFFSET, data_in);
Xil_Out32(XGPIOPS_OUTEN_OFFSET, data_in);
while(1)
{
Xil_Out32(XGPIOPS_DATA_LSW_OFFSET, (0 << 7) <<16 );
Xil_Out32(XGPIOPS_DATA_LSW_OFFSET, (0x1 << 7));
usleep(200000);
Xil_Out32(XGPIOPS_DATA_LSW_OFFSET, (0 << 7) <<16);
Xil_Out32(XGPIOPS_DATA_LSW_OFFSET, (0x0 << 7));
usleep(200000);
}
}
调用BSP库函数的方式读写GPIO:
类似于STM32调用现成的bsp库模式,Vitis也提供一套自己的Bsp库,这个比读写寄存器的方式好弄,不用一个个去查寄存器了,参考Vitis提供的官方例程,xgpiops_polled_example,初始化函数、读写使能函数、读写函数,封装函数都给写好了,直接抄就完了,具体代码参考小梅哥zynq7020开发板代码。
可以使用板子上的按键去控制灯的亮灭,对于小梅哥ZYNQ7020 开发板,按键连接的管脚是MIO47,我的理解对应为BANK1,使用GPIO复用MIO47,使用GPIO bsp库中的函数
XGpioPs_SetDirectionPin设置47管脚为输入,即读取值,如果按键按下,此时管脚被拉到地,读到的值应该是0,以此为判断,给LED管脚Pin 7以一定的延时交替输出0和1即可,注意不同于操作寄存器的方式,调用BSP库的方式读写BANK1的管脚不需要更换寄存器,仍然使用bsp提供的函数(API):XGpioPs_ReadPin即可。
GPIO中断:
ug585 Chapter7给出了中断的架构:

中断分为SPI、SGI、PPI,对应共享外设中断、软件生成中断和私有外设中断,PPI对应定时器、看门狗等,GPIO产生中断对应SPI。

之后参考xilinx提供的GPIO中断例程和小梅哥教程,中断部分的函数如下所示:
#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define GPIO_INTERRUPT_ID XPAR_XGPIOPS_0_INTR
#define GPIO_BANK XGPIOPS_BANK1
static XGpioPs Gpio;
static XScuGic Intc;
//中断相关函数
XScuGic_Config *IntcConfig;
Xil_ExceptionInit();
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (NULL == IntcConfig) {
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(&Intc, IntcConfig,
IntcConfig->CpuBaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
&Intc);
Status = XScuGic_Connect(&Intc, GPIO_INTERRUPT_ID,
(Xil_ExceptionHandler)XGpioPs_IntrHandler,
&Gpio);
if (Status != XST_SUCCESS) {
return Status;
}
/* Enable falling edge interrupts for all the pins in bank 0. */
XGpioPs_SetIntrType(&Gpio, GPIO_BANK, 0x00, 0xFFFFFFFF, 0x00);
/* Set the handler for gpio interrupts. */
XGpioPs_SetCallbackHandler(&Gpio, &Gpio, IntrHandler);
/* Enable the GPIO interrupts of Bank 0. */
XGpioPs_IntrEnable(&Gpio, GPIO_BANK, (1 << Input_Pin - 32));
/* Enable the interrupt for the GPIO device. */
XScuGic_Enable(&Intc, GPIO_INTERRUPT_ID);
/* Enable interrupts in the Processor. */
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
XScuGic_InterruptHandler是总的中断处理函数,GIC相关中断处理函数。
XScuGic_Connect处理的是:GPIO中断服务函数和GPIO的中断源进行关联,如果产生GPIO中断,那么此时应该去执行这个中断处理函数XGpioPs_IntrHandler,如果ID的是GPIO的中断源,那么运行这个中断函数。其中中断处理函数为:
void XGpioPs_IntrHandler(const XGpioPs *InstancePtr)
{
u8 Bank;
u32 IntrStatus;
u32 IntrEnabled;
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
for (Bank = 0U; Bank < InstancePtr->MaxBanks; Bank++) {
IntrStatus = XGpioPs_IntrGetStatus(InstancePtr, Bank);
IntrEnabled = XGpioPs_IntrGetEnabled(InstancePtr,Bank);
if ((IntrStatus & IntrEnabled) != (u32)0) {
XGpioPs_IntrClear(InstancePtr, Bank,
(IntrStatus & IntrEnabled));
InstancePtr->Handler(InstancePtr->
CallBackRef, Bank,
(IntrStatus & IntrEnabled));
}
}
}
这个函数我理解是处理INT_STAT、INT_EN寄存器,使能中断。后面的XGpioPs_SetIntrType处理的是INT_TYPE、INT_POLARITY、INT_ANY寄存器。
SetCallbackHandler函数设置IntrHandler回调函数,回调函数代码:
static void IntrHandler(void *CallBackRef, u32 Bank, u32 Status)
{
XGpioPs *Gpio = (XGpioPs *)CallBackRef;
u32 DataRead;
/* Push the switch button */
DataRead = XGpioPs_ReadPin(Gpio, Input_Pin);
if (DataRead == 0) {
flag++;
}
}
表示按键按下,读到MIO47对应GPIO为0,全局静态变量flag = 1,在主函数中设置flag为1的时候,LED闪烁的函数即可实现按键按下,读到GPIO输入为0产生中断,控制LED灯的亮灭。
里面的XGpioPs_IntrEnable(&Gpio, GPIO_BANK, (1 << Input_Pin - 32)); 这个函数操作的是XGPIOPS_INTEN_OFFSET,作用是:使能GPIO的输入作为中断源。需要注意的是这里Key按键是47,前面制定了Bank1,那么对于MIO47复用的GPIO,对应的应该是GPIO Bank1的第47-32,即第15位输入为中断源,而不是整个GPIO的47。对于GPIO的读写操作函数比如XGpioPs_WritePin,可以直接写对应的管脚号47,那是因为函数内部有相关判断和操作代码:
if (PinNumber > 15U) {
/* There are only 16 data bits in bit maskable register. */
PinNumber -= (u8)16;
RegOffset = XGPIOPS_DATA_MSW_OFFSET;
} else {
RegOffset = XGPIOPS_DATA_LSW_OFFSET;
}
DataVar &= (u32)0x01;
Value = ~((u32)1 << (PinNumber + 16U)) & ((DataVar << PinNumber) | 0xFFFF0000U);
XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
((u32)(Bank) * XGPIOPS_DATA_MASK_OFFSET) +
RegOffset, Value);
整体代码如下:
#include "xparameters.h"
#include "xgpiops.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "xplatform_info.h"
#include <xil_printf.h>
#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define GPIO_INTERRUPT_ID XPAR_XGPIOPS_0_INTR //#define XPS_GPIO_INT_ID 52U
/* The following constants define the GPIO banks that are used. */
#define GPIO_BANK XGPIOPS_BANK1
#define Output_Pin 7
#define Input_Pin 47
static XGpioPs Gpio;
static XScuGic Intc;
static int flag;
static void IntrHandler(void *CallBackRef, u32 Bank, u32 Status)
{
XGpioPs *Gpio = (XGpioPs *)CallBackRef;
u32 DataRead;
/* Push the switch button */
DataRead = XGpioPs_ReadPin(Gpio, Input_Pin);
if (DataRead == 0) {
flag++;
}
}
int main()
{
XGpioPs_Config *ConfigPtr;
int Status;
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
if (ConfigPtr == NULL) {
return XST_FAILURE;
}
XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr);
XGpioPs_SetDirectionPin(&Gpio, Input_Pin, 0x0);
XGpioPs_SetDirectionPin(&Gpio, Output_Pin, 1);
XGpioPs_SetOutputEnablePin(&Gpio, Output_Pin, 1);
XGpioPs_WritePin(&Gpio, Output_Pin, 0x0);
XScuGic_Config *IntcConfig; /* Instance of the interrupt controller */
Xil_ExceptionInit();
/*
* Initialize the interrupt controller driver so that it is ready to
* use.
*/
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (NULL == IntcConfig) {
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(&Intc, IntcConfig,
IntcConfig->CpuBaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
&Intc);
Status = XScuGic_Connect(&Intc, GPIO_INTERRUPT_ID,
(Xil_ExceptionHandler)XGpioPs_IntrHandler,
&Gpio);
if (Status != XST_SUCCESS) {
return Status;
}
/* Enable falling edge interrupts for all the pins in bank 0. */
XGpioPs_SetIntrType(&Gpio, GPIO_BANK, 0x00, 0xFFFFFFFF, 0x00);
/* Set the handler for gpio interrupts. */
XGpioPs_SetCallbackHandler(&Gpio, &Gpio, IntrHandler);
/* Enable the GPIO interrupts of Bank 0. */
XGpioPs_IntrEnable(&Gpio, GPIO_BANK, (1 << Input_Pin - 32));
/* Enable the interrupt for the GPIO device. */
XScuGic_Enable(&Intc, GPIO_INTERRUPT_ID);
/* Enable interrupts in the Processor. */
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
while(1)
{
if(flag > 0)
{
flag--;
XGpioPs_WritePin(&Gpio, Output_Pin, 0x1);
usleep(500000);
XGpioPs_WritePin(&Gpio, Output_Pin, 0x0);
usleep(500000);
}
}
}
实现按键按下,GPIO控制的灯亮。
整体代码仅供参考,参考来源为小梅哥ACZ702裸机教程和Xilinx GPIO中断例程,写这篇文章纯粹自己为了记录一下,捋一下思路。
小梅哥Zynq7020开发板GPIO点灯记录
1251

被折叠的 条评论
为什么被折叠?



