糖尿病康复,内容丰富有趣,生活中的好帮手!
糖尿病康复 > 对比一段ADC键值读取的代码

对比一段ADC键值读取的代码

时间:2020-12-01 21:48:06

相关推荐

对比一段ADC键值读取的代码

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

周五了,评论文章,选两个同学赠送书籍《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);

}

#升级代码

staticvoidadc_keys_poll(structinput_polled_dev*dev)

{

structadc_keys_state*st=dev->private;

inti,value,ret;

u32diff,closest=0xffffffff;

intkeycode=0;

ret=iio_read_channel_processed(st->channel,&value);

if(unlikely(ret<0)){

/*Forciblyreleasekeyifanywaspressed*/

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_pollvalue:%dkeycode:%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_pollkeycode:%d\n",keycode);

}

/*他这么做有个好处,就是只要硬件设计合理的情况下,电阻不同批料有误差的情况下,也不至于需要驱动工程师去修改电压值或者误差范围才能识别按键。*/

/*这个做法肯定是在量产出现了很多问题后作出的改善。*/

input_sync(dev->input);

st->last_key=keycode;

}

完整的驱动代码可以看下面这段,我觉得这小段代码的意义非常大。很有意思,通过一个for循环找到一个最接近的数值,判断为按下的这个键值。而且如果按下和 抬起来的数值接近,就判断为没有按键按下。

这就不需要再去考虑一个问题,那就误差范围的问题了。之前的那套旧代码使用的是误差范围,如果ADC值设定为 2000 ,误差范围设定为100,那么读取的ADC值在1900~2100范围内都可以认为是这个按键按下的。

但是存在一个情况,就是不同批次,不同物料的硬件,误差总是不能令人满意,软件需要不断的调整这个误差范围。在不断的调整过程中,发现调整误差范围已经不能解决当下的问题了,所以就产生了新的算法。而我们现在看到的这个新的算法就这样应运而生了。当然,实际情况可能还要复杂化,可能需要加上一些滤波算法先过滤等等。

总之,各位可以保存下来这段代码,在需要的时候拿来使用。展现自己的码农魅力。

#完整升级代码

/*

*InputdriverforresistorladderconnectedonADC

*

*Copyright(c)AlexandreBelloni

*

*Thisprogramisfreesoftware;youcanredistributeitand/ormodifyit

*underthetermsoftheGNUGeneralPublicLicenseversion2aspublishedby

*theFreeSoftwareFoundation.

*/

#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>

structadc_keys_button{

u32voltage;

u32keycode;

};

structadc_keys_state{

structiio_channel*channel;

u32num_keys;

u32last_key;

u32keyup_voltage;

conststructadc_keys_button*map;

};

staticvoidadc_keys_poll(structinput_polled_dev*dev)

{

structadc_keys_state*st=dev->private;

inti,value,ret;

u32diff,closest=0xffffffff;

intkeycode=0;

ret=iio_read_channel_processed(st->channel,&value);

if(unlikely(ret<0)){

/*Forciblyreleasekeyifanywaspressed*/

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_pollvalue:%dkeycode:%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_pollkeycode:%d\n",keycode);

}

/*他这么做有个好处,就是只要硬件设计合理的情况下,电阻不同批料有误差的情况下,也不至于需要驱动工程师去修改电压值或者误差范围才能识别按键。*/

/*这个做法肯定是在量产出现了很多问题后作出的改善。*/

input_sync(dev->input);

st->last_key=keycode;

}

staticintadc_keys_load_keymap(structdevice*dev,structadc_keys_state*st)

{

structadc_keys_button*map;

structfwnode_handle*child;

inti;

st->num_keys=device_get_child_node_count(dev);

if(st->num_keys==0){

dev_err(dev,"keymapismissing\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,"Keywithinvalidormissingvoltage\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,"Keywithinvalidormissinglinux,code\n");

fwnode_handle_put(child);

return-EINVAL;

}

i++;

}

st->map=map;

return0;

}

staticintadc_keys_probe(structplatform_device*pdev)

{

structdevice*dev=&pdev->dev;

structadc_keys_state*st;

structinput_polled_dev*poll_dev;

structinput_dev*input;

enumiio_chan_typetype;

inti,value;

interror;

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))

returnPTR_ERR(st->channel);

if(!st->channel->indio_dev)

return-ENXIO;

error=iio_get_channel_type(st->channel,&type);

if(error<0)

returnerror;

if(type!=IIO_VOLTAGE){

dev_err(dev,"Incompatiblechanneltype%d\n",type);

return-EINVAL;

}

if(device_property_read_u32(dev,"keyup-threshold-microvolt",

&st->keyup_voltage)){

dev_err(dev,"Invalidormissingkeyupvoltage\n");

return-EINVAL;

}

st->keyup_voltage/=1000;

error=adc_keys_load_keymap(dev,st);

if(error)

returnerror;

platform_set_drvdata(pdev,st);

poll_dev=devm_input_allocate_polled_device(dev);

if(!poll_dev){

dev_err(dev,"failedtoallocateinputdevice\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,"Unabletoregisterinputdevice:%d\n",error);

returnerror;

}

return0;

}

#ifdefCONFIG_OF

staticconststructof_device_idadc_keys_of_match[]={

{.compatible="adc-keys",},

{}

};

MODULE_DEVICE_TABLE(of,adc_keys_of_match);

#endif

staticstructplatform_driver__refdataadc_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("AlexandreBelloni<alexandre.belloni@free->");

MODULE_DESCRIPTION("InputdriverforresistorladderconnectedonADC");

MODULE_LICENSE("GPLv2");

如果觉得《对比一段ADC键值读取的代码》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。