net processing_system7_0_GPIO<16> LOC = G22 | IOSTANDARD = LVCMOS33; # SW1 net processing_system7_0_GPIO<17> LOC = H22 | IOSTANDARD = LVCMOS33; # SW2 net processing_system7_0_GPIO<18> LOC = F21 | IOSTANDARD = LVCMOS33; # SW3 net processing_system7_0_GPIO<19> LOC = H19 | IOSTANDARD = LVCMOS33; # SW4 net processing_system7_0_GPIO<20> LOC = H18 | IOSTANDARD = LVCMOS33; # SW5 net processing_system7_0_GPIO<21> LOC = H17 | IOSTANDARD = LVCMOS33; # SW6 net processing_system7_0_GPIO<22> LOC = M15 | IOSTANDARD = LVCMOS33; # SW7 这里的processing_system7_0_GPIO指的的就是EMIO,所以可以看到EMIO[1:6]是用的OLED,而在GPIO寄存器配置中: GPIO Bank0, MIO[0:31] GPIO Bank1, MIO[32:53] GPIO Bank2, EMIO[0:31] GPIO Bank3, EMIO[32:63]
是顺序排列的,所以上面这里的EMIO[1:6]对应的就是GPIO[55:60],然后我们可以在devicetree源文件中找到oled的配置:
zed_oled { compatible = \ /* GPIO Pins */ vbat-gpio = <&gpiops 55 0>; vdd-gpio = <&gpiops 56 0>; res-gpio = <&gpiops 57 0>; dc-gpio = <&gpiops 58 0>; /* SPI-GPIOs */ spi-bus-num = <2>; spi-speed-hz = <4000000>; spi-sclk-gpio = <&gpiops 59 0>; spi-sdin-gpio = <&gpiops 60 0>; }; 这里面的GPIO的号码正好对应了刚才计算出来的数字,到这里也就能理解这些号码的意义了:-)(这里需要事先设置好GPIO控制器,可以在前面找到gpio-controller字段)。
但是看到这里不禁又有了另一个疑问,为什么设备树只有oled的配置,而没有led和switch的配置,这也是当初我找不到内核在哪里操作led的原因。有一次无意间在ZedBoard_Linux_Design的doc目录下的DemoFeatures.txt中发现内核中自带了这样两个命令:
SWITCHES/LEDS: Scripts are included for writing to the LEDs and reading the state of the switches. To read the state of the switches, run the command: read_sw It will return the state of the switches as both hexadecimal and decimal. A script for changing the state of the LEDs is also included. To turn all 8 LEDs on, run one of the following two commands: write_led 255 write_led 0xFF 然后再仔细一看ramdisk中read_sw,write_led里面的内容:
thinki@G31T-M2:$ cat write_led #!/bin/sh value=$(($1)); if [ $value -ge 0 ]; then for i in 0 1 2 3 4 5 6 7; do led=$(($i+61)); echo $(($value&0x01)) > /sys/class/gpio/gpio$led/value; value=$(($value/2)); done; fi;
thinki@G31T-M2:$ cat read_sw #!/bin/sh value=0; for i in 0 1 2 3 4 5 6 7; do sw=$((76-$i)); sw_tmp=`cat /sys/class/gpio/gpio$sw/value`; value=$(($value*2)); value=$(($value+$sw_tmp)); done; printf \可以看到这里直接操作了sysfs下面的gpio class进行LED的写和Switch的读,但是这些目录以及相关的文件并不是凭空而来的,最后在ramdisk的etc/init.d/rcS中可以找到这样一段脚本:
echo \for i in 0 1 2 3 4 5 6 7; do sw=$(($i+69)); led=$(($i+61)); echo $sw > /sys/class/gpio/export; echo $led > /sys/class/gpio/export; echo out > /sys/class/gpio/gpio$led/direction; done; 这才是这些目录真正的开端,启动时配置了这些,你只要Google一下sysfs gpio就可以找到一大堆东西,这里我简要介绍一下,也就是linux下gpio支持sysfs空间的操作,也就是可以绕过创建设备节点进行此操作,不过首先需要开启内核对sysfs的支持以及gpiolib对sysfs的支持,相关的代码依旧在drivers/gpio/gpiolib.c文件中,我们可以找到相关的宏:
#ifdef CONFIG_GPIO_SYSFS 这个宏下面的内容都是针对SYSFS相关的支持,这里涉及的知识比较多,需要理解Linux 2.6的设备模型相关的知识。如果是直观的操作的话,你只需要在/sys/class/gpio下进行echo XXX > export操作,不过这里XXX必须是内核支持的gpio号,比如内核启动之后在rcS脚本中echo的就是[61:68]和[69:76],这样就会在/sys/class/gpio目录下创建gpioXXX目录,然后我们可以设置led对应的gpioXXX目录下的direction属性为out就能够设置GPIO为输出,最后只需要像write_led脚本中那样向/sys/class/gpio/gpioXXX/value echo 0或者1就可以了,最终你会看到LED灯点亮!
当然你也可以使用类似于pmodoled驱动的形式,最终以/dev目录下的设备节点来操作底层硬件,这样的话就需要注册platform驱动并且通过设备树中的节点进行匹配,可以在设备树源文件最后添加下面的配置信息:
emio-oled { }; compatible = \/* GPIO Pins */ ld0-gpio = <&gpiops 61 0>; ld1-gpio = <&gpiops 62 0>; ld2-gpio = <&gpiops 63 0>; ld3-gpio = <&gpiops 64 0>; ld4-gpio = <&gpiops 65 0>; ld5-gpio = <&gpiops 66 0>; ld6-gpio = <&gpiops 67 0>; ld7-gpio = <&gpiops 68 0>; 有时间我把我写的代码贴出来晒晒!
在platform驱动中的probe函数中来进行cdev的初始化与file_operations的设置,具体可以参考pmodoled驱动中的代码。
同时gpiolib还支持debugfs,可以查看哪些GPIO口被哪些设备分配了,不过使用之前需要先挂载debugfs,它与sysfs一样也是基于内存的文件系统: mount -t debugfs debugfs /sys/kernel/debug 然后在板子上输出gpio文件的信息: zynq> mount -t debugfs debugfs /sys/kernel/debug zynq> cat /sys/kernel/debug/gpio GPIOs 0-117, platform/e000a000.gpio, xgpiops: zynq> mount -t debugfs debugfs /sys/kernel/debug zynq> cat /sys/kernel/debug/gpio GPIOs 0-117, platform/e000a000.gpio, xgpiops: gpio-7 (mmc_led ) out lo gpio-55 (OLED VBat ) out lo gpio-56 (OLED VDD ) out lo gpio-57 (OLED_RESET ) out hi gpio-58 (OLED_D/C ) out hi gpio-59 (spi_gpio.2 ) out lo gpio-60 (spi_gpio.2 ) out lo gpio-61 (sysfs ) out lo gpio-62 (sysfs ) out lo gpio-63 (sysfs ) out lo gpio-64 (sysfs ) out lo gpio-65 (sysfs ) out lo gpio-66 (sysfs ) out lo gpio-67 (sysfs ) out lo gpio-68 (sysfs ) out lo gpio-69 (sysfs ) in hi gpio-70 (sysfs ) in lo gpio-71 (sysfs ) in lo gpio-72 (sysfs ) in hi gpio-73 (sysfs ) in hi gpio-74 (sysfs ) in lo gpio-75 (sysfs ) in hi gpio-76 (sysfs ) in lo 可以看到其中GPIO[55:60]被pmodoled驱动分配了,而GPIO[61:76]则是被sysfs分配了,最前面的GPIO[7]则是被linux的led驱动分配了,代码的位置是在drivers/leds/leds-gpio.c。 最终要深入的话还是建议看内核源码和文档: drivers/gpio/gpiolib.c Documentation/gpio.txt
参考链接:
使用 /sys 文件系统访问 Linux 内核 linux那些事之sysfs 在 Linux 下用户空间与内核空间数据交换的方式