arduino
利用I2C進行Arduino間通訊。
完整I2C原理參見lab4課後習題
Question 1,Wire.h
的使用實例參見lab2課後習題 Question 4。
Wire.begin();
初始化Wire.h
,未指定7-bit slave地址則為加入者為master。
if (Serial.available()) {
Wire.beginTransmission(SLAVE_ADDRESS);
while (Serial.available()) {
Wire.write(Serial.read());
delay(10);
}
Wire.endTransmission();
}
當serial port有資料時,開啟slave地址,並讀入serial port的資料並送出資料到I2C的bus,最後再結束傳輸。
Wire.onReceive(handler)
當slave接收到master的傳輸觸發中斷程序,會被呼叫的由使用者自定義的函數。根據源碼void onReceive( void (*)(int) );
,handler是使用function pointer實踐。
void myHandler(int numBytes){
Serial.println("Receive Data:");
while (Wire.available()) {
Serial.print((char)Wire.read());
}
Serial.println("");
}
在這中斷程序,一開始會接受到從master讀入byte的個數int numBytes
。Wire.available()
會回傳可讀byte數,由於布林函數0
以外的數字皆為正,因此Wire有資料會依序讀入Wire的資料到serial port並印出,直到Wire.available()
回傳0,代表可讀的byte為0,才結束中斷程序。
自己的學號字串以LCD跑馬燈顯示。
熟悉16×2 I2C LCD模組使用,與<LiquidCrystal_I2C.h>
函式庫成員函數的調用。參閱函式庫介紹How to control a character I2C LCD with Arduino。
函數 | 作用 |
---|---|
LiquidCrystal_I2C lcd(addr, col, row) | 創建物件 |
lcd::init() | 初始化 |
lcd::backlight() | 開啟背光 |
lcd::begin(cols, rows); | 定義LCD的長寬(n列×n行) |
lcd::clear() | 清空LCD |
lcd::setCursor(col, row); | 設定輸入值起始位置 |
lcd::print(text string); | 在螢幕上顯示資料 |
lcd::scrollDisplayRight(); | 將畫面向右捲動,可搭配delay()使用 |
lcd::scrollDisplayLeft(); | 將畫面向左捲動,可搭配delay()使用 |
lcd::home() | 使光標移至左上角 |
lcd::cursor() | 顯示光標 |
lcd::noCursor() | 關閉上一個功能 |
lcd::blink() | 顯示一整塊長方形方格的光標 |
lcd::noBlink() | 關閉上一個功能 |
lcd::display() | 顯示所有print到LCD的東西 |
lcd::noDisplay() | 關閉上一個功能 |
lcd::write() | 與print()不同點在於是直接寫進LCD,而非人所看到的資料,例如lcd.write(64); 是印ASCII Code 64號的@ 符號到LCD上 |
lcd::autoscroll() | 下一位輸入值會把前一位輸入值往旁邊一個推 |
lcd::noAutoscroll() | 關閉上一個功能 |
lcd::leftToRight() | 之後的文字會從光標右邊開始寫 |
lcd::rightToLeft() | 之後的文字會從光標左邊開始寫 |
lcd::lcd.createChar(custom characters, byte[]); | 先自定義5 x 8 led matrix的像素點陣列,並重新定義字元 |
// Make custom characters
byte Heart[] = {
B00000,
B01010,
B11111,
B11111,
B01110,
B00100,
B00000,
B00000
};
// Create new characters
lcd.createChar(0, Heart);
//use the number of the custom character that we want to display
lcd.setCursor(0, 1);
lcd.write(0);
利用keypad與LCD實現二項加減乘除。
此部分為lab4功能的子集合。
利用keypad與LCD實現四則運算計算機。
輸入值 | 功能 |
---|---|
A |
|
B |
|
C |
|
D |
|
# |
|
* |
清除 |
if (enter == '#') {
infix[count] = '\0';
break;
} else if (enter == 'A') {
infix[count] = '+';
當有輸進值enter != NO_KEY
時,對輸入字串進行替換,以符合原始資料,並顯示在LCD上lcd.print(infix[count]);
,當出現#
則代替為終止字符,並跳出無窮迴圈。
double pfix_parser(char *ifix);
四則運算為資料結構(DS)介紹stack功能章節實作。
infix轉postfix
'n'
因此可接受多位數值)(
。)
輸出堆疊中的運算子至左括號(
。postfix轉value
把運算元push進或pop出stack直接計算。
簡述I2C如何藉由SCL
與SDA
傳送資料 ?
I2C 是序列式的傳輸,只有兩條線,一條叫做SDA
送資料,另一條叫做SCL
傳clock。
Wire.begin();
//初始化Wire.h並作為master or slave加入I2C bus
//如未指定7-bit slave地址(函數重載),則為加入者為master
Wire.beginTransmission(SLAVE_ADDRESS);
//傳slave地址
SCL=high + SDA falling
。R/W bit
,指定要讀(0:write)或寫(1:Read)資料。SCL=high + SDA raising
。ACK
一次確認SDA
主動權由master交給slave控制。Wire.write(INNER_REGISTER_ADDR); //指定slave內部寄存器地址
SCL=high + SDA falling
。SCL=high + SDA raising
。ACK
一次確認
SDA
主動權由master交給slave控制。SDA
拉低。Wire.write(0); //寫入1個byte的數據
Wire.endTransmission(false);
//If false, endTransmission() sends a
//restart message after transmission.
Wire.requestFrom(SLAVE_ADDRESS, DATA_SIZE, true);
if (Wire.available()) {
Serial.print("Data returned: ");
while (Wire.available()) {
//return the number of bytes available for reading.
Serial.print( (char)Wire.read() );
}
Serial.println();
}
Wire.endTransmission(); //結束傳輸
SCL=high + SDA falling
。R/W bit
去判斷資料要傳送還是發送,每次傳送8-bit資料。SCL=high + SDA raising
。ACK
一次確認
SDA
主動權由master交給slave控制。SDA
拉低。SCL
先拉高,隨後SDA
跟著拉高。上課的時候提到可以有多個master,請問若同時有兩個以上的master送出訊號會發生什麼事?並且描述該如何解決。
在各種bus arbitration機制中,最基本的禮貌就是"先聽再說":當你要說話時,先聽聽有沒有別人在說話,如果有的話就閉嘴,如果沒有別人在說話,你才可以說話。根據 I2C 的規格說明,需要兩個機制:時脈同步(clock synchronization)和匯流排仲裁(bus arbitration)。
時脈同步(clock synchronization)
時序可分為兩種master clock和slave clock,而在時脈同步系統中,master經由網路伺服器、GPS接收器等裝置得到準確時序,再將此時序連接到各個slave端,以確保整個系統的時序是精準且同步的。master一天之中會多次更新時脈訊號給slave,避免slave時序延遲。
匯流排仲裁(bus arbitration)
在匯流排中用於初始化資料傳輸的裝置稱為主匯流排(bus master)。當一個系統會有超過一個主匯流排時,這些裝置間會互相分享匯流排系統,即當目前的主匯流排已經交出主導權時,其餘的匯流排可以獲得處理器的控制權,bus仲裁即為主匯流排間互相交換主控權的過程。
其中有兩種Bus仲裁方式,分別為 :
ref :
電算機為何總和超過32767
時字串會溢位?如何解決?
Arduino中,int
的資料型態為signed 16bits
,因此當數值超過float
、double
、long
。
Arduino中常見的資料型態 :
資料型態 | 說明 | 記憶體長度 | 數值範圍 |
---|---|---|---|
boolean |
布林 | 8 bits | true (1, HIGH), false(0, LOW) |
byte |
位元組 | 8 bits | 0~255 |
char |
字元 | 8 bits | -128~127 |
short |
短整數 | 16 bits | -32768~32767 |
word |
字 | 16 bits | 0~65535 |
long |
長整數 | 32 bits | -2147483648~2147483647 |
float |
浮點數 | 32 bits | ±3.4028235e+38 |
double |
倍精度浮點數 | 32 bits | ±3.4028235e+38 |
double與float皆相同,根據官方doc定義
Double precision floating point number. On the Uno and other ATMEGA based boards, this occupies 4 bytes. That is, the double implementation is exactly the same as the float, with no gain in precision.
On the Arduino Due, doubles have 8-byte (64 bit) precision.
雙浮點精度由3個部分包含 :
組成 | 功能 |
---|---|
sign bit (符號) | 正負號 |
exponent (指數) | 次方數 |
mantissa (尾數) | 精確度 |
I2C傳輸協定,在之前用MPU6050
時,已有簡單的討論,其優點在於接線數較少,實作中遇到了一些內建函數回傳值的問題,輸出回傳值後即了解,slave端接收訊號時,會計算接收位元組數進行回傳。
LCD的部分,相較於七段顯示器,LCD較好操作。LCD模組配有掃描式驅動IC,並配合每pixel之電容,可讓面板上的數值持續顯示,不像七段顯示器要用迴圈的方式才能使數值常駐顯示。而四則運算的原理,在資料結構中有學過,先將輸入字元重新排列後,再以push、pop的方式處理。直接使用資料結構之程式碼,在調整LCD顯示問題即可完成。
此次實驗為操作LCD與利用I2C進行Arduino間通訊,使用的是Wire.h
函式庫,比較上次實驗是利用SPI進行通訊,主要使用的函式庫式是SPI.h
。因為LCD本身有自定義的函式庫,只需要事先利用LiquidCrystal_I2C.h
函式庫設定LCD位址,再把需要顯示的字元print出就可以顯示在螢幕上。
做個小整理。
延續lab2學UART(Serial Monitor)、lab3學SPI(傳字串),這次lab4學I2C(傳字串)三大傳輸協定是算是告一段落,原理介紹文章可參閱【Maker 電子學】作者Bird,而對應相關模組 :
介面 | 外接線 | 模組 |
---|---|---|
UART | RX(接收)、TX(發送) | 藍芽HC-05 (lab3) |
I2C | SDA(資料)、SCL(時脈) | 三軸陀螺儀與三軸加速計MPU6050 (lab2)、顯示器16*2 I2C LCD (lab4) |
SPI | MOSI(主出從入)、MISO(主入從出)、SCLK(時脈)、SS(選擇1)、SS2(選擇2)… | 無 |
3者對應使用到的函式庫 :
<SoftwareSerial.h>
enables serial communication with a digital pin other than the serial port.The native serial support happens via a piece of hardware (built into the chip) called a UART.<Wire.h>
communicate with I2C / TWI devices.<SPI.h>
communicate with SPI devices, with the Arduino as the master device.