在為客戶提供單片機系統、控制器、工控PLC自動化傳感系統、電路板、電子產品、儀器儀表、裝置設備、軟件EXE、安卓APP編程等開發設計定制服務的時候(業務QQ 2531263726),我會經常分享一些開發過程中的技術總結和難點、基礎知識和學習筆記發布在微信公眾號(永珂在線)與大家共享,一直以來我都力求把文章寫得更有故事性、更有吸引力,更簡單易懂,這是因為看著其他部分作者和老師寫的教程和技術文檔很枯燥就很惱火,雖然他們功力深厚但是出拳無味,我就想改變。我想,技術文檔打破傳統寫法,是可以寫得跟小說一樣引人入勝有吸引力的,我的文筆會比較輕快明麗、淺顯易懂,甚至扯淡。以前的幾篇文章寫得比較爛,我也不去修改了,此文開始:通俗易懂、引人入勝、循序漸進。當然,我也不是專業作家,不能一蹴而就,只寄望于一次比一次更好。廢話就說到這里,故事開始,進入正題:
1. 背景介紹
幾個月前,一位尊敬的客戶R加我的QQ(業務QQ 2531263726)好友:
“你好,我們有個控制器的項目需要幫助,請問能來談談嗎?”
“好,可以,請問地址在哪里?”
“成都市某某縣某某路某某號,電話18然后嘰里呱啦。”
“嗯,好的,我安排一下時間過來。”
于是我就和客戶簡單電話溝通了一下任務需求,就是要做什么東西有什么要求,然后約好了時間面談。本案是為了一款新研制的清洗設備開發一個控制器,從最初到現在幾次改進設計最終選定此款串口觸摸屏作為顯示器件。
最初的設計方案出于成本和其他因素的考慮是利用OLED和LCD作為設備的顯示器,顯示設備的運行狀態、流量、溫度和壓力參數。這兩種顯示器價格便宜,但是功能較為單一、美觀性稍微差一點,后來有一天,客戶甩了一個東西給我,是一個4.5寸的串口觸摸屏和一本巴掌那么大小的寥寥幾頁的說明手冊給我,要把所有的顯示和按鍵功能集成到這個觸屏上面。簡單了解一下,這個屏雖然以前沒有使用過,但是串口通信和控制我這里熟悉得很,我想只要廠家能把產品生產出來并且有說明書,我這里就能二次開發出滿足要求的功能。
根據說明手冊里面提供的信息,去下載了詳細的使用說明書、配套軟件和參考例程,粗略一看,使用起來似乎不難,但是真正在后面開發的時候卻又是一波三折。
2. 串口觸摸屏的介紹和開發說明
這款串口觸摸屏的尺寸是4.5寸,在橫向(寬邊)有853個點像素,縱向(窄邊)479個點像素,在使用的時候不超出這個尺寸參數都是能夠在屏幕上顯示。
這個觸摸屏完全可以采用串口通信進行操作,包括參數顯示、觸摸按鍵控制等,串口通信協議按照圖 2設置即可,很簡單的幾個參數設置,波特率、數據位、校驗位、停止位。
在開發中只需要接4根線就可以運行觸摸屏,包括:
VCC引腳:自然是接5V或者3.3V的直流電源正極;
GND引腳:接直流電源的負極;
TX引腳: 如果是由單片機控制觸摸屏的顯示,就接單片機串口的RX引腳;
RX引腳: 如果是由單片機控制觸摸屏的顯示,就接單片機串口的TX引腳;
2.1 串口觸摸屏的程序開發說明
實際上此款觸摸屏的顯示和控制只需要單片機的串口發送或者接收一些特定的字符串指令即可。
例如,我們想對觸摸屏清屏,只需要利用單片機串口向觸摸屏發送字符串"CLS(0);\r\n"即可,其中的”CLS(0);”是清屏指令,用0號顏色黑色清除屏幕,\r\n表示讓觸摸屏立刻執行這一指令。
以上就類似于單片機向觸摸屏打了一個招呼,交代一些事情:
“嗨!觸摸屏兄弟,把你的屏幕用黑色作為背景清除一下。”
那么一般來說,觸摸屏收到指令執行完成后還需要向單片機反饋一個消息,發送字符串“OK”給單片機,就像在說“OK,弄好了!”
這個“OK”字符串只有觸屏接收到“\r\n”字符串指令,并且執行完成后才會向單片機反饋。不過大家做事也不能著急,在單片機在向觸屏發送指令確認執行后,需要延時200-300毫秒,等觸屏兄弟休息一下,收到觸屏的“OK”的回話后再接著干活。
又如,我們想要在屏幕上某個位置開始顯示12點的漢字“永珂在線”,就只需要利用單片機串口向觸摸屏發送字符串"DS12(0, 0,'永珂在線',1); \r\n ",就能在觸摸屏的(0,0)位置開始顯示字符串“永珂在線”四個漢字,其中的1表示字體的顏色白色。同樣地,觸屏執行完成后,也會向單片機反饋“OK”。當然,也不是每個指令語句后面都要加“\r\n”,多發送幾條指令后再加“\r\n”,將是更好的編程選擇。
這款串口屏的使用就是類似這樣的,我只是舉例兩個例子,更多的背景設置、觸摸按鍵配置等等功能,在你購買這塊觸摸屏的時候,在商家給你的說明書里有詳細介紹。
2.2 在單片機里面寫程序
觸摸屏了解了,線接好了,接下來的工作就是編寫單片機的程序。
(1)這里我首先介紹的是一款以51為核心的單片機(STC15F2K60S2)的與觸屏通信操作的編程方法。
第一步,既然是串口觸摸屏,首先自然是配置單片機的串口,直接一點,上代碼吧。
void UartInit(void) //115200bps@11.0592MHz 串口1初始化
{
SCON = 0x50; //8位數據,可變波特率
AUXR &= 0xBF; //定時器1時鐘為Fosc/12,即12T
AUXR &= 0xFE; //串口1選擇定時器1為波特率發生器
TMOD &= 0x0F; //設定定時器1為16位自動重裝方式
TL1 = 0xFE; //設定定時初值
TH1 = 0xFF; //設定定時初值
ET1 = 0; //禁止定時器1中斷
TR1 = 1; //啟動定時器1
ES=1; //使能串口中斷
EA=1; //打開總中斷
}
以上UartInit程序段,就將單片機的串口1配置為波特率為115200bps,8位數據的通信參數。
第二步,編寫串口發送的程序段,串口發送的程序段分為兩部分,第一個是串口發送一個字符,另一個是串口發送字符串。
void SendData(unsigned char dat) //(1)發送一個字節(一個字符)
{ unsigned char i=0;
ACC=dat;
SBUF=dat;
do
{ _nop_();
i++;
}while(!TI && i<200);
TI=0;
}
在這里為了防止串口發送進入死循環,我將官方參考例程做了一下小修改,增加了一個計數變量i,當執行了200個指令周期的延時后假如串口還沒有發送完一個字符,就強制退出這個串口字符發送程序。
void SendString(unsigned char *s) //(2)發送字符串
{
unsigned char i=0;
while(*s)
{
SendData(*s++);
i++;
if(i>=250) break;//防止進入死循環,一次最多250個字符
}
Delay1ms(10);
}
在這里也將官方參考例程做了一下小修改,同樣為了避免進入死循環,增加了一個計數變量i,一個字符串最大只能有250個字符,超過這個數量則退出字符串發送程序。
接下來的操作就是比較簡單的,在程序的任意位置調用SendString子函數就能進行觸摸屏的操作,例如調用SendString("DS24(510,410,'中',1);"); 就能在屏幕的(510,410)位置顯示一個白色的24點陣漢字“中”。
第三步,前面說了單片機向觸屏發送數據和指令,自然單片機還需要接收觸屏返回的反饋信號、按鍵標號等信息,這里我采用串口中斷來接收來自觸屏的反饋數據。
unsigned char xdata received_data[4]; //串口接收到的按鍵編號數據
unsigned char xdata feedback_data[2];//串口屏執行指令后的反饋數據
void Usart() interrupt 4 //串口1的中斷
{
unsigned char received_temp;
if(RI) //假如有數據接收事件,接收到數據
{RI=0;
received_temp=SBUF; //串口接收到的數據
received_data[0]=received_data[1];
received_data[1]=received_data[2];
received_data[2]=received_data[3];
received_data[3]=received_temp;
if(received_data[0]=='B' && received_data[1]=='N' && received_data[2]==':' && received_data[3]!='0' ) //觸摸按鍵返回的指令格式是BN:X
{command_received=1;
command_data=received_data[3]; //返回BN:X指令中的X代碼
}
feedback_data[0]=feedback_data[1];
feedback_data[1]=received_temp;
if (feedback_data[0]=='O' && feedback_data[1]=='K')
{feedback_ok=1;
}
}
if(TI)
{TI=0;
}
}
這里我定義了2個數據緩沖區,received_data是觸屏按鍵編號緩沖,當你用手指在觸屏上點擊不同的按鍵區域的時候,觸屏會根據預先的配置返回BN:1,BN:2,BN:3等等字符串,其中的數字1,2,3就表示你手指點擊的屏幕上哪個編號的區域,BN:1表示手指點擊了1號區域,BN:2表示手指點擊了2號區域,以此類推。
feedback_data是單片機向觸摸屏發送指令后,觸屏接收到并執行后向單片機返回的“OK”信息,當單片機收到“OK”就表示觸屏把上一條指令執行完成了。
(2)以上是51單片機的,下面講一講STM32單片機的部分參考程序,本段程序來自于該款觸屏的說明書。
第一步,自然是串口初始化。
void USART1_Init(void)
{
USART_InitTypeDef USART_InitStructure;
USART_ClockInitTypeDef USART_ClockInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE );
USART_ClockInitStructure.USART_Clock = USART_Clock_Disable; // 時鐘低電平活勱
USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low; // 時鐘低電平
USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge; // 時鐘第二個邊沿迕行數據捕獲
USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable; // 最后一位數據癿時鐘脈沖丌從SCLK輸出
/* Configure the USART1 synchronous paramters */
USART_ClockInit(USART1, &USART_ClockInitStructure); // 時鐘參數刜始化謳置
USART_InitStructure.USART_BaudRate = 115200; // 波特率為:115200
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 8 位數據
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 在幀結尾傳輸 1 個停止位
USART_InitStructure.USART_Parity = USART_Parity_No ; // 奇偶失能
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 硬件流控刢失能
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 収送使能+接收使能
/* Configure USART1 basic and asynchronous paramters */
USART_Init(USART1, &USART_InitStructure);
/* Enable USART1 */
USART_ClearFlag(USART1, USART_IT_RXNE); //清中斷,以免一啟用中斷后立即產生中斷
USART_ITConfig(USART1,USART_IT_RXNE, ENABLE); //使能 USART1 中斷源
USART_ITConfig(USART1, USART_IT_TC, ENABLE);
USART_Cmd(USART1, ENABLE); //USART1 總開關:開啟
}
第二步,串口發送字符串的子程序。
void GpuSend(char * buf1)
{ u8 i=0;
while (1)
{ if (buf1[ i]!=0)[ i]
{ USART_SendData(USART1, buf1[ i]); //収送一個 byte 刡串口[ i]
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET){}; //等待収送結束
i++;
}
else return;
}
}
第三步,在任意程序里調用串口發送指令去命令觸屏工作。
GpuSend("CLS(13);BOX(0,0,219,175,15);BOX(1,1,218,174,0);BOXF(2,2,217,17,3);PL(2,
18,218,18,0);\r\n");
這里的STM32的程序只是部分程序段,在使用的過程中還有時鐘配置、引腳配置等其他程序,這里不一一贅述了。
3. 你需要注意的事
該怎么調試
有時候作為開發者我們的設想是很好的,以為一次就可以寫出天衣無縫的代碼,然而現實是比較殘酷的,有時候以為是嚴格按照規則和使用說明來寫的程序,但是稀里糊涂地就會出現這樣或者那樣的問題,這時候就需要調試了。
磨刀不誤砍柴工,既然是串口通信,那串口助手就是少不了的,如圖 2的Commix軟件就是不錯的選擇。將串口數據線(TTL電平的)或者USB轉串口TTL數據線插到電腦上,將數據線的TX接單片機的RX,數據線的RX接單片機的TX,然后數據線的地線和單片機共地即可。在串口助手界面你能看到單片機發送出來的數據,你也可以模擬觸屏向單片機發送反饋數據。
以上的方式是用串口助手替代觸屏與單片機通信,有一定作用但是不能完全反映單片機與觸屏的工作情況,因為此時數據鏈路是沒有和觸屏連接的,我們要完全監視單片機與觸屏的工作狀態,就需要對連接線路進行簡單搭建。
如圖 6,這樣的接線方式,就能利用電腦上的串口助手完全監視單片機與觸屏之間的通信了,不管是單片機串口與觸屏通信的發送還是接收過程數據,都能顯示在串口助手的界面。
要確定字符串是否結束
可以在2.2中知道,在串口發送的時候是以最后一個字符是不是0來作為結束標志的。曾經我有過這樣的經歷,定義了一個數組A[5],分別給第0-4為元素賦值,例如我要顯示單詞“stand”,就令A[0]=’s’; A[1]=’t’; A[2]=’a’; A[3]=’n’; A[4]=’d’;我曾經天真地以為再調用SendString(A)這樣子就可以了,可是,這樣子做的結果就是屏幕老是進入初始化狀態,當時不明原因,后來通過串口助手調試發現,在字符串“stand”后面老是多了一個異常字符,后來,我就重新定義了一下數組為A[6],并令最后一位元素A[5]=0,如此之后觸屏恢復正常顯示。出現這個問題的原因就是由于之前字符串沒有結束標志,導致串口在A發送完后隨機發送了幾個數據給觸屏,導致混亂。
不要輕易使用while循環
在之前的程序設計中,我會使用while來等待ADC轉換完成,等溫度采集完成,等待串口發送完成。可以后來發現觸屏經常死屏,最初懷疑觸屏有問題,可是廠家堅持說沒有出現這樣的狀況,那我就自己先查查程序了,初步是懷疑while的過度使用,所以把所有的while循環都加上限制,例如假如ADC轉換等待多少時間后還沒有轉換完成則跳出循環,假如串口發送多少個字符后還沒有完成發送那也跳出循環。當把所有的while都加上跳出的限制后,一切就變得正常了。
好了,這篇文章就介紹到這里,本文花了我幾天的有空時間,也是寫得字數最多的一篇文章。謝謝查看,希望有幫助。
四川成都控制器電路板儀器儀表開發定制可以找我
|