ADC按键典型电路

一、设备树

1
2
3
4
5
6
7
8
9
10
11
12
13
// vi rk3399pro-firefly-core.dtsi +55
adc-keys {
compatible = "adc-keys";
io-channels = <&saradc 2>; //adc选用的是saradc通道2
io-channel-names = "buttons";
poll-interval = <100>;
keyup-threshold-microvolt = <1800000>; //表示按键抬起,saradc通道2的电压(单位微伏)。
adc-power-key {
linux,code = <KEY_POWER>; //按键上报的键值
label = "power key";
press-threshold-microvolt = <0>; //表示按键按下,saradc通道2的电压。
};
};

二、上报键值

最关键的上报按键事件的代码:adc-keys.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
static void adc_keys_poll(struct input_polled_dev *dev)
{
struct adc_keys_state *st = dev->private;
int i, value, ret;
u32 diff, closest = 0xffffffff;
int keycode = 0;

ret = iio_read_channel_processed(st->channel, &value);//读取adc通道值
if (unlikely(ret < 0)) {
/* Forcibly release key if any was pressed */
// 如果未按下任何键,则强制释放键
value = st->keyup_voltage;
} else {
for (i = 0; i < st->num_keys; i++) {
//st->map[i].voltage = "press-threshold-microvolt" = 设备树设定的按下时adc值
diff = abs(st->map[i].voltage - value);
if (diff < closest) {//找出最接近的键值
closest = diff;
keycode = st->map[i].keycode;
}
}
}
//如果adc的值与开路电压的adc的误差比上面的最小误差都小,那么判定为未按下按键
if (abs(st->keyup_voltage - value) < closest)
keycode = 0;
//如果上一次有键值,并且这次的键值和上次不一样,就说明上一次的按键已经松开
//并且这次检测,又有新的按键被按下,就先发送上次键值的松开事件
if (st->last_key && st->last_key != keycode)
input_report_key(dev->input, st->last_key, 0);
//再发送新的键值的按下事件
if (keycode)
input_report_key(dev->input, keycode, 1);

input_sync(dev->input);
st->last_key = keycode;
}

三、使用实例

①修改设备树:

1
2
3
4
5
adc_demo: adc_demo {
status = "okay";
compatible = "firefly,rk3399-adc";
io-channels = <&saradc 0>;
};

②编写驱动程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <linux/iio/types.h>
#include <linux/input.h>
#include <linux/input-polldev.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/iio/consumer.h>
#include <linux/interrupt.h>
#include <linux/pinctrl/consumer.h>

static struct delayed_work adc_poll_work; //定义一个工作队列
static struct iio_channel *chan; //定义 IIO 通道结构体

static void firefly_demo_adc_poll (struct work_struct *work)
{
int val,ret;
int Vresult;
/*
使用标准电压将 AD 转换的值转换为用户所需要的电压值。其计算公式如下:
Vref / (2^n-1) = Vresult / raw
Vref 为标准电压
n 为 AD 转换的位数
Vresult 为用户所需要的采集电压
raw 为 AD 采集的原始数据
例如,标准电压为 1.8V,AD 采集位数为 10 位,AD 采集到的原始数据为 568,则:
Vresult = (1800mv * 568) / 1023;
*/
ret = iio_read_channel_raw(chan, &val);
Vresult = (1800 * val) / 1023; //Vresult = (1800mv * val) / 1023;
printk("the adc channel voltage is %d mV\n",Vresult);
schedule_delayed_work(&adc_poll_work,msecs_to_jiffies(1000)); //1秒后调用队列
}

static int firefly_adc_probe(struct platform_device *pdev)
{
printk("firefly_adc_probe!\n");
chan = iio_channel_get(&(pdev->dev), NULL);
if (IS_ERR(chan)){
chan = NULL;
printk("%s() have not set adc chan\n", __FUNCTION__);
return -1;
}

if (chan) {
INIT_DELAYED_WORK(&adc_poll_work, firefly_demo_adc_poll);//初始化工作队列
schedule_delayed_work(&adc_poll_work,msecs_to_jiffies(1000));//1秒后调用队列
}
return 0;
}

static int firefly_adc_remove(struct platform_device *pdev)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
iio_channel_release(chan);
cancel_delayed_work_sync(&adc_poll_work);
return 0;
}

static const struct of_device_id firefly_adc_match[] = {
{ .compatible = "firefly,rk3399-adc" },
{},
};

static struct platform_driver firefly_adc_driver = {
.probe = firefly_adc_probe,
.remove = firefly_adc_remove,
.driver = {
.name = "firefly_adc",
.owner = THIS_MODULE,
.of_match_table = firefly_adc_match,
},
};

static int __init firefly_adc_init(void)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return platform_driver_register(&firefly_adc_driver);
}

static void __exit firefly_adc_exit(void)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
platform_driver_unregister(&firefly_adc_driver);
}

module_init(firefly_adc_init);
module_exit(firefly_adc_exit);

MODULE_LICENSE("GPL");