Processing 也有支援網頁的 Processing.js, 參考 :
# https://processing.org/exhibition/
# https://en.wikipedia.org/wiki/Processing.js
Arduino 程式可由五個部分組成 :
//1. 匯入函式庫與定義 (可有可無)
#include <SoftEasyTranfer.h>
#define LEDPIN 13;
//2. 宣告常數與全域變數 (可有可無)
const float PI=3.14159;
int r;
//3. 設定函式 (必要)
void setup() {}
//4. 無限迴圈 (必要)
void loop() {}
//5. 自訂函式 (可有可無)
float area(float r) {
float a=PI*r*r;
return a;
}
其中 setup() 與 loop() 是一定要有的函式 (均無參數無傳回值), 其他則視需要而定. Arduino 語言採用 C/C++ 語法, 加上以 Wiring 為基礎的電子設計核心函式庫組合而成, 包括 Digital I/O, Analog I/O 等函式庫. 內建的函式庫可直接調用, 但若有使用第三方函式庫 (例如驅動感測器模組所需的函式庫), 則必須使用 include 前置指令引入. 此外, 也可以用前置指令 define 定義一個常數或巨集 (運算式).
前置指令乃 C 編譯器指令, 不屬於 C 語言本身, 其用途有三 :
- 引入標頭檔 : 例如 #include <myLibrary.h> 或 "myLibrary.h"
- 定義常數 : 例如 #define PI 3.14159
- 定義巨集 : 例如 #define AREA(r) PI*r*r
標頭檔可用角括號 < > 或雙引號 "", 差別是用雙引號時, 前置處理器會先從原始檔所在位置開始去搜尋標頭檔; 而用角括號則會先從 libraries 目錄開始找.
以下整理這些基本語法以利後續實驗時查考之用, 以 UNO/Nano/Pro Mini 這些主要板子為對象. Arduino 語法文件請參考 :
# https://www.arduino.cc/en/Reference/HomePage
變數與函式命名 (識別字) :
- 只能使用英數字與底線組合, 且不能以數字開頭, 英文字母有分大小寫.
- 不可使用保留字.
char c;
int i=0;
boolean b=false;
因源自 C/C++ 語言, 因此宣告變數時必須指定其值之資料類型, 函式若有傳回值也要指定傳回值之資料類型, 否則須宣告為 void, 例如 setup() 與 loop() 兩個必要函式就不會有傳回值, 故必須宣告為 void.
宣告於 setup() 與 loop() 或自訂函數外的變數稱為全域變數 (global), 在程式的任何地方皆能存取; 宣告於函數內部的變數 (包含傳入之引數) 稱為區域變數 (local), 其值僅在函數內部有效. 如果全域變數與函數中的區域變數同名, 則在函數中存取到的是區域變數, 例如 :
int i=1;
void setup() {
Serial.begin(9600);
int i=0;
Serial.println(i); //輸出 0 (區域變數)
}
void loop() {}
資料類型 :
Arduino 的資料型態與 C 語言一樣, 但資料長度可能因板子而異, 下表適用於 UNO, Nano, Pro Mini 等以 ATmega328 為處理器的板子, 只有 Due 板子有所不同 :
資料型態 | 說明 | 記憶體長度 | 數值範圍 |
boolean | 布林 | 8 bits | true (1, HIGH), false(0, LOW) |
byte | 位元組 | 8 bits | 0~255 |
char | 字元 | 8 bits | -128~127 |
short | 短整數 | 16 bits | -32768~32767 |
int | 整數 | 16 bits | -32768~32767 |
word | 字 | 16 bits | 0~65535 |
long | 長整數 | 32 bits | -2147483648~2147483647 |
float | 浮點數 | 32 bits | +-3.4028235e+38 |
double | 倍精度浮點數 | 32 bits | +-3.4028235e+38 |
Arduino 還有兩個非數值的資料類型 :
資料型態 | 說明 |
void | 用來表示函數無傳回值時之資料類型 |
String | 用來表示字串 (Arduino 0019 Alpha 版以後) |
要注意的是 : 一般 Arduino 板子的 double 跟 float 是完全一樣的, 都是 32 位元, 而 Due 板則跟一般 C 語言一樣是 64 位元. 其次, 整數的 short 與 int 也是一樣 16 位元的 (但在 Due 板子, int 是 32 位元). 文件中整數類的 char, int, 與 long 這三種型態有分 signed (有號) 與 unsigned (無號的), 沒有提到 unsigned short, 但我測試是有的.
資料型態 | 說明 | 記憶體長度 | 數值範圍 |
unsigned char | 字元 | 8 bits | 0~255 |
unsigned short | 短整數 | 16 bits | 0~65535 |
unsigned int | 整數 | 16 bits | 0~65535 |
unsigned long | 長整數 | 32 bits | 0~4294967295 |
有號與無號差別在於最高位元是否拿來當正負符號 (2 的補數), signed 使用最高位元來表示正負數, 1 為負數, 0 為正數; 而 unsigned 則全部都是正數, 因此其可表示的正數範圍是 signed 的兩倍, 例如儲存字元用的 char 也可以用來儲存較小的整數 (8 位元), 若宣告為 unsigned char, 其範圍便從 -128~127 變成 0~255.
Char, short, int, 與 long 預設是 signed, 亦即只有全部用做正數的變數才需要宣告 unsigned. 正負值都有的變數宣告為 signed 是多此一舉. 整數類中最常用的是 int, 因為其範圍可以滿足大部分應用所需.
Char 是其實是 8 位元整數, 用來表示 ASCII 字元, 例如 'A' 實際上是以其 ASCII 碼 65 儲存的, 好處是可以透過運算處理字元, 例如 'A' + 2 就得到 'C'. 所以字元可以用下面兩種方式表示 :
char c='A'; //字元必須用單引號括起來, 不能用雙引號
char c=65;
Byte 與 word 類型只有正數, byte 與 char 一樣是 8 位元, 等於 unsigned char; 而 word 與 int 一樣是 16 位元, 等於 unsigned int.
布林值可用 true/false (必須小寫), 0/1, 或者 HIGH/LOW 表示, 依據使用場合何者較有意義而定. 其中 HIGH/LOW 是 Arduino 定義的常數, 適合用在 digitalWrite() 函數中表示 LED 輸出位準, 而 true/false 適合用在程式邏輯的分岐判斷.
指定資料類型時須注意不能超過範圍, 超過時將歸零或變成負數等非預期結果, 成為 roll-over (反折), 例如 byte 最大 255, 若存入 256, 仍可通過編譯, 但真正存入的值會變成 0; 而整數最大為 32767, 若存入 32768, 結果變成 -32768, 例如 :
void setup() {
Serial.begin(9600);
byte b=256;
Serial.println(b); //輸出 0
int i=32768;
Serial.println(i); //輸出 -32768
}
void loop() {}
另外, 初始化一個變數時, 所賦予之字面值 (Literal) 的表示方法也會影響運算結果之正確性, 例如 2048*16=32768, 用 int 來儲存是不夠的, 必須宣告為 long. 此外字面值 2048 或 16 也至少要有一個後面加一個 "L" 或者用 (long) 強制轉型才行, 表示要以資料類型 long 儲存, 否則它會以 int 為類型進行運算, 例如 :
void setup() {
Serial.begin(9600);
long a=2048*16;
Serial.println(a); //輸出 -32768 (非預期之結果)
long b=2048L*16;
Serial.println(b); //輸出 32768 (預期之結果)
long c=(long)2048*16;
Serial.println(c); //輸出 32768 (預期之結果)
}
void loop() {}
整數字面值強制轉型的後綴有三個, 大小寫均可 :
- L 或 l : 強制轉成長整數
- U 或 u : 強制轉成無號之整數 (int)
- UL 或 ul : 強制轉成無號之長整數
void setup() {
Serial.begin(9600);
float a=1/2;
Serial.println(a); //輸出 0.00 (被當成整數處理)
float b=1.0/2;
Serial.println(b); //輸出 0.50 (至少一個是浮點數即可)
float c=(float)1/2;
Serial.println(c); //輸出 0.50 (強制轉型)
}
void loop() {}
Arduino 內建六個函式來轉換不同資料型態如下表 :
型態轉換函式 | 說明 |
char(x) | 將任何型態之 x 轉成 char 型態 |
byte(x) | 將任何型態之 x 轉成 byte 型態 |
int(x) | 將任何型態之 x 轉成 int 型態 |
word(x), word(h,l) | 將任何型態之 x 轉成 word 型態, x 可拆成高位元組 h 與低位元組 l |
long(x) | 將任何型態之 x 轉成 long 型態 |
float(x) | 將任何型態之 x 轉成 float 型態 |
要注意, 記憶體長度大的轉成較小時會有 roll-over 問題, 例如 :
void setup() {
Serial.begin(9600);
int i=257;
byte b=byte(i); //輸出 1, byte 類型最大值 255, 256 時反折為 0
Serial.println(b);
}
void loop() {}
另外, 整數除了用十進位表示外, 也可以用二進位 (0b/0B 開頭), 八進位 (0 開頭), 與十六進位表示 (0x/0X 開頭), 例如 :
void setup() {
Serial.begin(9600);
byte dec=128;
byte bin=0b10000000;
byte oct=0200;
byte hex=0x80;
Serial.println(dec); //輸出 128
Serial.println(bin); //輸出 128
Serial.println(oct); //輸出 128
Serial.println(hex); //輸出 128
}
void loop() {}
內建常數 :
Arduino 定義了八個內建常數 (注意, true 與 false 須小寫) :
常數 | 說明 |
HIGH | 輸出高電位 |
LOW | 輸出低電位 |
INPUT | 輸入腳 |
OUTPUT | 輸出腳 |
INPUT_PULLUP | 啟動上拉電阻之輸入腳 |
LED_BUILTIN | 內建 LED (Pin 13) |
true | 真 (=1) |
false | 假 (=0) |
PI | 圓周率 3.14159 |
DEG_TO_RAD | 角度轉弧度常數=PI/180=0.0174533 |
RAD_TO_DEG | 弧度轉角度常數=180/PI=57.29578 |
例如 LED 閃爍程式 :
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH);
delay(1000);
digitalWrite(LED_BUILTIN, LOW);
delay(1000);
}
也可以用 const 自行定義常數 (但不可重複定義內建常數), 例如可定義常數 LED 以取代上內建的 LED_BUILTIN, 效果一樣 :
const int LED=13;
常數賦值後無法更改, 否則編譯時即會失敗. 此外常數名稱通常使用大寫, 但這只是習慣, 並非語法規則. 另外, Arduino 的 I/O 針腳預設是 INPUT, 因此只需要宣告哪些腳要當 OUTPUT 即可, pinMode(2, INPUT) 其實是多此一舉的.
陣列與字串 :
陣列可以先宣告再賦值 (使用大括號), 例如 :
int a[4]; //宣告含有四個整數元素之陣列
a={1,2,3,4}; //陣列賦值
或者元素逐一賦值 :
int a[4];
a[0]=1;
a[1]=2;
a[2]=3;
a[3]=4;
或者宣告同時賦值 :
int a[4]={1,2,3,4}; //宣告同時賦值
也可以不填元素數目, 由編譯器自動計算 :
int a[]={1,2,3,4}; //由編譯器自動計算
但多維陣列只能第一維不寫交給編譯器去算, 其他維必須填寫, 例如 :
int a[][2]={{0,0},{0,1},{1,0},{1,1}}; //這樣 OK
int a[][]={{0,0},{0,1},{1,0},{1,1}}; //這樣不 OK (編譯失敗)
int a[4][2]={{0,0},{0,1},{1,0},{1,1}}; //當然 OK
陣列之存取是透過以 0 起始的索引, 最後一個元素之索引為長度減 1, 必須注意不可超過範圍. 由於 Arduino IDE 的 C 編譯器不會檢查索引是否超出範圍 (但會檢查是否夠放), 因此良好的 coding 習慣是利用常數來表示陣列大小, 例如 :
void setup() {
Serial.begin(9600);
const int COUNT=4; //使用常數設定陣列大小
int a[COUNT]={1,2,3,4};
for (int i=0; i
}
}
void loop() {}
1
2
3
4
26624 (超限的索引 4 存取到陣列外之數值)
可知, 陣列宣告的元素個數可以比賦值的多, 但不能少 (亦即要夠放, 否則會編譯失敗). 沒有被賦值的元素會有預設值, 整數類是 0, char 是空字元, 浮點數則是 0.00, 例如 :
Serial.begin(9600);
int a[5]={1,2,3,4}; //5 個元素只賦值 4 個
for (int i=0; i<5; i++) {
Serial.println(a[i]);
}
}
void loop() {}
1
2
3
4
0 (未賦值=預設值)
Arduino 裡的字串可以用兩種方法來做, 一是用 C 語言本來就有的 char 類型的陣列; 二是用 Arduino IDE 0095 版後提供的 String 資料類型 (C 語言本身無此資料型別, 而是透過 string.h 函式庫提供). 使用 char[] 儲存字串時要注意, 必須在陣列結尾加一個 '\0' (NULL) 字元 (直接用整數 0 也是可以的), 這是字串的結束符號 :
char a[5]={'A','B','C','D','\0'}; //四個字元外加一個 NULL
char a[5]={'A','B','C','D', 0};
C 語言中字元陣列與字串的差別如下圖所示, C 是利用字元陣列後面另加一個 NULL 來表示字串的 :
字串變數也可以直接用字串常值 (Literal) 來賦值 (注意, Arduino 中, 字元須用單引號, 字串須用雙引號括起來, C 語言則沒有字串), 如果自行指定長度, 必須比資料字元數多 1, 否則編譯失敗, 要不然乾脆不要指定, 由編譯器處理就好 :
char a[5]="ABCD"; //自行指定陣列大小, 須比資料字元多 1
char a[]="ABCD"; //由編譯器自動計算陣列大小
例如 :
void setup() {
Serial.begin(9600);
char a[]="ABCD"; //由編譯器自動計算陣列大小
Serial.println(sizeof(a)); //輸出 5, 多一個 byte 存放結尾的 NULL (0)
for (int i=0; i<sizeof(a); i++) {
Serial.print(a[i]); //輸出 ABCD
}
}
void loop() {}
Serial.begin(9600);
float a[]={1.0, 2.0, 3.0, 4.0};
Serial.println(sizeof(a)); //輸出 16, 4*4=16
for (int i=0; i<sizeof(a); i++) {
Serial.print(a[i]); //輸出 1.002.003.004.000.00-0.000.000.000.000.000.000.000.000.00ovfovf
}
}
Serial.begin(9600);
float a[]={1.0, 2.0, 3.0, 4.0};
Serial.println(sizeof(a)/sizeof(float)); //輸出 4
for (int i=0; i<sizeof(a)/sizeof(float); i++) {
Serial.print(a[i]); //輸出 1.002.003.004.00
}
}
void loop() {}
void setup() {
Serial.begin(9600);
int a[][2]={{0,0},{0,1},{1,0},{1,1}};
Serial.println(sizeof(a)); //輸出 16
Serial.println(sizeof(a)/sizeof(int)); //輸出 8
}
void loop() {}
void setup() {
Serial.begin(9600);
int a[]={1,2,3,4};
Serial.println(sum(a,4)); //輸出 10
}
int sum(int a[], int size) { //傳入陣列位址與大小
int sum=0;
for (int i=0; i<size; i++) {
sum += a[i];
}
return sum;
}
void loop() {}
OK, 回到字串主題, 字串也可以組成陣列, 當然可以用兩層有 NULL 的字元陣列來表示, 例如 :
char users[][5]={{'P','e','t','e','r','\0'},{'A','m','y','\0'},{'K','e','l','l','y','\0'}};
注意, 這裡第二維必須填上各字串中最長字元數 (不含 NULL), 第一維 (字串數) 則可不填, 編譯器會自動設定. 當然, 若第一維填入比最長字串還長的數也是可以的, 編譯器只讀到 NULL 便停止, 後面多出來的字元 (存的是無法預測的值) 其實是浪費的.
比較方便的賦值方式是直接用字串常數賦值 :
char users[][5]={"Peter","Amy","Kelly"};
# https://www.arduino.cc/en/Reference/StringObject
也可以參考 C 語言的 string.h :
# C 語言標準函數庫分類導覽 - 字串處理 string.h
字串函數 | 說明 |
length() | 傳回字串長度 |
indexOf(val), indexOf(val, from) | 從左方搜尋子字串, 傳回首次出現位置索引 |
lastIndexOf(val), lastIndexOf(val, from) | 從右方搜尋子字串, 傳回首次出現位置索引 |
substring(from), substring(from, to) | 傳回子字串 |
replace(substr1, substr2) | 將子字串 substr1 以子字串 substr2 取代 |
concat(str) | 將 str 字串串接在後面 |
remove(index), remove(index, count) | 刪除索引 index 到結尾之字元, 或刪除指定字元數 count |
toLowerCase() | 傳回轉成小寫後之字串 |
toUpperCase() | 傳回轉成大寫後之字串 |
charAt(index) | 傳回指定索引之字元 |
setCharAt(index, c) | 將字串索引 index 之字元以指定字元 c 取代 |
equals(str) | 是否與指定字串 str 雷同, 傳回 true/false |
equalsIgnoreCase(str) | 是否與指定字串 str 相同 (不分大小寫), 傳回 true/false |
compareTo(str) | 與 str 字串逐字比較 ASCII 字元, 雷同傳回 0, 在後傳回正值, 在前傳回負值 |
startsWith(str) | 是否以指定字串 str 開頭, 傳回 true/false |
endsWith(str) | 是否以指定字串 str 結尾, 傳回 true/false |
trim() | 清除開頭與結尾的空白字元 |
toInt() | 將以數字開頭直到非數字字元之字串轉成長整數傳回 |
toFloat() | 將以數字開頭直到非浮點數字元之字串轉成浮點數傳回 |
reserve(bytes) | 要求保留指定 bytes 數之記 |
例如 :
void setup() {
Serial.begin(9600);
String str="Hello World!";
Serial.println(str.length()); //輸出 12
Serial.println(str.indexOf(" ")); //輸出 5 (有找到傳回索引)
Serial.println(str.indexOf(" ", 6)); //輸出 -1 (沒找到傳回 -1)
Serial.println(str.lastIndexOf("!")); //輸出 11
Serial.println(str.substring(6)); //輸出 World!
Serial.println(str.substring(0,7)); //輸出 Hello W
str.replace("World","Tony");
Serial.println(str); //輸出 Hello Tony!
str.concat(" Good Day!");
Serial.println(str); //輸出 Hello Tony! Good Day!
str.remove(16); //從 Day 前面空格開始刪
Serial.println(str); //輸出 Hello Tony! Good
str.remove(11,5); //從 G 前面空格開始刪
Serial.println(str); //輸出 Hello Tony!
str.toLowerCase();
Serial.println(str); //輸出 hello tony!
str.toUpperCase();
Serial.println(str); //輸出 HELLO TONY!
Serial.println(str.charAt(1)); //輸出 E
str.setCharAt(5,'+'); //將空格改為 +
Serial.println(str); //輸出 HELLO+TONY!
String str2="Hello+Tony!";
Serial.println(str.equals(str2)); //輸出 0
Serial.println(str.equalsIgnoreCase(str2)); //輸出 1
Serial.println(str.compareTo(str2)); //輸出 -32 (不同, str 在前)
Serial.println(str.compareTo("HELLO+TONY!")); //輸出 0 (雷同)
Serial.println(str.startsWith("HELLO")); //輸出 1
Serial.println(str.endsWith("TONY!")); //輸出 1
str=" HELLO ";
str.trim();
Serial.println(str.length()); //輸出 5 (已刪除前後空格)
str="180 Days";
Serial.println(str.toInt()); //輸出 180
str="65.245KG";
Serial.println(str.toFloat()); //輸出 65.25 (四捨五入到小數第二位)
}
void loop() {}
注意, 以上字串處理函式是 Arduino C 專用, 一般 C 語言沒有這些函式. 另外, 字串串接在 Arduino C 可以像 Python/Javacript 那樣用 + 直接串接, 在傳統 C 語言要用 strcat() 函式或指標, 不能用 +, 參考 :
# Arduino - 字串
流程控制與迴圈 :
這部分與 C 語言完全一樣, 值得注意的是 switch case 指令的 case 值只能用字元或整數. 其他資料型態如字串, 浮點數, 布林值均不允許. 例如 :
switch (val) {
case 1 :
//do something (for 1)
break;
case 2 :
//do something (for 2)
break;
case 3 :
case 4 :
//do something (for 3 or 4)
break;
default :
//do something
break;
}
每一個 case 若為獨立處理方式, 必須用 break 斷開, 否則會連續執行下一個 case (稱為 fall-through), 例如上面 case 3 與 case 4 都會執行 case=4 的程式碼. Case 值為字元範例如下 :
switch (val) {
case 'Y' :
//do something
break;
case 'N' :
//do something
break;
case '?' :
//do something
break;
default :
//do something
break;
}
在 "Beginning C for Arduino, 2nd edition" 這本書裡的第四章提到有範圍的 case, 使用刪節號 "..." 表示範圍, 例如 :
char grade;
int score;
switch (score) {
case 0...59 :
grade='F';
break;
case 60...69 :
grade='D';
break;
case 70...79 :
grade='C';
break;
case 80...89 :
grade='B';
break;
case 90...99 :
grade='A';
break;
default :
break;
}
指標 :
C 語言的指標在 Arduino C 也有提供, 在全華黃新華等著 "微電腦原理與應用" 的 3-5 節有稍微提到, 而 "Beginning C for Arduino, 2nd edition" 這本書的第八章則有詳細介紹. 指標專門用來儲存記憶體的位址以間接地存取變數的內容, 是四十多年來 C 語言能笑傲江湖的關鍵功能, 一般高階語言如 Java 並不提供, 少部分雖有指標功能, 但不像 C 的指標那麼自由, 可以對指標進行算術計算來操控記憶體, 參考 :
# Pointer (computer programming)
Arduino 所有的板子之 SRAM 都在 64KB 以下 (UNO/Nano/Pro mini 等板子所使用的 ATMega328 微控器只有 2KB), 因此 Arduino 的指標在 SRAM 記憶體中都是占 2 個 bytes 來儲存變數的位址, 16 bits 最高可定址 2^16=64K Bytes.
指標的宣告範例如下 :
int *ptrPrice;
指標名稱與一般變數命名規則相同, 但為了可讀性, 建議以 ptr 開頭. 其次, 指標宣告中要有一個星號, 通常放在指標名稱前面, 這是告訴編譯器, 這個名稱不是一般變數, 而是一個儲存位址用的指標. 星號不一定要緊貼指標名稱, 也可以這樣寫 :
int* ptrPrice;
int * ptrPrice;
而前面的資料類型 int 是指此指標所指位址之記憶體中儲存之資料型態. 取得一個變數的存放位址用 & 運算子, 而要存取指標的內容則用 * 運算子, 例如 :
void setup() {
Serial.begin(9600);
int price=100;
int *ptrPrice; //宣告一個指標變數 ptrPrice 儲存位址, 所指位址存放 int 數值
*ptrPrice=99;
Serial.print("The address of ptrPrice="); //顯示指標變數本身位址
Serial.println((long)&ptrPrice); //輸出 2294 (因板子而異)
Serial.print("The content of ptrPrice="); //顯示指標變數內容 (是個位址)
Serial.println((long)ptrPrice); //輸出 25344 (因板子而異)
Serial.print("The address of price="); //顯示一般變數之位址
Serial.println((long)&price); //輸出 2996 (因板子而異)
Serial.print("The content of price="); //顯示一般變數內容
Serial.println((int)price); //輸出 100
Serial.print("The content of *ptrPrice="); //顯示指標變數所指位址之內容
Serial.println((int)*ptrPrice); //輸出 99
}
void loop() {}
此範例中各變數之記憶體位址關係可用下列圖表示 :
可知指標變數 ptrPrice 存放在記憶體位址 2294, 其內容是一個位址 (整數值), 指向另一個記憶體位址 25344, 其內容為 99; 而一般變數 price 則存放在記憶體位址 2296, 其內容是個整數值 100.
參考 :
# Arduino 程式設計
# Arduino : 關於記憶體二三事
# Arduino 筆記 – 認識 Arduino
switch (val) {
case 1 :
//do something (for 1)
break;
case 2 :
//do something (for 2)
break;
case 3 :
case 4 :
//do something (for 3 or 4)
break;
case 'Y' :
//do something
break;
case 'N' :
//do something
break;
case '?' :
//do something
break;
switch (score) {
case 0...59 :
grade='F';
break;
case 60...69 :
grade='D';
break;
case 70...79 :
grade='C';
break;
case 80...89 :
grade='B';
break;
case 90...99 :
grade='A';
break;
default :
break;
}
C 語言的指標在 Arduino C 也有提供, 在全華黃新華等著 "微電腦原理與應用" 的 3-5 節有稍微提到, 而 "Beginning C for Arduino, 2nd edition" 這本書的第八章則有詳細介紹. 指標專門用來儲存記憶體的位址以間接地存取變數的內容, 是四十多年來 C 語言能笑傲江湖的關鍵功能, 一般高階語言如 Java 並不提供, 少部分雖有指標功能, 但不像 C 的指標那麼自由, 可以對指標進行算術計算來操控記憶體, 參考 :
# Pointer (computer programming)
Arduino 所有的板子之 SRAM 都在 64KB 以下 (UNO/Nano/Pro mini 等板子所使用的 ATMega328 微控器只有 2KB), 因此 Arduino 的指標在 SRAM 記憶體中都是占 2 個 bytes 來儲存變數的位址, 16 bits 最高可定址 2^16=64K Bytes.
指標的宣告範例如下 :
int *ptrPrice;
指標名稱與一般變數命名規則相同, 但為了可讀性, 建議以 ptr 開頭. 其次, 指標宣告中要有一個星號, 通常放在指標名稱前面, 這是告訴編譯器, 這個名稱不是一般變數, 而是一個儲存位址用的指標. 星號不一定要緊貼指標名稱, 也可以這樣寫 :
int* ptrPrice;
int * ptrPrice;
而前面的資料類型 int 是指此指標所指位址之記憶體中儲存之資料型態. 取得一個變數的存放位址用 & 運算子, 而要存取指標的內容則用 * 運算子, 例如 :
void setup() {
Serial.begin(9600);
int price=100;
int *ptrPrice; //宣告一個指標變數 ptrPrice 儲存位址, 所指位址存放 int 數值
*ptrPrice=99;
Serial.print("The address of ptrPrice="); //顯示指標變數本身位址
Serial.println((long)&ptrPrice); //輸出 2294 (因板子而異)
Serial.print("The content of ptrPrice="); //顯示指標變數內容 (是個位址)
Serial.println((long)ptrPrice); //輸出 25344 (因板子而異)
Serial.print("The address of price="); //顯示一般變數之位址
Serial.println((long)&price); //輸出 2996 (因板子而異)
Serial.print("The content of price="); //顯示一般變數內容
Serial.println((int)price); //輸出 100
Serial.print("The content of *ptrPrice="); //顯示指標變數所指位址之內容
Serial.println((int)*ptrPrice); //輸出 99
}
void loop() {}
此範例中各變數之記憶體位址關係可用下列圖表示 :
可知指標變數 ptrPrice 存放在記憶體位址 2294, 其內容是一個位址 (整數值), 指向另一個記憶體位址 25344, 其內容為 99; 而一般變數 price 則存放在記憶體位址 2296, 其內容是個整數值 100.
# Arduino 筆記 – 認識 Arduino
感謝版主,幫助甚大。
回覆刪除或者元素逐一賦值 :
回覆刪除int a[4];
a[0]=1;
a[1]=2;
a[2]=3;
a[3]=4;
如果直接在Arduino的IDE上這樣key的結果是"a does not name a type"
最後我是把程式分成在
int a[4];
void main{
a[0]=1;
a[1]=2;
a[2]=3;
a[3]=4;
}
才順利完成驗證,不知道問題原因?
有沒有其他更好的寫法?
感謝大大!
感謝,幫助我解決問題!
回覆刪除請問,Arduino ide 1.0.5-r2 輸入.寫入程式後,驗證時出現' 'does not name a type
回覆刪除因為沒有貼您的程式碼, 我猜您是每給 global variable 全域變數宣告資料型態才會這樣. 例如 :
回覆刪除a=1;
void setup() { }
void loop() { }
就會出現 'a' does not name a type 錯誤.
C 語言是靜態語言, 每一個變數都有其資料型態, 一定要宣告後才能使用, 只要改為 :
int a=1;
就可以了.
太感謝了,您的文章讓我受益良多。
回覆刪除你好請問
回覆刪除你知道哪裡可以有人為人解釋arduino code的嗎, 付費諮詢的也可以 ,
有一組手電同程式很想做來實驗 ,但看不懂程式 ,國外公開的網站的分享沒有版權問題
麻煩你 無限感激
聯絡:eddie5492001t@yahoo.com.tw
Dear Ed, 我不知道是否有解讀程式碼這種服務, 但如果方便程式又不會太長的話可以貼到這裡, 我如果有時間又看得懂的話可以略盡棉薄之力, 或許其他網友看到也會解析. 如程式太長, 可 mail : tony1966@ms5.hinet.net, 看看我能否讀懂.
回覆刪除真是感謝你,可以讓我完全實驗
回覆刪除我將code寄到你信箱了,它上面一行沒幾個字,可是很多行,要三張a4紙才放的下
回覆刪除Thank you
可以請教你個問題嗎?我是剛開始學arduino,我買了一塊arduino uno r3的板子,但是一直無法寫入程式,出現的錯誤訊息是這樣的avrdude: stk500_getsync() not in sync: resp=0x00
回覆刪除請問我要從哪查起,版型跟com都確認沒錯,謝謝
您好, 這可能是 IDE 程式沒有抓到 Arduino 板子, 要先到 工具/開發板 選取 Arduino/Genuino UNO 板與 COM 埠才行.
回覆刪除你好!我想請教您有關陣列的相關問題。我是用Genuino UNO板子。
回覆刪除int APIN0 = A0; //定義腳位
int APIN1 = A1;
int APIN2 = A2;
int APIN3 = A3;
int APIN4 = A4;
int a[5]; //儲存五個感測器的數值
a[0] = A0;
a[1] = A1;
a[2] = A2;
a[3] = A3;
a[4] = A4;
int i=(a[0]+a[1]+a[2]+a[3]+a[4])/5;
A0~A4為數位腳讀取的數值,請問可以這樣寫嗎?因為我需要算這5個腳作的平均值然後讓,但是一直失敗不知道是不是陣列不能這樣撰寫。謝謝
讀取類比輸入腳要呼叫 analogRead() 函數喔!
回覆刪除A[0]=analogRead(A0);
A[1]=analogRead(A1);
....
參考 :
http://yhhuang1966.blogspot.com/2015/10/arduino.html
好的我知道了!謝謝您的指導
回覆刪除請問用Lora1傳送三組電壓給另一台Lora2,
回覆刪除Lora2收到一組字串+RCV=20,20,458456433,我分別要458、456、433,然後再把458+456+433加起來做平均,要如何寫?
抱歉, 我 Lora 還沒時間學習, 您的問題應該是如何剖析收到的字串吧? 458456433 傳送時應該用符號隔開, 這樣收到後才能做字串處理.
回覆刪除好的,感謝,我再試試
刪除問一下~如果是我想用測距算出來的資料轉成用7段顯示器顯示~那該如何使用語法去讀取算出來的資訊
回覆刪除Hi, 您可以將類比輸入取得的整數, 例如 1023, 用 char() 強制轉型為字元後, 以索引或 charAt() 逐一取出, 再轉碼輸出到四顆 LED, 參考 :
回覆刪除https://atceiling.blogspot.com/2019/08/arduino517-led_12.html
1.arduino語法中輸出小數點後第二位的是?
回覆刪除2.arduino語法中, 可以輸出的是?
3.arduino語法中, 可以讀取資料的是?
4.arduino語法中, 可以寫入位元的是?
請問大大,該本文'定義常數: #define PI 3.14159'的內容,
回覆刪除我在其他程式上有看到將#define後面的置換值(常數值:3.14159)加上了一個括號'#define PI (3.14159)',
那加上括號會有改變原有表示嗎?還是不影響呢?
Hi, 前置指令 #define 用來定義一個常數或運算式的值, 如果是定義常數, 有無括號都可以, 例如 #define PI 3.14159 與 #define PI (3.14159) 是相同意思; 但如果拿來當運算式, 作用相當於一個簡單型函式, 這時有沒有括號就大不同了, 參考 : https://blog.csdn.net/linux12121/article/details/52602633
回覆刪除