rotary-encoder旋钮编码器驱动

旋钮原理

                  _____       _____       _____
                 |     |     |     |     |     |
  Channel A    __|     |_____|     |_____|     |____

                     _____       _____       _____
                    |     |     |     |     |     |
  Channel B    _____|     |_____|     |_____|     |__
  
  

如果顺时针是这样的波形的话,逆时针就是它反过来从左到右看波形。
这样就出现了以下规则:
顺时针的时候:channelA电平变化:1–>1–>0–>0
channelB电平变化:0–>1–>1–>0
逆时针的时候:channelA电平变化:0–>1–>1–>0
channelB电平变化:1–>1–>0–>0
可以从上述规律中得出:只有第一、三阶段有不一样,第二、四阶段都是一样的电平。
我们可以以第二阶段作为起始点,第四阶段作为结束点,那么第三阶段就是判断是否顺时针还是逆时针了。按照这个思路,得出了下面encoder_interrupt_handler中断处理函数。

驱动架构

创建一个自己的platform_driver 结构体

static struct platform_driver encoder_driver = {
	.probe = encoder_probe,
	.remove = encoder_remove,
	.driver = {
		.name = DEV_NAME,
		.owner = THIS_MODULE,
		.of_match_table = of_match_ptr(encoder_match),
	}
};
module_platform_driver(encoder_driver);

新建一个设备驱动程序中的设备匹配表

static const struct of_device_id encoder_match[] = {
	{.compatible = "hyb_encoder"},
	{},
};

以下是我自己定义的结构体


enum encoder_direction{
	CLOCKWISE_DIRECTION = 0,  //顺时针
	ANTICLOCKWISE_DIRECTION,  //逆时针
};
struct hyb_encoder{
	int irq_portA;
	int irq_portB;
	int irq_numA;
	int irq_numB;
	unsigned char direction; // 顺时针true 逆时针false
	bool start_rolate_flag;//开始转动标志位
	struct input_dev *inputdev;
};

如果of_match_table与设备树中的节点compatible匹配上的话,就进入到probe函数中

static int encoder_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct hyb_encoder *encoder_dev;
	struct input_dev *input;
	int err;
	encoder_dev = kzalloc(sizeof(struct hyb_encoder), GFP_KERNEL);
	if (!encoder_dev)    
	{
		goto err_exit1;
	}
	
	//从设备树dtb获取所需要的参数变量
	err = get_parse_dt(dev, encoder_dev);
	if (!err)
	{
		goto  err_exit1;
	}

	input = input_allocate_device();
	if (!input)
	{
		goto err_exit;
	}
	encoder_dev->inputdev = input;
	input->name = pdev->name;
	input->id.bustype = BUS_HOST;
	input->dev.parent = dev;
	input_set_abs_params(encoder_dev->inputdev, EV_ABS, 0, 2, 0, 1);//绝对值
	err = input_register_device(input);
	if (err) {
		printk( "failed to register input device\n");
		goto err_exit;
	}

	platform_set_drvdata(pdev, encoder_dev);
	
	return 0;
err_exit:
	input_free_device(input);  
err_exit1:
	 kfree(encoder_dev);
	 return -1;
}
static int encoder_remove(struct platform_device *pdev)
{
	struct hyb_encoder *encoder_dev = platform_get_drvdata(pdev);
	free_irq(encoder_dev->irq_numA, encoder_dev);
	free_irq(encoder_dev->irq_numA, encoder_dev);
	gpio_free(encoder_dev->irq_portA);
	gpio_free(encoder_dev->irq_portB);
	input_unregister_device(encoder_dev->inputdev);
	kfree(encoder_dev);
	return 0;
}

从设备树上获取相应的值

int get_parse_dt(struct device *dev, struct hyb_encoder *encoder_dev)
{
        int ret;
	struct device_node *np = dev->of_node;
	if (!np)
	{
		return 0;
	}
	encoder_dev->irq_portA = of_get_named_gpio(np, "irq_A_gpio", 0);
	encoder_dev->irq_portB = of_get_named_gpio(np, "irq_B_gpio", 0);
	printk("encoder_devA B = %d, %d\n",encoder_dev->irq_portA, encoder_dev->irq_portB);
	if (!encoder_dev->irq_portA || !encoder_dev->irq_portB)
	{
		goto err_exitA;
	}
	
	ret = gpio_request(encoder_dev->irq_portA, "IRQ_A");
	if (ret)
	{
		goto err_exitA;
	}
	ret = gpio_request(encoder_dev->irq_portB, "IRQ_B");
	if (ret)
	{
		goto err_exitB;
	}
	//申请中断号
	encoder_dev->irq_numA = gpio_to_irq(encoder_dev->irq_portA);
	encoder_dev->irq_numB = gpio_to_irq(encoder_dev->irq_portB);
	//申请中断函数
	ret = request_irq(encoder_dev->irq_numA, encoder_interrupt_handler, IRQF_SHARED|IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, DEV_NAME, encoder_dev);
	if (ret)    
	{
		printk("irq_numA request irq Failed!\n");
		goto err_exit;
	}
	ret = request_irq(encoder_dev->irq_numB, encoder_interrupt_handler,IRQF_SHARED|IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, DEV_NAME, encoder_dev );

	if (ret)
	{
		printk("irq_numB request irq Failed!\n");
		goto err_exit;
	}
	return 1;

err_exit:
	free_irq(encoder_dev->irq_numA, encoder_dev);
	free_irq(encoder_dev->irq_numB, encoder_dev);
	gpio_free(encoder_dev->irq_portB);
err_exitB:
	gpio_free(encoder_dev->irq_portA);
err_exitA:
	return 0;
}


中断处理函数

static irqreturn_t encoder_interrupt_handler(int irq, void* dev_id)
{
	struct hyb_encoder *encoder_dev = dev_id;
	int valueA = gpio_get_value(encoder_dev->irq_portA);
	int valueB = gpio_get_value(encoder_dev->irq_portB);
	
	if (valueA == 1 && valueB == 1)
	{
		encoder_dev->start_rolate_flag = true;
	}
	else if (valueA == 1 && valueB == 0)
	{
		if (encoder_dev->start_rolate_flag)
		{
			encoder_dev->direction = CLOCKWISE_DIRECTION;
		}
	}
	else if (valueA == 0 && valueB == 1)
	{
		if (encoder_dev->start_rolate_flag)
		{
			encoder_dev->direction = ANTICLOCKWISE_DIRECTION;
		}
	}
	else if (valueA == 0 && valueB == 0)
	{
		if (encoder_dev->start_rolate_flag)
		{
			encoder_dev->start_rolate_flag = false;
			input_report_abs(encoder_dev->inputdev, EV_ABS, encoder_dev->direction);
			input_sync(encoder_dev->inputdev);
		}	
	}
	return IRQ_HANDLED;
}

设备树

创建一个encoder节点

encoder {
		compatible = "hyb_encoder";
		irq_A_gpio = <&gpio3 RK_PA3 GPIO_ACTIVE_LOW>;
		irq_B_gpio = <&gpio3 RK_PA6 GPIO_ACTIVE_LOW>;
		status = "okay";
	};

调试过程

查看是否有输入设备

[root@TWDZ-RK356X:/]# cat /proc/bus/input/
devices   handlers
[root@TWDZ-RK356X:/]# cat /proc/bus/input/devices 
I: Bus=0019 Vendor=0000 Product=0000 Version=0000
N: Name="encoder"
P: Phys=
S: Sysfs=/devices/platform/encoder/input/input0
U: Uniq=
H: Handlers=event0 
B: PROP=0
B: EV=9
B: ABS=8

I: Bus=0019 Vendor=0000 Product=0000 Version=0000
N: Name="rk805 pwrkey"
P: Phys=rk805_pwrkey/input0
S: Sysfs=/devices/platform/fdd40000.i2c/i2c-0/0-0020/rk805-pwrkey/input/input1
U: Uniq=
H: Handlers=kbd event1 cpufreq 
B: PROP=0
B: EV=3
B: KEY=10000000000000 0

这是获取event事件的demo。

[root@hyb-RK356X:/]# ./getevent 
add device 3: /dev/input/event0
  name:     "encoder"
[  132.363605] 01
[  132.366603] 11
[  132.407065] 10
[  132.409108] 00
/dev/input/event0: 0003 0003 00000000
/dev/input/event0: 0000 0000 00000000
[  135.081109] 10
[  135.091760] 11
[  135.594262] 01
[  135.602564] 00
/dev/input/event0: 0003 0003 00000001
/dev/input/event0: 0000 0000 00000000