基礎教程12 霍爾感測器與Arduino外部中斷
來自專欄 Arduino 魔法書8 人贊了文章
我是潘,曾經是個工程師。這是為 http://Ardui.Co 製作的 「Arduino 公開課」 系列的入門教程。第5.1課介紹了溫控風扇,這次我們要通過中斷函數並使用霍爾感測器測量風扇速轉。有任何疑問請在評論區提出,我會逐一回答。
霍爾感測器是一種磁感應元件,只要有磁場(磁鐵、金屬物體、電感)接近,就會產生相應的電平信號。霍爾感測器應用很廣,電機/車輪測速、電流測量、運動與接近檢測等。汽車、航天、工業製造等領域都是不可或缺的元件。
常見的霍爾感測器模塊分為兩種,一種是線性模塊,根據磁感應強度輸出相應的模擬量(電壓),另一種是開關型模塊,磁感應達到一定強度,輸出一個脈衝信號。另外,線性模塊也可以採用數字通信,只不過中間加入了 ADC 模塊,主要用在需要高精度、高穩定性的測量場合。
這是最常見的霍爾感測器模塊,採用A3144E霍爾感測器原件,同時提供了模擬和數字信號輸出。測速原理是,在轉盤上設置磁感應點,每次磁場穿過感測器時,輸出一個脈衝信號:
感測器的接線很簡單,除了兩根電源線,將 Vin 接入 Arduino 的 D3(3號數字埠)即可,如果出現傳輸信號不穩定的情況,可以接一隻 5K 上拉電阻。
在風扇轉盤的邊緣貼上1顆小磁鐵,然後將感測器固定在上方,記錄每秒圈數乘以60,即可換算出 rpm (每分鐘轉速)。
速度是距離/次數與時間的比值,因此程序要設計兩個變數,一個計時,另一個計數。
計時用到 millis(),該函數的作用是記錄系統累計運行時間,返回毫秒(值類型為 unsigned long),最長紀錄為50天,超時後返回0秒。如果要更精確,可以用micro()返回微秒。
millis() 返回的是累計時間,但我們要測量輸出的是每一秒內的旋轉次數,怎麼處理呢?其實,只要設置一個臨時變數,標記下一秒的時間點,當 millis() 到達這個時間點後,輸出 milli() 至 millis() + 1000ms 之間的觸發次數即可。
同時,我們採用外部中斷函數計數。當感測器被磁鐵觸發,程序中斷,執行計數函數,記錄一次。
/* 作者:Ardui.Co 效果:霍爾感測器測速 版本:1.0 更新時間:2017年2月14日*/const byte interruptPin = 3;const long taketime = 1000; // 每次測量的時間unsigned long time; //設置變數 time,計時float Val = 0; //設置變數 Val,計數void setup() { Serial.begin(9600); attachInterrupt(digitalPinToInterrupt(interruptPin), count, FALLING);//觸發信號必須是變化的,上升或下降皆可 time = millis();//開始計時,time獲得當前系統時間} void loop() { if (millis() >= time) { Serial.println(Val*60);//轉換成rpm,單磁鐵觸發解析度為60rpm,2個磁鐵為30rpm time = millis() + taketime;//標記未來的時間點,1000ms後執行if判斷,輸出結果。另,降低刷新頻率,可以提高解析度 Val = 0;//輸出速度結果後清零,記錄下一秒的觸發次數 }} void count() { Val += 1;}
使用 digitalPinToInterrupt(interruptPin) 原因是提高程序的兼容性,只要修改interruptPin 即可在不同 Arduino 版本上映射對應的中斷埠號。
特別注意的是,觸發外部中斷函數的模式,必須是 RISING 或者 FALLING。如果是CHANGE,感測器每一次觸發都會產生「高 – 低」、「高 – 低」兩個信號,也就是說函數會被觸發兩次;如果是LOW,磁鐵穿過感測器的過程中,外部中斷函數會被連續觸發,這個時間內 count() 會多次計數;
上面程序使用未來的時間點 millis() + 1000ms 來控制中斷,但還有一種演算法,就是當前時間去比較過去的時間,將時間範圍控制在1000ms以內。
/* 作者:Ardui.Co 效果:霍爾感測器測試方法2 版本:1.0 更新時間:2017年2月19日*/const byte interruptPin = 3;const long taketime = 1000; // 每次測量的時間unsigned long time; //設置變數time,計時float Val = 0; //設置變數Val,計數void setup( ) { Serial.begin(9600); attachInterrupt(digitalPinToInterrupt(interruptPin), count, FALLING);//觸發信號必須是變化的,上升或下降皆可 time = millis( ); //開始計時,time獲得當前系統時間}void loop( ) { if (millis( ) - taketime < time) return;//當前時間減測量時間,獲得1000ms前的時間點,時間未到則跳出 Serial.println(Val * 60); time = millis( );//輸出結果後,獲取當前時間 Val = 0;}void count() { Val += 1;}
兩個程序效果是完全一樣的,但從理解上,一個是用未來時間點來規定測量時間,一個是用過去時間點。
https://www.zhihu.com/video/1000111692763779072
大部分 PC 散熱風扇內置了霍爾感測器,我們用示波器測量第3個引腳:
每 10~12 ms 出現一個波谷,轉速約為 900~1000rpm,與剛才測量的結果有一些出入。(請思考為什麼?答案會在評論區介紹)
降低刷新速度或者增加觸發點,都能提高測量的解析度。比如,對於轉速特別低的設備,每秒不到1圈,我們將刷新率降到5秒,解析度就能提高至 60rpm / 5 = 12rpm。也可以增加 n 個磁感應點,每次觸發低電平信號,則記錄 1/n 圈,解析度也相應提高到1/n 。
現在,我們通過外部感測器產生中斷請求,然後來計數。那麼,是否可以由內部計時,定時來觸發中斷?答案是肯定的,上面兩個程序就使用了內部中斷的一種——程序定時器,它們以 millis() 計時,然後在規定的時間點上輸出結果。
但是,millis() 計時是十分不精準的,因為,雖然程序中斷也是內部中斷的一種,但是在各種中斷的優先順序別中,跟普通程序沒有任何區別,外部中斷會打斷 millis() 的計時。儘管外部中斷的時間非常短,但其執行時間不會被記入millis()。
後面將深入介紹 Arduino 的內部中斷和硬體定時器的使用。
推薦閱讀:
※如何使用 Arduino 製作一個繪圖儀
※使用Arduino製作自動寵物餵食機
※Arduino簡介
※將你的樹莓派打造成一個 Tor 中繼節點