Java Embedded (9)紅外線測距模組與類比數位轉換
|
Java Embedded (8)Raspberry Pi GPIO 的基礎應用與實作(下) << 前情 9-1 認識紅外線測距模組瞭解使用Raspberry Pi的GPIO控制LED和讀取按鈕,這一章開始說明GPIO一些比較不一樣的應用,首先是讀取距離感應器資訊。在生活中的很多裝置都需要讀取距離的資訊,最常見的就是行動電話,接電話時把電話靠近耳朵,螢幕就會自動關閉,電話離開的時候會自動開啟螢幕,所以行動電話裡面一定有一個可以偵測距離的感應器。還有自動掃地機這類裝置,它可以偵測裝置與障礙物的距離,根據這些距離的資訊,它就不會撞到牆璧或是家具。 測試距離的零件有很多種選擇,從最精確的雷射測距器,或是一般用途的超音波測距器,它們的差異是可以偵測的距離和精確度。這一章使用紅外線測距模組做為應用程式的接近感應器,透過類比數位轉換晶片,也可以偵測物體的距離。你需要準備這些需要的零件:
這些零件會用在幾個不同階段的練習,剛開始的時候不會用到全部的零件,最後的練習會連接好所有的零件,搭配寫好的應用程式,執行一些比較複雜的工作。先認識紅外線測距模組零件,這是它的外觀: 這種紅外線測距模組依照偵測距離有幾種不同的規格,這裡使用的「GP2Y0A41SK0F」可以偵測4到30公分。你可以依照實際的需求選擇不同的規格,例如「GP2Y0A21YK0F」可以偵測10到80公分。紅外線測距模組使用Japanese Solderless Terminal(JST)連接針腳,購買的時候要注意是否附這種規格的連接線。它有三個針腳: 紅外線測距模組偵測前方的物體,使用訊號針腳輸出的電壓值表示距離。輸出3.1V的時候是4公分,0.3V的時候是30公分。Raspberry Pi可以搭配類比數位轉換晶片,例如MCP3008,讀取紅外線測距模組的輸出電壓,就可以精確的算出偵測的物體距離。 不過這裡先把紅外線測距模組當作「接近感應器」來使用,Raspberry Pi的GPIO只能輸出與輸入數為的訊號,也就是只有高與低電壓兩種。設定為輸入用圖的GPIO針腳,接收到0V到1.19V範圍的電壓為低電壓訊號,接收到1.34V到3.3V範圍的電壓為高電壓訊號。 在GP2Y0A41SK0F的規格書,有一個像這樣的距離與電壓變化圖表,偵測距離為10公分的時候,輸出電壓大約為1.25V: 根據這樣的特性,使用GPIO高電壓訊號的1.34V電壓值,使用「12.3F * Math.pow(1.34, -1)」的公式,可以算出偵測距離為9.179公分的時候,GPIO可以接收到高電壓的訊號: 應用程式可以使用輸入用途的GPIO針腳,偵測紅外線測距模組的輸出針腳,接收到高電壓的時候,表示有物體接近到9.179公分。 9-2 連接紅外線測距模組Sharp GP2Y0A41SK0F紅外線測距模組採用JST)連接針腳,提供紅、黑、黃三個顏色的線路,你可以把它們插在麵包板上,再由Raspberry Pi連接需要的線路,包含5V、接地(GND)與設定為輸入用途的GPIO針腳。 為了讓測試的效果比較明顯,另外外連接一個LED,物體接近時會點亮LED。依照下面的線路圖連接所有的零件: 9-3 撰寫物體接近感應的應用程式連接好線路以後,最好再檢查一次,如果沒有錯誤的話,就可以啟動Raspiberry Pi,接下來準備撰寫應用程式。在NetBeans建立一個名稱為「DMSDemo01」的Java應用程式,記得為這個專案加入GPIO類別庫。參考列的程式碼完成這個應用程式: package dmsdemo01;
import com.pi4j.io.gpio.GpioController;
import com.pi4j.io.gpio.GpioFactory;
import com.pi4j.io.gpio.GpioPinDigitalInput;
import com.pi4j.io.gpio.GpioPinDigitalOutput;
import com.pi4j.io.gpio.PinPullResistance;
import com.pi4j.io.gpio.RaspiPin;
public class DMSDemo01 {
public static void main(String[] args) {
// 建立GPIO控制物件
final GpioController gpio = GpioFactory.getInstance();
// 建立控制GPIO_01輸入的物件,紅外線測距模組的輸出針腳
final GpioPinDigitalInput pin01 =
gpio.provisionDigitalInputPin(RaspiPin.GPIO_01,
PinPullResistance.PULL_DOWN);
// 建立控制GPIO_04,LED
final GpioPinDigitalOutput pin04 =
gpio.provisionDigitalOutputPin(RaspiPin.GPIO_04);
int counter = 0;
while (true) {
// 讀取紅外線測距模組的輸出針腳的狀態
boolean isHigh = pin01.isHigh();
// 如果有物體靠近
if (isHigh) {
counter++;
System.out.println("Approaching... " + counter);
if (counter > 2) {
break;
}
}
// 設定LED針腳的狀態與紅外線測距模組的輸出針腳一樣
pin04.setState(isHigh);
delay(500);
}
gpio.shutdown();
System.exit(0);
}
private static void delay(int ms) {
try {
Thread.sleep(ms);
}
catch (InterruptedException e) {
System.out.println(e.toString());
}
}
}
儲存與執行這個應用程式,使用一個物體或是你的手,放在紅外線測距模組前面並前後移動。接近感應器三次以後程式會自動結束。執行的結果會像這樣: Approaching... 1 Approaching... 2 Approaching... 3 如果應用程式希望在物體接近的時候,除了點亮LED外,也可以蜂鳴器發出警示聲音,就要先認識蜂鳴器這種零件。蜂鳴器有很多種規格,分為交流電與直流電,直流電的蜂鳴器也有各種不同的電壓規格。因為這裡採用GPIO輸出的針腳讓蜂鳴器發出警示聲音,GPIO輸出是直流電3.3V的電壓,所以你在選購蜂鳴器的時候,要特別留意它的電壓規格,一般販售的直流電3V到6V規格都可以使用。 蜂鳴器零件有兩個針腳,長針腳或標示為「+」的連接3.3V,短針腳連接GND,通電以後蜂鳴器就會發出聲音。這是一般蜂鳴器零件的外觀: 使用前面已經連接好的線路,參考下面的線路圖,把LED、電阻和蜂鳴器連接好。連接GPIO04針腳控制LED,蜂鳴器直接插在麵包板上,短針腳連接GND以後,長針腳連接到GPIO04針腳,不過要特別留意,連接蜂鳴器長針腳的時候,不可以經過電阻,否則電流就不夠讓蜂鳴器發出聲音: 完成零件與線路連接以後,執行之前已經寫好的程式碼,就可以執行測試的工作。在執行這個應用程式之前,先提醒一件關於蜂鳴器的事情,雖然建議你購買的蜂鳴器只有小小的一顆,不過它的聲音還蠻大的,所以你購買的蜂鳴器在頂端可能會貼一張圓形的貼紙,建議你不要把它撕下來,這樣聲音就會小一些。執行這個應用程式,測試看看物體接近紅外線測距模組的的時候,LED應該就會點亮,蜂鳴器也會發出警示聲音。 9-4 將類比訊號轉換為數位資訊Raspberry Pi提供的GPIO輸入應用,只能夠讀取數位的資訊。以GPIO輸入針腳來說,它只能夠偵測到高電壓與低電壓兩種訊號,也就是數位的1(高電壓)與0(低電壓)資訊。 這裡使用的紅外線測距模組,就是一種輸出類比訊號的零件,如果需要偵側物體的距離,之前說明的方式就沒有辦法完成。接下來使用一個常見的類比數位轉換零件「MCP3008」,把類比的資訊轉換為Raspbbery Pi可以處理的數位資訊,讓應用程式可以延伸到很普遍的類比零件與設備,例如可變電阻、搖桿、溫度感應器與紅外線測距模組。這是MCP3008晶片的外觀: MCP3008的規格是8通道10位元(bit)的數位類比轉換器,它可以輸入並轉換八個類比訊號,轉換後的數位資訊是0到1023。下面的圖型是它的針腳位置與名稱: 依照下面的線路圖,連接所有的零件: 為了讓應用程式可以更靈活的使用MCP3008,在應用程式為這個晶片建立一個包裝功能與提供服務的類別: package dmsdemo02;
import com.pi4j.io.gpio.GpioPinDigitalInput;
import com.pi4j.io.gpio.GpioPinDigitalOutput;
public class MCP3008 {
private static final boolean DEBUG = true;
/**
* MCP3008的八個輸入埠
*/
public enum MCP3008Channels {
/**
* MCP3008的八個輸入埠
*/
CH_00(0), CH_01(1), CH_02(2), CH_03(3),
CH_04(4), CH_05(5), CH_06(6), CH_07(7);
private int channel;
private MCP3008Channels(int channel) {
this.channel = channel;
}
public int getChannel() {
return channel;
}
}
// Serial data out
private GpioPinDigitalInput serialDataOutput = null;
// Serial data in、Serial clock、Chip select
private GpioPinDigitalOutput serialDataInput = null;
private GpioPinDigitalOutput serialClock = null;
private GpioPinDigitalOutput chipSelect = null;
public MCP3008(GpioPinDigitalOutput serialClock,
GpioPinDigitalInput serialDataOutput,
GpioPinDigitalOutput serialDataInput,
GpioPinDigitalOutput chipSelect) {
this.serialClock = serialClock;
this.serialDataOutput = serialDataOutput;
this.serialDataInput = serialDataInput;
this.chipSelect = chipSelect;
}
/**
* 讀取指定輸入埠的資料
*
* @param channel 輸入埠
* @return 讀取的資料
*/
public int read(int channel) {
chipSelect.high();
serialClock.low();
chipSelect.low();
int adccommand = channel;
// 0x18 => 00011000
adccommand |= 0x18;
adccommand <<= 3;
// 傳送讀取的輸入埠給MCP3008
for (int i = 0; i < 5; i++) {
// 0x80 => 0&10000000
if ((adccommand & 0x80) != 0x0) {
serialDataInput.high();
}
else {
serialDataInput.low();
}
adccommand <<= 1;
tickPin(serialClock);
}
int adcOut = 0;
// 讀取指定輸入埠的資料
for (int i = 0; i < 12; i++) {
tickPin(serialClock);
adcOut <<= 1;
if (serialDataOutput.isHigh()) {
adcOut |= 0x1;
}
}
chipSelect.high();
// 移除第一個位元
adcOut >>= 1;
return adcOut;
}
/**
* 讀取指定輸入埠的資料
*
* @param channel 輸入埠
* @return 讀取的資料(電壓)
*/
public float readVoltage(int channel) {
float result = -1;
int value = read(channel);
result = value * 3.3F / 1023;
return result;
}
private void tickPin(GpioPinDigitalOutput pin) {
pin.high();
pin.low();
}
}
完成MCP3008類別以後,其它的工作就簡單多了。參考下列的程式碼完成這個應用程式: package dmsdemo02;
import com.pi4j.io.gpio.GpioController;
import com.pi4j.io.gpio.GpioFactory;
import com.pi4j.io.gpio.GpioPinDigitalInput;
import com.pi4j.io.gpio.GpioPinDigitalOutput;
import com.pi4j.io.gpio.RaspiPin;
public class DMSDemo02 {
public static void main(String[] args) {
System.out.println("DMSDemo02 start...");
// 建立GPIO控制物件
GpioController gpio = GpioFactory.getInstance();
// 建立GPIO_04輸入針腳物件
final GpioPinDigitalInput serialDataOutput =
gpio.provisionDigitalInputPin(RaspiPin.GPIO_04);
// 建立控制GPIO_05、GPIO_01、GPIO_06輸出物件
final GpioPinDigitalOutput serialDataInput =
gpio.provisionDigitalOutputPin(RaspiPin.GPIO_05);
final GpioPinDigitalOutput serialClock =
gpio.provisionDigitalOutputPin(RaspiPin.GPIO_01);
final GpioPinDigitalOutput chipSelect =
gpio.provisionDigitalOutputPin(RaspiPin.GPIO_06);
// 建立MCP3008物件
final MCP3008 mcp3008 = new MCP3008(
serialClock, serialDataOutput, serialDataInput, chipSelect);
int counter = 0;
while (true) {
// 讀取連接到MCP3008 0號通道的紅外線測距模組
float adcValue = mcp3008.readVoltage(
MCP3008.MCP3008Channels.CH_00.getChannel());
// 轉換為距離(公分)
double distance = 12.3F * Math.pow(adcValue, -1);
if (distance < 5) {
counter++;
System.out.println("Approaching... " + counter);
if (counter > 2) {
break;
}
}
System.out.println("Distance: " +
String.format("%2.2f", distance));
delay(1000);
}
gpio.shutdown();
System.exit(0);
}
/**
* 暫停指定的時間(毫秒、1000分之一秒)
*
* @param ms
*/
public static void delay(int ms) {
try {
Thread.sleep(ms);
}
catch (InterruptedException e) {
System.out.println("================= " + e);
}
}
}
示範影片: 執行這個應用程式以後,可以在畫面上顯示目前偵測的距離。不過這裡使用的紅外線測距模組,有效範圍是4到30公分,超過這個範圍的偵測結果都要忽略。 課程相關的檔案都可以GitHub瀏覽與下載。 |

Java 學習之路









https://github.com/macdidi5/JavaEmbedded




cocolan1007
08/31不好意思請問一下~
按照老師您說的步驟把9-4 將類比訊號轉換為數位資訊的實作做完後,在樹莓派上跑出來的距離會變成Distance: infinity
請問會是因為我用的是pi3嗎?
還是程式碼哪部分需要更改?