--- tags: 單晶片 --- # [2] Arduino講義: 環境與跑馬燈+按鍵+多工七段顯示器+鍵盤 ## Install the Arduino * 至Arduino官方網站下載開發環境: (https://www.arduino.cc/en/Main/Software) * 開起「裝置管理員」,將 Arduino 接上,看一下是否有新增 USB 序列裝置(COM6),**不一定為 COM6**。 ![](https://i.imgur.com/7n0TLcm.png) * **若「USB序列裝置」出現無法辨識的裝置** **方法一 : 按滑鼠右鍵,選擇「更新驅動程式」,並點選「自動搜尋更新的驅動程式軟體」,可以排除問題。** **方法二 : 按滑鼠右鍵,選擇「更新驅動程式」,並點選「瀏覽電腦上的驅動程式軟體」,瀏覽下載Arduino的地方(arduino-1.8.8),找到drivers檔,點選並安裝。**![](https://i.imgur.com/SSvHWRm.png) **方法三 : 搜尋COM port安裝檔,下載後執行 https://sparks.gogo.co.nz/ch340.html** ![](https://i.imgur.com/xDlQzCs.jpg) * 開啟Arduino IDE ![](https://i.imgur.com/Dwc2efw.png) * 按下工具→序列埠,同時將arduino接上,選擇Uno,即可以使用。 ![](https://i.imgur.com/BPaazpM.png) * 在「開發板」的部分記得確認是**Arduino/Genuino Uno**。 ![](https://i.imgur.com/Z2JcGLV.png) ## Arduino Common Function **1.setup()** 設定程式參數,如:指定接腳的輸入輸出狀態,setup()裡的程式碼只會執行一次。 **2.loop()** 在loop()函式中的程式碼將會不停地重複執行,直到電源關閉為止。 ![](https://i.imgur.com/bJVIF0x.png) --- > ## Introduction of GPIO (General Purpose Input Output) ![](https://i.imgur.com/ukpB4Ih.png) >通用輸入/輸出,每個 GPIO 端口可透過軟件分別配置成 INPUT 或者 OUTPUT ,其接腳可以供使用者由程式控制自由使用。 >* **Input mode:** MCU 透過 GPIO 腳從外面收資料/信號,可以進行外接感測器(溫度、紅外線感測器等)狀態、高低電位等資訊的獲取。 >* **Output mode:** MCU 透過 GPIO 腳將資料/信號送出,可用於控制LED、蜂鳴器及伺服馬達…等。 >* GPIO 端口可以當 I2C(**LCD 16x2**)、SPI (10~13)、UART(**Bluetooth**)…等多種用途,也能作為 IRQ (中斷要求)(2、3)的信號。 --- **3.pinMode(pin,mode)** 用於設定接腳模式,該函數無返回值,pin代表所要配置的接腳,mode代表設置的模式-INPUT或OUTPUT。 Ex: ```C++= pinMode(13,OUTPUT)//將第13腳設定成「輸出」模式 pinMode(12,INPUT)//將第12腳設定成「輸入」模式 ``` **4.digitalWrite(pin,value)** 函數有兩個參數 digitalWrite(pin 接腳,電壓值), 電壓值為HIGH(高電位)與LOW(低電位)。 Ex: ```C++= digitalWrite(13,HIGH)//第13腳位為輸出「高電位」 ``` **5.digitalRead(pin)** 在接腳為輸入的情況下,可以獲取接腳的電壓情況—HIGH或LOW。 **6.delay(ms)** 設定程式運行停止時間長度,單位為ms,故delay(1000)為延遲1秒。 **7.millis()** millis()函數會回傳Arduino從執行程式碼開始至目前為止的milliseconds數值,其資料型態為unsigned long,該數值大約在50天之後會溢位(overflow),屆時會從0開始重新計數。 ![](https://i.imgur.com/Iq2nZV6.png) 以上方程式碼為例,每一秒印出millis()的回傳值,利用delay(1000)產生1000毫秒的延遲。 然而,相鄰的兩個數值的差值可能並未完全為1000,則是Serial.print()印出資料需要的時間差。 ## **Serial Library** ![](https://i.imgur.com/uP3t3at.png) **1. Serial.begin()** 啟動Serial Port並設定鮑率(baud rate) Ex: ``` C++= Serial.begin(9600);//開啟Serial Port,且通訊速率為9600bps(Bits Per Second) ``` **2. Serial.print()** 將字串透過TX腳位傳遞出去,並且顯示在Serial Monitor中 **3. Serial.println()** 傳送資料到外部電腦並切換至下一行 **4. Serial.write()** 以二進位碼(byte)輸出 ![](https://i.imgur.com/iIpMaef.png) **5. Serial.read()** 讀取進來的第一個位元組 (first incoming byte)。 **6. Serial.available()** 取得 Serial Port 可讀取的資料位元組數目 (number of bytes),如果 Serial port 有資料進來,Serial.available() 會回傳大於 0 的數值。 --- 通常我們會使用Serial.available()來檢查是否有資料進來,在使用Serial.read()把資料讀取出來放到變數後供後續使用。例如: ```c++= int incomingByte = 0; // 用來儲存收進來的 data byte void setup() { Serial.begin(9600); Serial.println("Hello"); } void loop() { // 檢查是否有資料可供讀取 if (Serial.available() > 0) { // 讀取進來的 byte incomingByte = Serial.read(); // 印出收到的資料 Serial.print("data received: "); Serial.print(incomingByte, DEC); Serial.print(", "); Serial.print(incomingByte, HEX); Serial.println(" (HEX)"); } ``` --- **<<Note>> Arduino其他相關語法請參考:[Arduino語言手冊](https://h2maker.wordpress.com/arduino/)** --- ## **Arduino講義:跑馬燈 + 按鍵 + 多工七段顯示器 + 4 x 4鍵盤** ## **1.跑馬燈(Electronic Marquee)** ### 模組說明 ![](https://i.imgur.com/cxsQfH9.png) **<<Note>>** **<font color = "red">需要限流電阻(大概 220Ω 或 330Ω),使 LED 流進電流不超過20mA。</font>** ## Arduino LED Blink ![](https://i.imgur.com/Rva87AE.png) > ### 程式說明 ```C++ const int LedPin = 8; void setup() { pinMode(LedPin, OUTPUT); //將第8腳位設定成OUTPUT模式 } void loop() { digitalWrite(LedPin, HIGH); //第8腳位輸出高電位,點亮LED delay(500); //等待500ms digitalWrite(LedPin, LOW); //第8腳位輸出低電位,熄滅LED delay(500); //等待500ms } ``` --- ## LAB 1-1 * 實驗目的:學習對Arduino的GPIO及LED基本控制方式。 * 實驗目標:設定5個GPIO來實現跑馬燈,模式如範例影片所示 * 參考程式碼(可任意新增改寫) ```c++= int Led[5] = {8, 9, 10, 11, 12}; void setup() { for(int i=0; i<5; i++) pinMode(Led[i], OUTPUT); } void loop() { /* Do something */ } ``` * 範例影片 {%youtube Zk_hLzrvVO4 %} --- ## LAB 1-2 (Bonus) * 實驗目的:學習對Arduino的GPIO及LED基本控制方式。 * 實驗目標:設定5個GPIO來實現跑馬燈,模式如範例影片所示 * 參考程式碼(可任意新增改寫) ```c++= #define ROW 30 #define COLUMN 5 int Led[COLUMN] = {8, 9, 10, 11, 12}; int pos[ROW][COLUMN] = { /* 模式 */ }; void setup() { for(int i=0; i<COLUMN; i++) pinMode(Led[i], OUTPUT); } void loop() { /* Do smoething */ } ``` * 範例影片 {%youtube Oh31ciRABMk %} --- ## 2.按鈕(Button) ![](https://i.imgur.com/jzfqQzY.png =50%x) ### **開關接法** ![](https://i.imgur.com/dUD0QDi.png) 若未按下開關,Arduino的接腳沒接地,但也沒有接到高電位。輸入的訊號會在0到1之間飄移,造成浮動訊號,Arduino就無法正確判斷輸入值。 ![](https://i.imgur.com/oh3j52w.png) 若開關沒有被按下,Pin2將會透過10kΩ接地,讀取到低電位(LOW);按下開關時,5V電源將流入Pin2,產生高電位(HIGH)。 ![](https://i.imgur.com/RHULvQZ.png) 若將電阻接到電源,則此電阻稱為「**上拉(pull-up)電阻**」(在開關接通時,輸入為「低電位」)。 > ### 使用Arduino內部的上拉電阻 Arduino內部的pin腳考量到拿來做數位輸入,有內建上拉電阻(除了D13腳位),可取代外接上拉電阻。 ```C++= pinMode(pin,INPUT_PULLUP); ``` ## LAB 2-1 Button & LED * 實驗目的:知道上拉電阻與下拉電阻的接法差別,並用Button控制LED * 實驗目標:按上拉電阻的Button時,LED向左一顆亮;按下拉電阻的Button時,LED向右邊一顆亮。 * 參考程式碼(可任意新增改寫) ```c++= const int Led[5] = {8, 9, 10, 11, 12}; //LED const int sw_up = 2; //按鈕1 const int sw_down = 3; //按鈕2 int pos = 2; //初始亮LED位置 void setup() { for(int i=0; i<5; i++){ pinMode(Led[i], OUTPUT); digitalWrite(Led[i], LOW); } digitalWrite(Led[2], HIGH); pinMode(sw_up, INPUT_PULLUP); //內建上拉電阻 pinMode(sw_down,INPUT); } void loop() { bool swstate_up = digitalRead(sw_up); bool swstate_down = digitalRead(sw_down); if(swstate_up==LOW){ /* Do something */ } else if(swstate_down==HIGH){ /* Do something */ } } ``` * 範例影片 {%youtube CJ8R-SKxiNw %} --- ### 彈跳問題 機械式開關在切換的過程中,訊號並非立即從1變成0或從0變成1,而是會經過如下圖一般有忽高忽低的情況。 雖然彈跳的時間很短暫,但arduino仍會讀取到其連續變化的開關訊號,而導致誤差。 ![](https://i.imgur.com/wTXp74A.png) > ### 消除彈跳(de-bouncing)方法 在發現訊號開始變化時先讀取,在經過一段時間後,若訊號仍為HIGH,則視為按下第二次按鈕。 ## LAB 2-2 de-bouncing * 實驗目的:解決Button彈跳問題 * 實驗目標:無論長按或短按,按一下按鈕改變LED亮暗,且沒有不規則閃爍的問題;另外一顆LED燈,保持以每秒亮按的頻率持續閃爍,不受按壓按紐影響 ==Hint:不使用delay()函式== * 參考程式碼(可任意新增改寫) ```c++= const int button = 2; const int led = 13; //UNO內建LED const int tick_led = 8; //每秒亮暗的led bool led_state = HIGH; bool tick_led_state = HIGH; void setup() { pinMode(button, INPUT_PULLUP); //上拉 pinMode(led, OUTPUT); digitalWrite(led, led_state); pinMode(tick_led, OUTPUT); digitalWrite(tick_led, tick_led_state); } void loop() { if(digitalRead(button)==HIGH){ //沒按按鈕 /* Do something */ } else if(digitalRead(button)!=HIGH){ //按按鈕 /* Do something */ } if(/* What happen */){ //每秒閃爍LED /* Do something */ } } ``` * 範例影片: {%youtube BZmZqPAOyYQ %} --- # Arduino講義:多工七段顯示器與掃描鍵盤 ## 3. 多工七段顯示器(Multiplexed Seven-Segment display) > ### 七段顯示器 ![](https://i.imgur.com/CL8AjzP.png) ![](https://i.imgur.com/jCHvCki.png) **<<Note>>** **注意!!需額外增加限流電阻,否則內部可能會燒毀!!** ![](https://i.imgur.com/bg7CQ4b.png) 依據電源連接方式的不同,七段顯示器可以分成「**共陽極**」與「**共陰極**」兩種: 共陰極代表所有的LED接地端都相連,故LED的另一端接「高電位」就會發光;相反地,共陽極則是輸入「低電位」來發光。 | 十進位數 | a | b |c|d|e|f|g| | - | - | - | - | - | - | - | - | |0|1|1|1|1|1|1|0| |1|0|1|1|0|0|0|0| |2|1|1|0|1|1|0|1| |3|1|1|1|1|0|0|1| |4|0|1|1|0|0|1|1| |5|1|0|1|1|0|1|1| |6|1|0|1|1|1|1|1| |7|1|1|1|0|0|0|0| |8|1|1|1|1|1|1|1| |9|1|1|1|1|0|1|1| > ### 多工七段顯示器 ![](https://i.imgur.com/k4HT6lO.png) ![](https://i.imgur.com/NTsPOqR.png =50%x)![](https://i.imgur.com/d4zoQH0.png =50%x) * 實際接腳位置: | 多工七段顯示器 | Arduino | | :-----: | :-: | | 0 |Pin10| | 1 |Pin11| | 2 |Pin12| | 3 |Pin13| | a |Pin2 | | b |Pin3 | | c |Pin4 | | d |Pin5 | | e |Pin6 | | f |Pin7 | | g |Pin8 | | dp |Pin9 | ## LAB 3-1 多工七段顯示器 * 實驗目的:學習對Arduino的GPIO與多工七段顯示器掃描基本控制方式 * 實驗目標:控制多工七段顯示器顯示"**2021**" * 參考程式碼(可任意新增改寫) ```c++= const int SEG_COM[4] = {10, 11, 12, 13}; //控制線 const int SEG_data[10][8] = {{1, 1, 1, 1, 1, 1, 0, 0}, //"0" //資料線 {0, 1, 1, 0, 0, 0, 0, 0}, //"1" {1, 1, 0, 1, 1, 0, 1, 0}, //"2" {1, 1, 1, 1, 0, 0, 1, 0}, //"3" {0, 1, 1, 0, 0, 1, 1, 0}, //"4" {1, 0, 1, 1, 0, 1, 1, 0}, //"5" {1, 0, 1, 1, 1, 1, 1, 0}, //"6" {1, 1, 1, 0, 0, 0, 0, 0}, //"7" {1, 1, 1, 1, 1, 1, 1, 0}, //"8" {1, 1, 1, 0, 0, 1, 1, 0}}; //"9" int display[4] = {2, 0, 2, 1}; //欲顯示數字 void setup() { for(int i=2; i<=13; i++){ pinMode(i, OUTPUT); digitalWrite(i, HIGH); //共陽極 } } void loop() { for(int i=0; i<=3; i++){ //個、十、百、千位數 digitalWrite(SEG_COM[i], LOW); //觸發第i位數顯示數字 SEG_Drive(disp[i]); //資料線寫入數值 delay(5); digitalWrite(SEG_COM[i], HIGH); //結束觸發第i位數 } } void SEG_Drive(char number){ //將字元變數從SEG_data[][]找到相對應的位置,並寫入a~g中 /* Do something */ } ``` * 範例影片: {%youtube -nnb3ZGt4ng %} --- ## LAB 3-2 多工七段顯示器數數 * 實驗目的:學習對Arduino的GPIO與多工七段顯示器掃描基本控制方式 * 實驗目標:控制多工七段顯示器 每隔一秒計數加一 * 參考程式碼(可任意新增改寫) ```C++ const int SEG_COM[4] = {10, 11, 12, 13}; //控制線 const int SEG_data[10][8] = {{1, 1, 1, 1, 1, 1, 0, 0}, //"0" //資料線 {0, 1, 1, 0, 0, 0, 0, 0}, //"1" {1, 1, 0, 1, 1, 0, 1, 0}, //"2" {1, 1, 1, 1, 0, 0, 1, 0}, //"3" {0, 1, 1, 0, 0, 1, 1, 0}, //"4" {1, 0, 1, 1, 0, 1, 1, 0}, //"5" {1, 0, 1, 1, 1, 1, 1, 0}, //"6" {1, 1, 1, 0, 0, 0, 0, 0}, //"7" {1, 1, 1, 1, 1, 1, 1, 0}, //"8" {1, 1, 1, 0, 0, 1, 1, 0}}; //"9" int disp[4]; //顯示數字 int number = 0; int timer = 0; void setup() { for(int i=2; i<=13; i++){ pinMode(i, OUTPUT); digitalWrite(i, HIGH); //共陽極 } } void loop() { number_transfer(number); //數值轉換入陣列中 /* Do something(改寫Lab3-1) */ } void number_transfer(int Num){ //四位數拆成四個獨立的數字,存入陣列disp[]中 /* Do something */ } void SEG_Drive(char number){ //將字元變數從SEG_data[][]找到相對應的位置,並寫入a~g中 /* Do something */ } ``` * 範例影片: {%youtube gwezpV3jCTA %} --- ## 4. 4x4鍵盤(Keypad) ![](https://i.imgur.com/9LfIBf6.png) > ### 按鈕偵測與掃描原理(1x3簡化版) --- ![](https://i.imgur.com/1oR4N7l.png) 假設開關的「行1」~「行3」輸入端全部都輸出高電位,則無論開關是否被按下,Arduino都將會接收到**高電位**。因此,為了偵測到使用者按下的開關鍵,撰寫程式時必須依序將「行1」~「行3」設置成**低電位**來檢測。 * 輪到「行1」腳被掃描時,「行1」腳輸入低電位,但A開關沒有被按下,所以Arduino的微控腳接收到高電位(1) ![](https://i.imgur.com/n9dBBz0.png) * 在掃描到「行2」時,「行2」腳輸入低電位,且Arduino的微控腳也接收到低電位(0),因此可以判斷出「行2」的B開關被按下。 ![](https://i.imgur.com/lTn2icb.png) * 輪到「行3」腳被掃描時,「行3」腳輸入低電位,但C開關沒有被按下,所以Arduino的微控腳接收到高電位(1) ![](https://i.imgur.com/crZsTYq.png) --- 實際上的使用需要運用到**雙重迴圈(4x4)**,才能夠分批掃描到每一列: ![](https://i.imgur.com/8YSHIj2.png) > ### 實際腳位配置 | 4x4 Keypad | Arduino | | :--------: | :-------: | | 1(ROW1) | Pin A0 | | 2(ROW2) | Pin A1 | | 3(ROW3) | Pin A2 | | 4(ROW4) | Pin A3 | | 5(COL1) | Pin 2 | | 6(COL2) | Pin 3 | | 7(COL3) | Pin 4 | | 8(COL4) | Pin 5 | ## LAB 4-1 掃描式鍵盤 * 實作目的:學習如何使用掃描式鍵盤 * 實作目標:<font color = "red">**在不使用<Keypad.h>的情況**</font>,以Keypad鍵入資料,將資料透過UART傳輸至內建的Serial Monitor * 參考程式碼(可自行改寫) ```C++ const byte colPins[4] = {2, 3, 4, 5}; //行腳位 const byte rowPins[4] = {A0, A1, A2, A3}; //列腳位 const char keymap[4][4] = {{'1', '2', '3', 'A'}, //Keypad對應符號 {'4', '5', '6' ,'B'}, {'7', '8', '9', 'C'}, {'*', '0', '#', 'D'}}; void setup() { Serial.begin(115200); for(int i=0; i<=3; i++){ pinMode(rowPins[i], INPUT_PULLUP); pinMode(colPins[i], OUTPUT); digitalWrite(colPins[i], HIGH); } } void loop() { /* Do something */ } ``` * 範例影片: {%youtube T9IXItpW41M %} --- > ### 匯入Keypad函式庫 Keypad程式庫的運作方式與LAB 4-1 大致相同,且多了除彈跳(de-bouncing)的部分。LAB 4-1有助於了解掃瞄鍵盤的原理,實際使用上仍是Keypad程式庫為主。 [**Keypad Library下載**](https://www.arduinolibraries.info/libraries/keypad) ![](https://i.imgur.com/aIS1wcX.png) ![](https://i.imgur.com/z7ufiOA.png) 使用Keypad函式庫,程式碼需定義按鍵模組的**行(col)**、**列(row)**、**連接Arduino的腳位**以及**按鍵所代表的字元**。 ![](https://i.imgur.com/MrWNGmx.png) ```c++= #include <Keypad.h> //引用Keypad函式庫 #define KEY_ROWS 4 //按鍵模組的列數 #define KEY_COLS 4 //按鍵模組的行數 const byte colPins[4] = {2, 3, 4, 5}; const byte rowPins[4] = {A0, A1, A2, A3}; const char keymap[KEY_ROWS][KEY_COLS] = { {'1','2','3','A'}, {'4','5','6','B'}, {'7','8','9','C'}, {'*','0','#','D'} }; Keypad myKeypad = Keypad(makeKeymap(keymap),rowPins,colPins,KEY_ROWS,KEY_COLS); void setup(){ Serial.begin(115200); } void loop(){ //透過Keypad函式庫裡的getkey()方法讀取按鍵的字元 char key = myKeypad.getKey(); if(key){ //若有按鍵按下則顯示按下的字元 Serial.println(key); } } ``` ## LAB 4-2 Keypad 輸出字串 * 實驗目的:學習如何使用掃描式鍵盤及其應用 * 實驗目標:利用Keypad輸入按鍵,並且將值暫存在buffer中,按下 '#' 後會將之前輸入的字元依序Print到Serial Monitor上。 * 範例影片: {%youtube sJYadVO4Eus %} ## LAB 4-3 碼錶 (Bonus) * 實驗目的:學習如何使用掃描式鍵盤與七段顯示器之結合應用 * 實驗目標:利用Keypad輸入按鍵,按 'A' 會進入計時模式,按 'B' 則會進入倒數計時模式,按 'C' 為開始,按 'D' 為暫停,按 '*' 為重新開始,按 '#' 為離開並進入選擇模式。(註:進入倒數計時模式時須先輸入欲倒數秒數) * 參考pin腳 | 多工七段顯示器 | Arduino | 4x4 Keypad|Arduino| | :-------: | :-----: |:------:|:------:| | 0 |Pin10|Row1|A0| | 1 |Pin11|Row2|A1| | 2 |Pin12|Row3|A2| | a |Pin2 |Row4|A3| | b |Pin3 |Col1|Pin13| | c |Pin4 |Col2|Pin9| | d |Pin5 |Col3|A4| | e |Pin6 |Col4|A5| | f |Pin7 | | g |Pin8 | 註 : 要使用 Pin 0 & 1 也可以,但不能使用Serial.print()等函式 * 實驗結果: {%youtube Eu8ZaBuxnWg %} :::info ## 課後習題 1. Arduino微處理器不僅包含MCU,還內建記憶體、類比/數位訊號轉換器以及周邊控制介面,相當於將完整的電腦功能塞入一個矽晶片中,所採用的微處理器是ATmega328P。根據下圖,標示在Arduino UNO板上的digital 與 analog I/O的位置、數量以及相關用途(如:通訊、中斷...)。 ![](https://i.imgur.com/mRo7MTS.png) 2. (1)描述機械式開關的彈跳現象 (2)在ATmeg328p微控器的數位接腳有內建上拉電阻,預設沒有啟用,該上拉電阻值介於20kΩ至50kΩ之間;而一般外接10kΩ的上拉電阻,甚至在要求高反應速率的電子訊號切換時會使用4.7kΩ或是1kΩ的上拉電阻。試問:上拉電阻值的高低對於處理雜訊干擾的能力之間的關係? 3. 描述多工七段顯示器及鍵盤掃描的工作原理 4. 心得 ::: :::success ## 作業繳交格式 **W2結報_第X組.zip** 壓縮檔裡包含: 1.W2結報.pdf 2.資料夾:W2 Lab1-1.ino Lab1-2.ino ... :::