国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

tp 雙擊喚醒 最終方案 實現流程

2019-11-09 15:55:41
字體:
來源:轉載
供稿:網友
折騰許久,終于調試出一個比較滿意的版本,采用的許多方法跟上一篇不一樣,在這里重新整理。 注冊和上報鍵值這里就不在累贅了,本篇博客主要要解決以下bug: 1.系統進入睡眠狀態后,如何通過tp喚醒系統。 2.如何解決快速雙擊時喚醒系統,長按時不喚醒系統。要喚醒進入睡眠狀態的系統,首先要了解以下函數接口:在了解函數接口前,講一下suspend和resume,Suspend:kernel會依次調用你注冊驅動里的suspend函數,將各種外設都進入節電模式。最后CPU進入power down 模式。Resume:當用RTC或者GPIO中的一個將cpu從power down 模式喚醒。依次也會調用各個驅動里的resume函數將外設喚醒,進入正常工作狀態。這些函數核心功能體現在它為底層的設備驅動提供用于上報wakeup event的接口。首先要定義一個可以在睡眠狀態下喚醒cpu的gpio,用enable_irq_wake(irq);和disable_irq_wake(ts->irq);disable_irq_wake(ts->irq);//禁止中斷喚醒功能enable_irq_wake(ts->irq);//使能中斷喚醒功能在進入suspend時,會將irq mask寫入到cpu。也就是告訴cpu哪些irq可以將其從睡眠中喚醒。device_init_wakeup(&client->dev, 1);用來設置一個驅動可以被喚醒,函數中的can_wakeup為1時,表明一個設備可以被喚醒, 在設備初始化的時候調用。//設置dev的can_wakeup標志,若是enable==1,同時調用device_wakeup_enable使能wakeup功能;int device_init_wakeup(struct device *dev, bool enable){... if (enable) { device_set_wakeup_capable(dev, true);//設置設備能不能被喚醒 ret = device_wakeup_enable(dev);//設置設備使不使用喚醒 } ...}// 設備模型中的所有設備都有兩個標志來控制喚醒事件(可使得設備或系統退出低功耗狀態)static inline void device_set_wakeup_capable(struct device *dev, bool capable){ dev->power.can_wakeup = capable;}static inline int device_set_wakeup_enable(struct device *dev, bool enable){ dev->power.should_wakeup = enable; return 0;}can_wakeup為1時表明一個設備可以被喚醒,設備驅動為了支持linux中的電源管理,有責任調用device_init_wakeup()來初始化can_wakeup。而should_wakeup則是在設備的電源狀態發生變化時被device_may_wakeup()用來測試,測試它該不該變化。pm_stay_awake(&ts->client->dev);中斷中,pm_stay_awake可以使的設備不立即進入休眠,這個響應是非??斓?。當設備有wakeup event正在處理時,需要調用該接口通知電源管理系統。那處理完成后呢?driver要調用pm_relax通知電源管理系統。pm_relax釋放這個“鎖”,讓系統可以重新休眠。pm_relax和pm_stay_awake成對出現,用于在event處理結束后通知電源管理系統。以下提供一個例子://想要具備喚醒系統的功能,就要從中斷上下功夫,如下:#include <xxx.h>...#include <linux/platform_device.h>#include <linux/pm_wakeup.h>struct device * dev;int xxx_isr(int irq, void *dev_id){pm_stay_awake(dev);....pm_relax(dev); return IRQ_HANDLED;}int xxx_PRobe(struct platform_device *pdev){int ret;int irq;int flag;dev = pdev->dev;...ret = request_irq(irq, xxx_isr, flag | IRQF_NO_SUSPEND, xxx, xxx);...enable_irq_wake(irq);device_init_wakeup(dev, true);...}int __init xxx_init(void){return platform_driver_register(&xxx_device_driver);}module_initcall(xxx_init);MODULE_LICENSE("GPL");這段代碼中對中斷做了兩個特殊的處理,一個是在申請中斷時加上了IRQF_NO_SUSPEND, 另一個是irq_enable_wake(irq); 這兩個函數都可以賦予IRQ喚醒系統的能力,前者是在suspend過程中dpm_suspend_noirq()->suspend_devices_irq()時保留IRQF_NO_SUSPEND類型的中斷響應,而后者直接跟irq_chip打交道,把喚醒功能的設置交由irq_chip driver處理。從使用角度我覺得,irq_enable_wake()會是一個更為保險且靈活的方法,畢竟更為直接而且禁用喚醒功能方便,disable_irq_wake()即可。需要在源代碼中添加如下:(改完之后,休眠狀態下測得的電流只是比正常休眠狀態下的電流多了tp的電流)//suspend中添加static int gsl_ts_suspend(struct device *dev){... gsl_halt_flag = 1; //進入掛起狀態 flag = 1; //flag==1時可以進入喚醒tp的代碼,接下的 enable_irq_wake(ts->irq); //打開此中斷,并設置為休眠可喚醒 pm_stay_awake(&ts->client->dev); //設置系統為可喚醒狀態...}//resume中添加static int gsl_ts_resume(struct device *dev){... enable_irq(ts->irq); //普通打開中斷,喚醒后,tp進入正常工作狀態 gsl_halt_flag = 0; //設置喚醒狀態 pm_relax(&ts->client->dev); //通知電源管理系統可以進入休眠狀態 flag = 0; //flag==0時不必進入喚醒tp的代碼,接下的 level = 0; //最終喚醒tp時,要有三步,即level==3...}//在中斷上半部關中斷static irqreturn_t gsl_ts_irq(int irq, void *dev_id){... disable_irq_nosync(ts->irq);...}//中斷執行完畢會打開中斷

如果有中斷關閉就要在程序執行完畢后打開中斷,如果邏輯出錯,會報如下錯誤: WARNING:at /sda1/yzhao-work-1/QC706EU-S/msm8916_1605_444/kernel/kernel/irq/manage.c:529 irq_set_irq_wake+0x88/0xe8() [ 8518.783716] —[ end trace 35ae10e4ba3033e2 ]— 一般是申請和釋放資源不匹配。

下面解決第二個bug,快速雙擊喚醒,長按不喚醒,這個可以有不同的邏輯實現,以下是我的邏輯,不正確的地方希望指出: 首先要熟悉上報坐標的流程,這里截取有用的一段進行分析,中斷的下半部函數為,gsl_ts_xy_worker,其中有process_gslX680_data處理坐標點,和上報坐標點。 gsl屏幕支持多點觸控,一次按下時把多個點的坐標值經過record_point計算成為一個點,后用report_data上報。函數中,有三個重要的變量,之前一直忽視了,卻起到了區分快速雙擊和長按的作用:cinfo.finger_num----->記錄按下的手指數id_state_old_flag[i]----->記錄上一次按下的狀態,是數組,按下為1,抬起為0id_state_flag[i]----->記錄此次按下的狀態,是數組,沒按為0,按下為1下面是這個函數的大致代碼,和加入的代碼,之后再分析區分思路static void process_gslX680_data(struct gsl_ts *ts){ int result = 0; //加入代碼,用于計算時間間隔,大于80個時鐘周期,兩次觸摸時間過長,level=0... record_point(x, y , id); //計算x和y report_data(ts, x_new, y_new, 200, id); //上報... //打印調試 pr_err("xhlin^le=%d cinfo.finger_num=%d, id_state_old_flag[i]=%d, id_state_flag[i]=%d,id_sign[i]=%d; /n",level,cinfo.finger_num,id_state_old_flag[1],id_state_flag[1],id_sign[1]);... if(flag == 1) //flag==1 進入休眠,準備喚醒 { result = jiffies - old; //計算兩次按下時間間隔 if(result > 80) //判斷時間間隔,大于80,說明兩次按下時間間隔長,level為0 level = 0; switch(level) { case 0: //第一階段 if((cinfo.finger_num == 1) && (id_sign[1] <= 8 )) { level = 1; //cinfo.finger_num == 1說明有一個手指頭按下,但是可能沒有抬起 old = jiffies; //記錄下第一次按下的時間 } break; case 1: //第二階段 if((cinfo.finger_num == 0) && (id_sign[1] ==0 )) level = 2; //在level為1的基礎上,cinfo.finger_num == 0說明有手指頭抬起,但是可能沒有按下第二次 break; case 2://第三階段 if((cinfo.finger_num == 1) && (id_sign[1] <= 8 )) level = 3; //在level為2的基礎上,cinfo.finger_num再次為1,說明有手指頭又按下,完成一次雙擊觸摸 break; } if(level == 3) //level為3時候,說明完成一次雙擊觸摸,可以喚醒系統 { level = 0; open_lcd(ts); //喚醒系統 } }...}//喚醒系統void open_lcd(struct gsl_ts *ts){ gsl_halt_flag = 0; flag = 0; old = 0; input_report_key(ts->input,KEY_POWER, 1); //power鍵按下 input_sync(ts->input); input_report_key(ts->input,KEY_POWER, 0); //power鍵抬起 input_sync(ts->input); }//一些全局變量static unsigned long old = 0;extern unsigned long volatile jiffies;static int level = 0;static int gsl_halt_flag;static int flag;

思路: 調試的時候,根據log:pr_err(“xhlin^le=%d cinfo.finger_num=%d, id_state_old_flag[1]=%d, id_state_flag[1]=%d,id_sign[1]=%d; /n”,level,cinfo.finger_num,id_state_old_flag[1],id_state_flag[1],id_sign[1]); 發現快速雙擊的時候,cinfo.finger_num會由1,變為0,再變為1 長按的時候,cinfo.finger_num一直為1,且id_sign[1]的值不間斷變大 快速雙擊log 快速雙擊log

長按log 長按log 所以以此區別,來實現雙擊喚醒,每次喚醒后,和每次無效雙擊后,要把level清0


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 崇信县| 芜湖县| 湟中县| 牙克石市| 武安市| 郴州市| 蒲江县| 延寿县| 唐海县| 屏东市| 西盟| 锡林浩特市| 湛江市| 富顺县| 海兴县| 腾冲县| 嵊州市| 中江县| 文安县| 丽江市| 原平市| 阜宁县| 青海省| 海阳市| 西乡县| 玉田县| 乌苏市| 灌南县| 古田县| 罗平县| 兴业县| 封丘县| 句容市| 巨鹿县| 布尔津县| 化州市| 佛山市| 麦盖提县| 油尖旺区| 兴化市| 特克斯县|