開發闆上有4個按鍵,4個可控的LED燈,本次學習目标是對應按鍵控制對應LED燈,每按下一下按鍵,對燈的狀态進行翻轉。
1.硬體原理:
LED1連接配接GPM4.0,LED2連接配接GPM4.1,LED3連接配接GPM4.2,LED4連接配接GPM4.3,燈亮:輸出低電平;燈滅:輸出高電平
按鍵硬體:
按鍵按下,下降沿觸發。
檢視資料手冊
按鍵寄存器位址都已經封裝好了,我們可以在驅動了通過宏直接調用。
2.gpio庫函數
//申請一個GPIO
int gpio_request(unsigned gpio, const char *label)
//申請一組
int gpio_request_array(const struct gpio *array, size_t num)
//釋放一個GPIO
void gpio_free(unsigned gpio)
//釋放一組GPIO
void gpio_free_array(const struct gpio *array, size_t num)
//把gpio定義成輸出模式
int gpio_direction_output(unsigned gpio, int value)
//把gpio定義成輸入模式,很多時候這個函數都是有問題的,慎用
int gpio_direction_input(unsigned gpio)
//擷取io的電平值
int gpio_get_value(unsigned gpio)
//設定io的電平
void gpio_set_value(unsigned gpio, int value)
//把gpio轉成對應的irq号(前提是該io支援irq)
int gpio_to_irq(unsigned gpio)
//把irq号轉成gpio口号(前提是該io支援irq)
int irq_to_gpio(unsigned irq)
參數的意思:
gpio: gpio口号,在頭檔案裡
label: 别名
array: gpio數組
num: gpio數組的元素個數
value: 預設的電平值
irq: irq号
傳回值:失敗小于0
驅動代碼:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#define LED1_GPIO EXYNOS4X12_GPM4(0)
#define LED2_GPIO EXYNOS4X12_GPM4(1)
#define LED3_GPIO EXYNOS4X12_GPM4(2)
#define LED4_GPIO EXYNOS4X12_GPM4(3)
struct key_desc{
char *name;
int gpio;
int int_flag; // 中斷觸發方式
};
int irq_key[4]={0};
int led_gpio[4]={LED1_GPIO,LED2_GPIO,LED3_GPIO,LED4_GPIO};
int value[4]={1,1,1,1};
// 定義所有按鍵的資訊集合, GPX3_2, 3_3, 3_4, 3_5.
/*
IRQF_DISABLED: 禁止中斷嵌套
IRQF_SHARED:這個中斷可以申請多次,這個标志位不能和下面的所有标志位同時使用,否則會出問題
IRQF_TRIGGER_RISING: 上升沿觸發
IRQF_TRIGGER_FALLING: 下降沿觸發
IRQF_TRIGGER_HIGH: 高電平觸發
IRQF_TRIGGER_LOW: 低電平觸發
IRQF_TRIGGER_MASK: 全部觸發
*/
struct key_desc all_keys[] = {
[0] = {
.name = "key1_button",
.gpio = EXYNOS4_GPX3(2),
.int_flag = IRQF_TRIGGER_FALLING | IRQF_DISABLED,
},
[1] = {
.name = "key2_button",
.gpio = EXYNOS4_GPX3(3),
.int_flag = IRQF_TRIGGER_FALLING | IRQF_DISABLED,
},
[2] = {
.name = "key3_button",
.gpio = EXYNOS4_GPX3(4),
.int_flag = IRQF_TRIGGER_FALLING | IRQF_DISABLED,
},
[3] = {
.name = "key4_button",
.gpio = EXYNOS4_GPX3(5),
.int_flag = IRQF_TRIGGER_FALLING | IRQF_DISABLED,
},
};
static struct gpio led_gpio_array[] = {
{ LED1_GPIO, GPIOF_OUT_INIT_HIGH, "LED1" },
{ LED2_GPIO, GPIOF_OUT_INIT_HIGH, "LED2" },
{ LED3_GPIO, GPIOF_OUT_INIT_HIGH, "LED3" },
{ LED4_GPIO, GPIOF_OUT_INIT_HIGH, "LED4" },
};
//中斷上文
irqreturn_t key_irq(int irq, void *args)
{
#if 0
int i=0;
static int num = 0;
if(num==2){
for(i = 0; i < 4; i++){
if(irq==irq_key[i]){
printk("key%d Down!\n", i+1);
value[i]^=1;
gpio_set_value(led_gpio[i],value[i]);
}
}
num=0;
}
num++;
#else
int i;
static int n = 0;
struct key_desc *p = (struct key_desc *)args;
udelay(20);
if(gpio_get_value(p->gpio)){ // 彈起
printk(" %s UP!\n", p->name);
} else { // 按下
printk(" %s Down!\n", p->name);
for(i = 0; i < ARRAY_SIZE(all_keys); i++)
{
n++;
if(p->gpio == EXYNOS4_GPX3(i+2))
{
gpio_set_value(led_gpio_array[i].gpio, n%2);//亮
}
if(n%5==0) n=0;
}
}
#endif
return IRQ_HANDLED;
}
static int __init tiny4412_key_init(void)
{
int ret;
int i = 0;
for(i = 0; i < ARRAY_SIZE(all_keys); i++)
{
irq_key[i]=gpio_to_irq(all_keys[i].gpio);
ret = request_irq(irq_key[i], key_irq, all_keys[i].int_flag,all_keys[i].name, &all_keys[i]);
if(ret < 0){
printk("request irq failed!\n");
return ret;
}
}
return 0;
}
static void __exit tiny4412_key_exit(void)
{
int i;
for(i = 0; i < ARRAY_SIZE(all_keys); i++)
free_irq(gpio_to_irq(all_keys[i].gpio), &all_keys[i]);
gpio_free_array(led_gpio_array,ARRAY_SIZE(led_gpio_array));
}
module_init(tiny4412_key_init);
module_exit(tiny4412_key_exit);
MODULE_LICENSE("GPL");
Makefile:
obj-m += up_and_down.o
KERN_DIR=/root/work/tiny4412/linux/linux-3.5
PWD := $(shell pwd)
modules:
$(MAKE) ARCH=arm -C $(KERN_DIR) M=$(PWD) modules
clean:
$(MAKE) ARCH=arm -C $(KERN_DIR) M=$(PWD) modules clean
在linux中make一下,把編譯生成的up_and_down.ko複制到開發中。
在開發闆平台,xshell序列槽助手中安裝驅動,指令:
insmod up_and_down.ko
當按下第一個按鍵時,第一個燈會亮,在按時會翻轉,第二第三個燈也是如此,哈哈~