软件环境:vivado 2017.4 硬件平台:XC7Z020
这个...标题既然这样起,完全是因为经过前几篇的测试,产生了个突发奇想,如果把ps侧的mio管脚类似于pl侧的emio操作方式,添加进设备树,那么,能不能像操作emio一样,操作mio。由于不是像linux下传统方式,调用gpio_request()和request_irq()等来操作gpio,所以说是一个偷鸡的方法,但是不得不说,居然还真的试成功了。
底图的话跟前几篇的没任何区别,主要是要仿照操作emio设备树的写法,把要操作的mio部分,单独的添加到设备树里,这里以mio50和mio51来说明,我的mio50接了个button,mio51外挂了一个LED,做出如下改写。
amba_pl: amba_pl { #address-cells = <1>; #size-cells = <1>; compatible = "simple-bus"; ranges ; axi_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_led 0 0>; linux,default-trigger = "none"; default-state = "off"; }; led1 { label = "led1"; gpios = <&axi_led 1 0>; linux,default-trigger = "none"; default-state = "off"; }; led2 { label = "led2"; gpios = <&axi_led 2 0>; linux,default-trigger = "none"; default-state = "off"; }; led3 { label = "led3"; gpios = <&axi_led 3 0>; linux,default-trigger = "none"; default-state = "off"; }; led-ps51 { label = "led-ps51"; gpios = <&gpio0 51 0> ; linux,default-trigger = "none"; default-state = "off"; }; }; gpio-keys { compatible = "gpio-keys"; #address-cells = <1>; #size-cells = <0>; poll-interval = <20>; key50{ label = "key50"; gpios = <&gpio0 50 0>; linux,code = <103>; }; }; };需要注意的点自然是gpios = <&gpio0 50/51 0>,对于ps侧的mio来说,基址当然是在gpio0,那么这个gpio0又在哪里定义的,是在zynq-7000.dtsi这个ps侧设备树中定义的。
gpio0: gpio@e000a000 { compatible = "xlnx,zynq-gpio-1.0"; #gpio-cells = <2>; clocks = <&clkc 42>; gpio-controller; interrupt-controller; #interrupt-cells = <2>; interrupt-parent = <&intc>; interrupts = <0 20 4>; reg = <0xe000a000 0x1000>; };设备树添加没问题后,编译、启动系统,理所当然的在/sys/class/leds目录下能看到可操作的led-ps51,在/sys/class/input目录下也能看到可供操作的event0。
至于led-ps51和event0是否真的有用,真的建立成功,还需要写app来测试,测试是这样计划的,通过按mio50的button来调教mio51的led闪烁频率,这样,即验证了mio的外中断,又验证了mio的输出,测试代码如下。
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <fcntl.h> #include <signal.h> #include <pthread.h> #include <linux/input.h> #define MIO_LED_BRIGHTNESS "/sys/class/leds/led-ps51/brightness" #define MIO_LED_TRIGGER "/sys/class/leds/led-ps51/trigger" #define MIO_INPUT_EVENT "/dev/input/event0" #define LED_MAX_SPEED 10 #define PERIOD_COEFF 16000 unsigned int led_speed_now; pthread_mutex_t lock; /* Blink LED */ static void *LED_Flash(void *dummy) { unsigned int led_period; int tmp; tmp = open(MIO_LED_BRIGHTNESS, O_WRONLY); if (tmp < 0){ exit(1); } while (1) { pthread_mutex_lock(&lock); led_period = (LED_MAX_SPEED - led_speed_now) * PERIOD_COEFF; pthread_mutex_unlock(&lock); write(tmp, "1", 2); usleep(led_period); write(tmp, "0", 2); usleep(led_period); } } int main() { pthread_t pth; struct input_event ev; int tmp; int key_code; int size = sizeof(ev); /* Configure MIO-LED */ led_speed_now = 5; tmp = open(MIO_LED_TRIGGER, O_WRONLY); if (tmp < 0) return 1; if (write(tmp, "default-on", 10) != 10) { printf("Error writing trigger"); return 1; } close(tmp); printf("Configured LED for use\n"); /* Create thread */ pthread_mutex_init(&lock, NULL); pthread_create(&pth, NULL, LED_Flash, "Blinking LED..."); /* Read event0 */ tmp = open(MIO_INPUT_EVENT, O_RDONLY); if (tmp < 0) { printf("\nOpen " MIO_INPUT_EVENT " failed!\n"); return 1; } /* Read and parse event, update global variable */ while (1) { if (read(tmp, &ev, size) < size) { printf("\nReading from " MIO_INPUT_EVENT " failed!\n"); return 1; } if (ev.value == 1 && ev.type == 1) { key_code = ev.code; if (key_code == KEY_UP) { pthread_mutex_lock(&lock); /* raise speed */ if (led_speed_now < 9) led_speed_now += 1; else led_speed_now -= 9; pthread_mutex_unlock(&lock); } printf("Speed: %i\n", led_speed_now); usleep(1000); } } }主函数里像上一篇一样,通过不断监测ev.value和ev.type的值来判断当前是否有键来按下,如果有,就增加led闪烁的速度,当速度加到头时候将速度清0,在子线程里,根据led_speed_now值得改变,不断计算led闪烁的周期,通过usleep延时来达到控制的目的,注意在app编译的时候,因为程序使用了线程,需要把-pthread加上。
程序运行起来以后,就能看到随着按键的不断按下,闪烁频率不断的增加啦,当然,随着按钮的按下,控制台也一样能看到如下打印。
因为做法比较偷鸡,但是确实是可行的,所以先在这里分享出来,后面会依旧测试传统的gpio_request()和request_irq()做法,有进展了会继续在这里分享给大家,么么哒(づ ̄ 3 ̄)づ。