最新教程下載下傳:http://www.armbbs.cn/forum.php?mod=viewthread&tid=98429
第5章 ThreadX GUIX上手之電阻觸摸和電容觸摸
本章教程為大家講解LTDC應用之LCD電阻觸摸晶片STMPE811的4點和2點觸摸校準和電容觸摸晶片FT5X06、GT911和GT811的使用。
5.1 初學者重要提示
5.2 電阻觸摸和電容觸摸相關知識
5.3 電阻屏硬體設計
5.4 電容屏硬體設計
5.5 電阻觸摸驅動設計
5.6 電容觸摸驅動設計
5.7 不同觸摸IC的識别
5.8 LCD觸摸移植和使用
5.9 實驗例程設計架構
5.10 實驗例程說明
5.12 總結
5.1 初學者重要提示
- 本章是為ThreadX GUIX的觸摸部分做準備。
- 電阻觸摸支援2點和4點校準,而電容屏無需校準。
- 電阻觸摸校準解決的是觸摸闆的線性度問題,而飛點要另外處理,目前程式已經做了支援。總的來說,V6配套的電阻觸摸方案已經比較成熟,可以放心用于項目。
- 屏蔽MDK AC6使用中文GBK編碼的警告方法,讓大家可以繼續使用GBK編碼漢字:http://www.armbbs.cn/forum.php?mod=viewthread&tid=98670 。
5.2 電阻觸摸和電容觸摸相關知識
這部分知識點在第4章的2.2小節有詳細說明,必看。
5.3 電阻屏硬體設計
電阻觸摸STMPE811的原理圖如下:
通過STMPE811的原理圖要了解以下幾點:
- I2C的兩根通信線I2C_SCL和I2C_SDA的上拉電阻在V7的主機闆上。
- 原理圖右側的GPIO-0到GPIO-7可以作為擴充IO使用,支援輸入和輸出。其中GPIO-4到GPIO-7用于電阻觸摸校準(使用那個IO是可以配置的)。
- 對于X-,X+,Y-和Y+,隻要不是X和Y進行組合,其它組合方式可以随意接,配套的觸摸校準算法都可以正常識别。
5.4 電容屏硬體設計
電容觸摸主要有三種:FT5X06,GT911和GT811,其中GT811已經停産。下面是FT5X06和GT911觸摸闆效果(觸摸闆和觸摸晶片是一體的):
觸摸晶片已經內建到柔性PCB上,且已經校準好。使用者使用的話,直接通過I2C方式讀取資料即可。下面是電容觸摸闆引出的引腳:
注意I2C_SDK和I2C_SCL的上拉電阻在V7主機闆上。
5.5 電阻觸摸驅動設計
下面将電阻觸摸程式設計中的相關問題逐一為大家做個說明。
5.5.1 STMPE811的驅動實作
電阻觸摸要比電容觸摸麻煩很多,因為電阻觸摸要做校準,還要做濾波,否則采集回來的觸摸值會抖動或者出現飛點,出現這種情況的主要原因是電阻觸摸闆的線性度不夠好。開發闆電阻屏使用的觸摸晶片是STMPE811,這個晶片其實就是12位分辨率的ADC,用來采集電阻觸摸闆的X軸ADC值和Y軸ADC值,然後按照一定的線性關系将ADC值轉換為實際的坐标值。其中這個線性關系是通過觸摸校準建立起來的,每次采集的X軸和Y軸ADC就可以代入這個線性關系,進而獲得實際的坐标值。
總的來說,STMPE811的驅動不難實作,可以結合STMPE811的資料手冊:http://www.armbbs.cn/forum.php?mod=viewthread&tid=23306 研究開發闆提供的驅動配置。配置好後僅需要提供讀取的X軸,Y軸的ADC值以及觸摸按下狀态(判斷STMPE811的中斷輸出引腳就可以了,如果有觸摸,這個引腳輸出低電平,反之,輸出高電平。通過判斷這個引腳就可以選擇是否讀取X軸,Y軸的ADC值,避免不必要的操作)。這些函數在bsp_ts_stmpe811.c檔案實作。而觸摸值濾波,觸摸掃描和觸摸校準是在bsp_ts_touch.c檔案裡面實作。
下面是清除觸摸中斷标志函數和X軸,Y軸的ADC值讀取函數,這些函數被bsp_touch.c檔案所調用,而函數TOUCH_PenInt是在bsp_ts_touch.c檔案,這裡也貼出來。
1. /*
2. ******************************************************************************************************
3. * 函 數 名: TOUCH_PenInt
4. * 功能說明: 判斷觸摸按下
5. * 形 參: 無
6. * 返 回 值: 0表示無觸筆按下,1表示有觸筆按下
7. ******************************************************************************************************
8. */
9. uint8_t TOUCH_PenInt(void)
10. {
11. if ((TP_INT_GPIO_PORT->IDR & TP_INT_PIN) == 0)
12. {
13. return 1;
14. }
15. return 0;
16. }
17.
18. /*
19. ******************************************************************************************************
20. * 函 數 名: STMPE811_ClearInt
21. * 功能說明: 清楚觸筆中斷
22. * 形 參: 無
23. * 返 回 值: 無
24. ******************************************************************************************************
25. */
26. void STMPE811_ClearInt(void)
27. {
28. STMPE811_WriteReg1(REG811_INT_STA, 0xFF);
29. }
30.
31. /*
32. ******************************************************************************************************
33. * 函 數 名: STMPE811_ReadX
34. * 功能說明: 讀取X坐标adc
35. * 形 參: 無
36. * 返 回 值: X坐标值adc
37. ******************************************************************************************************
38. */
39. uint16_t STMPE811_ReadX(void)
40. {
41. /* 按照 XY 讀取模式,連續讀取3位元組資料,然後分解出X,Y
42. | byte0 | byte1 | byte2 |
43. | X[11:4], | X[3:0],Y[11:8] | Y[7:0] |
44. */
45. uint8_t buf[3];
46.
47. #if 0
48. STMPE811_ReadBytes(buf, REG811_TSC_DATA1, 3);
49.
50. s_AdcX = ((uint16_t)buf[0] << 4) | (buf[1] >> 4);
51. s_AdcY = ((uint16_t)(buf[1] & 0xF) << 8) | buf[2];
52. #else
53. if (STMPE811_ReadReg1(REG811_TSC_CTRL) & 0x80)
54. {
55. STMPE811_ReadBytes(buf, REG811_TSC_DATA1, 3);
56.
57. s_AdcX = ((uint16_t)buf[0] << 4) | (buf[1] >> 4);
58. s_AdcY = ((uint16_t)(buf[1] & 0xF) << 8) | buf[2];
59.
60. #if 0
61. /* for debug */
62. {
63. static int32_t s_t1 = 0;
64. int32_t tt;
65.
66. tt = bsp_GetRunTime();
67. if (tt - s_t1 > 1000)
68. {
69. printf("\r\n");
70. s_t1 = tt;
71. }
72. printf("(%7d) %5d %5d\r\n", tt, s_AdcX, s_AdcY);
73. }
74. #endif
75. }
76. else
77. {
78. s_AdcX = 0;
79. s_AdcY = 0;
80. }
81. #endif
82.
83. return s_AdcX;
84. }
85.
86. /*
87. ******************************************************************************************************
88. * 函 數 名: STMPE811_ReadX
89. * 功能說明: 讀取Y坐标adc
90. * 形 參: 無
91. * 返 回 值: Y坐标值adc
92. ******************************************************************************************************
93. */
94. uint16_t STMPE811_ReadY(void)
95. {
96. return s_AdcY;
97. }
複制
下面将程式設計中的關鍵地方做個闡釋:
- 第9-16行,通過判斷STMPE811的中斷輸出引腳的高低電平來判斷觸摸闆是否被按下,如果有觸摸,這個引腳輸出低電平,反之,輸出高電平。通過判斷這個引腳就可以選擇是否讀取X軸,Y軸的ADC值,避免不必要的操作。
- 第26-29行,清除觸摸中斷标志,檢測到觸摸屏未被按下時,要做清除。
- 第39-84行,讀取X軸ADC數值。
- 第94-97行,讀取Y軸ADC數值。
5.5.2 電阻觸摸掃描函數TOUCH_Scan
接下來再來看bsp_touch.c檔案中STMPE811觸摸掃描函數TOUCH_Scan的實作:
1. /*
2. ******************************************************************************************************
3. * 函 數 名: TOUCH_Scan
4. * 功能說明: 觸摸闆事件檢測程式。該函數被周期性調用,每ms調用1次. 見 bsp_Timer.c
5. * 形 參: 無
6. * 返 回 值: 無
7. ******************************************************************************************************
8. */
9. void TOUCH_Scan(void)
10. {
11. uint16_t usAdcX;
12. uint16_t usAdcY;
13. static uint16_t s_usXBuf[SAMPLE_COUNT];
14. static uint16_t s_usYBuf[SAMPLE_COUNT];
15. static uint8_t s_ucPos = 0;
16. static uint8_t s_count = 0;
17. static uint8_t s_down = 0;
18. static uint16_t s_usSaveAdcX, s_usSaveAdcY; /* 用于觸筆擡起事件,儲存按下和移動的最後采樣值 */
19. static uint8_t s_ms = 0;
20.
21. if (g_GT811.Enable == 1)
22. {
23. GT811_Timer1ms(); /* 電容觸摸屏程式計數器 */
24. return;
25. }
26.
27. if (g_GT911.Enable == 1)
28. {
29. GT911_Timer1ms(); /* 電容觸摸屏程式計數器 */
30. return;
31. }
32.
33. if (g_tFT5X06.Enable == 1)
34. {
35. FT5X06_Timer1ms(); /* 電容觸摸屏程式計數器 */
36. return;
37. }
38.
39. /* 下面用于電阻觸摸 */
40.
41. if (g_tTP.Enable == 0)
42. {
43. return;
44. }
45.
46. if (++s_ms >= 2)
47. {
48. return;
49. }
50.
51. /* 2ms進入一次 */
52. s_ms = 0;
53.
54. /* 觸筆中斷發生 */
55. if (TOUCH_PenInt())
56. {
57. /* 獲得原始的ADC值,未濾波 */
58. usAdcX = STMPE811_ReadX();
59. usAdcY = STMPE811_ReadY();
60.
61. if (TOUCH_PressValid(usAdcX, usAdcY))
62. {
63. /* 按壓30ms之後才開始采集資料 */
64. if (s_count >= DOWN_VALID / 2)
65. {
66. s_usXBuf[s_ucPos] = usAdcX;
67. s_usYBuf[s_ucPos] = usAdcY;
68.
69. /* 采集20ms資料進行濾波 */
70. if (++s_ucPos >= SAMPLE_COUNT / 2)
71. {
72. s_ucPos = 0;
73.
74. /* 對ADC采樣值進行軟體濾波 */
75. g_tTP.usAdcNowX = TOUCH_DataFilter(s_usXBuf, SAMPLE_COUNT / 2);
76. g_tTP.usAdcNowY = TOUCH_DataFilter(s_usYBuf, SAMPLE_COUNT / 2);
77.
78. if (s_down == 0)
79. {
80. s_down = 1;
81. /* 觸摸按下事件 */
82. TOUCH_PutKey(TOUCH_DOWN, g_tTP.usAdcNowX, g_tTP.usAdcNowY);
83.
84. s_usSaveAdcX = g_tTP.usAdcNowX;
85. s_usSaveAdcY = g_tTP.usAdcNowY;
86. }
87. else
88. {
89. if (TOUCH_MoveValid(s_usSaveAdcX, s_usSaveAdcY, g_tTP.usAdcNowX, g_tTP.usAdcNowY))
90. {
91. /* 觸摸移動事件 */
92. TOUCH_PutKey(TOUCH_MOVE, g_tTP.usAdcNowX, g_tTP.usAdcNowY);
93.
94. s_usSaveAdcX = g_tTP.usAdcNowX;
95. s_usSaveAdcY = g_tTP.usAdcNowY;
96. }
97. else
98. {
99. g_tTP.usAdcNowX = 0; /* for debug stop */
100. }
101. }
102. }
103. }
104. else
105. {
106. s_count++;
107. }
108. }
109. else
110. {
111. if (s_count > 0)
112. {
113. if (--s_count == 0)
114. {
115. /* 觸摸釋放事件 */
116. //TOUCH_PutKey(TOUCH_RELEASE, g_tTP.usAdcNowX, g_tTP.usAdcNowY);
117. TOUCH_PutKey(TOUCH_RELEASE, s_usSaveAdcX, s_usSaveAdcY);
118.
119. g_tTP.usAdcNowX = 0;
120. g_tTP.usAdcNowY = 0;
121.
122. s_count = 0;
123. s_down = 0;
124.
125. STMPE811_ClearInt(); /* 清觸筆中斷标志 */
126. }
127. }
128. s_ucPos = 0;
129. }
130. }
131. }
複制
下面将程式設計中的關鍵地方做個闡釋:
- 第9行,此函數要每1ms被調用一次。
- 第21-37行,用于GT811,GT911和FT5X06的程式計數器。
- 第46-52行,設定每2ms進行一次STMPE811檢測。
- 第55行,這個就是本章前面小節說的利用STMPE811的中斷輸出引腳的高低電平來判斷觸摸闆是否被按下。
- 第58-59行,讀取X軸ADC數值和Y軸ADC數值。
- 第61行,通過函數TOUCH_PressValid檢測剛剛讀取的X軸,Y軸數值是否在有效的範圍内。
函數TOUCH_PressValid的具體實作如下,其中全局變量g_tTP.usMaxAdc = 4095,因為電阻觸摸晶片STMPE811是12位ADC,最大觸摸值就是2^12 – 1 = 4095。
/* 有效ADC值的判斷門限. 太接近ADC臨界值的坐标認為無效 */
#define ADC_VALID_OFFSET 2
/*
*********************************************************************************************************
* 函 數 名: TOUCH_PressValid
* 功能說明: 判斷按壓是否有效,根據X, Y的ADC值進行大緻判斷
* 形 參: 無
* 返 回 值: 1 表示有效; 0 表示無效
*********************************************************************************************************
*/
static uint8_t TOUCH_PressValid(uint16_t _usX, uint16_t _usY)
{
if ((_usX <= ADC_VALID_OFFSET) || (_usY <= ADC_VALID_OFFSET)
|| (_usX >= g_tTP.usMaxAdc - ADC_VALID_OFFSET)
|| (_usY >= g_tTP.usMaxAdc - ADC_VALID_OFFSET))
{
return 0;
}
else
{
return 1;
}
}
複制
- 第64行,DOWN_VALID的宏定義是 #define DOWN_VALID 30
由于是每2ms進行一次檢測,這裡就表示延遲30ms後進行觸摸資料采集。延遲30ms是為了消除觸摸抖動。
- 第70行,SAMPLE_COUNT 的宏定義是 #define SAMPLE_COUNT 20
由于是每2ms進行一次檢測,這裡就表示采集夠10組資料,即20ms後進行下一步操作。
- 第75-76行,對X軸和Y軸的ADC數值都進行軟體濾波。軟體濾波函數TOUCH_DataFilter的實作方法是對10組數值由小到大進行排序,對第3個,第4個和第5個數值求和,然後求平均,将平均值作為最終的ADC數值。
- 第78-86行,變量辨別s_down = 0表示觸摸之前是未按下狀态,在此條件裡面設定s_down = 1表示觸摸已經按下,并通過函數TOUCH_TransX(這個函數比較關鍵,是通過觸摸校準函數得到的一個線性關系)将目前的X軸和Y軸ADC數值轉換成實際的坐标值,然後調用函數TOUCH_PutKey将目前的坐标資訊存儲到FIFO裡面。
- 第89-100行設定變量辨別s_down = 1後會進入此條件裡面,在這個條件裡面通過函數TOUCH_MoveValid判斷目前是否是有效的移動,如果是,就繼續調用函數TOUCH_PutKey将目前的坐标資訊存儲到FIFO裡面,如果不是,就設定全局變量g_tTP.usAdcNowX = 0。
- 第111-128行,如果通過STMPE811的中斷輸出引腳檢測到觸摸未按下,然後判斷變量s_count是否大于0,如果大于0的話,做減減運算,算是做了一個松手延遲,防止抖動。減到0的時候,将觸摸未按下或者說觸摸釋放消息通過函數TOUCH_PutKey存儲到FIFO裡面。
5.5.3 電阻屏觸摸校準原理(2點)
由于不同電阻觸摸闆的線性度參差不齊,不能直接采用比例關系将電阻觸摸晶片STMPE811的傳回
值轉換成實際的坐标。比如我們操作的顯示屏分辨率是800*480,電阻觸摸晶片采用STMPE811(12位ADC,觸摸值範圍0-4095),獲得目前的觸摸值是(1024, 2048),按照比例關系轉換成坐标值就是(1024*800/4096,2048*800/4096),即(200,400)。采用這種方法效果不好,容易出現點選不準确的問題。
鑒于此原因,需要通過觸摸校準在ADC數值和顯示屏分辨率之間建立一個新的線性關系,簡單的說就是由比例關系y = ax更新為y = ax + b。如果有了新的觸摸ADC數值,代入這個線性關系裡面就可以得到目前實際的坐标值,觸摸校準的作用就在這裡了。
具體實作原理圖如下:
在左上角和右下角分别設定兩個坐标點(LcdX0, LcdY0)和(LcdX1, LcdY1),然後讓使用者去點選,會得到兩組ADC數值(AdcX0,AdcY0)和(AdcX1, AdcY1)。
根據這四個坐标點,可以建立兩組方程,一個X軸的,一個Y軸。
- 将數值(AdcX0, LcdX0)和(AdcX1, LcdX1)代入方程y = ax + b得到X軸方程 :y = (x - AdcX0)*(LcdX1 - LcdX0)/(AdcX1 - AdcX0) + LcdX0。
- 将數值(AdcY0, LcdY0)和(AdcY1, LcdY1)代入方程y = ax + b得到Y軸方程y = (x - AdcY0)*(LcdY1 - LcdY0)/(AdcY1 - AdcX0) + LcdY0。
後面采集到的ADC數值直接代入上面公式就可以得到校準後的實體坐标值(實際的分辨率坐标)。
5.5.4 電阻屏觸摸校準原理(4點)
4點觸摸校準實作,略複雜,實作原理如下(如果了解起來麻煩的話,會用就行,一般情況下2點校準就行):
在LCD的左上角,右上角,左下角和右下角分别标坐标點(LcdX1, LcdY1),(LcdX4, LcdY4),(LcdX3, LcdY3)和(LcdX2, LcdY2)。然後讓使用者去點選,會得到四組ADC數值(AdcX1, AdcY1),(AdcX4, AdcY4),(AdcX3, AdcY3)和(AdcX2, AdcY2)。
計算X軸:
1、将數值(AdcX1,AdcY1)和(AdcX2, AdcY2)代入方程y = ax + b ,得到一組方程 y = (x - AdcX1)*(AdcY2- AdcY1)/(AdcX2- AdcX1) + AdcY1
2、這裡将AdcX2用AdcX3替換,那麼坐标方程就變為 y = (x - AdcX1)*(AdcY2- AdcY1)/(AdcX3- AdcX1) + AdcY1。
3、同理,将AdcX1用AdcX4替換,那麼坐标方程就變為 y = (x - AdcX4)*(AdcY2- AdcY1)/(AdcX3- AdcX4) + AdcY1。那麼将采集的X值代入上面兩個方程會得到兩個數值,假設數值是x1和x2。
4、再将(x1, LcdX1))和(x2, LcdX2)代入方程y = ax + b得到一組方程 y = (x - x1)*(LcdX2- LcdX1)/(x2- x1) + LcdX1。
将采集的X軸ADC數值再次代入這個方程就得到了最終的實體坐标(實際的分辨率坐标)。
計算Y軸:
1、将數值(AdcX1, AdcY1)和(AdcX2, AdcY2)代入方程y = ax + b得到一組方程 y = (x - AdcX1)*(AdcY2- AdcY1)/(AdcX2- AdcX1) + AdcY1
2、這裡将AdcY2用AdcY4替換,那麼坐标方程就變為 y = (x - AdcX1)*(AdcY4- AdcY1)/(AdcX2- AdcX1) + AdcY1
3、同理,将AdcX1用AdcX3替換,那麼坐标方程就變為 y = (x - AdcX3)*(AdcY2- AdcY1)/(AdcX2- AdcX3) + AdcY1那麼将采集的X值代入上面兩個方程會得到兩個數值,假設數值是x1和x2。
4、再将(x1, LcdY1))和(x2, LcdY2)代入方程y = ax + b得到一組方程 y = (x - x1)*(LcdY2- LcdY1)/(x2- x1) + LcdY1。
将采集的Y軸ADC數值再次代入這個方程就得到了最終的實體坐标(實際的分辨率坐标)。
5.5.5 電阻屏觸摸校準的實作
對2點和4點觸摸校準原理有所了解後,再看代碼部分就比較好了解了:
1. /*
2. ******************************************************************************************************
3. * 函 數 名: TOUCH_Calibration
4. * 功能說明: 觸摸屏校準
5. * 形 參: _PointCount : 校準點數,2 或 4.
6. * 返 回 值: 無
7. ******************************************************************************************************
8. */
9. void TOUCH_Calibration(uint8_t _PointCount)
10. {
11. uint16_t usAdcX;
12. uint16_t usAdcY;
13. uint8_t usCount;
14. uint8_t I;
15. uint32_t n;
16.
17. /* 校準點數,2點或4點 */
18. if (_PointCount == 4)
19. {
20. g_tTPParam.CalibPointCount = 4;
21. }
22. else
23. {
24. g_tTPParam.CalibPointCount = 2;
25. }
26.
27. TOUCH_CelarFIFO(); /* 清除無效的觸摸事件 */
28.
29. for (I = 0; I < g_tTPParam.CalibPointCount; i++)
30. {
31. TOUCH_DispPoint(i); /* 顯示校準點 */
32.
33. TOUCH_WaitRelease(); /* 等待觸筆釋放 */
34.
35. usCount = 0;
36. for (n = 0; n < 500; n++)
37. {
38. usAdcX = TOUCH_ReadAdcX();
39. usAdcY = TOUCH_ReadAdcY();
40.
41. if (TOUCH_PressValid(usAdcX, usAdcY))
42. {
43. if (++usCount > 5)
44. {
45. /* 按壓有效, 儲存校準點ADC采樣值 */
46. if (I == 0)
47. {
48. g_tTPParam.usAdcX1 = usAdcX;
49. g_tTPParam.usAdcY1 = usAdcY;
50. }
51. else if (I == 1)
52. {
53. g_tTPParam.usAdcX2 = usAdcX;
54. g_tTPParam.usAdcY2 = usAdcY;
55. }
56. else if (I == 2)
57. {
58. g_tTPParam.usAdcX3 = usAdcX;
59. g_tTPParam.usAdcY3 = usAdcY;
60. }
61. else
62. {
63. g_tTPParam.usAdcX4 = usAdcX;
64. g_tTPParam.usAdcY4 = usAdcY;
65. }
66. break;
67. }
68. }
69. else
70. {
71. usCount = 0;
72. }
73. bsp_DelayMS(10);
74. }
75. if (n == 500)
76. {
77. return;
78. }
79. }
80.
81. TOUCH_WaitRelease(); /* 等待觸筆釋放 */
82.
83. /* 識别觸摸的 X, Y 和 顯示面闆的 X,Y 是否需要交換 */
84. g_tTPParam.XYChange = 0; /* 1表示X Y需要交換 */
85. if (LCD_GetHeight() < LCD_GetWidth())
86. {
87. if (TOUCH_Abs(g_tTPParam.usAdcX1 – g_tTPParam.usAdcX2) <
88. TOUCH_Abs(g_tTPParam.usAdcY1 – g_tTPParam.usAdcY2))
89. {
90. g_tTPParam.XYChange = 1;
91. }
92. }
93. else
94. {
95. if (TOUCH_Abs(g_tTPParam.usAdcX1 – g_tTPParam.usAdcX2) >
96. TOUCH_Abs(g_tTPParam.usAdcY1 – g_tTPParam.usAdcY2))
97. {
98. g_tTPParam.XYChange = 1;
99. }
100. }
101.
102. g_tTPParam.usLcdX1 = TP_X1;
103. g_tTPParam.usLcdY1 = TP_Y1;
104. g_tTPParam.usLcdX2 = TP_X2;
105. g_tTPParam.usLcdY2 = TP_Y2;
106. g_tTPParam.usLcdX3 = TP_X3;
107. g_tTPParam.usLcdY3 = TP_Y3;
108. g_tTPParam.usLcdX4 = TP_X4;
109. g_tTPParam.usLcdY4 = TP_Y4;
110.
111. /* 在最後一步,将校準參數儲存入Flash 或者EEPROM */
112. TOUCH_SaveParam();
113. }
複制
下面将程式設定中的關鍵地方做個闡釋:
- 第18-25行,用于标記是4點觸摸校準還是2點觸摸校準。
- 第31行,顯示觸摸校準點,2點觸摸校準的話,顯示左上角和右下角的校準點位置。4點觸摸校準的話,顯示左上角,右上角,左下角和右下角的校準點位置。
- 第33行,用于等待觸摸筆釋放,當校準完畢1個點後,等待釋放時使用。
- 第35-79行,讀取500次X軸和Y軸的ADC數值,每10ms讀取1次,每個觸摸點的最大讀取時間就是5秒。如果5秒内還沒有觸摸就會進入到第77行直接退出觸摸校準。
- 第41行檢測到有按下的話,會連續讀取5次,確定已經按下了,然後标記本次按下得到的ADC數值。根據執行的是4點觸摸校準還是2點觸摸校準,這個操作會執行4次或者2次。
- 第84-100行,這裡涉及到一個知識點,即X軸鏡像,Y軸鏡像和XY交換的處理辦法,詳情在此貼進行了說明:http://www.armbbs.cn/forum.php?mod=viewthread&tid=93300 。
- 第112行,将校準後的參數存儲到EEPROM裡面,下次開發闆上電可以直接從EEPROM裡面讀取校準參數。
5.5.6 電阻屏觸摸ADC值轉實體坐标
電阻屏觸摸ADC值轉實體坐标的公式就是由前面5.3和5.4小節而來。
1. /*
2. ******************************************************************************************************
3. * 函 數 名: CalTwoPoint
4. * 功能說明: 根據2點直線方程,計算Y值
5. * 形 參: 2個點的坐标和x輸入量
6. * 返 回 值: x對應的y值
7. ******************************************************************************************************
8. */
9. static int32_t CalTwoPoint(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x)
10. {
11. return y1 + ((int32_t)(y2 - y1) * (x - x1)) / (x2 - x1);
12. }
13.
14. /*
15. ******************************************************************************************************
16. * 函 數 名: TOUCH_TransX
17. * 功能說明: 将觸摸ADC值轉換為像素坐标
18. * 形 參: 無
19. * 返 回 值: X 坐标值,允許負值
20. ******************************************************************************************************
21. */
22. static int16_t TOUCH_TransX(uint16_t _usAdcX, uint16_t _usAdcY)
23. {
24. if (g_tTPParam.CalibPointCount == 2)
25. {
26. uint16_t x;
27. int32_t y;
28.
29. if (g_tTPParam.XYChange == 0)
30. {
31. x = _usAdcX;
32. if (x == 0)
33. {
34. y = 0;
35. }
36. else
37. {
38. //y = CalTwoPoint(g_tTPParam.usAdcX1, TP_X1, g_tTPParam.usAdcX2, TP_X2, x);
39. y = CalTwoPoint(g_tTPParam.usAdcX1, g_tTPParam.usLcdX1, g_tTPParam.usAdcX2,
40. g_tTPParam.usLcdX2, x);
41. }
42. }
43. else
44. {
45. x = _usAdcY;
46. if (x == 0)
47. {
48. y = 0;
49. }
50. else
51. {
52. //y = CalTwoPoint(g_tTPParam.usAdcY1, TP_X1, g_tTPParam.usAdcY2, TP_X2, x);
53. y = CalTwoPoint(g_tTPParam.usAdcY1, g_tTPParam.usLcdX1, g_tTPParam.usAdcY2,
54. g_tTPParam.usLcdX2, x);
55. }
56. }
57. return y;
58. }
59. else /* 4點校準 */
60. {
61. uint16_t x, x1, x2;
62. int32_t y;
63.
64. if (g_tTPParam.XYChange == 0) /* X Y 坐标不交換 */
65. {
66. x = _usAdcX;
67.
68. /* 根據 Y ADC 實時計算直線方程的參考點x1, x2
69. if _usAdcY = usAdcY1 then 取點 = (AdcX1, TP_X1, AdcX4, TP_X4, _usAdcY)
70. if _usAdcY = usAdcY2 then 取點 = (AdcX3, TP_X3, AdcX2, TP_X2, _usAdcY)
71.
72. 其中 TP_X1 = TP_X3; TP_X4 = TP_X1 , 這是程式設定的校準位置的像素坐标, 是固定的。
73. 我們僅需要動态計算對第1個和第3個參數。同樣采用2點直線方程計算。
74. */
75. x1 = CalTwoPoint(g_tTPParam.usAdcY1, g_tTPParam.usAdcX1, g_tTPParam.usAdcY2,
76. g_tTPParam.usAdcX3, _usAdcY);
77. x2 = CalTwoPoint(g_tTPParam.usAdcY1, g_tTPParam.usAdcX4, g_tTPParam.usAdcY2,
78. g_tTPParam.usAdcX2, _usAdcY);
79. }
80. else /* X Y 坐标交換 */
81. {
82. x = _usAdcY;
83.
84. /* 根據 X ADC 實時計算直線方程的參考點x1, x2
85. if _usAdcX = usAdcX1 then 取點 = (AdcY1, TP_X1, AdcY4, TP_X4, _usAdcX)
86. if _usAdcX = usAdcX2 then 取點 = (AdcY3, TP_X3, AdcY2, TP_X2, _usAdcX)
87.
88. 其中 TP_X1 = TP_X3; TP_X4 = TP_X1 , 這是程式設定的校準位置的像素坐标, 是固定的。
89. 我們僅需要動态計算對第1個和第3個參數。同樣采用2點直線方程計算。
90. */
91. x1 = CalTwoPoint(g_tTPParam.usAdcX1, g_tTPParam.usAdcY1, g_tTPParam.usAdcX2,
92. g_tTPParam.usAdcY3, _usAdcX);
93. x2 = CalTwoPoint(g_tTPParam.usAdcX1, g_tTPParam.usAdcY4, g_tTPParam.usAdcX2,
94. g_tTPParam.usAdcY2, _usAdcX);
95. }
96.
97. if (x == 0)
98. {
99. y = 0;
100. }
101. else
102. {
103. /* 根據2點直線方程,計算坐标 */
104. //y = CalTwoPoint(x1, TP_X1, x2, TP_X2, x);
105. y = CalTwoPoint(x1, g_tTPParam.usLcdX1, x2, g_tTPParam.usLcdX2, x);
106. }
107. return y;
108. }
109. }
110.
111. /*
112. ******************************************************************************************************
113. * 函 數 名: TOUCH_TransY
114. * 功能說明: 将觸摸ADC值轉換為像素坐标
115. * 形 參: 無
116. * 返 回 值: Y 坐标值,允許負值
117. ******************************************************************************************************
118. */
119. static int16_t TOUCH_TransY(uint16_t _usAdcX, uint16_t _usAdcY)
120. {
121. if (g_tTPParam.CalibPointCount == 2) /* 2點校準 */
122. {
123. /* 類似函數TOUCH_TransX,省略未貼出 */
124. }
125. else /* 4點校準 */
126. {
127. /* 類似函數TOUCH_TransX,省略未貼出 */
128. }
129. }
複制
下面将程式設計中幾個關鍵地方做個闡釋:
- 第9-12行,y =ax+b類型的直線方程,根據前四個參數輸入的兩個坐标點可以确定一條直線,然後輸入第5個參數x,可以得到此坐标點對應的y值。
- 第22行,函數TOUCH_TransX的作用是将X軸采集的ADC值轉換為實體坐标值。
- 第24-58行,用于處理2點觸摸校準,對應的公式就是本章5.4小節的内容。
- 第59-108行,用于處理4點觸摸校準,對應的公式就是本章5.5小節的内容。
- 第119-129行,函數TOUCH_TransY的作用是将Y軸采集的ADC值轉換為實體坐标值。
這裡注意g_tTPParam.XYChange = 1情況的處理,之是以會有這種情況,詳情看此貼:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=93300 。
5.5.7 電阻觸摸的使用方法
電阻觸摸的使用主要分為三步:
- 第1步,使能觸摸,在bsp.c檔案的函數bsp_Init裡面實作。
/*
*********************************************************************************************************
* 函 數 名: bsp_Init
* 功能說明: 初始化所有的硬體裝置。該函數配置CPU寄存器和外設的寄存器并初始化一些全局變量。隻需要調用一次
* 形 參:無
* 返 回 值: 無
*********************************************************************************************************
*/
void bsp_Init(void)
{
/* 省略未寫 */
bsp_InitI2C(); /* 初始化I2C總線 */
TOUCH_InitHard(); /* 初始化觸摸晶片,LCD面闆型号的檢查也在此函數,是以要在函數LCD_InitHard前調用 */
LCD_InitHard(); /* 初始化LCD */
}
複制
- 第2步,每毫秒調用1次觸摸掃描。
/*
*********************************************************************************************************
* 函 數 名: bsp_RunPer1ms
* 功能說明: 該函數每隔1ms被Systick中斷調用1次。詳見 bsp_timer.c的定時中斷服務程式。一些需要周期性處理
* 的事務可以放在此函數。比如:觸摸坐标掃描。
* 形 參: 無
* 返 回 值: 無
*********************************************************************************************************
*/
void bsp_RunPer1ms(void)
{
TOUCH_Scan(); /* 觸摸屏 */
}
複制
- 第3步,使用者程式裡面采用下面的架構進行觸摸消息處理。
TOUCH_DOWN表示按下消息。
TOUCH_MOVE表示觸摸移動消息。
TOUCH_RELEASE表示觸摸釋放消息。
根據這幾個消息,使用者可以在程式裡面判斷目前擷取的實體坐标值是否在設定的區域内來執行觸摸操作。
int16_t tpX, tpY;
uint8_t ucTouch; /* 觸摸事件 */
ucTouch = TOUCH_GetKey(&tpX, &tpY); /* 讀取觸摸事件 */
if (ucTouch != TOUCH_NONE)
{
switch (ucTouch)
{
case TOUCH_DOWN: /* 觸筆按下事件 */
/* 在觸筆所在位置顯示一個小圈 */
if ((tpX > 0) && (tpY > 0))
{
}
break;
case TOUCH_MOVE: /* 觸筆移動事件 */
/* 實時重新整理觸摸ADC采樣值和轉換後的坐标 */
{
/* 在觸筆所在位置顯示一個小圈 */
if ((tpX > 0) && (tpY > 0))
{
}
}
break;
case TOUCH_RELEASE: /* 觸筆釋放事件 */
/* 在觸筆所在位置顯示一個小圈 */
if ((tpX > 0) && (tpY > 0))
{
}
break;
}
}
複制
5.6 電容觸摸驅動設計
電容觸摸相比電阻觸摸就要簡單很多了,因為電容觸摸不需要做觸摸校準,而且用的是觸摸闆和觸摸晶片一體的,也不需要做寄存器初始化配置,上電後直接讀取參數即可。
由于GT811已經停産,這裡重點把GT911和FT5X06做個說明。
5.6.1 電容屏觸摸IC---FT5X06
電容觸摸IC是支援多點觸摸的,FT5X06支援多達10點觸摸同時按下,并提供了I2C和SPI兩種
通信接口方式,開發闆使用的是I2C通信接口。更多相關知識學習可以在這裡下載下傳FT5X06資料手冊和應用手冊:http://www.armbbs.cn/forum.php?mod=viewthread&tid=16461 。
注意,這個晶片傳回的就是實際的坐标值,比如顯示屏的分辨率是800*480,那麼傳回的就是在這個分辨率範圍内的實際坐标,然後通過函數TOUCH_PutKey将FT5X06讀出的實際坐标值存儲到FIFO中即可,具體實作代碼如下:
1. /*
2. ******************************************************************************************************
3. * 函 數 名: FT5X06_Scan
4. * 功能說明: 讀取觸摸資料。讀取全部的資料。放在主程式 bsp_Idle()中執行
5. * 形 參: 無
6. * 返 回 值: 無
7. ******************************************************************************************************
8. */
9. void FT5X06_Scan(void)
10. {
11. uint8_t buf[CFG_POINT_READ_BUF];
12. uint8_t i;
13. static uint8_t s_tp_down = 0;
14. uint16_t x, y;
15. static uint16_t x_save, y_save;
16. static uint8_t s_count = 0;
17.
18. if (g_tFT5X06.Enable == 0)
19. {
20. return;
21. }
22.
23. /* 10ms 執行一次 */
24. if (g_tFT5X06.TimerCount < 10)
25. {
26. return;
27. }
28.
29. g_tFT5X06.TimerCount = 0;
30.
31. #if 1 /* 方案1: 檢測INT引腳電平. */
32. if (TOUCH_PenInt() == 0)
33. {
34. #else /* 方案2:不用INT引腳,讀狀态寄存器 */
35. FT5X06_ReadReg(2, buf, 1);
36. if ((buf[0] & 0x07) == 0)
37. {
38. #endif
39. /* 持續按下時,INT電平是脈沖信号。每隔18ms出現1個寬度4ms的高電平。 */
40. if (s_tp_down == 1)
41. {
42. if (++s_count > 2)
43. {
44. s_count = 0;
45. s_tp_down = 0;
46. TOUCH_PutKey(TOUCH_RELEASE, x_save, y_save);
47. }
48. }
49. return;
50. }
51. s_count = 0;
52.
53. /* 有觸摸,讀取完整的資料 */
54. FT5X06_ReadReg(0, buf, CFG_POINT_READ_BUF);
55.
56. g_tFT5X06.Count = buf[2] & 0x07;
57. if (g_tFT5X06.Count > FT5X06_TOUCH_POINTS)
58. {
59. g_tFT5X06.Count = FT5X06_TOUCH_POINTS;
60. }
61.
62. g_tFT5X06.Count = 0;
63. for (i = 0; i < FT5X06_TOUCH_POINTS; i++)
64. {
65. uint8_t pointid;
66.
67. pointid = (buf[5 + 6*i]) >> 4;
68. if (pointid >= 0x0f)
69. {
70. break;
71. }
72. else
73. {
74. g_tFT5X06.Count++;
75. g_tFT5X06.X[i] = (int16_t)(buf[3 + 6*i] & 0x0F)<<8 | (int16_t)buf[4 + 6*i];
76. g_tFT5X06.Y[i] = (int16_t)(buf[5 + 6*i] & 0x0F)<<8 | (int16_t)buf[6 + 6*i];
77. g_tFT5X06.Event[i] = buf[0x3 + 6*i] >> 6;
78. g_tFT5X06.id[i] = (buf[5 + 6*i])>>4;
79. }
80. }
81.
82. /* 檢測按下 */
83. {
84. if (g_tFT5X06.ChipID == 0x55) /* 4.3寸 480 * 272 */
85. {
86. x = g_tFT5X06.Y[0];
87. y = g_tFT5X06.X[0];
88.
89. /* 判斷值域 */
90. if (x > 479)
91. {
92. x = 479;
93. }
94.
95. if (y > 271)
96. {
97. y = 271;
98. }
99. }
100. else if (g_tFT5X06.ChipID == 0x0A) /* 5.0寸 800 * 480 */
101. {
102. x = g_tFT5X06.X[0];
103. y = g_tFT5X06.Y[0];
104.
105. /* 判斷值域 */
106. if (x > 799)
107. {
108. x = 799;
109. }
110. if (y > 479)
111. {
112. y = 479;
113. }
114. }
115. else /* id == 0x06 表示7寸電容屏(FT晶片) */
116. {
117. x = g_tFT5X06.X[0];
118. y = g_tFT5X06.Y[0];
119.
120. /* 判斷值域 */
121. if (x > 799)
122. {
123. x = 799;
124. }
125. if (y > 479)
126. {
127. y = 479;
128. }
129. }
130. }
131.
132. if (s_tp_down == 0)
133. {
134. s_tp_down = 1;
135.
136. TOUCH_PutKey(TOUCH_DOWN, x, y);
137. }
138. else
139. {
140. TOUCH_PutKey(TOUCH_MOVE, x, y);
141. }
142. x_save = x; /* 儲存坐标,用于釋放事件 */
143. y_save = y;
144.
145. #if 0
146. for (i = 0; i < CFG_POINT_READ_BUF; i++)
147. {
148. printf("%02X ", buf[i]);
149. }
150. printf("\r\n");
151. #endif
152.
153. #if 0 /* 列印5個坐标點資料 */
154. printf("(%5d,%5d,%3d,%3d) ", g_tFT5X06.X[0], g_tFT5X06.Y[0], g_tFT5X06.Event[0],
155. g_tFT5X06.id[0]);
156. printf("(%5d,%5d,%3d,%3d) ", g_tFT5X06.X[1], g_tFT5X06.Y[1], g_tFT5X06.Event[1],
157. g_tFT5X06.id[1]);
158. printf("(%5d,%5d,%3d,%3d) ", g_tFT5X06.X[2], g_tFT5X06.Y[2], g_tFT5X06.Event[2],
159. g_tFT5X06.id[2]);
160. printf("(%5d,%5d,%3d,%3d) ", g_tFT5X06.X[3], g_tFT5X06.Y[3], g_tFT5X06.Event[3],
161. g_tFT5X06.id[3]);
162. printf("(%5d,%5d,%3d,%3d) ", g_tFT5X06.X[4], g_tFT5X06.Y[4], g_tFT5X06.Event[4],
163. g_tFT5X06.id[4]);
164. printf("\r\n");
165. #endif
166. }
複制
下面将程式設計中幾個關鍵地方做個闡釋:
- 第32行,通過判斷FT5X06的中斷輸出引腳的高低電平來判斷觸摸闆是否被按下,如果有觸摸,這個引腳輸出低電平,反之,輸出高電平。通過判斷這個引腳可以避免不必要的操作。
- 第35-36行,從寄存器2讀取一個資料,判斷是否有觸摸資料,這種方式就沒有直接判斷中斷引腳友善。
- 第40-50行,如果沒有觸摸資料,而且變量标志s_tp_down = 1(此變量等于1表示之前處于觸摸按下或者移動狀态,如果等于0表示之前處于未被觸摸狀态),那麼此時要通過函數TOUCH_PutKey存儲松手消息,這一步比較重要,切不可省略。如果變量标志s_tp_down = 0,直接退出即可。另外,特别注意一點,這裡是通過s_count變量連續記錄到兩次松手消息才執行。
- 第50-80行,如果有觸摸,将所有觸摸值全部記錄下來(由于支援多點觸摸,會有多個觸摸值)。
- 第83-130行,根據不同的分辨率的顯示屏,做值域範圍處理,防止超出範圍。
- 第132-137行,如果變量s_tp_down = 0表示之前處于未被觸摸狀态,這裡設定此變量為1,并通過函數TOUCH_PutKey存儲按下消息和目前的坐标值。
- 第138-141行,如果變量不等于0(其實這裡也就是1)表示之前處于按下狀态,此時觸摸處于移動狀态,這裡不用重複設定此變量了,但通過函數TOUCH_PutKey存儲按下消息和目前的坐标值。
- 第153-165行,這裡預留的條件編譯主要是友善調試階段使用。
5.6.2 電容屏觸摸IC---GT911
GT911支援多達5點觸摸同時按下,并提供了I2C通信接口方式,更多相關知識學習可以在這裡下載下傳GT911資料手冊:http://www.armbbs.cn/forum.php?mod=viewthread&tid=87205 。
GT911的使用基本跟FT5X06是一樣(注意,晶片GT911傳回的就是實際的坐标值,比如顯示屏的分辨率是800*480,那麼傳回的就是在這個分辨率範圍内的實際坐标,跟FT5X06也是一樣的),具體實作代碼如下(這裡沒有使用多點觸摸功能,僅存了一個觸摸值,一般情況下已經夠用):
1. /*
2. ******************************************************************************************************
3. * 函 數 名: GT911_Scan
4. * 功能說明: 讀取GT911觸摸資料。讀取全部的資料,需要 720us左右。放在 bsp_Idle()中執行
5. * 形 參: 無
6. * 返 回 值: 無
7. ******************************************************************************************************
8. */
9. void GT911_Scan(void)
10. {
11. uint8_t buf[48];
12. static uint8_t s_tp_down = 0;
13. uint16_t x, y;
14. static uint16_t x_save, y_save;
15. uint8_t clear_flag = 0;
16. static uint8_t s_count = 0;
17.
18. if (g_GT911.Enable == 0)
19. {
20. return;
21. }
22.
23. /* 10ms 執行一次 */
24. if (g_GT911.TimerCount < 10)
25. {
26. return;
27. }
28.
29. g_GT911.TimerCount = 0;
30.
31. #if 1 /* 方案1: 檢測INT引腳電平. */
32. if (TOUCH_PenInt() == 0)
33. {
34. #else /* 方案2:不用INT引腳,讀狀态寄存器 */
35. GT911_ReadReg(GT911_READ_XY_REG, buf, 1);
36. if (buf[0] == 0)
37. {
38. #endif
39. if (s_tp_down == 1)
40. {
41. if (++s_count > 1)
42. {
43. s_count = 0;
44. s_tp_down = 0;
45. TOUCH_PutKey(TOUCH_RELEASE, x_save, y_save);
46. }
47. }
48. return;
49. }
50. s_count = 0;
51.
52. #if 1 /* 一般應用隻讀1點 */
53. GT911_ReadReg(GT911_READ_XY_REG, buf, 8);
54. #else /* 讀5個觸摸點 */
55. GT911_ReadReg(GT911_READ_XY_REG, buf, 40);
56. #endif
57.
58. GT911_WriteReg(GT911_READ_XY_REG, &clear_flag, 1); /* 讀完坐标後必須寫0清除 */
59.
60. /*
61. 0x814E R/W Bufferstatus Large_Detect number of touch points
62. Bit7: Buffer status,1表示坐标(或按鍵)已經準備好,主要可以讀取;0表示未就緒,資料無效。
63. 當主要讀取完坐标後,必須通過I2C将此标志(或整個位元組)寫為0。
64. Bit4: HaveKey, 1表示有按鍵,0表示無按鍵(已經松鍵)。
65. Bit3~0: Number of touch points, 屏上的坐标點個數
66.
67. 0x814F R Point1 track id
68. 0x8150 R Point1Xl 觸摸點 1,X 坐标低 8 位
69. 0x8151 R Point1Xh 觸摸點 1,X 坐标高 8 位
70. 0x8152 R Point1Yl 觸摸點 1,Y 坐标低 8 位
71. 0x8153 R Point1Yh 觸摸點 1,Y 坐标高 8 位
72. 0x8154 R Point1 觸摸點 1,觸摸面積低 8 位
73. 0x8155 R Point1 觸摸點 1,觸摸面積高 8 位
74. 0x8156 ----
75.
76. 0x8157 R Point2 track id
77. 0x8158 R Point2Xl 觸摸點 2,X 坐标低 8 位
78. 0x8159 R Point2Xh 觸摸點 2,X 坐标高 8 位
79. 0x815A R Point2Yl 觸摸點 2,Y 坐标低 8 位
80. 0x815B R Point2Yh 觸摸點 2,Y 坐标高 8 位
81. 0x815C R Point2 觸摸點 2,觸摸面積低 8 位
82. 0x815D R Point2 觸摸點 2,觸摸面積高 8 位
83. 0x815E ----
84.
85. 0x815F R Point3 track id
86. 0x8160 R Point3Xl 觸摸點 3,X 坐标低 8 位
87. 0x8161 R Point3Xh 觸摸點 3,X 坐标高 8 位
88. 0x8162 R Point3Yl 觸摸點 3,Y 坐标低 8 位
89. 0x8163 R Point3Yh 觸摸點 3,Y 坐标高 8 位
90. 0x8164 R Point3 觸摸點 3,觸摸面積低 8 位
91. 0x8165 R Point3 觸摸點 3,觸摸面積高 8 位
92. 0x8166 ----
93.
94. 0x8167 R Point4 track id
95. 0x8168 R Point4Xl 觸摸點 4,X 坐标低 8 位
96. 0x8169 R Point4Xh 觸摸點 4,X 坐标高 8 位
97. 0x816A R Point4Yl 觸摸點 4,Y 坐标低 8 位
98. 0x816B R Point4Yh 觸摸點 4,Y 坐标高 8 位
99. 0x816C R Point4 觸摸點 4,觸摸面積低 8 位
100. 0x816D R Point4 觸摸點 4,觸摸面積高 8 位
101. 0x816E ----
102.
103. 0x816F R Point5 track id
104. 0x8170 R Point5Xl 觸摸點 5,X 坐标低 8 位
105. 0x8171 R Point5Xh 觸摸點 5,X 坐标高 8 位
106. 0x8172 R Point5Yl 觸摸點 5,Y 坐标低 8 位
107. 0x8173 R Point5Yh 觸摸點 5,Y 坐标高 8 位
108. 0x8174 R Point5 觸摸點 5,觸摸面積低 8 位
109. 0x8175 R Point5 觸摸點 5,觸摸面積高 8 位
110. 0x8176 --
111.
112. */
113. g_GT911.TouchpointFlag = buf[0];
114. g_GT911.Touchkeystate = buf[1];
115.
116. g_GT911.X0 = ((uint16_t)buf[3] << 8) + buf[2];
117. g_GT911.Y0 = ((uint16_t)buf[5] << 8) + buf[4];
118. g_GT911.P0 = ((uint16_t)buf[7] << 8) + buf[6];
119.
120. #if 0 /* 其餘4點一般不用 */
121. g_GT911.X1 = ((uint16_t)buf[9] << 8) + buf[10];
122. g_GT911.Y1 = ((uint16_t)buf[11] << 8) + buf[12];
123. g_GT911.P1 = ((uint16_t)buf[13] << 8) + buf[14];
124.
125. g_GT911.X2 = ((uint16_t)buf[17] << 8) + buf[16];
126. g_GT911.Y2 = ((uint16_t)buf[19] << 8) + buf[18];
127. g_GT911.P2 = ((uint16_t)buf[21] << 8) + buf[20];
128.
129. g_GT911.X3 = ((uint16_t)buf[24] << 8) + buf[23];
130. g_GT911.Y3 = ((uint16_t)buf[26] << 8) + buf[25];
131. g_GT911.P3 = ((uint16_t)buf[28] << 8) + buf[27];
132.
133. g_GT911.X4 = ((uint16_t)buf[31] << 8) + buf[30];
134. g_GT911.Y4 = ((uint16_t)buf[33] << 8) + buf[32];
135. g_GT911.P4 = ((uint16_t)buf[35] << 8) + buf[34];
136. #endif
137.
138. /* 檢測按下 */
139. {
140. /* 坐标轉換 :
141. 電容觸摸闆左下角是 (0,0); 右上角是 (479,799)
142. 需要轉到LCD的像素坐标 (左上角是 (0,0), 右下角是 (799,479)
143. */
144. {
145. x = g_GT911.X0;
146. y = g_GT911.Y0;
147.
148. if (x > 799)
149. {
150. x = 799;
151. }
152.
153. if (y > 479)
154. {
155. y = 479;
156. }
157. }
158. }
159.
160. if (s_tp_down == 0)
161. {
162. s_tp_down = 1;
163.
164. TOUCH_PutKey(TOUCH_DOWN, x, y);
165. }
166. else
167. {
168. TOUCH_PutKey(TOUCH_MOVE, x, y);
169. }
170. x_save = x; /* 儲存坐标,用于釋放事件 */
171. y_save = y;
172.
173. #if 0
174. {
175. uint8_t i;
176.
177. for (i = 0; i < 34; i++)
178. {
179. printf("%02X ", buf[i]);
180. }
181. printf("\r\n");
182.
183. printf("(%5d,%5d,%3d) ", g_GT911.X0, g_GT911.Y0, g_GT911.P0);
184. printf("(%5d,%5d,%3d) ", g_GT911.X1, g_GT911.Y1, g_GT911.P1);
185. printf("(%5d,%5d,%3d) ", g_GT911.X2, g_GT911.Y2, g_GT911.P2);
186. printf("(%5d,%5d,%3d) ", g_GT911.X3, g_GT911.Y3, g_GT911.P3);
187. printf("(%5d,%5d,%3d) ", x, y, g_GT911.P4);
188. printf("\r\n");
189. }
190. #endif
191. }
複制
下面将程式設計中幾個關鍵地方做個闡釋:
- 第18-21行,使能标志,在檔案bsp_touch.c檔案裡面有一個函數bsp_DetectLcdType專門檢測不同的觸摸IC,如果是GT911的話,變量g_tFT5X06.Enable會被置1。
- 第24-27行,表示函數GT911_Scan每10ms執行1次。
- 第32行,通過判斷GT911的中斷輸出引腳的高低電平來判斷觸摸闆是否被按下,如果有觸摸,這個引腳輸出低電平,反之,輸出高電平。通過判斷這個引腳可以避免不必要的操作。
- 第35-36行,從寄存器2讀取一個資料,判斷是否有觸摸資料,這種方式就沒有直接判斷中斷引腳友善。
- 第39-49行,如果沒有觸摸資料,而且變量标志s_tp_down = 1(此變量等于1表示之前處于觸摸按下或者移動狀态,如果等于0表示之前處于未被觸摸狀态),那麼此時要通過函數TOUCH_PutKey存儲松手消息,這一步比較重要,切不可省略。如果變量标志s_tp_down = 0,直接退出即可。另外,特别注意一點,這裡是通過s_count變量連續記錄到兩次松手消息才執行。
- 第52-136行,僅讀取一組。這裡沒有使用多點觸摸功能,僅存了一個觸摸值,一般情況下已經夠用。
- 第138-158行,對讀取的值域範圍處理,防止超出範圍。
- 第160-165行,如果變量s_tp_down = 0表示之前處于未被觸摸狀态,這裡設定此變量為1,并通過函數TOUCH_PutKey存儲按下消息和目前的坐标值。
- 第166-169行,如果變量不等于0(其實這裡也就是1)表示之前處于按下狀态,此時觸摸處于移動狀态,這裡不用重複設定此變量了,但通過函數TOUCH_PutKey存儲按下消息和目前的坐标值。
- 第173-190行,這裡預留的條件編譯主要是友善調試階段使用。
5.6.3 電容觸摸的使用方法
電容觸摸的使用主要分為三步:
- 第1步,使能觸摸,在bsp.c檔案的函數bsp_Init裡面實作。
/*
*********************************************************************************************************
* 函 數 名: bsp_Init
* 功能說明: 初始化所有的硬體裝置。該函數配置CPU寄存器和外設的寄存器并初始化一些全局變量。隻需要調用一次
* 形 參:無
* 返 回 值: 無
*********************************************************************************************************
*/
void bsp_Init(void)
{
/* 省略未寫 */
bsp_InitI2C(); /* 初始化I2C總線 */
TOUCH_InitHard(); /* 初始化觸摸晶片,LCD面闆型号的檢查也在此函數,是以要在函數LCD_InitHard前調用 */
LCD_InitHard(); /* 初始化LCD */
}
複制
- 第2步,每毫秒調用1次觸摸掃描。對于電容屏來說,這個函數主要提供了一個計數器功能。并且需要使用者在主任務裡面一直調用函數bsp_Idle,這個函數主要實作電容觸摸屏掃描。
/*
*********************************************************************************************************
* 函 數 名: bsp_RunPer1ms
* 功能說明: 該函數每隔1ms被Systick中斷調用1次。詳見 bsp_timer.c的定時中斷服務程式。一些需要周期性處理
* 的事務可以放在此函數。比如:觸摸坐标掃描。
* 形 參: 無
* 返 回 值: 無
*********************************************************************************************************
*/
void bsp_RunPer1ms(void)
{
TOUCH_Scan(); /* 觸摸屏 */
}
/*
*********************************************************************************************************
* 函 數 名: bsp_Idle
* 功能說明: 空閑時執行的函數。一般主程式在for和while循環程式體中需要插入 CPU_IDLE() 宏來調用本函數。
* 本函數預設為空操作。使用者可以添加喂狗、設定CPU進入休眠模式的功能。
* 形 參: 無
* 返 回 值: 無
*********************************************************************************************************
*/
void bsp_Idle(void)
{
/* --- 喂狗 */
/* --- 讓CPU進入休眠,由Systick定時中斷喚醒或者其他中斷喚醒 */
/* 例如 emWin 圖形庫,可以插入圖形庫需要的輪詢函數 */
//GUI_Exec();
/* 例如 uIP 協定,可以插入uip輪詢函數 */
TOUCH_CapScan();
}
複制
- 第3步,使用者程式裡面采用下面的架構進行觸摸消息處理。
TOUCH_DOWN表示按下消息。
TOUCH_MOVE表示觸摸移動消息。
TOUCH_RELEASE表示觸摸釋放消息。
根據這幾個消息,使用者可以在程式裡面判斷目前擷取的實體坐标值是否在設定的區域内來執行觸摸操作。
int16_t tpX, tpY;
uint8_t ucTouch; /* 觸摸事件 */
ucTouch = TOUCH_GetKey(&tpX, &tpY); /* 讀取觸摸事件 */
if (ucTouch != TOUCH_NONE)
{
switch (ucTouch)
{
case TOUCH_DOWN: /* 觸筆按下事件 */
/* 在觸筆所在位置顯示一個小圈 */
if ((tpX > 0) && (tpY > 0))
{
}
break;
case TOUCH_MOVE: /* 觸筆移動事件 */
/* 實時重新整理觸摸ADC采樣值和轉換後的坐标 */
{
/* 在觸筆所在位置顯示一個小圈 */
if ((tpX > 0) && (tpY > 0))
{
}
}
break;
case TOUCH_RELEASE: /* 觸筆釋放事件 */
/* 在觸筆所在位置顯示一個小圈 */
if ((tpX > 0) && (tpY > 0))
{
}
break;
}
}
複制
5.7 不同觸摸IC的識别
由于V6開發闆要做不同顯示屏的自适應,是以關聯了多個檔案。自适應方法也比較簡單,因為觸摸晶片GT911,GT811,FT5X06和STMPE811都是I2C接口,通過I2C位址就區分開了,具體代碼如下:
1. /*
2. ******************************************************************************************************
3. * 函 數 名: bsp_DetectLcdType
4. * 功能說明: 通過I2C觸摸晶片,識别LCD模組類型。結果存放在全局變量 g_LcdType 和 g_TouchType
5. * 形 參: 無
6. * 返 回 值: 無
7. ******************************************************************************************************
8. */
9. void bsp_DetectLcdType(void)
10. {
11. uint8_t i;
12.
13. g_TouchType = 0xFF;
14. g_LcdType = 0xFF;
15.
16. bsp_DelayUS(5000);
17.
18. touch_printf("正在識别觸摸屏型号\r\n");
19.
20. /* 50ms,等待GT811複位就緒,才能探測GT811晶片 ID */
21. for (i = 0; i < 5; i++)
22. {
23. /*
24. GT811電容觸摸闆和GT911的I2C位址相同
25. 一般就 0x28 、 0xBA 兩種。
26. 通過讀取觸摸IC的晶片ID來識别。
27. */
28. if (i2c_CheckDevice(0x28) == 0)
29. {
30. uint32_t id;
31.
32. bsp_DelayUS(500);
33. g_GT911.i2c_addr = 0x28;
34. id = GT911_ReadID();
35. if (id == 0x00313139)
36. {
37. g_GT911.i2c_addr = 0x28;
38. g_TouchType = CT_GT911;
39. g_LcdType = LCD_70_800X480;
40. touch_printf("檢測到7.0寸電容觸摸屏GT911(0x28) 800x480\r\n");
41. }
42. else /* GT811 */
43. {
44. g_GT811.i2c_addr = 0x28;
45. g_TouchType = CT_GT811;
46. g_LcdType = LCD_70_800X480;
47. touch_printf("檢測到7.0寸電容觸摸屏GT811(0x28) 800x480\r\n");
48. }
49. break;
50. }
51.
52. if (i2c_CheckDevice(0xBA) == 0)
53. {
54. uint32_t id;
55.
56. bsp_DelayUS(500);
57. g_GT911.i2c_addr = 0xBA;
58. id = GT911_ReadID();
59. if (id == 0x00313139)
60. {
61. g_GT911.i2c_addr = 0xBA;
62. g_TouchType = CT_GT911;
63. g_LcdType = LCD_70_800X480;
64. touch_printf("檢測到7.0寸電容觸摸屏GT911(0xBA) 800x480\r\n");
65. }
66. else /* GT811 */
67. {
68. g_GT811.i2c_addr = 0xBA;
69. g_TouchType = CT_GT811;
70. g_LcdType = LCD_70_800X480;
71. touch_printf("檢測到7.0寸電容觸摸屏GT811(0xBA) 800x480\r\n");
72. }
73. break;
74. }
75.
76. /* FT系列電容觸摸: 4.3寸id = 0x55 5.0寸id = 0x0A 7.0寸id = 0x06 */
77. if (i2c_CheckDevice(FT5X06_I2C_ADDR) == 0)
78. {
79. uint8_t id;
80.
81. bsp_DelayUS(50000); /* 延遲50ms */
82. id = FT5X06_ReadID();
83. if (id == 0x55)
84. {
85. g_TouchType = CT_FT5X06;
86. g_LcdType = LCD_43_480X272;
87. touch_printf("檢測到4.3寸電容觸摸屏\r\n");
88. }
89. else if (id == 0x0A)
90. {
91. g_TouchType = CT_FT5X06;
92. g_LcdType = LCD_50_800X480;
93. touch_printf("檢測到5.0寸電容觸摸屏\r\n");
94. }
95. else /* id == 0x06 表示7寸電容屏(FT晶片) */
96. {
97. g_TouchType = CT_FT5X06;
98. g_LcdType = LCD_70_800X480;
99. touch_printf("檢測到7.0寸電容觸摸屏FT\r\n");
100. }
101. break;
102. }
103.
104. /* 電阻觸摸闆 */
105. if (i2c_CheckDevice(STMPE811_I2C_ADDRESS) == 0)
106. {
107. /*
108. 0 = 4.3寸屏(480X272)
109. 1 = 5.0寸屏(480X272)
110. 2 = 5.0寸屏(800X480)
111. 3 = 7.0寸屏(800X480)
112. 4 = 7.0寸屏(1024X600)
113. 5 = 3.5寸屏(480X320)
114. */
115. uint8_t id;
116.
117. g_TouchType = CT_STMPE811; /* 觸摸類型 */
118.
119. STMPE811_InitHard(); /* 必須先配置才能讀取ID */
120.
121. id = STMPE811_ReadIO(); /* 識别LCD硬體類型 */
122.
123. touch_printf("檢測到電阻觸摸屏, id = %d\r\n", id);
124. switch (id)
125. {
126. case 0:
127. g_LcdType = LCD_43_480X272;
128. break;
129.
130. case 1:
131. g_LcdType = LCD_50_480X272;
132. break;
133.
134. case 2:
135. g_LcdType = LCD_50_800X480;
136. break;
137.
138. case 3:
139. g_LcdType = LCD_70_800X480;
140. break;
141.
142. case 4:
143. g_LcdType = LCD_70_1024X600;
144. break;
145.
146. case 5:
147. g_LcdType = LCD_35_480X320;
148. break;
149.
150. default:
151. g_LcdType = LCD_35_480X320;
152. break;
153. }
154. break;
155. }
156.
157. bsp_DelayMS(10);
158. }
159.
160. if (i == 5)
161. {
162. touch_printf("未識别出顯示子產品\r\n");
163. }
164. }
165.
複制
下面将程式設計中幾個關鍵地方做個闡釋:
- 第21行,上電後,這幾款傳感器一定要延遲一段時間才可以正常讀取ID,是以這裡做了一個for循環做檢測。
- 第28-74行,GT811電容觸摸闆和GT911的I2C位址相同,一般就0x28和0xBA兩種。程式裡面對這兩個位址都做了檢測,然後通過ID區分是GT811還是GT911。
- 第77-102行,FT5X06電容觸摸闆檢測,通過ID區分不同分辨率。
- 第105-158行,STMPE811電阻觸摸屏檢測,通過函數STMPE811_ReadIO傳回的IO狀态可以識别3.5寸480*320分辨率顯示屏,4.3寸480*272分辨率顯示屏,5寸480*272和800*480分辨率顯示屏以及7寸800*480和1024*600分辨率顯示屏。
- 第160-163行,用于調試。
5.8 LCD觸摸移植和使用
LCD驅動的移植與第4章4.7小節相同,這裡重點說下觸摸部分的移植。
通過前面的講解,移植觸摸驅動到自己的闆子上,最簡單的辦法是将開發闆上與觸摸相關的檔案全部移植過來,然後在這些檔案的基礎上進行修改。下面分兩種情況進行說明:
- 電容屏觸摸的移植比較簡單,如果使用者用的觸摸IC跟開發闆一樣,直接拿來用即可,如果不一樣,需要先将觸摸IC的驅動實作,然後按照開發闆提供的GT811、GT911和FT5X06的觸摸掃描函數,照葫蘆畫瓢實作一個即可。
- 電阻屏的移植稍麻煩些,如果使用者用的觸摸IC跟開發闆一樣,直接拿來用即可,如果不一樣,需要先将觸摸IC的驅動實作,然後仿照bsp_ts_stmpe811.c檔案提供觸摸按下狀态函數,X軸,Y軸的ADC數值讀取函數和清除觸摸中斷标志函數。最後用重新實作的這幾個函數替換bsp_ts_touch.c檔案中的原函數即可。另外要注意一點,這種方式實作後,雖然觸摸校準依然可以使用,但是開發闆的觸摸校準參數儲存在EEPROM中,使用者可以根據自己的實際情況選擇存儲媒體。
當然,如果大家不想用開發闆實作的方案,想自己重新實作一個,也是沒問題的。
5.9 實驗例程設計架構
通過程式設計架構,讓大家先對配套例程有一個全面的認識,然後再了解細節,本次實驗例程的設計架構如下:
第1階段,上電啟動階段:
- 這部分在第14章進行了詳細說明。
第2階段,進入main函數:
- 第1步,硬體初始化,主要是HAL庫,系統時鐘,滴答定時器,LED,序列槽,LCD,SDRAM等。
- 第2步,LCD應用程式設計部分。通過按鍵執行功能,同時LCD界面做了一個簡單的畫闆功能。
5.10 實驗例程說明
配套例子:
V6-2002_LCD Touch
實驗目的:
- 學習LCD的電阻觸摸和電容觸摸。
實驗内容:
- 電容屏無需校準,電阻屏需要校準。
- LCD界面實作了一個簡單的畫闆功能,友善檢測觸摸是否準确。
- 啟動1個200ms的自動重裝定時器,讓LED2每200ms翻轉一次。
LCD界面顯示效果如下:
5.11 總結
本章節為大家講解的電阻觸摸方案和電容觸摸方案都是經過實戰總結的,實際項目中比較實用。