一、FSMC外擴(kuò)內(nèi)存SRAM
FSMC(Flexible Static Memory Controller,靈活的靜態(tài)存儲(chǔ)控制器)是STM32系列采用的一種新型的存儲(chǔ)器擴(kuò)展技術(shù),使用FSMC外設(shè)來管理擴(kuò)展的存儲(chǔ)器。在外部存儲(chǔ)器擴(kuò)展方面具有獨(dú)特的優(yōu)勢(shì),可根據(jù)系統(tǒng)的應(yīng)用需要,方便地進(jìn)行不同類型大容量靜態(tài)存儲(chǔ)器的擴(kuò)展。FSMC可以用于驅(qū)動(dòng)包括SRAM、NOR FLASH以及NAND FLSAH類型的存儲(chǔ)器,不能驅(qū)動(dòng)如SDRAM這種動(dòng)態(tài)的存儲(chǔ)器。而在STM32F429系列的控制器中,它具有FMC外設(shè),支持控制SDRAM存儲(chǔ)器。
FSMC是STM32系列中內(nèi)部集成256 KB以上FlaSh,后綴為xC、xD和xE的高存儲(chǔ)密度微控制器特有的存儲(chǔ)控制機(jī)制。之所以稱為“可變”,是由于通過對(duì)特殊功能寄存器的設(shè)置,F(xiàn)SMC能夠根據(jù)不同的外部存儲(chǔ)器類型,發(fā)出相應(yīng)的數(shù)據(jù)/地址/控制信號(hào)類型以匹配信號(hào)的速度,從而使得STM32系列微控制器不僅能夠應(yīng)用各種不同類型、不同速度的外部靜態(tài)存儲(chǔ)器,而且能夠在不增加外部器件的情況下同時(shí)擴(kuò)展多種不同類型的靜態(tài)存儲(chǔ)器,滿足系統(tǒng)設(shè)計(jì)對(duì)存儲(chǔ)容量、產(chǎn)品體積以及成本的綜合要求。
- 常用存儲(chǔ)器介紹
存儲(chǔ)器的種類:存儲(chǔ)器是計(jì)算機(jī)結(jié)構(gòu)的重要組成部分。存儲(chǔ)器是用來存儲(chǔ)程序代碼和數(shù)據(jù)的部件,有了存儲(chǔ)器計(jì)算機(jī)才具有記憶功能。
1.RAM存儲(chǔ)器:RAM是“Random Access Memory”的縮寫,被譯為隨機(jī)存儲(chǔ)器。所謂“隨機(jī)存取”,指的是當(dāng)存儲(chǔ)器中的消息被讀取或?qū)懭霑r(shí),所需要的時(shí)間與這段信息所在的位置無關(guān)。這個(gè)詞的由來是因?yàn)樵缙谟?jì)算機(jī)曾使用磁鼓作為存儲(chǔ)器,磁鼓是順序讀寫設(shè)備,而RAM可隨讀取其內(nèi)部任意地址的數(shù)據(jù),時(shí)間都是相同的,因此得名。
實(shí)際上現(xiàn)在RAM已經(jīng)專門用于指代作為計(jì)算機(jī)內(nèi)存的易失性半導(dǎo)體存儲(chǔ)器。根據(jù)RAM的存儲(chǔ)機(jī)制,又分為動(dòng)態(tài)隨機(jī)存儲(chǔ)器DRAM(Dynamic RAM)以及靜態(tài)隨機(jī)存儲(chǔ)器SRAM(Static RAM)兩種。
2.DRAM存儲(chǔ)器:動(dòng)態(tài)隨機(jī)存儲(chǔ)器 DRAM的存儲(chǔ)單元以電容的電荷來表示數(shù)據(jù),有電荷代表 1,無電荷代表 0,,代表 1 的電容會(huì)放電,代表 0的電容會(huì)吸收電荷,因此它需要定期刷新操作,這就是“動(dòng)態(tài)(Dynamic)”。
根據(jù) DRAM的通訊方式,又分為同步和異步兩種,這兩種方式根據(jù)通訊時(shí)是否需要使用時(shí)鐘信號(hào)來區(qū)分。由于使用時(shí)鐘同步的通訊速度更快,所以同步 DRAM 使用更為廣泛,這種DRAM 被稱為 SDRAM(Synchronous DRAM)。常見的DRAM大多是SDRAM。
為了進(jìn)一步提高 SDRAM的通訊速度,人們?cè)O(shè)計(jì)了DDR SDRAM (Double DataRate SDRAM)存儲(chǔ)器。DDR SDRAM在時(shí)鐘的上升沿及下降沿各表示一個(gè)數(shù)據(jù),也就是說在 1 個(gè)時(shí)鐘周期內(nèi)可以表示 2數(shù)據(jù),在時(shí)鐘頻率同樣的情況下,提高了一倍的速度。
DDRII和 DDRIII,它們的通訊方式并沒有區(qū)別主要是通訊同步時(shí)鐘的頻率提高了。
3.SRAM存儲(chǔ)器:靜態(tài)隨機(jī)存儲(chǔ)器 SRAM的存儲(chǔ)單元以鎖存器來存儲(chǔ)數(shù)據(jù),見圖。這種電路結(jié)構(gòu)不需要定時(shí)刷新充電,就能保持狀態(tài)(當(dāng)然,如果斷電了,數(shù)據(jù)還是會(huì)丟失的),所以這種存儲(chǔ)器被稱為“靜態(tài)(Static)”RAM。
同樣地,SRAM 根據(jù)其通訊方式也分為同步SRAM和異步 SRAM,相對(duì)來說,異步SRAM用得較多。
所以在實(shí)際應(yīng)用場(chǎng)合中,SRAM一般只用于CPU內(nèi)部的高速緩存(Cache),而外部擴(kuò)展的內(nèi)存一般使用DRAM。DRAM和SRAM的特性對(duì)比如下:
4.非易失性存儲(chǔ)器:非易失性存儲(chǔ)器種類非常多,半導(dǎo)體類的有 ROM 和 FLASH,而其它的則包括光盤、軟盤及機(jī)械硬盤。
ROM是“Read Only Memory”的縮寫,意為只能讀的存儲(chǔ)器。由于技術(shù)的發(fā)展,后來設(shè)計(jì)出了可以方便寫入數(shù)據(jù)的ROM,而這個(gè)“Read Only Memory”的名稱被沿用下來了,現(xiàn)在一般用于指代非易失性半導(dǎo)體存儲(chǔ)器,包括FLASH存儲(chǔ)器,有些人也把它歸到ROM類里邊。
FLASH存儲(chǔ)器又稱為閃存,它也是可重復(fù)擦寫的儲(chǔ)器,部分書籍會(huì)把FLASH存儲(chǔ)器稱為FLASH ROM,但它的容量一般比EEPROM大得多,且在擦除時(shí),一般以多個(gè)字節(jié)為單位。根據(jù)存儲(chǔ)單元電路的不同,F(xiàn)LASH存儲(chǔ)器又分為NOR FLASH和NAND FLASH。
- SRAM控制原理
STM32控制器芯片內(nèi)部有一定大小的SRAM及FLASH作為內(nèi)存和程序存儲(chǔ)空間,但當(dāng)程序較大,內(nèi)存和程序空間不足時(shí),就需要在STM32芯片的外部擴(kuò)展存儲(chǔ)器了。STM32F407系列芯片可以擴(kuò)展外部SRAM用作內(nèi)存。
給STM32芯片擴(kuò)展內(nèi)存與給PC擴(kuò)展內(nèi)存的原理是一樣的,只是PC上一般以內(nèi)存條的形式擴(kuò)展,而且內(nèi)存條實(shí)質(zhì)是由多個(gè)內(nèi)存顆粒(即SDRAM芯片)組成的通用標(biāo)準(zhǔn)模塊,而STM32擴(kuò)展時(shí),直接與SRAM芯片連接。
- SRAM外觀與內(nèi)部框圖
①地址數(shù)據(jù)接口:A0-A17用作地址線,I/O0-I/O15用于傳輸數(shù)據(jù),一次性傳輸16位數(shù)據(jù),并行傳輸
②存儲(chǔ)矩陣:每個(gè)存儲(chǔ)單元16位,SRAM內(nèi)部包含的存儲(chǔ)陣列,可以把它理解成一張表格,數(shù)據(jù)就填在這張表格上。和表格查找一樣,指定一個(gè)行地址和列地址,就可以精確地找到目標(biāo)單元格而這樣的表則被稱為存儲(chǔ)矩陣。
當(dāng)選中一個(gè)數(shù)據(jù)單元后,可以通過UB#或 LB#其中一個(gè)設(shè)置為低電平,I/O會(huì)對(duì)應(yīng)輸出該地址的高 8位和低 8位數(shù)據(jù),因此它們被稱為數(shù)據(jù)掩碼信號(hào)。
③控制電路:控制電路主要包含了片選、讀寫使能以及上面提到的寬度控制信號(hào)UB#和LB#。利用CE#片選信號(hào),可以把多個(gè)SRAM芯片組成一個(gè)大容量的內(nèi)存條。OE#和WE#可以控制讀寫使能,防止誤操作。整個(gè)數(shù)據(jù)的傳輸都是在控制電路的控制下完成的,當(dāng)想要讀數(shù)據(jù)時(shí),使用到OE線,把它拉低;想要寫數(shù)據(jù)時(shí)應(yīng)當(dāng)把WE線拉低;只想讀存儲(chǔ)單元的高8位,則拉低UB線;只想讀存儲(chǔ)單元的低8位,則拉低LB線;一次性讀26位都拉低即可。
SRAM的控制比較簡單,只要控制信號(hào)線使能了訪問,從地址線輸入要訪問的地址,即可從I/O數(shù)據(jù)線寫入或讀出數(shù)據(jù)。
- SRAM的讀時(shí)序
對(duì)什么時(shí)候使能讀信號(hào)、什么時(shí)候拉低片選、地址線號(hào)線維持的時(shí)間等都有一定的要求,重點(diǎn)時(shí)序包括:讀周期時(shí)間(tRC)、地址建立時(shí)間(tAA)、OE建立時(shí)間(tDOE),這些具體的時(shí)間都可在SRAM芯片的數(shù)據(jù)手冊(cè)上找到。
- SRAM的寫時(shí)序
重點(diǎn)時(shí)序:寫周期時(shí)間(tWC)、地址建立時(shí)間(tSA)、WE脈寬(tPWE)
- SRAM的讀寫流程
讀寫時(shí)序的流程很類似,過程如下:
(1) 主機(jī)使用地址信號(hào)線發(fā)出要訪問的存儲(chǔ)器目標(biāo)地址;
(2) 控制片選信號(hào)CE#使能存儲(chǔ)器芯片;
(3) 若是要進(jìn)行讀操作,則控制讀使能信號(hào)OE#表示要讀數(shù)據(jù),若進(jìn)行寫操作則控制寫使能信號(hào)WE#表示要寫數(shù)據(jù);
(4) 使用掩碼信號(hào)LB#與UB#指示要訪問目標(biāo)地址的高、低字節(jié)部分;
(5) 若是讀取過程,存儲(chǔ)器會(huì)通過數(shù)據(jù)線向主機(jī)輸出目標(biāo)數(shù)據(jù),若是寫入過程,主要使用數(shù)據(jù)線向存儲(chǔ)器傳輸目標(biāo)數(shù)據(jù)。
- STM32-FSMC控制器框圖分析
①通訊引腳:由于控制不同類型存儲(chǔ)器的時(shí)候會(huì)有一些不同的引腳,看起來有非常多,其中地址線FSMC_A和數(shù)據(jù)線FSMC_D是所有控制器都共用的。
注:其中比較特殊的FSMC_NE是用于控制SRAM芯片的控制信號(hào)線,STM32具有FSMC_NE1/2/3/4號(hào)引腳,不同的引腳對(duì)應(yīng)STM32內(nèi)部不同的地址區(qū)域。
②存儲(chǔ)器控制器:上面不同類型的引腳是連接到FSMC內(nèi)部對(duì)應(yīng)的存儲(chǔ)控制器中NOR/PSRAM/SRAM設(shè)備使用相同的控制器,NAND/PC卡設(shè)備使用相同的控制器,不同的控制器有專用的寄存器用于配置其工作模式。
控制SRAM的有FSMC_ BCR 、FSMC_ BTR以及FSMC_BWTR寄存器。每種寄存器都有4個(gè),分別對(duì)應(yīng)于4個(gè)不同的存儲(chǔ)區(qū)域,各種寄存器介紹如下:
a.FSMC_BCR控制寄存器:可配置要控制的存儲(chǔ)器類型、數(shù)據(jù)線寬度以及信號(hào)有效極性能 參數(shù)。
b.FMC_BTR時(shí)序寄存器:用于配置SRAM訪問時(shí)的各種時(shí)間延遲,如數(shù)據(jù)保持時(shí)間、地址保持時(shí)間等。
c.FMC_BWTR寫時(shí)序寄存器:與FMC_BTR寄存器控制的參數(shù)類似,它專門用于控制寫時(shí)序的時(shí)間參數(shù)。
③時(shí)鐘控制邏輯:FSMC外設(shè)掛載在AHB總線上,時(shí)鐘信號(hào)來自于HCLK(默認(rèn)168MHz),控制器的同步時(shí)鐘輸出就是由它分頻得到。
例如,NOR控制器的FSMC_CLK引腳輸出的時(shí)鐘,它可用于與同步類型的SRAM芯片進(jìn)行同步通訊,它的時(shí)鐘頻率可通過FSMC_BTR寄存器的CLKDIV位配置,可以配置為HCLK的1/2或1/3,也就是說,若它與同步類型的SRAM通訊時(shí),同步時(shí)鐘最高頻率為84MHz。
后面示例中的SRAM為異步類型的存儲(chǔ)器,不使用同步時(shí)鐘信號(hào),所以時(shí)鐘分頻配置不起作用。
- FSMC地址映射
FSMC連接好外部的存儲(chǔ)器并初始化后,就可以直接通過訪問地址來讀寫數(shù)據(jù)。
FSMC訪問存儲(chǔ)器的方式與I2C EEPROM、SPI FLASH的不一樣,后兩種方式都需要控制I2C或SPI總線給存儲(chǔ)器發(fā)送地址,然后獲取數(shù)據(jù);在程序里,這個(gè)地址和數(shù)據(jù)都需要分開使用不同的變量存儲(chǔ),并且訪問時(shí)還需要使用代碼控制發(fā)送讀寫命令。
而使用FSMC外接存儲(chǔ)器時(shí),其存儲(chǔ)單元是映射到STM32的內(nèi)部尋址空間的;在程序里,定義一個(gè)指向這些地址的指針,然后就可以通過指針直接修改該存儲(chǔ)單元的內(nèi)容,F(xiàn)SMC外設(shè)會(huì)自動(dòng)完成數(shù)據(jù)訪問過程,讀寫命令之類的操作不需要程序控制。
FSMC的NOR/PSRAM/SRAM/NAND FLASH以及PC卡的地址被映射到了External RAM地址空間內(nèi),使得訪問FSMC控制的存儲(chǔ)器時(shí),就跟訪問STM32的片上外設(shè)寄存器一樣。
FSMC把整個(gè)External RAM存儲(chǔ)區(qū)域分成了4個(gè)Bank區(qū)域,并分配了地址范圍及適用的存儲(chǔ)器類型,如NOR及SRAM存儲(chǔ)器只能使用Bank1的地址。
在NOR及SRAM區(qū)域,每個(gè)Bank的內(nèi)部又分成了4個(gè)小塊,每個(gè)小塊有相應(yīng)的控制引腳用于連接片選信號(hào),如FSMC_NE[4:1]信號(hào)線可用于選擇BANK1內(nèi)部的4小塊地址區(qū)域,當(dāng)STM32訪問0x68000000-0x6BFFFFFF地址空間時(shí),會(huì)訪問到Bank1的第3小塊區(qū)域,相應(yīng)的FSMC_NE3信號(hào)線會(huì)輸出控制信號(hào)。
FSMC讀時(shí)序如下:
FSMC寫時(shí)序如下:
- FSMC外擴(kuò)SRAM實(shí)例
實(shí)驗(yàn)要求:配置STM32F407的FSMC以支持驅(qū)動(dòng)IS61LV25616 SRAM芯片的讀寫。
SRAM電器接線圖如下
實(shí)驗(yàn)步驟:
1.配置RCC
2.配置FSMC管腳
3.配置FSMC協(xié)議
tRC =ADDSET + DATAST,tRC是SRAM芯片的一個(gè)需求時(shí)間,可以通過查詢芯片手冊(cè)得到tRC的最小滿足時(shí)間。IS61LV25616的型號(hào)有IS61LV25616-8、IS61LV25616-10、IS61LV25616-12、IS61LV25616-15,分別代表高速訪問時(shí)間為8,10,12,15ns,本實(shí)驗(yàn)中以-10為例可知tRC最小時(shí)間為10ns ,tDOE最大時(shí)間為5ns,在配置是應(yīng)當(dāng)將tRC的值設(shè)置為大于10ns
tDOE =DATASET,tDOE就是讀使能信號(hào)拉低之后到得到有效數(shù)據(jù)之間的時(shí)間。
ADDSET一般是地址發(fā)出后到讀使能信號(hào)開始拉低的時(shí)間,對(duì)于SRAM未作要求,大于0即可。FSMC的主頻為168M,一個(gè)周期約等于6ns
4.編寫代碼
//main.c
#define SRAM_BANK_ADDR ((uint32_t)0x68000000)//外擴(kuò)存儲(chǔ)器首地址
int main(void)
{
uint8_t *p = (uint8_t *)SRAM_BANK_ADDR ;
uint8_t i;
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_FSMC_Init();
printf("this is fsmc sram testn");
//寫內(nèi)存
for(i=1; i<=10; i++)
{
p[i] = i;
}
//讀內(nèi)存
for(i=1; i<=10; i++)
{
printf("p[%d] = %dn",i, p[i] );
}
while (1)
{
Remote_Infrared_KeyDeCode();
}
}
二、觸摸屏專題講解
- 觸摸屏簡介
觸摸屏又稱觸控面板,它是一種把觸摸位置轉(zhuǎn)化成坐標(biāo)數(shù)據(jù)的輸入設(shè)備,可以響應(yīng)用戶點(diǎn)擊的位置(對(duì)點(diǎn)擊事件進(jìn)行響應(yīng)),是典型的人交互設(shè)備,使得人機(jī)交互更加人性化。根據(jù)觸摸屏的檢測(cè)原理,主要分為電阻式觸摸屏和電容式觸摸屏。
電阻觸摸屏:電阻屏造價(jià)便宜,能適應(yīng)較惡劣的環(huán)境,但它只支持單點(diǎn)觸控(一次只能檢測(cè)面板上的一個(gè)觸摸位置,不管有多少觸點(diǎn)同時(shí)被觸控,一次只檢測(cè)一個(gè)),觸摸時(shí)需要一定的壓力,使用久了容易造成表面磨損,影響屏幕壽命。
電容觸摸屏:具有支持多點(diǎn)觸控(如玩手機(jī)游戲時(shí)可以同時(shí)有多個(gè)控鍵發(fā)生作用)、檢測(cè)精度高的特點(diǎn),電容屏通過與導(dǎo)電物體產(chǎn)生的電容效應(yīng)來檢測(cè)觸摸動(dòng)作,只能感應(yīng)導(dǎo)電物體的觸摸,濕度較大或屏幕表面有水珠時(shí)會(huì)影響電容屏的檢測(cè)效果。
目前電容式觸摸屏被大部分應(yīng)用在智能手機(jī)、平板電腦等電子設(shè)備中,而在汽車導(dǎo)航、工控機(jī)等設(shè)備中電阻式觸摸屏仍占主流。
觸摸屏與顯示屏的關(guān)系:觸摸屏實(shí)際上是在液晶屏上面貼了一層大小相等的透明的薄膜,這個(gè)薄膜能夠感知觸碰,根據(jù)薄膜反饋的觸摸位置,我們就能知道用戶觸碰在屏幕的什么位地方了。為了美觀,電路一般都鋪設(shè)在顯示屏的邊框上進(jìn)行流通,最終通過fpc排線流出,所以絕對(duì)的無邊框屏幕時(shí)不存在的。
- 電容觸摸屏檢測(cè)原理
與電阻式觸摸屏不同,電容式觸摸屏不需要通過壓力使觸點(diǎn)變形。它的基本原理是利用充電時(shí)間檢測(cè)電容大小,若手指觸摸屏幕,會(huì)影響觸摸點(diǎn)附近兩個(gè)電極之間的耦合,從而改變兩個(gè)電極之間的電容量,若檢測(cè)到某電容的電容量發(fā)生了改變,即可獲知該電容處有觸摸動(dòng)作從而通過檢測(cè)出電容值的變化來獲知觸摸信號(hào)。
簡單理解為么一個(gè)電容容量大小固定,充電時(shí)間不變。但是手指觸摸時(shí),由于手指的導(dǎo)電性,會(huì)有一部分電流流過手指使得電容的充電速度變慢,充電時(shí)間變長,由充電時(shí)間發(fā)生變化可檢測(cè)到觸點(diǎn)的位置。
- 電阻觸摸屏檢測(cè)原理
電阻式的觸摸屏結(jié)構(gòu)主要由表面硬涂層、兩個(gè)ITO層、間隔點(diǎn)以及玻璃底層構(gòu)成,這些結(jié)構(gòu)層都是透明的,整個(gè)觸摸屏覆蓋在液晶面板上,透過觸摸屏可看到液晶面板。表面涂層起到保護(hù)作用,玻璃底層起承載的作用,而兩個(gè)ITO層是觸摸屏的關(guān)鍵結(jié)構(gòu),它們是涂有銦錫金屬氧化物的導(dǎo)電層。兩個(gè)ITO層之間使用間隔點(diǎn)使兩層分開,當(dāng)觸摸屏表面受到壓力時(shí),表面彎曲使得上層ITO與下層ITO接觸,在觸點(diǎn)處連通電路。
我們可以把上面的ITO層看作X層,下面的ITO層看作Y層,每一層都會(huì)加上一個(gè)電壓,ITO層是均勻的導(dǎo)電物質(zhì),那么整個(gè)一層的電阻是呈線性排布,不同地方的電阻大小不同。流過ITO層的電流恒定不變,假設(shè)對(duì)X層和Y層都加上一個(gè)5V電壓,當(dāng)有觸控時(shí),上下兩層檢測(cè)到觸點(diǎn)的電壓都為2.5V,那么理想狀態(tài)下觸點(diǎn)為中心,假設(shè)上層檢測(cè)到觸點(diǎn)的電壓3V,那么上層觸點(diǎn)在順著電壓方向屏幕的3/5處,下層檢測(cè)到觸點(diǎn)的電壓2V,下層觸點(diǎn)在順著電壓方向屏幕的2/5處,這樣就相當(dāng)于確定了一個(gè)坐標(biāo),從而確定觸點(diǎn)的位置。
兩個(gè)ITO涂層的兩端分別引出X-、X+、Y-、Y+四個(gè)電極,這是電阻屏最常見的四線結(jié)構(gòu),通過這些電極,外部電路向這兩個(gè)涂層可以施加勻強(qiáng)電場(chǎng)或檢測(cè)電壓。
- 電阻觸摸屏校準(zhǔn)
電阻觸摸屏的校準(zhǔn)就是確定位置和對(duì)應(yīng)的電壓值之間的比例關(guān)系。假設(shè)添加X的場(chǎng)電壓為5V,理想狀體下,我們希望檢測(cè)x方向最左側(cè)為0V,最右側(cè)為5V,但是實(shí)際上最左側(cè)可能為大于0V,最右側(cè)小于5V,但是ITO層在電壓方向的電壓變化仍是線性關(guān)系,那么最左側(cè)的電壓值稱為偏移值,線性關(guān)系稱為斜率。Y層同理。
X0 = xfac*ADC_X + xoff,xfac是斜率, xoff是偏移值
Y0 = yfac*ADC_Y + yoff,yfac是斜率, yoff是偏移值
- 觸摸控制芯片XPT2406
該芯片將觸點(diǎn)的電壓值數(shù)據(jù)通過SPI的方式傳輸?shù)?a target="_blank">MCU,可以看到,紅框內(nèi)部由時(shí)鐘線、片選線、數(shù)據(jù)輸入、數(shù)據(jù)輸出,這是典型的SPI傳輸結(jié)構(gòu)。除此之外還有一個(gè)中斷管腳PENIRQ,當(dāng)觸控一個(gè)點(diǎn)后,中斷管腳發(fā)生作用,告訴MCU內(nèi)部的轉(zhuǎn)換數(shù)據(jù)已經(jīng)準(zhǔn)備好,讓MCU進(jìn)行讀數(shù)據(jù)。
- 觸摸屏操作實(shí)例
實(shí)驗(yàn)內(nèi)容:在觸點(diǎn)處顯示圓圈(本次涉及到的液晶屏代碼將在后續(xù)講解,本次內(nèi)容很少而且簡單,不影響閱讀)
說明:當(dāng)觸摸屏有觸點(diǎn)按下時(shí),PENIRQ引腳會(huì)輸出低電平,說明數(shù)據(jù)已經(jīng)準(zhǔn)備好。直到?jīng)]有觸摸的時(shí)候,它才會(huì)輸出高電平。因此可以設(shè)置低電平中斷,在中斷處理過程中向控制芯片發(fā)送讀數(shù)據(jù)的命令并讀取數(shù)據(jù)。
硬件連線如下,其中INT管腳與中斷管腳及逆行連接,用于觸發(fā)中斷。
XPT2406命令字(控制字節(jié))如下:
位3選擇0,說明讀取的數(shù)值位12位分辨率,即讀取的電壓值位12位。發(fā)送命令后應(yīng)當(dāng)連續(xù)發(fā)送兩個(gè)空命令(一個(gè)命令8位)來獲得數(shù)據(jù)。至于為什么要繼續(xù)發(fā)送兩個(gè)空命令,具體原因見本公眾號(hào)SPI文章傳輸原理部分。
步驟:(部分配置省略,只進(jìn)行重要配置說明)
1.配置RCC
2.配置iSPI2
3.配置中斷:將PG7管腳配置為中斷模式
4.編寫代碼
//main.c
#include "spi.h"
#include "gpio.h"
#include "Touch.h"
#include "lcd.h"
//校驗(yàn)參數(shù)(計(jì)算得出)
Pen_Holder Pen_Point ={
.xfac = 0.259067,
.xoff = -20,
.yfac = 0.158228,
.yoff = -38,
};
void touch_adjust()
{
Draw_Circle(50, 50, 5,Yellow) ;
Draw_Circle(100, 100, 5,Yellow) ;
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_FSMC_Init();
MX_SPI2_Init();
MX_USART1_UART_Init();
printf("this is touch lcd testn");
lcd_init();
lcd_clear(Blue);
//touch_adjust();
while (1)
{
if( Pen_Point.Key_Sta==Key_Down ) //觸摸屏被按下
{
HAL_Delay(20); //按下之后會(huì)有抖動(dòng),因此延時(shí)
if(!HAL_GPIO_ReadPin(GPIOG,GPIO_PIN_7)) //確實(shí)按下
{
HAL_NVIC_DisableIRQ(EXTI9_5_IRQn);//關(guān)閉中斷
//如果不關(guān)可能會(huì)因?yàn)橐恍┟潭俅斡|發(fā)中斷
printf("++++++touch+++++rn");
if( Convert_Pos() )
//存放點(diǎn)擊的X,Y坐標(biāo)值,成功返回1失敗返回0
{
printf("x = %d,y = %drn",Pen_Point.X0,Pen_Point.Y0);
//畫一個(gè)以X0,Y0為圓心,半徑為10,顏色為黃色的圓
Draw_Circle(Pen_Point.X0, Pen_Point.Y0, 10,Yellow) ;
//恢復(fù)為0,以便存放下次觸發(fā)位置
Pen_Point.X0 = 0;
Pen_Point.Y0 = 0;
}
HAL_Delay(200);//跳出毛刺
//再次開啟中斷以便下次觸發(fā)
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
}
Pen_Point.Key_Sta=Key_Up;
}
}
}
說明:關(guān)于程序中的校驗(yàn)參數(shù)怎么得到。我們可以看到程序中有touch_adjust()這樣一個(gè)函數(shù),這個(gè)函數(shù)的功能就是在屏幕上分別畫以(50,50)(100,100)為中心,半徑為5的圓,在程序初始化時(shí)就調(diào)用此函數(shù),之后用觸筆分別點(diǎn)擊這兩個(gè)圓,將每個(gè)圓點(diǎn)擊后檢測(cè)到的X和Y電壓值帶入二元一次方程。解方程即可得參數(shù)。
//gpio.c
//中斷處理函數(shù)
extern Pen_Holder Pen_Point;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_7)
{
Pen_Point.Key_Sta = Key_Down;
}
}
//Touch.h
#ifndef _TOUCH_H_
#define _TOUCH_H_
#include "stm32f4xx.h"
//ADS7843/7846/UH7843/7846/XPT2046/TSC2046 指令集
#define CMD_RDY 0X90 //0B10010000即用差分方式讀Y坐標(biāo)
#define CMD_RDX0XD0 //0B11010000即用差分方式讀X坐標(biāo)
/*按鍵狀態(tài)*/
#define Key_Down 0x01
#define Key_Up 0x00
/* 筆桿結(jié)構(gòu)體 */
typedef struct
{
uint16_t X0; //原始坐標(biāo)
uint16_t Y0;
uint16_t ADC_X; //X方向的電壓值
uint16_t ADC_Y;//Y方向的電壓值
uint8_t Key_Sta;//筆的狀態(tài)
//觸摸屏校準(zhǔn)參數(shù)
float xfac;
short xoff; //X方向的電壓值偏移值
float yfac;
short yoff;
}Pen_Holder;
//宏定義,用于片選
#define SPI_TOUCH_CS_LOW() HAL_GPIO_WritePin(GPIOG, GPIO_PIN_15, GPIO_PIN_RESET)
#define SPI_TOUCH_CS_HIGH() HAL_GPIO_WritePin(GPIOG, GPIO_PIN_15, GPIO_PIN_SET)
uint8_t Convert_Pos(void);
#endif
//Touch.c
#include "stm32f4xx.h"
#include "Touch.h"
#include "spi.h"
extern Pen_Holder Pen_Point; /* 定義筆實(shí)體 全局*/
extern SPI_HandleTypeDef hspi2;
/*封裝了一個(gè)發(fā)送命令的函數(shù)*/
static uint8_t WR_Cmd(uint8_t cmd)
{
uint8_t Tx_DATA[1] = {cmd};//將命令放到Tx_DATA中
uint8_t Rx_DATA[1] = {0};//Rx_DATA用來接收返回值
HAL_SPI_TransmitReceive( &hspi2,Tx_DATA,Rx_DATA,1,0x1000);
return Rx_DATA[0];
}
//從SPI讀數(shù)據(jù)
//從7846/7843/XPT2046/UH7843/UH7846讀取adc?值
/*************************************************/
/* 功 能:讀取X軸或Y軸的ADC值*/
/* 入口參數(shù):CMD命令 */
/*************************************************/
uint16_t XPT_Read_AD(uint8_t CMD)
{
uint16_t NUMH,NUML;
uint16_t Num = 0;
SPI_TOUCH_CS_LOW(); // CS=0 開始SPI通信
WR_Cmd(CMD);
HAL_Delay(1); //延時(shí)等待轉(zhuǎn)換完成
NUMH=WR_Cmd(0x00);
NUML=WR_Cmd(0x00); //發(fā)送兩次空命令來獲取電壓值
Num=((NUMH)< 8)+NUML;
Num >?>=4; //只有高12位有效
SPI_TOUCH_CS_HIGH(); // CS=1 結(jié)束通信
//printf("num = %dn", Num);
return(Num);
}
#define READ_TIMES 10 //讀取次數(shù)
#define LOST_VAL 4 //丟棄值
/*************************************************/
/*功 能:讀取X軸或Y軸的ADC值 */
/*入口參數(shù):CMD_RDX:讀取X的ADC值 */
/* CMD_RDY:讀取Y的ADC值 */
/*說 明:與上一個(gè)函數(shù)相比,這個(gè)帶有濾波,多讀幾個(gè)數(shù)據(jù),去掉一些后求平均*/
/*************************************************/
uint16_t XPT_Read_XY(uint8_t xy)
{
uint16_t i, j;
uint16_t buf[READ_TIMES];
uint16_t sum=0;
uint16_t temp;
for(i=0;i< READ_TIMES;i++)
{
buf[i]=XPT_Read_AD(xy);
}
for(i=0;i< READ_TIMES-1; i++)//排序
{
for(j=i+1;j< READ_TIMES;j++)
{
if(buf[i] >buf[j])//升序排列
{
temp=buf[i];
buf[i]=buf[j];
buf[j]=temp;
}
}
}
sum=0;
for(i=LOST_VAL;i< READ_TIMES-LOST_VAL;i++)
sum+=buf[i];
temp=sum/(READ_TIMES-2*LOST_VAL);
return temp;
}
/*************************************************/
/*功 能:讀取X軸和Y軸的ADC值 */
/*入口參數(shù):Pen_Point.X_ADC,&Pen_Point.Y_ADC */
/*出口參數(shù):0:成功(返回的X,Y_ADC有效) */
/* 1: 失?。ǚ祷氐腦,Y_ADC無效) */
/*************************************************/
uint8_t Read_XPT(uint16_t *x,uint16_t *y)
{
uint16_t xtemp,ytemp;
xtemp=XPT_Read_XY(CMD_RDX);
ytemp=XPT_Read_XY(CMD_RDY);
if(xtemp< 100||ytemp< 100)
return 1;//讀取失敗
*x=xtemp;
*y=ytemp;
return 0;//讀取成功
}
/*************************************************/
/*功 能:連續(xù)兩次讀取ADC值 */
/*原 理: 把兩次讀取的值作比較,在誤差范圍內(nèi)可取 */
/*入口參數(shù):Pen_Point.X_ADC,&Pen_Point.Y_ADC */
/*出口參數(shù):0:成功(返回的X,Y_ADC值有效) */
/* 1: 失敗(返回的X,Y_ADC值無效) */
/*************************************************/
#define ERR_RANGE 50 //誤差范圍
uint8_t Read_XPT2(uint16_t*x,uint16_t *y)
{
uint16_t x1,y1;
uint16_t x2,y2;
uint8_t res;
res=Read_XPT(&x1,&y1); //第一次讀取ADC值
if(res==1)
return(1);//讀取失敗返回1
HAL_Delay(3);
res=Read_XPT(&x2,&y2);//第二次讀取ADC值
if(res==1)
return(1); //讀取失敗返回1
//前后兩次采樣在±50內(nèi)
if(((x2<=x1&&x1< x2+ERR_RANGE)||(x1<=x2&&x2< x1+ERR_RANGE))
&&((y2<=y1&&y1< y2+ERR_RANGE)||(y1<=y2&&y2< y1+ERR_RANGE)))
{
*x=(x1+x2)/2;
*y=(y1+y2)/2;
return 0; //正確讀取,返回0
}else return 1; //誤差太大,讀取錯(cuò)誤
}
//轉(zhuǎn)換結(jié)果
//根據(jù)觸摸屏的校準(zhǔn)參數(shù)來決定轉(zhuǎn)換后的結(jié)果,保存在X0,Y0中
uint8_t Convert_Pos(void)
{
HAL_Delay(8);
if(Read_XPT2(&Pen_Point.ADC_X,&Pen_Point.ADC_Y) ==0 )
{
//printf("adc_x= %d, adc_y = %dn",Pen_Point.ADC_X,Pen_Point.ADC_Y);
Pen_Point.X0=Pen_Point.xfac*Pen_Point.ADC_X+Pen_Point.xoff;
Pen_Point.Y0=Pen_Point.yfac*Pen_Point.ADC_Y+Pen_Point.yoff;
return 1;
}
else return 0;
}
評(píng)論
查看更多