1. 程式人生 > >【驅動】第5課、TS(觸控式螢幕)驅動之學習筆記

【驅動】第5課、TS(觸控式螢幕)驅動之學習筆記

前言

  當課程過長時,把課程分為幾小節學習,一小節一結束一練習,可減輕學習難度加快程序。最近總感覺困頓,是因為學而不思則罔,練習太少,知識反覆練習犯錯而咀嚼太少,知識掌握程度太低。本課TS加大練習量!



目錄
壹、程式設計步驟
第一步:編寫出可以列印“pen down” "pen up"狀態的程式碼;
1.input_dev系統;
2.s3c_ts_init, s3c_ts_exit;
3.pen_down_up_irq, enter_wait_pen_down_mode, enter_wait_pen_up_mode;
第二步:加入INT_ADC_S中斷處理和對觸點的座標讀取;
1.start_adc, enter_measure_xy_mode, adc_irq,
第三步:加入觸點的多次測量,求平均值/中間值;


第四步:加入觸點的座標x,y的過濾程式碼,過濾成功則求平均值/中間值;
第五步:加入對長按/滑動的檢測,新增定時器功能;
第六步:加入上報功能;
貳、測試
第七步:測試

 



壹、程式設計步驟學習筆記
---------------------------------------------------------------------------------
第三步、核心崩潰
1、錯誤報告:
...
Kernel panic - not syncing: Fatal exception in interrupt:
2、缺陷原始碼:
static irqreturn_t SubInt_Adcs_irq(int irq, void *dev_id)
{
static int adc_cnt = 0; /* 觸發ADC轉換的次數計數 */
static int adc_ax[NUM];
static int adc_ay[NUM];
int i, j, tmp;

// x = s3c2440_adc_regs->adcdat0 & 0x3ff;
// y = s3c2440_adc_regs->adcdat1 & 0x3ff;
adc_ax[adc_cnt] = s3c2440_adc_regs->adcdat0 & 0x3ff;
adc_ay[adc_cnt] = s3c2440_adc_regs->adcdat1 & 0x3ff;

if(s3c2440_adc_regs->adcdat0 & (1<<15)) //此時觸控筆為擡起狀態;
{
adc_cnt = 0;
enter_wait_pen_down_mode();
}
else if(!(s3c2440_adc_regs->adcdat0 & (1<<15))) //此時觸控筆為落下狀態;
{
//adc_cnt++; //問題所在?造成空指標adc_ax/y[7]?修改:釋放註釋!答:是的!
if(adc_cnt == NUM)
{
/* 排序求中間值,並上報 */
for(i = 0; i < NUM - 1; i++)
for(j = i + 1; j < NUM - 1; j++) //問題所在?造成空指標?j < NUM - 1應改為:j < NUM
{
/* 從小到大排序 */
if(adc_ax[i] > adc_ay[j]) //問題所在?造成空指標?adc_ay[j]應改為adc_ax[j]
{
tmp = adc_ax[i];
adc_ax[i] = adc_ax[j];
adc_ax[j] = tmp;
}
if(adc_ay[i] > adc_ay[j])
{
tmp = adc_ay[i];
adc_ay[i] = adc_ay[j];
adc_ay[j] = tmp;
}
}
printk("x = %d, y = %d\n", adc_ax[3], adc_ay[3]);
adc_cnt = 0;
enter_wait_pen_up_mode();
}
else
{
/* 再次測量 */
//adc_cnt++; //問題所在?造成空指標?修改:若新增在此,上面if(adc_cnt == NUM)改為if(adc_cnt == NUM-1);
enter_auto_measure_mode();
start_adc();
}
}
return IRQ_HANDLED;
}
3、分析:Linux Kernel Panic報錯解決思路


<1>有兩種主要型別kernel panic:
I. hard panic(也就是Aieee資訊輸出)
II. soft panic (也就是Oops資訊輸出)
<2>常見Linux Kernel Panic報錯內容:
Kernel panic-not syncing fatal exception in interrupt
kernel panic – not syncing: Attempted to kill the idle task!
kernel panic – not syncing: killing interrupt handler!
Kernel Panic – not syncing:Attempted to kill init !
<3>什麼會導致Linux Kernel Panic? 原因?
答:對於hard panic而言,最大的可能性是驅動模組的中斷處理(interrupt handler)導致的,一般是因為驅動模組在中斷處理程式中訪問一個空指
針(null pointre)。一旦發生這種情況,驅動模組就無法處理新的中斷請求,最終導致系統崩潰。
4、問題發現及解決
答:查詢到問題程式碼: //adc_cnt++; if(adc_cnt == NUM){...}else{//adc_cnt++; ...}
當語句adc_cnt++; 放在if()判斷之前,沒有問題,當語句adc_cnt++; 放在if()判斷之後,將造成對空指標adc_ax/y[NUM]的訪問和操作,隨即造成核心崩潰!
修改:語句adc_cnt++; 放在if()判斷之前。
編譯及驅動模組載入執行結果:成功。
-------------
#define NUM 7
int a[NUM] = {1, 2, 3, 4, 5, 6, 7};
int main(void)
{
    printf("中位數: a[NUM/2] = %d\n", a[NUM/2]);
    return 0;
}
/*在VC++6.0中的執行結果:
中位數: a[NUM/2] = 4
Press any key to continue
*/

-----------------------------------------------------------------
第四步、加入觸點的座標x,y的過濾程式碼
Y軸最高點與最低點座標差為:784。
Y軸最高點與最低點座標差為:803。
字型一般為8x16,TD35屏尺寸:320*240。
因此,如果容錯誤差為一個字元的顯示單元,則:X(誤差)=26, Y(誤差)=40。
-----------------------------------------------------
第五/六步:加入對長按/滑動的檢測,新增定時器並上報
1、錯誤報告
問題1:(爆發式列印)
Pen Up!
Failed to filter ts contact coordinates!
Pen Up!
Failed to filter ts contact coordinates!
Pen Up!
Failed to filter ts contact coordinates!
Pen Up!
...
問題2:
原始碼為 s3c2440_adc_regs->adcdly = 0xffff; 時的列印:
Pen Down!
Failed to filter ts contact coordinates!
Pen Down!
Failed to filter ts contact coordinates!
Failed to filter ts contact coordinates!
Pen Down!
Pen Down!
Failed to filter ts contact coordinates!
Pen Up!
原始碼為 s3c2440_adc_regs->adcdly = 0x3ff; 時的列印:
Pen Up!
Pen Down!
Failed to filter ts contact coordinates!
Failed to filter ts contact coordinates!
Pen Up!
Pen Down!
Pen Up!
Pen Down!
Failed to filter ts contact coordinates!

2、缺陷原始碼:
問題1:
static irqreturn_t SubInt_Adcs_irq(int irq, void *dev_id)
{
#define LEN 7 /* 觸點座標陣列長度 */
static int adc_cnt = 0; /* 已經A/D轉換的次數計數值 */
static int adc_ax[LEN];
static int adc_ay[LEN];
adc_ax[adc_cnt] = s3c2440_adc_regs->adcdat0 & 0x3ff;
adc_ay[adc_cnt] = s3c2440_adc_regs->adcdat1 & 0x3ff;

if(s3c2440_adc_regs->adcdat0 & (1<<15)) //此時觸控筆為擡起狀態;
{
adc_cnt = 0;
input_report_abs(s3c2440_ts_dev, ABS_PRESSURE, 0);
input_report_key(s3c2440_ts_dev, BTN_TOUCH, 0);
input_sync(s3c2440_ts_dev);

enter_wait_pen_down_mode();
}
else if(!(s3c2440_adc_regs->adcdat0 & (1<<15))) //此時觸控筆為落下狀態;
{
adc_cnt++;
if(adc_cnt == LEN){
if(filter_ts(adc_ax, adc_ay, LEN)) /* 過濾A/D轉換之後的座標資料 */
{
/* 取中間值,上報或列印 */
//printk("x = %d, y = %d\n", adc_ax[LEN/2], adc_ay[LEN/2]);
input_report_abs(s3c2440_ts_dev, ABS_X, adc_ax[LEN/2]);
input_report_abs(s3c2440_ts_dev, ABS_Y, adc_ay[LEN/2]);
input_report_abs(s3c2440_ts_dev, ABS_PRESSURE, 1);

input_report_key(s3c2440_ts_dev, BTN_TOUCH, 1);
input_sync(s3c2440_ts_dev);
}
adc_cnt = 0; /* 計數清零 */
enter_wait_pen_up_mode();
mod_timer(&ts_timer, jiffies + HZ/100);
}
else{
enter_auto_measure_mode();
start_adc();
}
}
return IRQ_HANDLED;
}
static void ts_timer_function(unsigned long data){
enter_auto_measure_mode();
start_adc();
}
3、分析
問題1:如上,即使點選一次觸控式螢幕後不再點選觸控式螢幕,也會繼續列印。顯然,SubInt_Adcs_irq()中觸筆狀態位的反覆判斷出現了問題:
if(s3c2440_adc_regs->adcdat0 & (1<<15)) //此時觸控筆為擡起狀態;
{...}
else if(!(s3c2440_adc_regs->adcdat0 & (1<<15))) //此時觸控筆為落下狀態;
{...}

原因:對各暫存器可讀判斷標誌位,如ADCDAT0'[15]等待中斷模式中筆尖的起落狀態: 0 = 筆尖落下態 1 = 筆尖擡起態
的內部生成機制不瞭解,例如理解方式有:
<1>絕對的物理觸擊螢幕然後置位ADCDAT0'[15]=0或1,且CPU始終動態採集資料;
<2>A/D轉換的結果也可影響暫存器ADCDAT0'[15]=0或1置位;
<3>
<4>

問題2:A/D啟動或初始化延時暫存器ADCDLY的值,過大時會造成CPU有時捕捉不到觸筆狀態的變化!

4、問題查詢及解決:
問題1:s_timer_function(){...}內也進行觸筆狀態判斷;
問題2:暫存器 s3c2440_adc_regs->adcdly = 0x3ff; 這樣配置!



貳、測試
-----------------------------------------------------------------------------------------
第七步、測試:
1. $ ./autogen.sh 錯誤:
解決:根據tslib編譯使用方法,解壓tslib-1.4.tar.gz前先進行操作:
sudo apt-get install autoconf
sudo apt-get install automake
sudo apt-get install libtool

2.執行ts_calibrate產生錯誤Calibration failed
解決:最終還是驅動的問題,更換 s3c2440_ts10.ko 為 s3c2440_ts9.ko,則可實現螢幕校準操作!
s3c2440_ts10.ko的問題是原始碼s3c2440_ts10.c中:
input_report_abs(ts.dev, ABS_X, ts.xp);
input_report_abs(ts.dev, ABS_Y, ts.yp);
寫成了:
input_report_abs(ts.dev, ABS_X, 1);
input_report_abs(ts.dev, ABS_Y, 1);

3.說明:

當根據tslib編譯使用方法.TXT的步驟安裝過一次,當重啟開發板進行第二次觸控式螢幕測試時,雖然不再需要安裝新檔案,但是仍需要先進行【2.先配置環境變數】,然後才能使用【3.再測試】測試ts驅動模組。

【2.先配置環境變數】
export TSLIB_TSDEVICE=/dev/event0
export TSLIB_CALIBFILE=/etc/pointercal
export TSLIB_CONFFILE=/etc/ts.conf
export TSLIB_PLUGINDIR=/lib/ts
export TSLIB_CONSOLEDEVICE=none
export TSLIB_FBDEVICE=/dev/fb0
【3. 再測試】
ts_calibrate    // "五點校準"法,校準螢幕;
ts_test       // 測試:畫圖 或 命令螢幕游標跟隨觸筆走;
ts_print      // 列印觸點座標(LCD螢幕座標);
ts_print_raw    // 列印觸點原始座標(A/D轉換後的電壓資料);