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

2019-07-13 09:06发布

原来使用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应用开发(下册)  周立功