微界面處理機實驗課程 === --- ## 課程大綱 * 實驗注意事項 * 8051 實驗: * 8051 介紹 * 編譯器及燒錄器使用說明 * 實驗 1. **GPIO 、Stepper motor** 2. **External、Timer Interrupt** 3. **Serial Transmit** 4. **LCD** 5. **ADC** * [侯老師上課講義](https://drive.google.com/drive/folders/1zhK2M2M1gDWQUY31ngVHN0YxNyyXuclq?usp=sharing) * References [MCS@51 MICROCONTROLLER FAMILY USER’S MANUAL](http://eia.udg.es/~gomis/all8051.pdf) * Arduino 實驗: * Arduino介紹 * 實驗 1. **Analog & Digital** 2. **超聲波偵測距離(HC-SR04)** 3. **藍牙傳輸** 4. **GLCD** * **附錄 (Arduino常用函式整理)** * Raspberry pi / Nano pi 實驗: * Nano pi 介紹 * 實驗 1. **基礎認識及使用** 2. **GPIO 及七段顯示** 3. **TCP/IP Socket 通訊** * 附錄 * 資料來源 --- ## 實驗注意事項: :::info 1. 發放零件後,請當面點清,離開後恕不補發零件。 2. 拿到零件後,請在 IC 及板子上貼上標籤,寫上姓名及學號。違者檢查實驗結果時不予承認。 3. 若將燒錄器燒壞,請至網路計算實驗室重新做一塊板子歸還。 4. 使用燒錄器,請注意排線的方向以免將 IC 燒毀。 5. 若有需要插拔電源時,請拿插頭,請勿拉扯電線,損壞器材者負責修好。 6. 若將零件燒壞,請自行至電子材料行購買同型號之零件補之。(電阻電容除外) 7. 在插麵包板時,電源線和地線已使用之顏色,其他電線勿再使用該顏色。 8. 插麵包板時,電線請貼平麵包板。 9. 若發現燒錄出來的結果不如預期,請示著先確認以下幾點是否正確: a. 程式的 Port number 和麵包板上的 Port 是否有相對應。 b. VCC(pin 40) 接 5v ,GND(pin 20) 接地,EA/VPP(pin 31) 接 5v(使用內部程式記憶體)。 c. 檔案長度是否超過晶片所能容納的大小。 * AT89S51:4K * AT89S52:8K ::: ## 8051 介紹 * The 8051 is the original member of the MCW-51 family, and is the core for allMCS-51 devices. The features of the 8051 core are - * 8-bit CPU optimized for control applications * Extensive Boolean processing (Single-blt logic) capabtilties * 64K Program Memory address space * 64K Data Memory address space * 4K bytes of on-chip Program Memory * 128 bytesof on-chip Data RAM * 32 bidirectional and individually addressable 1/0 lines * Two 16-bit timer/counters * Full duplex UART * 6-source/5-vector interrupt structure with two priority levels * On-chip clock oscillator #### Block Diagram of the 8051 Core ![](https://i.imgur.com/RIKAWFe.jpg) #### Pin configuration of AT89S51 ![](https://i.imgur.com/l3lZNj6.gif) --- ## 編譯器及燒錄器使用說明 ### 程式編譯: * **[sdcc 下載](https://drive.google.com/open?id=1zhK2M2M1gDWQUY31ngVHN0YxNyyXuclq)** 1. 將 sdcc 解壓縮後的資料夾儲存於 `c:\`,並將燒錄程式 WLPROV2.20 安裝於電腦中 2. 用 [NotePad](https://notepad-plus-plus.org/zh/) 或 WordPad 將程式完成後,儲存成 .c 檔,並將程式儲存於 **`c:\sdcc\exam`** 底下。 3. 開啟命令提示字元(cmd),輸入 `cd c:\sdcc\bin`。 ![](https://i.imgur.com/XVlJwc2.jpg) 4. 輸入 `sdc51 yourcode.c`,此時會用 sdc51.exe 來編譯程式,若出現錯誤訊息,再回去修改程式,若沒問題,將產生 `.hex` 的檔案。 失敗畫面如下,請回去檢查程式哪裡錯誤,上面會顯示出行數與問題點 ![](https://i.imgur.com/ZalKIpA.jpg) 成功畫面如下,會生成 9 個檔案,最後使用燒錄器將 .hex 檔燒錄至 89S52 單晶片。 ![](https://i.imgur.com/kihcRJr.jpg) **注意!! 請確定排線連接至 89S52 單晶片的腳位是否正確。** --- ### 燒錄器使用說明(硬體燒錄) * **[SP200SE-driver 下載](https://drive.google.com/open?id=1zhK2M2M1gDWQUY31ngVHN0YxNyyXuclq)** 1. 將 8051 晶片接上燒錄器,並連結至電腦,請注意PIN1位置,如下圖:![](https://i.imgur.com/1pkKUbS.jpg) 2. 開啟燒錄軟體 `WLPROV2.20` ![](https://i.imgur.com/7XiCSwC.jpg) 3. `Device` 選擇確切的晶片型號,本圖以 AT89C51 為例 ![](https://i.imgur.com/71IT3uY.jpg) 4. 點選 `Load` 選擇編譯好的.hex 檔,成功後如下圖,如重新選擇 Device 必須重新執行此步驟。 ![](https://i.imgur.com/5MNmLnA.jpg) 5. 點選 `Erase` 清除晶片上的資料 ![](https://i.imgur.com/xP0f8MM.jpg) 6. 點選 `Program` 將.hex 檔燒錄至晶片中 ![](https://i.imgur.com/Qtjjx04.jpg) 7. 點選 `Verify` 驗證是否燒錄成功 ![](https://i.imgur.com/1UG0GU5.jpg) 8. 完成後即可將晶片放置到插好電路的麵包版上,進行實驗。 --- ## 實驗一 GPIO 控制 Stepper motor ### **步進馬達介紹:** 馬達在自動化控制中使用的場合相當頻繁,而馬達的種類可以分為直流馬達、交流馬達和步進馬達,本節以 8051 來做步進馬達的基本控制實驗,步進馬達可以靠開迴路控制做精確的定位,因此普遍地應用電腦的週邊設備上如印表機、繪圖機、磁碟機及產業生產的自動化機具如 NC 車床、切割機,此外機器人的各個關節控制也大量的使用步進馬達。 步進馬達是一種以數位脈衝信靠控制的電機裝置,將相對的信號轉變為輸出的旋轉角度,每一個基本旋轉角度稱為一個步進角度,此乃步進馬達名稱的由來,因此他可以做精確的定位。常見的步進角度為 1.8 度,若輸入 200 個控制脈波即可令馬達旋轉一圈。 * **主要特性:** 1. 馬達旋轉角度與輸入脈波成比例,角度誤差小,而且不會產生累積誤差。 2. 可以數位脈波控制信號做開迴路方式控制,避免使用複雜的回授控制電路,系統製作成本。 3. 利用輸入脈波的頻率高低即可做轉速的調整。 4. 馬達的啟動、停止、加速、減速、正反轉反應快,容易控制。 5. 直接連至負載如輪子,做超低速同步運轉。 6. 步進馬達的結構簡單,可靠性高,不須要太多的保養,使用壽命長。 市面上容易購置得到的步進馬達為六線四相式,本次所做的實驗採用步進馬達規格使用 5.1V 電壓,耗電 1A,步進角度為 1.8 度。 * 圖 1-1 四相六線式步進馬達接線圖 ![](https://i.imgur.com/wkpmC2m.jpg) * 圖 1-2 步進馬達接線圖 * 六線步進馬達 ![](https://i.imgur.com/WDY1MuH.jpg) * 五線步進馬達 ![](https://i.imgur.com/JXJkynP.jpg) ### 步進馬達驅動: * 一般步進馬達的驅動方式可以採用以下 3 種方式: 1. 單相激磁控制 2. 二相激磁控制 3. 半步激磁控制 #### 1. 單相激磁控制 * 圖 1-3 單相激磁控制動作時序圈 ![](https://i.imgur.com/fL3EhTs.jpg) 圖 1-3 為單相激磁控制的動作時序圖,每一步轉動只有一相線圈被激磁, 以此種方式控制最簡單,較省電,但轉距較小,阻尼效果差,振動大。將其動作時序加以分析,若以數位信號來做控制脈波驅動可以下表來完成設計。 B7|B6|B5|B4|B3|B2|B1|B0|數位輸出值 --|--|--|--|--|--|--|--|-------- 0 | 0| 0| 0| 0| 0| 0| 1|1 0 | 0| 0| 0| 0| 0| 1| 0|2 0 | 0| 0| 0| 0| 1| 0| 0|4 0 | 0| 0| 0| 1| 0| 0| 1|8 也就是說依序送出控制數位 `1-2-4-8` 則馬達正向轉一步。若送出控制數值 `8-4-2-1` 則馬達便反向運轉一步,若脈波持續送出則馬達會一直轉動下去。 #### 2. 二相激磁控制 * 圖 1-4 二相激磁控制動作時序圖 ![](https://i.imgur.com/uSfT0CQ.jpg) 圖 1-4 為二相激磁控制的動作時序圖,每一步級運轉時有兩相線圈被激磁,因此他的耗電是單相激磁電流的 2 倍,轉距可以增為 2 倍,阻尼效果佳, 一般採用此方式控制較多。下表為數位驅動脈波信號設計: B7|B6|B5|B4|B3|B2|B1|B0|數位輸出值 --|--|--|--|--|--|--|--|-------- 0 | 0| 0| 0| 1| 0| 0| 1|9 0 | 0| 0| 0| 0| 0| 1| 1|3 0 | 0| 0| 0| 0| 1| 1| 0|6 0 | 0| 0| 0| 1| 1| 0| 0|12 #### 3. 半步激磁控制 * 圖 1-5 半步激磁控制動作時序圖 ![](https://i.imgur.com/Wlt7HBT.jpg) 圖 1-5 為其動作時序圖,以此方式做控制乃是將單向及二相激磁混合加以應用,其耗用電流為單相激磁電流的 1.5 倍,步級角度為單相激磁的一半,故可以做更精確的定位控制。下表為控制脈波設計: B7|B6|B5|B4|B3|B2|B1|B0|數位輸出值 --|--|--|--|--|--|--|--|-------- 0 | 0| 0| 0| 0| 0| 0| 1|1 0 | 0| 0| 0| 0| 0| 1| 1|3 0 | 0| 0| 0| 0| 0| 1| 0|2 0 | 0| 0| 0| 0| 1| 1| 0|6 0 | 0| 0| 0| 0| 1| 0| 0|4 0 | 0| 0| 0| 1| 1| 0| 0|12 0 | 0| 0| 0| 1| 0| 0| 0|8 0 | 0| 0| 0| 1| 0| 0| 1|9 ### 實驗電路圖 * 圖 1-6 步進馬達控制電路 ![](https://i.imgur.com/oJYV8wi.jpg) 圖 1-6 為步進馬達的控制電路,在此利用 8051 埠 4 個位元 P1.0~P1.3 送出數位脈波信號並配合反向器做信號緩衝結合 NPN 型達靈頓電晶體 TIP102來驅動步進馬達運轉。功率晶體 TIP102的電流增益馬達高達 2500,只要送入很小的基極電流便可以使電晶體進入飽和區,而 TIP102 可以承受的最大集極電流可到 4A,因此非常適合用來推動步進馬達動作。另一方面為了減輕 8255 的負荷加上反相器做脈波系號緩衝,經過緩衝器的信號,再送到功率晶體的基極 上。當輸出埠送出低電位,使得 LED 點亮,經過反相器成為高電位驅使電晶體飽和而控制激磁電流導通。 另外在每一相線圈的兩旁都並上一只二極體用來保護電晶體,避免馬達電感性元件在電晶體 OFF 時所形成的反向高壓破壞晶體,可以經由二極體迴路而適當的放電掉。由於步進馬達耗用電流不小,因此建議外加一 +5V 電源供給器(電流約 1A 以上)加在馬達的電源上,以免馬達動作時干擾到 8051 的正常運作。 本次實驗是以單相激磁控制方式來控制步進馬達正反轉。 * 註 1. 若運行時,led有正確閃爍並且馬達抖動,試著把程式中的 dt 改小再來測試。 2. 如果你的步進馬達是五線的接腳順序為粉紅、橘、黃、藍。 3. 紅框內是 Reset 電路,當 pin 9 為高電位時就會觸發 Reset,此電路可以不用接,要 Reset 時接正電觸發就行。 ![](https://i.imgur.com/DmQP6NJ.jpg) ### 步進馬達範例程式 ```c=0 #include "reg51.h" void delay(int t); void turn(); code char step [ ] = {0x01,0x04,0x02,0x08}; int dir , dt; void main(){ dt=3000; dir=1; while(1){ turn(); } } void turn(){ int x; if(dir == 1){ //turn right for(x=0;x<4;x++) { P1=step[x]; delay(dt); } } } void delay(int t){ int x; for(x=0;x<t;x++) ; } ``` * **請儲存成 .c 檔,並儲存於 c:\sdcc\exam ,編譯及燒錄請參見 編譯器及燒錄器使用說明** ### 實驗一進階題 1. 先讓馬達正轉,速度要隨時間上升,到一定速度後,讓馬達反轉,並且速度隨時間下降。 2. 速度變化須讓助教能肉眼看出,否則不算通過。 --- ## 實驗二 External、Timer Interrupt 多個外部中斷源和計時器 ### **中斷介紹:** #### 8051 Interrupt: 1. INT0:外部中斷,由 8051 單晶片 Pin 12 輸入。 2. Timer0:計時/計數器中斷。 3. INT1:外部中斷,由 8051 單晶片 Pin 13輸入。 4. Timer1:計時/計數器中斷。 5. UART:串列埠中斷。 #### IE INTERRUPT ENABLE REGISTER ![](https://i.imgur.com/mNYmC1K.jpg) #### IP INTERRUPT PRIORITY REGISTER ![](https://i.imgur.com/Nm7WHqD.jpg) ![](https://i.imgur.com/1Q6v6UX.jpg) #### TCON TIMER/COUNTER CONTROL REGISTER ![](https://i.imgur.com/wJ0zixn.jpg) #### TMOD TIMER/COUNTER MODE CONTROL REGISTER ![](https://i.imgur.com/K02pCFf.jpg) ![](https://i.imgur.com/M7gQzH1.jpg) * **Mode 0** The 13-bit register consists of all 8 bits of THx and the lower 5 bits of TLx. The upper 3 bits of TLx are indeterminate and should be ignored. Setting the run flag does not clear these registers. ![](https://i.imgur.com/R1k8q9A.jpg) * **Mode 1** Mode 1 is the same as Mode 0,except that the Timer register uses all 16 bits. In this mode, THx and TLx are cascaded;there is no prescaler. ![](https://i.imgur.com/8Av4EAQ.jpg) * **Mode 2** Mode 2 configures the Timer registeras an 8-bit Counter (TLx) with automatic reload. Overtlowfrom TLx not only sets TFx, but also reloads TLx with the ecmtentsof THx. which is preset by software.The reload leaves THx unchanged. ![](https://i.imgur.com/Rjar0hr.jpg) * **Mode 3** Timer 1 in Mode 3 simply holds its count. The effect is the same as setting TR1 = 0. Timer 0 in Mode 3 establishes TL0 and TH0 as two separate counters. TL0 use a the Timer 0 control bits:C/T, GATE, TR0,INT0, and TF0.TH0 is locked into a timer function (counting machine cycles) and takes over the use of TR1 and TF1 from Timer 1.Thus TH0 now controls the Timer 1 interrupt. Mode 3 is provided for applications requiring an extra 8-bit timer or counter. When Timer 0 is in Mode 3 Timer 1 can be turned on and off by switching it out of and into its own Mode 3, or can still be used by the serial port as a baud rate generator,or in any application not requiring an interrupt. ![](https://i.imgur.com/w2gxvQw.jpg) #### Example: Mode 1 16-Bit Counter ![](https://i.imgur.com/AHAvlkM.jpg) * **Overflow per 0.05 s** $OSC = 12 M$ $1 pulse = \frac{1}{OSC/12}= 10^{-6}(sec)$ $0.05(sec) = 0.05/10^{-6}=50000 pulse$ $THx = (65536-50000) / 256$ $TLx = (65536-50000)$%$256$ ### 實驗介紹: 8051 提供有兩個外部中斷輸入控制 `INT0` 及 `INT1` ,如果系統需要更多的外部中斷輸入時,應該如何設計?此時可以參考圖 2-1 的電路,由外部電路來產生多點的外部中斷輸入。在電路中,中斷輸入一樣是由 `INT0` 接腳輸入,當按鍵 `S1` 或 `S2` 任一個開關按下時 `AND` 閘輸出成為低電位而產生中斷信號,當8051 偵測到中斷時,便會執行 `INT0` 中斷服務程式,此時服務程式必須先判斷是哪一按鍵按下了,如果是 `S1` 按下則 `P3.0` 變為低電位,如果是 `S2` 按下則 `P3.1` 變為低電位,一旦知道了哪一按鍵按下後,便可以跳至相對的控制迴圈去執行對應的中斷服務程式。 利用 8051 計時器(`Timer 0`)來控制 8 個 LED,讓其以週期為 1(s)來做明暗顯示。在實際應用中可以將各個輸入點加以擴充,並選擇多點輸入的 `AND` 閘, 或是以兩點輸入的 `AND` 閘串接起來做設計,另外也要選擇其他輸入位元做哪一按鍵按下判斷。 ### 實驗電路圖 * 圖 2-1 ![](https://i.imgur.com/GxX36yT.jpg) ### 多個中斷範例程式 ```c=0 #include "reg51.h" #define SIZE 8 /***************************************************/ /* a period = (65535 - 50000) = 3CAF (0.05 s/pulse) */ /* 0.05 x 20 = 1 s/pulse */ /***************************************************/ #define _TH0_ 0x3c #define _TL0_ 0xaf #define COUNTER 20 void delay(int t); void show(int method); code char light[] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f}; char counter; void main(){ TCON=0x00; TMOD=0x01; IP=0x01; IE=0x83; TL0 = _TL0_; TH0 = _TH0_; TR0 = 1; P1 = 0xff; counter = 1; while(1){ ; } } void tmiintrt(void) interrupt 1{ TL0 = _TL0_; TH0 = _TH0_; counter++; if(counter == COUNTER){ if (P1 == 0xff) P1 = 0x00; else P1 = 0xff; counter = 1; } } void intx(void) interrupt 0{ if(P3==0xfa) show(0); else if (P3 == 0xf9) show(1); else show(2); } void show(int method){ int x; if (method ==0 || method == 2){ for (x=0;x<SIZE;x++){ P1=light[x]; delay(10000); } } if (method ==1 || method == 2){ for (x=SIZE-1;x>=0;x--){ P1=light[x]; delay(10000); } } } void delay(int t){ int x; for (x=0;x<t;x++); } ``` ### 實驗二進階題 1. 按任意鍵(兩鍵需不同方向),讓燈號按造順序一次亮兩顆,到尾巴時會重複,跑兩次 2. 中途再按一下會使燈號停留在原本位置 3. 再按任意鍵,依所按鍵之指定方向繼續跑,重複1的情況 4. 請換成外部中斷1來實作 --- ## 實驗三 Serial Transmit 由 8051 傳送資料至 PC ### RS232 通訊介面 8051 本身有提供一組全雙工的串列傳輸介面,是由 TXD(腳位 11)來傳送串列資料而由 RXD(腳位 10)來接收資料,其動作邏輯位準皆為 TTL 準位(0V, 5V),如果要與 PC 做串列資料傳輸或是連接控制用必須經過 RS232 信號 (+12V、-12V)位準的轉換。目前市面上已有現成的 TTL 至 RS232 位準轉換 IC 編號為 HIN232 或 MAX232,只要外加四只電容器,便能完成介面位準轉換的工作了。圖 3-1 是設計在單板上的 RS232 通訊介面電路,經由 MAX 轉換出來的 RS232 串列信號在連至 D23PIN 母座與外部 PC 連線,便可建立 RS232 的通訊介面。 **現在實驗以 TTL 轉 USB 取代 RS232 電路。** ### TTL 轉 USB 驅動安裝 實驗發放的 TTL 轉 USB 有兩種(紅色 CP2102 及黑色長線),驅動不同,請根據拿到的線材來安裝對應驅動。 * 紅色 CP2102 驅動: [CP210x USB to UART Bridge VCP Drivers](https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers) 下載對應版本後按步驟安裝 (例:64位元執行CP210xVCPInstaller_x64.exe),將 TTL 插入電腦開啟 裝置管理員->序列埠 ,讀取到裝置並無驚嘆號即安裝成功。 ![](https://i.imgur.com/XVzYORU.jpg) * 長黑線 TTL 驅動: [IO-Cable_PL-2303_Drivers-Generic_Windows_PL2303_Prolific](https://drive.google.com/open?id=1EunV-3C8BwUG6M7ZexW2oCNl0WQDPoj2) 下載後按步驟安裝 ![](https://i.imgur.com/h5lICom.jpg) 完成後至裝置管理員選取並更新驅動軟體 ![](https://i.imgur.com/nFd6bjd.jpg) 選擇「瀏覽電腦上的驅動程式軟體」 ![](https://i.imgur.com/y6F612O.jpg) 選擇「讓我從電腦上的裝置驅動程式清單中挑選」 ![](https://i.imgur.com/euhh2Nm.jpg) 選擇版本 **3.3.2.105[2008/10/27]** ![](https://i.imgur.com/qEiG420.jpg) 關閉後即成功更新 ![](https://i.imgur.com/B3RF995.jpg) ### 終端機使用說明 [PuTTY 官方網頁檔案下載](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html) PuTTY 使用說明 1. 點擊 PuTTY 主程式 2. 選擇 Serial ![](https://i.imgur.com/LmzbTh3.jpg) 3. 修改 COM port,後方數字請在裝置管理員->序列埠 確認 ![](https://i.imgur.com/P5FChyM.jpg) ![](https://i.imgur.com/6B1vS3Y.jpg) 4. 修改鮑率為 9600 ![](https://i.imgur.com/9Q5Cg0m.jpg) 5. 點擊 Open 開啟終端機 ![](https://i.imgur.com/fgchLRi.jpg) 6. TTL 接至電路板開始測試程式 ### 4X4 鍵盤 在控制電路中,通常需要以按鍵來控制程式執流程或是輸入資料,如果是按鍵數不多時可以使用一個按鍵對應一條輸入位元線,但是若按鍵數太多時, 通常是以矩陣式掃描法來做按鍵偵測,圖 3-2 是一個 16 個按鍵的控制電路,使用 8051 埠 18 條 I/0 線做 16 個按鍵的鍵盤掃描,由 p1.0~p1.3 送出掃描信號, 而由 p1.4~p1.7 讀取按鍵資料返回碼。 以程式掃描的方式的偵測那一按鍵按下,一次掃描一行四個按鍵,掃描的順序如下: 1. 送出掃描訊號 1110 以掃描 S0 行的四個按鍵,讀取按鍵資料,判斷該行是否有鍵按下,若有鍵被按下,則連接至被按下的該鍵返回線狀態為 0 。 2. 送出掃描信號 1101 以掃描 S1 行的四個按鍵,讀取按鍵資料,判斷該行是否有鍵按下。 3. 送出掃描信號 1011 以掃描 S2 行的四個按鍵,讀取按鍵資料,判斷該行是否有鍵按下。 4. 送出掃描信號 0111 以掃描 S3 行的四個按鍵,讀取按鍵資料,判斷該行是否有鍵按下。 5. 回到步驟 1 繼續做按鍵掃描。 以上的步驟連續地重覆,若有按鍵被按下,就將該按鍵解碼出來,至於如何解碼,可以使用雙重迴圈做計數編號,當某一按鍵按下時,其按鍵編號便是計數編號,有關按鍵編號、掃描信號及讀取按鍵資料返回碼列表如下: |鍵號|輸入|輸入|輸入|輸入|輸出|輸出|輸出|輸出|偵測按鍵 | |:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:-----:| |Pin |P1.7|P1.6|P1.5|P1.4|P1.3|P1.2|P1.1|P1.0| | |0 |1 |1 |1 |0 |1 |1 |1 |0 |K0 | |1 |1 |1 |0 |1 |1 |1 |1 |0 |K1 | |2 |1 |0 |1 |1 |1 |1 |1 |0 |K2 | |3 |0 |1 |1 |1 |1 |1 |1 |0 |K3 | |4 |1 |1 |1 |0 |1 |1 |0 |1 |K4 | |5 |1 |1 |0 |1 |1 |1 |0 |1 |K5 | |6 |1 |0 |1 |1 |1 |1 |0 |1 |K6 | |7 |0 |1 |1 |1 |1 |1 |0 |1 |K7 | |8 |1 |1 |1 |0 |1 |0 |1 |1 |K8 | |9 |1 |1 |0 |1 |1 |0 |1 |1 |K9 | |10 |1 |0 |1 |1 |1 |0 |1 |1 |K10 | |11 |0 |1 |1 |1 |1 |0 |1 |1 |K11 | |12 |1 |1 |1 |0 |0 |1 |1 |1 |K12 | |13 |1 |1 |0 |1 |0 |1 |1 |1 |K13 | |14 |1 |0 |1 |1 |0 |1 |1 |1 |K14 | |15 |0 |1 |1 |1 |0 |1 |1 |1 |K15 | ### UART (Universal Asynchronous Receiver Transmitter) 傳送資料時,不需要使用額外傳輸線來傳送同步訊號,就能正確的將傳送資料,稱為「非同步傳輸」,簡稱UART(Universal Asynchronous Receiver Transmitter),不過必須在資料前後都加上同步訊號,將同步訊號與資料混和後,使用同一條傳輸線來傳輸,以及通訊兩端要設為相同的鮑率。 序列通訊在軟體設定里需要做多項設定,最常見的設定包括速率(Baud)、同位檢查(Parity Check)和停止位(Stop Bit)。 比如資料 11001010 被傳輸時,資料的前後就需加入 Start bit 及Stop bit ,其中也可加入 Parity bit (同位檢查)來驗證資料的正確性。 * Start bit: 1 個位元, * Stop bit: 1、1.5 或 2 位元,由使用傳送與接收兩方決定,但兩者的選擇必須一致。 * Parity bit:用來驗證資料的正確性。一般不使用,如果使用(一般是最高位或最低位),可選擇奇同位(Odd Parity)或偶同位(Even Parity)。 * 奇同位: 所有傳送的數位(含字元的各數位和檢查位)中「1」的個數為奇數。 * 偶同位: 所有傳送的數位(含字元的各數位和檢查位)中「1」的個數為偶數。 同位檢查可以用於接受方檢查傳輸是否傳送生錯誤,若某一位元組中「1」的個數發生錯誤,則位元組傳輸中有錯誤發生。若同位檢查正確,則無發生錯誤或發生偶數個錯誤。 ### Baud 鮑(Baud),指的是有效數據信號調製載波的速率,即單位時間內載波狀態變化的次數。 只有在每個符號只代表一個 bit 的情況,鮑率與比特率才在數值上相等,但是它們的意義並不相同。 「鮑」(Baud)本身已是速率,所以不需要寫成 Baud Rate。 ### 8051 Serial Interface: * Data Frame Example ![](https://i.imgur.com/VSXwabu.jpg) #### SBUF SERIAL DATA BUFFER The Serial Data Buffer is actually two separate registers, a transmit buffer and a receive buffer register. When data is moved to SBUF,it goes to the transmit buffer where it is held for serial transmission.(Moving a byte to SBUF is what initiate a the transmission.) When data is moved from SBUF, it comes from the receive buffer. ![](https://imgur.com/IUQFIB7.jpg) #### SCON Serial Port Control Register ![](https://imgur.com/fo1bgKs.jpg) * Mode 0 Serial data enters and exits through RXD. TXD outputs the shift clock. 8 bits are transrnitted/received: 8 data bits (LSB ilrst). The baud rate is fixed at $\frac{1}{12}$ the oscillator frequency. * Baud Rates: $Mode0 BaudRate = \frac{Oscillator Frequency}{12}$ * Mode 1 10 bits are transmitted (through TXD) or received (through RXD): a start bit (0), 8 bits data (LSB first), and a stop bit (1). On receive, the stop bit goes into RB8 in Special Function Register SCON. The baud rate is variable. * Baud Rates: When Timer 1 is used as the baud rate generator,the baud rates in Modes 1 and 3 are determined by the Timer 1 overflow rate. $Modes 1 and 3 Baud Rate = 2^{SMOD1}\times\frac{Timer 1 OverflowRate}{32}$ The Timer1 interrupt should be disabled in this application.The Timer itself can be configured for either “timer”or “counter” operation and in any of its 3 running modes. In most applications, it is configured for “timer” operation in the auto-reload mode (high nibble of TMOD = 0010B).In this case the baud rate is given by the formula: $Modes 1 and 3 Baud Rate = 2^{SMOD1}\times\frac{1}{32}\times\frac{{Oscillator Frequency}}{12\times[256-(TH1)]}$ ![](https://imgur.com/TRgflV7.jpg) * Mode 2 11 bits are transmitted (through TXD) or received (through RXD): a start bit (0), 8 data bits (LSB first), a programmable 9th data bit, and a stop bit (1). On Transmit, the 9th data bit (TB8 in SCON) can be assigned the value of 0 or 1 .Or, for example, the paritybit (P in the PSW) could be moved into TB8. On received the 9th data bit goes into RB8 in SCON, while the stop bit is ignored. (The validity of the stop bit can be checked with Framing ErrorDetection.) The baud rate is programmable to either $\frac{1}{32}$ or $\frac{1}{64}$ the oscillator frequency. * Baud Rates: The baud rate in Mode 2 depends on the value of bit SMOD1 in Special Function Register PCON. If SMOD1 = 0 (which is the value on reset), the baud rate is $\frac{1}{64}$ the oscillator frequency.If SMOD1 = 1, the baud rate is $\frac{1}{32}$ the oscillator frequency. $Mode2 BaudRate = 2^{SMOD1}\times\frac{Oscillator Frequency}{64}$ * Mode 3 11 bits are transmitted (through TXD) or received (through RXD): a start bit (0), 8 data bits (LSB first), a progremmable 9th data bit and a stop bit(1). In fact Mode 3 is the same as Mode 2 in all respects except the baud rate. The baud rate in Mode 3 is variable. * Baud Rates: as Mode 1 #### POWER CONTROL REGISER ![](https://imgur.com/JVluVpF.jpg) ### 實驗電路圖 * 圖3-1 ![](https://i.imgur.com/wR7NLST.jpg) * 圖3-2 ![](https://imgur.com/Kva9Dhq.jpg) 藍色框以 MAX232 將 RS232 轉 TXD、RXD 傳輸,但現以 TTL 轉 USB 取代(不用接)。 * 黑色長線: * Red: 5v * Black: Ground * White: RXD * Green: TXD * 紅色 CP2102(自行接杜邦線): * 5v * Ground * RXD * TXD * **TTL 與 8051 電路連接方式:** * TTL TXD -> 8051 RXD * TTL RXD -> 8051 TXD * TTL Ground -> 8051 Ground * 請根據石英震盪器修改程式碼及 Terminal 的 baud rate 石英震盪器|TH1 |baud rate --------|----|--------- 11.06MHZ|0xfd|9600 12MHZ |0xe6|1200 ### 4x4 鍵盤與串列傳輸 ```c=0 #include "reg51.h" /*******************************************/ /* keyboard's infomation */ /*******************************************/ code char ser[]={0xfe,0xfd,0xfb,0xf7}; code int key[]={1,2,3,12,4,5,6,13,7,8,9,14,10,0,11,15}; code char keymsg[]={0xee,0xed,0xeb,0xe7, 0xde,0xdd,0xdb,0xd7, 0xbe,0xbd,0xbb,0xb7, 0x7e,0x7d,0x7b,0x77}; code char msg[] = "0123456789abcdef"; code char startmsg[] = "Hello"; void send(char ch); void delay (int t); int getkey(); void main( ) { int x; int y; SCON=0x50; TMOD=0x20; TH1=0xfd; TR1=1; TI=0; for(y=0;y<5;y++){ send(startmsg[y]); } while(1){ x = getkey(); if( x>=0 && x<=15) send(msg[x]); } } void send(char ch){ SBUF=ch; while(TI == 0) ; TI=0; } int getkey(){ int i,k=-1; while(1){ for(i=0;i<4;i++){ P1=ser[i]; if(P1!=ser[i]){ if(P1==keymsg[i]) k=key[i]; else if(P1==keymsg[i+4]) k=key[i+4]; else if(P1==keymsg[i+8]) k=key[i+8]; else if(P1==keymsg[i+12]) k=key[i+12]; } if (k!=-1) break ; } if(k!=-1) while(1){ P1=ser[i]; if(P1==ser[i]) return k; } } } void delay (int t){ int x; for (x=0;x<t;x++) ; } ``` ### 實驗三進階題 1. 請使用 Baud rate 2400 來傳輸,demo 時請從 pietty 輸入 2400 Baud rate 讓助教檢查。 2. 製作計算機,規則如下 | 鍵號| 功能| |:---:|:-----:| | C|清除算式 | | D| +| | E| -| | F| x| | B| ÷| | A| =| 1. 僅需做整數運算即可 EX:5/2=2 即可 2. 不須做連續計算,一行算式只須有一個四則運算即可 * hint: send 無法直接輸出 int ,請將答案轉成 char 輸出 --- ## 實驗四 LCD 介面設計 ### LCD 介紹 LCD 可以分為兩型,一種是文字模型 LCD,另一種是為繪圖模式 LCD。市面上有各個不同廠牌的文字顯示型 LCD,仔細的察看一下,我們可以發現大部分的控制器皆是使用同一顆晶片來做控制,編號為 HD44780A。 #### LCD 特性 +5V 供電,亮度可調整。 * 內藏振盪電路,系統內含重置電路。 * 提供各種控制命令,如清除顯示器、字元閃爍、游標閃爍、顯示移位等多種功能。 * 顯示用資料 RAM 共有 80 個位元組。 * 字元產生器 ROM 有 160 個 5×7 點矩陣字型。 * 字元產生器 RAM 可由使用者自行定義 8 個 5×7 的點矩陣字型。 #### 接腳說明 一般市售的 LCD 均有統一的接腳,其接腳功能說明如下: ![](https://i.imgur.com/GwIYfbm.jpg) * D0~D7: 雙向的資料匯流排,LCD 資料讀寫方式可以分為 8 位元及 4 位元兩種,以 8 位元資料進行讀寫則 D0~D7 皆有效,若以 4 位元方式進行讀寫,則只用到 D7~D4。 * RS: 暫存器選擇控制線,一般接至地址線 A0。 當 RS = 0 時,並做寫入的動作時,可以寫入指令暫存器,若 RS = 0 ,且做讀取的動作,可以讀取忙碌旗號及地址計數器的內容。 如果 RS = 1 則為讀寫資料暫存器用。 * R/W: LCD 讀寫控制線,R/W = 0 時,LCD 執行寫入的動作,R/W = 1 時則做讀取的動作。 * EN: 致能控制線,高電位動作。 * VDD: 電源正電。 * VO: 亮度調整電壓輸入控制接腳,當輸入 0V 時字元顯示最亮。 * Vss: 電源地端。 |RS |R/W|功能 | |:-:|:-:|:------------:| | 0| 0|寫命令到LCD| | 0| 1|讀取忙碌旗號和位址計數器AC(記錄目前游標位址)內容| | 1| 0|寫資料到DDRAM(要顯示的文字)或CGRAM(要造字的字形)| | 1| 1|從DDRAM或CGRAM讀取資料| #### LCD 內部的記憶體 LCD 內部記憶體共分為 3 種: 1. 固定字型 ROM,稱為 CG(Character Generator) ROM。 2. 資料顯示 RAM,稱為 DD(Data Display) RAM。 3. 使用者自訂字型 RAM,稱為 CG RAM。 #### CG ROM CG ROM 內儲存著 192 個 5×7 點矩陣的字型,這些字型均以固定,例如我們將 "A" 寫入 LCD 中,就是將 "A" 的字型點矩陣資料找出來而顯示在 LCD 上。 ![](https://i.imgur.com/1VIgRlt.jpg) LCD字型編碼表(此圖版權屬張義和,例說89S51-C語言,新文京開發出版社所有) #### DD RAM DD RAM 內用來儲存寫至 LCD 內部的字元,DD RAM 的位址分佈從 00H 到 4FH,分別代表 LCD 的各位置,如下所示,例如我們要將 "A" 寫入第二行的第一個位置,先設定 DD RAM 位址為 40H,再寫入 41H (0b 0100 0001) 至 LCD 即可。 * LCD 顯示資料 RAM 位址 * 16 字 ×2 行 |顯示位置 | 0| 1| 2|...| 13| 14| 15| |:-----------:|:-:|:-:|:-:|:-:|:-:|:-:|:-:| |第一行DDRAM位址|00H|01H|02H|...|0DH|0EH|0FH| |第二行DDRAM位址|40H|41H|42H|...|4DH|4EH|4FH| ![](https://i.imgur.com/wngSW05.jpg) 由於 DD RAM 位址設定指令格式 DB7 為 1 (0b 1000 0000),以下範例程式寫入內容為 (0x80 + DD RAM 位址) #### CG RAM 此區域只有 64 位元組,可由使用者將自行設計的字型寫入 LCD 中,一個字的大小為 5×7 點矩陣,共可以儲存 8 個字型,其顯示為 00H 到 07H。 #### 控制方式 以 CPU 來控制 LCD 模組,方式十分簡單,LCD 模組其內部可以看成兩組暫存器,一個為指令暫存器,一個為資料暫存器,由 RS 接腳來控制。所有對指令暫存器或資料暫存器的存取均需檢查 LCD 內部的忙碌旗號(Blue Flag),此旗號用來告知 LCD 內部正在工作,並不允許接收任何的控制命令。而此一位元的檢查可以令 RS=0 時,讀取位元 7 來加以判斷,當位元為 0 時,以可以寫入指令或資料暫存器。 #### LCD 控制指令 LCD 控制指令有以下幾項: 1. 清除顯示器 2. 游標歸位設定 3. 設定字元進入模式 4. 顯示器開關 5. 顯示游標移位 6. 功能設定 7. CG RAM 位址設定 8. DD RAM 位址設定 9. 忙碌旗號讀取 10. 寫資料到 CG RAM 或 DD RAM 中 11. 從 CG RAM 或 DD RAM 中讀取資料 ![](https://i.imgur.com/Rl3sICd.jpg) 1. 清除顯示器 指令碼為 0X01,將 LCD DD RAM 資料全部填入空白碼 20H,執行此指令將清除顯示器的內容,同時游標移到左上角。 2. 游標歸位設定 指令碼為 0X02,位址計數器被除為 0,DD RAM 資料不變,游標移到左上角。 3. 設定字元進入模式指令格式為: |DB7|DB6|DB5|DB4|DB3|DB2|DB1|DB0| |:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:| |0 |0 |0 |0 |0 |1 |I/D|S | * I/D:位址計數器遞增或遞減控制,I/D=1 時為遞增,I/D=0 時為遞減。每次讀寫顯示 RAM 中字元碼一次則位址計數器會加一或減一。游標所顯示的位置也會同時向右移一個位置(I/D=1)或向左移一個位置(I/D=0)。 * S:顯示幕移動或不移動控制,當 S=1 時,寫入一個字元到 DD RAM 時,顯示幕向左(I/D=1)或向右(I/D=0)移動一格,而游標位置不變。當 S=0 時, 則顯示幕不移動。 4. 顯示器開關指令格式為: |DB7|DB6|DB5|DB4|DB3|DB2|DB1|DB0| |:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:| |0 |0 |0 |0 |1 |D |C |B | * D:顯示幕開啟或開關控制位元,D=1 時,顯示幕開啟,D=0 時,則顯示幕關閉。 * C:游標出現控制位元,C=1 則游標會出現在位址計數器所指的位置,C=0 則游標不出現。 * B:游標閃爍控制位元,B=1 游標出現後會閃爍,B=0,游標不閃爍。 5. 顯示游標移位指令格式為: |DB7|DB6|DB5|DB4|DB3|DB2|DB1|DB0| |:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:| |0 |0 |0 |1 |S/C|R/L|X |X | * X 表示 0 或 1 皆可 |S/C|R/L|動作 | |:-:|:-:|:-----------:| | 0| 0|游標向左移 | | 0| 1|游標向右移 | | 1| 0|字元和游標向左移| | 1| 1|字元和游標向右移| 6. 功能設定 指令格式為: |DB7|DB6|DB5|DB4|DB3|DB2|DB1|DB0| |:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:| |0 |0 |1 |DL |N |F |X |X | * DL:資料長度選擇位元,DL=1 時為 8 位元資料轉移,DL=0 時則為 4 位元資料轉移,使用 D7~D4 4 個位元,分 2 次送入一個完整的字元資料。 * N:顯示幕為單列或雙列選擇,N=1 為雙列顯示,N=0 則為單列顯示。 * F:大小字元顯示選擇,F=1 時為 5X10 點矩陣字會大些,F=0 則為 5X7 點矩陣字型。 7. CG RAM 位址設定指令格式為: |DB7|DB6|DB5|DB4|DB3|DB2|DB1|DB0| |:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:| |0 |1 |A5 |A4 |A3 |A2 |A1 |A0 | 設定 CG RAM 位址為 6 位元的地址值,便可對 CG RAM 讀/寫資料。 * DB5~DB3 表示第 n+1 個字 (0<=n<8) * DB2~DB0 表示第 n+1 個字第 m+1 行地址 (0<=m<7) * 每行寫入資料只取後 5bit,前 3bit 寫 0 ![](https://i.imgur.com/uwvBTsE.jpg) LCD字型編碼表(此圖版權屬張義和,例說89S51-C語言,新文京開發出版社所有) 8. DD RAM 位址設定指令格式為: |DB7|DB6|DB5|DB4|DB3|DB2|DB1|DB0| |:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:| |1 |A6 |A5 |A4 |A3 |A2 |A1 |A0 | 設定 DD RAM 為 7 位元的地址值,便可對 DD RAM 讀/寫資料,DB7為1。 9. 記碌旗號讀取指令格式為 |DB7|DB6|DB5|DB4|DB3|DB2|DB1|DB0| |:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:| |BF |A6 |A5 |A4 |A3 |A2 |A1 |A0 | LCD 之忙碌旗號 BF 用以指示 LCD 目前的工作情況,當 BF=1 時,表示正在做內部資料的處理,不接受外界送來的指令或資料。當 BF=0 時,則表示已準備接收命令或資料。當程式讀取資料的內容時,位元 7 表示忙碌旗號,而另外 7 個位元的地址值表示 CG RAM 或 DD RAM 中的位址,至於是指向那一位址則依最後寫入的位址設定指令而定。 10. 寫資料到 CG RAM 或 DD RAM 中 先設定 CG RAM 或 DD RAM 位址,再寫資料到其中。 11. 從 CG RAM 或 DD RAM 中讀取資料 先設定 CG RAM 或 DD RAM 位址,再讀取其中的資料。 ### LCD 電路圖 下圖是將 LCD 設計在 8051 單板上的線路圖,使用兩個 I/0 解碼位址 F000H 及 F001H。F000H 為 LCD 命令寫入或狀態查詢的 I/0 地址,F001H 則為資料讀出或寫入的地址,此 2 位置是由 A0 接腳連至 LCD RS 腳位來加以控制,而 R/W 控制線接 8051 的 WR 接腳,當 I/0 寫入時送出低電位動作的信號。 ![](https://i.imgur.com/CcCjNt7.jpg) ```c=0 #include "8051.h" //data bus P2 // ENABLE P3.4; // R_W P3.3; // RS P3.2; void LCDcmd(char hex); void LCDdata(char hex); code char init[] = {0x38,0x0e,0x06,0x02,0x01}; code char ser[]={0xfe,0xfd,0xfb,0xf7}; code int key[]={1,2,3,12,4,5,6,13,7,8,9,14,10,0,11,15}; code char keymsg[]={0xee,0xed,0xeb,0xe7, 0xde,0xdd,0xdb,0xd7, 0xbe,0xbd,0xbb,0xb7, 0x7e,0x7d,0x7b,0x77}; code char msg[] = "0123456789abcdef"; int rx_flag; char ch; void delay (int t); int getkey(); void main(){ int i,x; P3_2 = 0; P3_3 = 0; P3_4 = 0; SCON=0x50; TMOD=0x20; TH1=0xfd; TR1=1; IE=0x90; rx_flag=0; for(i = 0; i < 5; i++){ LCDcmd(init[i]); } while(1){ if (rx_flag==1){ rx_flag = 0; SBUF = ch; } else{ x=getkey(); LCDcmd(0x80); LCDdata(msg[x]); } } } void interr_s(void) interrupt 4{ if (RI){ RI = 0; rx_flag = 1; ch = SBUF; LCDcmd(0xc0); LCDdata(ch); } else{ TI = 0; } } int getkey(){ int i,k=-1; while(1){ for(i=0;i<4;i++){ P1=ser[i]; if(P1!=ser[i]){ if(P1==keymsg[i]) k=key[i]; else if(P1==keymsg[i+4]) k=key[i+4]; else if(P1==keymsg[i+8]) k=key[i+8]; else if(P1==keymsg[i+12]) k=key[i+12]; } if (k!=-1) break ; } if(k!=-1) while(1){ P1=ser[i]; if(P1==ser[i]) return k; } } } void LCDcmd(char hex){ delay(1000); P2 = hex; P3_4 = 1; P3_4 = 0; } void LCDdata(char hex){ delay(1000); P2 = hex; P3_4 = 1; P3_2 = 1; P3_4 = 0; P3_2 = 0; } void delay(int t){ int i; for(i = 0; i < t; i++) ; } ``` ### 實驗四進階題 * Phonebook: 建立一個能儲存5筆手機資料的電話簿 1. 數字鍵:在輸入號碼模式下,輸入號碼 2. E:enter鍵;在輸入號碼模式下,將號碼儲存進電話簿 3. C:清除當前畫面 4. F:進入電話簿模式,查看儲存號碼 5. D:電話簿模式下,查看下一筆儲存資料 6. B:電話簿模式下,返回輸入號碼模式 * 補充 * 輸入模式以及電話簿模式下,號碼顯示於第一排 * 若有使用功能鍵(英文字母),則第二排第一位顯示該功能鍵字母 * 電話簿模式下,只需將儲存號碼依序顯示(按D鍵下一筆) * 當輸入超過5筆資料,從第一筆開始覆蓋 --- ## 實驗五 ADC 轉換,LED 輸出 在本實驗中將 ADC0804 接到 Port2,另由 Port1 外接 LED 組做為輸出裝置,程式中在對 ADC 下達起始轉換後,是以輪詢方式來取得轉換後的資料。由於 Port2 為 ADC 使用,故採用 CS 與 RD 直接接地。 ### ADC Converter An analog-to-digital converter (ADC, A/D, or A-to-D) is a system that converts an analog signal, such as a sound picked up by a microphone or light entering a digital camera, into a digital signal. A digital-to-analog converter (DAC) performs the reverse function; it converts a digital signal into an analog signal. ### Sampling Sampling is the reduction of a continuous-time signal to a discrete-time signal. ![](https://i.imgur.com/Aogp7VG.png) * Sampling rate Define the rate at which new digital values are sampled from the analog signal. The rate of new values is called the sampling rate or sampling frequency of the converter. * Resolution The resolution of the converter indicates the number of discrete values it can produce over the range of analog values. The values are usually stored electronically in binary form. The resolution determines the magnitude of the quantization error and therefore determines the maximum possible average signal-to-noise ratio for an ideal ADC without the use of oversampling. ### ADC0804 Reference [ADC0801/ADC0802/ADC0803/ADC0804/ADC0805 8-Bit uP Compatible A/D Converters datasheet (Rev. C)](http://www.ti.com/lit/ds/symlink/adc0804-n.pdf) * Pin Configuration and Functions ![](https://i.imgur.com/L5gzz51.jpg) ![](https://i.imgur.com/37qaaKH.jpg) * Start Conversion ![](https://i.imgur.com/wih8S1i.jpg) * Output Enable and Reset With INTR ![](https://i.imgur.com/7XT0yS0.jpg) ### 實驗電路圖 ![](https://i.imgur.com/7wPKKe1.png) ### ADC 轉換 ```c=0 #include "reg51.h" #define LED P0 #define DB P1 #define WR P3 void main(){ IT0 = 1; EX0 = 1; EA = 1; LED = 0x00; WR &= 0xbf; WR |= 0x40; while(1); } void int_0(void) interrupt 0{ LED = DB ^ 0xff; WR &= 0xbf; WR |= 0x40; } ``` ### 實驗五進階題 * 轉動可變電阻,使燈泡依序由第一個開始亮,亮到最後一個。 不亮 - > 亮第1顆 - > 亮第1.2顆 - > 亮1.2.3顆 -> ... --- ## Arduino 介紹 Arduino 是一個開放的硬體平台,包括一個簡單易用的 IO 電路板,以及一個基於eclipse的軟體發展環境。Arduino 既可以被用來開發亦能夠獨立執行,並具備一定互動性的硬體平台,也可以被用來開發與 PC 相連接的周邊裝置,這些裝置甚至還能夠與執行在PC上的軟體(如flash、processing 等)進行溝通。 使用者可以利用Arduino板子接上各種電子裝置,例如 LED 燈、喇叭、馬達、開關、溫濕度感測器、紅外線發射與接收器、LCD 顯示裝置,以及 Ethernet, WiFi, XBee, Bluetooth, RFID, GPS 等各種通訊模組。若再配合撰寫一些自動控制的程式,就能利用Arduino做出各式各樣的自動控制應用,例如利用溫度感測器控制風扇的運轉、使用可變電阻控制燈光的明暗、控制馬達的轉速、利用紅外線遙控家電/ 利用伺服機 (Servo) 控制機械手臂或機器人,以及製作自走車、飛行器等等,用途非常的廣泛。 * Arduino Products: * Arduino Uno ![](https://i.imgur.com/oyic8GK.jpg) * Arduino Mega ![](https://i.imgur.com/ugAi2hh.jpg) * Arduino Micro ![](https://i.imgur.com/5hIpgsJ.jpg) * Arduino Esplora ![](https://i.imgur.com/7N44v2T.jpg) --- ### Arduino Uno Details ![](https://i.imgur.com/QGEgRh5.jpg) 1. USB:傳輸資料及供電(不需額外電源)。 2. 輸入電壓 Power Jack:電池或變壓器供電,與 USB 擇一即可。 3. FTDI USB 晶片: USB 的 Client 端的晶片,透過這個晶片就可以跟電腦溝通連結傳遞資料。 4. Voltage Regulator:穩壓器,保持電壓的穩定。 5. TX/RX LED 燈:當 Arduino 從 TX 接腳傳遞資料出去,或從RX接腳接收資料時,LED燈會閃爍,且這兩個 LED 與 Digital接腳 0、1 (RX/TX) 相通。 6. LED 燈:連接 Digital 13 腳位,顯示高或低電壓。 7. Digital腳位: Digital腳位0到13:做為 Digital 輸入與輸出之腳位,腳位數字前標示有 "~" 可以做為 PWM 模擬類比輸出之腳位,分別有3、5、6、9、10、11,PWM詳細內容實驗中會介紹。 GND:接地接腳。 AREF:模擬參考。Analog REFerence,取得 Arduino 的參考電壓。3.3V。 8. RX <- 0 和 TX -> 1: UART 資料傳遞時,透過 RX 接收傳入資料, TX 傳出資料。 TX/RX 與 USB 連結,故在燒錄程式時請確保 TX/RX 沒有任何接線。 9. LED 燈:電源顯示燈。 10. 重新執行程式按鈕。 11. ICSP Header:In-circuit serial programming 序列燒錄方式, PIC燒錄器燒錄程式的方法,程式記憶體為 Flash 的版本使用方便的 ICSP 序列燒錄方式。 12. Microcontroller:控制晶片。 13. Analog 腳位:A0、A1、A2、A3、A4、A5,用於 Analog 訊號輸入。值域 0~1023。 14. 電源控制腳位: Vin:電源輸入 Gnd:接地 5V:電源輸出 3.3V:電源輸出 RESET:重新執行程式 ### Arduino編譯器介面 ![](https://i.imgur.com/ajvljHk.jpg) 1. Compile:編譯程式。 2. Upload:將編譯過之程式上傳至Arduino,請事先確認USB是否連接。 3. Serial 訊息框:監控USB(RX/TX)輸出或輸入資料,程式內print的資訊也會顯示在此視窗。 4. 程式編輯區:撰寫程式碼的區塊。 5. 系統訊息:顯示系統編譯、執行、燒錄等訊息之區塊。右下角顯示為當前連結之port編號。 ### Arduino使用教學 1. 安裝 Arduino 程式編譯器 ![](https://i.imgur.com/vI8b2Gk.jpg) 2. 將 Arduino 透過 USB 接頭接上電腦 3. 安裝Driver(控制台->硬體和音效->裝置管理員->連結埠) ![](https://i.imgur.com/LrnYDV3.jpg) ![](https://i.imgur.com/LaVgYZ1.jpg) 尋找 Arduino 所安裝子資料夾下的 Drivers ![](https://i.imgur.com/01Ao4IF.jpg) 4. 確認 COM port 編號(控制台->裝置管理員->連結埠) ![](https://i.imgur.com/xYXSBLG.jpg) 5. 打開Arduino編譯器,選取 COM ![](https://i.imgur.com/avsG2uB.jpg) 6. 程式 ![](https://i.imgur.com/w36jcgX.jpg) 7. compile無誤後upload至板子,開啟Serial Monitor觀看結果 Compiling ![](https://i.imgur.com/a6XDFVs.jpg) Uploading ![](https://i.imgur.com/gqMswuR.jpg) Result ![](https://i.imgur.com/yVrHFT0.jpg) --- ## 實驗一 Analog & Digital ### 實驗目的 了解 Analog and Digital 輸入輸出與模式轉換,analog 值與 digital 值的差異。 應用 Digital 腳位作 LED 閃爍實驗,Analog 腳位作電阻值實驗。了解如何將 Arduino 板上的值傳回電腦(Serial Monitor)。 ### 實驗原理 * pinMode(pin,mode) 函數是用配置接腳為輸入或輸出模式,它是一個無返回值函數,函數有兩個參數 pin 和 mode ,pin 參數表示所要配置的接腳,mode 參數表示設置的模式 INPUT (輸入) 或OUTPUT (輸出)。ANALOG IN 不需要設定 mode。 * digitalWrite(pin,value) 函數有兩個參數 digitalWrite(pin接腳,電壓值), 電壓值為 HIGH(高電位)與 LOW(低電位)。 * digitalRead(pin) 在接腳為輸入的情況下,可以獲取接腳的電壓情況 HIGH或 LOW,參數 pin 表示所要獲取電壓值的接腳,該函數返回值為 int 型,表示接腳的電壓情況。 * analogWrite(pin, value) 有兩個參數 analogWrite(pin接腳,電壓值)。 (Arduino 並無實際的 analo g輸出,詳細請見附錄 analogWrite) * analogRead(pin) 函數 讀取接腳 analog 的數值,數值範圍為 0 ~1023,參數 pin 為接腳位置 * Serial.print() 將字串透過TX腳位傳遞出去,並且顯示在 Serial Monitor 中。 * 材料 Arduino Uno*1、麵包板*1、開關*1、LED*8、電阻*1、可變電阻*1 ### 實驗一 可變電阻控制LED閃爍 1. 將Arduino及各套件依照電路圖所示連接 2. 將程式碼打上並編譯後燒錄至板子 3. 需按下按鈕才能使LED打開,打開狀態下再按一次按鈕則關閉 4. 觀察實驗結果:LED依電阻改變,以不同頻率閃爍 5. 打開 Serial Monitor 查看讀取之電阻值 ### 實驗一接線圖 ![](https://i.imgur.com/XPkQpJ7.jpg) ### 實驗一程式碼 ```c=0 #define analogInPin A0 #define KeyPin 12 int flag=0; int i=0; int sensorValue = 0; void setup() { Serial.begin(9600); //Serial 傳輸速度 pinMode(KeyPin, INPUT); for(i=2;i<10;i++){ pinMode (i, OUTPUT); } } void loop() { sensorValue = analogRead(analogInPin); Serial.println(flag); if(digitalRead(KeyPin)==1){ if(flag==0) flag=1; else flag=0; } while(digitalRead(KeyPin)==1){ ; } if(flag==1){ for(i=2;i<10;i++){ digitalWrite(i, HIGH); } delay( sensorValue); for(i=2;i<10;i++){ digitalWrite(i, LOW); } delay(sensorValue); } else if(flag==0){ for(i=2;i<10;i++){ digitalWrite(i, LOW); } delay(200); } } ``` --- ## 實驗二 超聲波偵測距離(HC-SR04) 1. 將 Arduino 及套件依照接線圖所示連接 2. 將程式碼編譯後燒錄至板子。 3. 觀察結果:當物體距離感測器小於一公尺時,LED會開始交互閃爍。距離感測器越近,則LED交互閃爍的頻率越快。 ### HC-SR04 | |Pin | |:--------:|:-------------:| |5V | Supply | |Trigger |Pulse Input | |Echo |Pulse Output | |0V |Ground | The basic principle of work: 1. Using IO trigger for at least 10us high level signal, 2. The Module automatically sends eight 40 kHz and detect whether there is a pulse signal back. 3. IF the signal back, through high level , time of high output IO duration is the time from sending ultrasonic to returning. **Test distance = (high level time×velocity of sound (340M/S) / 2** ![](https://i.imgur.com/K4Ta3fd.jpg) #### Reference * [**HC-SR04**](https://cdn.sparkfun.com/datasheets/Sensors/Proximity/HCSR04.pdf) ### 實驗二接線圖 ![](https://i.imgur.com/HbJuDBM.jpg) ### 實驗二程式碼 ```c=0 #define TRIGPIN 12 #define ECHOPIN 13 #define LED1 8 #define LED2 9 long ping() { digitalWrite(TRIGPIN, LOW); delayMicroseconds(2); digitalWrite(TRIGPIN, HIGH); delayMicroseconds(10); digitalWrite(TRIGPIN, LOW); return pulseIn(ECHOPIN, HIGH)/58; // t = ( 0.01 (m) * 2 (back and forth)) / 340 (m/s Sonic speed) = 5.8 * 10e-5 (sec) = 58 (microsecons) // 1 cm per 58 microsecond } void setup() { pinMode(TRIGPIN, OUTPUT); pinMode(ECHOPIN, INPUT); pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); } void loop() { long cm = ping(); if (cm <= 100) { digitalWrite(LED1, HIGH); delay(cm*1.5 + 10); digitalWrite(LED1, LOW); digitalWrite(LED2, HIGH); delay(cm*1.5 + 10); digitalWrite(LED2, LOW); } delay(100); } ``` --- ## 實驗三 藍牙傳輸 ### 實驗目的 以藍牙傳輸模組 HC-06/HC-05 搭配 Arduino UNO,透過 Android 手機之 APP 進行藍牙傳輸實驗。 ### 實驗原理 1. 手機APP Android手機App的介面包含:搜尋與連接藍牙裝置的選單、 斷線、訊息傳送框等選擇區域(Button)。其中訊息傳送框窗包含提示標籤 (Label) 、文字輸入區 (TextBox) 與傳送按鈕(Button)。 2. Arduino端 利用 Arduino 的序列埠模擬函式 (Software Serial) 來建立與 HC-06的串列通訊,由HC-06模組擷取手機傳進來訊息,顯示訊息及輸入指令的工作都交給 Arduino 的序列埠,使用者也可以由 Arduino 將指令透過我們自訂的腳位寫回 HC-06。 3. 藍牙傳輸 藍牙序列埠模組,是一種能將 TTL 序列資料改以藍牙無線方式收發的介面裝置。 市面上的藍牙裝置通常都屬於 slave (從端)設備,像藍牙滑鼠∕鍵盤、藍牙 GPS 、藍牙遙控玩具…等等。一般的 Arduino 藍牙遙控和通訊實驗,也都是用「從端」模式,所以 HC-06 模組足以應付大多數需求。 常見的兩種支援 SPP(Serial Port Profile,序列埠規範)的藍牙模組 1. HC-05 為主/從 (master/slave)一體型,出廠預設通常是「從端」模式,但是能自行透過AT命令修改 2. HC-06 為從端模式,出廠前就設定好,不能更改;市面上販售的通常是「從端」模式。 多數的藍牙序列埠模組都能讓使用者自行調整參數,常見參數如下: 1. 名稱:一般最多允許32個英∕數字 2. 配對密碼:通常預設為 1234 3. 操作模式:主(master)、從(slave)或回應測試 (loopback),通常預設為 slave。 4. 傳輸鮑率(baud rate):可調整 4800bps~1382400bps。如不指定,廠商都會給定一個預設值,例如 9600bps(HC-06預設) ### 實驗材料 Arduino UNO * 1、藍牙傳輸模組 HC-06 * 1或 HC-05、麵包板 * 1 Android 系統之智慧型手機 * 1 ### 實驗內容 #### 前置設定 由於 HC-06 、 HC-05 藍牙模組出廠預設名稱為 "HC-06" 或 "HC-05",為了不造成之後手機連線時搜尋藍牙模組名稱上的混淆,首先必須先更改藍牙模組的名稱,在此介紹藍牙模組的操作模式: 1. 自動連線(automatic connection),又稱透通模式( transparent communication)。 平時使用的「自動連線」模式只是把 RX 腳位傳入的資料,轉成藍牙無線訊號傳遞出去;或者將接收到的無線資料,從 TX 腳位傳給 Arduino ,模組本身不會解讀資料,也不接受控制。 2. 命令回應(order-response),又稱為AT模式(AT mode)。 操控藍牙模組的指令統稱AT命令(AT-command)。AT命令並非透過藍牙無線傳輸,而是模組的TX和RX接腳。藍牙模組只有在AT模式,才能接收AT命令。 本實驗所採用的 HC-06 藍牙模組在與其他裝置連線之前,都是處於 AT 模式,只要一通電, HC-06 藍牙模組就會進入 AT 模式,且通電後,在尚未與裝置連線之前, HC-06 模組上的 LED 將不停地閃爍,一旦與其他藍牙裝置連線後(如:手機或電腦), LED 將維持點亮狀態。 **HC-06 的AT命令,採用 9600bps 的速率(預設)傳送,HC-05的AT命令,採用 38400bps 的速率(預設)傳送**。 ### HC-06 設定 #### 連接方式(**燒錄程式碼時請不要接上模組**) ***TX 接 RX,RX 接 TX !!!!*** |**Arduino UNO**|**HC-06**| |:---------------:|:--------:| |**Pin 10 ( RX )**| **TX** | |**Pin 11 ( TX )**| **RX** | |**5V** | **VCC** | |**GND** | **GND** | #### 設定步驟(HC-06): 1. 先將下列程式碼編譯並燒錄至 Arduino 板子 ```c=0 #include <SoftwareSerial.h> // 定義連接藍牙模組的序列埠 SoftwareSerial BT(10, 11); // 接收腳,傳送腳 char val; // 儲存接收資料的變數 void setup() { Serial.begin(9600); // 與電腦序列埠連線 Serial.println("BT is ready!"); BT.begin(9600); //HC-06藍牙模組的預設連線速率 } void loop() { if (Serial.available()) { val = Serial.read(); BT.print(val); } // 若收到Serial Monitor的資料,則傳送到藍牙模組 if (BT.available()) { val = BT.read(); Serial.print(val); } // 若收到藍牙模組的資料,則送到Serial Monitor } ``` 2. 輸入AT指令設定 燒錄完程式碼接上模組後,打開Serial monitor,下面選項請改成 **NL&CR及9600 baud**,上方的Command區輸入以下指令,**以下命令部分注意請用大寫**。 1. AT+NAME:設定模組名稱,預設名稱為 HC-06,名稱在此請設定為自己的學號 Ex: AT+NAMEn960xxxxx 2. AT+PIN:更改配對密碼,透過這個AT 命令修改配對密碼。在此實驗中請設定密碼為 2019 Ex: AT+PIN2019 3. AT+BAUD4 設定 藍芽傳輸的 baud rate 為 9600 * 補充:HC-06 AT指令 |指令 |功能 | |:--------:|:---------------------:| |AT+VERSION|回應靭體的版本 | |AT+NAMExyz|設定裝置名稱為「xyz」 | |AT+PIN1234|設定連線密碼為「1234」 | |AT+BAUD4 |設定 baud rate 為 9600 | |AT+BAUD5 |設定 baud rate 為 19200| |AT+BAUD6 |設定 baud rate 為 38400| |AT+BAUD7 |設定 baud rate 為 57600| #### Reference * [**Microsoft Word - HC-06 datasheet 201104201104 revised.doc**](https://www.olimex.com/Products/Components/RF/BLUETOOTH-SERIAL-HC-06/resources/hc06.pdf) ### HC-05 設定 #### 連接方式(**燒錄程式碼時請不要接上模組**) 連接方式(燒錄程式碼時請不要接上模組!!) ***RX 接 RX,TX 接 TX !!!!*** |Arduino UNO |HC-05 | |:--------------:|:--------------:| |**Pin 0 ( RX )**|**RX** | |**Pin 1 ( TX )**|**TX** | |**5V** |**VCC** | |**GND** |**GND** | |**3.3V** |**EN (KEY)** | |**RESET** |**Arduino GND**| #### 設定步驟(HC-05) HC-05 不用先燒錄,接腳接好後即可開啟序列阜監控視窗下 AT 命令,下面選項請改成 **NL&CR 及 38400 baud**。 1. AT+NAME=學號:設定模組名稱,預設名稱為 HC-06,名稱在此請設定為自己的學號 Ex: AT+NAME=n960xxxxx 2. AT+PSWD=2019:更改配對密碼,透過這個AT 命令修改配對密碼。在此實驗中請設定密碼為 2019 Ex: AT+PSWD=2019 3. AT+UART=9600,0,0 設定 藍芽傳輸的 baud rate 為 9600,一個 stop bit,沒有 parity bit。 * 補充:HC-06 AT指令 |指令 |功能 | |:----------------:|:---------------------:| |AT |OK | |AT+ORGL |回復出廠默認 | |AT+VERSION? |回應版本 | |AT+ADDR? |獲取藍芽地址(自己) | |AT+NAME? |藍芽名稱 | |AT+NAME=1234 |設定名稱為1234 | |AT+UART=? |查詢鮑率 | |AT+UART=9600,0,0 |設定鮑率9600(其他以此類推)| |AT+ PSWD=1234 |設定密碼1234 | |AT+ROLE=? |查詢主/從模式 | |AT+ROLE=0 |設定被動模式 | |AT+ROLE=1 |設定主動模式 | |AT+CMODE=0 |0 指定藍芽地址連接模式 1任意藍芽地址連接模式| |AT+BIND=14,1,61757|主動模式連接被動模式的地址,(後面改成被動藍芽的AT+ADDR?),若兩塊HC-05 相連,密碼、鮑率要一致。| #### Reference * [**istd016A.pdf**](http://www.electronicaestudio.com/docs/istd016A.pdf) ### 手機安裝 Bluetooth_terminal 請下載並安裝 **Bluetooth_terminal** 的手機端藍牙傳輸 APP。 將以下程式碼編譯並燒錄至 Arduino 後,開啟手機本身的藍牙配對功能,搜尋並找到設定之模組名稱及輸入密碼,即可成功連接上藍牙模組,連線成功後開啟 Arduino Serial Monitor,並在 APP 的文字框中輸入任意字串,即可在 Serial Monitor 中顯示手機傳輸之字串。 ### 實驗三接線 |**Arduino UNO** |**HC-05/HC-06**| |:---------------:|:-------------:| |**Pin 10 ( RX )**| **TX** | |**Pin 11 ( TX )**| **RX** | |**5V** | **VCC** | |**GND** | **GND** | ### 實驗三程式碼 ```c=0 #include <SoftwareSerial.h> #include <Wire.h> // the maximum received command length from an Android system (over // the bluetooth) #define MAX_BTCMDLEN 128 // 建立傳輸接收的序列埠,接腳請注意! SoftwareSerial BTSerial(10,11); // Arduino RX/TX byte cmd[MAX_BTCMDLEN]; // received 128 bytes from an Android system int len = 0; // received command length void setup() { Serial.begin(9600); BTSerial.begin(9600); // HC-06 出廠的鮑率 } void loop() { char str[MAX_BTCMDLEN]; int insize, ii; int tick=0; while ( tick<MAX_BTCMDLEN ) { // 因為鮑率同為9600, Android送過來的字元可能被切成數份 if ( (insize=(BTSerial.available()))>0 ){ // 讀取藍牙訊息 for ( ii=0; ii<insize; ii++ ){ cmd[(len++)%MAX_BTCMDLEN]=char(BTSerial.read()); } } else { tick++; } } if ( len ) { // 用串列埠顯示從Android手機傳過來的訊息 sprintf(str,"%s",cmd); Serial.println(str); cmd[0] = '\0'; } len = 0; } ``` --- ## 實驗四 GLCD ### 實驗目的 運用 Adafruit_GFX、SWTFT、TouchSreen 等 Library 在 GLCD 上實現觸控繪圖功能。 ### 實驗原理 首先將實驗目的中的三個libraries載入IDE的”libraries”資料夾。(解壓縮後丟入`C:\Users\********\Documents\Arduino\libraries\` 裡面,一個 library 通常會含有 `.cpp`、`.h` 和文字說明檔,請檢查資料夾內的檔案存放路徑是否正確,檢查 `.cpp` 和`.h` 檔是否存放在最直接底層資料夾。 :::danger 例如路徑: `C:\Users\********\Documents\Arduino\libraries\TouchScreen\TouchScreen\TouchScreen.h` 或 `C:\Users\********\Documents\Arduino\libraries\New\TouchScreen\TouchScreen.h` 皆為錯誤! ::: :::success 正確路徑應為: `C:\Users\********\Documents\Arduino\libraries\TouchScreen\TouchScreen.h` ::: 核心 library--Adafruit_GFX 是由開源硬體公司 Adafruit 所開發,此 library 提供許多圖形基礎元素的基礎定義(Ex: points, lines, circles……),並且搭配 hardware specific library (在此處我們使用SWTFT library) 設定我們使用的GLCD之格式、顯示方式等,結合TouchScreen library加入觸控功能,以達到實驗目的。 ### 實驗材料 Arduino UNO*1、2.4吋TFT GLCD*1 ### 實驗內容 請依下表將 GLCD 與 Arduino 連接(確認接腳後直接覆蓋嵌上即可),再將下列程式碼編譯並燒錄至板子,試著變換顏色在GLCD上畫圖。 ![](https://i.imgur.com/Q5RuJC7.jpg) ![](https://i.imgur.com/IYvMfFK.jpg) * GLCD 接腳定義 ![](https://i.imgur.com/xjNp87A.jpg) ### 實驗四程式碼 ```c=0 #include <Adafruit_GFX.h> // Core graphics library #include <SWTFT.h> // Hardware-specific library #include <TouchScreen.h> #define YP A1 // must be an analog pin, use "An" notation! #define XM A2 // must be an analog pin, use "An" notation! #define YM 7 // can be a digital pin #define XP 6 // can be a digital pin #define TS_MINX 150 #define TS_MINY 120 #define TS_MAXX 920 #define TS_MAXY 940 // For better pressure precision, we need to know the resistance // between X+ and X- Use any multimeter to read it // For the one we're using, its 300 ohms across the X plate TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300); // Assign human-readable names to some common 16-bit color values: #define BLACK 0x0000 #define BLUE 0x001F #define RED 0xF800 #define GREEN 0x07E0 #define CYAN 0x07FF #define MAGENTA 0xF81F #define YELLOW 0xFFE0 #define WHITE 0xFFFF SWTFT tft; #define BOXSIZE 40 #define PENRADIUS 3 int oldcolor, currentcolor; void setup(void) { Serial.begin(9600); Serial.println(F("Paint!")); tft.reset(); uint16_t identifier = tft.readID(); tft.begin(identifier); testFillScreen(); testText(); delay(2000); tft.fillScreen(BLACK); tft.fillRect(0, 0, BOXSIZE, BOXSIZE, RED); tft.fillRect(BOXSIZE, 0, BOXSIZE, BOXSIZE, YELLOW); tft.fillRect(BOXSIZE*2, 0, BOXSIZE, BOXSIZE, GREEN); tft.fillRect(BOXSIZE*3, 0, BOXSIZE, BOXSIZE, CYAN); tft.fillRect(BOXSIZE*4, 0, BOXSIZE, BOXSIZE, BLUE); tft.fillRect(BOXSIZE*5, 0, BOXSIZE, BOXSIZE, MAGENTA); tft.drawRect(0, 0, BOXSIZE, BOXSIZE, WHITE); currentcolor = RED; pinMode(13, OUTPUT); } #define MINPRESSURE 1 #define MAXPRESSURE 1000 void loop() { digitalWrite(13, HIGH); Point p = ts.getPoint(); digitalWrite(13, LOW); /*if sharing pins, you'll need to fix the directions of touchscreen pins*/ //pinMode(XP, OUTPUT); pinMode(XM, OUTPUT); pinMode(YP, OUTPUT); //pinMode(YM, OUTPUT); // we have some minimum pressure we consider 'valid' // pressure of 0 means no pressing! if (p.z > MINPRESSURE && p.z < MAXPRESSURE) { if (p.x < (TS_MINY-5)) { Serial.println("erase"); // press the bottom of the screen to erase tft.fillRect(0, BOXSIZE, tft.width(), tft.height()-BOXSIZE, BLACK); } // turn from 0->1023 to tft.width p.y = map(p.y, TS_MINX, TS_MAXX, tft.width(), 0); p.x = map(p.x, TS_MINY, TS_MAXY, tft.height(), 0); if (p.x < BOXSIZE) { oldcolor = currentcolor; if (p.y < BOXSIZE) { currentcolor = RED; tft.drawRect(0, 0, BOXSIZE, BOXSIZE, WHITE); } else if (p.y < BOXSIZE*2) { currentcolor = YELLOW; tft.drawRect(BOXSIZE, 0, BOXSIZE, BOXSIZE, WHITE); } else if (p.y < BOXSIZE*3) { currentcolor = GREEN; tft.drawRect(BOXSIZE*2, 0, BOXSIZE, BOXSIZE, WHITE); } else if (p.y < BOXSIZE*4) { currentcolor = CYAN; tft.drawRect(BOXSIZE*3, 0, BOXSIZE, BOXSIZE, WHITE); } else if (p.y < BOXSIZE*5) { currentcolor = BLUE; tft.drawRect(BOXSIZE*4, 0, BOXSIZE, BOXSIZE, WHITE); } else if (p.y < BOXSIZE*6) { currentcolor = MAGENTA; tft.drawRect(BOXSIZE*5, 0, BOXSIZE, BOXSIZE, WHITE); } if (oldcolor != currentcolor) { if (oldcolor == RED) tft.fillRect(0, 0, BOXSIZE, BOXSIZE, RED); if (oldcolor == YELLOW) tft.fillRect(BOXSIZE, 0, BOXSIZE, BOXSIZE, YELLOW); if (oldcolor == GREEN) tft.fillRect(BOXSIZE*2, 0, BOXSIZE, BOXSIZE, GREEN); if (oldcolor == CYAN) tft.fillRect(BOXSIZE*3, 0, BOXSIZE, BOXSIZE, CYAN); if (oldcolor == BLUE) tft.fillRect(BOXSIZE*4, 0, BOXSIZE, BOXSIZE, BLUE); if (oldcolor == MAGENTA)tft.fillRect(BOXSIZE*5, 0, BOXSIZE, BOXSIZE, MAGENTA); } } if (((p.x-PENRADIUS) > BOXSIZE) && ((p.x+PENRADIUS) < tft.height())) { tft.fillCircle(p.y, p.x, PENRADIUS, currentcolor); } } } unsigned long testFillScreen() { unsigned long start = micros(); tft.fillScreen(BLACK); tft.fillScreen(RED); tft.fillScreen(GREEN); tft.fillScreen(BLUE); tft.fillScreen(BLACK); return micros() - start; } unsigned long testText() { tft.fillScreen(BLACK); unsigned long start = micros(); tft.setCursor(0,BOXSIZE*3); tft.setTextColor(RED); tft.setTextSize(3); tft.println("Hello World!"); return micros() - start; } ``` --- ## 附錄 (Arduino常用函式整理) ### I/O * pinMode(Pin, Mode) 設定Arduino腳位為輸入或輸出模式 * Pin:接腳位置, 0~13 * Mode:OUTPUT, INPUT,INPUT_PULLUP * digitalWrite(Pin, Value) 寫入 Digital 訊號至指定腳位。 * Pin:digital腳位0~13(注意先以pinMode設定) * Value:HIGH(1), LOW(0) * digitalRead(pin) 讀取指定腳位的digital訊號。 * Pin:digital腳位0~13(注意先以pinMode設定),讀取值為 1(HIGH) or 0(LOW)。 * analogWrite(Pin, Value) 寫入analog訊號至指定腳位。Arduino無法直接輸出analog數據,故以 PWM 的方式藉由 Duty Cycle 模擬輸出 analog 訊號。 PWM訊號頻率約為490Hz。以輸入的analog值(0~255) mapping 到Duty Cycle的高低電壓分布(0~100%) * Pin:analog腳位3, 5, 6, 9, 10, 11(單板上腳位數字有`~`) * Value:0~255 (to 0 ~ 100% Duty cycle) ![](https://i.imgur.com/lNc8yXf.jpg) * analogRead(pin) 讀取指定腳位的analog值,值域0~1023。 * Pin:A0~A5 analogWrite與analogRead不需要以pinMode設定腳位 ### System * millis() 取得系統運作的時間(自接電開始),單位:ms。 最長記錄時間9小時22分 * micros() 取得系統運作的時間(自接電開始),單位:us。 * delay(time) 延遲指定時間,使程式碼暫停指定的時間。單位:ms,unsigned long int。 * delayMicroseconds(time) 延遲指定時間,使程式碼暫停指定的時間,雖然可以達到微秒的延遲,但精細度較不佳。單位:microsecond,unsigned long int。 比起使用delay,使用millis()或micros()紀錄開始時間,利用時間相減較為精確,程式也不會於 delay()中 busy waiting。 EX: 若要在0.5秒後觸發 1. delay的方式: ```c=0 loop{ Serial.println(“start”); delay(500); Serial.println(“stop”); } ``` 2. 時間相減的方式: ```c=0 loop{ Serial.println(“start”); int temp = millis(); while ( millis() – temp <=500){ Serial.println(“do something else unlike delay() can’t do anything”); } Serial.println(“stop”); } ``` ### Serial * Serial.begin(baud rate) 設定Serial port 傳輸速率 (baud rate)。 baud rate:300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, or 115200。 通常使用 9600 為 Arduino 傳輸速率。 * int Serial.read() 取得Serial接收到的資料,資料型態為 int。 ### Timer * variable Timer Timer變數類型。宣告後可用 Timer 函式庫內的函數。 * int every(period, callback, count) 以period為週期時間執行 callback 函式, count 為執行次數,不一定要填寫。every 函數會回傳目前事件的 ID 。 * Period:間隔時間,單位:ms,long int。 * callback:函式名稱,自定函式。 * count:執行次數,單位:次,int。 * int oscillate(pin, period, initial state, count) 控制 Timer 函數的變動。 oscillate 可以控制指定腳位的的訊號高低,在函式參數輸入後 oscillate 會以 period 參數為時間間隔, initial state 為初始狀態切換 pin 腳的 High、Low 訊號切換。 count 參數決定執行次數,不一定要填寫。 oscillate() 函式會回傳事件的 ID 。 * Pin:digital pin腳位。 * Period:觸發oscillate間隔時間,單位:ms,unsigned long int。 * Initial state:指定腳位的初始狀態,HIGH or LOW。 * int pulse(pin, period, initial state) 控制 Timer 函數的變動。與 oscillate() 相比只運作一次便停止。 pulse() 函式會回傳事件ID。 * Pin:digital pin 腳位。 * Period:觸發 pulse 前的間隔時間,單位:ms,unsigned long int。 * Initial state:指定腳位的初始狀態,HIGH or LOW。 * update() 執行在 loop() 內,用來執行以及更新 Timer ,啟動 Timer 的所有事件。 * int stop(int ID) 停止正在執行的 Timer 事件。傳入事件欲停止之事件函式,回傳事件 ID。 使用 Stop 函式必須取得事件 ID ,可以在設定 timer 種類時使用 int 記錄事件 ID 。 Ex:LedEvent = t.oscillate(LED,Time,LOW) ### Others * map(value, fromLow, fromHigh, toLow, toHigh) 將傳入的數值從原始範圍轉換到另一範圍,又為 mapping 。不接受原始範圍以外的 value 輸入。計算以 integer為單位。下限的數值可以大於或小於上限,可以由此達到倒轉值域(互補數)的效果。 * value:欲轉換之值,int。 * fromLow:原始值域的下限,int。 * fromHigh:原始值域的上限,int。 * toLow:欲轉換值域的下限,int。 * toHigh:欲轉換值域的上限,int。 ### Interrupts * noInterrupts() 中止所有中斷的使用,無參數與回傳值。 * interrupts() 重新啟用所有中斷,使用於呼叫 noInterrupts() 之後,無參數與回傳值, Arduino 內的中斷優先權是固定的,但可以利用 noInterrupts() 來防止被中斷,執行完後在 interrupts()。 * External interrupts * attachInterrupt(interruptNum, ISR, mode) Arduino Uno 有兩個腳位可提供外部中斷,分別是 Digital pin 2(int.0)、Digital pin 3(int.1)。不同的 Arduino 單板所支援的中斷腳位數也不同。 * interruptNum:設置中斷腳位,0 or 1 (int.0 or int.1)。 * ISR:Interrupt Service Routine,中斷觸發時執行之函式。 * mode:觸發中斷模式,共分為五種。 1. LOW:當腳位值為low時觸發中斷。 2. HIGH:當腳位值為high時觸發中斷。 3. CHANGE:當腳位值改變時觸發中斷。 4. RISING:當腳位值從low變至high時觸發中斷。 5. FALLING:當腳位值從high變至low時觸發中斷。 * External interrupts 範例程式 ```c=0 void setup() { attachInterrupt(0, ISR_RISE, RISING); //pin2 } void loop() { } void ISR_RISE(){ Serial.println("external interrupt 0"); } ``` * Timer interrupt Arduino Uno 有三個 Timer,Timer0、Timer1、Timer2,大部分 arduino 內建的函式使用 Timer0,例如 millis()、delay() 等,所以盡量不要使用 Timer0,使用 analogWrite() 時(PWM),Timer0負責pin5、6,Time1負責 pin9、10,Timer2負責 pin11、3,以及一些 Library 也會使用Timer,使用 Timer 時請小心注意。 三個Timer 分別為8bit、16bit、8bit counter,Timer也有各種模式,但大部分都是使用 PWM,作為 Timer/Counter 的只有一個。 * Register介紹: * **TCCRxA : Timer/Counter Control Register A** ![](https://i.imgur.com/e0qIljG.jpg) ![](https://i.imgur.com/5rT6RP2.jpg) ![](https://i.imgur.com/UK8i1RA.jpg) ![](https://i.imgur.com/Mic4142.jpg) 設定 Timer 模式,使用Timer/Counter 時設定為 0x00 ,其餘模式皆為 PWM 不同的設定。 * **TCCRxB : Timer/Counter Control Register B** ![](https://i.imgur.com/uowCscm.jpg) ![](https://i.imgur.com/AHd2dzp.jpg) ![](https://i.imgur.com/KTi0UEH.jpg) ![](https://i.imgur.com/auwEh3e.jpg) 設定 clock 輸入源,其中CS12、CS11、CS10 決定prescaler (預除器),並決定timer每一個clock的時間。 * **TCNTx : Timer/Counter Register** Timer 的記數 register,Timer 0、1、2上限分別為255、65535、255 (8、16、8 bit)。 * **OCRxA : Output Compare Register A** OCIE0A開啟時,紀錄 TCNTx 到達多少發生中斷。 * **OCRxB : Output Compare Register B** OCIE0B開啟時,紀錄 TCNTx 到達多少發生中斷。 ![](https://i.imgur.com/iRzbPO5.jpg) * **TIMSKx : Time interrupt mask register** ![](https://i.imgur.com/EX4hbB4.jpg) Timer interrupt 的 Enable。 * OCIExB : Output Compare B Match Interrupt Enable Enable中斷,當 TCNTx 達到OCR1B時發生中斷,OCR1B 依 Timer 長度自行設定。 * OCIExA : Output Compare A Match Interrupt Enable Enable中斷,當 TCNTx 達到OCR1A時發生中斷,OCR1A 依 Timer 長度自行設定。 * TOIE 0: Overflow Interrupt Enable Enable中斷,當TCNTx溢位時發生中斷。 * **TIFRx : Timer/Counter 0 Interrupt Flag Register** ![](https://i.imgur.com/71CRtC9.jpg) ![](https://i.imgur.com/5e3G06S.jpg) ![](https://i.imgur.com/srztmxh.jpg) * Timer 中斷對應的向量名稱 |Interrupt|Function Name | |:-------:|:---------------:| |OCIExA |TIMERx_COMPA_vect| |OCIExB |TIMERx_COMPB_vect| |TOIEx |TIMERx_OVF_vect | * Timer interrupt 範例程式 ```c=0 void setup() { Serial.begin(9600); TCCR1A = 0x00; // timer1 16bit use to count TCCR1B = 0x05; TCNT1 = 65536-15625; // 16000000/1024 = 15625 times per sec to overflow TIMSK1 = 0x07; OCR1A = 65536 - 10416; OCR1B = 65536 - 5208; } void loop() { } ISR(TIMER1_OVF_vect){ noInterrupts(); TCNT1 = 65536 - 15625; Serial.print("overflow interrupt: "); Serial.println(millis()); interrupts(); } ISR(TIMER1_COMPA_vect){ noInterrupts(); Serial.print("compare to a's interrupt: "); Serial.println(millis()); interrupts(); } ISR(TIMER1_COMPB_vect){ noInterrupts(); Serial.print("compare to b's interrupt: "); Serial.println(millis()); interrupts(); } ``` ``` --- ## Nano pi 介紹 NanoPi M1 是 FriendlyARM 推出的一款完全開源的掌上神器,它的大小只有樹莓派的大約 2/3,可運行 Ubuntu MATE 、Debian 等作業系統。 NanoPi M1 採用 Allwinner H3 SoC,其處理器為部分採四核心 Cortex-A7 架構,運作時脈為 1.2GHz ,搭配 512MB DDR3 記憶體,與 Raspberry Pi 系列相比,規格大致與 Raspberry Pi 2 Model B相近。兩者採用的處理器架構相同,但相比之下 NanoPi M1 快了 300MHz。在繪圖處理器部分, NanoPi M1 採用 Mali400MP2 雙核心繪圖處理器,其運作時脈為 600MHz。 ### Nanopi開發板介面介紹 ![](https://i.imgur.com/vI27XH5.jpg) CPU:Allwinner H3, Quad-core Cortex-A7@1.2GHz GPU:Mali400MP2@600MHz,Supports OpenGL ES2.0 DDR3 RAM:512MB/1GB 網絡:10/100M乙太網路 A/V out:3.5mm耳機座/Via HDMI MIC:內置麥克風 IR Receiver:內置紅外接收模塊 USB Host:Type A型號,USB 2.0 x 3 MicroSD Slot:x1 MicroUSB :支持供電和數據傳輸,有OTG功能 視頻輸出: HDMI 1.4 1080P高清顯示, CVBS DVP Camera接口:24pin,0.5mm間距豎直貼片FPC座 Debug UART:4Pin,2.54mm排針 GPIO:40pin,2.54mm間距,兼容RasberryPi2 GPIO,含UART, SPI, I2C, PWM, IO等腳位資源 按鍵:電源按鍵x1,復位按鍵x1 Power Supply: DC 5V/2A ### 系統開發介面 一般來說,在樹莓派 (Raspberry Pi)、 Nano pi 上做開發時,多數會是在上面灌 Linux 作業系統(如 Ubuntu、Debian 等等),因此灌有此類作業系統之開發板,與一般常見Linux作業系統之電腦,使用起來並無二致,指令也都相同。唯一可能會有的差異是,灌在電腦上之系統在一開始會包含較多功能、函式可供使用,而在 pi 上可能會需要針對需求再去做擴增。 對於樹莓派 (Raspberry Pi)、Nano pi 此類之控制板,大致上主要有下列兩種方法可以用來顯示其終端機/ GUI 之介面。 1. HDMI 輸出至螢幕 此種方法為最簡單之方法, Nano pi 上配有 HDMI 視頻輸出,只需接上配有 HDMI接口之螢幕,即可在螢幕上顯示介面。此外 Nano pi 亦有 USB 接口,能外接滑鼠、鍵盤,如此一來便與一般電腦一樣,可以直接使用、開發。 * 優點: 使用起來與電腦相似,容易上手,特別若是使用者對於 Linux 作業系統熟 悉者。 * 缺點: 至少需有外接螢幕、鍵盤支援。特別是外接螢幕需有HDMI接口,若為 DVI、VGA接口,則需更改參數(但未必都行得通)。 2. Serial port Nano Pi 有提供原生的序列 (Serial)傳輸介面,因此只要透過 USB 轉 TTL 序列傳輸線,再於電腦端以終端機程式開起 Serial port(COM) 去和 Nano pi 溝通,如此就可以在不需要螢幕和鍵盤滑鼠的情況下登入 Nano pi了。 此本講義接下來都會以此種方式教學,因此詳細使用方法將會於後面章節講解。 * 優點: 無須外接螢幕、鍵盤支援。可於配備 windows、ubuntu、Mac OS… 等作業 系統之電腦下開發,僅需 USB 轉 TTL 序列傳輸線,以及在電腦端配有終端機程式即可。 * 缺點: 顯示畫面僅能為文字介面,無法以GUI 顯示。因此使用者需要懂得藉由文字介面對終端機下指令、編譯程式,達到與 Nano pi 溝通。對於習慣 GUI 介面之使用者,在使用上會較為不適應,需要時間上手。 ## 作業系統建置 Nano pi 篇 ### 事前準備(下載) 1. [作業系統](https://drive.google.com/file/d/131brTkfErCwNDWWHy4V9ZcAOqvnMDvz0/view) 2. [etcher](https://www.balena.io/etcher/) 3. [Pietty](http://123.briian.com/forum.php?mod=viewthread&tid=3229) ### 燒錄系統 1. 將記憶卡插入電腦 2. Etcher 3. 按Flash,完成後將記憶卡安全移除 ![](https://i.imgur.com/8kVn88N.jpg) ### 硬體準備 ![](https://i.imgur.com/4sn7lKD.jpg) * USB-TTL 轉接線,接線定義: * 紅 +5V * 黑 GND * 綠 TXD * 白 RXD ### 啟動 Nano pi 1. 將TTL轉接線插入電腦 開啟裝置管理員(不知如何開,請搜尋電腦) ![](https://i.imgur.com/fBsdcwU.jpg) 2. 若有驅動程式錯誤的問題,請下載此檔案做更新 1. [TTL 驅動程式](http://wp.brodzinski.net/wp-content/uploads/2014/10/IO-Cable_PL-2303_Drivers-Generic_Windows_PL2303_Prolific.zip) 2. 安裝下載檔案 3. 至裝置管理員選取並更新驅動軟體![](https://i.imgur.com/qpt8ELX.jpg) 4. 選擇「瀏覽電腦上的驅動程式軟體」![](https://i.imgur.com/gtE5IiD.jpg) 5. 選擇「讓我從電腦上的裝置驅動程式清單中挑選」![](https://i.imgur.com/eu5keYw.jpg) 6. 選擇版本3.3.2.105[2008/10/27]![](https://i.imgur.com/1E07GEG.jpg) 7. 關閉後即成功更新,但由於windows會自動更新驅動,因此重新開機後,需再重複做一次。 3. 打開 pietty 1. 選 Putty 模式![](https://i.imgur.com/UdvutQJ.jpg) 2. 選 Serial 3. COM 值設定 USB-TTL 裝置所顯示的值 4. Speed 設定 115200 5. 設定完按 Open ![](https://i.imgur.com/1xuTtt9.jpg) 4. 將 TTL 轉轉接線與 Nano pi 接上 5. 將 Nano pi 接上電源 ![](https://i.imgur.com/H5WuM1k.jpg) 6. 登入 Nano pi * 帳號 fa * 密碼 fa (密碼在打字時沒顯示是正常的,帳號及密碼皆輸入完後按 Enter) Nano pi 設置到此為止。 ## 作業系統建置 Raspberry pi 篇 ### 事前準備(下載) 1. [作業系統](https://www.raspberrypi.org/downloads/raspbian/) 2. [etcher](https://www.balena.io/etcher/) 3. [Pietty](http://123.briian.com/forum.php?mod=viewthread&tid=3229) ### 燒錄系統 1. 將記憶卡插入電腦 2. Etcher 3. 按Flash,完成後,將記憶卡安全移除![](https://i.imgur.com/BmVzHiW.jpg) 4. Raspberry pi 的 Serial port 預設是關閉的並且是預設給藍芽傳輸,所以必須更改一下開機檔案。 將記憶卡拔出並重新插入電腦,不要格式化,進到 SD 卡中編輯 config.txt,將每個 # 都分行,以及最後的 dtparam=audio=on 也是,並且新增兩行force_turbo=1以及dtoverlay=pi3-disable-bt 如下圖,完成後安全的退出SD卡並插入樹梅派中。 ![](https://i.imgur.com/8B6DL04.jpg) ### 硬體準備 ![](https://i.imgur.com/aSbL1Uk.jpg) * USB-TTL 轉接線,接線定義: * 紅 +5V * 黑 GND * 綠 TXD * 白 RXD ### 啟動 Raspberry pi 1. 將TTL轉接線插入電腦 開啟電腦裝置管理員(不知如何開,請搜尋電腦) ![](https://i.imgur.com/f0AQK4U.jpg) 2. 若有驅動程式錯誤的問題,請下載[此檔案](http://wp.brodzinski.net/wp-content/uploads/2014/10/IO-Cable_PL-2303_Drivers-Generic_Windows_PL2303_Prolific.zip)做更新 1. 安裝下載檔案 2. 至裝置管理員選取並更新驅動軟體![](https://i.imgur.com/7OaMlx0.jpg) 3. 選擇「瀏覽電腦上的驅動程式軟體」![](https://i.imgur.com/b4ImavE.jpg) 4. 選擇「讓我從電腦上的裝置驅動程式清單中挑選」![](https://i.imgur.com/NEYHXOr.jpg) 5. 選擇版本3.3.2.105[2008/10/27]![](https://i.imgur.com/AselJYc.jpg) 6. 關閉後即成功更新,但由於windows會自動更新驅動,因此重新開機後,需再重複做一次。 3. 打開pietty 1. 選 Putty 模式 ![](https://i.imgur.com/Vjc0nqx.jpg) 2. 選 Serial 3. COM 值設定 USB-TTL 裝置所顯示的值 4. Speed 設定 115200 5. 設定完按 Open ![](https://i.imgur.com/JcwFs9h.jpg) 4. 將 TTL 轉接線與 Raspberry pi 接上 5. 將 Raspberry pi 接上電源 6. 登入 Raspberry pi raspberrypi login: * 帳號: pi * 密碼: raspberry (密碼在打字時沒顯示是正常的,帳號及密碼皆輸入完後按 Enter ) ![](https://i.imgur.com/Mm1JDzV.jpg) ### 遠端 Raspberry pi 以上步驟足以完成微介實驗,以下步驟可以讓你使用樹梅派上更加方便,若有筆電建議你往下做,若是桌電,只要是與樹梅派使用相同區域網路也可以完成。 Raspberry 內建 WiFi 功能,比起 Nano pi 只有 TTL 以及 乙太網路(也可自行插無線網卡), Raspberry 可以藉由 WiFi 用 SSH (Secure Shell)等遠端連線。 1. 將樹梅派連上 WiFi 以下作法建議使用筆電或手機開行動熱點會方便很多。 修改 `/etc/wpa_supplicant/wpa_supplicant.conf` 輸入 `$ sudo nano /etc/wpa_supplicant/wpa_supplicant.conf` 加入以下內容 ``` network={ ssid="wifi’s name" psk="wifi’s password" } ``` 以上的 `" "` 符號不能省略 完成後依序輸入 `ctrl + x` -> `y` -> `enter` 離開。 接著輸入 `$ sudo reboot` 重開樹梅派。 用筆電開行動數據的同學可以檢查樹梅派重開後有沒有連上網路,一旦連上 WiFi,樹梅派未來重開都會自動連接。 ![](https://i.imgur.com/KYpxRX8.jpg) 上圖連了兩塊的樹梅派,黃色部分為樹梅派區域網路 ip。 2. 開啟 SSH (secure shell) 使用 SSH 可以未來都不用 TTL 線來連接樹梅派,只需要 WiFi,但 SSH 在樹梅派預設是關閉的,所以必須啟用 SSH。 終端機輸入 `$ sudo raspi-config` ![](https://i.imgur.com/fDc6tLS.jpg) 選擇 `5 Interfacing Options` ![](https://i.imgur.com/xdhyq7X.jpg) 選擇 `SSH` -> `yes` -> `ok` 最後選擇 `Finish` 離開。 3. 至此未來可以不用 TTL 來連接樹梅派,重新開一個 pietty 以及樹梅派,當確定樹梅派連上網路時,在主機或 ip 位置輸入樹梅派的 ip , port 固定 22 ,選擇連線,就可以登入樹梅派。 ![](https://i.imgur.com/tOItTHc.jpg) 4. 登入樹梅派且確定接上網路後,請先更新樹梅派,終端機輸入 `$ sudo apt-get update` 然後輸入 `$ sudo apt-get upgrade` 過程會很久請耐心等候。 5. 安裝 vncserver 遠端操作樹梅派 在上面不管是TTL或 SSH 都只有終端機能夠使用,但樹梅派的作業系統 Rasbian 是有圖形化介面的, hdmi 接上螢幕就能使用,沒有螢幕或轉接頭的的情況下,若要使用圖形化介面,也可以使用遠端連線 接上網路後,終端機輸入 `$ sudo apt-get install tightvncserver` 安裝 vncserver,安裝完成後輸入 `$ vncserver` 開啟 vncserver,第一次開啟時會要你輸入兩組密碼及驗證密碼,作為遠端登入的密碼,一組為遠端登入另一組為其他人瀏覽的密碼。 另一方面,在筆電端安裝 chrome 的插件,[vnc viewer for chrome](https://chrome.google.com/webstore/detail/vnc%C2%AE-viewer-for-google-ch/iabmpiboiopbgfabjmgeedhcmjenhbla) 或其他 vnc 軟體。 安裝好後可以在 chrome 的應用程式找到 vnc viewer。 開啟 vnc viewer 後輸入樹梅派的 ip 後面加上 ::5901,5901為預設 vncserver 的 port,可以更改或增加, Picture Quality 選擇 Automatic 後點選 Connect。 ![](https://i.imgur.com/WXCRrlH.jpg) 連接時會要你輸入剛剛設置的 vncserver 連線密碼,接著就能進入到樹梅派的圖形介面了。 ![](https://i.imgur.com/VNlkGeA.jpg) Nano pi 若購買網卡裝上後也能達到一樣的效果,作法差不多,請自行摸索。 --- ## 實驗一 基礎認識及使用 ### 基礎編譯 請用C語言寫一個簡單程式,輸出 Hello Nanopi/Raspberry pi! 樹梅派請安裝編輯軟體 vim 輸入 `$ sudo apt-get install vim` ### 教學: 1. vim hello.c * 輸入 `i` 進入編寫模式,`Esc` 跳出編寫模式,進入命令模式 * 儲存檔案 :在命令模式輸入`:w` * 離開 vim:在命令模是輸入 `:q` * 儲存並離開:在命令模式輸入 `:wq` 2. 寫 code (用C語言),不會的上網查! 3. **編譯程式**: * 輸入 `$ gcc -o hello hello.c` 將 hello.c 以 gcc (GNU Compiler Collection) 編譯成執行檔 hello 4. **執行**: * 編譯成功後(沒出現警訊),輸入 `$ ./hello` 執行 hello 執行檔 5. 查看結果是否正確 6. 若要關機,請輸入 `$ sudo shutdown` ### Linux 基本指令 以下指令為 Linux 系統很常用到的指令,在 terminal 的指令的前方都會加上 $ 做標記,但在輸入時請不要加上。 ``` $ ls # list 顯示當前目錄下的檔案 $ pwd # print working directory 顯示當前目錄的絕對路徑 $ cd # change directory 移動到主目錄 $ cd xxx # 移動到當前目錄下名為 xxx 的子目錄 $ cd /xxx/xxx/xxx # 移動到絕對路徑為 /xxx/xxx/xxx 的目錄中 $ cd .. # 移動到上層目錄 $ cd / # 移動到根目錄 $ mv /xxx/xxx/a /yyy/yyy/a # move 將絕對路徑為 /xxx/xxx/a 的 a 檔案搬移至 絕對路徑 /yyy/yyy/a $ cp /xxx/xxx/a /yyy/yyy/a # move 將絕對路徑為 /xxx/xxx/a 的 a 檔案複製至絕對路徑 /yyy/yyy/a $ touch xxx # 用來創建名為 xxx 空白檔案 $ mkdir xxx # 創建名為 xxx 目錄 $ rm xxx # 刪除名為 xxx 檔案 $ rmdir xxx # 刪除名為 xxx 目錄 ``` 不會使用或想查詢功能請參考 [鳥哥的 Linux 私房菜 – Linux 檔案與目錄管理](http://linux.vbird.org/linux_basic/0220filemanager.php),尤其是絕對路徑及相對路徑請一定要搞懂。 ### Fork 使用 #### Fork 簡介 Unix 及類 Unix 系統作業系統中,Fork 是一種建立自身行程副本的操作。它通常是內核實作的一種系統呼叫。 Fork 是類 Unix 作業系統上建立行程的一種主要方法,甚至歷史上是唯一方法。 Linux 允許用戶創建用戶行程的子行程,在 C 語言中通過 `pid_t fork(void);` 函數實現 Fork ,並複製當前行程的數據段和堆棧段,子行程和父行程共用代碼段。亦即程式碼經過 `fork()` 後會變為兩行程(一父一子),從 `fork()` 函數那行之後兩行程繼續執行。而在父行程中,Fork 函數返回值爲子行程的 pid (如果成功調用的話),在子行程中,Fork 函數的返回值爲 0。因此可以根據返回值的不同確定程序的運行流程。 ### 實驗一內容 修改下列範例,在終端機螢幕,產生出 16 個 "I am father" 以及 16 個 "I am child"。 ### 實驗一程式碼 ```c=0 $include <stdio.h> $include <stdlib.h> $include <unistd.h> int main(){ pid_t pid; int n=0; for( n=0 ; n< ; n++){ pid = fork(); } if( ){ printf("Fork Failed\n"); exit(-1); } else if( ) { printf("i am father\n"); } else { printf("i am child\n"); exit(0); } } ``` --- ## 實驗二 GPIO 及七段顯示 ### 實驗目的 GPIO (General-purpose input/output),通用型之輸入輸出的簡稱, PIN 腳依現實考量可作為通用輸入(GPI)、通用輸出(GPO) 或作為外部中斷,也可用來作為通用輸入與輸出 (GPIO) ,如當 clk generator , chip select 等。一個MCU最基本的就是從 GPIO 開始進行控制,簡單的動作往往可以發揮最關鍵的效果,任何外部的控制,都是要透過GPIO進行控制,沒有它就無法完成一個完整的控制。 ### 事前準備 1. 下載 Wiring library 2. 兩種板子的 library 不同,請使用對應的 Library: [Nano Pi Wiring library](https://github.com/wertyzp/WiringNP) [Raspberry Pi Wiring library](https://github.com/WiringPi/WiringPi) 3. 解壓縮後存至自備的 USB 隨身碟中。 * 由於Nano pi 沒有內建 WiFi 功能,因此除非用有線網路,不然若是要安裝套件,則必須從外接隨身碟進行或取安裝所需檔案。 ### 掛載拇指碟 Nano pi 請照下面方式做, Raspberry pi 也可以以下列方式做,若你已經可以遠端 Raspberry pi 的話,可以插入 USB 後,直接以圖形化介面將 WiringPi 解壓縮後的檔案直接拉進 Raspberry pi 中,再開啟終端機進入到 WiringPi 目錄裡面執行 `$ bash build`。 ![](https://i.imgur.com/xHQGDwf.jpg) 若無法以 Raspberry pi 進行遠端桌面的話則請參照下面方式進行。 在 Windows 系統中如果裝了數顆硬碟、光碟機或是隨身碟,系統就會自動幫這些設備(device)編號,例如第一顆硬碟通常就會是 C 槽,第二顆就是 D 槽,如果又有光碟機或隨身碟,就依序編下去,而在「我的電腦」中打開後,就可以存取設備(硬碟、光碟或隨身碟)中的檔案。 在 Linux 系統中所有可存取的檔案都被安置在一個很大的目錄樹(也就是根目錄「/」)底下的某ㄧ個位置,如果要存取某個設備上的檔案,就必須先把這個設備掛載(mount)上來後,才能夠讀取或寫入資料,例如在新增硬碟、存取 USB 隨身碟或是讀取 CD-ROM 時,都需要掛載的動作。 * 掛載步驟: 1. 進入系統後,先查USB隨身碟是被系統辨識成那個裝置 `$ sudo fdisk -l` 往下查隨身碟容量可以找到隨身碟的編號 以下假設為sda1 (請根據你查到的更改) ![](https://i.imgur.com/fOxxqkC.jpg) 2. 建立USB隨身碟的掛載點: `$ sudo mkdir /mnt/usb` ![](https://i.imgur.com/UFj7udg.jpg) 3. 掛載USB隨身碟 `$ sudo mount -v -t auto /dev/sda1 /mnt/usb` `-v` 顯示資訊 `-t auto `讓系統自動分辯檔案系統 ![](https://i.imgur.com/VfqEvbe.jpg) 4. 到 `/mnt/usb` 可看到usb的內容 ### 安裝 Wiring library 1. 將 USB 隨身碟裡的WiringNP複製到 Nano pi 裡 * Nano pi: `$ sudo cp –r /mnt/usb/WiringNP-master /XXX/XXX` * Raspberry pi: `$ sudo cp –r /mnt/usb/WiringPi-master /XXX/XXX` (其中的 /xxx/xxx 依你自己想存放之位置, Nano pi 建議為 `/home/fa/` ,Raspberry pi 建議為 `/home/pi/` ) ![](https://i.imgur.com/K7jHF0y.jpg) 2. 進入Nano pi(Raspberry pi)裡面的WiringNP(WiringPi)資料夾, * Nano pi: `cd /home/fa/WiringNp-master` * Raspberry pi: `cd /home/pi/WiringPi-master` 執行 `$ bash build` 3. 驗證: Raspberry pi 或 Nano pi 執行過 `$ bash build` 後都請進行以下驗證。 * 在終端機輸入 `$ gpio -v` ,會看到如圖之畫面。 ![](https://i.imgur.com/Xzr3cxp.jpg) * 在終端機輸入 `$ gpio readall`,會看到如下頁圖之畫面,此畫面顯示各腳位之功能。 ![](https://i.imgur.com/Skdn5sF.jpg) 4. 注意!使用 Wiring library 在編譯時需在編譯指令多加 `–lwiringPi` `$ gcc -o blink blink.c –lwiringPi` 而執行程式時,需在前方加 `sudo` `$ sudo ./blink` * 加上sudo時若會跑出 `sudo:unable to resolve host NanoPi-M1`,可不必理會,它還是會照常執行。 ### Wiring library 基本用法介紹 * pinMode(pin,mode) 函數是用配置接腳為輸入或輸出模式,它是一個無返回值函數,函 數有兩個參數pin和mode ,pin參數表示所要配置的接腳,mode 參數表示設置的模式--INPUT(輸入)或OUTPUT(輸出)。 * digitalWrite(pin,value) 函數有兩個參數digitalWrite(pin接腳,電壓值), 電壓值為 HIGH(高電位)與LOW(低電位)。 * digitalRead(pin) 在接腳為輸入的情況下,可以獲取接腳的電壓情況—HIGH或 LOW,參數pin表示所要獲取電壓值的接腳,該函數返回值為int 型,表示接腳的電壓情況。 ### 七段顯示器介紹 七段顯示器分成共陽極和共陰極兩種,不管是哪一種,內部構造都是由 8 個 LED 發光二極體所組成,其中七個是筆劃,另外一個是小數點,如下圖所示,依順時針方向分別為 a, b, c, d, e, f, g 以及小數點 dp (decimal point): ![](https://i.imgur.com/PhVpSjL.jpg) 而腳位圖如下圖: ![](https://i.imgur.com/Zb0IIDU.jpg) 要產生數字,方法是點亮指定的 LED。例如要產生數字 0,便點亮 a, b, c, d, e, f 等節段;要產生數字 1,便點亮 b, c 等節段;要產生數字 2,便點亮 a, b, d, e, g 等節段。 * 共陽極、共陰極介紹 可以看到,腳位圖中,上下排中間都接到GND,代表此顆七段顯示器為共陰極。若要使某筆畫之LED燈亮,則必須給對應腳位高電位訊號,如此一來才能使電流通過該顆LED燈。反之,共陽極七段顯示器中間則為高電為,因此若要使某筆畫LED燈亮,則必須給予該對應腳位低電位訊號。 ### 實驗二內容 運用GPIO控制七段顯示器,顯示數字。 * 實驗步驟 1. 將Nano pi和各套件材料依電路圖所示連接 2. 在終端機上編寫程式碼 3. 編譯程式碼並執行程式 4. 七段顯示器會由0開始漸增顯示數字 ### 實驗二接線圖 ![](https://i.imgur.com/uCMUe7L.jpg) ### 實驗二程式碼 (此程式碼為使用共陽級七段顯示器,請測試共陰或是共陽極,因此需自行修改code) ```c=0 #include <wiringPi.h> int main(void) { wiringPiSetup() ; pinMode (12, OUTPUT) ;//B pinMode (13, OUTPUT) ;//A pinMode (14, OUTPUT) ;//F pinMode (30, OUTPUT) ;//G pinMode (21, OUTPUT) ;//C pinMode (22, OUTPUT) ;//D pinMode (23, OUTPUT) ;//E void init(int num[10][7]); int num[10][7]; int i; init(num); for(i=0;;i++) { if(i==10){ i=0; } digitalWrite(13,num[i][0]); digitalWrite(12,num[i][1]); digitalWrite(21,num[i][2]); digitalWrite(22,num[i][3]); digitalWrite(23,num[i][4]); digitalWrite(14,num[i][5]); digitalWrite(30,num[i][6]); delay(800); } } void init(int num[10][7]){ //0 num[0][0]=0; num[0][1]=0; num[0][2]=0; num[0][3]=0; num[0][4]=0; num[0][5]=0; num[0][6]=1; //1 num[1][0]=1; num[1][1]=0; num[1][2]=0; num[1][3]=1; num[1][4]=1; num[1][5]=1; num[1][6]=1; //2 num[2][0]=0; num[2][1]=0; num[2][2]=1; num[2][3]=0; num[2][4]=0; num[2][5]=1; num[2][6]=0; //3 num[3][0]=0; num[3][1]=0; num[3][2]=0; num[3][3]=0; num[3][4]=1; num[3][5]=1; num[3][6]=0; //4 num[4][0]=1; num[4][1]=0; num[4][2]=0; num[4][3]=1; num[4][4]=1; num[4][5]=0; num[4][6]=0; //5 num[5][0]=0; num[5][1]=1; num[5][2]=0; num[5][3]=0; num[5][4]=1; num[5][5]=0; num[5][6]=0; //6 num[6][0]=1; num[6][1]=1; num[6][2]=0; num[6][3]=0; num[6][4]=0; num[6][5]=0; num[6][6]=0; //7 num[7][0]=0; num[7][1]=0; num[7][2]=0; num[7][3]=1; num[7][4]=1; num[7][5]=0; num[7][6]=1; //8 num[8][0]=0; num[8][1]=0; num[8][2]=0; num[8][3]=0; num[8][4]=0; num[8][5]=0; num[8][6]=0; //9 num[9][0]=0; num[9][1]=0; num[9][2]=0; num[9][3]=0; num[9][4]=1; num[9][5]=0; num[9][6]=0; } ``` --- ## 實驗三 TCP/IP Socket 通訊 ### TCP Protocol 簡介 「TCP/IP」是 Transmission Control Protocol (TCP) 和 Internet Protocol (IP) 的簡稱,為網路上的通訊協定。即使是不同的電腦設備與作業環境,都可以透過這些通訊協定來互通訊息。 在TCP的兩端主機,可透過彼此的溝通,確保資料在傳輸中的正確性,以及傳輸速率的控制,這些動作只是兩端的主機之間溝通即可,完全無關於中間所經過的任何結點(例: router 或 switch ) 也就是說,兩端主機彼此說好就好,不用與中間任何結點協商或更動任何設定。 * 實驗需求之背景知識(背景執行) 由於此次實驗會需要同時執行兩個程式,若以 UART 方式對 Nano pi 開啟虛擬終端機介面,要同時開啟兩個終端機較為麻煩,而且非必要。因此在這要講解如何讓程式在背景執行,並且在需要時能夠叫回前景,或是在不需要時能夠殺掉。 * 將程式放到背景中執行,常用的方法有兩種: 1. 在執行該程式命令時,將該程式放在背景執行。在要執行程式後面加上" &"即可,範例如下: `$ ./server &` 輸入上述指令後,會看到如下圖之訊息: ![](https://i.imgur.com/4Qj91V5.jpg) `[1]`代表給此工作之序號 `719` 代表此程式之 PID 上述做法雖然直接且方便,但許多程式在一開始會需要輸入基本參數或設定,才會將它放入背景執行,若直接放置背景又會必須要叫回前景,又或是當程式執行到某個階段,使用者才有要將它放置背景 執行之需求。第二種方法將較適用於以上情況。 2. 程式執行時,按 `ctrl+z` ,則程式會暫停,且置於背景中。會顯示如下圖之訊息: ![](https://i.imgur.com/6A8mhES.jpg) 注意!此時程式為暫停態,若要在背景繼續執行,可輸入下列指令: `$ bg %1` or `$ bg 719` ( `%1` 為工作序號; `719` 為 PID) * 查詢背景工作中程式 若想知道當下有哪些程式在執行,可在終端機輸入下列指令: `$ jobs` 會顯示如下圖之訊息: ![](https://i.imgur.com/wTyK0RN.jpg) 若加上 `-l` ,則會連同 PID 一起顯示,如下圖: ![](https://i.imgur.com/1MET7Jh.jpg) * 將程式喚回前景執行 若需要將背景中執行之程式換到前景執行,則可輸入下列指令: `$ fg %1` or `$ fg 719` (`%1` 為工作序號; `719` 為 PID) * 刪除背景工作中程式 若想要刪除在背景工作中之程式,則可輸入下列指令: `$ kill %1` or `kill 719` (`%1` 為工作序號; `719` 為 PID) ### 實驗三內容 在 Linux 上建立簡易 TCP/IP socket 通訊 * TCP socket通訊流程圖: ![](https://i.imgur.com/iWpWhtv.jpg) * 各個函式之功用簡介如下: * `socket()` 創建一個新的確定類型之socket,類型用一個整型數值標識,並为它分配系統資源。 * `bind()` 一般用於服務器端,將一個 socket 與一個 socket 地址結構相關聯。也就是將 IP address 跟 port 跟 socket 綁在一起了。 * `listen()` 用於服務器端,使一個绑定的 TCP socket 進入監聽狀態。 * `connect()` 用於客戶端,替一個 socket 分配一個自由的本地端口,試圖獲得一個新的 TCP 連接。 * `accept()` 用於服務器端。它接受一個從遠端客戶端發出的創建一個新的 TCP 連接的接入請求,創建一個新的 socket。 * `end()` 和 `recv()`,或者 `write()`和 `read()` ,或者 `recvfrom()` 和`sendto()` , 用於往socket中發送和接受數據。 * `close()` 用於系統釋放分配给一個 socket 的資源。 如果是 TCP ,連接會被中斷。 #### 實作步驟 由於 Nano pi 無內建 WiFi 功能,加上實驗室環境並無太多有線網路能供使用,因此只需在 Nano pi 上自己對自己通訊即可。做法如下: 1. 建立兩個程式檔,一個為server,一個為client,並且編譯。 2. 自己對自己通訊時,client 端要求連線之Server IP設為127.0.0.1,此IP為Localhost,代表本機之位置。或是在終端機輸入ifconfig,也能查看到Localhost。 3. 先執行server,再將server丟到背景執行,接著再執行client。 ### 實驗三程式碼 #### 程式碼-Server ```c=0 #include<stdio.h> #include<string.h> //strlen #include<sys/socket.h> #include<arpa/inet.h> //inet_addr #include<unistd.h> //write int main(int argc , char *argv[]) { int socket_desc , client_sock , c , read_size; struct sockaddr_in server , client; char client_message[2000]; //Create socket socket_desc = socket(AF_INET , SOCK_STREAM , 0); if (socket_desc == -1){ printf("Could not create socket\n"); } printf("Socket created\n"); //Prepare the sockaddr_in structure server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons( 8888 ); //Bind if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0){ //print the error message perror("bind failed. Error"); return 1; } printf("bind done\n"); //Listen listen(socket_desc , 3); //Accept and incoming connection printf("Waiting for incoming connections...\n"); c = sizeof(struct sockaddr_in); //accept connection from an incoming client client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c); if (client_sock < 0){ perror("accept failed\n"); return 1; } printf("Connection accepted\n"); //Receive a message from client if( (read_size = recv(client_sock , client_message , 2000 , 0)) > 0 ){ //Send the message back to client write(client_sock , client_message , strlen(client_message)); } if(read_size == 0){ printf("Client disconnected\n"); } else if(read_size == -1){ printf("recv failed"); } return 0; } ``` #### 程式碼-Client ```c=0 #include <fcntl.h> // for open #include <unistd.h> // for close #include<stdio.h> //printf #include<string.h> //strlen #include<sys/socket.h> //socket #include<arpa/inet.h> //inet_addr int main(int argc , char *argv[]) { int sock; struct sockaddr_in server; char message[1000] , server_reply[2000]; //Create socket sock = socket(AF_INET , SOCK_STREAM , 0); if (sock == -1){ printf("Could not create socket\n"); } printf("Socket created\n"); server.sin_addr.s_addr = inet_addr("127.0.0.1"); server.sin_family = AF_INET; server.sin_port = htons( 8888 ); //Connect to remote server if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0){ perror("connect failed. Error\n"); return 1; } printf("Connected\n"); //keep communicating with server printf("Enter message : "); fgets(message,sizeof(message),stdin); //Send some data if( send(sock , message , strlen(message) , 0) < 0){ printf("Send failed\n"); return 1; } //Receive a reply from the server if( recv(sock , server_reply , 2000 , 0) < 0){ printf("recv failed\n"); } printf("Server reply :"); printf("%s",server_reply); close(sock); return 0; ``` --- ## 附錄 ### Ubuntu(Linux)連接Serial方法 有兩種方法: 1. 請用終端機開putty程式,再連線,方法如下: * sudo apt-get install putty * sudo putty * 選serial * serial line 輸入 /dev/ttyUSB0 speed 輸入115200 * 記住用sudo開啟putty 並且用圖形介面設定serial跟speed 不要直接用終端下command 連(有時雖然能成功 但有時會失敗) 2. 裝 neocon,然後用終端機連。 在自己 Ubuntu 系統依照以下步驟安裝 neocon: 1. `sudo apt-get install subversion` 2. `svn checkout http://svn.openmoko.org/developers/werner/neocon/` 3. `cd neocon` 4. `make` 5. `sudo cp neocon /usr/bin/` 6. 連接 USB-TTL 線 7. `sudo neocon /dev/ttyUSB0 115200`,即可連上 ## 資料來源 * Nano pi 介紹 * https://read01.com/n4mk3N.html * http://www.techbang.com/posts/42158-only-350-yuan-open-source-with-hdmi-mini-computer-nanopi-m1 * https://read01.com/n4mk3N.html * http://wiki.friendlyarm.com/wiki/index.php/NanoPi_M1/zh * Nano pi輸出介面方式 * https://www.raspberrypi.com.tw/1999/connect-to-raspberry-pi-via-serial/ * Fork相關 * https://www.byvoid.com/zht/blog/linux-c-1 * USB隨身碟掛載 * https://blog.gtwang.org/linux/linux-mount/ * WiringNP library * https://github.com/wertyzp/WiringNP/blob/master/INSTALL * Wiring library教學 * http://www.banana-pi.org/m3-download.html$ * GPIO * https://zh.wikipedia.org/wiki/GPIO * 七段顯示器 * http://programmermagazine.github.io/201307/htm/article1.html * Socket * http://fanli7.net/a/caozuoxitong/Unix/20120625/175942.html * http://www.binarytides.com/server-client-example-c-sockets-linux/ * http://www.ttvs.cy.edu.tw/kcc/kcc70/net/tcpintro.html * http://dns2.asia.edu.tw/~wzyang/slides/info_net/info_B/CH10TCP.pdf * http://beej-zhtw.netdpi.net/09-man-manual/9-2-bind