1. 程式人生 > >-10-GPIO驅動程式【Xilinx-Petalinux學習】

-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圖如下:
這裡寫圖片描述
從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配置

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 <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>
#include <pthread.h>
#include <linux/input.h>

#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的驅動就測試完成了,參考官方的程式碼能夠暫時滿足我的需求,其他的驅動方式稍後再學習吧。