前面幾篇文章,從最基礎的寄存器點燈,到設備樹點燈,再到GPIO子系統點燈,一步步了解嵌入式Linux開發的各種點燈原理。
點燈用到的都是GPIO的輸出功能,這篇,通過按鍵的使用,來學習GPIO輸入功能的使用。
1 硬件介紹
1.1 板子上按鍵原理圖
先來看原理圖,我板子上有4個按鍵sw1~sw4:
1.1.1 SW1
SW1是板子的系統復位按鍵,不可編程使用
1.1.2 SW2、SW3
SW2:SNVS_TAMPER1,GPIO5_1
平時是低電平,按下去是高電平。
SW3:ONOFF
它也是系統級的按鍵,用于長按進行開關機。
1.1.3 SW4
SW4是BOOT_MODE1腳,用來進行串行燒錄模式切換,需要再與復位鍵配合使用。
本篇僅測試按鍵功能,因此可以該按鍵。
1.1.4 使用其中2個按鍵
板子上這4個按鍵的功能特性如下表:
本實驗使用SW2和SW4這兩個按鍵來進行實驗。
2 軟件編寫
2.1 修改設備樹文件
2.1.1 修改iomuxc節點
修改imx6ull-myboard.dts,在iomuxc節點的imx6ull-evk字節點下創建一個名為pinctrl_key的子節點,節點內容如下:
pinctrl_key: keygrp {
fsl,pins = <
MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x3080 /* SW2 */
MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11 0xF080 /* SW4 */
>;
};
這部分是對引腳進行配置,這兩個引腳的定義是在imx6ull-pinfunc-snvs.h文件中:
引腳宏定義后面的值,是對引腳功能的配置:
SW2:0x3080,即0011 0000 1000 0000
SW4:0xF080,即1000 0000 1000 0000
對照之前講解GPIO的PAD寄存器的配置,根據兩個按鍵的實際電路配置上拉或下拉。
/*
*bit 16:0 HYS關閉
*bit [15:14]: [00]下拉 [01]47k上拉 [10]100k上拉 [11]22k上拉 <---
*bit [13]: [0]kepper功能 [1]pull功能
*bit [12]: [0]pull/keeper-disable [1]pull/keeper-enable
*bit [11]: 0 關閉開路輸出
*bit [10:8]: 00 保留值
*bit [7:6]: 10 速度100Mhz
*bit [5:3]: 000 輸出disable <---
*bit [2:1]: 00 保留值
*bit [0]: 0 低轉換率
*/
注:SW4 (MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11)這個GPIO,在設備中實際已經被其它設備(spi4)使用了。
在imx6ull-myboard.dts的300多行處,有:
pinctrl_spi4: spi4grp { fsl,pins = < MX6ULL_PAD_BOOT_MODE0__GPIO5_IO10 0x70a1 MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11 0x70a1 MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x70a1 MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x80000000 >; };
理論上我們應該把這里的配置給注釋掉,因為1個IO是不能同時進行2種功能的。由于本次實驗不使用spi4,暫且也先不管它,看看會有什么影響,如果影響了本實驗,再給把這里的配置給注掉。
2.1.2 添加key節點
在根節點下創建名為key的按鍵節點,內容如下:
key {
#address-cells = <1>;
#size-cells = <1>;
compatible = "myboard-key";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_key>;
key1-gpio = <&gpio5 1 GPIO_ACTIVE_HIGH>; /* SW2 */
key2-gpio = <&gpio5 11 GPIO_ACTIVE_LOW>; /* SW4 */
status = "okay";
};
2.2 編寫按鍵驅動程序
按鍵驅動,也屬于字符設備驅動,和之前的字符設備驅動的框架一樣,主要的修改點在按鍵的硬件初始化配置已經按鍵的讀取。
新建一個key-Bsp.c
2.2.1 按鍵的硬件初始化
初始化的流程,就是使用OF函數來從設備樹中獲取key節點,然后使用GPIO子系統的API函數,將GPIO配置為輸入。
static int keyio_init(void)
{
keydev.nd = of_find_node_by_path("/key");
if (keydev.nd== NULL)
{
return -EINVAL;
}
keydev.key1_gpio = of_get_named_gpio(keydev.nd ,"key1-gpio", 0);
keydev.key2_gpio = of_get_named_gpio(keydev.nd ,"key2-gpio", 0);
if ((keydev.key1_gpio < 0)||(keydev.key2_gpio < 0))
{
printk("can't get key\r\n");
return -EINVAL;
}
printk("key1_gpio=%d, key2_gpio=%d\r\n", keydev.key1_gpio, keydev.key2_gpio);
/* 初始化key所使用的IO */
gpio_request(keydev.key1_gpio, "key1"); /* 請求IO */
gpio_request(keydev.key2_gpio, "key2"); /* 請求IO */
gpio_direction_input(keydev.key1_gpio); /* 設置為輸入 */
gpio_direction_input(keydev.key2_gpio); /* 設置為輸入 */
return 0;
}
2.2.2 讀取按鍵的值
讀取按鍵的值,也是GPIO子系統的API函數來讀取。讀取到按鍵的值后,將該值傳遞出來給應用層使用,注意這里使用了原子操作的方式atomic_set和atomic_read實現數據的寫入和讀取。
/* 定義按鍵值 */
#define KEY1VALUE 0X01 /* 按鍵值 */
#define KEY2VALUE 0X02 /* 按鍵值 */
#define INVAKEY 0X00 /* 無效的按鍵值 */
static ssize_t key_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
int ret = 0;
int value;
struct key_dev *dev = filp->private_data;
if (gpio_get_value(dev->key1_gpio) == 1) /* key1按下 */
{
printk("get key1: high\r\n");
while(gpio_get_value(dev->key1_gpio)); /* 等待按鍵釋放 */
atomic_set(&dev->keyvalue, KEY1VALUE);
}
else if (gpio_get_value(dev->key2_gpio) == 0) /* key2按下 */
{
printk("get key2: low\r\n");
while(!gpio_get_value(dev->key2_gpio)); /* 等待按鍵釋放 */
atomic_set(&dev->keyvalue, KEY2VALUE);
}
else
{
atomic_set(&dev->keyvalue, INVAKEY); /* 無效的按鍵值 */
}
value = atomic_read(&dev->keyvalue);
ret = copy_to_user(buf, &value, sizeof(value));
return ret;
}
2.3 編寫按鍵應用程序
新建一個key-App.c
按鍵的應用層程序,主要就通過驅動程序提供的按鍵讀取接口,來循環讀取按鍵的值,并在按鍵按下時,將按鍵的值打印出來。
/* 定義按鍵值 */
#define KEY1VALUE 0X01
#define KEY2VALUE 0X02
#define INVAKEY 0X00
int main(int argc, char *argv[])
{
int fd, ret;
char *filename;
int keyvalue;
if(argc != 2)
{
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
/* 打開key驅動 */
fd = open(filename, O_RDWR);
if(fd < 0)
{
printf("file %s open failed!\r\n", argv[1]);
return -1;
}
/* 循環讀取按鍵值數據! */
while(1)
{
read(fd, &keyvalue, sizeof(keyvalue));
if (keyvalue == KEY1VALUE)
{
printf("KEY1 Press, value = %#X\r\n", keyvalue);
}
else if (keyvalue == KEY2VALUE)
{
printf("KEY2 Press, value = %#X\r\n", keyvalue);
}
}
ret= close(fd); /* 關閉文件 */
if(ret < 0)
{
printf("file %s close failed!\r\n", argv[1]);
return -1;
}
return 0;
}
3 實驗測試
3.1 編譯程序
3.1.1 編譯設備樹
編譯設備樹文件,并將編譯出的dtb文件復制到啟動文件夾:
網絡方式啟動開發板,查看key節點:
3.1.2 編譯按鍵驅動程序
3.1.3 編譯按鍵應用程序
3.2 測試
3.3 查看CPU占用率
先Ctrl+C結束掉此按鍵進程,然后使用如下指令來后臺運行按鍵程序:
./key-App /dev/key &
然后再使用指令:
top
來查看CPU是使用情況。從下圖可以看出,此時CPU的使用率是99.8%,全被按鍵檢查程序占用了,因為按鍵程序中有個while循環在一直讀取按鍵的值。
使用指令:
ps
查看按鍵的進程號,如下圖為149,再使用:
kill -9 149
來殺掉按鍵進程,然后再使用top指令查看,可以看到CPU的使用率又變回了0。
實際的按鍵使用中,一般不會使用本篇的這種持續檢測導致CPU占滿的方式,本篇只是先來介紹GPIO的輸入功能的使用,后續會使用更加高效的按鍵檢測機制來實現按鍵檢測功能。
4 總結
本篇主要介紹了i.MX6ULL的按鍵檢測的使用,主要的知識點是設備樹的修改,以及GPIO的輸入配置與高低電平的讀取。
-
嵌入式
+關注
關注
5087文章
19148瀏覽量
306176 -
Linux
+關注
關注
87文章
11322瀏覽量
209862 -
GPIO
+關注
關注
16文章
1213瀏覽量
52183 -
i.MX6
+關注
關注
1文章
37瀏覽量
16322
發布評論請先 登錄
相關推薦
評論