1. 程式人生 > >觸控式螢幕驅動

觸控式螢幕驅動

轉載linux觸控式螢幕驅動詳解

觸控式螢幕工作流程

  1. 按下,產生中斷;
  2. 在按下中斷處理程式中,啟動ADC轉換XY座標;
  3. ADC轉換結束,產生ADC中斷;
  4. 在ADC中斷處理函式中,上報資料(input_report_),啟動定時器;
  5. 定時時間到,再次啟動ADC(處理長按、滑動);
  6. 鬆開。

原始碼

#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/input.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/io.h> #include <plat/adc.h> #include <plat/regs-adc.h> #include <plat/ts.h>
struct s3c_ts_regs{ u32 adccon; u32 adctsc; u32 adcdly; u32 adcdata0; u32 adcdata1; u32 adcupdn; } *ts_regs; static struct input_dev *ts_dev; static struct timer_list ts_timer;//定義timer結構體 static void enter_wait_down_mode(void) { //進入等待觸控式螢幕按下的模式 ts_regs->adctsc=0xd3; } static void enter_wait_up_mode
(void) { //進入等待觸控式螢幕擡起的模式 ts_regs->adctsc=0x1d3; } static void enter_auto_measure_mode(void) { //進入自動測量模式 ts_regs->adctsc=(1<<3)|(1<<2); } static void start_adc(void) { ts_regs->adccon |= (1<<0); } /*過濾偏差較大的數值*/ static int s3c_filter_ts(int x[], int y[]) { #define ERR_LIMIT 10//允許相鄰資料的差值 int avr_x, avr_y; int det_x, det_y; /*取相鄰兩個數值的平均值*/ avr_x = (x[0] + x[1])/2; avr_y = (y[0] + y[1])/2; det_x = (x[2] > avr_x) ? (x[2] - avr_x) : (avr_x - x[2]); det_y = (y[2] > avr_y) ? (y[2] - avr_y) : (avr_y - y[2]); if ((det_x > ERR_LIMIT) || (det_y > ERR_LIMIT)) return 0; avr_x = (x[1] + x[2])/2; avr_y = (y[1] + y[2])/2; det_x = (x[3] > avr_x) ? (x[3] - avr_x) : (avr_x - x[3]); det_y = (y[3] > avr_y) ? (y[3] - avr_y) : (avr_y - y[3]); if ((det_x > ERR_LIMIT) || (det_y > ERR_LIMIT)) return 0; return 1; } /*超時處理函式,長按、滑動啟動adc測量*/ static void ts_timer_func (u32 data) { if (ts_regs->adcdata0 & (1<<15)) { /* 已經鬆開 */ input_report_abs(ts_dev, ABS_PRESSURE, 0); input_report_key(ts_dev, BTN_TOUCH, 0); input_sync(ts_dev); enter_wait_down_mode(); } else { /* 測量X/Y座標 */ enter_auto_measure_mode(); start_adc(); } } static irqreturn_t ts_irq (int irq, void *dev_id) { if(ts_regs->adcdata0&(1<<15))//擡起 { input_report_abs(ts_dev, ABS_PRESSURE, 0); input_report_key(ts_dev, BTN_TOUCH, 0); input_sync(ts_dev); enter_wait_down_mode(); } else//按下 { enter_auto_measure_mode(); start_adc(); } return IRQ_HANDLED; } static irqreturn_t adc_irq(int irq, void *dev_id) { static int x[4], y[4]; int adcdat0, adcdat1; static int cnt=0; adcdat0=ts_regs->adcdata0; adcdat1=ts_regs->adcdata1; if(ts_regs->adcdata0&(1<<15)) { /*優化措施1:若adc完成時,發現觸控筆鬆開,則丟棄此次數值*/ input_report_abs(ts_dev, ABS_PRESSURE, 0); input_report_key(ts_dev, BTN_TOUCH, 0); input_sync(ts_dev); enter_wait_down_mode();//等待按下 cnt=0; } else { /*優化措施2:取過濾後的4次資料的平均值*/ x[cnt]=adcdat0&0x3ff; y[cnt]=adcdat1&0x3ff; ++cnt; if(cnt==4) { if (s3c_filter_ts(x, y)) { input_report_abs(ts_dev, ABS_X, (x[0]+x[1]+x[2]+x[3])/4); input_report_abs(ts_dev, ABS_Y, (y[0]+y[1]+y[2]+y[3])/4); input_report_abs(ts_dev, ABS_PRESSURE, 1); input_report_key(ts_dev, BTN_TOUCH, 1); input_sync(ts_dev); } cnt = 0; enter_wait_up_mode(); mod_timer(&ts_timer, jiffies+HZ/100);//10ms } else//還每到4次就鬆開 { enter_auto_measure_mode(); start_adc(); } } return IRQ_HANDLED;//接收到了中斷訊號,並處理 } static int s3c_ts_init(void) { struct clk* clk; /*1分配一個input-dev結構體*/ ts_dev=input_allocate_device(); /*2設定*/ set_bit(EV_KEY,ts_dev->evbit);//按鍵類事件 set_bit(EV_ABS,ts_dev->evbit);//絕對位移類 /*2.1能產生這類事件的哪些事件*/ set_bit(BTN_TOUCH,ts_dev->keybit);//觸控事件 /*x方向 最小值 最大值(10位)*/ input_set_abs_params(ts_dev, ABS_X, 0, 0x3FF, 0, 0); input_set_abs_params(ts_dev, ABS_Y, 0, 0x3FF, 0, 0); input_set_abs_params(ts_dev, ABS_PRESSURE, 0, 1, 0, 0); /*3註冊*/ input_register_device(ts_dev); /*4硬體相關操作*/ clk=clk_get(NULL, "adc"); clk_enable(clk);//開啟ADC ts_regs=ioremap(0x58000000, sizeof(struct s3c_ts_regs));//對映 /* [15] : ECFLG, 1 = A/D 轉換結束標誌位 * [14] : PRSCEN, 1 = prescaler enable預分頻使能 * [13:6]: PRSCVL, adc clk = PCLK / (PRSCVL + 1);1M=50/(49+1)預分頻係數 * [5:3] : SEL_MUX, 000 = AIN 0 通道選擇 * [2] : STDBM 不待機 * [0] : 1 = 啟動adc,啟動後自動清零 */ ts_regs->adccon = (1<<14) | (49<<6) | (0<<3); ts_regs->adcdly=0xffff;//使電壓穩定後再發出adc中斷,**很重要** /* 中斷引腳 中斷函式 觸發方式 名稱 傳入dev*/ request_irq(IRQ_TC,ts_irq,IRQF_SAMPLE_RANDOM,"ts_pen",NULL); request_irq(IRQ_ADC,adc_irq,IRQF_SAMPLE_RANDOM,"adc",NULL); /*定時器處理滑動*/ init_timer(&ts_timer);//初始化timer ts_timer.function = (void*)ts_timer_func;//超時時間到了,進入定時處理函式 add_timer(&ts_timer); printk("init"); enter_wait_down_mode(); return 0; } static void s3c_ts_exit(void) { free_irq(IRQ_TC,NULL); free_irq(IRQ_ADC,NULL); iounmap(ts_regs); del_timer(&ts_timer); input_unregister_device(ts_dev); input_free_device(ts_dev);//釋放結構體 } module_init(s3c_ts_init);//入口函式 module_exit(s3c_ts_exit);//出口函式 MODULE_LICENSE("GPL");

insmod lcd_ts.ko
ls /dev/event*
hexdump /dev/event0

在這裡插入圖片描述
type事件型別_ABS(3)、EV_KEY(1)
code事件:ABS_Y(1)、ABS_PRESSURE(18)

編譯tslib進行測試