"); //-->
Zynq7000 Linux 学习第一课
概述:学习zynq linux,完成第一个linux下的应用程序编写,本文主要针对linux如何控制gpio做详细的讲解;
1. 设备树
1.1 设备树概念概述
设备树是linux 内核版本v2.6.23 以后才开始支持的,为什么要引出设备树的概念,是因为linux操作系统支持多种CPU架构下运行,但是每个处理器厂家的cpu支持的接口各有不同,为了兼容各个平台,linux在内核中做了很多mach_xxx的工作,这使得linux的内核越来越庞大,为了改变这种局面,所以引出了设备树的概念,u-boot在启动的时候向内核传递处理器的详细信息,通过dtb来实现;这个就是以二进制形式呈现的cpu设备信息;需要说明的是每个版本对应的设备树描述方式会有所不同,对应的DTC执行方式也会不一样,所以生成设备树一定要选择跟edk tool相匹配的dtc BSP去生成dts文件;
1.2 zynq gpio类型分类
zynq有三种GPIO , a. MIO b. EMID c.AXI-GPIO
MIO是PS直接下挂并引出的,EMIO也是PS直接下挂,但是引出使用需要在.xdc文件中做约束,占用PL的io管教
AXI-GPIO 是硬件逻辑生成的GPIO控制单元,通过AXI-lite总线下挂到PS端;
该例程描述的是AXI-gpio的控制 ;
1.3 AXI-gpio 设备树生成的范本
我使用的linux内核版本是 linux-xlnx-xilinx-v2016.3 , vivado sdk生成的AXI-GPIO的格式如下,
axi_gpio_0: gpio@41200000 {
#gpio-cells =;
compatible = "xlnx,xps-gpio-1.00.a";
gpio-controller ;
reg =;
xlnx,all-inputs =;
xlnx,all-inputs-2 =;
xlnx,all-outputs =;
xlnx,all-outputs-2 =;
xlnx,dout-default =;
xlnx,dout-default-2 =;
xlnx,gpio-width =;
xlnx,gpio2-width =;
xlnx,interrupt-present =;
xlnx,is-dual =;
xlnx,tri-default =;
xlnx,tri-default-2 =;
};
因为axi-gpio是通过axi-lite总线下挂到PS端的,所以其实这个设备树是下挂在amba_pl 设备下的,有关如何生成设备树的步骤会在其他文章中做详解;
2. 驱动加载
2.1 默认的内核选项中是没有开启对xilinx axi-gpio的支持的,需要在内核中打开对应的选项;
如下
参照上图,驱动的位置在 Device Drivers -> GPIO Support -> Memory mapped GPIO drivers
Xilinx GPIO support 选项在本例子中选择编程模块加载的方式,对应的选项为,
在内核根目录下运行 make modules ,会生成一个 gpio-xilinx.ko 的驱动模块文件,把这个文件copy到SD卡中,在板子进入系统后手动加载;
2.2 加载axi-gpio过程
a.挂载sd卡到系统目录 /mnt下面,命令 为 mount /dev/mmcblk0p1 /mnt/
b.打开/mnt目录,看到下面有按照2.1步骤生成的gpio-xilinx.ko 文件,运行 insmod gpio-xilinx.ko加载驱动
c.这个时候就会出现 XGpio: /amba_pl/gpio@41200000: registered, base is 902 代表axi-gpio被成功加载;
d. 打开/sys/class/gpio目录会看到gpiochip902 gpiochip906 这两个GPIO chip
2.3 gpiochip讲解
linux下对gpio是按照组来管理的,在这个例子中看到的gpiochip为gpiochip902 gpiochip906 , 这两个chip是怎么来的困扰了我好久,简单结说是按照驱动加载的顺序从上而下自动生成的,
linux-xlnx-xilinx-v2016.3 采用的内核版本为4.6.0 , 可以通过命令uname -a 来查看内核版本
这个内核版本默认是有1024个GPIO的,按照注册的先后自动生成gpiochip , zynq7000 PS端共有MIO EMIO共计118 个,所以这个906刚好是1024-118 = 906 ,906就是这么来的; 例程中的
axi-gpio共计有4个gpio ,所以gpiochip902 中的902 = 906-4 ;后续有增加会相应的增加gpiochip ;
这些信息可以在 /sys/class/gpio/gpiochipxxx 下面的ngpio看到,命令为 走到/sys/class/gpio/gpiochipxxx 目录下 cat ngpio ;
3 命令行测试gpio是否正常工作
3.1 完成了设备树及驱动加载,接下来就可以通过简单的命令行来操作axi-gpio了 ;
3.2 打开 /sys/class/gpio ,输入命令 echo 902 > export ,这样就会看到路径下多出来一个gpio902 ,这个就是我们可以直接用来操作的gpio设备了,需要注意的是902 > export 中间都有空格,如果没有空格不能正确的生成gpio902 文件夹;
3.3 对于gpiochip902中一共管理着4个gpio , 你要操作哪一个gpio 就需要相应的输入命令
echo 902 > export ,echo 903 > export ,echo 904 > export ,echo 905 > export ,gpio number从chip的base开始,不能超出范围;
3.4 打开你需要控制的gpio会看到下面有value 、direction 等文件,直接操作这个文件就可以控制gpio了;
3.5 例程中的gpio是直接接到led灯上的,所以设置成输出控制高低就可以看到led的亮和灭,命令如下
echo out > direction
echo 1 > value
echo 0 > value
4. 完整的linux下应用程序代码
// The specific GPIO being used must be setup and replaced thru
// this code. The GPIO of 240 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 gpio240 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, on the ML507 reference system,
// the GPIO can be toggled about every 4 usec.
// 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 240 > /sys/class/gpio/export
// bash> echo out > /sys/class/gpio/gpio240/direction
// bash> echo 1 > /sys/class/gpio/gpio240/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/gpio240/value
// echo 0 > /sys/class/gpio/gpio240/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
#include
#include
#include
int main()
{
int valuefd, exportfd;
int 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, "902", 4);
write(exportfd, "903", 4);
write(exportfd, "904", 4);
write(exportfd, "905", 4);
close(exportfd);
printf("GPIO exported successfully\n");
// Update the direction of the GPIO to be an output
directionfd = open("/sys/class/gpio/gpio903/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/gpio903/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) ;
}
}
5. 小结
按照如上步骤可以完成对gpio的控制,其实还有一个方法就是直接运用devmem命令来操作物理地址,以达到控制gpio的目的,这个方法最直接,无需加载驱动都可以完成;
example
# Read 32-bit register from physical address, match it to your AXI4-lite.
devmem 0x41200000
# Write 32-bit register with value
devmem 0x41200000 32 0x12345678
要想正确的操作gpio还需要参照axi-gpio的使用说明,找到对应的地址偏移寄存器的作用;
a.按照规格书里面将的地址偏移0x00000004 对应的是gpio的方向,初始值为0x0000000f,代表该axi-gpio的4个gpio全部被配置成了输入,往里面直接写0x00000000 将其配置成输出,命令如下
devmem 0x41200000 32 0x00000000 ;
b.地址偏移0x0是gpio的值,最后4bit分别对应1个gpio,用命令
devmem 0x41200000 32 0x0000000f 可以点亮4个led灯;
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。