开发板: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灯亮了!!!如下图所示。
对上面的一些操作进行下说明:
/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 <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
// 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灯在不停地闪烁!!!