让linux支持EC11等旋转编码器


最近,产品基本定型了,所以开始着手升级版。说是升级版,只是在操作上面有所修改。产品需要使用旋钮进行人机交互。对linux驱动十窍通了九窍,一窍不通,所以从最低级的开始看起,然后把gpio_key.c这个东西,看了一遍又一遍。都快看吐了 。

首先修改设备树文件,这个东西是linux3.0的新东西,弄的我无限糊涂,到现在依然糊涂。我只能稍微修改一点需要的东西,让我写肯定写不出来。以后再慢慢研究。在这个文件的KEY_X0里面,把对应的,改成。因为是使用设备树这种方式,所以需要修改对应的gpio_keys_get_devtree_pdata这个函数,


修改以上两处,这个函数的改动就结束了。这样改完编译会发现有错误,因为button里面没有ckgpio这些东西。所以修改对应linux目录中include/linux/gpio_key.h

struct gpio_keys_button {
	/* Configuration parameters */
	unsigned int code;	/* input event code (KEY_*, SW_*) */
	int gpio;		/* -1 if this key does not support gpio */
	int active_low;
	const char *desc;
	unsigned int type;	/* input event type (EV_KEY, EV_SW, EV_ABS) */
	int wakeup;		/* configure the button as a wake-up source */
	int debounce_interval;	/* debounce ticks interval in msecs */
	bool can_disable;
	int value;		/* axis value for EV_ABS */
	unsigned int irq;	/* Irq number in case of interrupt keys */
};
改成:

struct gpio_keys_button {
	/* Configuration parameters */
	unsigned int code;	/* input event code (KEY_*, SW_*) */
	int gpio;		/* -1 if this key does not support gpio */
	int active_low;
	const char *desc;
	unsigned int type;	/* input event type (EV_KEY, EV_SW, EV_ABS) */
	int wakeup;		/* configure the button as a wake-up source */
	int debounce_interval;	/* debounce ticks interval in msecs */
	bool can_disable;
	int value;		/* axis value for EV_ABS */
	unsigned int irq;	/* Irq number in case of interrupt keys */
	int ckgpio; /*zxf*/     保存旋转编码器的另外一个引脚
	unsigned int oldcode;   记录原始键值
	unsigned int ckcode; /*zxf*/ 记录新键值
};
这样设备树就修改完了。

接下来修改中断触发方式,我只需下降沿,就可以判断是正转还是反转了,所以:

	if (button->ckgpio != button->gpio)
		irqflags = IRQF_TRIGGER_FALLING;对应旋钮部分
	else
		irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;对应按键部分

因为ec11这种东西有旋钮部分,也有按钮部分,所以把对应的中断响应函数进行了区分,按钮部分不动,旋钮部分改成:

static irqreturn_t gpio_knob_gpio_isr(int irq, void *dev_id)
{
	struct gpio_button_data *bdata = dev_id;

	BUG_ON(irq != bdata->irq);
	if(gpio_get_value(bdata->button->ckgpio) )判断对应的另外一个引脚电平状态。
		bdata->button->code = bdata->button->oldcode;
	else
		bdata->button->code = bdata->button->ckcode;
	//printk(KERN_ALERT"gpio:%d, ckgpio:%d, check=%d", bdata->button->gpio, bdata->button->ckgpio,bdata->button->check);

	if (bdata->timer_debounce)
		mod_timer(&bdata->timer,
			jiffies + msecs_to_jiffies(bdata->timer_debounce));
	else
		schedule_work(&bdata->work);
	return IRQ_HANDLED;
}
中断分为上半部分,和下半部分,上述代码是中断的上半部分。功能就是检测另外一个引脚的点评状态,并给code赋不同的键值。

中断的下半部分,事件上报函数改成:

static void gpio_knob_gpio_report_event(struct gpio_button_data *bdata)
{
	struct gpio_keys_button *button = bdata->button;
	struct input_dev *input = bdata->input;
	unsigned int type = button->type ?: EV_KEY;
	
	input_event(input, type, button->code, 1);
	//printk(KERN_ALERT"type:%d,code:%d\n", type,button->code);
	input_sync(input);
	input_event(input, type, button->code, 0);
	//printk(KERN_ALERT"type:%d,code:%d\n", type,button->code);
	input_sync(input);
}
可以看到这里面有两条重复的语句,原因是按键有按下和放开两个状态,而我的旋钮,我值捕捉到了下降沿,所以上升沿部分我自己编造一个,反正内核不知道我是认为编造的。红色部分很重要,如果只有蓝色部分,那就相当于按键一直按下,而没有弹起。cat event*会发现一直不停的输出。我在这就卡了两天,因为没有好好理解什么是key。

中断部分修改完了以后,修改gpio_keys_setup_key()函数。

if (gpio_is_valid(button->gpio)) {

		error = gpio_request(button->gpio, desc);
		if (error < 0) {
			dev_err(dev, "Failed to request GPIO %d, error %d\n",
				button->gpio, error);
			return error;
		}
		if (button->ckgpio != button->gpio){
			error = gpio_request(button->ckgpio, desc);
			if (error < 0) {
				dev_err(dev, "Failed to request GPIO %d, error %d\n",
					button->ckgpio, error);
				return error;
			}

		}

		error = gpio_direction_input(button->gpio);
		if (error < 0) {
			dev_err(dev,
				"Failed to configure direction for GPIO %d, error %d\n",
				button->gpio, error);
			goto fail;
		}

		if (button->ckgpio != button->gpio){
			error = gpio_direction_input(button->ckgpio);
			if (error < 0) {
				dev_err(dev,
					"Failed to configure direction for GPIO %d, error %d\n",
					button->ckgpio, error);
				goto fail;
			}
		}
就是增加了对旋钮部分按键的配置操作。

现在编译应该可以通过,使用hexdump测试event会发现,只有一个方向的旋转才有输出。另一个方向是没有反映的,通过跟踪代码发现,是在input_handle_event()函数出现的问题:

case EV_KEY:       
		if (is_event_supported(code, dev->keybit, KEY_MAX) &&
		    !!test_bit(code, dev->key) != value) {
		    
			if (value != 2) {
				__change_bit(code, dev->key);
				if (value)
					input_start_autorepeat(dev, code);
				else
					input_stop_autorepeat(dev);
			}

			disposition = INPUT_PASS_TO_HANDLERS;
		}
		break;
红色部分就是跟踪后发现问题的地方,另一个方向旋转并不能进入到这个if语句中,再继续跟踪发现:

static inline int is_event_supported(unsigned int code, unsigned long *bm, unsigned int max)
{
	return code <= max && test_bit(code, bm);
}
这个函数的test_bit函数出的问题,它会测试input_dev是否支持这个键值。这个地方卡了我两周的时间,因为不了解linux的输入子系统。我现在还不知道,可以上报的那个键值是在哪设置的,有知道的前辈请不吝告诉我。为了让input_dev支持另外一个键值,在gpio_keys_setup_key()函数增加如下代码:

set_bit(button->ckcode, input->keybit);
这样就可以了。
这个事情进行到一半的时候,想过自己重新写。可是真的没有信心能写出来,这个事情我还欠缺很多。把原文件也是弄得面目全非。以后再继续学习把。







你可能感兴趣的:(linux,记录)