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