基于周立功AP-283Demo板按键驱动开发(基于IMX287A开发板)

原来使用Tiny4412开发板的时候写过一篇案件驱动的文章:https://blog.csdn.net/morixinguan/article/details/50629317。

如图所示,这就是周立功平台的283demo板,下图框框所示的是5个按键:


这五个按键的原理图:


分别接在GPIO2_6、GPIO2_5、GPIO2_4、GPIO1_18、GPIO1_17,所以需要用排针插上去:


也就是以下这个区域:


于是我们可以得知,KEY1、KEY2位于BANK1,KEY3、KEY4、KEY5位于BANK2。飞思卡尔在板级文件中已经给出了将管脚以及如何将GPIO编号计算的方法写成了宏,有兴趣的朋友可以去根据芯片手册分析一下计算方法:

参考:

https://blog.csdn.net/morixinguan/article/details/79176841

https://blog.csdn.net/morixinguan/article/details/79134503

https://blog.csdn.net/morixinguan/article/details/79138325

计算方法的实现在:arch/arm/plat-mxs/include/mach/pinctrl.h

#define PINS_PER_BANK		32
#define GPIO_TO_PINS(gpio)	((gpio) % 32)
#define GPIO_TO_BANK(gpio)	((gpio) / 32)

#define MXS_GPIO_MASK		0x7FFFFFFF
#define MXS_PIN_TO_GPIO(p)	(((p) & MXS_PIN_PINID_MAX) |\
				 ((((p) >> MXS_PIN_BANK_BIT) &\
				 MXS_PIN_BANK_MAX) * PINS_PER_BANK))

#define MXS_PIN_BANK_BIT	24
#define MXS_PIN_BANK_MAX	(MXS_GPIO_MASK >> (MXS_PIN_BANK_BIT - 1))
#define MXS_PIN_PINID_MAX	((1 << MXS_PIN_BANK_BIT) - 1)
#define MXS_PIN_TO_BANK(p)	(((p) >> MXS_PIN_BANK_BIT) & MXS_PIN_BANK_MAX)
#define MXS_PIN_TO_PINID(p)	((p) & MXS_PIN_PINID_MAX)

#define MXS_PIN_ENCODE(b, p)	\
		((((b) & MXS_PIN_BANK_MAX) << MXS_PIN_BANK_BIT) |\
		 ((p) & MXS_PIN_PINID_MAX))

按键的引脚的编号也被定以成了宏:

#define PINID_LCD_D17		MXS_PIN_ENCODE(1, 17)
#define PINID_LCD_D18		MXS_PIN_ENCODE(1, 18)
#define PINID_SSP0_DATA4	MXS_PIN_ENCODE(2, 4)
#define PINID_SSP0_DATA5	MXS_PIN_ENCODE(2, 5)
#define PINID_SSP0_DATA6	MXS_PIN_ENCODE(2, 6)

接下来要做的事情就是将引脚的编号转换为GPIO的编号,调用MXS_PIN_TO_GPIO,再将对应的宏传入即可。

按键的编程很简单,一般遵循,中断+input子系统的模版即可很容易写出相应的驱动程序,特别要注意的地方,中断底半部,什么是中断上下文?什么是

参考:

Linux内核基础:https://blog.csdn.net/morixinguan/article/details/79705634

中断处理:https://blog.csdn.net/morixinguan/article/details/79707359

input子系统:https://blog.csdn.net/morixinguan/article/details/79783061

下面我们看看完整的按键驱动:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/err.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/workqueue.h>
//按键管脚定义
#define PINID_LCD_D17		MXS_PIN_ENCODE(1, 17)
#define PINID_LCD_D18		MXS_PIN_ENCODE(1, 18)
#define PINID_SSP0_DATA4	MXS_PIN_ENCODE(2, 4)
#define PINID_SSP0_DATA5	MXS_PIN_ENCODE(2, 5)
#define PINID_SSP0_DATA6	MXS_PIN_ENCODE(2, 6)

//定义输入设备指针
struct input_dev *inputdev ;

struct imx28x_key_struct {
	/* 按键能产生的键值*/
	int key_code;  
	/* GPIO编号 */
	int gpio;  
	/* 按键的工作队列  */
	struct work_struct work;  
};

struct imx28x_key_struct keys_list[] ={
	{.key_code = KEY_A, .gpio = MXS_PIN_TO_GPIO(PINID_LCD_D17)},
	{.key_code = KEY_B, .gpio = MXS_PIN_TO_GPIO(PINID_LCD_D18)},
	{.key_code = KEY_C, .gpio = MXS_PIN_TO_GPIO(PINID_SSP0_DATA4)},
	{.key_code = KEY_D, .gpio = MXS_PIN_TO_GPIO(PINID_SSP0_DATA5)},
	{.key_code = KEY_E, .gpio = MXS_PIN_TO_GPIO(PINID_SSP0_DATA6)}
};

static void imx28x_scankeypad(struct work_struct *_work)
{
	/* 通过工作队列指针而获得它所属的 imx28x_key_struct 类型的对象 */
	struct imx28x_key_struct *key_tmp = container_of(_work, struct imx28x_key_struct, work);
	int gpio = key_tmp->gpio;
	int code = key_tmp->key_code;
	/* 每隔 10mS 检查按键是否已经提起,如果没有提起就一直等待 */
	while(!gpio_get_value(gpio)){ 
		mdelay(10); 
	}
	/* 报告按键提起事件 */
	input_report_key(inputdev, code, 0);  
	input_sync(inputdev);
}

static irqreturn_t imx28x_key_intnerrupt(int irq, void *dev_id, struct pt_regs *regs)
{
	int i = (int)dev_id;
	/* 获取按键的 GPIO */
	int gpio = keys_list[i].gpio; 
	/* 获取按键的键值  */
	int code = keys_list[i].key_code; 
	/*
	* 延迟 20uS,看按键是不是按下,如果不是,就是抖动
	*/
	udelay(20);
	if (gpio_get_value(gpio)) {
		return IRQ_HANDLED;
	}
	/* 先报告键按下事件 */
	input_report_key(inputdev, code, 1); 
	/* 报告同步事件*/	
	input_sync(inputdev);	
    /* 提交工作队列,实现中断的下半部处理 */	
	schedule_work(&(keys_list[i].work));  
	return IRQ_HANDLED;
}

static int __devinit iMX28x_key_init(void)
{
	int i = 0, ret = 0;
	int irq_no = 0;
	int code, gpio;.
	/* 为输入设备驱动对象申请内存空间*/
	inputdev = input_allocate_device(); 
	if (!inputdev) {
		return -ENOMEM;
	}
	inputdev->name = "EasyARM-i.MX28x_key";
	/* 设置输入设备支持按键事件 */
	set_bit(EV_KEY, inputdev->evbit); 
	for (i = 0; i < sizeof(keys_list)/sizeof(keys_list[0]); i++) {
		code = keys_list[i].key_code;
		gpio = keys_list[i].gpio;
		/* 为每个按键都初始化工作队列  */
		INIT_WORK(&(keys_list[i].work), imx28x_scankeypad);
		/* 设置输入设备支持的键值  */
		set_bit(code, inputdev->keybit); 
		/* 为每个按键都初始化 GPIO */
		gpio_free(gpio);
		ret = gpio_request(gpio, "key_gpio");
		if (ret) {
			printk("request gpio failed %d \n", gpio);
			return -EBUSY;
		}
		/* 当 GPIO 被设置为输入工作状态后,就可以检测中断信号 */
		gpio_direction_input(gpio);
		/* 把每个 GPIO 中断响应方式都设置为下降沿响应 */
		irq_no = gpio_to_irq(gpio);
		set_irq_type(gpio, IRQF_TRIGGER_FALLING);
		/* 为每个按键的中断都安装中断处理函数,其私有数据为按键信息在 keys_list 数组下的索引 */
		ret = request_irq(irq_no, imx28x_key_intnerrupt, IRQF_DISABLED, "imx28x_key", (void *)i);
		if (ret) {
			printk("request irq faile %d!\n", irq_no);
			return -EBUSY;
		}
	}
	input_register_device(inputdev); /* 注册设备驱动  */
	printk("EasyARM-i.MX28x key driver up \n");
	return 0;
}

static void __exit iMX28x_key_exit(void)
{
	int i = 0;
	int irq_no;
	for (i = 0; i < sizeof(keys_list)/sizeof(keys_list[0]); i++) {
		/* 为每个按键释放 GPIO */
		irq_no = gpio_to_irq(keys_list[i].gpio); 
		/* 为每个按键卸载中断处理函数 */
		free_irq(irq_no, (void *)i); 
	}
	/* 注销输入设备驱动 */
	input_unregister_device(inputdev); 
	printk("EasyARM-i.MX28x key driver remove \n");
}

module_init(iMX28x_key_init);  
module_exit(iMX28x_key_exit);  
MODULE_LICENSE("GPL");

再写一个用于编译驱动模块的Makefile:

obj-m   += imx287_button.o
  
ROOTFS = .  
KERNEL_SRC  = /home/vmuser/workspace/kernel_si/kernel/linux-2.6.35.3
  
all:  
	make -C $(KERNEL_SRC) M=`pwd` modules    
clean:  
	make -C $(KERNEL_SRC) M=`pwd` clean 
install:  
	make -C $(KERNEL_SRC) M=`pwd` modules_install INSTALL_MOD_PATH=$(ROOTFS)  	  

参考:嵌入式Linux应用开发(下册)  周立功




猜你喜欢

转载自blog.csdn.net/morixinguan/article/details/80981964