软件环境: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 ̄)づ。