Arduino IDE相比Visual C++ 6.0等開發(fā)環(huán)境在調(diào)試支持上顯得非常簡陋,而調(diào)試是編程必備技能,不會調(diào)試,編程就變?yōu)榱说托У摹霸囌`”,同時也失去了深入理解一些程序運行細節(jié)的機會。
在Arduino編程中,利用串口輸出一些程序運行過程的信息,是調(diào)試和監(jiān)控程序運行的最基本手段。
Arduino的Serial庫中,可用于輸出的方法主要有write()
、println()
和print()
,后兩者的區(qū)別只是println()
在輸出之后自動加一個換行,下次輸出的位置從下一行開始。
write()
的用法如下:
-
Serial.write(val)
,val
可為整型或字符型變量,在串口監(jiān)視器中輸出val
對應(yīng)ASCII碼的字符,如val=65
,則串口監(jiān)視器輸出大寫字母A。若val
的值超出了ASCII碼范圍,或者對應(yīng)到如“回車”(ASCII碼為13)這樣的不可打印字符,則會在串口監(jiān)視器中看到亂碼或無顯示。 -
Serial.write("string")
,在程序中需要原樣輸出一些字符串的時候,可以用這種方式。
綜上,write()
可接受字符型和整型參數(shù),但輸出將自動轉(zhuǎn)化為字符形式。
由于編程調(diào)試時,我們所監(jiān)控的一些變量,很多是整型(如循環(huán)變量),我們希望直接在串口監(jiān)視器中看到它們的值(而非對應(yīng)的ASCII字符),這種情況下用write()
就不那么方便了。
相比write()
,print()
和println()
的優(yōu)勢就在于方便直接輸出整型數(shù)據(jù)。
void setup() {
Serial.begin(9600);
}
void loop()
{
int c=1; //定義一個整型變量(模擬調(diào)試過程中要監(jiān)視的變量)
Serial.write("Serial.write() output:");
Serial.write(c); //以write方式輸出測試
Serial.println(); //換行
Serial.write("Serial.println() output:");
Serial.println(c); //以println方式輸出測試
delay(1000);
}
上面這個示例代碼的運行結(jié)果如下:
我們可以看到,由于
write()
輸出的是變量c
的值(1)對應(yīng)ASCII碼,由于這是個非可見字符,所以在串口監(jiān)視器中表現(xiàn)為空白,而println()
是直接輸出變量c
的值,因而我們看到了“1”。此外,
println()
還支持第二個參數(shù),可以指定輸出的數(shù)制。可用的參數(shù)包括DEC
(十進制)、HEX
(十六進制)、OCT
(八進制)和BIN
(二進制)。
小結(jié):調(diào)試過程中,需要監(jiān)控的變量為字符類型并且希望以字符的形式觀察變量,可用write()
,如果希望以數(shù)值(整型)的形式觀察變量,則用println()
。
小試牛刀
Arduino的串口監(jiān)視器不僅能夠輸出,還能夠接受輸入。在有些情況下,為了方便程序識別輸入數(shù)據(jù)的結(jié)束,可以在輸入序列之后添加“結(jié)束符”。串口監(jiān)視器下方有這樣一個關(guān)于結(jié)束符的選項:分別是沒有結(jié)束符
、換行符
、回車符
和NL和CR
(換行和回車符),它們有什么區(qū)別呢? 我們可以用println()
來檢測一下:
void setup() {
Serial.begin(9600);
}
void loop() {
char c;
if(Serial.available()) {
c=Serial.read(); //讀取串口監(jiān)視器中輸入的數(shù)據(jù)
Serial.println(c, DEC); //將讀取數(shù)據(jù)的ASCII碼以十進制的形式顯示
}
delay(1000);
}
運行這段代碼,分別在串口監(jiān)視器中輸入一個字符A,然后設(shè)置不同的結(jié)束符,看到println()
顯示的接收字符的數(shù)量及ASCII碼的不同。
其中,第一個數(shù)65,是‘A’的ASCII碼。在設(shè)置了結(jié)束符后,10是換行符(‘\n’)的ASCII碼,13是回車符('\r')的ASCII碼。
因為Serial.read()
這個操作,每執(zhí)行一次,就從串行通信接收緩沖區(qū)中讀取一個字節(jié)的數(shù)據(jù),因為這個操作放在了loop()
函數(shù)中,所以就會自動讀取下一個字符,直到所有數(shù)據(jù)讀取完畢,即Serial.available()==0
這就提示我們,如果是在循環(huán)結(jié)構(gòu)中讀取串口數(shù)據(jù),要特別注意輸入時有沒有自動添加結(jié)束符,最好在代碼中進行判斷,以此來達到不管用戶有沒有添加結(jié)束符,都能夠正確運行的目的。
利用上面這個代碼,我們還可以做另外一個實驗:有時候我們做串口輸入的時候,不寫if ( Serial.available() )
直接用Serial.read()
似乎也能得到正確結(jié)果,那么是否還要“多此一舉”呢?
我們可以把if ( Serial.available() )
這個if結(jié)構(gòu)注釋掉,然后運行程序看看。這時你會發(fā)現(xiàn),沒有了這個判斷,不管串行通信緩沖區(qū)中有沒有收到數(shù)據(jù),程序都會去讀,在沒有數(shù)據(jù)的情況下,“讀到”的當(dāng)然是非法數(shù)據(jù)。所以,為了程序的可靠性,在做串口讀操作的時候,還是要加上if ( Serial.available() )
這個結(jié)構(gòu)的。另外,Serial.available()
能夠返回當(dāng)前從串口輸入數(shù)據(jù)的字節(jié)數(shù),這對于我們判斷輸入的情況及“讀取前幾個字節(jié)”這種需求是非常有用的。