Java Embedded (9)紅外線測距模組與類比數位轉換 by Michael | CodeData
top

Java Embedded (9)紅外線測距模組與類比數位轉換

分享:

Java Embedded (8)Raspberry Pi GPIO 的基礎應用與實作(下) << 前情

9-1 認識紅外線測距模組

瞭解使用Raspberry Pi的GPIO控制LED和讀取按鈕,這一章開始說明GPIO一些比較不一樣的應用,首先是讀取距離感應器資訊。在生活中的很多裝置都需要讀取距離的資訊,最常見的就是行動電話,接電話時把電話靠近耳朵,螢幕就會自動關閉,電話離開的時候會自動開啟螢幕,所以行動電話裡面一定有一個可以偵測距離的感應器。還有自動掃地機這類裝置,它可以偵測裝置與障礙物的距離,根據這些距離的資訊,它就不會撞到牆璧或是家具。

測試距離的零件有很多種選擇,從最精確的雷射測距器,或是一般用途的超音波測距器,它們的差異是可以偵測的距離和精確度。這一章使用紅外線測距模組做為應用程式的接近感應器,透過類比數位轉換晶片,也可以偵測物體的距離。你需要準備這些需要的零件:

  • 紅外線測距模組、Sharp GP2Y0A41SK0F,一個
  • LED,一個
  • 蜂鳴器(3V),一個
  • 電阻(220ohm),一個
  • 杜邦線(公-母),七條
  • 麵包板連接線(公-公),四條
  • 麵包板,一個
  • MCP3008、類比數位轉換晶片

這些零件會用在幾個不同階段的練習,剛開始的時候不會用到全部的零件,最後的練習會連接好所有的零件,搭配寫好的應用程式,執行一些比較複雜的工作。先認識紅外線測距模組零件,這是它的外觀:

JavaEmbedded_09_01

這種紅外線測距模組依照偵測距離有幾種不同的規格,這裡使用的「GP2Y0A41SK0F」可以偵測4到30公分。你可以依照實際的需求選擇不同的規格,例如「GP2Y0A21YK0F」可以偵測10到80公分。紅外線測距模組使用Japanese Solderless Terminal(JST)連接針腳,購買的時候要注意是否附這種規格的連接線。它有三個針腳:

JavaEmbedded_09_02

紅外線測距模組偵測前方的物體,使用訊號針腳輸出的電壓值表示距離。輸出3.1V的時候是4公分,0.3V的時候是30公分。Raspberry Pi可以搭配類比數位轉換晶片,例如MCP3008,讀取紅外線測距模組的輸出電壓,就可以精確的算出偵測的物體距離。

不過這裡先把紅外線測距模組當作「接近感應器」來使用,Raspberry Pi的GPIO只能輸出與輸入數為的訊號,也就是只有高與低電壓兩種。設定為輸入用圖的GPIO針腳,接收到0V到1.19V範圍的電壓為低電壓訊號,接收到1.34V到3.3V範圍的電壓為高電壓訊號。

在GP2Y0A41SK0F的規格書,有一個像這樣的距離與電壓變化圖表,偵測距離為10公分的時候,輸出電壓大約為1.25V:

JavaEmbedded_09_03

根據這樣的特性,使用GPIO高電壓訊號的1.34V電壓值,使用「12.3F * Math.pow(1.34, -1)」的公式,可以算出偵測距離為9.179公分的時候,GPIO可以接收到高電壓的訊號:

JavaEmbedded_09_04

應用程式可以使用輸入用途的GPIO針腳,偵測紅外線測距模組的輸出針腳,接收到高電壓的時候,表示有物體接近到9.179公分。

9-2 連接紅外線測距模組

Sharp GP2Y0A41SK0F紅外線測距模組採用JST)連接針腳,提供紅、黑、黃三個顏色的線路,你可以把它們插在麵包板上,再由Raspberry Pi連接需要的線路,包含5V、接地(GND)與設定為輸入用途的GPIO針腳。

為了讓測試的效果比較明顯,另外外連接一個LED,物體接近時會點亮LED。依照下面的線路圖連接所有的零件:

JavaEmbedded_09_05

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,通電以後蜂鳴器就會發出聲音。這是一般蜂鳴器零件的外觀:

JavaEmbedded_09_06

使用前面已經連接好的線路,參考下面的線路圖,把LED、電阻和蜂鳴器連接好。連接GPIO04針腳控制LED,蜂鳴器直接插在麵包板上,短針腳連接GND以後,長針腳連接到GPIO04針腳,不過要特別留意,連接蜂鳴器長針腳的時候,不可以經過電阻,否則電流就不夠讓蜂鳴器發出聲音:

JavaEmbedded_09_07

完成零件與線路連接以後,執行之前已經寫好的程式碼,就可以執行測試的工作。在執行這個應用程式之前,先提醒一件關於蜂鳴器的事情,雖然建議你購買的蜂鳴器只有小小的一顆,不過它的聲音還蠻大的,所以你購買的蜂鳴器在頂端可能會貼一張圓形的貼紙,建議你不要把它撕下來,這樣聲音就會小一些。執行這個應用程式,測試看看物體接近紅外線測距模組的的時候,LED應該就會點亮,蜂鳴器也會發出警示聲音。

9-4 將類比訊號轉換為數位資訊

Raspberry Pi提供的GPIO輸入應用,只能夠讀取數位的資訊。以GPIO輸入針腳來說,它只能夠偵測到高電壓與低電壓兩種訊號,也就是數位的1(高電壓)與0(低電壓)資訊。

這裡使用的紅外線測距模組,就是一種輸出類比訊號的零件,如果需要偵側物體的距離,之前說明的方式就沒有辦法完成。接下來使用一個常見的類比數位轉換零件「MCP3008」,把類比的資訊轉換為Raspbbery Pi可以處理的數位資訊,讓應用程式可以延伸到很普遍的類比零件與設備,例如可變電阻、搖桿、溫度感應器與紅外線測距模組。這是MCP3008晶片的外觀:

JavaEmbedded_09_08

MCP3008的規格是8通道10位元(bit)的數位類比轉換器,它可以輸入並轉換八個類比訊號,轉換後的數位資訊是0到1023。下面的圖型是它的針腳位置與名稱:

JavaEmbedded_09_09

依照下面的線路圖,連接所有的零件:

JavaEmbedded_09_10

為了讓應用程式可以更靈活的使用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瀏覽與下載。

http://github.com/macdidi5/JavaEmbedded

後續 >> Java Embedded (10)GPIO進階應用 – 步進馬達

分享:
按讚!加入 CodeData Facebook 粉絲群

相關文章

留言

留言請先。還沒帳號註冊也可以使用FacebookGoogle+登錄留言

cocolan100708/31

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

熱門論壇文章

熱門技術文章