MTK开发之—最简单的触摸按键驱动

现在比较喜欢直接上代码,代码也不算难,就直入主题了!

本程序实际包含了设备树、pinctrl子系统、gpio中断、工作队列等的使用,是一个较完善的触摸按键驱动,已在MTK8163上验证!

代码实例

#include <linux/irq.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/string.h>
#include <linux/gpio.h>
#include <linux/timer.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/fs.h>
#include <linux/input.h>
#include <linux/workqueue.h>
#include <linux/pinctrl/consumer.h>

static struct input_dev *touch_input_dev;

static struct work_struct work;
static struct workqueue_struct *wq;

static struct pinctrl *pin_ctrl;
static struct pinctrl_state *tp_int10, *tp_int11;

static unsigned int int10_gpio32,int11_gpio43;
static unsigned int irq_num10, irq_num11;
static unsigned char touch_event=0;

static void touch_work_handle(struct work_struct *work)
{
	if(touch_event=='a') {
		printk("work queue handle: aaa event\n");
	}
 	else if(touch_event=='b') {
		printk("work queue handle: bbb event\n");
	}
	touch_event=0;
}

static int tp_get_gpio_info(struct platform_device *pdev)
{
	int ret;
	/*如果设备树节点中把pinctrl-names = "default",则以下代码可以不写*/
	/*获取设备对应的pin control state holder*/
	pin_ctrl = devm_pinctrl_get(&pdev->dev);
	if (IS_ERR(pin_ctrl)) {
		ret = PTR_ERR(pin_ctrl);
		dev_err(&pdev->dev, "tp Cannot find touch pin_ctrl!\n");
		return ret;
	}
	/*查找pin control state*/
	tp_int10 = pinctrl_lookup_state(pin_ctrl, "tp_int10");
	if (IS_ERR(tp_int10)) {
		ret = PTR_ERR(tp_int10);
		dev_err(&pdev->dev, "tp Cannot find touch pinctrl default %d!\n", ret);
	}
	tp_int11 = pinctrl_lookup_state(pin_ctrl, "tp_int11");
	if (IS_ERR(tp_int11)) {
		ret = PTR_ERR(tp_int11);
		dev_err(&pdev->dev, "tp Cannot find touch pinctrl default %d!\n", ret);
	}
	/*设置gpio功能, 将获取到的信息直接设置到硬件*/
	pinctrl_select_state(pin_ctrl, tp_int10);
	pinctrl_select_state(pin_ctrl, tp_int11);
	printk("tp_get_gpio_info-----\n");
	return ret;
}
static irqreturn_t tp_eint_interrupt_handler(int irq, void *dev_id)
{
	if(irq_num10 == irq) {
	    touch_event='a';
		printk("interrupt:---eint10---\n");
	}
	else if(irq_num11 == irq) {
		touch_event='b';
		printk("interrupt:---eint11---\n");
	}
	if (!work_pending(&work)) {
		queue_work(wq, &work);
	}
	return IRQ_HANDLED;
}

static int touch_probe(struct platform_device *dev)
{
	int ret, err;
	struct device_node *node = NULL;
	int debounce = 17;//消抖时间设置,单位(ms)

	tp_get_gpio_info(dev);//得到gpio信息
	//从设备树中根据compatible从树根开始寻找节点
	node = of_find_compatible_node(NULL, NULL, "mediatek,touchsensor");
	if (node) {
		//此函数从设备树中根据名称提取gpio口编号
		//会将设备树中类似<&pio 32 0>的属性信息转换为对应的GPIO编号
		int10_gpio32= of_get_named_gpio(node, "int10_gpio32", 0);
		int11_gpio43= of_get_named_gpio(node, "int11_gpio43", 0);
		//标记已有驱动使用了该gpio,此处就没判断返回结果了
		gpio_request(int10_gpio32, "int10_gpio32");
		gpio_request(int11_gpio43, "int11_gpio43");
		//设置gpio消抖时间
		gpio_set_debounce(int10_gpio32, debounce);
		gpio_set_debounce(int11_gpio43, debounce);
		//从设备树interupts属性中提取到对应的中断号
		irq_num10 = irq_of_parse_and_map(node, 2);
		printk("tp_probe_irq = %d\n", irq_num10);
		//request_irq用于申请激活中断,设置以后可以在/proc/interrupts中看到对应的中断名字
		ret = request_irq(irq_num10, (irq_handler_t) tp_eint_interrupt_handler, \
							 IRQF_TRIGGER_FALLING, "touchsensor-eint", NULL);
		irq_num11 = irq_of_parse_and_map(node, 3);
		printk("tp_probe_irq = %d\n", irq_num11);
		ret = request_irq(irq_num11, (irq_handler_t) tp_eint_interrupt_handler, \
							IRQF_TRIGGER_FALLING, "touchsensor-eint", NULL);
		if (ret > 0) {
			ret = -1;
			printk("tp_probe request_irq IRQ LINE NOT AVAILABLE!ret = %d.",ret);
		}
	}
	/*********************************************************/
	touch_input_dev = input_allocate_device();	//分配设备,注册设备支持event类型
	__set_bit(EV_SYN, touch_input_dev->evbit);  //设置产生同步事件
	__set_bit(EV_KEY, touch_input_dev->evbit);	//设置产生按键事件
	__set_bit(KEY_VOLUMEUP, touch_input_dev->keybit);//设置产生哪些按键值
	touch_input_dev->name ="TouchSensor";
	err = input_register_device(touch_input_dev);//向input子系统注册input_dev设备
	if (err) {
		printk( "touch register input device failed (%d)\n", err);
		input_free_device(touch_input_dev);
		return err;
	}
	/*********************************************************/
	//create_workqueue创建一个workqueue队列,为系统中的每个CPU都创建一个内核线程
	wq = create_singlethread_workqueue("kworkqueue_iqs");//只创建一个内核线程
	flush_workqueue(wq);//清理指定工作队列中的所有任务
	//work表示要初始化的工作,touch_mcu_worker是工作对应的处理函数
	INIT_WORK(&work, touch_work_handle);

	printk("touch_probe====\n");
	
	return 0;
}

#ifdef CONFIG_OF//有设备树的情况下就用这个匹配
static const struct of_device_id TP_of_match[] = {
	{.compatible = "mediatek,touchsensor"},
	{},
};
#endif

static struct platform_driver tp_platform_driver = {
	.probe = touch_probe,
	.driver = {
		   .name = "touchsensor",
		   .owner = THIS_MODULE,
#ifdef CONFIG_OF
		   .of_match_table = TP_of_match,
#endif
	},
};

static int __init touchsensor_init(void)
{	
	platform_driver_register(&tp_platform_driver);
	printk("touchsensor init finish-------------\n");
	return  0;
}

static void __exit touchsensor_exit(void)
{
	free_irq(irq_num10, NULL);//释放irq_num10中断
	free_irq(irq_num11, NULL);//释放irq_num11中断
	input_unregister_device(touch_input_dev);//注销touch_input_dev
	input_free_device(touch_input_dev); 	 //删除touch_input_dev
	return  0;
}

module_init(touchsensor_init);
module_exit(touchsensor_exit);

MODULE_AUTHOR("mstar");
MODULE_DESCRIPTION("touchsensor");
MODULE_LICENSE("GPL");

设备树

设备树中,touchsensor 节点进行了重写,描述了四个gpio与中断信息,其中pinctrl是属于pinctrl子系统的描述

&touchsensor {
	interrupt-parent = <&pio>;
	interrupts = <8 IRQ_TYPE_EDGE_FALLING>,
				 <9 IRQ_TYPE_EDGE_FALLING>,
				 <10 IRQ_TYPE_EDGE_FALLING>,
				 <11 IRQ_TYPE_EDGE_FALLING>;
				 
	eint-debounce = <256>;	
	pinctrl-names = "tp_int8", "tp_int9","tp_int10","tp_int11";
	pinctrl-0 = <&touchsensor_pins_tp_int8>;
	pinctrl-1 = <&touchsensor_pins_tp_int9>;
	pinctrl-2 = <&touchsensor_pins_tp_int10>;
	pinctrl-3 = <&touchsensor_pins_tp_int11>;
	
	int8_gpio30 = <&pio 30 0>;
	int9_gpio31 = <&pio 31 0>;
	int10_gpio32 = <&pio 32 0>;
	int11_gpio43 = <&pio 43 0>;
	status = "okay";
};

运行测试

从adb进入查看中断信息
adb shell
cat /proc/interrupts
如下所示,右边的名称就是程序中request_irq()函数申请时所设置的名称
在这里插入图片描述
用镊子或者手指触发板子上的gpio30,gpio31,串口就有如下打印,说明程序触发中断后,
再进入工作队列中处理,与我们想要的结果一致
在这里插入图片描述

函数释义

gpio_request函数

linux下,一般来说,一个GPIO只是分配给一个设备的,所以这个设备的驱动会请求这个GPIO。这样,在其他的设备也想请求这个GPIO的时候会返回失败。事实上,gpio_request只是给这个GPIO做一个标示,并没有什么实质的作用。

操作GPIO是通过gpio_set_value、gpio_direction_output之类的函数去做的,即便没有request,一样可以设置GPIO的电平。
对于设备驱动来说,应该保证每一个在初始化的时候(一般是probe),对和设备有关的GPIO都进行一次gpio_request,在remove时候时候使用gpio_free。当然,如果probe失败,应该在probe里面free掉已经request过的GPIO。每次使用的时候不需要再request和free了,只需要直接gpio_set_value就可以了。

irq_of_parse_and_map

编写驱动的时候需要用到中断号,我们用到中断号,中断信息已经写到了设备树里面,因此可以通过 irq_of_parse_and_map 函数从 interupts 属性中提取到对应的设备号,函数原型如下:
unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
dev:设备节点。
index:索引号,interrupts 属性可能包含多条中断信息,通过 index 指定要获取的信息。
返回值:中断号。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东皇※太一

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值