-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
yaml --- r: 143147 b: refs/heads/master c: 73969ff h: refs/heads/master i: 143145: b3f9d3d 143143: c7087cf v: v3
- Loading branch information
Daniel Mack
authored and
Dmitry Torokhov
committed
Mar 8, 2009
1 parent
173f92e
commit 2469724
Showing
6 changed files
with
349 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
--- | ||
refs/heads/master: b0ecc7309443dbcf1a0ce2d93f39f5d92c124d42 | ||
refs/heads/master: 73969ff0eda233f140bcbed1251431387b43f383 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
rotary-encoder - a generic driver for GPIO connected devices | ||
Daniel Mack <daniel@caiaq.de>, Feb 2009 | ||
|
||
0. Function | ||
----------- | ||
|
||
Rotary encoders are devices which are connected to the CPU or other | ||
peripherals with two wires. The outputs are phase-shifted by 90 degrees | ||
and by triggering on falling and rising edges, the turn direction can | ||
be determined. | ||
|
||
The phase diagram of these two outputs look like this: | ||
|
||
_____ _____ _____ | ||
| | | | | | | ||
Channel A ____| |_____| |_____| |____ | ||
|
||
: : : : : : : : : : : : | ||
__ _____ _____ _____ | ||
| | | | | | | | ||
Channel B |_____| |_____| |_____| |__ | ||
|
||
: : : : : : : : : : : : | ||
Event a b c d a b c d a b c d | ||
|
||
|<-------->| | ||
one step | ||
|
||
|
||
For more information, please see | ||
http://en.wikipedia.org/wiki/Rotary_encoder | ||
|
||
|
||
1. Events / state machine | ||
------------------------- | ||
|
||
a) Rising edge on channel A, channel B in low state | ||
This state is used to recognize a clockwise turn | ||
|
||
b) Rising edge on channel B, channel A in high state | ||
When entering this state, the encoder is put into 'armed' state, | ||
meaning that there it has seen half the way of a one-step transition. | ||
|
||
c) Falling edge on channel A, channel B in high state | ||
This state is used to recognize a counter-clockwise turn | ||
|
||
d) Falling edge on channel B, channel A in low state | ||
Parking position. If the encoder enters this state, a full transition | ||
should have happend, unless it flipped back on half the way. The | ||
'armed' state tells us about that. | ||
|
||
2. Platform requirements | ||
------------------------ | ||
|
||
As there is no hardware dependent call in this driver, the platform it is | ||
used with must support gpiolib. Another requirement is that IRQs must be | ||
able to fire on both edges. | ||
|
||
|
||
3. Board integration | ||
-------------------- | ||
|
||
To use this driver in your system, register a platform_device with the | ||
name 'rotary-encoder' and associate the IRQs and some specific platform | ||
data with it. | ||
|
||
struct rotary_encoder_platform_data is declared in | ||
include/linux/rotary-encoder.h and needs to be filled with the number of | ||
steps the encoder has and can carry information about externally inverted | ||
signals (because of used invertig buffer or other reasons). | ||
|
||
Because GPIO to IRQ mapping is platform specific, this information must | ||
be given in seperately to the driver. See the example below. | ||
|
||
---------<snip>--------- | ||
|
||
/* board support file example */ | ||
|
||
#include <linux/input.h> | ||
#include <linux/rotary_encoder.h> | ||
|
||
#define GPIO_ROTARY_A 1 | ||
#define GPIO_ROTARY_B 2 | ||
|
||
static struct rotary_encoder_platform_data my_rotary_encoder_info = { | ||
.steps = 24, | ||
.axis = ABS_X, | ||
.gpio_a = GPIO_ROTARY_A, | ||
.gpio_b = GPIO_ROTARY_B, | ||
.inverted_a = 0, | ||
.inverted_b = 0, | ||
}; | ||
|
||
static struct platform_device rotary_encoder_device = { | ||
.name = "rotary-encoder", | ||
.id = 0, | ||
.dev = { | ||
.platform_data = &my_rotary_encoder_info, | ||
} | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
/* | ||
* rotary_encoder.c | ||
* | ||
* (c) 2009 Daniel Mack <daniel@caiaq.de> | ||
* | ||
* state machine code inspired by code from Tim Ruetz | ||
* | ||
* A generic driver for rotary encoders connected to GPIO lines. | ||
* See file:Documentation/input/rotary_encoder.txt for more information | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 as | ||
* published by the Free Software Foundation. | ||
*/ | ||
|
||
#include <linux/kernel.h> | ||
#include <linux/module.h> | ||
#include <linux/init.h> | ||
#include <linux/interrupt.h> | ||
#include <linux/input.h> | ||
#include <linux/device.h> | ||
#include <linux/platform_device.h> | ||
#include <linux/gpio.h> | ||
#include <linux/rotary_encoder.h> | ||
|
||
#define DRV_NAME "rotary-encoder" | ||
|
||
struct rotary_encoder { | ||
unsigned int irq_a; | ||
unsigned int irq_b; | ||
unsigned int pos; | ||
unsigned int armed; | ||
unsigned int dir; | ||
struct input_dev *input; | ||
struct rotary_encoder_platform_data *pdata; | ||
}; | ||
|
||
static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) | ||
{ | ||
struct rotary_encoder *encoder = dev_id; | ||
struct rotary_encoder_platform_data *pdata = encoder->pdata; | ||
int a = !!gpio_get_value(pdata->gpio_a); | ||
int b = !!gpio_get_value(pdata->gpio_b); | ||
int state; | ||
|
||
a ^= pdata->inverted_a; | ||
b ^= pdata->inverted_b; | ||
state = (a << 1) | b; | ||
|
||
switch (state) { | ||
|
||
case 0x0: | ||
if (!encoder->armed) | ||
break; | ||
|
||
if (encoder->dir) { | ||
/* turning counter-clockwise */ | ||
encoder->pos += pdata->steps; | ||
encoder->pos--; | ||
encoder->pos %= pdata->steps; | ||
} else { | ||
/* turning clockwise */ | ||
encoder->pos++; | ||
encoder->pos %= pdata->steps; | ||
} | ||
|
||
input_report_abs(encoder->input, pdata->axis, encoder->pos); | ||
input_sync(encoder->input); | ||
|
||
encoder->armed = 0; | ||
break; | ||
|
||
case 0x1: | ||
case 0x2: | ||
if (encoder->armed) | ||
encoder->dir = state - 1; | ||
break; | ||
|
||
case 0x3: | ||
encoder->armed = 1; | ||
break; | ||
} | ||
|
||
return IRQ_HANDLED; | ||
} | ||
|
||
static int __devinit rotary_encoder_probe(struct platform_device *pdev) | ||
{ | ||
struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data; | ||
struct rotary_encoder *encoder; | ||
struct input_dev *input; | ||
int err; | ||
|
||
if (!pdata || !pdata->steps) { | ||
dev_err(&pdev->dev, "invalid platform data\n"); | ||
return -ENOENT; | ||
} | ||
|
||
encoder = kzalloc(sizeof(struct rotary_encoder), GFP_KERNEL); | ||
input = input_allocate_device(); | ||
if (!encoder || !input) { | ||
dev_err(&pdev->dev, "failed to allocate memory for device\n"); | ||
err = -ENOMEM; | ||
goto exit_free_mem; | ||
} | ||
|
||
encoder->input = input; | ||
encoder->pdata = pdata; | ||
encoder->irq_a = gpio_to_irq(pdata->gpio_a); | ||
encoder->irq_b = gpio_to_irq(pdata->gpio_b); | ||
|
||
/* create and register the input driver */ | ||
input->name = pdev->name; | ||
input->id.bustype = BUS_HOST; | ||
input->dev.parent = &pdev->dev; | ||
input->evbit[0] = BIT_MASK(EV_ABS); | ||
input_set_abs_params(encoder->input, | ||
pdata->axis, 0, pdata->steps, 0, 1); | ||
|
||
err = input_register_device(input); | ||
if (err) { | ||
dev_err(&pdev->dev, "failed to register input device\n"); | ||
goto exit_free_mem; | ||
} | ||
|
||
/* request the GPIOs */ | ||
err = gpio_request(pdata->gpio_a, DRV_NAME); | ||
if (err) { | ||
dev_err(&pdev->dev, "unable to request GPIO %d\n", | ||
pdata->gpio_a); | ||
goto exit_unregister_input; | ||
} | ||
|
||
err = gpio_request(pdata->gpio_b, DRV_NAME); | ||
if (err) { | ||
dev_err(&pdev->dev, "unable to request GPIO %d\n", | ||
pdata->gpio_b); | ||
goto exit_free_gpio_a; | ||
} | ||
|
||
/* request the IRQs */ | ||
err = request_irq(encoder->irq_a, &rotary_encoder_irq, | ||
IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE, | ||
DRV_NAME, encoder); | ||
if (err) { | ||
dev_err(&pdev->dev, "unable to request IRQ %d\n", | ||
encoder->irq_a); | ||
goto exit_free_gpio_b; | ||
} | ||
|
||
err = request_irq(encoder->irq_b, &rotary_encoder_irq, | ||
IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE, | ||
DRV_NAME, encoder); | ||
if (err) { | ||
dev_err(&pdev->dev, "unable to request IRQ %d\n", | ||
encoder->irq_b); | ||
goto exit_free_irq_a; | ||
} | ||
|
||
platform_set_drvdata(pdev, encoder); | ||
|
||
return 0; | ||
|
||
exit_free_irq_a: | ||
free_irq(encoder->irq_a, encoder); | ||
exit_free_gpio_b: | ||
gpio_free(pdata->gpio_b); | ||
exit_free_gpio_a: | ||
gpio_free(pdata->gpio_a); | ||
exit_unregister_input: | ||
input_unregister_device(input); | ||
input = NULL; /* so we don't try to free it */ | ||
exit_free_mem: | ||
input_free_device(input); | ||
kfree(encoder); | ||
return err; | ||
} | ||
|
||
static int __devexit rotary_encoder_remove(struct platform_device *pdev) | ||
{ | ||
struct rotary_encoder *encoder = platform_get_drvdata(pdev); | ||
struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data; | ||
|
||
free_irq(encoder->irq_a, encoder); | ||
free_irq(encoder->irq_b, encoder); | ||
gpio_free(pdata->gpio_a); | ||
gpio_free(pdata->gpio_b); | ||
input_unregister_device(encoder->input); | ||
platform_set_drvdata(pdev, NULL); | ||
kfree(encoder); | ||
|
||
return 0; | ||
} | ||
|
||
static struct platform_driver rotary_encoder_driver = { | ||
.probe = rotary_encoder_probe, | ||
.remove = __devexit_p(rotary_encoder_remove), | ||
.driver = { | ||
.name = DRV_NAME, | ||
.owner = THIS_MODULE, | ||
} | ||
}; | ||
|
||
static int __init rotary_encoder_init(void) | ||
{ | ||
return platform_driver_register(&rotary_encoder_driver); | ||
} | ||
|
||
static void __exit rotary_encoder_exit(void) | ||
{ | ||
platform_driver_unregister(&rotary_encoder_driver); | ||
} | ||
|
||
module_init(rotary_encoder_init); | ||
module_exit(rotary_encoder_exit); | ||
|
||
MODULE_ALIAS("platform:" DRV_NAME); | ||
MODULE_DESCRIPTION("GPIO rotary encoder driver"); | ||
MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); | ||
MODULE_LICENSE("GPL v2"); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
#ifndef __ROTARY_ENCODER_H__ | ||
#define __ROTARY_ENCODER_H__ | ||
|
||
struct rotary_encoder_platform_data { | ||
unsigned int steps; | ||
unsigned int axis; | ||
unsigned int gpio_a; | ||
unsigned int gpio_b; | ||
unsigned int inverted_a; | ||
unsigned int inverted_b; | ||
}; | ||
|
||
#endif /* __ROTARY_ENCODER_H__ */ |