單片機學習中的常見問題
在單片機學習中,我們常常遇到這樣那樣的零星問題,這里我將我遇到的問題總結(jié)如下,都是個人見解,如果不對,萬望指出。(持續(xù)更新中)
問題0001:51單片機下載失敗常見原因
1.查看單片機是否插反了;
2.下載軟件選擇正確的單片機型號(注意STC89C52和STC89C52RC是不同的) ;
3.檢測是否連接了晶振 ,時候有晶振接觸不良的現(xiàn)象 ;
4.查看電腦com是否有多個,是否選錯了com口 (是否安裝了相應(yīng)的驅(qū)動);
5.單片機開發(fā)板是否重新上電了(下載單片機程序需要冷啟動過程) ;
6.注意是否有短路(比如開發(fā)板下面最好鋪一張紙,不要被短路) ;
7.下載軟件用的什么版本,舊版本可能需要設(shè)置最低、最高波特率,并且還會出現(xiàn)很多奇怪的現(xiàn)象,比如第一次燒寫用一個波特率,第二次就不行了,但是有時候卻一直可以(個人經(jīng)驗)。新版本不用設(shè)置,并且不用等待軟件提醒上電才可以上電,點擊下載后,直接重新上電即可,建議盡量使用新版 ;
8.如果使用的是CH340/CH341或者PL2302,檢測RXD/TXD是否連接反了。(注意PL2302,驅(qū)動,新版的驅(qū)動只支持正版芯片,這個一定要注意,很多人會沿用老版本驅(qū)動,不建議用PL2302)
9.換一個單片機試試看,驗證單片機是否已損壞。
10.一部分人修改上一次工程代碼,然后直接編譯,下載無效果?赡苁切碌拇a編譯有錯(編譯有三個按鈕,請注意區(qū)別差異),導致目標文件未生成,hex文件未更新,所以下載的還是上一次的程序。這里要注意。
11.STC-ISP軟件選擇hex文件后,要勾“每次下載前都重新裝置文件”,這樣有三大好處:(1)免去每次編譯都重新手動裝在hex文件(2)避免因忘記手動重新裝載hex文件,而導致下載的hex文件還是上一次的hex文件,代碼下載后現(xiàn)象和上一次一樣,誤以為還是程序問題(3)方便快速調(diào)試代碼。
12.連接線可以出現(xiàn)問題,比如接觸不良,線頭脫斷等。
13.芯片“鎖死”。STC-ISP軟件設(shè)置問題,上一次下載程序的時候,在STC-ISP可能勾選了“下次冷啟動時,P1.0 / P1.1為0/0才可以下載程序” , 這種情況,試著將此兩個IO拉低,下載程序試試看。
(其他原因后續(xù)更新中-------2016年2月2日最后更新此問題)
問題0002:學習單片機我們都需要什么軟件
最近看到群里面有很多人為了學習單片機,下載了STC-ISP,然后又下載了串口軟件、單片機小精靈、定時器計算神器之類的,可謂五花八門,但是實際我們并不需要那么多的軟件。
大家注意到STC-ISP軟件已經(jīng)附帶了很多功能了,學會使用這些功能可以讓我們事半功倍。另外,STC手冊上有C語言和匯編語言程序,值得參考。下面在分析幾款常用軟件給大家:
keil4 : 點擊打開鏈接 (附帶我下載的兩個仿真插件,可以做很多仿真)
STC-ISP 下載方式:進入www.stcmcu.comwww.stcmcu.com 然后CTRL+F搜索STC-ISP即可。
BCompare : 點擊打開鏈接 (快速比較文件差異,便于對比程序)
Proteus7.5 仿真:點擊打開鏈接 (文件中包含我錄制的破解過程,目前我的系統(tǒng)為W8.1暫時未發(fā)現(xiàn)使用問題)
Multisim 仿真 :點擊打開鏈接
Source Insight : 點擊打開鏈接(適合編寫代碼,非常方便)
SecureCRT7 :點擊打開鏈接 (免破解)
Altium Designer6.9 : 點擊打開鏈接
Altium Designer14.2.3 : 點擊打開鏈接
屏幕錄制專家: 點擊打開鏈接 (有時候交流溝通,錄制視頻更加簡單直接)
另外,單片機采用C語言編程的學生,有兩本書必須要度:
華為技術(shù)有限公司c語言編程規(guī)范.pdf :點擊打開鏈接
高質(zhì)量C編程指南—林銳.pdf : 點擊打開鏈接
問題0003:51類單片機需要冷啟動,但是一定要手動重啟開關(guān)一次嗎
51類單片機下載程序時必須冷啟動,這是大家都知道的。昨天,一個學生告訴我他是用的普中科技開發(fā)板,他用普中科技的下載軟件去下載程序,發(fā)現(xiàn)不是很好用。我告訴他STC-ISP下載軟件非常好用,他說STC-ISP軟件必須經(jīng)過手動重啟開關(guān)完成下載,而普中科技的開發(fā)板,可以完成全自動下載,只需點擊“下載”按鈕即可,無需手動重啟開關(guān),并且普中科技技術(shù)支持告訴他,只能使用他們的軟件才能夠完成全自動下載(我對此只能呵呵)。本人也是采用普中科技開發(fā)板,普中科技的自動下載功能確實存在,但是我使用過發(fā)現(xiàn)并不是很好用,而且需要結(jié)合它的開發(fā)板硬件才行。這里介紹一下我使用過的兩種“全自動”下載(可能方法還有很多,需要廣大網(wǎng)友的補充)
(1)硬件法:利用STC自動下載器,點擊下載后,模塊會自動重新上電,完成下載任務(wù),需要淘寶購買,約9~13元。
(2)軟件法:利用串口結(jié)合IAP功能,點擊下載,模塊自動完成下載,無需手動重啟。(版本古老的芯片無此功能)此方法可參考我的筆記:stc單片機“全自動下載”(程序版) http://blog.csdn.NET/yagnruinihao/article/details/21739665
問題0004:變量的定義與程序的執(zhí)行
為了簡化問題,這里把程序簡化為2行(先不要看變量i是否有意義)
在C語言方面,這是可以的(盡管這樣不是很規(guī)范),但是在keil4中,卻出現(xiàn)了問題,網(wǎng)上百度,說可能是有中文字符,但是我將程序刪除后,編譯沒有錯誤,切換輸入法,再次輸入圖片2的內(nèi)容,出錯,將int i=0 ; 剪切到第一行,錯誤消失。還有一個可能是軟件出錯(經(jīng)過測試,排除可能性)。
所以,在單片機編程中,最好將變量的定義放在執(zhí)行語句的前面。
問題0005:程序中的“死循環(huán)等待”正確嗎?
在單片機程序中,有時候會需要“死循環(huán)等待”,也就是等到某一條件我們才需要執(zhí)行下面的操作。這里以溫度傳感器DS18B20為例,主機首先需要發(fā)送一個存在檢測脈沖,如果DS18B20存在,則它會以60us至240us的電平來回應(yīng)主機,這里就會需要一個“死循環(huán)等待”,我們必須等到DS18B20“回應(yīng)完成”,這時候主機才能繼續(xù)發(fā)送指令到DS18B20中去,這時候很多人會采用這樣的方式:(首先假設(shè)sbit ds18b20_io_bit = P3^7 ;)
則,while( !ds18b20_io_bit) ;
這樣的代碼其實是有問題的,在未連接DS18B20或者接觸不良時,DS18B20并不能回應(yīng)主機,這樣的代碼就會有問題,尤其在大工程中,所以,為了安全,我通常的做法是:
第一步:
while( (!ds18b20_io_bit) ;
P1=0x01 ; //led
如果,P1口的led有變化,則表示我們“程序在邏輯上和時序上沒有問題”
第二步:
將上面的代碼修改為:
unsigned long i=0
while( ( (!ds18b20_io_bit) && (i++)) ;
然后打印 i ,
因為i一般都很小,所以這時候我一般將unsigned long 修改為unsigned char 類型,然后最后修改為這樣
while( (!ds18b20_io_bit)&& (i++ <250)) ;
注意這里的250不是隨意寫的,是經(jīng)過測試的,經(jīng)過測試,我讀出的i值遠小于250,我才這樣做的,如果在其他類似的地方,打印出來i是其他較大的值,那就重新定義i的類型為unsigned int 或者unsigned long ,根據(jù)具體情況而定。
上面的修改似乎意義不大,但是在大工程中,這樣不會因為DS18B20的“不存在”而導致整個程序“死循環(huán)等待”。
問題0006:指針能否指向特殊功能寄存器
特殊功能寄存器的尋址方式只有直接尋址和位尋址。沒有間接尋址方式。而指針卻是間接尋址方式,不能通過指向特殊功能寄存器的指針來操作特殊功能寄存器。
例:利用指向0x90的指針來操作P1口的LCD燈(高電平時LCD點亮)(P1口的地址是0x90,但是屬于特殊功能寄存器區(qū))
#include
#include "./stc/stc12c5a60s2.h"
void main(void)
{
unsigned char *p=0x90;
while(1)
{
*p=0x55;/*從這里看,似乎應(yīng)該是LCD每隔一個被點亮,但是事實卻并非這樣*/
}
}
問題0007:“判忙” 函數(shù)重要嗎
在某些芯片操作時,會有一些引腳或者寄存器或地址的數(shù)據(jù)被用來表明當前芯片處于的狀態(tài)為忙碌狀態(tài)或者空閑狀態(tài),如果忙碌,單片機需要用輪詢的方式等待直到芯片空閑下來才繼續(xù)發(fā)送指令或數(shù)據(jù)。這里以1602液晶屏為例:
一個朋友用ST89C52操作1602液晶屏,操作成功,后來換了芯片STC12C5A60S2,同樣的程序,顯示出來的卻是“亂碼”,后來經(jīng)過測試,程序部門似乎沒有問題,于是,我在LCD1602的“寫命令”函數(shù)和“寫數(shù)據(jù)”函數(shù)中
加入了延時(只是為了快速測試),下載后,兩個芯片都運行正常。其實對于LCD1602,一般都不需要“判忙”,它運行時比較快的,但是如果將一個沒有“判忙”的1602液晶屏模塊封裝起來,在調(diào)試一個大工程時,這時候
因為換了芯片,導致整體效果錯誤,這就是增加了調(diào)試難度。比如在利用LCD1602學習使用時鐘芯片DS1302時,如果顯示亂碼,這時候,因為你的LCD1602是實驗的基礎(chǔ)環(huán)節(jié),肯定是你之前就已經(jīng)封裝好了的,這時候
你肯定會懷疑DS1302環(huán)節(jié)操作出錯了,這就增加了調(diào)試的難度和時間。所以,“判忙”函數(shù)必不可少。這里,又有一個技巧,就是防止程序“卡死”,含義與本文中的問題0002類似。
起初LCD1602的核心操作為:
[cpp] view plain copy
do{
;
}while( (lcd1602ReadStatus()) & 0x80));
后來我變?yōu)?/p>
[cpp] view plain copy
unsigned long i=0 ;
do{
i++;
}while( (lcd1602ReadStatus()) & 0x80);
UartSendValue("i = " ,i) ;/*利用串口打印出i的值*/
打印出i的值為 “1”(基于芯片STC12C5A60S1,11.0592MHZ,其他型號的單品機可能打印出來的效果不同,甚至同樣的型號單片機、同樣的晶振、同樣型號的1602 操作打印出來的值也是不一樣的),又變化為:
[cpp] view plain copy
unsigned char i=0 ;/*同時,需要注意這里,修改了類型*/
do{
i++;
}while( ((lcd1602ReadStatus()) & 0x80) && (i<10)); /*經(jīng)過驗證,10已經(jīng)可以滿足足夠的時間了*/
這樣做才是安全的。(尤其在大系統(tǒng)中,不會因為1602的”不存在“導致整個系統(tǒng)的運行失敗,經(jīng)典案例如下)
(在2015年2月7日,我?guī)椭粋大四學生調(diào)試畢設(shè)時,也發(fā)現(xiàn)了同樣的問題,此同學在1602液晶屏的寫命令、寫數(shù)據(jù)函數(shù)里面加入了判忙函數(shù),判忙核心代碼如下:
[cpp] view plain copy
do{
;
}while( (lcd1602ReadStatus()) & 0x80));
,最后因在系統(tǒng)設(shè)計時,暫時用了1602顯示,后來為了做上位機,就用了串口通信代替1602, 拔掉了1602液晶屏,但是沒有屏蔽掉主函數(shù)中關(guān)于1602的操作,導致了程序卡死在判忙函數(shù)中,因為如果沒有硬件連接,通過數(shù)據(jù)口讀取到的1602狀態(tài)數(shù)據(jù)是0xff,然后0xff&0x80,這樣得到的結(jié)果永遠是1(除非再次插上液晶屏或者手動將數(shù)據(jù)最高bit對應(yīng)的IO口拉低),導致程序卡死在這里,而且這些功能都是封裝好的,所以就更加加大了難度,導致一周沒有調(diào)試出來,后來,我給他指出兩種方法(1)在主函數(shù)中屏蔽所有關(guān)于1602液晶屏的函數(shù),實驗后,系統(tǒng)運行正常(2)修改判忙函數(shù),因為一勞永逸,試驗后,運行正常,即使主函數(shù)中沒有屏蔽1602相關(guān)函數(shù),1602硬件暫時取下,也是可以成功運行的)
另外,還有很多同學運用了延時的方法去操作,似乎大部分人都得到了滿意的結(jié)果,那么,這里有出現(xiàn)了兩個問題:(1)延時的結(jié)果有三種:延時太小、延時正好、延時太大,對于大家來說,延時正好、延時太大都是可以完成系統(tǒng)的設(shè)計的,但是,你又如何保證你的延時不會小呢?大部分人想,這非常簡單,把延時設(shè)計的非常大就好了,那么,就引出了問題(2)延時太大,不利于系統(tǒng)整體設(shè)計,導致了效率太低 。 何去何從,大家可以想一想。
問題0008:調(diào)試程序之“窮途末路”
有時候,在調(diào)試程序時,明明都是嚴格按照datasheet的說明和時序圖編寫的代碼,就是沒有效果,然后借鑒別人的代碼,代碼相同,但還是沒有效果 。 這時候,先要懷疑硬件,如果硬件沒有問題,有可能還會有一個原因,那就是單片機的問題(或者是keil軟件的問題,這個不得而知了)。我之前遇到過編寫一個代碼,沒有效果,最后把全部代碼屏蔽掉,只在main函數(shù)中,點亮了LED,但還是失敗了,后來,復制代碼,重新新建工程,粘貼代碼,就好了。(這里不是噴STC,其實STC在國內(nèi)做的較好,但是總會有一些奇葩的時候),這里分享一下,僅個人經(jīng)歷。
問題0009:如何一步步測試編寫代碼
單片機程序測試調(diào)試似乎對于剛?cè)腴T的同學有一定難度,但是入門之后我們快速調(diào)試程序呢?很多人寫代碼一氣呵成,然后在main函數(shù)中調(diào)用10幾個子函數(shù),然后直接通過液晶屏或者其他的手段觀察效果是正確,這樣的方法對嗎 ?以下是常用方法:
1.借助于網(wǎng)上的視頻講解或源代碼,(對于32位的MCU尚可理解,但是51、PIC.MSP430單片機使用者如果入門后還是同樣的方法,你就OUT了),但只適用于部分網(wǎng)上資料較多的芯片。
2.借助"邏輯分析儀"儀器去分析(這種神器我也是通過網(wǎng)上資料介紹,有所了解,但是并沒有使用過,不過好像非常好用的),通過邏輯分析儀可以分析我們的時序是否存在問題,但是個人感覺比較適用于菜鳥和破解單片機程序的高手,對于大多數(shù)用戶,沒有必須花錢購買
3.利用程序本身去驗證程序,編寫一個函數(shù),測試一個函數(shù),操作簡單,屢試不爽,這里以大家熟知的51類單片機STC12C5A60S2和數(shù)字溫度傳感器DS18B20為例(單總線協(xié)議通信)。
下圖為DS18B20的“復位”時序,因為每次操作都需要MCU發(fā)送一個復位信號,所以,“復位”函數(shù)相當重要。
我們知道,當DS18B20存在時,紅色部分的時間內(nèi),總線將會被DS15B20拉低,呈現(xiàn)低電平 ; 如果DS15B20不存在,則在單片機使用總線(藍色)后,紅色部分仍然是高電平。
假設(shè)我們有以下定義:
[cpp] view plain copy
typedef unsigned char UB8 ;
typedef unsigned short int UW16 ;
typedef unsigned long UL32 ;
typedef char SB8;
typedef short int SW16 ;
typedef long SL32 ;
#define HIGH_LEVEL 1
#define LOW_LEVEL 0
sbit ds18b20_io_bit = P1^7 ;/*根據(jù)硬件選擇*/
#define DS18B20_EXISTENCE 0 /*存在DS18B20*/
#define DS18B20_NOT_EXISTENCE -1/*不存在DS18B20*/
然后編寫“復位函數(shù)”
[cpp] view plain copy
/******************************************************
Function :ds18b20Init
Input :N/A
Output :N/A
Return :DS18B20_EXISTENCE或者DS18B20_NOT_EXISTENCE
Description :N/A
Note :經(jīng)過測試在11.0592MHZ和12.00MHZ條件下都可以
檢測到ds18b20的存在。
******************************************************/
SB8 ds18b20Init(void)
{
SB8 existenceFlag ;
UB8 i=0 ;
ds18b20_io_bit = HIGH_LEVEL ;
_nop_() ;
_nop_() ;
/*master transmit the reset pulse */
ds18b20_io_bit = LOW_LEVEL ;
delay720usForDs18b20();
/*rising edge*/
ds18b20_io_bit = HIGH_LEVEL ;
/*ds18b20 wait 15us to 60us then transmit a presence pulse by
pulling the 1-wire low for 60us to 240 us */
delay15usForDs18b20();
delay15usForDs18b20();
delay15usForDs18b20();
delay15usForDs18b20();
if( ! ds18b20_io_bit)
{/*ds18b20 eixstence*/
existenceFlag = DS18B20_EXISTENCE;
}
else
{/*no ds18b20*/
existenceFlag = DS18B20_NOT_EXISTENCE;
}
while((!ds18b20_io_bit) && (i<250))
{
i++;/*wait*//*暫時,具體見調(diào)試*/
/*經(jīng)過測試,這里的250對于11.0592MHZ和12MHZ都遠遠足夠了*/
}
return existenceFlag ;
}
然后,我們驗證此函數(shù)是否可以成功完成“復位功能”:(時序不再詳細分析)
[cpp] view plain copy
#include
#include "ds18b20.h"
void main(void)
{
SB8 flag = -3 ;
P1=0x00;//連接leds,高電平點亮
flag = ds18b20Init();
if( flag == DS18B20_EXISTENCE) //檢測結(jié)果:存在,0
{
P1 = 0x01 ;
}
else if(flag == DS18B20_NOT_EXISTENCE)//檢測結(jié)果:不存在,-1
{
P1=0x02;
}
else
{
P1 = 0x04 ;
}
while(1) ;
}
這樣,我們通過LED就可以驗證函數(shù)了:
(1)正常連接DS18B20,結(jié)果如果是P1的第一個燈亮了(P1=0x01),則表示單片機可以檢測到DS18B20的存在,說明函數(shù)可能對了(可以檢測到DS18B20的存在);
(2)拔掉DS18B20,結(jié)果如果是P1的第二個燈亮了(P1=0x02),則表示單片機未檢測到DS18B20的存在,說明函數(shù)完全正確。(可以同時檢測到DS18B20的存在和不存在)
注意:只有當(1)(2)都正確后,才表示此“復位”函數(shù)編寫正確了。然后繼續(xù)進行下面的函數(shù)編寫。
同樣的,對于很多芯片,我們使用51單片機驅(qū)動時,都會涉及到“讀操作”和“寫操作”,在編寫讀寫函數(shù)時,通常嚴格按照時序編寫“寫”函數(shù)和“讀”函數(shù),然后向芯片的內(nèi)部RAM寫入一個數(shù)據(jù)(這個數(shù)據(jù)最好不是0xff,因為當芯片未連接時,單片機讀出的數(shù)據(jù)就是0xff,這時候如果我們寫0xff,然后讀取,因為結(jié)果肯定是0xff,所以并不能證明我們的讀寫函數(shù)書寫正確),然后讀出該地址的數(shù)據(jù),驗證數(shù)據(jù)是否相同,如果相同,表明讀寫操作正確,但是這里同時需要注意一個問題:讀/寫操作周期為多少?即需要隔多長時間MCU才可以繼續(xù)讀/寫此芯片,因為對于芯片,我們讀寫一次后,可能需要過一段時間才可以繼續(xù)讀寫,如果沒有注意這個問題,就可能導致:編寫的代碼好像沒錯,但是在后期我們連續(xù)對芯片進行讀/寫操作時,沒有注意這個延時等待啊,就會出現(xiàn)很奇怪的問題(通常這種錯誤導致的結(jié)果是隨機的),這時候反過來調(diào)試過程可能就繁瑣了,如果連續(xù)操作間隔較長,安全的做法是:在每一次讀/寫操作后,都加一個延時,這樣就保證肯定OK了。
問題0010:藍牙串口模塊使用的正確步驟
很多人現(xiàn)在都開始使用藍口模塊,利用手機藍牙或者PC藍牙進行控制單片機系統(tǒng)。但是很多人一上來就直接把藍牙模塊和單片機相連接,然后編寫程序,效果實現(xiàn)不了,就不知該如何了。藍牙模塊很多都是結(jié)合了串口通信的,通常拿到模塊,需要經(jīng)過一下步驟(自己總結(jié),可做參考):
(1)首先,需要藍牙模塊和PC的硬件連接,這里有兩種方法:
把你的開發(fā)板的單片機取下來,然后從開發(fā)板引出電源線VCC和GND,還有P30、P31接口,連接藍牙模塊的四個端口(注意RXD和TXD不要反接了)
如果你有USB-TTL模塊,可以直接連接藍牙模塊,無需單片機開發(fā)板轉(zhuǎn)接過去。
(2)通過PC的串口軟件(比如STC-ISP軟件自帶的串口),設(shè)置好軟件的波特率和通信數(shù)據(jù)格式,通過串口軟件發(fā)送AT命令(通常購買時賣家提供了),看是否有返回數(shù)據(jù),如果沒有返回,有可能是P30、P31接口需要換過來,如果發(fā)送命令,有返回信息且不是亂碼,表示藍牙模塊和PC可以串口通信了。
(3)手機下載軟件“藍牙串口”,通過此軟件連接藍牙模塊(藍牙的名稱和配對密碼可通過 AT命令設(shè)置),然后給藍牙模塊供電,PC打開串口軟件,手機發(fā)送任意字符到藍牙模塊,會發(fā)現(xiàn)PC串口軟件接收到了信息,這表示藍牙可以正常工作。
(4)編寫單片機程序,此時需要分為兩步:
先不連接藍牙模塊,編寫程序,單片機接收到串口數(shù)據(jù),做出相應(yīng)動作,寫好后下載,然后打開PC的串口軟件,發(fā)送命令,檢驗現(xiàn)象是否正確,如果正確,開始第二步
把藍牙模塊連接到單片機,手機藍牙連接模塊,手機發(fā)送命令,此時只要步驟現(xiàn)象經(jīng)過反復驗證沒有問題,第二步肯定沒問題。(如果第二步不對,那就返回去做第一步)
(5)其他。如果需要用PC的藍牙進行控制,還需要購買藍牙適配器,也就是模塊發(fā)送信息到電腦時,需要一個接收器。如果不是很必要,就用手機藍牙軟件就好了,省錢省事。
問題0011:不知道遙控器的編碼協(xié)議,如何解碼
不同的遙控器,其協(xié)議也不同.
要看你的目的是什么 , 分兩種情況:
(1)你只是想單純的解碼出來而已 :可以查看一下你用的遙控器類型,然后百度一下試試看 , 然后結(jié)合示波器觀看。
(2)你想解碼出來后,用到其他設(shè)計上去 :這里有兩種方法
較為嚴格 : 就是參考(1)的思路去實驗,但是花時太多。
<2>不是很嚴格,但是很實用 : 先在網(wǎng)上看一個紅外遙控器的協(xié)議,然后根據(jù)它的協(xié)議去解碼,然后,查看解碼的是否正確(通過原碼和反碼初步驗證) , 然后把按下每一個按鍵,去記錄下他的編碼,然后下一設(shè)計要用到它的時候,你就把上次記錄的編碼,做一個數(shù)組,然后自己去定義每一個編碼對應(yīng)的功能即可。(因為實際上很多類型的編碼,無非是引導碼的時間不同,當然我們可以不關(guān)心引導碼 ,還有就是0和1的定義不同,再或者就是先傳輸?shù)氖歉呶贿是低位 , 就是以上三點不同---->個人理解,可能有所偏差,但是對于我們,我們可以不關(guān)心解碼得到的是對的,還是不對的--->這里的“不對”是指是否和其真實的編碼相同,而不是解碼失敗。我們只需要關(guān)注,接收到的信息是否和數(shù)組里面的一樣,如果一樣,就完成某種功能。 當然,這里有更加簡單的方法,就是無需每次記錄,用一個薄碼開關(guān),去選擇學習模式和工作模式,這時候定義一個數(shù)組,然后先進入學習模式下,將接收到的信息放在數(shù)組中,然后進入工作模式,工作模式下只是對比此次接受的編碼和數(shù)組當中的編碼,如果相同,則完成一定的任務(wù)就可以了)
問題0012:Proteus仿真的那點事
越來越多的人,問我以下問題
(1)為什么我的程序在Proteus中可以運行,但在實物上卻運行失敗。
(2)為什么我的程序在實物上可以運行,但是Proteus中卻運行失敗。
現(xiàn)在在這里做統(tǒng)一解答 : (1)Proteus仿真和實物有差異,在Proteus中成功的代碼不一定可以用于實物,反過來,實物上已經(jīng)成功的代碼也不一定可以用于Proteus。如果出現(xiàn)了一方成功,一方失敗,那么很大的就是延時。(當然不排除其他可能性,但這個延時問題可能最大)
(2)如果軟件仿真和硬件實驗,只有一方可以成功,而另一方失敗。 那么表明你的邏輯上沒有錯誤。參照第一條,稍做修改即可 。
問題0013:程序的嚴格性
程序的嚴格性似乎總是被很多人遺忘,在編寫程序時,一定要把隱患消滅掉,否則后患無窮。這里以1602液晶屏為例,希望可以作為一個切入點,但是要通過問題表象,看到問題本質(zhì),而不僅僅局限于1602液晶屏,思想更加重要。大家都知道1602液晶屏的數(shù)據(jù)口是8位數(shù)據(jù)口,但是實際上還有一種模式,就是4位數(shù)據(jù)口,實質(zhì)上很簡單,就是利用4位數(shù)據(jù)口傳送數(shù)據(jù),傳送兩次而已,其他無差別,但這不是這里討論的關(guān)鍵。下面看一下網(wǎng)友的代碼(只放關(guān)鍵部分):
[cpp] view plain copy
#define DataPort P0
void LCD_Write_Data(unsigned char Data)
{
//while(LCD_Check_Busy()); //忙則等待
DelayMs(5);
RS_SET;
RW_CLR;
EN_SET;
DataPort= Data;
_nop_();
EN_CLR;
EN_SET;
DataPort= Data<<4;
_nop_();
EN_CLR;
}
其中,我們之關(guān)心兩句話,就是DataPort= Data; 和DataPort= Data<<4; 這兩句話什么意思呢 ? 就是通過P0端口的高4個Bit,把數(shù)據(jù)傳送兩次,使用的是P04,P05,P06,P07。這里的實驗網(wǎng)友成功了,他認為這個代碼沒有問題,但是這里的隱患確實非常明顯的,如果不該掉,可能導致這樣的情況發(fā)生:在1602的液晶屏4位數(shù)據(jù)口模式的獨立實驗中成功了,但是在一個復雜的系統(tǒng)設(shè)計中,卻出現(xiàn)了很多意料之外的情況。實際上,問題較為突出,比如第一句DataPort= Data ; 從上面我們看出,對于1602而言,他只關(guān)心和他的4位數(shù)據(jù)口連接的 P04,P05,P06,P07的狀態(tài),但是上面的復制,卻影響到了P00,P01,P02,P03,這里明顯不行的,比如P00,P01,P02,P03連接了LED或者其他設(shè)備,這時候會影響到他們的運行。所以我的建議是,修改為:
[cpp] view plain copy
第一次傳送:
DataPort &= 0x0f ; /*P0高4位清零,準備傳送數(shù)據(jù)*/
DataPort |= (temp & 0xf0) ;/*P0傳送數(shù)據(jù)temp的高4位,P0的低4位不受影響*/
第二次傳送:
DataPort &= 0x0f ; /*P0高4位清零,準備傳送數(shù)據(jù)*/
DataPort |= ((temp & 0x0f)<<4) ;/*P0傳送數(shù)據(jù)temp的低4位,P0的低4位不受影響*/
這里,希望新手可以通過這表象,看到問題的本質(zhì),將隱患扼殺在搖籃,否則簡單的1602的4位數(shù)據(jù)口模式,會成為你調(diào)試復雜系統(tǒng)調(diào)試的噩夢。
問題0014:液晶如何顯示小數(shù)并且小數(shù)自動四舍五入
液晶顯示數(shù)據(jù)的小小技巧:(主要針對液晶顯示浮點型數(shù)據(jù))
我們在使用液晶屏的時候,無論是1602/12864、NOKIA、J2004A還是其他的,常常遇到需要顯示一個變量(尤其是小數(shù)),那么顯示變量又有一些小小的技巧,這里把我的一些使用方法,給新手介紹一下,歡迎新人學習,更加歡迎高手拍磚(但不能總是拍,得讓我知道為什么被拍)
(1)首先,我們需要先做一些預備知識:如何進行四舍五入:
[cpp] view plain copy
float temp = 2.376 ;
我們需要保留兩位小數(shù),也就是2.38(四舍五入后的結(jié)果)
在程序中,我們只需要一句話:temp = (unsigned int)((temp*100)+0.5) / 100 ;就完成了這個功能,得到的結(jié)果就是2.38(不信可以試試看額)
(2)液晶如何顯示小數(shù)
使用以下代碼:
[cpp] view plain copy
#include
#include
#include "lcd1602.h"
void main(void)
{
float a = 182.376 ;
lcd1602Init();
lcd1602WriteCommand(0X0C);
/*顯示原來的數(shù)字,非重點*/
lcd1602AddressWriteByte(LCD1602_ROW0,0,((unsigned long)a)%1000/100+'0') ;
lcd1602AddressWriteByte(LCD1602_ROW0,1,((unsigned long)a)%100/10+'0') ;
lcd1602AddressWriteByte(LCD1602_ROW0,2,((unsigned long)a)%10+'0') ;
lcd1602AddressWriteByte(LCD1602_ROW0,3,'.') ;
lcd1602AddressWriteByte(LCD1602_ROW0,4,(unsigned long)(a*10)%10+'0') ;
lcd1602AddressWriteByte(LCD1602_ROW0,5,(unsigned long )(a*100)%10+'0') ;
lcd1602AddressWriteByte(LCD1602_ROW0,6,(unsigned long )(a*1000)%10+'0') ;
lcd1602AddressWriteByte(LCD1602_ROW0,8,0x7e) ;
/*重點部分*/
/*顯示,百位這里我一直最高是百位,所以沒有判斷更加位,實際操作時,應(yīng)該添加其他機制,使其更加智能*/
lcd1602AddressWriteByte(LCD1602_ROW0,10,((unsigned long)a)%1000/100+'0') ;
/*顯示十位*/
lcd1602AddressWriteByte(LCD1602_ROW0,11,((unsigned long)a)%100/10+'0') ;
/*顯示各位*/
lcd1602AddressWriteByte(LCD1602_ROW0,12,((unsigned long)a)%10+'0') ;
/*顯示小數(shù)點*/
lcd1602AddressWriteByte(LCD1602_ROW0,13,'.') ;
/*顯示小數(shù)點后第一位*/
lcd1602AddressWriteByte(LCD1602_ROW0,14,(unsigned long)(a*10)%10+'0') ;
/*顯示小數(shù)點后第二位,此時需要四舍五入計算*/
lcd1602AddressWriteByte(LCD1602_ROW0,15,(unsigned long )(a*100+0.5)%10+'0') ;
lcd1602AddressWriteString(LCD1602_ROW1,0,"QQ:279729201") ;
while(1);
}
下載程序后效果如下圖所示:
如果此時修改數(shù)據(jù)temp為182.372,則顯示為下圖:
發(fā)現(xiàn)液晶不僅顯示了數(shù)據(jù),而且有了四舍五入的心結(jié)果。實際上,這里為了實驗效果,部分代碼并沒有做很好的處理,主要是為了凸顯出重點,網(wǎng)友們可以在我的基礎(chǔ)上不斷修改,希望大家可以通過點,看到面,不要局限于這一個方式。
編輯:admin 最后修改時間:2018-05-18