android 电容屏(三):驱动调试之驱动程序分析篇
平臺信息:
內(nèi)核:linux3.4.39
系統(tǒng):android4.4?
平臺:S5P4418(cortex a9)
作者:瘋耔(歡迎轉(zhuǎn)載,請注明作者)
歡迎指正錯誤,共同學(xué)習(xí)、共同進(jìn)步!!
?
關(guān)注博主新浪博客:http://weibo.com/cpjphone
?
以goodix的gt8105為例
一、總體架構(gòu)
硬件部分:先看一個總體的圖吧,其實(shí)觸摸屏原理也比較簡單,觸摸屏和主控芯片間的聯(lián)系,如下主要有三部分:
?
1、IIC部分,初始化gt8105的數(shù)據(jù)和傳回主控制的坐標(biāo)位置信息就是通過IIC這條線傳輸?shù)?#xff1b;
2、INT,當(dāng)gt8105初觸摸時,會發(fā)出中斷通知主控接收信息(坐標(biāo)數(shù)據(jù));
3、gt8105電源、復(fù)位這一部分,不同芯片有所不同,可以根據(jù)觸摸屏芯片來配置。
?軟件部分:
?二、電容觸摸屏的主要參數(shù)(這部分面試的時候也許有人會問的)
??????????????? 記得剛出來找工作時有人問我一些問題,我答不上來,現(xiàn)在感覺很清晰(那時候剛畢業(yè)IIC我都說不全)
1、IIC
(1)、clk370KHz~400KHz;
(2)、觸摸屏工作在從模式,這個比較簡單;
2、電容檢測頻率,也就是每秒檢測的次數(shù):(大概)
(1)、單指≥100Hz;
(2)、五指≥80Hz;
(3)、十指≥60Hz。
3、手指按下,沒抬起時觸發(fā)多少中斷?
??????????? 中斷個數(shù)也就是檢測頻率,按下沒提起一直有中斷。這樣我們就可有判斷單點(diǎn)、劃線之類的操作;
4、校準(zhǔn)功能、自動校準(zhǔn)(有個別電容屏沒有的,用軟件校準(zhǔn))
(1)、初始化校準(zhǔn)
???????????? 不同的溫度、濕度及物理空間結(jié)構(gòu)均會影響到電容傳感器在閑置狀態(tài)的基準(zhǔn)值。一般電容觸摸屏?xí)诔跏蓟?200ms內(nèi)根據(jù)環(huán)境情況自動獲得新的檢測基準(zhǔn)。完成觸摸屏檢測的初始化。
(2)、 自動溫漂補(bǔ)償
????????????? 溫度、濕度或灰塵等環(huán)境因素的緩慢變化,也會影響到電容傳感器在閑置狀態(tài)的基準(zhǔn)值。實(shí)時檢測各點(diǎn)數(shù)據(jù)的變化,對歷史數(shù)據(jù)進(jìn)行統(tǒng)計(jì)分析,由此來修正檢測基準(zhǔn)。從而降低環(huán)境變化對觸摸屏檢測的影響。????
5、推薦工作條件(環(huán)境溫度為 25°C,VDD=2.8V)
| 參數(shù) | 最小值 | 典型值 | 最大值 | 單位 |
| 模擬AVDD(參考AGND) | 2.5 | 2.8 | 3.6 | V |
| 數(shù)字DVDD(參考DGND) | 2.5 | 2.8 | 3.6 | V |
| 電源紋波 | ? | 50(注意電池、充電器的影響) | ? | mV |
| 工作溫度 | -20 | +25 | +85 | 度 |
| 工作濕度 | - | - | 95 | % |
三、硬件接口電路:
如下圖:
?
| SDA | IIC數(shù)據(jù) 要上拉電阻,為1K; |
| SCL | IIC?時鐘(400KHz) |
| TP_EN | 使能腳(gt8105為高電平) |
| INT | 中斷(一直點(diǎn)到觸摸屏?xí)r中斷是一直發(fā)出的) |
| VCC | 3.3V?這個電壓一直有 |
| GND | 地 |
?
對于S5P4418電路:
?
與LCD接口部分;
與主控觸摸屏中斷輸入管腳;
?
與主控I2C數(shù)據(jù)傳遞引腳連接;
復(fù)位控制引腳與主控連接;
-------
軟件部分,整體流程如下:
三、IIC配置
設(shè)備到芯片的數(shù)據(jù)、初始化值都是從這條總線上傳輸?shù)?#xff0c;首先我們要配置這個條總線,
/linux/arch/arm/mach-exynos/mach-smdkv310.c,這個因平臺而已,地址右移也跟情況而定,如果本來就是7bit的地址就不用移位。
static struct i2c_board_info i2c_devs5[] __initdata = { #if CONFIG_TOUCHSCREEN_GT8105 { I2C_BOARD_INFO("Goodix-TS", (0xaa>>1)), .irq = IRQ_EINT(5), } #endif };?
四、電源、復(fù)位(使能腳)
1、電源
??????????3.3V的電源是一直有的,這個硬件上給就行了。
2、復(fù)位(時能腳),這個因觸摸屏而已,gt8105工作時要高電平。
在:linux3.0/drivers/input/touchscreen/goodix_touch.h中
#define RESETPIN_CFG s3c_gpio_cfgpin(EXYNOS4_GPB(4), S3C_GPIO_OUTPUT) #define RESETPIN_SET0 gpio_direction_output(EXYNOS4_GPB(4),0) #define RESETPIN_SET1 gpio_direction_output(EXYNOS4_GPB(4),1) static void goodix_reset(void) { int err; err = gpio_request(EXYNOS4_GPB(4), "GPX1"); if (err) printk(KERN_ERR "#### failed to request GPB_4 ####\n"); RESETPIN_CFG; //配置管腳功能 RESETPIN_SET0;//管腳拉低 mdelay(20); //延時 RESETPIN_SET1;//管腳拉高 mdelay(60); gpio_free(EXYNOS4_GPB(4)); }?4418:在:linux3.0/drivers/input/touchscreen/goodix_tool.h中
五、中斷配置
在:linux3.0/drivers/input/touchscreen/goodix_touch.h中
#define INT_PORT EXYNOS4_GPX0(5) #ifdef INT_PORT #define TS_INT IRQ_EINT(5)//中斷引腳,中斷號 #define INT_CFG S3C_GPIO_SFN(0x0F) #else 在:linux3.0/drivers/input/touchscreen/goodix_touch.h中 中斷申請 #ifdef INT_PORT client->irq=TS_INT; if (client->irq) { ret = request_irq(client->irq, goodix_ts_irq_handler , IRQ_TYPE_EDGE_RISING|IRQ_TYPE_EDGE_FALLING,client->name, ts); #endif?
?
?
?
?上面三部完成了觸摸屏工作的最基本配置,保證IIC、上電、INT正常,觸摸屏就可以工作。
六、驅(qū)動程序分析(完整代碼見?goodix_touch.c/goodix_touch.h)
?驅(qū)動有幾個比較重要的部分:probe函數(shù)分析;中斷申請、工作隊(duì)列調(diào)度;中斷下半部函數(shù)的執(zhí)行,坐標(biāo)值計(jì)算、上報。
1、probe函數(shù)分析
static int goodix_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct goodix_ts_data *ts; ………… // 1,分配觸摸屏結(jié)構(gòu)內(nèi)核空間; ts = kzalloc(sizeof(*ts), GFP_KERNEL); ………… // 2,初始化工作隊(duì)列,這個比較重要,中斷觸發(fā)后,調(diào)用隊(duì)列中的goodix_ts_work_func函數(shù),計(jì)算上報坐標(biāo)值; INIT_WORK(&ts->work, goodix_ts_work_func); ………… // 3, 觸摸芯片初始化; for(retry=0; retry<3; retry++) { ret=goodix_init_panel(ts); ………… } //4、觸摸屏復(fù)位,拉高; goodix_reset(); #ifdef INT_PORT // 5,中斷申請,TS_INT就是我們所設(shè)定的中斷腳; client->irq=TS_INT; ret = request_irq(client->irq, goodix_ts_irq_handler , IRQ_TYPE_EDGE_RISING|IRQ_TYPE_EDGE_FALLING, client->name, ts); ……………… #endif // 6、分配input驅(qū)動內(nèi)核空間; ts->input_dev = input_allocate_device(); // 7,input初始化參數(shù)設(shè)定,我們在前面提到Linux與Android 多點(diǎn)觸摸協(xié)議里有對這部分說明; ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ; ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); ts->input_dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE); // absolute coor (x,y) #ifdef HAVE_TOUCH_KEY for(retry = 0; retry < MAX_KEY_NUM; retry++) { input_set_capability(ts->input_dev,EV_KEY,touch_key_array[retry]); } #endif input_set_abs_params(ts->input_dev, ABS_X, 0, ts->abs_x_max, 0, 0); input_set_abs_params(ts->input_dev, ABS_Y, 0, ts->abs_y_max, 0, 0); input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 255, 0, 0); //8、這部分針對觸摸屏參數(shù)設(shè)定; #ifdef GOODIX_MULTI_TOUCH input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, ts->abs_x_max, 0, 0); input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, ts->abs_y_max, 0, 0); input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, ts->max_touch_num, 0, 0); #endif //9、觸摸屏版本信息設(shè)定; sprintf(ts->phys, "input/ts"); ts->input_dev->name = goodix_ts_name; ts->input_dev->phys = ts->phys; ts->input_dev->id.bustype = BUS_I2C; ts->input_dev->id.vendor = 0xDEAD; ts->input_dev->id.product = 0xBEEF; ts->input_dev->id.version = 10427; //screen firmware version //10,對于input子系統(tǒng)來說,這個是重頭戲了,只有注冊了input子系統(tǒng),其他的才有做用; ret = input_register_device(ts->input_dev); ……………… // 11,對睡眠喚醒操作; #ifdef CONFIG_HAS_EARLYSUSPEND ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; ts->early_suspend.suspend = goodix_ts_early_suspend; ts->early_suspend.resume = goodix_ts_late_resume; register_early_suspend(&ts->early_suspend); #endif ……………… }?
(1)、分配觸摸屏結(jié)構(gòu)內(nèi)核空間;
struct goodix_ts_data { uint16_t addr; uint8_t bad_data; struct i2c_client *client; struct input_dev *input_dev; int use_reset; //use RESET flag int use_irq; //use EINT flag int read_mode; //read moudle mode,20110221 by andrew struct hrtimer timer; struct work_struct work; char phys[32]; int retry; struct early_suspend early_suspend; int (*power)(struct goodix_ts_data * ts, int on); uint16_t abs_x_max; uint16_t abs_y_max; uint8_t max_touch_num; uint8_t int_trigger_type; uint8_t green_wake_mode; };?
(2)、初始化工作隊(duì)列,這個比較重要,中斷觸發(fā)后,調(diào)用隊(duì)列中的goodix_ts_work_func函數(shù),計(jì)算上報坐標(biāo)值;這個和中斷申請一起分析;
(3)、觸摸芯片初始化;
??????????對觸摸芯片寄存器的初始化,這里面對中斷方式設(shè)定等,一般芯片廠的FAE在調(diào)試的時候會修改這里面的值,這個也是因芯片而異,有的在驅(qū)動里做,可以直接改;有的直接做成固件了,那部分要FAE幫忙了。
uint8_t cfg_info_group1[] = { 0x65,0x00,0x25,0x80,0x19,0x00,0x00,0x2C,0x11,0x11,0x32,0x02,0x08,0x10,0x20,0x00, 0x00,0x88,0x88,0x88,0x03,0x13,0x32,0x64,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, 0x08,0x09,0x0A,0x0B,0x0C,0xFF,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16, 0x17,0x18,0x19,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 };?
(4)、觸摸屏復(fù)位,拉高;
??????????gt8015在工作時要拉高,所以我們做一個拉低—延時--拉高的操作;
(5)、中斷申請,TS_INT就是我們所設(shè)定的中斷腳,和(2)一起后面分析;
(6)、分配input驅(qū)動內(nèi)核空間;
?
ts->input_dev= input_allocate_device();?
(7)、input初始化參數(shù)設(shè)定,我們在前面提到Linux與Android 多點(diǎn)觸摸協(xié)議里有對這部分說明;(8)、這部分針對觸摸屏參數(shù)設(shè)定;
(9)、觸摸屏版本信息設(shè)定;
cat /proc/bus/input/devices時可以看到下面信息(這個是pixcir的觸摸屏) I: Bus=0018 Vendor=0000 Product=0000 Version=0000 N: Name="pixcir-ts" P: Phys= S: Sysfs=/devices/platform/s3c2440-i2c.5/i2c-5/5-005c/input/input3 U: Uniq= H: Handlers=kbd event3 B: PROP=0 B: EV=b B: KEY=400 0 0 0 0 1000 40000800 0 0 0 0 B: ABS=2650000 1000000?
(10)、對于input子系統(tǒng)來說,這個是重頭戲了,驅(qū)動注冊到input子系統(tǒng);
?
input_register_device(ts->input_dev);?
(11),觸摸屏睡眠喚醒操作,這部分不做詳細(xì)說明,感興趣的可以看下……
2、中斷申請、工作隊(duì)列調(diào)度
(1)、中斷申請
ret = request_irq(client->irq, goodix_ts_irq_handler , IRQ_TYPE_EDGE_RISING|IRQ_TYPE_EDGE_FALLING, client->name, ts); 第一個參數(shù): 中斷號,client->irq,client->irq=TS_INT; #define TS_INT IRQ_EINT(5)對應(yīng)到我們要申請的中斷; 第二個參數(shù):中斷執(zhí)行函數(shù),goodix_ts_irq_handler ; 第三個參數(shù):中斷觸發(fā)方式:上升沿觸發(fā)、下降沿觸發(fā)、高電平觸發(fā)、低電平觸發(fā) IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_FALLING, IRQ_TYPE_LEVEL_LOW, IRQ_TYPE_LEVEL_HIGH 第四個參數(shù): 第五個參數(shù):?
(2)、中斷處理函數(shù) goodix_ts_irq_handler
static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id) { struct goodix_ts_data *ts = dev_id; queue_work(goodix_wq, &ts->work); return IRQ_HANDLED; }?
看下queue_work()這個函數(shù)中的兩個參數(shù):
a、goodix_wq
goodix_wq=create_singlethread_workqueue("goodix_wq"); //createa work queue and worker thread?
在函數(shù) goodix_ts_init中,創(chuàng)建工作隊(duì)列和工作線程,初始化時創(chuàng)建線程。
b、&ts->work
??????????? 在函數(shù)goodix_ts_probe()中:
INIT_WORK(&ts->work,goodix_ts_work_func);?????????? 在工作隊(duì)列&ts->work中增加 goodix_ts_work_func任務(wù)。
??????????也就是當(dāng)中斷函數(shù)觸發(fā)時,執(zhí)行中斷函數(shù)goodix_ts_irq_handler(),中斷函數(shù)里面對隊(duì)列調(diào)度,調(diào)用隊(duì)列中的goodix_ts_work_func()函數(shù)。
?
3、中斷下半部函數(shù)的執(zhí)行g(shù)oodix_ts_work_func()函數(shù)
這就是核心部分,坐標(biāo)點(diǎn)的計(jì)算、上報、多點(diǎn)處理都在這個函數(shù)中執(zhí)行。
static void goodix_ts_work_func(struct work_struct *work) { int ret=-1; int tmp = 0; uint8_t point_data[(1-READ_COOR_ADDR)+1+2+5*MAX_FINGER_NUM+1]={ 0 }; //read address(1byte)+key index(1byte)+point mask(2bytes)+5bytes*MAX_FINGER_NUM+coor checksum(1byte) uint8_t check_sum = 0; uint16_t finger_current = 0; uint16_t finger_bit = 0; unsigned int count = 0, point_count = 0; unsigned int position = 0; uint8_t track_id[MAX_FINGER_NUM] = {0}; unsigned int input_x = 0; unsigned int input_y = 0; unsigned int input_w = 0; unsigned char index = 0; unsigned char touch_num = 0; struct goodix_ts_data *ts = container_of(work, struct goodix_ts_data, work); if(g_enter_isp)return; COORDINATE_POLL: if((ts->int_trigger_type> 1)&& (gpio_get_value(INT_PORT) != (ts->int_trigger_type&0x01))) { goto NO_ACTION; } if( tmp > 9) { dev_info(&(ts->client->dev), "I2C transfer error,touchscreen stop working.\n"); goto XFER_ERROR ; } if(ts->bad_data) msleep(20); point_data[0] = READ_COOR_ADDR; //read coor address //1、讀取觸摸屏值,手指數(shù)、坐標(biāo)值等; ret=i2c_read_bytes(ts->client, point_data, ((1-READ_COOR_ADDR)+1+2+5*ts->max_touch_num+1)); ………… //2、判斷是否有手指按下; finger_current = (point_data[3 - READ_COOR_ADDR]<<8) + point_data[2 – READ_COOR_ADDR]; if(finger_current)//3、如果有手指按下 { point_count = 0, finger_bit = finger_current; //3,循環(huán)判斷有多少手指按下; for(count = 0; (finger_bit != 0) && (count < ts->max_touch_num); count++)//cal how many point touch currntly { if(finger_bit & 0x01) { track_id[point_count] = count; point_count++; } finger_bit >>= 1; } //4、把按下手指數(shù)賦給touch_num; touch_num = point_count; //5、計(jì)算坐標(biāo)值; check_sum = point_data[2 - READ_COOR_ADDR] + point_data[3 - READ_COOR_ADDR]; //cal coor checksum count = 4 - READ_COOR_ADDR; for(point_count *= 5; point_count > 0; point_count--) check_sum += point_data[count++]; check_sum += point_data[count]; if(check_sum != 0) //checksum verify error { printk("coor checksum error!\n"); if(ts->int_trigger_type> 1) goto COORDINATE_POLL; else goto XFER_ERROR; } } //6、讀取值坐標(biāo)值上報; if(touch_num) { //7、touch_num為按下手指個數(shù),依次循環(huán)讀取; for(index=0; index<touch_num; index++) { position = 4 - READ_COOR_ADDR + 5*index; //8、讀出X的值; input_x = (unsigned int) (point_data[position]<<8) + (unsigned int)( point_data[position+1]); //9、讀出Y的值; input_y = (unsigned int)(point_data[position+2]<<8) + (unsigned int) (point_data[position+3]); input_w =(unsigned int) (point_data[position+4]); //10、如果讀出值超出范圍,退出; if((input_x > ts->abs_x_max)||(input_y > ts->abs_y_max)) continue; //11、下面的函數(shù)依次上報坐標(biāo), input_mt_sync單點(diǎn)同步 input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x); input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y); input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w); input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, input_w); input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, track_id[index]); input_mt_sync(ts->input_dev); } } //12、沒有觸摸時,初始值為0; else { input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0); input_mt_sync(ts->input_dev); } //13、同步多點(diǎn)值; input_sync(ts->input_dev); if(ts->int_trigger_type> 1) { msleep(POLL_TIME); goto COORDINATE_POLL; } goto END_WORK_FUNC; NO_ACTION: END_WORK_FUNC: XFER_ERROR: return; }?
? ? ? ? ? ? ? ??總的來數(shù),當(dāng)我們手指按下是,不管是單個手指,還是多個手指,坐標(biāo)值和一些信息存儲到觸摸芯片的相應(yīng)寄存器中,然后再通過IIC讀出,送到主控中就可以了,其他事情就是android去處理了。
?????????? 如下圖所示,規(guī)格書中坐標(biāo)及重量:XY坐標(biāo)緩存寄存器的高低位:
?
中斷觸發(fā)--中斷函數(shù)--工作隊(duì)列調(diào)度--功能函數(shù)執(zhí)行
轉(zhuǎn)載于:https://www.cnblogs.com/Ph-one/p/4838552.html
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的android 电容屏(三):驱动调试之驱动程序分析篇的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iOS:网络编程解析协议一:HTTP超文
- 下一篇: MAX2323E - 原理图系列