日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

【正点原子STM32连载】第三十七章 触摸屏实验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1

發(fā)布時(shí)間:2023/12/10 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【正点原子STM32连载】第三十七章 触摸屏实验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1)實(shí)驗(yàn)平臺(tái):正點(diǎn)原子MiniPro H750開(kāi)發(fā)板
2)平臺(tái)購(gòu)買地址:https://detail.tmall.com/item.htm?id=677017430560
3)全套實(shí)驗(yàn)源碼+手冊(cè)+視頻下載地址:http://www.openedv.com/thread-336836-1-1.html
4)對(duì)正點(diǎn)原子STM32感興趣的同學(xué)可以加群討論:879133275

第三十七章 觸摸屏實(shí)驗(yàn)

本章,我們將介紹如何使用STM32H750來(lái)驅(qū)動(dòng)觸摸屏,STM32H750本身并沒(méi)有觸摸屏控制器,但是它支持觸摸屏,可以通過(guò)外接帶觸摸屏的LCD模塊(比如正點(diǎn)原子TFTLCD模塊),來(lái)實(shí)現(xiàn)觸摸屏控制。在本章中,我們將向大家介紹STM32控制正點(diǎn)原子TFTLCD模塊(包括電阻觸摸與電容觸摸),實(shí)現(xiàn)觸摸屏驅(qū)動(dòng),最終實(shí)現(xiàn)一個(gè)手寫(xiě)板的功能。
本章分為如下幾個(gè)小節(jié):
37.1 觸摸屏簡(jiǎn)介
37.2 硬件設(shè)計(jì)
37.3 程序設(shè)計(jì)
37.4 下載驗(yàn)證

37.1 觸摸屏簡(jiǎn)介

觸摸屏是在顯示屏的基礎(chǔ)上,在屏幕或屏幕上方分布一層與屏幕大小相近的傳感器形成的組合器件。觸摸和顯示功能由軟件控制,可以獨(dú)立也可以組合實(shí)現(xiàn),用戶可以通過(guò)偵測(cè)傳感器的觸點(diǎn)再配合相應(yīng)的軟件實(shí)現(xiàn)觸摸效果。目前最常用的觸摸屏有兩種:電阻式觸摸屏與電容式觸摸屏。下面,我們來(lái)分別介紹。
37.1.1 電阻式觸摸屏
正點(diǎn)原子2.4/2.8/3.5寸TFTLCD模塊自帶的觸摸屏都屬于電阻式觸摸屏,下面簡(jiǎn)單介紹下電阻式觸摸屏的原理。
電阻觸摸屏的主要部分是一塊與顯示器表面非常貼合的電阻薄膜屏,這是一種多層的復(fù)合薄膜,具體結(jié)構(gòu)如下圖37.1.1.1所示。

圖37.1.1.1 電阻觸摸屏多層結(jié)構(gòu)圖
表面硬涂層起保護(hù)作用,主要是一層外表面硬化處理、光滑防擦的塑料層。玻璃
底層用于支撐上面的結(jié)構(gòu),主要是玻璃或者塑料平板。透明隔離點(diǎn)用來(lái)分離開(kāi)外層ITO和內(nèi)層ITO。ITO層是觸摸屏關(guān)鍵結(jié)構(gòu),是涂有銦錫金屬氧化物的導(dǎo)電層。還有一個(gè)結(jié)構(gòu)上圖沒(méi)有標(biāo)出,就是PET層。PET層是聚酯薄膜,處于外層ITO和表面硬涂層之間,很薄很有彈性,觸摸時(shí)向下彎曲,使得ITO層接觸。
當(dāng)手指觸摸屏幕時(shí),兩個(gè)ITO層在觸摸點(diǎn)位置就有接觸,電阻發(fā)生變化,在X和Y兩個(gè)方向上產(chǎn)生電信號(hào),然后送到觸摸屏控制器,具體情況如下圖37.1.1.2所示。觸摸屏控制器偵測(cè)到這一接觸并計(jì)算出X和Y方向上的AD值,簡(jiǎn)單來(lái)講,電阻觸摸屏將觸摸點(diǎn)(X,Y)的物理位置轉(zhuǎn)換為代表X坐標(biāo)和Y坐標(biāo)的電壓值。單片機(jī)與觸摸屏控制器進(jìn)行通信獲取到AD值,通過(guò)一定比例關(guān)系運(yùn)算,獲得X和Y軸坐標(biāo)值。

圖37.1.1.2 電阻式觸摸屏的觸點(diǎn)坐標(biāo)結(jié)構(gòu)
電阻觸摸屏的優(yōu)點(diǎn):精度高、價(jià)格便宜、抗干擾能力強(qiáng)、穩(wěn)定性好。
電阻觸摸屏的缺點(diǎn):容易被劃傷、透光性不太好、不支持多點(diǎn)觸摸。
從以上介紹可知,觸摸屏都需要一個(gè)AD轉(zhuǎn)換器,一般來(lái)說(shuō)是需要一個(gè)控制器的。正點(diǎn)原子 TFTLCD模塊選擇的是四線電阻式觸摸屏,這種觸摸屏的控制芯片有很多,包括:ADS7543、ADS7846、TSC2046、XPT2046和HR2046等。這幾款芯片的驅(qū)動(dòng)基本上是一樣的,也就是你只要寫(xiě)出了XPT2046的驅(qū)動(dòng),這個(gè)驅(qū)動(dòng)對(duì)其他幾個(gè)芯片也是有效的。而且封裝也有一樣的,完全PIN-TO-PIN兼容。所以在替換起來(lái),很方便。
正點(diǎn)原子TFTLCD模塊自帶的觸摸屏控制芯片為XPT2046或HR2046。這里以XPT2046作為介紹。XPT2046是一款4導(dǎo)線制觸摸屏控制器,使用的是SPI通信接口,內(nèi)含12位分辨率125KHz轉(zhuǎn)換速率逐步逼近型A/D轉(zhuǎn)換器。XPT2046支持從1.5V到5.25V的低電壓I/O接口。XPT2046能通過(guò)執(zhí)行兩次A/D轉(zhuǎn)換(一次獲取X位置,一次獲取Y位置)查出被按的屏幕位置,除此之外,還可以測(cè)量加在觸摸屏上的壓力。內(nèi)部自帶2.5V參考電壓可以作為輔助輸入、溫度測(cè)量和電池監(jiān)測(cè)模式之用,電池監(jiān)測(cè)的電壓范圍可以從0V到6V。XPT2046片內(nèi)集成有一個(gè)溫度傳感器。在2.7V的典型工作狀態(tài)下,關(guān)閉參考電壓,功耗可小于0.75mW。
XPT2046的驅(qū)動(dòng)方法也是很簡(jiǎn)單,主要看懂XPT2046通信時(shí)序圖,如下圖37.1.1.3所示。

圖37.1.1.3 XPT2046通信時(shí)序圖
依照時(shí)序圖,就可以很好寫(xiě)出這個(gè)通信代碼,上圖具體過(guò)程:拉低片選,選中器件發(fā)送命令字清除BUSY讀取16位數(shù)據(jù)(高12位數(shù)據(jù)有效即轉(zhuǎn)換的AD值)拉高片選,結(jié)束操作。這里的難點(diǎn)就是需要搞清楚命令字該發(fā)送什么?只要搞清楚發(fā)送什么數(shù)值,就可以獲取到AD值。命令字的詳情如下圖37.1.1.4所示:

圖37.1.1.4 命令字詳情圖
位7,開(kāi)始位,置1即可。位3,為了提供精度,MODE位清0選擇12位分辨率。位2,是進(jìn)行工作模式選擇,為了達(dá)到最佳性能,首選差分工作模式即該位清0即可。位1-0是功耗相關(guān)的,直接清0即可。而位6-4的值要取決于工作模式,在確定了差分功能模式后,通道選擇位也確定了,如圖37.1.1.5所示。

圖37.1.1.5 差分模式輸入配置圖(SER/DFR=0)
從上圖,就可以知道:當(dāng)我們需要檢測(cè)Y軸位置時(shí),A2A1A0賦值為001;檢測(cè)X軸位置時(shí),A2A1A0賦值為101。結(jié)合前面對(duì)其他位的賦值,在X,Y方向與屏幕相同的情況下,命令字0xD0就是讀取X坐標(biāo)AD值,0x90就是讀取Y坐標(biāo)的AD值。假如X,Y方向與屏幕相反,0x90就是讀取X坐標(biāo)的AD值,而0xD0就是讀取Y坐標(biāo)的AD值。
關(guān)于這個(gè)芯片其他的功能,也可以參考芯片的datasheet。
電阻式觸摸屏就介紹到這里。
37.1.2 電容式觸摸屏
現(xiàn)在幾乎所有智能手機(jī),包括平板電腦都是采用電容屏作為觸摸屏,電容屏是利用人體感應(yīng)進(jìn)行觸點(diǎn)檢測(cè)控制,不需要直接接觸或只需要輕微接觸,通過(guò)檢測(cè)感應(yīng)電流來(lái)定位觸摸坐標(biāo)。正點(diǎn)原子4.3/7寸TFTLCD模塊自帶的觸摸屏采用的是電容式觸摸屏,下面簡(jiǎn)單介紹下電容式觸摸屏的原理。
電容式觸摸屏主要分為兩種:
1、表面電容式電容觸摸屏。
表面電容式觸摸屏技術(shù)是利用ITO(銦錫氧化物,是一種透明的導(dǎo)電材料)導(dǎo)電膜,通過(guò)電場(chǎng)感應(yīng)方式感測(cè)屏幕表面的觸摸行為進(jìn)行。但是表面電容式觸摸屏有一些局限性,它只能識(shí)別一個(gè)手指或者一次觸摸。
2、投射式電容觸摸屏。
投射電容式觸摸屏是傳感器利用觸摸屏電極發(fā)射出靜電場(chǎng)線。一般用于投射電容傳感技術(shù)的電容類型有兩種:自我電容和交互電容。
自我電容又稱絕對(duì)電容,是最廣為采用的一種方法,自我電容通常是指掃描電極與地構(gòu)成的電容。在玻璃表面有用ITO制成的橫向與縱向的掃描電極,這些電極和地之間就構(gòu)成一個(gè)電容的兩極。當(dāng)用手或觸摸筆觸摸的時(shí)候就會(huì)并聯(lián)一個(gè)電容到電路中去,從而使在該條掃描線上的總體的電容量有所改變。在掃描的時(shí)候,控制IC依次掃描縱向和橫向電極,并根據(jù)掃描前后的電容變化來(lái)確定觸摸點(diǎn)坐標(biāo)位置。筆記本電腦觸摸輸入板就是采用的這種方式,筆記本電腦的輸入板采用XY的傳感電極陣列形成一個(gè)傳感格子,當(dāng)手指靠近觸摸輸入板時(shí),在手指和傳感電極之間產(chǎn)生一個(gè)小量電荷。采用特定的運(yùn)算法則處理來(lái)自行、列傳感器的信號(hào)來(lái)確定手指的位置。
交互電容又叫做跨越電容,它是在玻璃表面的橫向和縱向的ITO電極的交叉處形成電容。交互電容的掃描方式就是掃描每個(gè)交叉處的電容變化,來(lái)判定觸摸點(diǎn)的位置。當(dāng)觸摸的時(shí)候就會(huì)影響到相鄰電極的耦合,從而改變交叉處的電容量,交互電容的掃面方法可以偵測(cè)到每個(gè)交叉點(diǎn)的電容值和觸摸后電容變化,因而它需要的掃描時(shí)間與自我電容的掃描方式相比要長(zhǎng)一些,需要掃描檢測(cè)XY根電極。目前智能手機(jī)/平板電腦等的觸摸屏,都是采用交互電容技術(shù)。
正點(diǎn)原子所選擇的電容觸摸屏,也是采用的是投射式電容屏(交互電容類型),所以后面僅以投射式電容屏作為介紹。
投射式電容觸摸屏采用縱橫兩列電極組成感應(yīng)矩陣,來(lái)感應(yīng)觸摸。以兩個(gè)交叉的電極矩陣,即:X軸電極和Y軸電極,來(lái)檢測(cè)每一格感應(yīng)單元的電容變化,如圖37.1.2.1所示:

圖37.1.2.1 投射式電容屏電極矩陣示意圖
示意圖中的電極,實(shí)際是透明的,這里是為了方便大家理解。圖中,X、Y軸的透明電極電容屏的精度、分辨率與X、Y軸的通道數(shù)有關(guān),通道數(shù)越多,精度越高。以上就是電容觸摸屏的基本原理,接下來(lái)看看電容觸摸屏的優(yōu)缺點(diǎn):
電容觸摸屏的優(yōu)點(diǎn):手感好、無(wú)需校準(zhǔn)、支持多點(diǎn)觸摸、透光性好。
電容觸摸屏的缺點(diǎn):成本高、精度不高、抗干擾能力差。
這里特別提醒大家電容觸摸屏對(duì)工作環(huán)境的要求是比較高的,在潮濕、多塵、高低溫環(huán)境下面,都是不適合使用電容屏的。
電容觸摸屏一般都需要一個(gè)驅(qū)動(dòng)IC來(lái)檢測(cè)電容觸摸,正點(diǎn)原子的電容觸摸屏使用的是IIC接口輸出觸摸數(shù)據(jù)的觸摸芯片。正點(diǎn)原子7’TFTLCD模塊的電容觸摸屏,采用的是15*10的驅(qū)動(dòng)結(jié)構(gòu)(10個(gè)感應(yīng)通道,15個(gè)驅(qū)動(dòng)通道),采用的是GT911/FT5206作為驅(qū)動(dòng)IC。正點(diǎn)原子4.3’TFTLCD模塊采用的驅(qū)動(dòng)IC是:GT9xxx(GT9147/GT917S/GT911/GT1151/GT9271),不同型號(hào)感應(yīng)通道和驅(qū)動(dòng)通道數(shù)量都不一樣,詳看數(shù)據(jù)手冊(cè),但是這些驅(qū)動(dòng)IC驅(qū)動(dòng)方式都類似,這里我們以GT9147為例給大家做介紹,其他的大家參考著學(xué)習(xí)即可。
GT9147與MCU通過(guò)4根線連接:SDA、SCL、RST和INT。GT9147的IIC地址,可以是0X14或者0X5D,當(dāng)復(fù)位結(jié)束后的5ms內(nèi),如果INT是高電平,則使用0X14作為地址,否則使用0X5D作為地址,具體的設(shè)置過(guò)程,請(qǐng)看:GT9147數(shù)據(jù)手冊(cè).pdf這個(gè)文檔。本章我們使用0X14作為器件地址(不含最低位,換算成讀寫(xiě)命令則是讀:0X29,寫(xiě):0X28),接下來(lái),介紹一下GT9147的幾個(gè)重要的寄存器。
1,控制命令寄存器(0X8040)
該寄存器可以寫(xiě)入不同值,實(shí)現(xiàn)不同的控制,我們一般使用0和2這兩個(gè)值,寫(xiě)入2,即可軟復(fù)位GT9147。在硬復(fù)位之后,一般要往該寄存器寫(xiě)2,實(shí)行軟復(fù)位。然后,寫(xiě)入0,即可正常讀取坐標(biāo)數(shù)據(jù)(并且會(huì)結(jié)束軟復(fù)位)。
2,配置寄存器組(0X8047~0X8100)
這里共186個(gè)寄存器,用于配置GT9147的各個(gè)參數(shù),這些配置一般由廠家提供給我們(一個(gè)數(shù)組),所以我們只需要將廠家給我們的配置,寫(xiě)入到這些寄存器里面,即可完成GT9147的配置。由于GT9147可以保存配置信息(可寫(xiě)入內(nèi)部FLASH,從而不需要每次上電都更新配置),我們有幾點(diǎn)注意的地方提醒大家:1,0X8047寄存器用于指示配置文件版本號(hào),程序?qū)懭氲陌姹咎?hào),必須大于等于GT9147本地保存的版本號(hào),才可以更新配置。2,0X80FF寄存器用于存儲(chǔ)校驗(yàn)和,使得0X8047~0X80FF之間所有數(shù)據(jù)之和為0。3,0X8100用于控制是否將配置保存在本地,寫(xiě)0,則不保存配置,寫(xiě)1則保存配置。
3,產(chǎn)品ID寄存器(0X8140~0X8143)
這里總共由4個(gè)寄存器組成,用于保存產(chǎn)品ID,對(duì)于GT9147,這4個(gè)寄存器讀出來(lái)就是:9,1,4,7四個(gè)字符(ASCII碼格式)。因此,我們可以通過(guò)這4個(gè)寄存器的值,來(lái)判斷驅(qū)動(dòng)IC的型號(hào),以便執(zhí)行不同的初始化。
4,狀態(tài)寄存器(0X814E)
該寄存器各位描述如表表37.1.2.1所示:

表 37.1.2.1 狀態(tài)寄存器各位描述
這里,我們僅關(guān)心最高位和最低4位,最高位用于表示buffer狀態(tài),如果有數(shù)據(jù)(坐標(biāo)/按鍵),buffer就會(huì)是1,最低4位用于表示有效觸點(diǎn)的個(gè)數(shù),范圍是:0~5,0,表示沒(méi)有觸摸,5表示有5點(diǎn)觸摸。最后,該寄存器在每次讀取后,如果bit7有效,則必須寫(xiě)0,清除這個(gè)位,否則不會(huì)輸出下一次數(shù)據(jù)!!這個(gè)要特別注意!!!
5,坐標(biāo)數(shù)據(jù)寄存器(共30個(gè))
這里共分成5組(5個(gè)點(diǎn)),每組6個(gè)寄存器存儲(chǔ)數(shù)據(jù),以觸點(diǎn)1的坐標(biāo)數(shù)據(jù)寄存器組為例,如表37.1.2.2所示:

表37.1.2.2 觸點(diǎn)1坐標(biāo)寄存器組描述
我們一般只用到觸點(diǎn)的x,y坐標(biāo),所以只需要讀取0X81500X8153的數(shù)據(jù),組合即可得到觸點(diǎn)坐標(biāo)。其他4組分別是:0X8158、0X8160、0X8168和0X8170等開(kāi)頭的16個(gè)寄存器組成,分別針對(duì)觸點(diǎn)24的坐標(biāo)。同樣GT9147也支持寄存器地址自增,我們只需要發(fā)送寄存器組的首地址,然后連續(xù)讀取即可,GT9147會(huì)自動(dòng)地址自增,從而提高讀取速度。
GT9147相關(guān)寄存器的介紹就介紹到這里,更詳細(xì)的資料,請(qǐng)參考:GT9147編程指南.pdf 這個(gè)文檔。
GT9147只需要經(jīng)過(guò)簡(jiǎn)單的初始化就可以正常使用了,初始化流程:硬復(fù)位延時(shí)10ms結(jié)束硬復(fù)位設(shè)置IIC地址延時(shí)100ms軟復(fù)位更新配置(需要時(shí))結(jié)束軟復(fù)位。此時(shí)GT9147即可正常使用了。然后,我們不停的查詢0X814E寄存器,判斷是否有有效觸點(diǎn),如果有,則讀取坐標(biāo)數(shù)據(jù)寄存器,得到觸點(diǎn)坐標(biāo)。特別注意,如果0X814E讀到的值最高位為1,就必須對(duì)該位寫(xiě)0,否則無(wú)法讀到下一次坐標(biāo)數(shù)據(jù)。
電容式觸摸屏部分,就介紹到這里。
37.1.3 觸摸控制原理
前面已經(jīng)簡(jiǎn)單地介紹了電阻屏和電容屏的原理,并且知道了不同類型的觸摸屏其實(shí)是屏幕+觸摸傳感器組成。那么這里就會(huì)有兩組相互獨(dú)立的參數(shù):屏幕坐標(biāo)和觸摸坐標(biāo)。要實(shí)現(xiàn)觸摸功能,就是要把觸摸點(diǎn)和屏幕坐標(biāo)對(duì)應(yīng)起來(lái)。
我們以LCD顯示屏為例,我們知道屏幕的掃描方向是可以編程設(shè)定的,而觸摸點(diǎn),在觸摸傳感器安裝好后,AD值的變化向方向則是固定的,我們以最常見(jiàn)的屏幕坐標(biāo)方向:先從左到右,再?gòu)纳系较聮呙铻槔?#xff0c;此時(shí),屏幕坐標(biāo)和觸點(diǎn)AD的坐標(biāo)有類似的規(guī)律:從坐標(biāo)原點(diǎn)出發(fā),水平方向屏幕坐標(biāo)增加時(shí),AD值的X方向也增加;屏幕坐標(biāo)的Y方向坐標(biāo)增加,AD值的Y方向也增加;坐標(biāo)減少時(shí)對(duì)應(yīng)的關(guān)系也類似,可以用圖37.1.3.1的示意圖來(lái)表示這種關(guān)系:

圖37.1.3.1 屏幕坐標(biāo)和觸摸坐標(biāo)的一種對(duì)應(yīng)關(guān)系
這里再來(lái)引入兩個(gè)概念,物理坐標(biāo)和邏輯坐標(biāo)。物理坐標(biāo)指觸摸屏上點(diǎn)的實(shí)際位置,通常以液晶上點(diǎn)的個(gè)數(shù)來(lái)度量。邏輯坐標(biāo)指這點(diǎn)被觸摸時(shí)A/D轉(zhuǎn)換后的坐標(biāo)值。仍以圖37.1.3.1為例,我們假定液晶最左上角為坐標(biāo)軸原點(diǎn)A,在液晶上任取一點(diǎn)B(實(shí)際人手比像素點(diǎn)大得多,一次按下會(huì)有多個(gè)觸點(diǎn),此處取十字線交叉中心),B在X方向與A相距100個(gè)點(diǎn),在Y方向與A距離200個(gè)點(diǎn),則這點(diǎn)的物理坐標(biāo)B為(100,200)。如果我們觸摸這一點(diǎn)時(shí)得到的X向A/D轉(zhuǎn)換值為200,Y向A/D轉(zhuǎn)換值為400,則這點(diǎn)的邏輯坐標(biāo)B’為(200,400)。
需要特別說(shuō)明的是,正點(diǎn)原子的電容屏的參數(shù)已經(jīng)在出廠時(shí)由廠家調(diào)好,所以無(wú)需進(jìn)行校準(zhǔn),而且可以直接讀到轉(zhuǎn)換后的觸點(diǎn)坐標(biāo);對(duì)于電阻屏,請(qǐng)大家理解并熟記物理坐標(biāo)和邏輯坐標(biāo)邏輯上的對(duì)應(yīng)關(guān)系,我們后面編程需要用到。
37.2 硬件設(shè)計(jì)

  • 例程功能
    正點(diǎn)原子的觸摸屏種類很多,并且設(shè)計(jì)了規(guī)格相對(duì)統(tǒng)一的接口。根據(jù)屏幕的種類不同,設(shè)置了相應(yīng)的硬件ID(正點(diǎn)原子自編ID),可以通過(guò)軟件判斷觸摸屏的種類。
    本章實(shí)驗(yàn)功能簡(jiǎn)介:開(kāi)機(jī)的時(shí)候先初始化LCD,讀取LCD ID,隨后,根據(jù)LCD ID判斷是電阻觸摸屏還是電容觸摸屏,如果是電阻觸摸屏,則先讀取24C02的數(shù)據(jù)判斷觸摸屏是否已經(jīng)校準(zhǔn)過(guò),如果沒(méi)有校準(zhǔn),則執(zhí)行校準(zhǔn)程序,校準(zhǔn)過(guò)后再進(jìn)入電阻觸摸屏測(cè)試程序,如果已經(jīng)校準(zhǔn)了,就直接進(jìn)入電阻觸摸屏測(cè)試程序。
    如果是4.3寸電容觸摸屏,則執(zhí)行GT9xxx的初始化代碼;如果是7寸電容觸摸屏(僅支持新款7寸屏,使用SSD1963+FT5206方案),則執(zhí)行FT5206的初始化代碼,在初始化電容觸摸屏完成后,進(jìn)入電容觸摸屏測(cè)試程序(電容觸摸屏無(wú)需校準(zhǔn)!!)。
    電阻觸摸屏測(cè)試程序和電容觸摸屏測(cè)試程序基本一樣,只是電容觸摸屏支持最多5點(diǎn)同時(shí)觸摸,電阻觸摸屏只支持一點(diǎn)觸摸,其他一模一樣。測(cè)試界面的右上角會(huì)有一個(gè)清空的操作區(qū)域(RST),點(diǎn)擊這個(gè)地方就會(huì)將輸入全部清除,恢復(fù)白板狀態(tài)。使用電阻觸摸屏的時(shí)候,可以通過(guò)按KEY0來(lái)實(shí)現(xiàn)強(qiáng)制觸摸屏校準(zhǔn),只要按下KEY0就會(huì)進(jìn)入強(qiáng)制校準(zhǔn)程序。
  • 硬件資源
    1)RGB燈
    RED :LED0 - PB4
    2)獨(dú)立按鍵
    KEY0 - PA1
    3)EEPROM AT24C02
    4)正點(diǎn)原子2.8/3.5/4.3/7/10寸TFTLCD模塊(僅限MCU屏,16位8080并口驅(qū)動(dòng))
    5)串口1(PA9/PA10連接在板載USB轉(zhuǎn)串口芯片CH340上面)
  • 原理圖
    LCD模塊的觸摸屏(電阻觸摸屏)總共有5根線與STM32H750連接,連接電路圖如下圖所示:
  • 圖37.2.1 觸摸屏與STM32H750的連接圖
    從圖中可以看出,T_MISO、T_PEN、T_CS、T_MOSI和T_SCK分別連接在STM32H750的:PD6、PB1、PC5、PB3和PB0上。
    如果是電容式觸摸屏,我們的接口和電阻式觸摸屏一樣,只是沒(méi)有用到五根線了,而是四根線,分別是:T_PEN(CT_INT)、T_CS(CT_RST)、T_CLK(CT_SCL)和T_MOSI(CT_SDA)。其中:CT_INT、CT_RST、CT_SCL和CT_SDA分別是GT9147/FT5206的:中斷輸出信號(hào)、復(fù)位信號(hào),IIC的SCL和SDA信號(hào)。這里,我們用查詢的方式讀取GT9147/FT5206的數(shù)據(jù),對(duì)于FT5206沒(méi)有用到中斷信號(hào)(CT_INT),所以同STM32H750的連接,最少只需要3根線即可,不過(guò)GT9147還需要用到CT_INT做IIC地址設(shè)定,所以需要4根線連接。
    37.3 程序設(shè)計(jì)
    37.3.1 HAL庫(kù)驅(qū)動(dòng)
    觸摸芯片我們使用到的是IIC和SPI的驅(qū)動(dòng),這部分的時(shí)序分析可以參考之前IIC/SPI的章節(jié),我們直接使用的是軟件模擬的方式,所以只需要使用HAL庫(kù)的驅(qū)動(dòng)的GPIO操作部分。
    觸摸IC初始化步驟
    1)初始化通信接口與其IO(使能時(shí)鐘、配置GPIO工作模式)
    觸摸IC用到的GPIO口,主要是PD6、PB1、PC5、PB3和PB0,因?yàn)槎际怯密浖M的方式,因此在這里我們只需使能GPIOB、GPIOC和GPIOD時(shí)鐘即可。參考代碼如下:
    __HAL_RCC_GPIOB_CLK_ENABLE(); /* 使能GPIOB時(shí)鐘 /
    __HAL_RCC_GPIOC_CLK_ENABLE(); / 使能GPIOC時(shí)鐘 /
    __HAL_RCC_GPIOD_CLK_ENABLE(); / 使能GPIOD時(shí)鐘 */
    GPIO模式設(shè)置通過(guò)調(diào)用HAL_GPIO_Init函數(shù)實(shí)現(xiàn),詳見(jiàn)本例程源碼。
    2)編寫(xiě)通信協(xié)議基礎(chǔ)讀寫(xiě)函數(shù)
    通過(guò)參考時(shí)序圖,在IIC驅(qū)動(dòng)或SPI驅(qū)動(dòng)基礎(chǔ)上,編寫(xiě)基礎(chǔ)讀寫(xiě)函數(shù)。讀寫(xiě)函數(shù)均以一字節(jié)數(shù)據(jù)進(jìn)行操作。
    3)參考觸摸IC時(shí)序圖,編寫(xiě)觸摸IC讀寫(xiě)驅(qū)動(dòng)函數(shù)
    根據(jù)觸摸IC的讀寫(xiě)時(shí)序進(jìn)行編寫(xiě)觸摸IC的讀寫(xiě)函數(shù),詳見(jiàn)本例程源碼。
    4)編寫(xiě)坐標(biāo)獲取函數(shù)(電阻觸摸屏和電容觸摸屏)
    查閱數(shù)據(jù)手冊(cè)獲得命令詞(電阻觸摸屏)/寄存器(電容觸摸屏),通過(guò)讀寫(xiě)函數(shù)獲取坐標(biāo)數(shù)據(jù),詳見(jiàn)本例程源碼。
    37.3.2 程序流程圖

    圖37.3.2.1 觸摸屏實(shí)驗(yàn)流程圖
    37.3.3 程序解析
    這里我們只講解核心代碼,詳細(xì)的源碼請(qǐng)大家參考光盤本實(shí)驗(yàn)對(duì)應(yīng)源碼。TOUCH驅(qū)動(dòng)源碼包括八個(gè)文件:ctiic.c、ctiic.h 、ft5206.c、ft5206.h、gt9xxx.c、gt9xxx.h、touch.c和touch.h。
    由于正點(diǎn)原子的TFTLCD的型號(hào)很多,觸摸控制這部分驅(qū)動(dòng)代碼根據(jù)不同屏幕搭載的觸摸芯片驅(qū)動(dòng)而有不同,在我們的屏幕上使用的是LCD ID來(lái)幫助軟件上區(qū)分。為了解決多種驅(qū)動(dòng)芯片的問(wèn)題,我們?cè)O(shè)計(jì)了touch.c/touch.h這兩個(gè)文件統(tǒng)一管理各類型的驅(qū)動(dòng)。不同的驅(qū)動(dòng)芯片類型可以在touch.c中集中添加,并通過(guò)touch.c中的接口統(tǒng)一調(diào)用,不同的觸摸芯片各自編寫(xiě)?yīng)毩⒌?c/.h文件,需要時(shí)被touch.c調(diào)用。電阻觸摸屏相關(guān)代碼也在touch.c中實(shí)現(xiàn)。

  • 觸摸管理驅(qū)動(dòng)代碼
    因?yàn)樾枰С值挠|摸驅(qū)動(dòng)比較多,為了方便管理和添加新的驅(qū)動(dòng),我們用touch.c文件來(lái)統(tǒng)一管理這些觸摸驅(qū)動(dòng),然后針對(duì)各類觸摸芯片編寫(xiě)?yīng)毩⒌尿?qū)動(dòng)。為了方便管理觸摸,我們?cè)趖ouch.h中定義一個(gè)用于管理觸摸信息的結(jié)構(gòu)體類型,具體代碼如下:
  • /* 觸摸屏控制器 */ typedef struct {uint8_t (*init)(void); /* 初始化觸摸屏控制器 */uint8_t (*scan)(uint8_t); /* 掃描觸摸屏.0,屏幕掃描;1,物理坐標(biāo); */void (*adjust)(void); /* 觸摸屏校準(zhǔn) */ uint16_t x[CT_MAX_TOUCH]; /* 當(dāng)前坐標(biāo) */ uint16_t y[CT_MAX_TOUCH]; /* 電容屏有最多10組坐標(biāo),電阻屏則用x[0],y[0]代表:此次掃 描時(shí),觸屏的坐標(biāo),用 x[9],y[9]存儲(chǔ)第一次按下時(shí)的坐標(biāo) */uint16_t sta; /* 筆的狀態(tài)* b15:按下1/松開(kāi)0;* b14:0,沒(méi)有按鍵按下;1,有按鍵按下.* b13~b10:保留* b9~b0:電容觸摸屏按下的點(diǎn)數(shù)(0,表示未按下,1表示按下)*//* 5點(diǎn)校準(zhǔn)觸摸屏校準(zhǔn)參數(shù)(電容屏不需要校準(zhǔn)) */float xfac; /* 5點(diǎn)校準(zhǔn)法x方向比例因子 */float yfac; /* 5點(diǎn)校準(zhǔn)法y方向比例因子 */short xc; /* 中心X坐標(biāo)物理值(AD值) */ short yc; /* 中心Y坐標(biāo)物理值(AD值) *//* 新增的參數(shù),當(dāng)觸摸屏的左右上下完全顛倒時(shí)需要用到.* b0: 0, 豎屏(適合左右為X坐標(biāo),上下為Y坐標(biāo)的TP)* 1, 橫屏(適合左右為Y坐標(biāo),上下為X坐標(biāo)的TP)* b1~6: 保留.* b7: 0, 電阻屏* 1, 電容屏*/uint8_t touchtype; } _m_tp_dev;extern _m_tp_dev tp_dev; /* 觸屏控制器在touch.c里面定義 */

    這里我們定義了函數(shù)指針,只要把相對(duì)應(yīng)的觸摸芯片的函數(shù)指針賦值給它,就可以通過(guò)這個(gè)通用接口很方便調(diào)用不同芯片的函數(shù)接口。正點(diǎn)原子不同的觸摸屏區(qū)別如下:
    1、在使用4.3寸屏、10.1寸屏電容屏?xí)r,使用的是匯頂科技的GT9xxx系列觸摸屏驅(qū)動(dòng)IC,這是一個(gè)IIC接口的驅(qū)動(dòng)芯片,我們要編寫(xiě)gt9xxx系列芯片的初始化程序,并編寫(xiě)一個(gè)坐標(biāo)掃描程序,這里我們先預(yù)留這兩個(gè)接口分別為gt9xxx_init()和gt9xxx_scan(),在gt9xxx.c文件中再專門實(shí)現(xiàn)這兩個(gè)驅(qū)動(dòng),標(biāo)記使用的為電容屏;
    2、類似地,在使用SSD1963 7寸屏、7寸800480/1024600 RGB屏?xí)r,我們的屏幕搭載的觸摸驅(qū)動(dòng)芯片是ft5206/GT911,FT5206觸摸IC預(yù)留這兩個(gè)接口分別為ft5206_init()和ft5206_scan(),在ft5206.c文件中再專門實(shí)現(xiàn)這兩個(gè)驅(qū)動(dòng),標(biāo)記使用的為電容屏;GT911也是調(diào)用gtxxx_init()和gt9xxx_scan()接口。
    3、當(dāng)為其它ID時(shí),默認(rèn)為電阻屏,而電阻屏默認(rèn)使用的是SPI接口的XPT2046芯片。由于電阻屏存在線性誤差,所以在使用前需要進(jìn)行校準(zhǔn),這也是為什么在前面的結(jié)構(gòu)體類型中存在關(guān)于校準(zhǔn)參數(shù)的成員。為了避免每次都要進(jìn)行校準(zhǔn)的麻煩,所以會(huì)使用AT24C02來(lái)存儲(chǔ)校準(zhǔn)成功后的數(shù)據(jù)。如何進(jìn)行校準(zhǔn)也會(huì)在后面進(jìn)行講解。作為電阻屏,它也有一個(gè)掃描坐標(biāo)函數(shù)即tp_scan()。
    (*init)(void)這個(gè)結(jié)構(gòu)體函數(shù)指針,默認(rèn)指向tp_init的,而在tp_init里對(duì)觸摸屏進(jìn)行初始化并對(duì)(*scan)(uint8_t)函數(shù)指針根據(jù)觸摸芯片類型重新做了指向。在這里簡(jiǎn)單看一下touch.c的觸摸屏初始化函數(shù)tp_init,其代碼如下:

    /*** @brief 觸摸屏初始化* @param 無(wú)* @retval 0,沒(méi)有進(jìn)行校準(zhǔn)* 1,進(jìn)行過(guò)校準(zhǔn)*/ uint8_t tp_init(void) {GPIO_InitTypeDef gpio_init_struct;tp_dev.touchtype = 0; /* 默認(rèn)設(shè)置(電阻屏 & 豎屏) */ tp_dev.touchtype |= lcddev.dir & 0X01; /* 根據(jù)LCD判定是橫屏還是豎屏 */if (lcddev.id == 0X5510 || lcddev.id == 0X4342 || lcddev.id == 0X4384 || lcddev.id == 0X1018) { /* 電容觸摸屏,4.3寸/10.1寸屏 */gt9xxx_init();tp_dev.scan = gt9xxx_scan; /* 掃描函數(shù)指向GT9147觸摸屏掃描 */tp_dev.touchtype |= 0X80; /* 電容屏 */return 0; }else if (lcddev.id == 0X1963 || lcddev.id == 0X7084 || lcddev.id == 0X7016) { /* SSD1963 7寸屏或者 7寸800*480/1024*600 RGB屏 */if (!ft5206_init()) {tp_dev.scan = ft5206_scan; /* 掃描函數(shù)指向FT5206觸摸屏掃描 */ }else {gt9xxx_init();tp_dev.scan = gt9xxx_scan; /* 掃描函數(shù)指向GT9147觸摸屏掃描 */ }tp_dev.touchtype |= 0X80; /* 電容屏 */return 0;}else{T_PEN_GPIO_CLK_ENABLE(); /* T_PEN腳時(shí)鐘使能 */T_CS_GPIO_CLK_ENABLE(); /* T_CS腳時(shí)鐘使能 */T_MISO_GPIO_CLK_ENABLE(); /* T_MISO腳時(shí)鐘使能 */T_MOSI_GPIO_CLK_ENABLE(); /* T_MOSI腳時(shí)鐘使能 */T_CLK_GPIO_CLK_ENABLE(); /* T_CLK腳時(shí)鐘使能 */gpio_init_struct.Pin = T_PEN_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 輸入 */gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; /* 高速 */HAL_GPIO_Init(T_PEN_GPIO_PORT, &gpio_init_struct); /* 初始化T_PEN引腳 */gpio_init_struct.Pin = T_MISO_GPIO_PIN;HAL_GPIO_Init(T_MISO_GPIO_PORT, &gpio_init_struct); /* 初始化T_MISO引腳 */gpio_init_struct.Pin = T_MOSI_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽輸出 */gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; /* 高速 */HAL_GPIO_Init(T_MOSI_GPIO_PORT, &gpio_init_struct); /* 初始化T_MOSI引腳 */gpio_init_struct.Pin = T_CLK_GPIO_PIN;HAL_GPIO_Init(T_CLK_GPIO_PORT, &gpio_init_struct); /* 初始化T_CLK引腳 */gpio_init_struct.Pin = T_CS_GPIO_PIN;HAL_GPIO_Init(T_CS_GPIO_PORT, &gpio_init_struct); /* 初始化T_CS引腳 */tp_read_xy(&tp_dev.x[0], &tp_dev.y[0]); /* 第一次讀取初始化 */at24cxx_init(); /* 初始化24CXX */if (tp_get_adjust_data()){return 0; /* 已經(jīng)校準(zhǔn) */}else /* 未校準(zhǔn)? */{lcd_clear(WHITE); /* 清屏 */tp_adjust(); /* 屏幕校準(zhǔn) */tp_save_adjust_data();}tp_get_adjust_data();}return 1; }

    正點(diǎn)原子的電容屏在出廠時(shí)已經(jīng)由廠家較對(duì)好參數(shù)了,而電阻屏由于工藝和每個(gè)屏的線性有所差異,我們需要先對(duì)其進(jìn)行“校準(zhǔn)”,我們?cè)谙乱稽c(diǎn)補(bǔ)充說(shuō)明它的實(shí)現(xiàn)。
    通過(guò)上面的觸摸初始化后,我們就可以讀取相關(guān)的觸點(diǎn)信息用于顯示編程了,注意到上面還有很多個(gè)函數(shù)還沒(méi)實(shí)現(xiàn),比如讀取坐標(biāo)和校準(zhǔn),我們?cè)诮酉聛?lái)的代碼中將它補(bǔ)充完整。
    2. 電阻屏觸摸函數(shù)
    前面我們介紹過(guò)了電阻式觸摸屏的原理,由于電阻屏的驅(qū)動(dòng)代碼都比較類似,我們決定把電阻屏的驅(qū)動(dòng)函數(shù)直接添加在touch.c/touch.h中實(shí)現(xiàn)。
    在touch.c的初始化函數(shù)tp_init中,對(duì)使用到的SPI接口IO進(jìn)行了初始化。接下來(lái)介紹一下獲取觸摸點(diǎn)在屏幕上坐標(biāo)的算法:先獲取邏輯坐標(biāo)(AD值),再轉(zhuǎn)換成屏幕坐標(biāo)。
    如何獲取邏輯坐標(biāo)(AD值),在前面已經(jīng)分析過(guò)了,所以這里我們看一下tp_read_ad()函數(shù)接口:

    /*** @brief SPI讀數(shù)據(jù)* @note 從觸摸屏IC讀取adc值* @param cmd: 指令* @retval 讀取到的數(shù)據(jù),ADC值(12bit)*/ static uint16_t tp_read_ad(uint8_t cmd) {uint8_t count = 0;uint16_t num = 0;T_CLK(0); /* 先拉低時(shí)鐘 */T_MOSI(0); /* 拉低數(shù)據(jù)線 */T_CS(0); /* 選中觸摸屏IC */tp_write_byte(cmd); /* 發(fā)送命令字 */delay_us(6); /* ADS7846的轉(zhuǎn)換時(shí)間最長(zhǎng)為6us */T_CLK(0);delay_us(1);T_CLK(1); /* 給1個(gè)時(shí)鐘,清除BUSY */delay_us(1);T_CLK(0);for (count = 0; count < 16; count++) /* 讀出16位數(shù)據(jù),只有高12位有效 */{num <<= 1;T_CLK(0); /* 下降沿有效 */delay_us(1);T_CLK(1);if (T_MISO)num++; }num >>= 4; /* 只有高12位有效. */T_CS(1); /* 釋放片選 */return num; }

    這里我們使用的是軟件模擬SPI,遵照時(shí)序編寫(xiě)SPI讀函數(shù)接口。而發(fā)送命令字是通過(guò)寫(xiě)函數(shù)tp_write_byte來(lái)實(shí)現(xiàn),詳看源碼。
    一次讀取的誤差會(huì)很大,我們采用平均值濾波的方法,多次讀取數(shù)據(jù)并丟棄波動(dòng)最大的最大和最小值,取余下的平均值。具體可以查看tp_read_xoy函數(shù)內(nèi)部實(shí)現(xiàn)。
    /* 電阻觸摸驅(qū)動(dòng)芯片 數(shù)據(jù)采集 濾波用參數(shù) */

    #define TP_READ_TIMES 5 /* 讀取次數(shù) */ #define TP_LOST_VAL 1 /* 丟棄值 *//*** @brief 讀取一個(gè)坐標(biāo)值(x或者y)* @note 連續(xù)讀取TP_READ_TIMES次數(shù)據(jù),對(duì)這些數(shù)據(jù)升序排列,* 然后去掉最低和最高TP_LOST_VAL個(gè)數(shù), 取平均值* 設(shè)置時(shí)需滿足: TP_READ_TIMES > 2*TP_LOST_VAL 的條件** @param cmd : 指令* @arg 0XD0: 讀取X軸坐標(biāo)(@豎屏狀態(tài),橫屏狀態(tài)和Y對(duì)調(diào).)* @arg 0X90: 讀取Y軸坐標(biāo)(@豎屏狀態(tài),橫屏狀態(tài)和X對(duì)調(diào).)** @retval 讀取到的數(shù)據(jù)(濾波后的), ADC值(12bit)*/ static uint16_t tp_read_xoy(uint8_t cmd) {uint16_t i, j;uint16_t buf[TP_READ_TIMES];uint16_t sum = 0;uint16_t temp;for (i = 0; i < TP_READ_TIMES; i++) /* 先讀取TP_READ_TIMES次數(shù)據(jù) */{buf[i] = tp_read_ad(cmd); }for (i = 0; i < TP_READ_TIMES - 1; i++) /* 對(duì)數(shù)據(jù)進(jìn)行排序 */{for (j = i + 1; j < TP_READ_TIMES; j++){if (buf[i] > buf[j]) /* 升序排列 */{temp = buf[i];buf[i] = buf[j];buf[j] = temp;}} }sum = 0;for (i = TP_LOST_VAL; i < TP_READ_TIMES - TP_LOST_VAL; i++){ /* 去掉兩端的丟棄值 */sum += buf[i]; /* 累加去掉丟棄值以后的數(shù)據(jù). */ }temp = sum / (TP_READ_TIMES - 2 * TP_LOST_VAL); /* 取平均值 */return temp; }

    有了前述代碼,我們就可以通過(guò)tp_read_xoy(uint8_t cmd)接口調(diào)取需要的x或者y坐標(biāo)的AD值了。這里我們加上橫屏或者豎屏的處理代碼,編寫(xiě)一個(gè)可以通過(guò)指針一次得到x和y的兩個(gè)AD值的接口,代碼如下:

    /*** @brief 讀取x, y坐標(biāo)* @param x,y: 讀取到的坐標(biāo)值* @retval 無(wú)*/ static void tp_read_xy(uint16_t *x, uint16_t *y) {uint16_t xval, yval;if (tp_dev.touchtype & 0X01) /* X,Y方向與屏幕相反 */{xval = tp_read_xoy(0X90); /* 讀取X軸坐標(biāo)AD值, 并進(jìn)行方向變換 */yval = tp_read_xoy(0XD0); /* 讀取Y軸坐標(biāo)AD值 */}else /* X,Y方向與屏幕相同 */{xval = tp_read_xoy(0XD0); /* 讀取X軸坐標(biāo)AD值 */yval = tp_read_xoy(0X90); /* 讀取Y軸坐標(biāo)AD值 */}*x = xval;*y = yval; }

    為了進(jìn)一步保證參數(shù)的精度,我們連續(xù)讀兩次觸摸數(shù)據(jù)并取平均值作為最后的觸摸參數(shù),并對(duì)這兩次濾波值平均后再傳給目標(biāo)存儲(chǔ)區(qū),由于AD的精度為12位,故該函數(shù)讀取坐標(biāo)的值0~4095,tp_read_xy2的代碼如下:
    /* 連續(xù)兩次讀取X,Y坐標(biāo)的數(shù)據(jù)誤差最大允許值 */

    #define TP_ERR_RANGE 50 /* 誤差范圍 *//*** @brief 連續(xù)讀取2次觸摸IC數(shù)據(jù), 并濾波* @note 連續(xù)2次讀取觸摸屏IC,且這兩次的偏差不能超過(guò)ERR_RANGE,滿足* 條件,則認(rèn)為讀數(shù)正確,否則讀數(shù)錯(cuò)誤.該函數(shù)能大大提高準(zhǔn)確度.** @param x,y: 讀取到的坐標(biāo)值* @retval 0, 失敗; 1, 成功;*/ static uint8_t tp_read_xy2(uint16_t *x, uint16_t *y) {uint16_t x1, y1;uint16_t x2, y2;tp_read_xy(&x1, &y1); /* 讀取第一次數(shù)據(jù) */tp_read_xy(&x2, &y2); /* 讀取第二次數(shù)據(jù) *//* 前后兩次采樣在+-TP_ERR_RANGE內(nèi) */ if (((x2 <= x1 && x1<x2+TP_ERR_RANGE)||(x1 <= x2 && x2<x1+TP_ERR_RANGE))&& ((y2 <= y1 && y1<y2+TP_ERR_RANGE)||(y1 <= y2 && y2<y1+TP_ERR_RANGE))){*x = (x1 + x2) / 2;*y = (y1 + y2) / 2;return 1;}return 0; }

    根據(jù)以上的流程,可以得到電阻屏觸摸點(diǎn)的比較精確的AD信息。每次觸摸屏幕時(shí)會(huì)對(duì)應(yīng)一組X、Y的AD值,由于坐標(biāo)的AD值是在X、Y方向都是線性的,很容易想到要把觸摸信息的AD值和屏幕坐標(biāo)聯(lián)系起來(lái),這里需要編寫(xiě)一個(gè)坐標(biāo)轉(zhuǎn)換函數(shù),前面在編寫(xiě)初始化接口時(shí)講到的校準(zhǔn)函數(shù)這時(shí)候就派上用場(chǎng)了。
    從前面的知識(shí)我們就知道觸摸屏的AD的XAD、YAD可以構(gòu)成一個(gè)邏輯平面,LCD屏的屏幕坐標(biāo)X、Y也是一個(gè)邏輯平面,由于存在誤差,這兩個(gè)平面并不重合,校準(zhǔn)的作用就是要將邏輯平面映射到物理平面上,即得到觸點(diǎn)在液晶屏上的位置坐標(biāo)。校準(zhǔn)算法的中心思想也就是要建立這樣一個(gè)映射函數(shù)現(xiàn)有的校準(zhǔn)算法大多是基于線性校準(zhǔn),即首先假定物理平面和邏輯平面之間的誤差是線性誤差,由旋轉(zhuǎn)和偏移形成。
    常用的電阻式觸摸屏校正方法有兩點(diǎn)校準(zhǔn)法和三點(diǎn)校準(zhǔn)法。本文這里介紹的是結(jié)合了不同的電阻式觸摸屏校正法的優(yōu)化算法:五點(diǎn)校正法。其中主要的原理是使用4點(diǎn)校正法的比例運(yùn)算以及三點(diǎn)校正法的基準(zhǔn)點(diǎn)運(yùn)算。五點(diǎn)校正法優(yōu)勢(shì)在于可以更加精確的計(jì)算出X和Y方向的比例縮放系數(shù),同時(shí)提供了中心基準(zhǔn)點(diǎn),對(duì)于一些線性電阻系數(shù)比較差電阻式觸摸屏有很好的校正功能。校正相關(guān)的變量主要有:
    ?x[5],y[5]五點(diǎn)定位的物理坐標(biāo)(LCD坐標(biāo))
    ?xl[5],yl[5]五點(diǎn)定位的邏輯坐標(biāo)(觸摸AD值)
    ?KX,KY橫縱方向伸縮系數(shù)
    ?XLC,YLC中心基點(diǎn)邏輯坐標(biāo)
    ?XC,YC中心基點(diǎn)物理坐標(biāo)(數(shù)值采用LCD顯示屏的物理長(zhǎng)寬分辨率的一半)
    x[5],y[5]五點(diǎn)定位的物理坐標(biāo)是已知的,其中4點(diǎn)分別設(shè)置在LCD的角落,一點(diǎn)設(shè)置在LCD正中心,作為基準(zhǔn)校正點(diǎn),校正關(guān)鍵點(diǎn)和距離布局如圖37.3.3.1所示。

    圖37.3.3.1 電阻屏五點(diǎn)校準(zhǔn)法的參考點(diǎn)設(shè)定
    校正步驟如下:

  • 通過(guò)先后點(diǎn)擊LCD的4個(gè)角落的校正點(diǎn),獲取4個(gè)角落的邏輯坐標(biāo)值。
  • 計(jì)算屏幕坐標(biāo)和四點(diǎn)間距:
    S1 = x[1]- x[0]
    S3 = x[2]- x[3]
    S2 = y[2]- y[1]
    S4 = y[3]- y[0]
    一般取點(diǎn)可以人為的設(shè)定S1=S3和S2=S4,以方便運(yùn)算。
    計(jì)算邏輯坐標(biāo)的四點(diǎn)“間距”,由于實(shí)際觸點(diǎn)肯定會(huì)存在誤差,所以觸摸點(diǎn)會(huì)落在實(shí)際設(shè)定點(diǎn)的更大范圍內(nèi),在圖37.3.1中,設(shè)定點(diǎn)為五個(gè)點(diǎn),但實(shí)際采樣時(shí)觸點(diǎn)有時(shí)會(huì)落在稍大的外圈范圍,圖中用紅色的圓圈標(biāo)注了,所以有必要設(shè)定一個(gè)誤差范圍:
    S1’ = xl[1]- xl[0]
    S3’ = xl[2]- xl[3]
    S2’ = yl[2]- yl[1]
    S4’ = yl[3]- yl[0]
    由于觸點(diǎn)的誤差,對(duì)于邏輯點(diǎn)S1’和S3’則大概率不會(huì)相等,同樣的,S2’和S4’也很驗(yàn)取到相等的點(diǎn),那么為了簡(jiǎn)化計(jì)算,我們強(qiáng)制以(S1’+S3’)/2的線長(zhǎng)作一個(gè)矩形一邊,以(S2’+S4’)/2為矩形另一邊,這樣構(gòu)建的矩形在誤差范圍是可以接受的,也方便計(jì)算,于是得到X和Y方向的近似縮放系數(shù):
    KX = (S1’ + S3’) / 2 / S1
    KY = (S2’ + S4’) / 2 / S2
  • 點(diǎn)擊LCD正中心,獲取中心點(diǎn)的邏輯坐標(biāo),作為校正的基準(zhǔn)點(diǎn)。這里也同樣的需要限制誤差,之后可以得到一個(gè)中心點(diǎn)的AD值坐標(biāo)(xl[4],yl[4]),這個(gè)點(diǎn)的AD值我們就作為我們對(duì)比的基準(zhǔn)點(diǎn),即xl[4]=XLC,yl[4]=YLC;
  • 完成以上步驟則校正完成。下次點(diǎn)擊觸摸屏的時(shí)候獲取的邏輯值XL和YL,便可以按下以公式轉(zhuǎn)換為物理坐標(biāo):
    X = (XL - XLC) / KX + XC
    Y = (YL - YLC) / KY + YC
    最后一步的轉(zhuǎn)換公式可能不好理解,大家換個(gè)角度,如果我們求到的縮放比例是正確的,在取新的觸摸的時(shí)候,這個(gè)觸摸點(diǎn)的邏輯坐標(biāo)和物理坐標(biāo)的轉(zhuǎn)換,必然與中心點(diǎn)在兩方向上的縮放比例相等,用中學(xué)數(shù)學(xué)直線斜率相等的情況,變換便可得到上述公式。
    通過(guò)上述得到校準(zhǔn)參數(shù)后,在以后的使用中,我們把所有得到的物理坐標(biāo)都按照這個(gè)關(guān)系式來(lái)計(jì)算,得到的就是觸摸點(diǎn)的屏幕坐標(biāo)。為了省去每次都需要校準(zhǔn)的麻煩,我們保存這些參數(shù)到AT24Cxx的指定扇區(qū)地址,這樣只要校準(zhǔn)一次就可以重復(fù)使用這些參數(shù)了。
    根據(jù)上面的原理,我們?cè)O(shè)計(jì)的校準(zhǔn)函數(shù)tp_adjust如下:
  • /*** @brief 觸摸屏校準(zhǔn)代碼* @note 使用五點(diǎn)校準(zhǔn)法(具體原理請(qǐng)百度)* 本函數(shù)得到x軸/y軸比例因子xfac/yfac及物理中心坐標(biāo)值(xc,yc)等4個(gè)參數(shù)* 我們規(guī)定: 物理坐標(biāo)即AD采集到的坐標(biāo)值,范圍是0~4095.* 邏輯坐標(biāo)即LCD屏幕的坐標(biāo), 范圍為L(zhǎng)CD屏幕的分辨率.** @param 無(wú)* @retval 無(wú)*/ void tp_adjust(void) {uint16_t pxy[5][2]; /* 物理坐標(biāo)緩存值 */uint8_t cnt = 0;short s1, s2, s3, s4; /* 4個(gè)點(diǎn)的坐標(biāo)差值 */double px, py; /* X,Y軸物理坐標(biāo)比例,用于判定是否校準(zhǔn)成功 */uint16_t outtime = 0;cnt = 0;lcd_clear(WHITE); /* 清屏 */lcd_show_string(40, 40, 160, 100, 16, TP_REMIND_MSG_TBL, RED);/*顯示提示信息*/tp_draw_touch_point(20, 20, RED); /* 畫(huà)點(diǎn)1 */tp_dev.sta = 0; /* 消除觸發(fā)信號(hào) */while (1) /* 如果連續(xù)10秒鐘沒(méi)有按下,則自動(dòng)退出 */{tp_dev.scan(1); /* 掃描物理坐標(biāo) */if ((tp_dev.sta & 0xc000) == TP_CATH_PRES) { /* 按鍵按下了一次(此時(shí)按鍵松開(kāi)了.) */outtime = 0;tp_dev.sta &= ~TP_CATH_PRES; /* 標(biāo)記按鍵已經(jīng)被處理過(guò)了. */pxy[cnt][0] = tp_dev.x[0]; /* 保存X物理坐標(biāo) */pxy[cnt][1] = tp_dev.y[0]; /* 保存Y物理坐標(biāo) */cnt++;switch (cnt){case 1:tp_draw_touch_point(20, 20, WHITE); /* 清點(diǎn)1 */tp_draw_touch_point(lcddev.width - 20, 20, RED); /* 畫(huà)點(diǎn)2 */break;case 2:tp_draw_touch_point(lcddev.width - 20, 20, WHITE);/* 清點(diǎn)2 */tp_draw_touch_point(20, lcddev.height - 20, RED); /* 畫(huà)點(diǎn)3 */break;case 3:tp_draw_touch_point(20, lcddev.height-20, WHITE); /* 清點(diǎn)3*//* 畫(huà)點(diǎn)4 */tp_draw_touch_point(lcddev.width-20, lcddev.height-20, RED); break;case 4:lcd_clear(WHITE); /* 畫(huà)第五個(gè)點(diǎn)了, 直接清屏 */ /* 畫(huà)點(diǎn)5 */tp_draw_touch_point(lcddev.width / 2, lcddev.height / 2, RED); break;case 5: /* 全部5個(gè)點(diǎn)已經(jīng)得到 */s1=pxy[1][0]-pxy[0][0]; /*第2個(gè)點(diǎn)和第1個(gè)點(diǎn)的X軸物理坐標(biāo)差值(AD值)*/ s3=pxy[3][0]-pxy[2][0]; /*第4個(gè)點(diǎn)和第3個(gè)點(diǎn)的X軸物理坐標(biāo)差值(AD值)*/ s2=pxy[3][1]-pxy[1][1]; /*第4個(gè)點(diǎn)和第2個(gè)點(diǎn)的Y軸物理坐標(biāo)差值(AD值)*/ s4=pxy[2][1]-pxy[0][1]; /*第3個(gè)點(diǎn)和第1個(gè)點(diǎn)的Y軸物理坐標(biāo)差值(AD值)*/px = (double)s1 / s3; /* X軸比例因子 */py = (double)s2 / s4; /* Y軸比例因子 */if (px < 0)px = -px; /* 負(fù)數(shù)改正數(shù) */if (py < 0)py = -py; /* 負(fù)數(shù)改正數(shù) */if (px < 0.95 || px > 1.05 || py < 0.95 || py > 1.05 || abs(s1)>4095||abs(s2)>4095||abs(s3)>4095||abs(s4)>4095|| abs(s1)==0 ||abs(s2)==0||abs(s3)==0||abs(s4)==0){ /* 比例不合格,差值大于坐標(biāo)范圍或等于0,重繪校準(zhǔn)圖形 */cnt = 0;/* 清除點(diǎn)5 */tp_draw_touch_point(lcddev.width/2,lcddev.height/2, WHITE); tp_draw_touch_point(20, 20, RED); /* 重新畫(huà)點(diǎn)1 */tp_adjust_info_show(pxy, px, py); /* 顯示當(dāng)前信息,方便找問(wèn)題 */continue;}tp_dev.xfac = (float)(s1 + s3) / (2 * (lcddev.width - 40));tp_dev.yfac = (float)(s2 + s4) / (2 * (lcddev.height - 40));tp_dev.xc = pxy[4][0]; /* X軸,物理中心坐標(biāo) */tp_dev.yc = pxy[4][1]; /* Y軸,物理中心坐標(biāo) */lcd_clear(WHITE); /* 清屏 */lcd_show_string(35, 110, lcddev.width, lcddev.height, 16, "Touch Screen Adjust OK!", BLUE); /* 校正完成 */delay_ms(1000);tp_save_adjust_data();lcd_clear(WHITE); /* 清屏 */return; /* 校正完成 */}}delay_ms(10);outtime++;if (outtime > 1000){tp_get_adjust_data();break;}} }

    注意該函數(shù)里面多次使用了lcddev.width和lcddev.height,用于坐標(biāo)設(shè)置,故在程序調(diào)用前需要預(yù)先初始化LCD得到LCD的一些屏幕信息,主要是為了兼容不同尺寸的LCD(比如320240、480320和800*480的屏都可以兼容)。
    有了校準(zhǔn)參數(shù)后,由于我們需要頻繁地進(jìn)行屏幕坐標(biāo)和物理坐標(biāo)的轉(zhuǎn)換,我們?yōu)殡娮杵猎黾右粋€(gè)tp_scan(uint8_t mode)用于轉(zhuǎn)換,為了實(shí)際使用上更靈活,我們使這個(gè)參數(shù)支持物理坐標(biāo)和屏幕坐標(biāo),設(shè)計(jì)的函數(shù)如下:

    /*** @brief 觸摸按鍵掃描* @param mode: 坐標(biāo)模式* @arg 0, 屏幕坐標(biāo);* @arg 1, 物理坐標(biāo)(校準(zhǔn)等特殊場(chǎng)合用)** @retval 0, 觸屏無(wú)觸摸; 1, 觸屏有觸摸;*/ uint8_t tp_scan(uint8_t mode) {if (T_PEN == 0) /* 有按鍵按下 */{if (mode) /* 讀取物理坐標(biāo), 無(wú)需轉(zhuǎn)換 */{tp_read_xy2(&tp_dev.x[0], &tp_dev.y[0]);}else if (tp_read_xy2(&tp_dev.x[0], &tp_dev.y[0]))/* 讀取屏幕坐標(biāo), 需要轉(zhuǎn)換*/{ /* 將X軸 物理坐標(biāo)轉(zhuǎn)換成邏輯坐標(biāo)(即對(duì)應(yīng)LCD屏幕上面的X坐標(biāo)值) */tp_dev.x[0] = (signed short)(tp_dev.x[0] - tp_dev.xc) / tp_dev.xfac + lcddev.width / 2;/* 將Y軸 物理坐標(biāo)轉(zhuǎn)換成邏輯坐標(biāo)(即對(duì)應(yīng)LCD屏幕上面的Y坐標(biāo)值) */tp_dev.y[0] = (signed short)(tp_dev.y[0] - tp_dev.yc) / tp_dev.yfac + lcddev.height / 2;}if ((tp_dev.sta & TP_PRES_DOWN) == 0) /* 之前沒(méi)有被按下 */{tp_dev.sta = TP_PRES_DOWN | TP_CATH_PRES; /* 按鍵按下 */tp_dev.x[CT_MAX_TOUCH - 1] = tp_dev.x[0]; /* 記錄第一次按下時(shí)的坐標(biāo) */tp_dev.y[CT_MAX_TOUCH - 1] = tp_dev.y[0];}}else{if (tp_dev.sta & TP_PRES_DOWN) /* 之前是被按下的 */{tp_dev.sta &= ~TP_PRES_DOWN; /* 標(biāo)記按鍵松開(kāi) */}else /* 之前就沒(méi)有被按下 */{tp_dev.x[CT_MAX_TOUCH - 1] = 0;tp_dev.y[CT_MAX_TOUCH - 1] = 0;tp_dev.x[0] = 0xffff;tp_dev.y[0] = 0xffff;}}return tp_dev.sta & TP_PRES_DOWN; /* 返回當(dāng)前的觸屏狀態(tài) */ }

    要進(jìn)行電阻觸摸屏的觸摸掃描,只要調(diào)取tp_scan()函數(shù),就能靈活地得到觸摸坐標(biāo)。電阻屏的觸摸就講到這里。
    3. 電容屏觸摸驅(qū)動(dòng)代碼
    電容觸摸芯片使用的是IIC接口。IIC接口部分代碼,我們可以參考 myiic.c和myiic.h的代碼,為了使代碼獨(dú)立,我們?cè)凇癟OUCH”文件夾下也是采用軟件模擬IIC的方式實(shí)現(xiàn)ctiic.c和ctiic.h,這樣IO的使用更靈活,這里部分參考IIC章節(jié)的知識(shí)就可以了,這里不重復(fù)介紹了。
    電容觸摸芯片除了IIC接口相關(guān)引腳CT_SCL和CT_SDA,還有CT_INT和CT_RST,接口圖如圖37.3.3.2所示。

    圖37.3.3.1 電容觸摸芯片接口圖
    gt9xxx_init的實(shí)現(xiàn)也比較簡(jiǎn)單,實(shí)現(xiàn)CT_INT和CT_RST引腳初始化和調(diào)用ct_iic_init函數(shù)實(shí)現(xiàn)對(duì)CT_SDA和CT_SCL初始化。由于電容觸摸屏在設(shè)計(jì)時(shí)是根據(jù)屏幕進(jìn)行參數(shù)設(shè)計(jì)的,參數(shù)已經(jīng)保存在芯片內(nèi)部。所以在初始化后,就可以參考手冊(cè)推薦的IIC時(shí)序從相對(duì)應(yīng)的坐標(biāo)數(shù)據(jù)寄存器中把對(duì)應(yīng)的XY坐標(biāo)數(shù)據(jù)讀出來(lái),再通過(guò)數(shù)據(jù)整理轉(zhuǎn)成LCD坐標(biāo)。
    與電阻屏不同的是,我們是通過(guò)IIC讀取狀態(tài)寄存器的值并非引腳電平。而gt9xxx系列是支持中斷或輪詢方式得到觸摸狀態(tài),本實(shí)驗(yàn)使用的是輪詢方式:
    1、按照讀時(shí)序,先讀取寄存器0x814E,若當(dāng)前buffer(buffer status為1)數(shù)據(jù)準(zhǔn)備好,則依據(jù)有效觸點(diǎn)個(gè)數(shù)到相對(duì)應(yīng)的坐標(biāo)數(shù)據(jù)地址處進(jìn)行坐標(biāo)數(shù)據(jù)讀取。
    2、若在1中發(fā)現(xiàn)buffer數(shù)據(jù)(buffer status為0)未準(zhǔn)備好,則等待1ms再進(jìn)行讀取。
    這樣,gt9xxx_scan()函數(shù)的實(shí)現(xiàn)如下:這樣,gt9xxx_scan()函數(shù)的實(shí)現(xiàn)如下:
    /* GT9XXX 10個(gè)觸摸點(diǎn)(最多) 對(duì)應(yīng)的寄存器表 */

    const uint16_t GT9XXX_TPX_TBL[10] = { GT9XXX_TP1_REG, GT9XXX_TP2_REG, GT9XXX_TP3_REG, GT9XXX_TP4_REG, GT9XXX_TP5_REG, GT9XXX_TP6_REG, GT9XXX_TP7_REG, GT9XXX_TP8_REG, GT9XXX_TP9_REG, GT9XXX_TP10_REG, }; /*** @brief 掃描觸摸屏(采用查詢方式)* @param mode : 電容屏未用到次參數(shù), 為了兼容電阻屏* @retval 當(dāng)前觸屏狀態(tài)* @arg 0, 觸屏無(wú)觸摸; * @arg 1, 觸屏有觸摸;*/ uint8_t gt9xxx_scan(uint8_t mode) {uint8_t buf[4];uint8_t i = 0;uint8_t res = 0;uint16_t temp;uint16_t tempsta;static uint8_t t = 0; /* 控制查詢間隔,從而降低CPU占用率 */ t++;if ((t % 10) == 0 || t < 10) { /* 空閑時(shí),每進(jìn)入10次CTP_Scan函數(shù)才檢測(cè)1次,從而節(jié)省CPU使用率 */gt9xxx_rd_reg(GT9XXX_GSTID_REG, &mode, 1); /* 讀取觸摸點(diǎn)的狀態(tài) */if ((mode & 0X80) && ((mode & 0XF) <= g_gt_tnum)){i = 0;gt9xxx_wr_reg(GT9XXX_GSTID_REG, &i, 1); /* 清標(biāo)志 */}if ((mode & 0XF) && ((mode & 0XF) <= g_gt_tnum)){ /* 將點(diǎn)的個(gè)數(shù)轉(zhuǎn)換為1的位數(shù),匹配tp_dev.sta定義 */temp = 0XFFFF << (mode & 0XF); tempsta = tp_dev.sta; /* 保存當(dāng)前的tp_dev.sta值 */tp_dev.sta = (~temp) | TP_PRES_DOWN | TP_CATH_PRES; tp_dev.x[g_gt_tnum - 1] = tp_dev.x[0]; /* 保存觸點(diǎn)0的數(shù)據(jù) */tp_dev.y[g_gt_tnum - 1] = tp_dev.y[0];for (i = 0; i < g_gt_tnum; i++){if (tp_dev.sta & (1 << i)) /* 觸摸有效? */{gt9xxx_rd_reg(GT9XXX_TPX_TBL[i], buf, 4); /* 讀取XY坐標(biāo)值 */if (lcddev.id == 0X5510) /* 4.3寸800*480 MCU屏 */{if (tp_dev.touchtype & 0X01) /* 橫屏 */{tp_dev.y[i] = ((uint16_t)buf[1] << 8) + buf[0];tp_dev.x[i] = 800 - (((uint16_t)buf[3] << 8) + buf[2]);}else{tp_dev.x[i] = ((uint16_t)buf[1] << 8) + buf[0];tp_dev.y[i] = ((uint16_t)buf[3] << 8) + buf[2];}}else /* 其他型號(hào) */{if (tp_dev.touchtype & 0X01) /* 橫屏 */{tp_dev.x[i] = (((uint16_t)buf[1] << 8) + buf[0]);tp_dev.y[i] = (((uint16_t)buf[3] << 8) + buf[2]);}else{tp_dev.x[i]=lcddev.width-(((uint16_t)buf[3]<<8)+buf[2]);tp_dev.y[i] = ((uint16_t)buf[1] << 8) + buf[0];}}//printf("x[%d]:%d,y[%d]:%d\r\n",i,tp_dev.x[i],i,tp_dev.y[i]);}}res = 1;if (tp_dev.x[0] > lcddev.width || tp_dev.y[0] > lcddev.height) { /* 非法數(shù)據(jù)(坐標(biāo)超出了) */if ((mode & 0XF) > 1) /*有其他點(diǎn)有數(shù)據(jù),則復(fù)第二個(gè)觸點(diǎn)的數(shù)據(jù)到第一個(gè)觸點(diǎn) */{tp_dev.x[0] = tp_dev.x[1];tp_dev.y[0] = tp_dev.y[1];t = 0; /* 觸發(fā)一次,則會(huì)最少連續(xù)監(jiān)測(cè)10次,從而提高命中率 */}else /* 非法數(shù)據(jù),則忽略此次數(shù)據(jù)(還原原來(lái)的) */{tp_dev.x[0] = tp_dev.x[g_gt_tnum - 1];tp_dev.y[0] = tp_dev.y[g_gt_tnum - 1];mode = 0X80;tp_dev.sta = tempsta; /* 恢復(fù)tp_dev.sta */}}else {t = 0; /* 觸發(fā)一次,則會(huì)最少連續(xù)監(jiān)測(cè)10次,從而提高命中率 */}}}if ((mode & 0X8F) == 0X80) /* 無(wú)觸摸點(diǎn)按下 */{if (tp_dev.sta & TP_PRES_DOWN) /* 之前是被按下的 */{tp_dev.sta &= ~TP_PRES_DOWN; /* 標(biāo)記按鍵松開(kāi) */}else /* 之前就沒(méi)有被按下 */{tp_dev.x[0] = 0xffff;tp_dev.y[0] = 0xffff;tp_dev.sta &= 0XE000; /* 清除點(diǎn)有效標(biāo)記 */} }if (t > 240)t = 10; /* 重新從10開(kāi)始計(jì)數(shù) */return res; } 大家可以打開(kāi)gt9xxx芯片對(duì)應(yīng)的編程手冊(cè),對(duì)照時(shí)序,即可理解上述的實(shí)現(xiàn)過(guò)程,只是程序中為了匹配多種屏幕和橫屏顯示,添加了一些代碼。 電容屏驅(qū)動(dòng)ft5206.c/ft5206.h的驅(qū)動(dòng)實(shí)現(xiàn)與gt9xxx的實(shí)現(xiàn)類似,大家參考本例程源碼即可。 電容屏的觸摸實(shí)驗(yàn)代碼講解到這里。 4. main函數(shù)和測(cè)試代碼 在main.c里面編程如下代碼: void rtp_test(void) {uint8_t key;uint8_t i = 0;while (1){key = key_scan(0);tp_dev.scan(0);if (tp_dev.sta & TP_PRES_DOWN) /* 觸摸屏被按下 */{if (tp_dev.x[0] < lcddev.width && tp_dev.y[0] < lcddev.height){if (tp_dev.x[0] > (lcddev.width - 24) && tp_dev.y[0] < 16){load_draw_dialog(); /* 清除 */}else {tp_draw_big_point(tp_dev.x[0], tp_dev.y[0], RED); /* 畫(huà)點(diǎn) */}}}else {delay_ms(10); /* 沒(méi)有按鍵按下的時(shí)候 */}if (key == KEY0_PRES) /* KEY0按下,則執(zhí)行校準(zhǔn)程序 */{lcd_clear(WHITE); /* 清屏 */tp_adjust(); /* 屏幕校準(zhǔn) */tp_save_adjust_data();load_draw_dialog();}i++;if (i % 20 == 0)LED0_TOGGLE();} }

    /* 10個(gè)觸控點(diǎn)的顏色(電容觸摸屏用) */
    const uint16_t POINT_COLOR_TBL[10] = {RED, GREEN, BLUE, BROWN, YELLOW, MAGENTA, CYAN, LIGHTBLUE, BRRED, GRAY};

    void ctp_test(void) {uint8_t t = 0;uint8_t i = 0;uint16_t lastpos[10][2]; /* 最后一次的數(shù)據(jù) */uint8_t maxp = 5;if (lcddev.id == 0X1018)maxp = 10;while (1){tp_dev.scan(0);for (t = 0; t < maxp; t++){if ((tp_dev.sta) & (1 << t)){ /* 坐標(biāo)在屏幕范圍內(nèi) */if (tp_dev.x[t] < lcddev.width && tp_dev.y[t] < lcddev.height) {if (lastpos[t][0] == 0XFFFF){lastpos[t][0] = tp_dev.x[t];lastpos[t][1] = tp_dev.y[t];}lcd_draw_bline(lastpos[t][0], lastpos[t][1], tp_dev.x[t], tp_dev.y[t], 2, POINT_COLOR_TBL[t]); /* 畫(huà)線 */lastpos[t][0] = tp_dev.x[t];lastpos[t][1] = tp_dev.y[t];if (tp_dev.x[t] > (lcddev.width - 24) && tp_dev.y[t] < 20){load_draw_dialog();/* 清除 */}}}else {lastpos[t][0] = 0XFFFF;}}delay_ms(10);i++;if (i % 20 == 0)LED0_TOGGLE();} }int main(void) {sys_cache_enable(); /* 打開(kāi)L1-Cache */HAL_Init(); /* 初始化HAL庫(kù) */sys_stm32_clock_init(240, 2, 2, 4); /* 設(shè)置時(shí)鐘, 480Mhz */delay_init(480); /* 延時(shí)初始化 */usart_init(115200); /* 串口初始化為115200 */mpu_memory_protection(); /* 保護(hù)相關(guān)存儲(chǔ)區(qū)域 */led_init(); /* 初始化LED */lcd_init(); /* 初始化LCD */key_init(); /* 初始化按鍵 */ tp_dev.init(); /* 觸摸屏初始化 */lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);lcd_show_string(30, 70, 200, 16, 16, "TOUCH TEST", RED); lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);if (tp_dev.touchtype != 0XFF) { /* 電阻屏才顯示 */lcd_show_string(30, 110, 200, 16, 16, "Press KEY0 to Adjust", RED); }delay_ms(1500); load_draw_dialog();if (tp_dev.touchtype & 0X80){ctp_test(); /* 電容屏測(cè)試 */}else {rtp_test(); /* 電阻屏測(cè)試 */} }

    上面沒(méi)有把main.c全部代碼列出來(lái),只是列出重要函數(shù),這里簡(jiǎn)單介紹一下這三個(gè)函數(shù)。
    rtp_test,該函數(shù)用于電阻觸摸屏的測(cè)試,該函數(shù)代碼比較簡(jiǎn)單,就是掃描按鍵和觸摸屏,如果觸摸屏有按下,則在觸摸屏上面劃線,如果按中“RST”區(qū)域,則執(zhí)行清屏。如果按鍵KEY0按下,則執(zhí)行觸摸屏校準(zhǔn)。
    ctp_test,該函數(shù)用于電容觸摸屏的測(cè)試,由于我們采用tp_dev.sta來(lái)標(biāo)記當(dāng)前按下的觸摸屏點(diǎn)數(shù),所以判斷是否有電容觸摸屏按下,也就是判斷tp_dev.sta的最低5位,如果有數(shù)據(jù),則畫(huà)線,如果沒(méi)數(shù)據(jù)則忽略,且5個(gè)點(diǎn)畫(huà)線的顏色各不一樣,方便區(qū)分。另外,電容觸摸屏不需要校準(zhǔn),所以沒(méi)有校準(zhǔn)程序。
    main函數(shù),則比較簡(jiǎn)單,初始化相關(guān)外設(shè),然后根據(jù)觸摸屏類型,去選擇執(zhí)行ctp_test還是rtp_test。
    軟件部分就介紹到這里,接下來(lái)看看下載驗(yàn)證。
    37.4 下載驗(yàn)證
    在代碼編譯成功之后,我們通過(guò)下載代碼到開(kāi)發(fā)板上,電阻觸摸屏測(cè)試如圖37.4.1所示界面:

    圖37.4.1 電阻觸摸屏測(cè)試程序運(yùn)行效果
    圖中我們?cè)陔娮杵辽袭?huà)了一些內(nèi)容,右上角的RST可以用來(lái)清屏,點(diǎn)擊該區(qū)域,即可清屏重畫(huà)。另外,按 KEY0 可以進(jìn)入校準(zhǔn)模式,如果發(fā)現(xiàn)觸摸屏不準(zhǔn),則可以按 KEY0,進(jìn)入校準(zhǔn), 重新校準(zhǔn)一下,即可正常使用。
    如果是電容觸摸屏,測(cè)試界面如圖37.4.2所示:

    圖37.4.2 電容觸摸屏測(cè)試界面
    圖中,同樣輸入了一些內(nèi)容。電容屏支持多點(diǎn)觸摸,每個(gè)點(diǎn)的顏色都不一樣,圖中的波浪線就是三點(diǎn)觸摸畫(huà)出來(lái)的,最多可以5點(diǎn)觸摸。按右上角的RST標(biāo)志,可以清屏。電容屏無(wú)需校準(zhǔn),所以按KEY0無(wú)效。KEY0校準(zhǔn)僅對(duì)電阻屏有效。

    總結(jié)

    以上是生活随笔為你收集整理的【正点原子STM32连载】第三十七章 触摸屏实验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。