完成以上 8 章之後, 要進行 arduino 之間的 i2c 通訊, 應該沒有難度吧.
但有個問題, 為什麼要以 i2c 通訊呢?
因為有很多原因, 單一片 arduino 不足以滿足要求, 比如 I/O 不足, 又或有些程式太煩覆, 希望可以拆開, 以多片 arduino 合成一個大的系統.
比如你想建立一個超多傳感的測量系統, arduino 的 I/O 絕對不能滿足. 而且, 要安裝不同的庫, arduino 的記憶體也是一個限制.
如果可以把工作分開, 由多個 arduino 板子組成一個更大的系統, 當中由一個 master 板子, 執行主程式, 其他 slave 去配合讀取不同的資料.
當然, 現在的 arduino, 一般只有一組 i2c, 所以整個系統就只可以有一組 i2c bus.
而在 i2c bus 上只可以有一個 master, 所以 i2c 有關的設備, 都只可以靠 master 去處理.
另外, 相同 i2c 地址的設備, 不會因為加入多塊 arduino 板子, 而可以同時使用.
這個缺憾, 需要有另一個系統去配合.
比如在 i2c 之外, 再加上SPI 的通訊, 就可以把 i2c 都分開了.
這個複雜的系統, 之後再去探討吧. 現在以一個 i2c bus 為基礎去開始.
首先, 嘗試做一個簡單系統. 假設你的板子 I/0 都用盡了, 你還想加入一個 DHT11 的溫濕度測量. 你可以簡單加入一片 mini 或 nano 的板子, 以 i2c 跟主板連上, 去擴充你的 I/O.
最簡單是修改 "由 master 向 slave 要求資料回傳" 的例子.
當 master 向 slave 發出請求時, 配合 "(8) 浮點的傳送" 的例子, 就可以把 溫濕度 數據以直接回傳.
由於某些 設備的庫, 不一定可以在 interrupt 內執行的, 可以嘗試在 slave 的 loop 內不斷更新, 當收到 master 的請求時, 就把最後的資料發過去.
詳情大家可以在相關程式中看到, 執行後, 在 master 隨意發送一些資料, slave 就會回傳.
而 slave 的 serial monitor 亦會同時顯示出已發送的資料供對比:
用類似的方法, 就可以把 非 i2c 的傳感, 放到另一片 arduino 中去讀取數據, 甚至先進行簡單分析. 例如 濾波.
一片 arduino slave 板子, 可以同時接上多個 非 i2c device 呢. 其主力工作, 就是為主程式準備所需的資料.
當 主程式需要時, 就可以向 slave 直接讀取了. 主程式亦不需要加入有關的庫, 會變得簡單一點.
當然, 這不是沒有付出的, 系統上, 就要增加了 arduion 板子. 是否值得, 還是看需要吧.
相關程式下載:
2017年8月30日 星期三
Arduino 之間的 I2C 通訊 (8) 浮點的傳送
之前有點忙, 在準備 "Arduino 之間的 I2C 通訊 (7) 單片機有效傳送數據的選擇" 中, 突然停了一下, 沒想到一停就停了一年多.
當中留下了一個問題給看官思考的, 就是有關 浮點的傳送.
網上經常會看到, 有人問用 串口通訊時, 如何把 浮點 的數據送出去?
一般的答案, 都會是轉成字串發送吧, 例如 "123.45".
如果需要的浮點, 有固定的大小, 精確度有限, 這個有可能失真的方法還不錯吧.
為什麼說"失真'呢? 因為原本的數值, 小點後可能是連續很長的, 要轉成字串, 就要把長度限制了, 自然會有失真.
但大家有否考慮過, 可以簡單又完全不失真的傳送呢?
注意, 這裡的"不失真", 只是跟原來儲存的數值比較, 並非所有數值都可以.
如果原來儲存的數值已經是有"失真"的話, 傳送時也只會原原本本的發過去.
這是什麼意思呢?
簡單說, 要記錄 pi, 用 float 本身就有精確度的限制, 發送出去的 float, 絕不可能突然變成 double 的精度吧. 可以做到的, 就是你給我一個 float, 我就把這個 float 原整的發出去. 詳細情況, 可以在最後的例子中看到.
如果要轉成字串, 要用多少個字符才足夠呢? 100? 1000? 就是一萬也不一定是完全一樣.
但如果有考慮過電腦內的記憶體的運作, 無論發送什麼數值的 float, 也只需 4 個 byte 就可以了.
為什麼? 發送 123.45 有 5 個數字, 還要記下小數位, 也只需 4 bytes?
沒錯, 就是 4 bytes, 這沒有什特別, 因為原本用來儲存 float 的記憶體, 就只需要 4 bytes.
只要我們把這 4 個 bytes 發出去, 就可以原原本本的把那個數值傳過去了.
但怎樣可以把這 4 個 bytes 發出去, 在 接收端得到一個 float 呢?
看看 wire 或 serial 有關的 function, 都沒有以 float 作為參數或結果的, 那可以怎樣做到.
學習 c 的人, 應該不會有問題吧. c 的 pointer 可是超好用的東西.
大家可以看看 wire 的庫, write 的方法之中, 其中一個就是:
比如 你的資料儲存在 float data = 123.45678, 當中 data 是一個 float.
只要用 wire.write((uint8_t *) &data, 4) 就可以把它的值, 原原本本的發出去.
注意: 由於 data 是 float, &data 是 (float *), c 是不會主動把 (float *) 轉成 (uint8_t *) 的, 所以需要自己指定轉換.
之後, 在接收端收到 4 個 byte, 用相反的方法, 把 4 個 byte 用 pointer 放進 float 對應的記憶體, 就可以還原了.
文字很難清楚說明, 還是直接看程式碼吧.
slave 回傳 float
以下是一個收到任何請求, 都回傳一個 float 數值的例子,
master 接收 float
下面是對應的 master 程式例子:
以上範例, 只在於說明如何發送參數給 slave 使用, 當中並沒有加入錯誤的檢測.
執行之後, 你會得到以下結果:
DF E9 F6 42 就是記憶體中, 用來儲存 123.45678 的 4 個 bytes 了.
但...為什麼 Data value 是 123.456779, 而不是 123.456780 呢? 不是說不會"失真"嗎?
這裡那 0.000001 的差別, 並非傳送時的失真, 而是 float 本身的失真的.
在 arduino 的 float, 是不能準確記下 123.456780 的, 其精確度所限, 會記錄成 123.456779.
正如上面說過, 傳送過程是會原原本本的發過去, 結果就是 123.456779 了.
不信的話, 你可以試試在 slave 的程式中, 把 data 以 6 位小數印出來看看吧.
相關程式下載:
當中留下了一個問題給看官思考的, 就是有關 浮點的傳送.
網上經常會看到, 有人問用 串口通訊時, 如何把 浮點 的數據送出去?
一般的答案, 都會是轉成字串發送吧, 例如 "123.45".
如果需要的浮點, 有固定的大小, 精確度有限, 這個有可能失真的方法還不錯吧.
為什麼說"失真'呢? 因為原本的數值, 小點後可能是連續很長的, 要轉成字串, 就要把長度限制了, 自然會有失真.
但大家有否考慮過, 可以簡單又完全不失真的傳送呢?
注意, 這裡的"不失真", 只是跟原來儲存的數值比較, 並非所有數值都可以.
如果原來儲存的數值已經是有"失真"的話, 傳送時也只會原原本本的發過去.
這是什麼意思呢?
簡單說, 要記錄 pi, 用 float 本身就有精確度的限制, 發送出去的 float, 絕不可能突然變成 double 的精度吧. 可以做到的, 就是你給我一個 float, 我就把這個 float 原整的發出去. 詳細情況, 可以在最後的例子中看到.
如果要轉成字串, 要用多少個字符才足夠呢? 100? 1000? 就是一萬也不一定是完全一樣.
但如果有考慮過電腦內的記憶體的運作, 無論發送什麼數值的 float, 也只需 4 個 byte 就可以了.
為什麼? 發送 123.45 有 5 個數字, 還要記下小數位, 也只需 4 bytes?
沒錯, 就是 4 bytes, 這沒有什特別, 因為原本用來儲存 float 的記憶體, 就只需要 4 bytes.
只要我們把這 4 個 bytes 發出去, 就可以原原本本的把那個數值傳過去了.
但怎樣可以把這 4 個 bytes 發出去, 在 接收端得到一個 float 呢?
看看 wire 或 serial 有關的 function, 都沒有以 float 作為參數或結果的, 那可以怎樣做到.
學習 c 的人, 應該不會有問題吧. c 的 pointer 可是超好用的東西.
大家可以看看 wire 的庫, write 的方法之中, 其中一個就是:
virtual size_t write(const uint8_t *, size_t);只要把 const uint8_t * 指到要發出的 float 去, size_t 設定為 4, 不就可以把 float 的 4 個 byte 發出去了嗎?
比如 你的資料儲存在 float data = 123.45678, 當中 data 是一個 float.
只要用 wire.write((uint8_t *) &data, 4) 就可以把它的值, 原原本本的發出去.
注意: 由於 data 是 float, &data 是 (float *), c 是不會主動把 (float *) 轉成 (uint8_t *) 的, 所以需要自己指定轉換.
之後, 在接收端收到 4 個 byte, 用相反的方法, 把 4 個 byte 用 pointer 放進 float 對應的記憶體, 就可以還原了.
文字很難清楚說明, 還是直接看程式碼吧.
slave 回傳 float
以下是一個收到任何請求, 都回傳一個 float 數值的例子,
#include#define SLAVE_ADDRESS 0x12 #define SERIAL_BAUD 115200 #define I2C_BUFFER_SIZE 32 uint8_t i2cBuffer[I2C_BUFFER_SIZE]; uint8_t i2cBufferCnt = 0; float data = 123.45678; void setup() { Wire.begin(SLAVE_ADDRESS); // join I2C bus as a slave with address 1 Wire.onRequest(requestEvent); // register event Serial.begin(SERIAL_BAUD); Serial.println("I2C Slave.08 started\n"); } void loop() { } void requestEvent() { Wire.write((uint8_t *) &data,4); }
master 接收 float
下面是對應的 master 程式例子:
#include#define SLAVE_ADDRESS 0x12 #define SERIAL_BAUD 115200 #define DATA_SIZE 4 void setup() { Wire.begin(); Serial.begin(SERIAL_BAUD); Serial.println("I2C Master.08 started"); Serial.println(); } float data = 0; void loop() { if (Serial.available()) { Wire.requestFrom(SLAVE_ADDRESS, DATA_SIZE); Wire.beginTransmission(SLAVE_ADDRESS); data = 0; uint8_t *ptr = (uint8_t *) &data; if (Wire.available()) { Serial.print("Data returned: "); while (Wire.available()) { uint8_t b = Wire.read(); *ptr++ = b; Serial.print(b, HEX); Serial.print(" "); } Serial.println(); Serial.print("Data value: "); Serial.println(data, 6); } Wire.endTransmission(); while(Serial.available()) Serial.read(); // Clear the serial buffer, in this example, just request data from slave } }
以上範例, 只在於說明如何發送參數給 slave 使用, 當中並沒有加入錯誤的檢測.
執行之後, 你會得到以下結果:
Data returned: DF E9 F6 42
Data value: 123.456779
DF E9 F6 42 就是記憶體中, 用來儲存 123.45678 的 4 個 bytes 了.
但...為什麼 Data value 是 123.456779, 而不是 123.456780 呢? 不是說不會"失真"嗎?
這裡那 0.000001 的差別, 並非傳送時的失真, 而是 float 本身的失真的.
在 arduino 的 float, 是不能準確記下 123.456780 的, 其精確度所限, 會記錄成 123.456779.
正如上面說過, 傳送過程是會原原本本的發過去, 結果就是 123.456779 了.
不信的話, 你可以試試在 slave 的程式中, 把 data 以 6 位小數印出來看看吧.
相關程式下載:
2017年8月22日 星期二
顯示器(一) Nokia 5110 (84x48 LCD)
Nokia 5110 既 LCD 都有好多唔同款既, 而 pin 位都有少少分別.
小弟買左既就係呢款, 接線亦會以呢款為跟據:
基本資料:
相關資料下載:
接線方法:
除特定連接 (例 D3-D7) 外, 一般例子都以 SPI 接法, 轉化如下:
(以上連接, 同一般 SPI 庫用 D10-D13 有D唔同, 但搵唔番出處了, 有待再驗證.)
小弟買左既就係呢款, 接線亦會以呢款為跟據:
基本資料:
相關資料下載:
- 舊庫 LCD5110_CN.zip (下載)
- LCD5110_Basic (下載)
- LCD5110_Graph (下載)
- LCD5110_SPIflash (下載; 需配合 SPIflash 下載)
接線方法:
除特定連接 (例 D3-D7) 外, 一般例子都以 SPI 接法, 轉化如下:
| 5110 | Mapped | UNO |
|---|---|---|
| RST | RST | D11 |
| CE | CS | D12 |
| DC | DC | D10 |
| Din | MOSI | D9 |
| Clk | SCK | D8 |
| Vcc | Vcc | 3.3V/5V |
| BL | BL | 3,3V |
| Gnd | Gnd | GND |
(以上連接, 同一般 SPI 庫用 D10-D13 有D唔同, 但搵唔番出處了, 有待再驗證.)
好可惜...我塊 5110 唔知乜事死左, 要買過塊再試.
2017年3月17日 星期五
Arduino 溫度濕度 DHT11
測量溫度, 濕度 基本上係大路野, 而比較便宜既傳感, 應該可以話係 DHT11 了.
一般平既可以買到有燈同無燈既版本, 多盞燈大約會貴 1 蚊左右, 但唔會話準左既.
有樣特別, 唔知係咪我買到既有問題, 有燈同無燈, 安裝係相反既.
至於邊隻準D, 真係唔知喇. 但係 DHT11 既模塊, 讀數都幾參差.
我買左三隻, 其中一隻有燈, 用 Adafruit 既 庫, 三隻既讀數都有差別.
溫度係 22-25 度, 濕度係 44-48%, 比較特別係 溫度高左既濕度就比較低.
由於我都唔知正確既溫度同濕度係幾多, 無辦法比較.
遲下買隻號稱準好多既 DHT22 番黎, 再比較一下.
基本資料:
相關資料下載:
DHT11 (AOSONG 版本, D-Robotics 版本, 中文版本)
DHT11 既庫太多了, 暫時用個大路既, Adafruit 出品.
接線方法:
得三隻腳, {+,Out,-} 或 {VCC, DATA, GND} , 由於係 數字輸出, 接 D? 腳, 測試用 D2.
測試程式:
在 Adafruit 庫中, 已有 DHTtester 的程式.
一般平既可以買到有燈同無燈既版本, 多盞燈大約會貴 1 蚊左右, 但唔會話準左既.
有樣特別, 唔知係咪我買到既有問題, 有燈同無燈, 安裝係相反既.
至於邊隻準D, 真係唔知喇. 但係 DHT11 既模塊, 讀數都幾參差.
我買左三隻, 其中一隻有燈, 用 Adafruit 既 庫, 三隻既讀數都有差別.
溫度係 22-25 度, 濕度係 44-48%, 比較特別係 溫度高左既濕度就比較低.
由於我都唔知正確既溫度同濕度係幾多, 無辦法比較.
遲下買隻號稱準好多既 DHT22 番黎, 再比較一下.
基本資料:
| 電壓: | 3.3-5V |
| 溫度測量範圍: | 20%-95%(0度-50度范围)误差:+-5% |
| 濕度測量範圍: | 0度-50度 温度测量误差:+-2度 |
| 输出形式: | 数字输出 |
相關資料下載:
DHT11 (AOSONG 版本, D-Robotics 版本, 中文版本)
DHT11 既庫太多了, 暫時用個大路既, Adafruit 出品.
接線方法:
得三隻腳, {+,Out,-} 或 {VCC, DATA, GND} , 由於係 數字輸出, 接 D? 腳, 測試用 D2.
| UNO | DHT11 |
|---|---|
| D2 | Out/Data |
| 3.3V / 5V | +/VCC |
| GND | -/GND |
測試程式:
在 Adafruit 庫中, 已有 DHTtester 的程式.
2017年3月13日 星期一
Arduino 時間系列 (二) DS1307
除左 DS3231 外, 比較常用既時鐘模塊應該算係 DS1307
基本資料:
相關資料下載:
DS1307 既庫又係超多, 以下只係我用開既, 唔代表係最好既
DS1307 Datasheet
接線方法:
I2C 的標準接法.
測試程式:
在相關庫的 examples 中.
基本資料:
| 電壓: | 3.3V |
相關資料下載:
DS1307 既庫又係超多, 以下只係我用開既, 唔代表係最好既
- DS1307 (來源: http://www.rinkydinkelectronics.com/library.php?id=34 )
- RTClib (來源: https://github.com/adafruit/RTClib )
DS1307 Datasheet
接線方法:
I2C 的標準接法.
| UNO | HS-SR04 |
|---|---|
| SQ | |
| DS | |
| A5 | SCL |
| A4 | SDA |
| 3.3V | VCC |
| GND | GND |
| BAT |
測試程式:
在相關庫的 examples 中.
Arduino 時間系列 (一) DS3231
今次要講既係時鐘模塊 DS3231
基本資料:
相關資料下載:
DS3231 既庫真係超多, 以下只係我用開既, 唔代表係最好既
DS3231 (來源: https://github.com/NorthernWidget/DS3231 )
RTClib (來源: https://github.com/adafruit/RTClib )
DS3231 Datasheet
接線方法:
I2C 的標準接法.
測試程式:
在相關庫的 examples 中.
基本資料:
| 電壓: | 3.3V |
相關資料下載:
DS3231 既庫真係超多, 以下只係我用開既, 唔代表係最好既
DS3231 (來源: https://github.com/NorthernWidget/DS3231 )
RTClib (來源: https://github.com/adafruit/RTClib )
DS3231 Datasheet
接線方法:
I2C 的標準接法.
| UNO | HS-SR04 |
|---|---|
| 32K | |
| SQW | |
| A5 | SCL |
| A4 | SDA |
| 3.3V / 5V | VCC |
| GND | GND |
測試程式:
在相關庫的 examples 中.
2017年3月5日 星期日
旋轉編碼器 KY-040
呢隻野, 真係比佢玩死. 之前做 CNC 手控時, 買左黎玩.
但係寫完程式測試, 一直都唔可以成功量度到轉動既訊號, 經常無故自動亂跳.
最近手痕再攞出黎玩, 見 Youtube 有幾清楚既教學 (https://www.youtube.com/watch?v=J9cDEef0IbQ), 就跟住做一次.
點知, 結果都係一樣. 其實個程式同我之前既做法差唔多.
心諗無理由我買幾人個都係壞既, 而且後尾發現, 原來只要我隻手放迎近佢就會亂跳, 好奇怪. 開始懷疑係線路上有問題.
用另一部機試, 完全正常...omg, 比佢玩左我好耐, 原來係部機既 USB 有問題.
只係知有問題, 唔知乜野問題.
再試, 改下個程式用 TFT 輸出, 外部比電, 完全正常.
之前買左個 USB 数字隔离器 諗住比部 CNC 用, 但最後都唔關事. 手痕就攞出黎試下, 果然係掂. 似係 arduion 受電腦經 USB 干擾....
"肺話"講完, 入正題, 講番點用, 唔係第日又唔記得.
測試程式可以用番上面條 Youtube 片果個, 又或者用我改左經 UsartGPU TFT 輸出既.
接線方法:
注意, 呢度既 CLK 同 DT, 其實個名有點亂, 應該即係兩個感應器既輸出, pinA 同 pinB.
佢既原理在 Youtube 片講得好清楚了, 主要係靠兩個差 90度既感應, 從而量度出旋轉既步數, 及推算方向 (順時針/逆時針). 所以, 就算 CLK 同 DT 交換插都唔會有問題, 只係可能順/逆既方向調轉左.
但係寫完程式測試, 一直都唔可以成功量度到轉動既訊號, 經常無故自動亂跳.
最近手痕再攞出黎玩, 見 Youtube 有幾清楚既教學 (https://www.youtube.com/watch?v=J9cDEef0IbQ), 就跟住做一次.
點知, 結果都係一樣. 其實個程式同我之前既做法差唔多.
心諗無理由我買幾人個都係壞既, 而且後尾發現, 原來只要我隻手放迎近佢就會亂跳, 好奇怪. 開始懷疑係線路上有問題.
用另一部機試, 完全正常...omg, 比佢玩左我好耐, 原來係部機既 USB 有問題.
只係知有問題, 唔知乜野問題.
再試, 改下個程式用 TFT 輸出, 外部比電, 完全正常.
之前買左個 USB 数字隔离器 諗住比部 CNC 用, 但最後都唔關事. 手痕就攞出黎試下, 果然係掂. 似係 arduion 受電腦經 USB 干擾....
"肺話"講完, 入正題, 講番點用, 唔係第日又唔記得.
測試程式可以用番上面條 Youtube 片果個, 又或者用我改左經 UsartGPU TFT 輸出既.
接線方法:
| UNO | KY-040 |
|---|---|
| D3 | CLK |
| D4 | DT |
| D8 | SW |
| 5V | + |
| GND | GND |
注意, 呢度既 CLK 同 DT, 其實個名有點亂, 應該即係兩個感應器既輸出, pinA 同 pinB.
佢既原理在 Youtube 片講得好清楚了, 主要係靠兩個差 90度既感應, 從而量度出旋轉既步數, 及推算方向 (順時針/逆時針). 所以, 就算 CLK 同 DT 交換插都唔會有問題, 只係可能順/逆既方向調轉左.
訂閱:
文章 (Atom)





