最近接触到的一个代码,这个代码看起来很简单,但是却蕴藏了人类的智慧与结晶。正是这些不断产生的智慧与结晶,让我们的电子产品越来越稳定,越来越智能。

周五了,评论文章,选两个同学赠送书籍《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;elsebutton->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 <linux/err.h>
#include <linux/iio/consumer.h>
#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>
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_OF
static const struct of_device_id adc_keys_of_match[] = {{ .compatible = "adc-keys", },{ }
};
MODULE_DEVICE_TABLE(of, adc_keys_of_match);
#endif
static 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 <alexandre.belloni@free-electrons.com>");
MODULE_DESCRIPTION("Input driver for resistor ladder connected on ADC");
MODULE_LICENSE("GPL v2");

推荐阅读:

Linux内核0.12完全注释

回复「 篮球的大肚子」进入技术群聊

回复「1024」获取1000G学习资料

对比一段ADC键值读取的代码相关推荐

  1. JS对比两个对象键值全等

    比较两个对象的键值是否全等,说的就是字面上的相等,也就是看起来的一模一样的,举个栗子 let o1 = { let o2 = {name: 'joe', name: 'joe' isOld: fals ...

  2. 基于android7.1+msm8937读取ADC采样值

    基于android7.1+msm8937读取ADC采样值 读取给设备供电的电压,比如16V,通过监控ADC(Analog-to-Digital Conversion)值来知道电源波动情况和实际给设备供 ...

  3. 【Redis】Redis 哈希 Hash 键值对集合操作 ( 哈希 Hash 键值对集合简介 | 查询操作 | 增加操作 | 修改操作 )

    文章目录 一.哈希 Hash 键值对集合 二.查询操作 1.Redis 中查询 Hash 键值对数据 2.查询 Hash 键是否存在 3.查询 Hash 中所有的键 Field 4.查询 Hash 中 ...

  4. 如何获取自己键盘上按键的键值(KeyCode)

    文章目录 通过C语言获取部分按键键值 获取字母按键键值 获取方向键键值 键码值汇总 字母和数字的键值 数字键盘上按键的键值 功能键的键值 控制键键值 多媒体按键键值 通过C语言获取部分按键键值 这里提 ...

  5. 键值 keyCode事件属性

    keyCode属性返回onkeypress事件触发的键值的字符代码,或onkeydown/onkeyup的键的代码. 两种代码类型的区别是: 字符代码 - 表示 ASCII 字符的数字 键盘代码 - ...

  6. C# 键值对 KeyValue 解析

    最近看到一个输入字符串或者字节数组解析成键值对的代码,可能对大家有用,简单的写了一下. 当然,你可以用JSON.NET去处理JSON类型的键值对,网上很多资料,就不多说,这里主要说是类似于自定数据格式 ...

  7. hash表--c语言 字符串键值配对——(key, value)

    c语言键值配对--(key, value) 看一个C++项目时,其中解析配置文的部分引发了我的思考. 配置文件问普通字符文件,内容都是类似 如下: ipaddr=127.0.0.1 port=888 ...

  8. php获取两个输入框的值,PHP获取多个文本框中值的实例代码

    假如有这样的需要:获取每个版块指定的文章数. 一,解决方法一,用两个循环,可能会产生大量的重复数据. for($i...){ for($j...){ ... } } 例子: 复制代码 代码示例: 代码 ...

  9. hashset java 键值对_Java中的各个容器的性能对比

    java中个个容器的属性,性能,参数对比: Java容器的性能及属性的对比 List:Vector,ArrayList,LinkedList Vector:内部是数组数据结构,可以理解为加锁的Arra ...

最新文章

  1. zookeeper 客户端_zookeeper进阶-客户端源码详解
  2. 我来了,新鲜活人报道。
  3. Linux内核和应用层程序通信get/setsockopt示例
  4. java 调用存储过程
  5. PMCAFF|产品经理必须懂得的五大心理学分支
  6. SpringBoot中怎样基于slf4j封装日志类输出日志
  7. 【LeetCode 55】【LeetCode 45】 跳跃游戏
  8. 联想服务器做完raid找不到硬盘,联想服务器RAID配置步骤
  9. IOS-C语言第8天,Struct (结构体)
  10. 自定义dialog弹窗html,自定义H5页面dialog弹窗
  11. TensorFlow 分布式
  12. ASP.NET Form Authentication安全漏洞及对策
  13. man ifconfig时提示:-bash: man: command not found
  14. 多格式无水印录屏软件
  15. 2019开放大学计算机应用基础,国家开放大学2019年电大计算机应用基础考试试题一试卷(国家开放大学).doc...
  16. 蓝桥杯备考-python刷题之路-动态规划算法(DP算法)Part3【最终代码实现
  17. 中易云嵌入式网关丨性能卓越+性价比高+应用场景丰富
  18. sec^3 不定积分
  19. uoni扫地机器人好用吗_扫地机器人好用吗?扫盲选购看这篇
  20. 库编译:opencv 交叉编译静态库

热门文章

  1. Underscore.js (1.7.0)-函数预览
  2. JSON数组分配输出每个li
  3. 演示:两台交换机成环后的STP计算原则
  4. Crusher Django 学习笔记4 使用Model
  5. [JavaScript]return false;和e.preventDefault();的区别
  6. Android应用开发——文件目录
  7. 39--打印从1到最大的n位数
  8. python基本语句及其意思_Python语法基础(1),一
  9. c语言贪吃蛇最简单代码_C语言指针,这可能是史上最干最全的讲解啦(附代码)!!!...
  10. Python 内置模块之 random