Zynq-7000系列之linux开发学习笔记:Linux下的MIO/EMIO GPIO驱动与操作(八)

开发板:Zynq7030数据采集板
PC平台:Ubuntu-18.04 + MobaXterm
开发环境:Xilinx Vivado + SDK -18.3
交叉编译工具:arm-linux-gnueabihf-
学习目标:通过Linux下GPIO驱动控制开发板上的LED灯

一、Zynq Linux的GPIO驱动

在前面文章:Zynq-7000系列之linux开发学习笔记:PS和PL端的GPIO使用(三)中分享了Zynq下GPIO的三种类型,由于使用的硬件关系,只分享EMIO和AXI_GPIO的具体配置流程。由于是在裸机的情况下进行的GPIO操作,需要在Vivado和SDK下对PS和PL端进行配置,还要在SDK里面编写程序,才能通过JTAG的方式下载到板子上运行。
但在Linux系统环境下,操作GPIO是非常方便的事情,一般都是通过驱动程序来进行操作。驱动程序可以通过自己编写来实现,也可以直接采用Linux下的驱动程序。

二、EMIO_GPIO

从前面几篇文章中可知:移植Linaro操作系统的流程中,生成FSBL阶段时配置了一个EMIO连接到PL端的PIN脚。
可以查看一下编译内核的时候,在 ./linux-xlnx-xilinx-v2018.3/arch/arm/configs 路径下的Xilinx Zynq官方配置文件 xilinx_zynq_defconfig ,是否使能了下面内容:

CONFIG_GPIO_SYSFS=y
CONFIG_SYSVIPC=y
CONFIG_GPIO_ZYNQ=y

查看通过SDK生成的设备树文件,包含有下面GPIO内容:

		gpio@e000a000 {
			compatible = "xlnx,zynq-gpio-1.0";
			#gpio-cells = <0x2>;
			clocks = <0x1 0x2a>;
			gpio-controller;
			interrupt-controller;
			#interrupt-cells = <0x2>;
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x14 0x4>;
			reg = <0xe000a000 0x1000>;
			emio-gpio-width = <0x1>;
			gpio-mask-high = <0x0>;
			gpio-mask-low = <0x5600>;
		};

1、Linux下Sysfs方式直接操作GPIO

在多GPIO的系统下,需要确定要控制GPIO口的编号,一种方法就是通过查看./sys/class/gpio路径下的gpiochips编号,它反映了GPIO在系统中的地址。还有一种方法比较麻烦,就不提了0.0.0

root@linaro-ubuntu-desktop:~$ cd /sys/class/gpio/
root@linaro-ubuntu-desktop:/sys/class/gpio$ ls
export  gpiochip906  unexport
root@linaro-ubuntu-desktop:/sys/class/gpio$ cat gpiochip906/label
zynq_gpio

上面内容表明第一个GPIO口编号为906,并以此编号开始计算后续GPIO编号。我们使用的EMIO是第一个EMIO,总共有54个MIO,所以我们需要设置的GPIO口编号为960。

/* Export a GPIO pin */
root@linaro-ubuntu-desktop:~$ echo 960 > /sys/class/gpio/export
root@linaro-ubuntu-desktop:~$ cd /sys/class/gpio/
root@linaro-ubuntu-desktop:/sys/class/gpio$ ls
export  gpio960  gpiochip906  unexport

/* Read the direction and value from the GPIO pin */
root@linaro-ubuntu-desktop:/sys/class/gpio$ ls gpio960
active_low  device  direction  edge  power  subsystem  uevent  value
root@linaro-ubuntu-desktop:/sys/class/gpio$ cat gpio960/direction
in
root@linaro-ubuntu-desktop:/sys/class/gpio$ cat gpio960/value
0

/* Set the direction to an output and write a value 1 to GPIO pin */
root@linaro-ubuntu-desktop:/sys/class/gpio$ echo out > gpio960/direction
root@linaro-ubuntu-desktop:/sys/class/gpio$ cat gpio960/direction
out
root@linaro-ubuntu-desktop:/sys/class/gpio$ echo 1 > gpio960/value
root@linaro-ubuntu-desktop:/sys/class/gpio$ cat gpio960/value
1

可以看到开发板上的LED灯亮了!!!如下图所示。
Zynq-7000系列之linux开发学习笔记:Linux下的MIO/EMIO GPIO驱动与操作(八)_第1张图片
对上面的一些操作进行下说明:
/sys/class/gpio/路径下相关文件说明:

File Description
export 将GPIO节点导入到用户空间
unexport 将GPIO节点移除用户空间
gpiochip* 该目录下保存系统中GPIO寄存器信息:每个寄存器控制引脚的起始编号base,寄存器名称,引脚总数

/sys/class/gpio/gpio960/路径下相关文件说明:

File Description
direction 配置GPIO口输方面,"in"为输入;"out"为输出
value GPIO为输出时可设置GPIO口电平,"0"为低电平;"1"为高电平。
edge 设置GPIO口输入时触发方式:“none”, “rising”, “falling”, or “both”
active_low 按照官方的说法是翻转电平,输入非零的数即上升沿变为下降沿

2、GPIO用户空间APP方式

上面的方法是直接在系统里通过SYSFS操作GPIO口,也可以通过写APP的方式来操作GPIO。
首先在虚拟机Ubuntu下编写GPIO的.C程序 ,用Xilinx的交叉编译工具链来编译程序,操作如下:

claude1009@ubuntu:~/xc7030/gpio$ gedit emio_gpio.c 
claude1009@ubuntu:~/xc7030/gpio$ arm-linux-gnueabihf-gcc emio_gpio.c -o linux_gpio
emio_gpio.c: In function ‘main’:
emio_gpio.c:67:5: warning: implicit declaration of function ‘write’; did you mean ‘fwrite’? [-Wimplicit-function-declaration]
     write(exportfd, "960", 4);
     ^~~~~
     fwrite
emio_gpio.c:68:5: warning: implicit declaration of function ‘close’; did you mean ‘pclose’? [-Wimplicit-function-declaration]
     close(exportfd);
     ^~~~~
     pclose
emio_gpio.c:103:9: warning: implicit declaration of function ‘sleep’ [-Wimplicit-function-declaration]
         sleep(1);
         ^~~~~
claude1009@ubuntu:~/xc7030/gpio$ ls
emio_gpio.c  linux_gpio

具体程序如下:

#include 
#include 
#include 


// The specific GPIO being used must be setup and replaced thru
// this code.  The GPIO of 897 is in the path of most the sys dirs
// and in the export write.
//
// Figuring out the exact GPIO was not totally obvious when there
// were multiple GPIOs in the system. One way to do is to go into
// the gpiochips in /sys/class/gpio and view the label as it should
// reflect the address of the GPIO in the system. The name of the
// the chip appears to be the 1st GPIO of the controller.
//
// The export causes the gpio897 dir to appear in /sys/class/gpio.
// Then the direction and value can be changed by writing to them.

// The performance of this is pretty good, using a nfs mount,
// running on open source linux, 
// the GPIO can be toggled about every 1sec.
// The following commands from the console setup the GPIO to be
// exported, set the direction of it to an output and write a 1
// to the GPIO.
//
// bash> echo 897 > /sys/class/gpio/export
// bash> echo out > /sys/class/gpio/gpio897/direction
// bash> echo 1 > /sys/class/gpio/gpio897/value

// if sysfs is not mounted on your system, the you need to mount it
// bash> mount -t sysfs sysfs /sys

// the following bash script to toggle the gpio is also handy for
// testing
//
// while [ 1 ]; do
//  echo 1 > /sys/class/gpio/gpio897/value
//  echo 0 > /sys/class/gpio/gpio897/value
// done

// to compile this, use the following command
// gcc gpio.c -o gpio

// The kernel needs the following configuration to make this work.
//
// CONFIG_GPIO_SYSFS=y
// CONFIG_SYSFS=y
// CONFIG_EXPERIMENTAL=y
// CONFIG_GPIO_XILINX=y

int main()
{
    int valuefd, exportfd, directionfd;

    printf("GPIO test running...\n");

    // The GPIO has to be exported to be able to see it
    // in sysfs

    exportfd = open("/sys/class/gpio/export", O_WRONLY);
    if (exportfd < 0)
    {
        printf("Cannot open GPIO to export it\n");
        exit(1);
    }

    write(exportfd, "960", 4);
    close(exportfd);

    printf("GPIO exported successfully\n");

    // Update the direction of the GPIO to be an output

    directionfd = open("/sys/class/gpio/gpio960/direction", O_RDWR);
    if (directionfd < 0)
    {
        printf("Cannot open GPIO direction it\n");
        exit(1);
    }

    write(directionfd, "out", 4);
    close(directionfd);

    printf("GPIO direction set as output successfully\n");

    // Get the GPIO value ready to be toggled

    valuefd = open("/sys/class/gpio/gpio960/value", O_RDWR);
    if (valuefd < 0)
    {
        printf("Cannot open GPIO value\n");
        exit(1);
    }

    printf("GPIO value opened, now toggling...\n");

    // toggle the GPIO as fast a possible forever, a control c is needed
    // to stop it

    while (1)
    {
        write(valuefd,"1", 2);
        sleep(1);
        write(valuefd,"0", 2);
        sleep(1);       
    }
}

然后在开发板端,通过NFS挂载到虚拟机Ubuntu下,然后再运行程序即可:

root@linaro-ubuntu-desktop:~$ mount -t nfs -o nolock 192.168.3.100:/home/claude1009 /mnt
root@linaro-ubuntu-desktop:~$ cd /mnt
root@linaro-ubuntu-desktop:/mnt$ cd xc7030/gpio/
root@linaro-ubuntu-desktop:/mnt/xc7030/gpio$ ls
emio_gpio.c  linux_gpio
root@linaro-ubuntu-desktop:/mnt/xc7030/gpio$ ./linux_gpio
GPIO test running...
GPIO exported successfully
GPIO direction set as output successfully
GPIO value opened, now toggling...

此时就可看见开发板上的LED灯在不停地闪烁!!!

你可能感兴趣的:(Zynq-7000系列)