对比一段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 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:学习,一定是脱“贫”致富的捷径
下一篇:RK 利用SARADC 来做多个按键

发表评论

最新留言

关注你微信了!
[***.104.42.241]2024年04月06日 06时47分49秒