文章目錄
一、結(jié)構(gòu)
1.1 setup()
在Arduino中程序運(yùn)行時(shí)將首先調(diào)用 setup() 函數(shù)。用于初始化變量、設(shè)置針腳的輸出\輸入類(lèi)型、配置串口、引入類(lèi)庫(kù)文件等等。每次 Arduino板子 上電或重啟后,setup 函數(shù)只運(yùn)行一次。
int buttonPin = 3; void setup() { Serial.begin(9600); pinMode(buttonPin, INPUT); } void loop() { // ... }
1.2 loop()
在 setup() 函數(shù)中初始化和定義了變量,然后執(zhí)行 loop() 函數(shù)。顧名思義,該函數(shù)在程序運(yùn)行過(guò)程中不斷的循環(huán),根據(jù)一些反饋,相應(yīng)改變執(zhí)行情況。通過(guò)該函數(shù)動(dòng)態(tài)控制 Arduino 主控板。
int buttonPin = 3; // setup 中初始化串口和按鍵針腳. void setup() { beginSerial(9600); pinMode(buttonPin, INPUT); } // loop 中每次都檢查按鈕,如果按鈕被按下,就發(fā)送信息到串口 void loop() { if (digitalRead(buttonPin) == HIGH) serialWrite('H'); else serialWrite('L'); delay(1000); }
二、結(jié)構(gòu)控制
結(jié)構(gòu)控制這一塊和C語(yǔ)言類(lèi)似,就是控制程序執(zhí)行流程的分支、循環(huán)、順序結(jié)構(gòu)的一些特定語(yǔ)法。
熟悉C語(yǔ)言的話(huà),這塊不用過(guò)一眼就好。
2.1 if
if(條件判斷語(yǔ)句)和 ==、!=、<、>(比較運(yùn)算符)
if 語(yǔ)句與比較運(yùn)算符一起用于檢測(cè)某個(gè)條件是否達(dá)成,如某輸入值是否在特定值之上等。if 語(yǔ)句的語(yǔ)法是:
if (someVariable > 50) { // 執(zhí)行某些語(yǔ)句 }
本程序測(cè)試 someVariable 變量的值是否大于 50。當(dāng)大于 50 時(shí),執(zhí)行一些語(yǔ)句。換句話(huà)說(shuō),只要 if 后面括號(hào)里的結(jié)果(稱(chēng)之為測(cè)試表達(dá)式)為真,則執(zhí)行大括號(hào)中的語(yǔ)句(稱(chēng)之為執(zhí)行語(yǔ)句塊);若為假,則跳過(guò)大括號(hào)中的語(yǔ)句。 if 語(yǔ)句后的大括號(hào)可以省略。若省略大括號(hào),則只有一條語(yǔ)句(以分號(hào)結(jié)尾)成為執(zhí)行語(yǔ)句。
if (x > 120) digitalWrite(LEDpin, HIGH); if (x > 120) digitalWrite(LEDpin, HIGH); if (x > 120){ digitalWrite(LEDpin, HIGH); } if (x > 120){ digitalWrite(LEDpin1, HIGH); digitalWrite(LEDpin2, HIGH); } // 以上所有書(shū)寫(xiě)方式都正確
在小括號(hào)里求值的表達(dá)式,需要以下操作符:
比較運(yùn)算操作符:
x == y(x 等于 y)
x != y(x 不等于 y)
x < y(x 小于 y)
x > y(x 大于 y)
x <= y(x 小于等于 y)
x >= y(x 大于等于 y)
警告:
注意使用賦值運(yùn)算符的情況(如 if (x = 10))。一個(gè)“=”表示的是賦值運(yùn)算符,作用是將 x 的值設(shè)為 10(將值 10 放入 x 變量的內(nèi)存中)。兩個(gè)“=”表示的是比較運(yùn)算符(如 if (x == 10)),用于測(cè)試 x 和 10 是否相等。后面這個(gè)語(yǔ)句只有 x 是 10 時(shí)才為真,而前面賦值的那個(gè)語(yǔ)句則永遠(yuǎn)為真。
這是因?yàn)?C 語(yǔ)言按以下規(guī)則進(jìn)行運(yùn)算 if (x=10):10 賦值給 x(只要非 0 的數(shù)賦值的語(yǔ)句,其賦值表達(dá)式的值永遠(yuǎn)為真),因此 x 現(xiàn)在值為 10。此時(shí) if 的測(cè)試表達(dá)式值為 10,該值永遠(yuǎn)為真,因?yàn)榉?0 值永遠(yuǎn)為真。所以,if (x = 10) 將永遠(yuǎn)為真,這就不是我們運(yùn)行 if 所期待的結(jié)果。另外,x 被賦值為 10,這也不是我們所期待的結(jié)果。
if 的另外一種分支條件控制結(jié)構(gòu)是 if…else 形式。
2.2 if…else
if/else是比if更為高級(jí)的流程控制語(yǔ)句,它可以進(jìn)行多次條件測(cè)試。比如,檢測(cè)模擬輸入的值,當(dāng)它小于500時(shí)該執(zhí)行哪些操作,大于或等于500時(shí)執(zhí)行另外的操作。代碼如下:
if (pinFiveInput < 500) { // 執(zhí)行A操作 } else { // 執(zhí)行B操作 }
else可以進(jìn)行額外的if檢測(cè),所以多個(gè)互斥的條件可以同時(shí)進(jìn)行檢測(cè)。
測(cè)試將一個(gè)一個(gè)進(jìn)行下去,直到某個(gè)測(cè)試結(jié)果為真,此時(shí)該測(cè)試相關(guān)的執(zhí)行語(yǔ)句塊將被運(yùn)行,然后程序就跳過(guò)剩下的檢測(cè),直接執(zhí)行到if/else的下一條語(yǔ)句。當(dāng)所有檢測(cè)都為假時(shí),若存在else語(yǔ)句塊,將執(zhí)行默認(rèn)的else語(yǔ)句塊。
注意else if語(yǔ)句塊可以沒(méi)有else語(yǔ)句塊。else if分支語(yǔ)句的數(shù)量無(wú)限制。
if (pinFiveInput < 500) { // 執(zhí)行A操作 } else if (pinFiveInput >= 1000) { // 執(zhí)行B操作 } else { // 執(zhí)行C操作 }
另外一種進(jìn)行多種條件分支判斷的語(yǔ)句是switch case語(yǔ)句。
2.3 switch case
switch / case語(yǔ)句
和if語(yǔ)句相同,switch…case通過(guò)程序員設(shè)定的在不同條件下執(zhí)行的代碼控制程序的流程。特別地,switch語(yǔ)句將變量值和case語(yǔ)句中設(shè)定的值進(jìn)行比較。當(dāng)一個(gè)case語(yǔ)句中的設(shè)定值與變量值相同時(shí),這條case語(yǔ)句將被執(zhí)行。
關(guān)鍵字break可用于退出switch語(yǔ)句,通常每條case語(yǔ)句都以break結(jié)尾。如果沒(méi)有break語(yǔ)句,switch語(yǔ)句將會(huì)一直執(zhí)行接下來(lái)的語(yǔ)句(一直向下)直到遇見(jiàn)一個(gè)break,或者直到switch語(yǔ)句結(jié)尾。
例子
switch (var) { case 1: //當(dāng)var等于1時(shí),執(zhí)行一些語(yǔ)句 break; case 2 //當(dāng)var等于2時(shí),執(zhí)行一些語(yǔ)句 break; default: //如果沒(méi)有任何匹配,執(zhí)行default //default可有可不有 }
語(yǔ)法
switch (var) { case label: // 聲明 break; case label: // 聲明 break; default: // 聲明 }
參數(shù)
var: 用于與下面的case中的標(biāo)簽進(jìn)行比較的變量值
label: 與變量進(jìn)行比較的值
2.4 for
for語(yǔ)句
描述
for語(yǔ)句用于重復(fù)執(zhí)行一段在花括號(hào)之內(nèi)的代碼。通常使用一個(gè)增量計(jì)數(shù)器計(jì)數(shù)并終止循環(huán)。for語(yǔ)句用于重復(fù)性的操作非常有效,通常與數(shù)組結(jié)合起來(lái)使用來(lái)操作數(shù)據(jù)、引腳。
for循環(huán)開(kāi)頭有3個(gè)部分:
(初始化;條件;增量計(jì)數(shù)){
//語(yǔ)句
}
“初始化”只在循環(huán)開(kāi)始執(zhí)行一次。每次循環(huán),都會(huì)檢測(cè)一次條件;如果條件為真,則執(zhí)行語(yǔ)句和“增量計(jì)數(shù)”,之后再檢測(cè)條件。當(dāng)條件為假時(shí),循環(huán)終止。
例子
//用PWM引腳將LED變暗 int PWMpin = 10; //將一個(gè)LED與47Ω電阻串聯(lián)接在10腳 void setup() { //無(wú)需設(shè)置 } void loop() { for (int i=0; i <= 255; i++) { analogWrite(PWMpin, i); delay(10); } }
編程提示
C語(yǔ)言的for循環(huán)語(yǔ)句比BASIC和其他電腦編程語(yǔ)言的for語(yǔ)句更靈活。除了分號(hào)以外,其他3個(gè)元素都能省略。同時(shí),初始化,條件,增量計(jì)算可以是任何包括無(wú)關(guān)變量的有效C語(yǔ)句,任何C數(shù)據(jù)類(lèi)型包括float。這些不尋常的for語(yǔ)句可能會(huì)解決一些困難的編程問(wèn)題。
例如,在增量計(jì)數(shù)中使用乘法可以得到一個(gè)等比數(shù)列:
for(int x = 2; x < 100; x = x * 1.5){ println(x); }
生成:2,3,4,6,9,13,19,28,42,63,94
另一個(gè)例子,使用for循環(huán)使LED產(chǎn)生漸亮漸滅的效果:
void loop() { int x = 1; for (int i = 0; i > -1; i = i + x) { analogWrite(PWMpin, i); if (i == 255) x = -1; // 在峰值轉(zhuǎn)變方向 delay(10); } }
2.5 while
while循環(huán)
描述
while循環(huán)會(huì)無(wú)限的循環(huán),直到括號(hào)內(nèi)的判斷語(yǔ)句變?yōu)榧佟1仨氁心芨淖兣袛嗾Z(yǔ)句的東西,要不然while循環(huán)將永遠(yuǎn)不會(huì)結(jié)束。這在您的代碼表現(xiàn)為一個(gè)遞增的變量,或一個(gè)外部條件,如傳感器的返回值。
語(yǔ)法
while(表達(dá)){
//語(yǔ)句
}
參數(shù)
表達(dá):為真或?yàn)榧俚囊粋(gè)計(jì)算結(jié)果
例子
var = 0; while(var < 200){ //重復(fù)一件事200遍 var++ }
2.6 do…while
do…while循環(huán)與while循環(huán)運(yùn)行的方式是相近的,不過(guò)它的條件判斷是在每個(gè)循環(huán)的最后,所以這個(gè)語(yǔ)句至少會(huì)被運(yùn)行一次,然后才被結(jié)束。
do
{
//語(yǔ)句
}while(測(cè)試條件);
例子
do{ delay(50); //等待傳感器穩(wěn)定 X = readSensors(); //檢查傳感器取值 }while(X <100); //當(dāng)x小于100時(shí),繼續(xù)運(yùn)行
2.7 break
break用于退出do,for,while 循環(huán),能繞過(guò)一般的判斷條件。它也能夠用于退出 switch語(yǔ)句。
例子
for (x = 0; x < 255; x ++) { digitalWrite(PWMpin, x); sens = analogRead(sensorPin); if (sens > threshold){ // 超出探測(cè)范圍 x = 0; break; } delay(50); }
2.8 continue
continue語(yǔ)句跳過(guò)當(dāng)前循環(huán)中剩余的迭代部分( do,for 或 while )。它通過(guò)檢查循環(huán)條件表達(dá)式,并繼續(xù)進(jìn)行任何后續(xù)迭代。
例子
for (x = 0; x < 255; x ++) { if (x > 40 && x < 120){ // 當(dāng)x在40與120之間時(shí),跳過(guò)后面兩句,即迭代。 continue; } digitalWrite(PWMpin, x); delay(50); }
2.9 return
終止一個(gè)函數(shù),如有返回值,將從此函數(shù)返回給調(diào)用函數(shù)。
語(yǔ)法
return;
return value; // 兩種形式均可
參數(shù)
value:任何變量或常量的類(lèi)型
例子
一個(gè)比較傳感器輸入閾值的函數(shù)
int checkSensor(){ if (analogRead(0) > 400) { return 1;} else{ return 0; } }
return關(guān)鍵字可以很方便的測(cè)試一段代碼,而無(wú)需“comment out(注釋掉)” 大段的可能存在bug的代碼。
void loop(){ //寫(xiě)入漂亮的代碼來(lái)測(cè)試這里。 return; //剩下的功能異常的程序 //return后的代碼永遠(yuǎn)不會(huì)被執(zhí)行 }
2.10 goto
程序?qū)?huì)從程序中已有的標(biāo)記點(diǎn)開(kāi)始運(yùn)行
語(yǔ)法
label:
goto label; //從label處開(kāi)始運(yùn)行
提示
不要在C語(yǔ)言中使用goto編程,某些C編程作者認(rèn)為goto語(yǔ)句永遠(yuǎn)是不必要的,但用得好,它可以簡(jiǎn)化某些特定的程序。許多程序員不同意使用goto的原因是, 通過(guò)毫無(wú)節(jié)制地使用goto語(yǔ)句,很容易創(chuàng)建一個(gè)程序,這種程序擁有不確定的運(yùn)行流程,因而無(wú)法進(jìn)行調(diào)試。
的確在有的實(shí)例中g(shù)oto語(yǔ)句可以派上用場(chǎng),并簡(jiǎn)化代碼。例如在一定的條件用if語(yǔ)句來(lái)跳出高度嵌入的for循環(huán)。
例子
for(byte r = 0; r < 255; r++){ for(byte g = 255; g > -1; g--){ for(byte b = 0; b < 255; b++){ if (analogRead(0) > 250){ goto bailout; } //更多的語(yǔ)句... } } } bailout:
三、擴(kuò)展語(yǔ)法
3.1 ;(分號(hào))
用于表示一句代碼的結(jié)束。
例子
int a = 13;
提示
在每一行忘記使用分號(hào)作為結(jié)尾,將導(dǎo)致一個(gè)編譯錯(cuò)誤。錯(cuò)誤提示可能會(huì)清晰的指向缺少分號(hào)的那行,也可能不會(huì)。如果彈出一個(gè)令人費(fèi)解或看似不合邏輯的編譯器錯(cuò)誤,第一件事就是在錯(cuò)誤附近檢查是否缺少分號(hào)。
3.2 {}(花括號(hào))
大括號(hào)(也稱(chēng)為“括號(hào)”或“大括號(hào)”)是C編程語(yǔ)言中的一個(gè)重要組成部分。它們被用來(lái)區(qū)分幾個(gè)不同的結(jié)構(gòu),下面列出的,有時(shí)可能使初學(xué)者混亂。
左大括號(hào)“{”必須與一個(gè)右大括號(hào)“}”形成閉合。這是一個(gè)常常被稱(chēng)為括號(hào)平衡的條件。在Arduino IDE(集成開(kāi)發(fā)環(huán)境)中有一個(gè)方便的功能來(lái)檢查大括號(hào)是否平衡。只需選擇一個(gè)括號(hào),甚至單擊緊接括號(hào)的插入點(diǎn),就能知道這個(gè)括號(hào)的“伴侶括號(hào)”。
目前此功能稍微有些錯(cuò)誤,因?yàn)镮DE會(huì)經(jīng)常會(huì)認(rèn)為在注釋中的括號(hào)是不正確的。
由于大括號(hào)被用在不同的地方,這有一種很好的編程習(xí)慣以避免錯(cuò)誤:輸入一個(gè)大括號(hào)后,同時(shí)也輸入另一個(gè)大括號(hào)以達(dá)到平衡。然后在你的括號(hào)之間輸入回車(chē),然后再插入語(yǔ)句。這樣一來(lái),你的括號(hào)就不會(huì)變得不平衡了。不平衡的括號(hào)常可導(dǎo)致許多錯(cuò)誤,比如令人費(fèi)解的編譯器錯(cuò)誤,有時(shí)很難在一個(gè)程序找到這個(gè)錯(cuò)誤。由于其不同的用法,括號(hào)也是一個(gè)程序中非常重要的語(yǔ)法,如果括號(hào)發(fā)生錯(cuò)誤,往往會(huì)極大地影響了程序的意義。
大括號(hào)中的主要用途
功能
void myfunction(datatype argument){ statements(s) }
循環(huán)
while (boolean expression) { statement(s) } do { statement(s) } while (boolean expression); for (initialisation; termination condition; incrementing expr) { statement(s) }
條件語(yǔ)句
if (boolean expression) { statement(s) } else if (boolean expression) { statement(s) } else { statement(s) }
3.3 //(單行注釋?zhuān)?/H3>
Comments(注釋?zhuān)?BR>注釋用于提醒自己或他人程序是如何工作的。它們會(huì)被編譯器忽略掉,也不會(huì)傳送給處理器,所以它們?cè)贏tmega芯片上不占用體積。 注釋的唯一作用就是使你自己理解或幫你回憶你的程序是怎么工作的或提醒他人你的程序是如何工作的。編寫(xiě)注釋有兩種寫(xiě)法:
例子
x = 5; // 這是一條注釋斜杠后面本行內(nèi)的所有東西是注釋 /* 這是多行注釋-用于注釋一段代碼 if (gwb == 0){ // 在多行注釋內(nèi)可使用單行注釋 x = 3; /* 但不允許使用新的多行注釋-這是無(wú)效的 } // 別忘了注釋的結(jié)尾符號(hào)-它們是成對(duì)出現(xiàn)的! */
小提示
當(dāng)測(cè)試代碼的時(shí)候,注釋掉一段可能有問(wèn)題的代碼是非常有效的方法。這能使這段代碼成為注釋而保留在程序中,而編譯器能忽略它們。這個(gè)方法用于尋找問(wèn)題代碼或當(dāng)編譯器提示出錯(cuò)或錯(cuò)誤很隱蔽時(shí)很有效。
3.4 /* */(多行注釋?zhuān)?/H3>
3.5 #define
#define 是一個(gè)很有用的C語(yǔ)法,它允許程序員在程序編譯之前給常量命名。在Arduino中,定義的常量不會(huì)占用芯片上的任何程序內(nèi)存空間。在編譯時(shí)編譯器會(huì)用事先定義的值來(lái)取代這些常量。
然而這樣做會(huì)產(chǎn)生一些副作用,例如,一個(gè)已被定義的常量名已經(jīng)包含在了其他常量名或者變量名中。在這種情況下,文本將被#defined 定義的數(shù)字或文本所取代。
通常情況下,優(yōu)先考慮使用 const 關(guān)鍵字替代 #define 來(lái)定義常量。
Arduino 擁有和 C 相同的語(yǔ)法規(guī)范。
語(yǔ)法
#define 常量名 常量值 注意,#是必須的。
例子
#define ledPin 3 //在編譯時(shí),編譯器將使用數(shù)值 3 取代任何用到 ledPin 的地方。 提示 在#define 聲明后不能有分號(hào)。如果存在分號(hào),編譯器會(huì)拋出語(yǔ)義不明的錯(cuò)誤,甚至關(guān)閉頁(yè)面。 #define ledPin 3; //這是一種錯(cuò)誤寫(xiě)法 類(lèi)似的,在#define聲明中包含等號(hào)也會(huì)產(chǎn)生語(yǔ)義不明的編譯錯(cuò)誤從而導(dǎo)致關(guān)閉頁(yè)面。 #define ledPin = 3 //這是一種錯(cuò)誤寫(xiě)法
3.6 #include
#include用于調(diào)用程序以外的庫(kù)。這使得程序能夠訪(fǎng)問(wèn)大量標(biāo)準(zhǔn)C庫(kù),也能訪(fǎng)問(wèn)用于arduino的庫(kù)。 AVR C庫(kù)(Arduino基于AVR標(biāo)準(zhǔn)語(yǔ)法)語(yǔ)法手冊(cè)請(qǐng)點(diǎn)擊這里。 注意#include和#define一樣,不能在結(jié)尾加分號(hào),如果你加了分號(hào)編譯器將會(huì)報(bào)錯(cuò)。
例子
此例包含了一個(gè)庫(kù),用于將數(shù)據(jù)存放在flash空間內(nèi)而不是ram內(nèi)。這為動(dòng)態(tài)內(nèi)存節(jié)約了空間,大型表格查表更容易實(shí)現(xiàn)。
#include <avr/pgmspace.h> prog_uint16_t myConstants[] PROGMEM = { 0, 21140, 702 , 9128, 0, 25764, 8456,0,0,0,0,0,0,0,0,29810,8968,29762,29762,4500};