ROHM感測器評價工具組是一種相容Arduino的擴展板(Shield),搭配有以下8款感測器:加速度感測器、氣壓感測器、地磁感測器、近接照度感測器、色彩感測器、霍爾感測器、溫度感測器和紫外線感測器。該感測器擴展板的設置指南請參閱 ROHM感測器評價工具組概述。各別感測器的Arduino程式以及詳細資料可以從官網上下載: http://www.rohm.com/web/global/sensor-shield-support
針對您想要使用的任何感測器,官網所列的程式包含了另外一個在Arduino草圖中的程式。如果您只想使用一個或二個感測器,那麼這樣做行得通,但是如果您決定讓單一Arduino UNO擴展板同時處理多個感測器(比如6個感測器)呢?!
上述連結是一個專門用於ROHMMultiSensor的GitHub程式庫,該單一Arduino程式能夠讓您控制ROHM感測器評價工具組中的所有感測器,不需要單獨包含每個感測器程式,因此可以節省您的時間。此外,該程式可讓您根據當下需求來設置感測器——這是原ROHM程式目前所缺乏的特性。該程式的另一個優點就是符合Arduino 1.5 IDE規範。它還包括自訂醒目語法、詳細的自述文件和大量實例!
我們假設您想用ROHM提供的程式來連接所有的感測器。這不僅會導致Arduino程式相當長,而且還會造成許多不必要的後台運算。比如,每個程式都有自己的I2C匯流排控制方法,但它們都相同。這些都會增加Arduino程式的整體大小和記憶體使用量。我們能不能只用一個I2C方法呢?下圖顯示了使用所需6個ROHM程式時的程式編譯器輸出:
9886位元組似乎不是很大,但是記住,該程式除了從感測器中提取資料並在序列埠上顯示之外,沒有做任何其他事情。這個數字告訴我們,儘管任務非常簡單,但是卻佔用了三分之一的儲存空間。這在添加更多功能時會造成儲存空間的問題。
現在,讓我們比較一下使用ROHMMultiSensor程式的編譯器輸出,其功能完全一樣:
您可以看到,我們節省了超過2000位元組的快閃記憶體儲存空間以及100位元組的記憶體需求!
讓我們看一下上述文章中所提到的例子,在單個Arduin擴展板上運行6個ROHM感測器。所用感測器包括:加速度感測器(kx022 – 1020)、氣壓感測器(BM1383GLV)、近接照度感測器模組(RPR-0521RS)、色彩感測器(BH1745NUC)、溫度感測器(BD1020HFV)和紫外線感測器(ML8511A)。
使用這些感測器的理由非常簡單:它們的工作電壓都是3V。由於我們只能在擴展板上為所有感測器設置一種電壓,因此我們必須使用大多數感測器能支援的電壓。使用ROHMMultiSensor程式時,代碼如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
// definition #define INCLUDE_KX022_1020 #define INCLUDE_BM1383GLV #define INCLUDE_RPR_0521RS #define INCLUDE_BH1745NUC #define INCLUDE_BD1020HFV #define INCLUDE_ML8511A // inclusion #include // instantiation KX022_1020 acc; BM1383GLV bar; RPR_0521RS als; BH1745NUC rgbc; BD1020HFV temp; ML8511A uv; void setup() { Serial.begin(9600); Wire.begin(); // initialization acc.init(); bar.init(); als.init(); rgbc.init(); temp.init(ANALOG_1); uv.init(ANALOG_2); Serial.println("X[g]\tY[g]\tZ[g]\tp[hPa]\tPS[cnt]\tALS[lx]\tR[-]\tG[-]\tB[-]\tC[-]\tt[dg C]\tUV[mW/cm^2]"); void loop() { //measurement float* accelValue = acc.measure(); float pressValue = bar.measure(); float psValue = als.measure(PS); float alsValue = als.measure(ALS); unsigned int* rgbcValue = rgbc.measure(); float tempValue = temp.measure(); float uvValue = uv.measure(); Serial.print(accelValue[0]); Serial.print('\t'); Serial.print(accelValue[1]); Serial.print('\t'); Serial.print(accelValue[2]); Serial.print('\t'); Serial.print(pressValue); Serial.print('\t'); Serial.print(psValue); Serial.print('\t'); Serial.print(alsValue); Serial.print('\t'); Serial.print(rgbcValue[0]); Serial.print('\t'); Serial.print(rgbcValue[1]); Serial.print('\t'); Serial.print(rgbcValue[2]); Serial.print('\t'); Serial.print(rgbcValue[3]); Serial.print('\t'); Serial.print(tempValue); Serial.print('\t'); Serial.println(uvValue); delete[] accelValue; delete[] rgbcValue; delay(100); } |
Arduino IDE串列繪圖器(Serial Plotter)的輸出如下圖所示。當然,這種輸出幾乎無法用在任何實務面需求。然而,它卻顯示了所有感測器都正在工作並測量資料中。
使用這個程式時,您必須按照以下五個步驟進行配置:定義、包含、產生實體、初始化和測量。這聽起來比較複雜,所以我們接下來詳細解釋每一步。
1.定義
第一步就是#define(定義)所有要使用的感測器。下面的步驟解釋了為什麼必須首先執行這些定義。
為了縮短代碼,您可以使用一個預程式設計的簡化定義。#define
INCLUDE_ALL_1V8_SENSORS#define
INCLUDE_ALL_3V0_SENSORS和#define
INCLUDE_ALL_3V0_SENSORS和#define
INCLUDE_ALL_1V8_SENSORS將會定義所有推薦供電電壓為1.8V的感測器。而電壓為3V和5V的定義語句分別為:#define
INCLUDE_ALL_3V0_SENSORS和#define
INCLUDE_ALL_5V0_SENSORS。
2.包含
這實際上就是您#include(包含)程式標頭檔案的地方。在此步驟之前必須完成所有感測器#define語句的原因很簡單:標頭檔案
(ROHMMultiSensor.h)中包含了其他的感測器特定檔,而這必須以定義適當感測器為基礎。這使得程式可以根據使用者需求動態改變其大小,從而節省Arduino快閃記憶體儲存空間和記憶體需求。
3. 產生實例
因為每個感測器都有自己的分類,因此存取其語言之前,您必須創建該類的一個實例。該過程被稱為產生實例,您所要做的就是輸入感測器分類名稱,然後緊跟實例名。比如:
BM1383GLV bar;
該語句會創建一個 BM1383GLV 類實例,而且您可以通過名稱bar存取該實例。
4. 初始化
每個感測器在使用前都必須初始化。您所要做的就是為每個感測器調用.init()方法。此方法在成功完成後返回0,如有任何問題,則返回1。您可以用以下代碼檢查所有感測器是否都已經成功初始化:
1 2 3 4 5 6 7 8 9 |
void setup() { if(bar.init() == 1) { // there was an error, you have to fix it! } // everything went fine, you can start measuring! |
.init()方法還有一個額外功能:更改感測器設置。您可以通過傳遞各種參數來改變感測器的行為。
比如我們想要改變壓力感測器的工作模式。在預設模式下,感測器會測量200毫秒,然後返回平均值。如果我們想讓感測器運行得更快(可能會變得不那麼準確),我們可以使用以下參數調用.init()方法:
bar.init(BM1383GLV_CONTINUOUS_100_MS);
現在,測量只會在100 ms的工作模式下進行。每次重新開機感測器之後,所有設置都會被重置為預設值。這通常只與整個Arduino重置(即按重新開機按鈕)相關,所以這並不是一個問題。
請參閱 GitHub 程式參考 檔,以獲得每個感測器當前已經實現的完整設置列表。
注:這些設置都是可供選擇更改的;通常,初始設置就已足夠使用(即直接調用.init(),無需任何參數)。
5.測量
此時,一切準備就緒,可以開始測量資料了。您可以通過調整感測器的.measure()方法執行此操作。該方法無需參數,其返回資料類型取決於感測器種類。有些感測器會回應單一數值:一個浮點或者一個整數。
float pressValue = bar.measure();
上述語句示例會透過BM1383GLV感測器測量壓力,並回覆成單位為hPa的壓力數值。
然而,有些感測器需要回覆多個數值。比如,KX022-1020 加速度計會測量三個軸上的減速度:X、Y和Z。顯然,我們需要使用一個陣列,以回覆所有數值。陣列所用記憶體是動態分配的,所以當我們不需要陣列時,我們必須重新釋放記憶體。
在下面的例子中,我們定義、包含、產生實例並啟動KX022-1020加速度感測器。然後,我們創建一個名為accelValue的浮點動態陣列。如果我們後面不再需要陣列,我們可以通過delete[]釋放記憶體。這樣可以確保沒有記憶體洩漏。記憶體洩漏是指動態分配的記憶體無法正確釋放,並且只要Arduino未重置就不可存取的情況。在這個例子中,您會看到如何正確釋放動態分配的記憶體。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
#define INCLUDE_KX022_1020 #include KX022_1020 acc; void setup() { Serial.begin(9600); Wire.begin(); acc.init(); Serial.println("X[g]\tY[g]\tZ[g]”); } void loop() { // dynamically create an array float* accelValue = acc.measure(); // now we can access the elements in accelValue array Serial.print(accelValue[0]); Serial.print('\t'); Serial.print(accelValue[1]); Serial.print('\t'); Serial.println(accelValue[2]); //safely deallocate the memory delete[] accelValue; delay(100); } |
如果您用過Arduino,您很可能已經見過中斷功能了。即使沒有,也請不要擔心,如果設置正確,中斷用起來很簡單。
簡單來講,中斷就是讓Arduino“跳轉”至代碼特定部分的信號。這部分代碼是一種稱為中斷服務程式的特殊函數,通常簡稱為ISR。中斷的最常見用途就是進行精確時序控制,所以ISR函數應盡可能短。此外,中斷函數不帶任何參數,也不會返回任何值。
讓我們來看一下需要正確設置中斷的BM1422GMV地磁感測器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
#define INCLUDE_BM1422GMV #include BM1422GMV mag; void isr(void) { mag.setFlagDrdy(); } void setup() { Serial.begin(9600); Wire.begin(); mag.init(isr); Serial.println("X[uT]\tY[uT]\tZ[uT]"); } void loop() { float* magValue = mag.measure(); Serial.print(magValue[0]); Serial.print('\t'); Serial.print(magValue[1]); Serial.print('\t'); Serial.println(magValue[2]); delete[] magValue; delay(100); } |
我們需要記憶體釋放(因為與加速度感測器類似,BM1422GMV會回覆三個軸的數值)以及ISR。您可以看到,中斷只是在BM1422GMV類中形成一個標誌,表示有新資料需要收集。.setFlagDrdy()方法是該程式的一部分。請注意,您必須向.init()方法提供ISR名稱。
然而,只在代碼中設置中斷服務程式是不夠的。我們需要在擴展板上選擇正確設置。
Arduino UNO具有兩個外部中斷:INT0(引腳D2)和 INT1(引腳D3)。我們必須將適當的引腳連到感測器上的INT引腳。您可以在類別的產生實例中選擇使用哪個中斷。使用Arduino引腳D2中斷的預設值為INT_0,或者如果Arduino D2上的中斷已經用於其他設置,您可以將其改為INT_1。
要將感測器中斷連至Arduino,我們應使用J3和J4排針。J3將中斷連至Arduino D2,而J4則將中斷連至D3。每個排針都有標記為INT1至INT5的引腳以及標記為INTR1至INTR5的引腳。這是因為感測器具有兩種不同的中斷輸出:
我們再看一下上述內容中的示例代碼。我們想要把Arduino的INT0連至BM1422GMV的INT引腳。假設感測器連到了I2C_1插槽。在這種情況下,我們應將INTR1引腳短接至J3。
如果任何其他感測器需要使用中斷,比如BH1745NUC色彩感測器,那麼中斷設置步驟如下所示:如果我們將感測器連至I2C_3並且要使用Arduino的中斷INT1,那麼我們應將引腳INT3短接到J4和J16上,因為該感測器需要一個外部上拉電阻。
有關中斷的詳細資訊以及針對不同感測器的設置資訊,請參考該程式README中的 注釋 部分。
1.地磁感測器(BM1422GMV)是唯一需要中斷才能工作的感測器。然而,所有其他I2C感測器也可以使用中斷。程式的運行不需要允許中斷,但是這在一些應用程式中可能非常有用。目前,除磁力計之外,程式中沒有實現其他感測器的中斷功能。
2.使用霍爾(BD7411G)感測器時,向Arduino上傳程式之前必須斷開感測器。否則,您將在上傳程式時收到以下錯誤資訊:avrdude: stk500_getsync() 錯誤。這是因為BD7411G上的OUT引腳直接連至 Arduino引腳D0,而D0還是串列RX引腳。如果沒有檢測到磁場,那麼BD7411G的輸出為(HIGH),這會阻斷所有從序列埠進入的訊息,包括草圖上傳。
3. .當使用其中一個類比感測器(BD1020HFV溫度感測器或ML8511A紫外線感測器)時,您必須提供感測器所連接的插槽名稱。擴展板上有兩個模擬插槽:ANALOG_1和ANALOG_2。為其中一個模擬感測器調用.init()方法時,您必須向.init()方法提供插槽名稱(即調用.init(ANALOG_1),以初始化連至插槽ANALOG_1的模擬感測器)。作為安全措施,如果您沒有提供任何內容,.init()的預設值將會是 ANALOG_1,然而,感測器必須連至插槽ANALOG_1。如果您從類比感測器獲取的資料很奇怪,那麼請首先確保您提供的插槽名稱與感測器相連的插槽一致。
本文的主要目的是為ROHM原始程式碼提供一種潛在的替代方案。顧名思義,如果您需要在ROHM感測器評價工具組中處理多個感測器,那麼ROHMMultiSensor程式將會非常有用,您的下一個大型專案可能會需要這個程式!
如果您有任何改進建議,請上GitHub進行分享:收集修改意見;或者在 問題(Issues)選項卡中建立GitHub問題。要建立一個GitHub問題非常簡單,您的回饋對我們來說非常有價值。另外,如果您喜歡這個程式,也讓您的代碼因此變得更漂亮,那麼請給ROHMMultiSensor檔案庫一顆星,然後盡情地開發吧!