本文共 6383 字,大约阅读时间需要 21 分钟。
软件环境:vivado 2017.4 硬件平台:XC7Z020
已经都快两个月没更新了,再不更新就要把最近搞得东西全都忘的一干二净了。说也搞笑,刚开始入行的时候,找到什么好东西,获得什么好的调试经验,都不愿意分享,想的都是些这是劳资辛辛苦苦搞出来的,凭啥,凭啥分享,写博客,写博客更是不可能中的不可能。现在就完全不一样了,做了什么都想写出来,都想记下来,要不然真的,过不了多久就全忘了,再多过阵子真就跟没干过一样了,可能因为年纪大了吧哈哈哈哈哈哈越来越记不住东西了,所以干脆想写了就写多点,不想写了就写少点,随时需要,随时能查,都是自己写的,稍微看下就能想起来,多好。
就先哔哔这么多,这篇主要想说下LINUX下EMIO怎么操作,也就是说在LINUX下,PL端的GPIO要怎么操作,以GPIO控制LED为例。
原理图是下面这样,因为后面会写EMIO中断也就是PL-PS在LINUX中断怎么搞,所以干脆模块就一起建了,后面中断也都是在这一个例子基础上写的程序。
这里控制PL端的GPIO用的是AXI_GPIO核,走的是AXI总线控制,所以每个模块都对应了一个总线地址。管脚约束完成以后生成bitstream,然后导入SDK生成fsbl和devicetree,基本操作就不啰嗦了,注意devicetree生成以后,有些地方要手动改一下,我的devicetree如下。
/* * CAUTION: This file is automatically generated by Xilinx. * Version: * Today is: Thu Aug 8 10:09:43 2019 *// { amba_pl: amba_pl { #address-cells = <1>; #size-cells = <1>; compatible = "simple-bus"; ranges ; axi_gpio_button: gpio@41210000 { #gpio-cells = <2>; #interrupt-cells = <2>; clock-names = "s_axi_aclk"; clocks = <&clkc 15>; compatible = "xlnx,xps-gpio-1.00.a"; gpio-controller ; interrupt-controller ; interrupt-names = "ip2intc_irpt"; interrupt-parent = <&intc>; interrupts = <0 29 4>; reg = <0x41210000 0x10000>; xlnx,all-inputs = <0x0>; xlnx,all-inputs-2 = <0x0>; xlnx,all-outputs = <0x0>; xlnx,all-outputs-2 = <0x0>; xlnx,dout-default = <0x00000000>; xlnx,dout-default-2 = <0x00000000>; xlnx,gpio-width = <0x2>; xlnx,gpio2-width = <0x20>; xlnx,interrupt-present = <0x1>; xlnx,is-dual = <0x0>; xlnx,tri-default = <0xFFFFFFFF>; xlnx,tri-default-2 = <0xFFFFFFFF>; }; axi_gpio_led: gpio@41200000 { #gpio-cells = <2>; clock-names = "s_axi_aclk"; clocks = <&clkc 15>; compatible = "xlnx,xps-gpio-1.00.a"; gpio-controller ; reg = <0x41200000 0x10000>; xlnx,all-inputs = <0x0>; xlnx,all-inputs-2 = <0x0>; xlnx,all-outputs = <0x1>; xlnx,all-outputs-2 = <0x0>; xlnx,dout-default = <0x00000000>; xlnx,dout-default-2 = <0x00000000>; xlnx,gpio-width = <0x4>; xlnx,gpio2-width = <0x20>; xlnx,interrupt-present = <0x0>; xlnx,is-dual = <0x0>; xlnx,tri-default = <0xFFFFFFFF>; xlnx,tri-default-2 = <0xFFFFFFFF>; }; gpio-leds { compatible = "gpio-leds"; #address-cells = <1>; #size-cells = <0>; led0 { label = "led0"; gpios = <&axi_gpio_led 0 0>; linux,default-trigger = "none"; default-state = "on"; }; led1 { label = "led1"; gpios = <&axi_gpio_led 1 0>; linux,default-trigger = "none"; default-state = "off"; }; led2 { label = "led2"; gpios = <&axi_gpio_led 2 0>; linux,default-trigger = "none"; default-state = "on"; }; led3 { label = "led3"; gpios = <&axi_gpio_led 3 0>; linux,default-trigger = "none"; default-state = "off"; }; }; gpio-keys-polled { compatible = "gpio-keys-polled"; #address-cells = <1>; #size-cells = <0>; poll-interval = <20>; up { label = "up"; gpios = <&axi_gpio_button 0 0>; linux,code = <103>; }; down { label = "down"; gpios = <&axi_gpio_button 1 0>; linux,code = <108>; }; }; };};
需要说明一下,自动生成的devicetree拿来直接用会有问题的, 自动生成的devicetree中#gpio-cells =<3>需要改成#gpio-cells =<2>,下面的gpio-leds和gpio-keys-polled是手动添加的,因为后面会说好几种不同的控制方式。
gpio-leds这一块能不能不添,可以,不添的话,用mmap内存映射可以控制emio的leds,添了的话,可以分别通过/sys/class/leds来控制各个leds,从compatible = "gpio-leds"可以看到,这里主要用的是内核中现成的leds-gpio.c的驱动,在内核中的位置是/kernel/drivers/leds/leds-gpio.c,kernel在启动过程中会自动寻找与compatible相对应的驱动加载(设备树相互驱动对应),而后实现相关功能,关于自定义驱动实现led下一篇会介绍,本篇不提。
gpio-keys-polled这篇先不介绍,因为牵扯中断,后面会说。
SDK这块基本就没什么了,接下来说下linux内核编译过程中需要注意的,.config文件中下面三项需要使能。
CONFIG_GPIO_SYSFS=yCONFIG_SYSFS=yCONFIG_GPIO_ZYNQ=y
然后制作uboot、kernel、rootfs等等也不哔哔了,准备工作结束。
如果devicetree那里手动添加的代码段部分添加都正确的情况下,进入这个目录后可以看见如下所示。
从led0~led3这4个led对我都是可见的,这里led的数量根据自己实际情况而定,有几个,就在devicetree上加几个分支。
led2 { label = "led2"; gpios = <&axi_gpio_led 2 0>; linux,default-trigger = "none"; default-state = "on"; };
再详细哔哔下devicetree下面led2的那个写法,label就是名字标识,写什么就会在/sys/class/leds看到什么,gpios< >里的取地址符后面的名称需要与vivado中address editor下图红框这里严格对应,default-trigger是默认触发方式,这个怂管,default-state是默认初始化状态,如果是on,则开发板一上电进kernel时候,led就是点亮状态,off的话进kernel时候就是关闭状态。
说下最简单的,直接手动控制。以led2为例,进入led2以后(下面所有的操作都是在进入led2路径后的操作,否则,echo后 >右侧的路径最好写全,只是我比较懒,就这么着吧)可以看到路径下有如下文件,说下主要用到的操作:
brightness用来点亮led,赋给brightness大于0的数,led就会亮,赋给brightness 0,led就会灭。
echo 1 > ./brightness led就会亮echo 0 > ./brightness led就会灭
max_brightness用来修改最大亮度。
cat ./max_brightness 用来查看当前最大亮度时对应的值
trigger用来使led闪烁。
echo timer > ./trigger 给led加入定时器
键入timer之后,路径下的文件会产生变化。
此时,修改delay_on的值,会改变led亮的时间(单位ms),改变delay_off的值,会改变led灭的时间(单位ms)。
echo 1000 > ./delay_on 使led亮持续1secho 1000 > ./delay_off 使led灭持续1s
键入如上命令后,可以看到led亮1s灭1s交替闪烁。
直接对/sys/class/leds/ledx/brightness文件的open、write,都是对linux下文件的基本操作。添加代码led_test.c
#include#include #include #include #include #include #define LED_0 "/sys/class/leds/led0/brightness"#define LED_1 "/sys/class/leds/led1/brightness"#define LED_2 "/sys/class/leds/led2/brightness"#define LED_3 "/sys/class/leds/led3/brightness" int main(){ int led0,led1,led2,led3; led0 = open(LED_0, O_WRONLY); led1 = open(LED_1, O_WRONLY); led2 = open(LED_2, O_WRONLY); led3 = open(LED_3, O_WRONLY); while(1) { write(led0, "1", 1); sleep(1); write(led0, "0", 1); sleep(1); write(led1, "1", 1); sleep(1); write(led1, "0", 1); sleep(1); write(led2, "1", 1); sleep(1); write(led2, "0", 1); sleep(1); write(led3, "1", 1); sleep(1); write(led3, "0", 1); sleep(1); } }
然后在linux下键入命令编译,将生成好的led_test拖到开发板执行。
arm-linux-gnueabihf-gcc -o led_test led_test.c
现象就是亮一秒,灭一秒然后换下一个led。
转载地址:http://rlrm.baihongyu.com/