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