admin管理员组文章数量:1532160
2023年12月17日发(作者:)
ZedBoard Linux开发 --- GPIO驱动详解
下载LOFTER客户端
本来这是要作为ZedBoard Linux的第一个学习实例,不过由于一开始实在找不到内核中针对ZedBoard
GPIO具体操作的代码在哪里,所以只能先从OLED开始看起,在学习完OLED驱动之后有了不少发现,比如OLED驱动中就有使用GPIO的操作,后来发现这些操作都被Linux内核中的GPIOLIB库管理着,相关的文档在Documentation/中有介绍,通读一遍之后就会有不少发现的,相关的GPIOLIB库文件位于drivers/gpio/gpio-lib.c文件中,不过这部分文件只是提供了库函数,而真正在ZedBoard启动时进行GPIO注册管理的文件是drivers/gpio/gpio-xilinxps.c,可以在这个文件中找到这样一个宏定义:
#define XGPIOPS_NR_GPIOS 118
这里一共注册了118个GPIO口,看看Datasheet就知道这里的意思应该是MIO[0:53]+EMIO[54:117],也就是54个MIO加上64个EMIO,看到这里我还是有一些疑问,因为并不是所有的IO口都作为GPIO来使用的,有很大一部分是进行IO复用的,下面是我在XPS中的MIO配置截图:
1
可以看到MIO中真正作为GPIO口使用的也就只有MIO[0,7,9:15,50:51],我当时就有疑问:如果我在Linux中申请了这一部分被复用的GPIO,这会不会与正在复用的那些功能起冲突?(至少在MCU中有很多复用功能是在配置了GPIO方向之后才能正常复用的)后来看来一下zynq的UG585手册,找到了下面这张图才解决了问题:
2
可以看到所有GPIO与其他复用的功能最后都是经过MIO网络路由到外部的GPIO端口的,也就是说即使在相应的GPIO寄存器中配置了GPIO的功能,那么这部分功能也不会生效!而配置这些复用功能的寄存器是在slcr(System Level Control Registers)寄存器中操作的,可以在UG585上找到这些寄存器具体的参数:
而在Digilent Linux内核中,slcr相关的文件可以在linux-digilent/arch/arm/mach-zynq/slcr.c中找到。另外除了MIO,还有
EMIO的配置,可以在上面的截图中看到XPS中配置了60个EMIO,并且在xps的ucf文件中可以找到配置相关注释:
#############################################################
# #
# GPIO Interface #
# #
#############################################################
………………………………
3
############################
# #
# On-board OLED #
# #
# Voltage control and #
# Bitbanged SPI over GPIO #
# #
############################
net processing_system7_0_GPIO<1> LOC = U11 | IOSTANDARD = LVCMOS33; # OLED-VBAT
net processing_system7_0_GPIO<2> LOC = U12 | IOSTANDARD = LVCMOS33; # OLED-VDD
net processing_system7_0_GPIO<3> LOC = U9 | IOSTANDARD = LVCMOS33; # OLED-RES
net processing_system7_0_GPIO<4> LOC = U10 | IOSTANDARD = LVCMOS33; # OLED-DC
net processing_system7_0_GPIO<5> LOC = AB12 | IOSTANDARD = LVCMOS33; # OLED-SCLK
net processing_system7_0_GPIO<6> LOC = AA12 | IOSTANDARD = LVCMOS33; # OLED-SDIN
############################
# #
# On-board LED's #
# #
############################
net processing_system7_0_GPIO<7> LOC = T22 | IOSTANDARD = LVCMOS33; # LD0
net processing_system7_0_GPIO<8> LOC = T21 | IOSTANDARD = LVCMOS33; # LD1
net processing_system7_0_GPIO<9> LOC = U22 | IOSTANDARD = LVCMOS33; # LD2
net processing_system7_0_GPIO<10> LOC = U21 | IOSTANDARD = LVCMOS33; # LD3
net processing_system7_0_GPIO<11> LOC = V22 | IOSTANDARD = LVCMOS33; # LD4
4
net processing_system7_0_GPIO<12> LOC = W22 | IOSTANDARD = LVCMOS33; # LD5
net processing_system7_0_GPIO<13> LOC = U19 | IOSTANDARD = LVCMOS33; # LD6
net processing_system7_0_GPIO<14> LOC = U14 | IOSTANDARD = LVCMOS33; # LD7
############################
# #
# On-board Slide Switches #
# #
############################
net processing_system7_0_GPIO<15> LOC = F22 | IOSTANDARD = LVCMOS33; # SW0
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 = "dglnt,pmodoled-gpio";
5
};
/* 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目录下的中发现内核中自带了这样两个命令:
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;
6
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 "0x%x %dn" $value $value;
可以看到这里直接操作了sysfs下面的gpio class进行LED的写和Switch的读,但是这些目录以及相关的文件并不是凭空而来的,最后在ramdisk的etc/init.d/rcS中可以找到这样一段脚本:
echo "++ Exporting LEDs & SWs"
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文件中,我们可以找到相关的宏:
7
#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 = "dglnt,emioled-gpio";
/* 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一样也是基于内存的文件系统:
8
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/, xgpiops:
zynq> mount -t debugfs debugfs /sys/kernel/debug
zynq> cat /sys/kernel/debug/gpio
GPIOs 0-117, platform/, 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。
9
最终要深入的话还是建议看内核源码和文档:
drivers/gpio/gpiolib.c
Documentation/
参考链接:
使用 /sys 文件系统访问 Linux 内核
linux那些事之sysfs
在 Linux 下用户空间与内核空间数据交换的方式
10
版权声明:本文标题:ZedBoard_Linux开发_---_GPIO驱动详解ZedBoard 安装 Ubuntu11.04_ 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dongtai/1702828744a26263.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论