Zynq-PS-SDK(1) 之 MIO 使用

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zhoutaopower/article/details/114557387

1、MIO 配置以及寄存器
Zynq 7020 的 PS 端(ARM 端)的外设 IO(也叫 IOP)分为 MIO 和 EMIO,他们有什么区别呢?

首先他们都是 PS 端的 IO 资源,MIO 有 54 个 Pin 脚,分为两个 Bank(Bank0、Bank1)是 PS 直接的管脚连接,可以接诸如 UART、SPI、IIC、GPIO 等具体的外设引脚;

EMIO 也是 PS 的资源,它接到了 PL 端,由 PL 端输出信号;

这里,我们可以简单的理解为,MIO 是可以通过 Pinmux 配置而直接连接到 PS 端外设的管脚,而 EMIO 是连接到 PL 端用于扩展的引脚;

外设的 MIO/EMIO 的链接如下所示:

这里,我们暂时只关心 MIO(后续看 EMIO),MIO 分为了两个 Bank(Bank0、Bank1),这两个 Bank 分别叫做 Bank 500 和 Bank 501,他们可以配置为不同的电压等级;

注意,这个是电压等级的 Bank,与后面的 GPIO bank 作区分;

Bank 500:MIO[15:0]

Bank 501:MIO[16:53]

提供 54 个 MIO 资源;

根据管脚复用的配置以及外设相关的资源,整个可实现的配置如下所示

MIO 的管脚复用配置逻辑如下所示:

每一路的 MIO(从 MIO[0] ~ MIO[53])都有一个这样的逐级配置,L3 的 MUX 结果可以作为 L2 MUX 的输入......最后配置出来成为最后的管脚;

举个例子,直接看寄存器;

MIO 配置的寄存器位于系统控制寄存器 slcr:

在 slcr 中:

很多类似这样的寄存器:slcr.MIO_PIN_[PIN_MUN] 其中的 PIN_MUN 就是每个 MIO 的编号,从 0 到 53,一共 54 个;

每一个的寄存器内容大致一样,我们随便抓一个看:MIO_PIN_00:


可以看到,每个 MIO 都可以配置是否上拉,电平标准(同一个 Bank 500 或者 Bank 501 电平标准必须一致),还有就是 Level 0 ~ Level 3 的 MUX,这个就是对应的上面那个 4 级 MUX 图的;

DisableRcvr:HSTL 的配置,是 High Speed Transceiver Logic 的简写,这个域是配置 MIO 的 HSTL 的;

PULLUP:配置 Piin 脚是否带上拉;

IO TYPE:配置 IO 的电平标准;

Speed:配置速度;

L3_SEL:配置管脚复用 L3 逻辑,参考管脚复用的 MIO Signal Routing 那个图;

L2_SEL:配置管脚复用 L2 逻辑,参考管脚复用的 MIO Signal Routing 那个图;

L1_SEL:配置管脚复用 L1 逻辑,参考管脚复用的 MIO Signal Routing 那个图;

L0_SEL:配置管脚复用 L0 逻辑,参考管脚复用的 MIO Signal Routing 那个图;

TRI_ENABLE:用于配置 MIO 是否三态;

注意:这个是 slcr 寄存器的配置,关于所有 slcr 寄存器的配置,都需要往 slcr.SLCR_UNLOCK 寄存器(0xF8000008)写入 0xDF0D,来解锁 slcr 寄存器的写权限;

2、工程中的配置
具体工程中,我们使用 Vivado 进行配置,首先创建 Block Design 来生成一个 PS:

双击这个大的 ZYNQ 图标,进入配置界面:

选择红色部分,并配置每个 IO 的管脚复用,并选择好他的电平标准(绿色);


配置时钟,板载 33.333333 MHZ,使用 6:2:1 时钟比例(参考  ZYNQ 时钟子系统

配置完毕,并导出硬件,Launch SDK;

3、使用 MIO-GPIO
硬件原理图中 PS 端的 LED 连接如下:

分别使用了 MIO0_LED 和 MIO13_LED,都属于 Bank 500(V3.3),此刻,我们如果想点个灯,那么需要将其配置为输出,并置 0 即可导通;

3.1、GPIO 硬件描述
UG585 的 Chapter 14 就是 PS 的 GPIO 的使用说明(这里包含了 MIO 和 EMIO,这一章,我们看 MIO)

它支持 MIO 和 EMIO 的配置,MIO 分为 2 个 IO Bank (0~1),EMIO 也分为 2 个 IO Bank(2~3):

GPIO 支持中断输入和正常的写和读:INT 开头的,是中断配置:

针对每一个pin,它都有如下寄存器:

注意:如果 MIO 的 TRI_ENABLE 这个被设置为 1,那么输出就为 3 态了;

3.2、GPIO 寄存器描述
每个 GPIO Bank 提供了一组寄存器用来描述他的行为,一共 4组,我们以 GPIO Bank 0 为例:

GPIO Bank 0 一共有 32bits 根信号;

3.2.1、MASK_0_LSW、MASK_0_MSW
Zynq 针对 GPIO 的操作,增加了一组方式,以避免对 32bit 寄存器进行读改写的操作,它的方式是将(以 GPIO Bank0 为例)的 32bit 的信号(对应 32 个 IO)分为两组,每组代表 16 bits 的 IO 信号,用两个寄存器描述:

高 16 位使用叫 MASK_0_MSW 的寄存器描述:低位 16 位使用叫 MASK_0_LSW 的寄存器描述:


他们分别有两个域,一个 Mask 域,一个 Data 域,Zynq 使用 MASK + DATA 的方式,来避免对 GPIO 寄存器的读改写;用法是:

往 Mask 中写入 1 的位置,不会被对应的 Data 中的写入改变;换句话来说,16 bit 信号中,可以选择哪些信号被 Mask,Mask 中被写入了 1 的信号,即便是 在 Data 域写入任何值, GPIO 的电平状态都不会被改变;举个栗子:

往 MIO[0] 写 1,那么就可以配置寄存器 MASK_0_LSW 寄存器的值为:0xFFF70001;

意思是,除了 MIO GPIO0 以外的其他的 1~15 都被 Mask,你往里面写什么,都无效;

3.2.2、Data_0
当然,你也可以使用读改写的方式来对 IO 进行配置,直接配置 Data_0 寄存器就可以(以 GPIO Bank0 为例):

3.2.3、Data_0_RO
当然,也可以读 GPIO 的电平值,通过直接读取 Data_0_RO 寄存器(以 GPIO Bank0 为例):

3.2.4、DIRECTION_0
GPIO 呢,当然要配置它的方向,即,输入还是输出(以 GPIO Bank0 为例):

3.2.5、OP_ENABLE_0
如果 GPIO 配置为输出了后,要真的输出信号,要配置这个输出使能寄存器(以 GPIO Bank0 为例):

3.2.6、INT_
这个是 IO 输入中断相关的配置以后用到中断在仔细介绍;

3.3、代码
Xilinx SDK 关于 GPIO 的操作,有专门的 API 接口(其实就是按照前面的寄存器配置逻辑来配置),按照具体的硬件原理图的 LED(MIO_0 和 MIO_13),代码如下:

XGpioPs Gpio;
#define STEPH_MIO_0 0
#define STEPH_MIO_1 13

#define MIO_OUTPUT 1
#define MIO_INPUT 0

#define MIO_OUTPUT_EN 1
#define MIO_OUTPUT_DIS 0

uint32_t Steph_LEDInit(void)
{
XGpioPs_Config *ConfigPtr = NULL;
int Status = XST_SUCCESS;

ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
if(!ConfigPtr)
{
xil_printf("XGpioPs_LookupConfig Error.\r\n");
return XST_FAILURE;
}

Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr,
ConfigPtr->BaseAddr);

if (Status != XST_SUCCESS) {
xil_printf("XGpioPs_CfgInitialize Error.\r\n");
return XST_FAILURE;
}

XGpioPs_SetDirectionPin(&Gpio, STEPH_MIO_0, MIO_OUTPUT);
XGpioPs_SetDirectionPin(&Gpio, STEPH_MIO_1, MIO_OUTPUT);
XGpioPs_SetOutputEnablePin(&Gpio, STEPH_MIO_0, MIO_OUTPUT_EN);
XGpioPs_SetOutputEnablePin(&Gpio, STEPH_MIO_1, MIO_OUTPUT_EN);
xil_printf("Steph_LED Initialize Finished...\r\n");

return Status;
}

void LED_ON(uint32_t led_pin)
{
XGpioPs_WritePin(&Gpio, led_pin, 0x0);
}

void LED_OFF(uint32_t led_pin)
{
XGpioPs_WritePin(&Gpio, led_pin, 0x1);
}

相关代码和工程在 gitee 持续更新:https://gitee.com/stephenzhou-tech/Zynq7020_PS

最新文章

最新文章