天天看點

tiny4412 Linux驅動4個按鍵控制4個LED

 開發闆上有4個按鍵,4個可控的LED燈,本次學習目标是對應按鍵控制對應LED燈,每按下一下按鍵,對燈的狀态進行翻轉。

1.硬體原理:

tiny4412 Linux驅動4個按鍵控制4個LED
tiny4412 Linux驅動4個按鍵控制4個LED

LED1連接配接GPM4.0,LED2連接配接GPM4.1,LED3連接配接GPM4.2,LED4連接配接GPM4.3,燈亮:輸出低電平;燈滅:輸出高電平

按鍵硬體:

tiny4412 Linux驅動4個按鍵控制4個LED
tiny4412 Linux驅動4個按鍵控制4個LED

按鍵按下,下降沿觸發。

檢視資料手冊

tiny4412 Linux驅動4個按鍵控制4個LED

按鍵寄存器位址都已經封裝好了,我們可以在驅動了通過宏直接調用。

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
           

當按下第一個按鍵時,第一個燈會亮,在按時會翻轉,第二第三個燈也是如此,哈哈~

繼續閱讀