对比一段ADC键值读取的代码
发布日期:2021-06-30 18:44:45
浏览次数:2
分类:技术文章
本文共 7847 字,大约阅读时间需要 26 分钟。
最近接触到的一个代码,这个代码看起来很简单,但是却蕴藏了人类的智慧与结晶。正是这些不断产生的智慧与结晶,让我们的电子产品越来越稳定,越来越智能。
周五了,评论文章,选两个同学赠送书籍《Linux内核完全剖析》基于0.12。
#功能
通过ADC值的不同来判断是哪个按键按下,这种方案应该是很常见了,而且这种方案可以节省GPIO口。实现起来的难度也不是特别大。
#硬件连接
#原来的旧代码
static void adc_key_poll(struct work_struct *work){ struct rk_keys_drvdata *ddata; int i, result = -1; ddata = container_of(work, struct rk_keys_drvdata, adc_poll_work.work); if (!ddata->in_suspend) { result = rk_key_adc_iio_read(ddata);/**读取SARADC值*/ if (result > INVALID_ADVALUE && ┊ result < (EMPTY_DEFAULT_ADVALUE - ddata->drift_advalue)) ddata->result = result; for (i = 0; i < ddata->nbuttons; i++) { struct rk_keys_button *button = &ddata->button[i]; if (!button->adc_value) continue; if (result < button->adc_value + ddata->drift_advalue && ┊ result > button->adc_value - ddata->drift_advalue) button->adc_state = 1; else button->adc_state = 0; if (button->state != button->adc_state) mod_timer(&button->timer, ┊ jiffies + DEBOUNCE_JIFFIES); } } schedule_delayed_work(&ddata->adc_poll_work, ADC_SAMPLE_JIFFIES);}
#升级代码
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); if (unlikely(ret < 0)) { /* Forcibly release key if any was pressed */ value = st->keyup_voltage; } else { /*先把电压值与所有按键列表的值比较,找出最接近的一个,以及找出最接近的差值。*/ for (i = 0; i < st->num_keys; i++) { diff = abs(st->map[i].voltage - value); if (diff < closest) { closest = diff; keycode = st->map[i].keycode; } } } //printk("adc_keys_poll value:%d keycode:%d\n",value,keycode); /* 然后把如果发现当前电压值与按键弹起的电压值很接近,(标准是小于上面找出的最小差值),那么认为没有按键按下,所以设置keycode为0,不上报。 */ 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); printk("adc_key_poll keycode:%d\n",keycode); } /*他这么做有个好处,就是只要硬件设计合理的情况下,电阻不同批料有误差的情况下,也不至于需要驱动工程师去修改电压值或者误差范围才能识别按键。*/ /*这个做法肯定是在量产出现了很多问题后作出的改善。*/ input_sync(dev->input); st->last_key = keycode;}
完整的驱动代码可以看下面这段,我觉得这小段代码的意义非常大。很有意思,通过一个for循环找到一个最接近的数值,判断为按下的这个键值。而且如果按下和 抬起来的数值接近,就判断为没有按键按下。
这就不需要再去考虑一个问题,那就误差范围的问题了。之前的那套旧代码使用的是误差范围,如果ADC值设定为 2000 ,误差范围设定为100,那么读取的ADC值在1900~2100范围内都可以认为是这个按键按下的。
但是存在一个情况,就是不同批次,不同物料的硬件,误差总是不能令人满意,软件需要不断的调整这个误差范围。在不断的调整过程中,发现调整误差范围已经不能解决当下的问题了,所以就产生了新的算法。而我们现在看到的这个新的算法就这样应运而生了。当然,实际情况可能还要复杂化,可能需要加上一些滤波算法先过滤等等。
总之,各位可以保存下来这段代码,在需要的时候拿来使用。展现自己的码农魅力。
#完整升级代码
/* * Input driver for resistor ladder connected on ADC * * Copyright (c) 2016 Alexandre Belloni * * 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#include #include #include #include #include #include #include #include #include #include struct adc_keys_button { u32 voltage; u32 keycode;};struct adc_keys_state { struct iio_channel *channel; u32 num_keys; u32 last_key; u32 keyup_voltage; const struct adc_keys_button *map;};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); if (unlikely(ret < 0)) { /* Forcibly release key if any was pressed */ value = st->keyup_voltage; } else { /*先把电压值与所有按键列表的值比较,找出最接近的一个,以及找出最接近的差值。*/ for (i = 0; i < st->num_keys; i++) { diff = abs(st->map[i].voltage - value); if (diff < closest) { closest = diff; keycode = st->map[i].keycode; } } } //printk("adc_keys_poll value:%d keycode:%d\n",value,keycode); /* 然后把如果发现当前电压值与按键弹起的电压值很接近,(标准是小于上面找出的最小差值),那么认为没有按键按下,所以设置keycode为0,不上报。 */ 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); printk("adc_key_poll keycode:%d\n",keycode); } /*他这么做有个好处,就是只要硬件设计合理的情况下,电阻不同批料有误差的情况下,也不至于需要驱动工程师去修改电压值或者误差范围才能识别按键。*/ /*这个做法肯定是在量产出现了很多问题后作出的改善。*/ input_sync(dev->input); st->last_key = keycode;}static int adc_keys_load_keymap(struct device *dev, struct adc_keys_state *st){ struct adc_keys_button *map; struct fwnode_handle *child; int i; st->num_keys = device_get_child_node_count(dev); if (st->num_keys == 0) { dev_err(dev, "keymap is missing\n"); return -EINVAL; } map = devm_kmalloc_array(dev, st->num_keys, sizeof(*map), GFP_KERNEL); if (!map) return -ENOMEM; i = 0; device_for_each_child_node(dev, child) { if (fwnode_property_read_u32(child, "press-threshold-microvolt", &map[i].voltage)) { dev_err(dev, "Key with invalid or missing voltage\n"); fwnode_handle_put(child); return -EINVAL; } map[i].voltage /= 1000; if (fwnode_property_read_u32(child, "linux,code", &map[i].keycode)) { dev_err(dev, "Key with invalid or missing linux,code\n"); fwnode_handle_put(child); return -EINVAL; } i++; } st->map = map; return 0;}static int adc_keys_probe(struct platform_device *pdev){ struct device *dev = &pdev->dev; struct adc_keys_state *st; struct input_polled_dev *poll_dev; struct input_dev *input; enum iio_chan_type type; int i, value; int error; st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); if (!st) return -ENOMEM; st->channel = devm_iio_channel_get(dev, "buttons"); if (IS_ERR(st->channel)) return PTR_ERR(st->channel); if (!st->channel->indio_dev) return -ENXIO; error = iio_get_channel_type(st->channel, &type); if (error < 0) return error; if (type != IIO_VOLTAGE) { dev_err(dev, "Incompatible channel type %d\n", type); return -EINVAL; } if (device_property_read_u32(dev, "keyup-threshold-microvolt", &st->keyup_voltage)) { dev_err(dev, "Invalid or missing keyup voltage\n"); return -EINVAL; } st->keyup_voltage /= 1000; error = adc_keys_load_keymap(dev, st); if (error) return error; platform_set_drvdata(pdev, st); poll_dev = devm_input_allocate_polled_device(dev); if (!poll_dev) { dev_err(dev, "failed to allocate input device\n"); return -ENOMEM; } if (!device_property_read_u32(dev, "poll-interval", &value)) poll_dev->poll_interval = value; poll_dev->poll = adc_keys_poll; poll_dev->private = st; input = poll_dev->input; input->name = pdev->name; input->phys = "adc-keys/input0"; input->id.bustype = BUS_HOST; input->id.vendor = 0x0001; input->id.product = 0x0001; input->id.version = 0x0100; __set_bit(EV_KEY, input->evbit); for (i = 0; i < st->num_keys; i++) __set_bit(st->map[i].keycode, input->keybit); if (device_property_read_bool(dev, "autorepeat")) __set_bit(EV_REP, input->evbit); error = input_register_polled_device(poll_dev); if (error) { dev_err(dev, "Unable to register input device: %d\n", error); return error; } return 0;}#ifdef CONFIG_OFstatic const struct of_device_id adc_keys_of_match[] = { { .compatible = "adc-keys", }, { }};MODULE_DEVICE_TABLE(of, adc_keys_of_match);#endifstatic struct platform_driver __refdata adc_keys_driver = { .driver = { .name = "adc_keys", .of_match_table = of_match_ptr(adc_keys_of_match), }, .probe = adc_keys_probe,};module_platform_driver(adc_keys_driver);MODULE_AUTHOR("Alexandre Belloni ");MODULE_DESCRIPTION("Input driver for resistor ladder connected on ADC");MODULE_LICENSE("GPL v2");
推荐阅读:
回复「 篮球的大肚子」进入技术群聊
回复「1024」获取1000G学习资料
转载地址:https://linus.blog.csdn.net/article/details/105153977 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
关注你微信了!
[***.104.42.241]2024年04月06日 06时47分49秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
配置nginx只允许域名访问,禁止ip访问【图文教程】
2019-04-30
Java代理【图文教程】_第1章_静态代理
2019-04-30
Java代理【图文教程】_第2章_jdk动态代理
2019-04-30
AOP面向切面编程【图文教程】_第1章
2019-04-30
AOP面向切面编程【图文教程】_第2章
2019-04-30
二叉树之前序、中序、后序和层次遍历【图文教程】
2019-04-30
java类的构成
2019-04-30
创建安装linux:centOS
2019-04-30
Xshell连接CentOS及安装hadoop的准备
2019-04-30
在linux上配置jdk和hadoop
2019-04-30
HDFS配置及常见命令
2019-04-30
xshell连接linux速度很慢或者连接一段时间后会自动断
2019-04-30
Hadoop Windows插件配置
2019-04-30
存储 HDFS内部运行原理
2019-04-30
二丶存储+分析处理信息MapReduce内部原理
2019-04-30
static代码块设置全局变量和eclipse java配好HDFS类对HDFS的操作
2019-04-30
互联网行业为何缺少web前端工程师?
2019-04-30
零基础学UI设计,海报设计需思考这些点!
2019-04-30