设备树:
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
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;
}
如果我们自己要用adc:
设备树:
adc_demo: adc_demo {
status = "okay";
compatible = "firefly,rk3399-adc";
io-channels = <&saradc 0>;
};
驱动代码:
......
#include <linux/iio/types.h>
#include <linux/iio/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); //获取adc通道的原始数据
//Vresult = (1800 * val) / 1023;
Vresult = (1800 * 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)
{
chan = iio_channel_get(&(pdev->dev), NULL); //获取要使用的adc通道
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)
{
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)
{
return platform_driver_register(&firefly_adc_driver);
}
static void __exit firefly_adc_exit(void)
{
platform_driver_unregister(&firefly_adc_driver);
}
module_init(firefly_adc_init);
module_exit(firefly_adc_exit);
MODULE_LICENSE("GPL");