原来使用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/79134503https://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/79707359input子系统:https://blog.csdn.net/morixinguan/article/details/79783061下面我们看看完整的按键驱动:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//按键管脚定义
#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
", 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!
", irq_no);
return -EBUSY;
}
}
input_register_device(inputdev); /* 注册设备驱动 */
printk("EasyARM-i.MX28x key driver up
");
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
");
}
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应用开发(下册) 周立功