0%

ZYNQ嵌入式之GPIO(基于PYNQ-Z2开发板)

本节主要介绍了ZYNQ7020中的GPIO,并以按键电灯实例演示了GPIO之EMIO、中断、AXI的用法

ZYNQ与PYNQ的区别

  • PYNQ是Xilinx的一个开源项目名称,ZYNQ是Xilinx的一个芯片系列。PYNQ-Z2平台用的芯片就是Xilinx的ZYNQ芯片。所以PYNQ的意思就是PYTHON Pruduct on ZYNQ
  • PYNQ全称为Python Productivity for Zynq,即在Zynq全可编程ARM&FPGA融合处理架构的基础上,添加了对Python的支持

ZYNQ的基本组成

  • Zynq-7000 SoC的主要组成部分:
    • Processing System(PS):
      • Application processor unit(APU)
      • Memory interfaces
      • I/O peripherals(IOP)
      • Interconnect(通信互连)
    • Programmable Logic(PL)
image-20230912143428606

GPIO的简介

  • GPIO是一个外设,用来对器件的引脚作观测(input)以及控制(output)

  • MIO(Multisue I/O),将来自PS外设和静态存储器接口的访问多路复用到PS的引脚上,与外部器件访问

  • EMIO是PS和PL之间的一个接口,EMIO是扩展的MIO,当PS的引脚不够用时,可以通过EMIO来进行扩展,从而使用PL的引脚

  • GPIO可以独立且动态地编程,作为输入/输出以及中断模式

  • GPIO被分成了4个Bank,总共有118个pins

    image-20230912152039276
    • Bank0的32位控制MIO引脚的[31:0]
    • Bank1的22位控制MIO引脚的[53:32] (Bank1被限制为22位,是因为MIO总共只有54个pins)
    • Bank2的32位控制EMIO信号的[31:0]
    • Bank3的32位控制EMIO信号的[63:32]
  • 软件通过一组存储映射的寄存器来控制GPIO,GPIO控制外设的本质都是读取寄存器

  • 寄存器组(控制Bank0与Bank1)

    • DATA_RO:用来反映器件引脚的状态
    • DATA(32bit):在GPIO被配置成输出的时候,该寄存器可以控制输出的数值。如果用其来读取,只能读取到上一次的输入数据
    • MASK_DATA_LSW:屏蔽DATA中低16位的某些位
    • MASK_DATA_MSW:屏蔽DATA中高16位的某些位
    • DIRM:Direction Mode,用于控制I/O引脚是作为输入还是输出。0:关闭输出驱动;1:使能输出驱动
    • OEN:Output Enable,当I/O被配置成输出时,该寄存器用于打开/关闭输出使能
    image-20230912160107464
  • MIO[8:7]在系统复位过程中作为VMODE引脚(作为输入),用于配置MIO Bank的电压。复位结束后,MIO[8:7]只能作为输出引脚


GPIO之EMIO按键控制LED实验

1.Vivado配置

  • 在hello world的实验基础上,添加EMIO的设置

    image-20230919172215091

    image-20230919172405627

  • 给GPIO_0口添加引脚:单击GPIO_0,按快捷键ctrl+t即可实现

    image-20230919172628794
  • 生成顶层文件

    image-20230920145113467
  • 在IO引脚配置页面将EMIO链接到KEY和LED的端口

    image-20230919173043947

  • 最后生成bit stream文件

  • 在导出硬件时记得选上

    image-20230919173216883

2.Vitis代码编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include<iostream>
#include<xparameters.h>
#include<xgpiops.h>

using namespace std;

#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID

#define EMIO54_KEY0 54 //PL端的第54个pin分配给KEY0
#define EMIO55_LED0 55 //PL端的第55个pin分配给LED0

XGpioPs_Config * ConfigPtr;
XGpioPs Gpio;

int main(void)
{
cout << "GPIO EMIO TEST !"<<endl;

//配置器件的ID,查找器件的配置信息
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
//初始化GPIO驱动
XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr);

//设置GPIO的方向(0:输入/1:输出)
XGpioPs_SetDirectionPin(&Gpio,EMIO54_KEY0, 0);
XGpioPs_SetDirectionPin(&Gpio,EMIO55_LED0, 1);

//设置输出使能(1:关闭/1:打开)
XGpioPs_SetOutputEnablePin(&Gpio, EMIO55_LED0, 1);

int key_num;
while(1)
{
//通过KEY点亮LED
key_num = XGpioPs_ReadPin(&Gpio, EMIO54_KEY0);
XGpioPs_WritePin(&Gpio, EMIO55_LED0, key_num);
}

return 0;
}

3.实验结果

QQ图片20230919231606

GPIO之EMIO中断按键控制LED实验

  • 实验要求:按键按下LED亮,再按下LED熄灭

  • Vivado中的配置与EMIO按键控制LED实验一致

  • Vitis中代码的编写:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    #include<iostream>
    #include "xparameters.h"
    #include "xgpiops.h"
    #include "sleep.h"
    #include "xscugic.h"
    using namespace std;

    /***********************参数声明**********************/
    /*中断相关参数*/
    #define GPIO_INTERRUPT_ID XPAR_XGPIOPS_0_INTR
    #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
    XScuGic Intc;
    /*普通GPIO相关参数*/
    #define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
    #define EMIO54_KEY0 54 //PL端的第54个pin分配给KEY0
    #define EMIO55_LED0 55 //PL端的第55个pin分配给LED0
    XGpioPs_Config * ConfigPtr;
    XGpioPs Gpio;
    /*其他过程变量*/
    u32 key_press = 0;
    u32 led_value = 0;
    /***********************函数声明**********************/
    /*中断相关函数*/
    //配置中断函数
    void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio,u16 GpioIntrId);
    //中断服务函数
    void IntrHandler();

    /***********************主程序**********************/
    int main(void)
    {
    //配置器件的ID,查找器件的配置信息
    ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
    //初始化GPIO驱动
    XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr);

    //设置GPIO的方向(0:输入/1:输出)
    XGpioPs_SetDirectionPin(&Gpio,EMIO54_KEY0, 0);
    XGpioPs_SetDirectionPin(&Gpio,EMIO55_LED0, 1);

    //设置输出使能(1:关闭/1:打开)
    XGpioPs_SetOutputEnablePin(&Gpio, EMIO55_LED0, 1);

    //设置中断系统
    SetupInterruptSystem(&Intc, &Gpio,GPIO_INTERRUPT_ID);

    while(1)
    {
    if(key_press)
    {
    led_value = ~led_value;

    key_press = 0;
    //清除之前的中断状态
    XGpioPs_IntrClearPin(&Gpio, EMIO54_KEY0);

    //将led_vaule的值写入LED
    XGpioPs_WritePin(&Gpio, EMIO55_LED0, led_value);

    //延时消抖200ms
    usleep(200000);

    //重新打开中断
    XGpioPs_IntrEnablePin(&Gpio, EMIO54_KEY0);
    }
    }
    return 0;
    }

    //配置中断
    void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio,u16 GpioIntrId)
    {
    XScuGic_Config *IntcConfig; /* Instance of the interrupt controller */

    //查找GIC器件配置信息,并进行初始化
    IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
    XScuGic_CfgInitialize(GicInstancePtr, IntcConfig,
    IntcConfig->CpuBaseAddress);

    //初始化ARM处理器异常句柄
    Xil_ExceptionInit();
    //来给IRQ异常注册处理程序
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
    (Xil_ExceptionHandler)XScuGic_InterruptHandler,
    GicInstancePtr);
    //使能处理器中断
    Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);


    //关联中断处理函数
    XScuGic_Connect(GicInstancePtr, GpioIntrId,
    (Xil_ExceptionHandler)IntrHandler,
    (void *)Gpio);

    //设置引脚中断触发类型,下降沿触发
    XGpioPs_SetIntrTypePin(Gpio, EMIO54_KEY0, XGPIOPS_IRQ_TYPE_EDGE_FALLING);

    //为GPIO器件使能中断
    XScuGic_Enable(GicInstancePtr, GpioIntrId);

    //打开中断使能信号
    XGpioPs_IntrEnablePin(Gpio, EMIO54_KEY0);

    }

    //中断服务函数
    void IntrHandler()
    {
    cout<<"Interrupt detected !\n"<<endl;
    key_press = 1;
    //关闭中断使能信号
    XGpioPs_IntrDisablePin(&Gpio, EMIO54_KEY0);
    }
  • 实验结果:虽然实现了按键控制LED的来回跳变,但总是在手松开时才开始跳变,原因依旧存疑


AXI_GPIO控制LED实验

  • 实验要求:按键按下LED亮,再按下LED熄灭

1.AXI的简介

  • AXI(高级可扩展接口)是一种高性能、低功耗的总线协议,用于在不同的硬件模块之间进行通信
  • PS和PL之间的连接通常使用AXI接口,以实现数据传输和控制信号的交换。这种连接方式可以实现高速数据传输和灵活的系统集成,使得PS和PL之间可以进行有效的通信和协作

2.Vivado配置

  • 选择axi gpio

    image-20230921133747826

  • 用几个引脚GPIO width就选几个

    image-20230921133842345

  • zynq和EMIO实验那样配置好之后,再添加下面配置

    image-20230921134458158

  • 选择自动连线时,gpio一定要选择custom

    image-20230921134209456

  • 最终block设计如下:

    image-20230921135309192

  • 引脚的配置如下:

    image-20230921134640038

3.Vitis代码编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#include "xparameters.h"
#include "xspips.h"
#include "xil_printf.h"
#include "sleep.h"
XSpiPs Spi0;

#define SpiPs_RecvByte(BaseAddress) \
(u8)XSpiPs_In32((BaseAddress) + XSPIPS_RXD_OFFSET)

#define SpiPs_SendByte(BaseAddress, Data) \
XSpiPs_Out32((BaseAddress) + XSPIPS_TXD_OFFSET, (Data))

int spi0_init();
void SpiRead(u8 *ReadBuffer, int ByteCount);
void SpiWrite(u8 *Sendbuffer, int ByteCount);

unsigned char ReadBuffer[1024];
unsigned char WriteBuffer[1024]={1,2,3,4,5,6,7,8,9,10};
int main(void) {
int Status;
int i,j;

xil_printf("SPI Selftest Example \r\n");

Status = spi0_init();
if (Status != XST_SUCCESS) {
xil_printf("SPI Selftest Example Failed\r\n");
return XST_FAILURE;
}

for (i = 0; i < 6; i++)
{
SpiWrite(WriteBuffer,10);
SpiRead(ReadBuffer,10);
//xil_printf("read back \n");
for (j = 0; j < 10; j++ )
{
xil_printf("%d,",ReadBuffer[j]);
}
xil_printf("\n");
memset(ReadBuffer, 0x00, 1024);
sleep(1);
}
xil_printf("Successfully ran SPI Selftest Example\r\n");
return XST_SUCCESS;
}

void SpiRead(u8 *ReadBuffer, int ByteCount)
{
int Count;
u32 StatusReg;

StatusReg = XSpiPs_ReadReg(Spi0.Config.BaseAddress,
XSPIPS_SR_OFFSET);

/*
* Polling the Rx Buffer for Data
*/
do{
StatusReg = XSpiPs_ReadReg(Spi0.Config.BaseAddress,
XSPIPS_SR_OFFSET);
}while(!(StatusReg & XSPIPS_IXR_RXNEMPTY_MASK));

/*
* Reading the Rx Buffer
*/
for(Count = 0; Count < ByteCount; Count++){
ReadBuffer[Count] = SpiPs_RecvByte(
Spi0.Config.BaseAddress);
}

}

void SpiWrite(u8 *Sendbuffer, int ByteCount)
{
u32 StatusReg;
int TransCount = 0;

StatusReg = XSpiPs_ReadReg(Spi0.Config.BaseAddress,
XSPIPS_SR_OFFSET);

while ((ByteCount > 0) &&
(TransCount < XSPIPS_FIFO_DEPTH)) {
SpiPs_SendByte(Spi0.Config.BaseAddress,
*Sendbuffer);
Sendbuffer++;
++TransCount;
ByteCount--;
}

/*
* Wait for the transfer to finish by polling Tx fifo status.
*/
do {
StatusReg = XSpiPs_ReadReg(
Spi0.Config.BaseAddress,
XSPIPS_SR_OFFSET);
} while ((StatusReg & XSPIPS_IXR_TXOW_MASK) == 0);

}

int spi0_init() {
int Status;
XSpiPs_Config *SpiConfig;

/*
* Initialize the SPI device.
*/
SpiConfig = XSpiPs_LookupConfig(XPAR_XSPIPS_0_DEVICE_ID);
if (NULL == SpiConfig) {
return XST_FAILURE;
}

Status = XSpiPs_CfgInitialize(&Spi0, SpiConfig, SpiConfig->BaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}

/*
* Perform a self-test to check hardware build.
*/
Status = XSpiPs_SelfTest(&Spi0);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
xil_printf("%s self test succ\r\n", __func__);

Status = XSpiPs_SetOptions(&Spi0, XSPIPS_MASTER_OPTION);
if (Status != XST_SUCCESS) {
xil_printf("%s XSpiPs_SetOptions fail\n", __func__);
return XST_FAILURE;
}
Status = XSpiPs_SetClkPrescaler(&Spi0, XSPIPS_CLK_PRESCALE_64);
if (Status != XST_SUCCESS) {
xil_printf("%s XSpiPs_SetClkPrescaler fail\n", __func__);
return XST_FAILURE;
}
XSpiPs_Enable(&Spi0);
xil_printf("spi 0 config finish\n");
return XST_SUCCESS;
}

Reference

欢迎来到ssy的世界