开发板上有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
#include
#include
#include
#include
#include
#include
#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
当按下第一个按键时,第一个灯会亮,在按时会翻转,第二第三个灯也是如此,哈哈~