---
tags: 單晶片上課講義
---
# Arduino講義:Arduino 計步器實作
## 課程大綱
[ToC]
---
## 教學進度整理
| 日期 | 課程規劃 |
| -------- | -------- |
| W1 9/10|課程介紹+嵌入式系統介紹|
| W2 9/17|Arduino Keypad+多工七段顯示+跑馬燈實作|
| <font color="red">W3 9/24|<font color="red">Arduino 計步器實作</font>|
| W4 10/1|中秋節放假|
| W5 10/8|Arduino 藍芽+密碼鎖實作+==工設概念介紹==|
| W6 10/15|Arduino 電算機實作+==工設概念介紹==|
| W7 10/22|Arduino超音波+步進馬達+==工設概念介紹==|
| W8 10/29|**==第一次合班== 與工設系合作之期末專題提案**|
| W9 11/5|Arduino避障自走車實作|
| W10 11/12 |Arduino Timer使用|
| W11 11/19|**==期中考==**|
| W12 11/26|Mbed 平台介紹、基礎程式燒錄|
| W13 12/3|STM32 RTOS 實作(1)|
| W14 12/10|**==第二次合班== 與工設系合作之期末專題報告構想及進度**|
| W15 12/17|STM32 RTOS 實作(2)|
| W16 12/24|STM32 USB host實作|
| W17 12/31|期末專題製作(無課程但須上課)|
| W18 1/7|**==第三次合班== 期末報告及成果展示**|
# Serial communication-UART/I2C/SPI/(GPIO)
![](https://i.imgur.com/1z7idwK.png)
# Arduino講義:Serial Communication (SPI/I2C)
## Introduction of SPI
* SPI 是 Serial Peripheral Interface的縮寫 ,中文叫做序列周邊介面。
* 一種主從式架構的同步資料協定,用於短距離通訊。
* SPI 裝置之間使用<font color="red">全雙工模式通訊</font>,包含一個<font color="red">主機(master)</font>和一個或多個<font color="red">從機(slave)</font>。
* **SPI** : <font color="red"> 全雙工、串列、同步</font>
* 主要用以連接 ADC、DAC、EEPROM、通訊傳輸 IC...等週邊晶片。
|模組名|模組外觀|
|:--:|:--:|
|MCP3008 ADC|![](https://i.imgur.com/EYUwaHv.png)|
|PCM1794 DAC|![](https://imgur.com/1YNCARy.png)|
|ILI9341觸碰液晶螢幕|![](https://i.imgur.com/BMy7ItZ.png)|
|ENC28J60網路擴展模組|![](https://i.imgur.com/Jw15bb3.png)|
* [模組來源](https://www.taiwaniot.com.tw/)
### SPI 接腳名稱及意義
* Arduino Uno
| 接腳名稱 | 腳位 | 說明 |
| -------- | :--------:|--------|
| MOSI (Master Output,Slave Input)|11| master 數據輸出,slave 數據輸入(主出從入)|
| MISO (Master Input,Slave Output) |12| master 數據輸入,slave 數據輸出(主入從出)|
| SCLK/SCK (Serial Clock)|13| 時脈信號,由 master 產生並控制(Clock) |
| SS (Slave Selected) |10| LOW(低電位)時表示裝置可以與Master通訊(Enable)|
* 其他
![](https://i.imgur.com/GVeKrCM.png)
* SPI 匯流排:
* 單一主機對單一從機
![](https://i.imgur.com/Icd0Jwy.png)
* 單一主機對複合從機
主機將欲操作之從機選擇線拉低(SS to LOW),再分別透過 MOSI/MISO 啟動數據發送/接收。
![](https://i.imgur.com/Hjxqgtj.png)
* SPI優點有三,一是非常簡單的硬體介面,二是完整的傳輸位協定靈活性,訊息大小、內容、目的可任意選擇,三是slave裝置直接使用master裝置的clock,不需要經密的振盪器。
* SPI也有缺點,一是隨著連接裝置數的增加,線路也是要增加的,二是通常只支援一個主裝置。
#### 傳輸方式
* 當 Master 對 Slave 做 select 之後 (連接到該 slave 的 SS 拉 low), Master 開始送出 clock, 同時 Master 的資料 (MSB, 最高位元), 也由 shift register 推出,以在 MOSI 上維持住它的值,而 Slave 的資料也是在同一時間送到 MISO,說明了 SPI 是一個全雙工同步的訊號系統。
![](https://i.imgur.com/3FOvziL.png)
---
## Data Modes
* CPOL(Clock Polarity)
* 用來決定在空閒時,時脈(SCK)信號線上的電位是HIGH還是LOW
* 寫入1時,時脈(SCLK)閒置時為HIGH;寫入0時,時脈(SCLK)閒置時為LOW。
write 1 ![](https://i.imgur.com/asip4a8.png)
write 0 ![](https://i.imgur.com/tuS19Vb.png)
| CPOL0 | Leading edge | Trailing edge |
| -------- | --------| --------|
| 0 | Rising | Falling |
| 1 | Falling | Rising |
* CPHA(Clock Phase)
* 用來決定要在時脈(SCK)的前緣或後緣取樣
| CPHA0 | Leading edge | Trailing edge |
| -------- | --------| --------|
| 0 | Sample | Setup |
| 1 | Setup | Sample |
### The Table of SPI Modes
| SPI Mode | Conditions | Leading Edge |Trailing Edge|
| -------- | --------| --------|--------|
|0| CPOL=0, CPHA=0 | <font color="red">Sample (Rising)</font> | Setup (Falling)|
|1| CPOL=0, CPHA=1 | Setup (Rising) | <font color="red">Sample (Falling)</font>|
|2| CPOL=1, CPHA=0 | <font color="red">Sample (Falling)</font> | Setup (Rising)|
|3| CPOL=1, CPHA=1 | Setup (Falling) |<font color="red">Sample (Rising)</font>|
:::success
舉例:CPOL =0 表示 clock 原本在 low,CPHA =0 表示資料在第1個 edge 被讀取. 也就是 rising edge 被讀取。其他以此類推。
:::
<!-- * 平常使用 SPI Library 時,SPI Mode 預設值為0,SCLK 為4Mhz。
``` c++=
SPISettings() {
init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0);
}
```
### SPI Transfer Format with CPHA = 0
![](https://i.imgur.com/br3jTsg.png)
### SPI Transfer Format with CPHA = 1
![](https://i.imgur.com/uLrqhYw.png) -->
---
### 相關函數(記得include SPI.h)
| 函數 | 作用 |
| -------- | -------- |
| SPI.begin()| 通過將 SCK,MOSI 和 SS 設定為輸出,將 SCK 和 MOSI 設為LOW,SS 為HIGH來初始化 SPI 匯流排。|
|SPI.end()|結束 SPI Bus|
|SPI.setBitOrder(order)|設定數據在 SPI Bus 上傳送順序為低位元(`LSBFIRST`)優先或高位元(`MSBFIRST`)優先|
|SPI.setClockDivider(divider)|設定 SPI 時脈的除數(Arduino硬體時脈為16Mhz),可用的分頻器為 2,4,8,16,32,64 或 128,預設設定為 SPI_CLOCK_DIV4|
|SPI.transfer(val)|由 SPI 介面發送並接收 1 個 byte 資料|
---
## LAB1
* 實驗目的 :利用 SPI 進行 Arduino 間通訊
* Arduino UNO 內定 10,11,12,13 為 SPI 通信界面使用
* Pin 10 :SS chip select從設備致能信號,由主設備控制
* Pin 11 : MOSI 主設備數據輸出,從設備數據輸入
* Pin 12 : MISO 主設備數據輸入,從設備數據輸出
* Pin 13 : SCLK ,由主設備產生
* schematic
![](https://i.imgur.com/tJEPZp5.png)
* Master example
![](https://i.imgur.com/YMFoZYD.png)
<!--
// #include <SPI.h>
// void setup (void)
// {
// digitalWrite(SS, HIGH); // 確保SS初始狀態為HIGH
// SPI.begin ();
// SPI.setClockDivider(SPI_CLOCK_DIV8); //設定時脈為16/8 = 2 Mhz
// }
// void loop (void)
// {
// char c;
// digitalWrite(SS, LOW); //開始與從機通訊 //SS pin為10
// // 傳送字串
// for (const char * p = "Hello, world!\n" ; c = *p; p++)
// SPI.transfer (c);
// digitalWrite(SS, HIGH); // 關閉與從機的通訊
// delay (1000);
// } -->
* Slave example
![](https://i.imgur.com/DpVxmIH.png)
<!-- #include <SPI.h>
char buf [100];
volatile byte pos;
volatile bool process_it;
void setup (void)
{
Serial.begin (115200);
SPCR |= bit (SPE); //開啟從機的SPI通訊
pinMode (MISO, OUTPUT); //設定主入從出
pos = 0; // buffer 裡頭為空
process_it = false;
SPI.attachInterrupt(); //啟用中斷函式
}
ISR (SPI_STC_vect) //SPI中斷程序
{
byte c = SPDR; //從SPI Data Register獲取資料(byte)
if (pos < sizeof buf) //若buffer空間足夠就寫入buffer裡頭
{
buf [pos++] = c;
if (c == '\n') // 收到換行字元表示要處理buffer
process_it = true;
}
}
void loop (void) // 等待中斷函式回傳true
{
if (process_it)
{
buf [pos] = 0;
Serial.println (buf);
pos = 0;
process_it = false;
}
}
``` -->
* SPCR
![](https://i.imgur.com/enGoXKH.png)
[參考資料](https://www.sparkfun.com/datasheets/Components/SMD/ATMega328.pdf)
* ISR 介紹 [click here](http://programmermagazine.github.io/201407/htm/article1.html)
<!-- :::info
關於中斷函式:[**點我**](http://coopermaa2nd.blogspot.tw/2011/04/attachinterrupt.html)
::: -->
## LAB2
* 實驗目標 :按下master端的按鈕,使slave端的LED亮起,放開後熄滅。
* 示範影片 :
<iframe width="560" height="315" src="https://www.youtube.com/embed/ttAO21XuXz0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<!--master code
#include <SPI.h>
#define button 2
int buttonvalue;
int x;
void setup() {
digitalWrite(SS,HIGH);
pinMode(button,INPUT);
SPI.begin();
SPI.setClockDivider(SPI_CLOCK_DIV8);
}
void loop() {
byte Mastersend;
buttonvalue=digitalRead(button);
if(buttonvalue == HIGH)
{x=1;}
else
{x=0;}
digitalWrite(SS,LOW);
Mastersend=x;
SPI.transfer(Mastersend);
digitalWrite(SS,HIGH);
delay(10);
// put your main code here, to run repeatedly:
}
slave code
#include <SPI.h>
#define LEDpin 7
volatile boolean received;
volatile byte Slavereceived;
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
pinMode(LEDpin,OUTPUT);
SPCR |= _BV(SPE);
pinMode (MISO,OUTPUT);
received = false;
SPI.attachInterrupt();
}
ISR(SPI_STC_vect)
{
Slavereceived = SPDR;
received = true ;
}
void loop() {
if(received)
{
if(Slavereceived==1)
{
digitalWrite(LEDpin,HIGH);
Serial.println("Slave LED ON");
}
else
{
digitalWrite(LEDpin,LOW);
Serial.println("Slave LED OFF");
}
delay(10);
}
}
-->
# Arduino講義:計步器
## MPU6050模組
* 這次要練習的是 GY-521 的 Sensor,其核心的晶片是 MPU-6050。
* MPU-6050 是一個內包含<font color="red">三軸陀螺儀(gx,gy,gz)</font>以及<font color="red">三軸加速計(ax,ay,az)</font>結合在一起的數位運動處理器(簡稱DMP),以 <font color="red"> I²C </font> 輸出6軸的旋轉矩陣的數位資料。
### GY-521規格
* 核心晶片:MPU-6050 模組(三軸陀螺儀 + 三軸加速度計)
* 供電電源:3-5v(內部低壓差穩壓)
* 通信方式:標準 I²C 通信協議
* 晶片內置 : 16bit AD 轉換器,16位數據輸出
* 陀螺儀範圍:±250 500 1000 2000 °/s
* 加速度範圍:±2 , ±4 , ±8 , ±16g
* MPU6050 晶片座標定義 :
將晶片平放在桌面上,將其表面文字轉至正確角度,此時,以晶片內部中心為原點,水平向右為x軸,水平向上為Y軸,指向天花板為Z軸。
![](https://i.imgur.com/oJZmbiu.png)
#### 三軸加速度計 :
- 加速度計的三軸分量均為<font color="red">16位有符號整數(正負影響方向)</font>,分別表示物件在各軸上的加速度
- 我們可以把加速度計想象為一個正立方體盒子裏放著一個球,這個球被彈簧固定在立方體的中心。當盒子運動時,根據假想球的位置即可算出當前加速度的值。想象如果在太空中,盒子沒有任何受力時,假想球將處於正中心的位置,三個軸的加速度均為0。
如果我們給盒子施加一個水平向左的力,那麽顯然盒子就會有一個向左的加速度,此時盒內的假想球會因為慣性作用貼向盒內的右側面。
為了保證數據的物理意義,MPU6050的加速度計是以假想球在三軸上座標值的相反數作為三個軸的加速度值。當假想球的位置偏向一個軸的正向時,該軸的加速度讀數為負值,當假想球的位置偏向一個軸的負向時,該軸的加速度讀數為正值。
|示意圖| |
|:-:|:-:|
|![](https://i.imgur.com/EIhj9bu.png)|![](https://i.imgur.com/5rDTfWm.png)|
- 三個加速度分量均以重力加速度 g 的倍數為單位,能夠表示的加速度範圍,即倍率可以統一設定,<font color="red">有4個可選倍率:2g、4g、8g、16g</font>。
- 以 ACC_X 為例,若<font color="red">倍率設定為 2g (默認)</font>,則意味著 ACC_X 取最小值-32768時,當前加速度為沿 X 軸正方向2倍的重力加速度;若設定為4g,取-32768時表示沿 X 軸正方向4倍的重力加速度,以此類推。顯然,<font color="red">倍率越低精度越好,倍率越高表示的範圍越大</font>,這要根據具體的應用來設定。
- 這三個加速度分量是16位的二進制補碼值,且是有符號的。故而其輸出範圍 -32768~32767。 32767/2 = <font color="red">16384 即加速度計的靈敏度</font>。
<!--
- 三軸加速度計 :
- 加速度計的三軸分量 ACC_X、ACC_Y 和 ACC_Z 均為<font color="red">16位有符號整數</font>,分別表示物件在三個軸向上的加速度,取負值時加速度沿座標軸負向,取正值時沿正向。
- 三個加速度分量均以<font color="red">重力加速度 g 的倍數為單位</font>,能夠表示的加速度範圍,即倍率可以統一設定,有4個可選倍率:2g、4g、8g、16g。
- 以 ACC_X 為例,若<font color="red">倍率設定為 2g (默認)</font>,則意味著 ACC_X 取最小值-32768時,當前加速度為沿 X 軸正方向2倍的重力加速度;若設定為4g,取-32768時表示沿 X 軸正方向4倍的重力加速度,以此類推。顯然,<font color="red">倍率越低精度越好,倍率越高表示的範圍越大</font>,這要根據具體的應用來設定。
- 這三個加速度分量是16位的二進制補碼值,且是有符號的。故而其輸出範圍 -32768~32767。 32767/2 = <font color="red">16384 即加速度計的靈敏度</font>。
-->
* <font color="red"> Q&A </font> 那這個靈敏度怎麼使用呢?我們使用 Serial Port 顯示的一組數據來舉個例子:
1. ACC_X=03702
2. 對應的加速度數據是:03702/16384 = 0.23g。
3. 具體的加速度公式 : <font color="red"> 加速度數據 = 加速度軸原始數據/加速度靈敏度</font>。
#### 三軸陀螺儀 :
* 繞 X、Y 和 Z 三個座標軸旋轉的角速度分量均為16位有符號整數。
- 從原點向旋轉軸方向看去,<font color="red">取正值時為順時針旋轉,取負值時為逆時針旋轉</font>。
* 三個角速度分量均以“度/秒”為單位,能夠表示的角速度範圍,即倍率可統一設定,有4個可選倍率:<font color="red">250度/秒、500度/秒、1000度/秒、2000度/秒</font>。
* 以 GYR_X 為例,若倍率設定為250度/秒,則意味著 GYR_X 取正最大值32768時,當前角速度為順時針250度/秒;若設定為500度/秒,取32768時表示當前角速度為順時針500度/秒。顯然,倍率越低精度越好,倍率越高表示的範圍越大。
* 這三個陀螺儀分量是16位的二進制補碼值,且是有符號的。故而其輸出範圍 -32768~32767。32767/2000 = <font color="red">16.4 即陀螺儀的靈敏度</font>。
### 下載 library
* MPU-6050 因為是靠 I2C 溝通的,所以是需要到 I2Cdevlib 去找相關的函式庫 library 。
* 可直接下載此處2個檔案即可 [MPU-6050](https://drive.google.com/file/d/1-CEXrQ3x5exROE9a_0oNvU6Wm2iZQ6tM/view?usp=sharing) 、 [I2Cdev](https://drive.google.com/file/d/1BdefjZYsf_hLud5HF0cMXINTgrV6aif4/view?usp=sharing)
<!--(https://drive.google.com/file/d/1EDCvJBOozDBpI7-4ffF62k8mr7yetxEQ/view?usp=sharing) 。-->
* 法A: 將 MPU-6050 與 I2Cdev 資料夾, 丟進 Arduino 的 libraries 資料夾。
* Windows library預設可用路徑
* 法一:C:\Users\snowwolf415\Documents\Arduino\libraries
* (可以用Library內建範例)
* 法二:C:\Program Files (x86)\Arduino\libraries)
* (有時無法使用Library內建範例)
* 法B: Arduino程式->草稿碼->匯入程式庫->加入.zip程式庫
---
### MPU6050 方向角
![](https://i.imgur.com/rEY2DoR.png)
---
### 學習如何讀出 MPU-6050 量測之值
* MPU-6050 腳位 :
| Arduino | MPU-6050 | 備註 |
| -------- | -------- | -------- |
| 3.3-**5V** | VCC |<font color="red">注意電源不可接錯</font> |
| GND | GND |<font color="red">注意電源不可接錯</font> |
| A5 | SCL |I2C 時脈線|
| A4 | SDA |I2C 數據線|
| 本次不使用 | XDA | |
| 本次不使用 | XCL | |
| 本次不使用 | AD0 | |
| 2 | INT |中斷腳位(本次不使用)|
* 本次不使用腳位
* 線路接法
![](https://pic1.zhimg.com/ad102a2c0e2f903d86a0c8dc79d81dc4_r.jpg)
* code
![](https://i.imgur.com/NT3FoLI.png)
---
## 應用方式
### Tilt angle (傾斜角)
* 行動裝置
* MPU6050透過公式求得
* 得出裝置的傾斜狀況
![](https://i.imgur.com/pEVxfHG.png)
### Euler Angle (歐拉角)
* 穿戴式
* MPU9250透過公式求得
* 固定座標系(磁力計輔助定向)
![](https://i.imgur.com/5xBgYFJ.png)
![](https://i.imgur.com/BJi9nDd.png)
|模組|角度方式||示意圖|
|:-:|:-:|:-:|:-:|
|MPU6050|MPU6050![](https://i.imgur.com/7IvkqqI.png)|加速度計/陀螺儀|Tilt angle (傾斜角)![](https://i.imgur.com/pEVxfHG.png)|
|MPU9250|MPU9250![](https://i.imgur.com/wtEDKku.png)|加速度計/陀螺儀/磁力計|Euler Angle (歐拉角)![](https://i.imgur.com/BJi9nDd.png)
---
## Lab3
### 計步器實作
* 實驗目標 : 利用 MPU-6050 做出簡易計步器之功能
* 實驗要求 : <font color="red">靜止時或輕輕移動時 </font>,計步器數值不會加一,需<font color="red">轉動加速度超過一定幅度</font>數值才會加一。
* 實驗步驟參考 :
<font color="red">! ! 提醒 : 此實驗步驟為只使用三軸加速度(ax,ay,az)之值來進行運算</font>
* Step 1 : 利用講義上面介紹的加速度公式,得量化之加速度數據
* Step 2 : 透過下方公式將加速度數據代入,回推出變化之角度
![](https://i.imgur.com/elp403K.png)
* [公式參考](https://www.digikey.com/en/articles/techzone/2011/may/using-an-accelerometer-for-inclination-sensing)
* Step 3 : 為避免雜訊干擾,需透過前面量測出來之數據來設立閥值進行校準
* Step 4 : 定義怎樣的移動算一步
* [相關函式](https://www.arduino.cc/en/Reference/MathHeader)
* 示範影片 :
<iframe width="560" height="315" src="https://www.youtube.com/embed/VoLB26HE9qQ" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
## Lab4
### 計步器實作
* 實驗目標 : 完善你的簡易計步器,為其加上七段顯示器
* 實驗要求 : 七段顯示器第一開始顯示0,第ㄧ步增加1,第二步增加4,第三步增加9,以此類推,靜止時或輕輕移動時七段顯示器顯示的步數不會增加
<!--
#include <math.h>
#include <MPU6050.h>
#include <Wire.h>
int SEG_COM[4] = {10,11,12,13};
int SEG_data[11][8]={{1,1,1,1,1,1,0,0},
{0,1,1,0,0,0,0,0},
{1,1,0,1,1,0,1,0},
{1,1,1,1,0,0,1,0},
{0,1,1,0,0,1,1,0},
{1,0,1,1,0,1,1,0},
{1,0,1,1,1,1,1,0},
{1,1,1,0,0,0,0,0},
{1,1,1,1,1,1,1,0},
{1,1,1,0,0,1,1,0},
{1,1,1,1,1,1,1,1},
};
int number=0;
int disp[4]={0,0,0,0};
int i=0;
int j=0;
int nu=0;
MPU6050 accelgyro;
int16_t ax,ay,az,gx,gy,gz;
double xp,yp,zp=0;
void setup() {
// put your setup code here, to run once:
Wire.begin();
Serial.begin(9600);
accelgyro.initialize();
for(int j=2; j<=13 ; j++)
{
pinMode(j,OUTPUT);
digitalWrite(j,HIGH);
}
}
void loop() {
// put your main code here, to run repeatedly:
accelgyro.getMotion6(&ax , &ay, &az , &gx, &gy, &gz);
number_transfer(nu);
for(int z=0;z<=3;z++)
{ if(disp[z]!=10){
SEG_Drive(disp[z]);
digitalWrite(SEG_COM[z],LOW);
SEG_Drive(disp[z]);
digitalWrite(SEG_COM[z],HIGH);}
else
{digitalWrite(SEG_COM[z],HIGH);}
}
double x= atan(ax/ sqrt(square(ay) + square(az)))*180/3.14;
double y=atan(ay/ sqrt(square(ax) + square(az)))*180/3.14;
double z=atan(sqrt(square(ax) + square(ay))/az)*180/3.14;
if(( fabs(xp-x)+fabs(yp-y) > 85 or (fabs(xp-x)+fabs(yp-y)>80 and fabs(zp-z)>150)) and xp!=0 )
{
number++;nu=nu+number*number; Serial.println(number);delay(250);
xp=0;
}
else{
xp=x;
yp=y;
zp=z;
}
delay(10);
}
void number_transfer(int Num)
{
if(Num<10){
disp[0]= Num%10;
disp[1]=10;
disp[2]=10;
disp[3]=10;
}
else if(Num<100 and Num>=10){
disp[0]= Num%10;
disp[1]=(Num-Num%10)/10%10;
disp[2]=10;
disp[3]=10;
}
else if(Num<1000 and Num>=100){
disp[0]= Num%10;
disp[1]=(Num-Num%10)/10%10;
disp[2]=(Num-Num%100)/100%10;
disp[3]=10;
}
else{
disp[0]= Num%10;
disp[1]=(Num-Num%10)/10%10;
disp[2]=(Num-Num%100)/100%10;
disp[3]=(Num-Num%1000)/1000%10;}
}
void SEG_Drive(int numb)
{
i=numb;
digitalWrite(2,SEG_data[i][0]);
digitalWrite(3,SEG_data[i][1]);
digitalWrite(4,SEG_data[i][2]);
digitalWrite(5,SEG_data[i][3]);
digitalWrite(6,SEG_data[i][4]);
digitalWrite(7,SEG_data[i][5]);
digitalWrite(8,SEG_data[i][6]);
digitalWrite(9,LOW);
delay(1);
}
-->
* 示範影片 :
<iframe width="560" height="315" src="https://www.youtube.com/embed/1ID8gcDv3iA" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<!--## Bonus1: SPI 通訊系統-->
<!--![](https://i.imgur.com/fVqYCCM.png)-->
<!--![](https://i.imgur.com/ObLDDR4.png)
* INPUT
* MPU6050根據裝置運動情形判斷走路步數
* OUTPUT
* LCD輸出當前步數
* Communication
* IN : 手機將欲完成之步數透過藍芽傳輸至HC-05,並透過UNO板調整步數的相關參數
* OUT: HC-05將當前步數透過藍芽傳輸至手機,並在達成步數時透過文字提示
## 微形化可能
![](https://i.imgur.com/vEgUSgj.png) -->
## Bonus1: SPI主從溝通
* 實驗目的:將LAB2的功能更加完善
* 實驗目標:除了原有的功能之外,將Slave端新增按鈕腳位,按下後Master端的LED亮起,放開後熄滅。
## Bonus2: 直立測試
* 實驗目的:學會使用傾斜角
* 實驗目標:使用MPU6050的加速度計算出傾斜角,判斷目前狀態,若為直立則顯示straight,
若為右傾斜則顯示right,若為左傾斜則顯示left,若為倒立則顯示upside down。
* 示範影片 :
<iframe width="560" height="315" src="https://www.youtube.com/embed/CbOx8X6qdSQ" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
?
<!-- ### 加分 LAB SPI -- 接收 slave 端的回饋值
- 實驗目的:SPI用 master 端與 slave 端溝通方式。
- 實驗目標:將 master 端的數值丟入 slave 端進行運算後,slave 端將計算後的數值送回 master 端並於 Serial Monitor 顯示計算結果。
- schematic:
![](https://i.imgur.com/av0BHIm.png)
- master:
```c=
#include <SPI.h>
void setup (void)
{
Serial.begin (115200);
Serial.println ();
digitalWrite(SS, HIGH);
SPI.begin ();
SPI.setClockDivider(SPI_CLOCK_DIV8);
}
byte transferAndWait (const byte what) //數值傳送至Slave端
{
byte a = ;
delayMicroseconds (20);
return a;
}
void loop (void)
{
byte a, b, c, d;
digitalWrite(SS, LOW);
/*在此填入加法指令與數值*/
transferAndWait ('a');
transferAndWait (0);
a = transferAndWait (17);
digitalWrite(SS, HIGH);
/*回傳值顯示在Serial Monitor上*/
Serial.println ("Adding results:");
Serial.println (a, DEC);
delay(1000);
digitalWrite(SS, LOW);
/*在此填入減法指令與數值*/
transferAndWait ('s');
transferAndWait (0);
a = transferAndWait (17);
digitalWrite(SS, HIGH);
/*回傳值顯示在Serial Monitor上*/
Serial.println ("Subtracting results:");
Serial.println (a, DEC);
delay (1000);
}
```
- slave:
```c=
volatile byte command = 0;
void setup (void)
{
/*設定主入從出*/
/*啟用slave模式*/
/*啟用中斷*/
}
ISR (SPI_STC_vect) //SPI中斷程序
{
byte c = SPDR;
switch (command)
{
case 0:
command = c; //將暫存器內的資料設給command
SPDR = 0; //清空暫存器
break;
/*收到加法or減法指令並在此進行計算*/
}
}
void loop (void)
{
if (digitalRead (SS) == HIGH) //若SPI通訊關閉,清除目前command
command = 0;
}
``` -->
## 課後問題 (三題)
:::info
* Q1.請簡述 URAT、I2C、SPI三介面之優缺點。
* Q2.SPI mode的用途為何?為何設計4個mode?請詳細說明
* Q3.MPU-6050 的接腳 XDA、XCL、AD0 功能為何?
* Q4.下方為不呼叫 MPU-6050 library 來讀出 MPU-6050 數值之程式,
請為其標上 <font color="red">完整的註解</font>。
```c=
#include<Wire.h>
const int MPU_addr=0x68;
void setup(){
Wire.begin();
Wire.beginTransmission(MPU_addr);
Wire.write(0x6B);
Wire.write(0);
Wire.endTransmission(true);
Serial.begin(38400);
}
void loop(){
Wire.beginTransmission(MPU_addr);
Wire.write(0x3B);
Wire.endTransmission(false);
Wire.requestFrom(MPU_addr,14,true);
AcX=Wire.read()<<8|Wire.read();
AcY=Wire.read()<<8|Wire.read();
AcZ=Wire.read()<<8|Wire.read();
GyX=Wire.read()<<8|Wire.read();
GyY=Wire.read()<<8|Wire.read();
GyZ=Wire.read()<<8|Wire.read();
Serial.print("AcX = "); Serial.print(AcX);
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);
}
```
*Q5.心得
:::