arduino
密碼鎖 - 自訂一個密碼(ex:1234
),使用keypad來輸入值,輸入完成後按A
送出。若輸入值與密碼相同則印出Unlock!
,錯誤則印出Wrong passwords!
。
if (enter != NO_KEY) {
if (enter == 'A') {
break;
} else {
value[count++] = enter;
Serial.print(enter);
}
}
將輸入的字串存於字符陣列,但'A'
不儲存。
if (strcmp(value, password) == 0) {
Serial.println("Unlock!");
}
使用C-string字串處理的函式,與正確密碼的字符陣列去做比較。
承上題,增加2項功能 :
random()
得出的3位驗證碼,並在輸入錯誤時再隨機變換一組驗證碼直到輸入正確)。延續lab1程式
if (enter != NO_KEY) {
if (enter == 'B') {/*revise password*/}
else if (enter == 'A') {/*enter password*/}
分2個個模式 - 按B
為修改密碼,按A
為輸入密碼。
void input(char terminal_char, char value[]);
void input_3(char terminal_char, char arr[]);
2個subfunction中相同點為傳入之參數 :
不同點1為使用時機與功能 :
class
,要用到的時候就可直接呼叫成員函數show_seg.show(verification);
,詳見lab4實現方法第2點。不同點2為是否有把最後終止字符儲存至字串陣列 :
char
型態儲存輸入之數值,最後不會儲存終止字符。if (strcmp(input_value, password) == 0) {
Serial.println("Unlock!");
}
atoi()
的正確轉換。long random(min, max)
回傳值的資料型態。7-seg display
這個class
成員函數的接口void SHOW_SEG::show(int num);
。if (verification_code == atoi(input_value)) {
Serial.println("Correct!");
}
使用keypad輸入一個三位數字,實現終極密碼 \(\text{answer} \in [0, 999]\)。
while (true) { //4-digit number
enter = keypad.getKey();
if (enter != NO_KEY) { //input
guess_ch[count++] = enter;
if (isDigit(enter)) {
Serial.print(enter);
} else if (enter == 'A') {
break;
}
}
}
if
判斷檢查是否輸入數值,內建NO_KEY
為按鍵沒有被按下的值。if
判斷輸入的合法性(validity),限定為輸入的資料型態為數字。guess = atoi(guess_ch);
由於在前一程式最後按下A
輸出時,會把字符'A'
存進輸入數值的最後,在這邊起到辨識數值長度的功能,因為atoi()
遇到非數字或字符串結束('/0') 時會結束轉換,並將結果返回。
利用MPU-6050
做出計步器功能。(只使用三軸加速度(\(a_x, a_y, a_z\))之值來進行運算,代表只有判斷旋轉角度)
MPU6050
是一種非常流行的空間運動傳感器芯片,可以獲取器件當前的3個加速度分量和3個旋轉角速度。由於其體積小巧,功能強大,精度較高,不僅被廣泛應用於工業,同時也是航模愛好者的神器,被安裝在各類飛行器上馳騁藍天。
自帶了一個數據處理子模塊DMP,已經內置了濾波算法。數據接口用的是\(\rm I^2C\)(讀音 : I-squared-C)總線協議(原理)。
數據寫入和讀出均通過其IC內部的寄存器實現,這些寄存器的地址都是1個byte,也就是8位的尋址空間。
int sensitivity = 32767 / 250;
當\(G_X\)取正最大值\(32768\)時,當前角速度為順時針\(250\;\omega/s\),故可得靈敏度,即為角速度1秒內1弧度所代表的數值為
\[ \frac{32767}{250\;\omega/s} \tag{1.1} \]
\[\tan^{-1}\Bigg[\; \frac{A_x} { \sqrt{(A_y)^2 + (A_z)^2} }\;\Bigg] \times \frac{180}{\pi} \tag{2.1}\]
\[\tan^{-1}\Bigg[\;\frac{A_y} { \sqrt{(A_x)^2 + (A_z)^2} }\;\Bigg] \times \frac{180}{\pi} \tag{2.2}\]
\[\tan^{-1}\Bigg[\;\frac{ \sqrt{(A_x)^2 + (A_y)^2} }{A_z} \;\Bigg] \times \frac{180}{\pi} \tag{2.3}\]
50
筆資料內,每測量一次更新最小與最大值,並相減。承上題,加上七段顯示器顯示。
延續lab3程式
觀察題目要求增加步數之數列為: \[0 + 1 + 8 + 27 + ...... +\; n^3 = \sum_{k=0}^{n} k^{3} \tag{1.1}\]
為簡化程式碼,使用OOP
觀念,將4-digit 7-segment display
驅動封裝成一個class
,因此在主函數只需要增加2行 :
SHOW_SEG::SHOW_SEG(int first_pin, int last_pin)
創建類對象,並調用有參構造函數,第一至第二參數為七段顯示器連續接口,即 \(\text{pin} \in [2, 13]\)。
void SHOW_SEG::show(int num);
調用這個類的成員函數show()
,外部使用者只需要輸入一個整數,即可使七段顯示器亮燈,不需知道內部是如何實現,實現函數封裝。
請說明非同步傳輸中的RS-232
, RS-422
以及RS-485
,並討論這三者的差別為何?
Arduino藉由UART
作為資料傳輸介面,其線路僅有傳輸(TX)、接收(RX)兩個,以邏輯電路的形式傳輸資料,高電位時為1、零電位時為0。在Arduino要與PC溝通時,由於PC沒有UART
的傳輸介面,則需要以下傳輸介面作為轉換,其差異如下:
傳輸介面 | 說明 |
---|---|
RS-232 | 點對點通訊,傳送和接收端共用一個接地電位,並用一根訊號線上的電壓來判斷傳送的狀態爲0或1,容易引起干擾。 |
RS-422 | 以差動的方式傳輸資料,兩根接收、兩根傳輸,因此可以同時發送與接收信號,即:全雙工通信(full-duplex)。藉由差動的方式可以減少雜訊干擾,可用於遠距離傳輸。 |
RS-485 | 與RS-422 差動傳輸原理相同,差別在於只有兩根線同時作為傳輸、接收使用,因此無法同時發送與接收信號,故只能使用半雙工通信(half-duplex)。因為僅需兩條線就可傳輸,適用於佈線成本高但是資料傳輸量需求不高的應用中。 |
|
傳輸介面 | 邏輯0 | 邏輯1 |
---|---|---|
UART | 0 V |
3.3 or 5 V |
RS-232 | 3 ~15 V |
-3 ~-15 V |
RS-422 | -2 ~-6 V |
2 ~6 V |
RS-485 | -1.5 ~-6 V |
1.5 ~6 V |
UART
和RS-232C
之間要如何轉換?
透過一顆RS-232C
的傳送/接收器叫做MAX232
,內部設計2個非常巧妙的電路 - charge pump升壓和反向電路,儘管只有這顆IC只有5V
供電,還能輸入RS-232
的正負電壓與高於5V
之電壓。以下2個電路在MAX232
內部是由4個電容器和一系列的開關切換電路自動完成 :
原始 | 後來 |
---|---|
兩個電容器C1 和C2 ,分別用5V 電源將它們充飽 |
讓C1 和C2 脫離電源,並串聯起來 |
![]() |
![]() |
原始 | 後來 |
---|---|
一個電容器充電 | 將它的極性對調,讓原來電位比較高的那支腳與系統的0 V 電位相連,由於電容器裡面的電位差不會改變,所以原來低電位那隻腳,現在相對於0V 來說就是負電壓 |
![]() |
![]() |
MPU-6050
的接腳XDA
、XCL
、AD0
功能為何?
接腳 | 介紹 |
---|---|
VCC |
提供MPU-6050 系統電壓,3~5V皆可。 |
GND |
MPU-6050 系統接地。 |
SCL |
\(\rm I^2C\)通訊協定所需的時脈週期。 |
SDA |
藉由\(\rm I^2C\)通訊協定傳輸資料。 |
XDA |
以MPU-6050 為master、額外模組為slave,傳輸資料給額外模組。 |
XCL |
以MPU-6050 為master、額外模組為slave,提供時脈週期給額外模組。 |
AD0 |
當有兩個MPU-6050 連接在同一個master(arduino board)上時,須分別給兩個slave(MPU-6050 )不同通訊位址。 LOW ->0x68 、HIGH ->0x69 ,可自行選擇其通訊位置。 |
INT |
中斷MPU-6050 計算程序,使arduino可讀取其資料,以避免MPU-6050 資料溢位。 |
請問取random
數字時不使用randomSeed()
的差別在哪?
random(max), random(min, max)
randomSeed(unsigned long seed)
randomseed()
時,其seed的來源為未連接的pin腳analogRead(0)
所讀取到的值,由於該腳位為連線,因此會受其他因素(其他模擬輸入的值、手與電路板的距離等)影響。此觀念與
C/C++
相同。
下方為不呼叫MPU-6050 library
來讀出MPU-6050
數值之程式,
請為其標上完整的註解。
#include<Wire.h>
MPU6050
使用\(\rm I^2C\)(讀音 : I-squared-C)總線協議(原理),因此我們需要頭文件<Wire.h>
來實現Arduino與MPU6050
之間的傳輸。const int MPU_addr=0x68;
//在程式執行開始時會執行一次,用於初始化設定
void setup(){
Wire.begin();
//初始化Wire.h並作為master or slave加入I2C bus
//如未指定7-bit slave地址(函數重載),則為加入者為master
Wire.beginTransmission(MPU_addr); //開啟MPU6050的傳輸
Wire.write(0x6B); //指定寄存器地址
Wire.write(0); //寫入一個字節的數據
Wire.endTransmission(true); //結束傳輸,true表示釋放總線
Wire
的傳輸模式,並指定器件的總線地址,MPU6050
的總線地址是0x68
(AD0
引腳為高電平時地址為0x69
),地址可於IC的datasheet中查閱或自己寫i2c_scanner檢測。然後寫入一個字節的寄存器起始地址,再寫入任意長度的數據。Wire
的傳輸模式。示例是向MPU6050
的0x6B
寄存器寫入一個byte0
。 Serial.begin(38400); //設定傳輸時的baud(每秒有幾個bit)
}
//重複執行在loop()函式中的程式碼,直到Arduino硬體關閉
void loop(){
Wire.beginTransmission(MPU_addr); //開啟MPU6050的傳輸
Wire.write(0x3B); //指定寄存器地址
Wire.endTransmission(false); //開啟傳輸模式
//If false, endTransmission() sends a
//restart message after transmission.
//The bus will not be released,
//which prevents another master device
//from transmitting between messages.
Wire.requestFrom(MPU_addr,14,true); //從MPU要求14byte資料
AcX=Wire.read()<<8|Wire.read(); //兩個字節組成一個16位整數
AcY=Wire.read()<<8|Wire.read();
AcZ=Wire.read()<<8|Wire.read();
//Tmp=Wire.read()<<8|Wire.read(); //題目少一行,與溫度有關之數值
GyX=Wire.read()<<8|Wire.read();
GyY=Wire.read()<<8|Wire.read();
GyZ=Wire.read()<<8|Wire.read();
Wire
的傳輸模式,然後寫一個字節的寄存器起始地址。接下來將指定地址的數據讀到Wire.h
的緩存中,並關閉傳輸模式,最後從緩存中讀取數據。MPU6050
的0x3B
寄存器開始讀取14
個byte的數據。由於Wire.read()
回傳值為下一個接收到byte,總共14
個Wire.read()
,因此master才會要跟slave要14
個byte。依序兩兩一組,先讀到的A向左位移8
bit,後讀到的B與A做Bitwise OR
因此等同於2個8
個bit整數concatenate成一個16
個bit整數。 Serial.print("AcX = "); Serial.print(AcX); //傳輸並顯示在serial monitor
Serial.print(" | AcY = "); Serial.print(AcY);
Serial.print(" | AcZ = "); Serial.print(AcZ);
Serial.print(" | GyX = "); Serial.print(GyX);
Serial.print(" | GyY = "); Serial.print(GyY);
Serial.print(" | GyZ = "); Serial.println(GyZ);
delay(333); //延遲333ms
}
印出數據
將結果透過TX腳位並藉由之前所設定之baud的傳輸速率,傳到電腦並顯示在serial monitor上。
參考資料
這次實驗還是沒能在時間內完成,在lab2-1的地方,原先我們使用switch
語法,增加兩個新功能,但在燒入Arduino時,始終無法操作keypad,搜尋了許多資料,最後只好改成if-else
語法才終於通過,其問題原因不明。
而在取MPU-6050
之數值時,我認為取角度來作計步器的條件不太好,計步器應該是要接受到震動再作用,所以若用加速度可能比較符合實際計步器。
此次實驗已經較為熟悉Arduino的操作方式以及接線方法,但是因為程式端的問題,所以我們組別還是超過上課時間後才把demo做完。這次主要卡在lab2-1新密碼字串處理的問題以及switch
裡的default
不知為何無法被Arduino IDE正確讀取到,因為被switch
語法折騰了很久,所以我們後來乾脆改用if-else
語法,才終於解決這部分的問題。
而處理字串的問題礙於時間關係因此沒有完善的處理好,本來的想法是利用string.length()
將每次輸入的新密碼長度記住後再將buffer清空,並重複每一次新密碼的輸入,但是不知為何會一直讀取到空字元,希望下次再次遇到類似問題時能順利解決。
lab1、lab2-1、lab2-2我傾向於不使用string
與password
這兩個class的調用,而使用傳統的C-style string
進行字串處理,以方便操作底層。
而lab3、lab4使用MPU-6050
做出計步器功能,這部分重點在於取50點數據中最小與最大值,並相減與臨界值進行比對。我認為題目限定判斷條件只用旋轉角度,而不使用加速度,不是一個好的設計,因為平常在跑步時,除非計步器沒有固定好在腰上,不然計步器是隨人的身體上下移動,而比較少會有大幅度旋轉的運動軌跡。
另外,以下記錄我遇到的問題與解決方法 :
項目 | 說明 |
---|---|
問題 | Arduino第一次Serial.prtint 常常出現亂碼、錯誤縮排。 |
推測原因(有待商榷) | 用stdin的觀念思考,緩衝區一開始未清空字符,印出不預期之空格。 |
解決方法 | 在設定鮑率Serial.begin() 前面加上Serial.println(); 先自行flush掉緩衝區內的字元。 |