对比一段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;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键值读取的代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: tensorflow入门(一)波士顿房价
- 下一篇: 如何自己实现一个栈