GPIO Product Guide笔记(Xilinx)

来源:互联网 发布:网络规划设计师有用吗 编辑:程序博客网 时间:2024/05/19 22:59

GPIO是通用并行IO接口的简称。他将总线信号转化为IO设备要求的信号类型,实现地址译码输出数据,锁定输入数据缓冲的功能。GPIO控制器的基本结构如图1所示。

这里写图片描述 
总线接口模块实现地址译码,并将特定总线信号转化为内部总线;中断逻辑模块,根据中断控制以及中断产生条件产生中断请求信号。输入/输出控制模块将内部总线信号转化为基本的输入/输出引线,并实现输出数据锁定,输入数据暂存的功能。

图中只画出1位数据的原理图,多位数据需要多组都同样的结构。输入/输出控制模块包括内部译码控制模块。输出使能控制寄存器(GPIO_TRI),输入数据输出寄存器数据输入寄存器(GPIO_DATA),数据采样寄存器(GPIO_IN)。需要首先向(GPIO_TRI) 写入零。然后再向 GPIO_DATA写数据。数据才能传输到外部引脚上。如果要输入外部引脚的数据,那么必须同时使能 GPIO_DATA_IN和 READ_REG_IN。并且使得MUX选择GPIO_IO的数据。同样可以通过控制MUX读取使能状态GPIO_TRI,由输入输出模块的原理图,可知程序可以先通过读取什么状态,了解外部引脚信号再根据需要修改输出到GPIO_IO上。

以下以赛灵思AXI总线GPIO IP核为例来介绍GPIO控制器的具体应用。该 GPIO控制器IP核支持两个独立的 GPIO通道,并且每个通道可以支持1-32位的数据输入/输出,可配置为单,输入单输出或双向输入输出。

控制器包括接口总线模块,中断产生逻辑模块以及双通道输入输出模块。双通道输入输出模块的原理框图2如下,该GPIO包括两个通道,并且独立工作,只有中断信号由同一个一个引脚输出,两个通道都可以输入输出,但是任意时刻仅作为输入或输出接口使用,其数据的传输方向通过什么控制,当输出低电平时,数据输出,反之则输入。 
这里写图片描述 
GPIO内部寄存器如表所示,所有寄存器采用小字节序,即数据的第一位对应引脚的地位。 GPIO的各部分分别控制 GPIO的各位为输入或输出,当 GPIO_TRI末位为0时,GPS对应的IO配置为输出。当GPIO_TRI某位为1,相应的IO配置为输入。如果我们只是配置 GPIO,作为简单的输出接口,不需要使用中断机制,那么对 GPIO编程控制,只需要先写GPIO_TRI,然后再输出数据到 GPIO_DATA就可以了。 
这里写图片描述 
2.具体实现 
Tips:这次演示一个Microblaze的流水灯

* background:首先你要在你的FPGA开发板(比如ZYBO)搭建好软核* Add IP ->gpio* 选择你要用的通道,比如我有7个LED,都为OUTPUT,你可以选择All output或者不选择(会作为inout)之后在SDK调用时对GPIO_TRI写0作为输出端口,连接好GPIO的时钟、控制线、复位(可以选择自动连接)

这里写图片描述
* 修改顶层模块的端口定义,加上LED 
* 修改XDC文件 
* 综合编译工程,生成bit文件 
* Export Hardware->Launch SDK 
* 编写C语言程序(这个就很简单了。。。)首先你要看看你的GPIO BASE Address是多少,每个外设都有自己的地址,学过微机原理的童鞋都好理解。如图所示,我的是0x40010000(记得去掉中间的”_”,SDK会报错)

这里写图片描述

我们先介绍一下GPIO外设常用的几个函数(学过51单片机,或者是STM32的童鞋就非常容易上手) 
其实这是直接对寄存器操作的函数,并不是GPIO外设特有的。(详细定义参考xil_io.h头文件)

    * #define Xil_In8(Addr)  (*(volatile u8  *)(Addr))    * #define Xil_In16(Addr) (*(volatile u16 *)(Addr))    * #define Xil_In32(Addr) (*(volatile u32 *)(Addr))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • Perform an input operation for a xx-bit memory location by reading from the
  • specified address and returning the value read from that address. 
    *
  • @param Addr contains the address to perform the input operation at. 
    *
  • @return The value read from the specified input address.

以上是读GPIO数据的函数,使用前确保你设置GPIO_TRI对应的那1位为1(或者你已经配置了all input) ,否则no effect

#define Xil_Out8(Addr, Value)  \       (*(volatile u8  *)((Addr)) = (Value))#define Xil_Out16(Addr, Value)  \       (*(volatile u16  *)((Addr)) = (Value))#define Xil_Out32(Addr, Value)  \       (*(volatile u32  *)((Addr)) = (Value))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • Perform an output operation for a xx-bit memory location by writing the
  • specified value to the specified address. 
    以上是写GPIO数据的函数,使用前确保你设置GPIO_TRI对应的那1位为0(或者你已经配置了all output),否则no effect

打开system.mss,还可以看到peripheral Drivers里面有GPIO外设提供的API函数 
实质就是把Xil_Out这样的函数封装了罢了,本次例子较为简单,就不用了(而且每次调用这些函数都要Initialize,有点麻烦)请自己打开页面去查看函数功能 
这里写图片描述 
附送代码:(均在FPGA开发板成功验证) 
1.直接写寄存器的方法

#include <stdio.h>#include <xparameters.h>#include <xgpio.h>#include <xintc.h>#include <xtmrctr.h>#include <xuartlite.h>#include "platform.h"#include "keys/keys.h"#include "queue/queue.h"#include <microblaze_sleep.h>#define LED_BASE           0x40010000#define LED_BASE_TRI       0x40010000 + 0x04int main(){       int i=1;       Xil_Out8(LED_BASE_TRI,0x00); //设置为输出       while(1)       {             //MB_Sleep(1000);             for( ;i<=0x80;i=i<<1)             {                    Xil_Out8(LED_BASE,i); //写数据                    MB_Sleep(10);             }             i = 0x01;       }    cleanup_platform();    return 0;}
  • 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

2.使用GPIO的API函数方法

#include <stdio.h>#include <xparameters.h>#include <xgpio.h>#include <xintc.h>#include <xtmrctr.h>#include <xuartlite.h>#include "platform.h"#include "keys/keys.h"#include "queue/queue.h"#include <microblaze_sleep.h>int main(){       int sts;       int i;    init_platform();    sts=XGpio_Initialize (&gpioLed, XPAR_HIER_PERIPH_GPIO_LEDS_DEVICE_ID);    if(sts != XST_SUCCESS) printf("pgio led init error!\n");    while(1)    {           for( i=0x01;i<=0x80;i=i<<1)             {              XGpio_DiscreteWrite (&gpioLed, 1, i);//led hign is effective              MB_Sleep(500);             }           i=0x01;    }
  • 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

反思:学习FPGA不自己去阅读英文文档是不行的,有问题可以去Xilinx英文论坛讨论。总而言之,官方的文档还是比较重要的第一手资料吧(By华科Peter)