-10-GPIO驱动程序【Xilinx-Petalinux学习】

〇、目的

为了在Linux用户空间中对板上的硬件I/O进行控制,需要编写驱动程序。
尝试了一些驱动程序的编写,发现Linux的Gpiolib方便一些,能够实现GPIO管脚的输出、输入、中断功能,相对于自己再去写设备驱动更方便一些。Gpiolib是基于SysFs接口实现的GPIO管脚的操作,用起来虽然方便,但是针对于按键、LED这些特定功能的I/O口,我们还可以使用Linux内核中的gpio-keys, leds-gpios驱动去实现更方便的I/O口操作。
下面介绍gpio-keys, leds-gpio的驱动,而Gpiolib网上有很多例子,以后再来学习。

一、工程配置

在上一节“EMIO Slice 自定义IP设计”中我们已经将Zynq SoC中PS的emio信号连接到了PL上的管脚,最终的bd图如下:
-10-GPIO驱动程序【Xilinx-Petalinux学习】_第1张图片
从EMIO(GPIO_0)中引出21个信号,分别连接到ZedBoard的BTN(5位),SW(8位)和LED(8位)上,他们的emio编号分别为:54~58,59~66,67,~74。在板子上还有一个LD9的LED,使用的是MIO 7的引脚。再加入管脚约束、综合、实现、生成bit和hdf后,我们再次进入petalinux的设计。
约束文件如下:

#btn
set_property PACKAGE_PIN N15 [get_ports {btn_tri_io[0]}]
set_property PACKAGE_PIN R18 [get_ports {btn_tri_io[1]}]
set_property PACKAGE_PIN T18 [get_ports {btn_tri_io[2]}]
set_property PACKAGE_PIN R16 [get_ports {btn_tri_io[3]}]
set_property PACKAGE_PIN P16 [get_ports {btn_tri_io[4]}]
set_property IOSTANDARD LVCMOS25 [get_ports {btn_tri_io[0]}]
set_property IOSTANDARD LVCMOS25 [get_ports {btn_tri_io[1]}]
set_property IOSTANDARD LVCMOS25 [get_ports {btn_tri_io[2]}]
set_property IOSTANDARD LVCMOS25 [get_ports {btn_tri_io[3]}]
set_property IOSTANDARD LVCMOS25 [get_ports {btn_tri_io[4]}]
#sw
set_property PACKAGE_PIN F22 [get_ports {sw_tri_io[0]}]
set_property PACKAGE_PIN G22 [get_ports {sw_tri_io[1]}]
set_property PACKAGE_PIN H22 [get_ports {sw_tri_io[2]}]
set_property PACKAGE_PIN F21 [get_ports {sw_tri_io[3]}]
set_property PACKAGE_PIN H19 [get_ports {sw_tri_io[4]}]
set_property PACKAGE_PIN H18 [get_ports {sw_tri_io[5]}]
set_property PACKAGE_PIN H17 [get_ports {sw_tri_io[6]}]
set_property PACKAGE_PIN M15 [get_ports {sw_tri_io[7]}]
set_property IOSTANDARD LVCMOS25 [get_ports {sw_tri_io[0]}]
set_property IOSTANDARD LVCMOS25 [get_ports {sw_tri_io[1]}]
set_property IOSTANDARD LVCMOS25 [get_ports {sw_tri_io[2]}]
set_property IOSTANDARD LVCMOS25 [get_ports {sw_tri_io[3]}]
set_property IOSTANDARD LVCMOS25 [get_ports {sw_tri_io[4]}]
set_property IOSTANDARD LVCMOS25 [get_ports {sw_tri_io[5]}]
set_property IOSTANDARD LVCMOS25 [get_ports {sw_tri_io[6]}]
set_property IOSTANDARD LVCMOS25 [get_ports {sw_tri_io[7]}]
#led
set_property PACKAGE_PIN T22 [get_ports {led_tri_io[0]}]
set_property PACKAGE_PIN T21 [get_ports {led_tri_io[1]}]
set_property PACKAGE_PIN U22 [get_ports {led_tri_io[2]}]
set_property PACKAGE_PIN U21 [get_ports {led_tri_io[3]}]
set_property PACKAGE_PIN V22 [get_ports {led_tri_io[4]}]
set_property PACKAGE_PIN W22 [get_ports {led_tri_io[5]}]
set_property PACKAGE_PIN U19 [get_ports {led_tri_io[6]}]
set_property PACKAGE_PIN U14 [get_ports {led_tri_io[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_io[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_io[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_io[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_io[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_io[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_io[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_io[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_io[7]}]

二、Petalinux配置

下面参考官方文档,网址:http://www.wiki.xilinx.com/Linux+GPIO+Driver
首先导入新的hdf和bit文件:

petalinux-config --get-hw-description=../hw_description/min_system_wrapper_hw_platform_1/

配置内核:

petalinux-config -c kernel

Device Drivers  --->
    -*- GPIO Support  --->
        <*>   Xilinx GPIO support
        <*>   Xilinx Zynq GPIO support
    [*] LED Support  --->
        <*>   LED Class Support
        <*>   LED Support for GPIO connected LEDs
        [*]   LED Trigger support  --->
            <*>   LED Timer Trigger
            <*>   LED One-shot Trigger
            <*>   LED Heartbeat Trigger
            <*>   LED backlight Trigger
            [*]   LED CPU Trigger
            <*>   LED GPIO Trigger
            <*>   LED Default ON Trigger 
            <*>   LED Transient Trigger
    Input device support  --->
        [*]   Keyboards  --->
        <*>   GPIO Buttons
        <*>   Polled GPIO buttons

编辑设备树文件:

gedit ./subsystems/linux/configs/device-tree/system-top.dts

修改system-top.dts,我把ZedBoard上的5个按键配置为键盘的左右上下回车功能,8个开关配置为F1~F8按键,8个EMIO连接的LED默认打开,1个MIO连接的LED配置为心跳模式。
其中设备树中的linux,code编号可以在下面的头文件中找到:
input-event-codes.h
input.h
我们使用了其中的若干按键:

#define KEY_ENTER               28
#define KEY_UP                  103
#define KEY_LEFT                105
#define KEY_RIGHT               106
#define KEY_DOWN                108
#define KEY_F1                  59
#define KEY_F2                  60
#define KEY_F3                  61
#define KEY_F4                  62
#define KEY_F5                  63
#define KEY_F6                  64
#define KEY_F7                  65
#define KEY_F8                  66
/dts-v1/;
/include/ "system-conf.dtsi"
/ {
    gpio-keys {
        compatible = "gpio-keys";
        #address-cells = <1>;
        #size-cells = <0>;
        autorepeat;
        btn-left {
        label = "btn-left";
        gpios = <&gpio0 54 0>;
        linux,code = <105>; /* KEY_LEFT */
        wakeup-source;
        autorepeat;
        };
        btn-right {
        label = "btn-right";
        gpios = <&gpio0 55 0>;
        linux,code = <106>; /* KEY_RIGHT */
        wakeup-source;
        autorepeat;
        };
        btn-up {
        label = "btn-up";
        gpios = <&gpio0 57 0>;
        linux,code = <103>; /* KEY_UP */
        wakeup-source;
        autorepeat;
        };
        btn-down {
        label = "btn-down";
        gpios = <&gpio0 56 0>;
        linux,code = <108>; /* KEY_DOWN */
        wakeup-source;
        autorepeat;
        };
        btn-enter {
        label = "btn-enter";
        gpios = <&gpio0 58 0>;
        linux,code = <28>; /* KEY_ENTER */
        wakeup-source;
        autorepeat;
        };
        sw-0 {
        label = "sw-0";
        gpios = <&gpio0 59 0>;
        linux,code = <59>; /* KEY_F1 */
        wakeup-source;
        autorepeat;
        };
        sw-1 {
        label = "sw-1";
        gpios = <&gpio0 60 0>;
        linux,code = <60>; /* KEY_F2 */
        wakeup-source;
        autorepeat;
        };
        sw-2 {
        label = "sw-2";
        gpios = <&gpio0 61 0>;
        linux,code = <61>; /* KEY_F3 */
        wakeup-source;
        autorepeat;
        };
        sw-3 {
        label = "sw-3";
        gpios = <&gpio0 62 0>;
        linux,code = <62>; /* KEY_F4 */
        wakeup-source;
        autorepeat;
        };
        sw-4 {
        label = "sw-4";
        gpios = <&gpio0 63 0>;
        linux,code = <63>; /* KEY_F5 */
        wakeup-source;
        autorepeat;
        };
        sw-5 {
        label = "sw-5";
        gpios = <&gpio0 64 0>;
        linux,code = <64>; /* KEY_F6 */
        wakeup-source;
        autorepeat;
        };
        sw-6 {
        label = "sw-6";
        gpios = <&gpio0 65 0>;
        linux,code = <65>; /* KEY_F7 */
        wakeup-source;
        autorepeat;
        };
        sw-7 {
        label = "sw-7";
        gpios = <&gpio0 66 0>;
        linux,code = <66>; /* KEY_F8 */
        wakeup-source;
        autorepeat;
        };
    };
    gpio-leds {
        compatible = "gpio-leds";
        led-ld0 {
        label = "led-ld0";
        gpios = <&gpio0 67 0>;
        default-state = "on";
        linux,default-trigger = "default-on";
        };
        led-ld1 {
        label = "led-ld1";
        gpios = <&gpio0 68 0>;
        default-state = "on";
        linux,default-trigger = "default-on";
        };
        led-ld2 {
        label = "led-ld2";
        gpios = <&gpio0 69 0>;
        default-state = "on";
        linux,default-trigger = "default-on";
        };
        led-ld3 {
        label = "led-ld3";
        gpios = <&gpio0 70 0>;
        default-state = "on";
        linux,default-trigger = "default-on";
        };
        led-ld4 {
        label = "led-ld4";
        gpios = <&gpio0 71 0>;
        default-state = "on";
        linux,default-trigger = "default-on";
        };
        led-ld5 {
        label = "led-ld5";
        gpios = <&gpio0 72 0>;
        default-state = "on";
        linux,default-trigger = "default-on";
        };
        led-ld6 {
        label = "led-ld6";
        gpios = <&gpio0 73 0>;
        default-state = "on";
        linux,default-trigger = "default-on";
        };
        led-ld7 {
        label = "led-ld7";
        gpios = <&gpio0 74 0>;
        default-state = "on";
        linux,default-trigger = "default-on";
        };
        led-ld9 {
        label = "led-ld9";
        gpios = <&gpio0 7 0>;
        default-state = "on";
        linux,default-trigger = "heartbeat";
        };
    };
};

新建测试程序

petalinux-create -t apps --name gpiolib_test --enable
gedit ./components/apps/gpiolib_test.c

复制下面的代码进去覆盖之前的:

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define LED_BRIGHTNESS    "/sys/class/leds/led-ld7/brightness"
#define    LED_TRIGGER    "/sys/class/leds/led-ld7/trigger"
#define    INPUT_EVENT    "/dev/input/event0"
#define    LED_MAX_SPEED    10
#define    PERIOD_COEFF    16000

unsigned int led_speed;
pthread_mutex_t lock;

/* Blink LED */
static void *LEDMod(void *dummy)
{
    unsigned int led_period;
    int tmp;
    tmp = open(LED_BRIGHTNESS, O_WRONLY);
    if (tmp < 0)
        exit(1);
    while (1) {
        pthread_mutex_lock(&lock);
        led_period = (LED_MAX_SPEED - led_speed) * 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 LED */
    led_speed = 5;
    tmp = open(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, LEDMod, "Blinking LED...");

    /* Read event0 */
    tmp = open(INPUT_EVENT, O_RDONLY);
    if (tmp < 0) {
        printf("\nOpen " INPUT_EVENT " failed!\n");
        return 1;
    }
    /* Read and parse event, update global variable */
    while (1) {
        if (read(tmp, &ev, size) < size) {
            printf("\nReading from " INPUT_EVENT " failed!\n");
            return 1;
        }

        if (ev.value == 1 && ev.type == 1) {    /* Down press only */
            key_code = ev.code;
            if (key_code == KEY_DOWN) {    /* lower speed */
                /* Protect from concurrent read/write */
                pthread_mutex_lock(&lock);
                if (led_speed > 0)
                    led_speed -= 1;
                pthread_mutex_unlock(&lock);
            } else if (key_code == KEY_UP) {    /* raise speed */
                pthread_mutex_lock(&lock);
                if (led_speed < 9)
                    led_speed += 1;
                pthread_mutex_unlock(&lock);
            }
            printf("Speed: %i\n", led_speed);
            usleep(1000);
        }
    }
}

编译,打包:

petalinux-build
cd ./images/linux
petalinux-package --boot --fsbl zynq_fsbl.elf --fpga min_system_wrapper.bit --u-boot –force

复制BOOT.bin和image.ub到SD卡,插入ZedBoard的SD卡插槽,配置为SD卡启动,启动开发板。

三、测试

启动后执行以下命令观察效果:

root@miz702:~# cd /sys/class/leds/
root@miz702:/sys/class/leds# ls
led-ld0  led-ld2  led-ld4  led-ld6  led-ld9
led-ld1  led-ld3  led-ld5  led-ld7  mmc0::

root@miz702:/sys/class/leds# cd led-ld5
root@miz702:/sys/class/leds/led-ld5# ls
brightness      max_brightness  subsystem       uevent
device          power           trigger
root@miz702:/sys/class/leds/led-ld5# cat brightness 
255
root@miz702:/sys/class/leds/led-ld5# echo 0 > brightness 
root@miz702:/sys/class/leds/led-ld5# echo 1 > brightness 
root@miz702:/sys/class/leds/led-ld5# cat trigger 
[none] nand-disk mmc0 timer oneshot heartbeat backlight gpio cpu0 cpu1 default-on transient flash torch 
root@miz702:/sys/class/leds/led-ld5# echo heartbeat > trigger 
root@miz702:/sys/class/leds/led-ld5# 


root@miz702:/sys/class/leds/led-ld5# cd /sys/class/input
root@miz702:/sys/class/input# ls
event0  input0  mice
root@miz702:/sys/class/input# gpiolib_test

测试过程:
0. 默认状态下ld0~ld7常亮,ld9闪烁
1. 查看了gpio-leds下的设备,包含了led-ld0到led-ld9
2. 进入led-ld5,进行了灭与亮的操作,然后配置为心跳模式,可以看到ld5与ld9同样的闪烁
3. 看到input0的设备,并运行gpiolib_test程序,可以看到ld7开始闪烁,按下up或down按键,可以调整闪烁频率。按下任何按键时,串口都有打印输出状态,说明btn和sw都工作OK,同时说明上一节做的EMIO Slice IP没有问题。

至此,gpio-leds和gpio-keys的驱动就测试完成了,参考官方的代码能够暂时满足我的需求,其他的驱动方式稍后再学习吧。

你可能感兴趣的:(Xilinx,FPGA,PetaLinux)