Java Embedded (12)整合應用 – PiFan by Michael | CodeData
top

Java Embedded (12)整合應用 – PiFan

分享:

Java Embedded (11)控制直流馬達 – 使用L293D晶片 << 前情

12-1 PiFan介紹

雖然已經瞭解各種不同零件與模組的應用,還是需要把它們整合在一起,才能夠完成實際的專案。以自走車的應用來說,需要整合直流馬達、紅外線測距模組與應用程式,如果要製作藍牙遙控車的話,就還需要搭配藍牙模組與行動裝置應用程式。

這個專案製作一個可以控制風扇的整合應用,你可以先參考示範影片:

PiFan需要下列主要的零件:

JavaEmbedded_12_01

下列是零件與設備規格列表:

  • 步進馬達一個、28YBJ-48、四相五線
  • 步進馬達控制模組、ULN2003
  • MCP3008、類比數位轉換晶片
  • 搖桿模組
  • 直流馬達,一個
  • L293D、直流馬達控制晶片,一個
  • 兩顆三號電池與電池盒,一個
  • 杜邦線(公-母)
  • 麵包板連接線(公-公)
  • 麵包板

大部份的零件與設備,都已經在之前的內容說明與實作。因為要控制風扇的方向,使用搖桿模組是最方便的,這是它的外觀:

JavaEmbedded_12_02

這種搖桿模組的應用很常見,例如遊戲設備的操作裝置。它也是一種類比訊號的模組,所以要搭配類比數位轉換晶片(MCP3008),讀取使用者的操作。

12-2 連接PiFan的零件與線路

因為這個專案使用比較多的零件與模組,所以分別說明它們的連接方式。大部份的作法跟之前說明的一樣,不過因為需要連接比較多的GPIO針腳,所以調整模組與晶片連接到Raspberry Pi的針腳。

搖桿模組與類比數位轉換晶片(MCP3008)

搖桿模組是一種類比訊號的模組,所以要搭配類比數位轉換晶片(MCP3008),它可以讓使用者上、下、左、右操作搖桿,也可以按下搖桿的按鈕。應用程式可以透過MCP3008讀取使用者操作的資訊。依照下列的線路圖連接搖桿模組與MCP3008:

JavaEmbedded_12_03

直流馬達與直流馬達控制晶片(L293D)

連接直流馬達與直流馬達控制晶片(L293D),與之前說明的作法一樣,只有調整連接到Raspbery Pi的GPIO針腳。依照下列的線路圖連接直流馬達與控制晶片:

JavaEmbedded_12_04

步進馬達與步進馬達控制模組(ULN2003)

連接步進馬達與步進馬達控制模組,與之前說明的作法一樣,只有調整連接到Raspbery Pi的GPIO針腳。依照下列的線路圖連接步進馬達與控制模組:

JavaEmbedded_12_05

12-3 實作直流馬達控制類別(L293D)

為了讓應用程式可以方便的控制直流馬達,另外設計一個包裝L293D功能的類別。這個類別也適合使用在遙控車或自走車的應用,不過目前只有使用它控制一個直流馬達。依照下列的程式碼,設計好包裝L293D功能的類別:

package pifan;

import com.pi4j.io.gpio.GpioPinDigitalOutput;

public class L293D {

    private GpioPinDigitalOutput leftPin01, leftPin02;
    private GpioPinDigitalOutput rightPin01, rightPin02;

    public L293D(GpioPinDigitalOutput... pins) {
        if (pins != null && (pins.length == 2 || pins.length == 4)) {
            if (pins.length >= 2) {
                leftPin01 = pins[0];
                leftPin02 = pins[1];
            }

            if (pins.length == 4) {
                rightPin01 = pins[2];
                rightPin02 = pins[3];
            }            
        }
        else {
            throw new IllegalArgumentException();
        }
    }

    public void leftForward() {
        leftPin01.setState(true);
        leftPin02.setState(false);
    }

    public void leftBackward() {
        leftPin01.setState(false);
        leftPin02.setState(true);
    }

    public void leftStop() {
        leftPin01.setState(false);
        leftPin02.setState(false);
    }

    public void rightForward() {
        rightPin01.setState(true);
        rightPin02.setState(false);
    }

    public void rightBackward() {
        rightPin01.setState(false);
        rightPin02.setState(true);
    }

    public void rightStop() {
        rightPin01.setState(false);
        rightPin02.setState(false);
    }

}

這個類別可以控制一到兩個直流馬達,還有馬達的正、反運轉。需要控制一個直流馬達的時候,使用下列建立物件的敘述:

// 建立控制直流馬達用的GPIO輸出針腳物件
final GpioPinDigitalOutput pin00 = 
        gpio.provisionDigitalOutputPin(RaspiPin.GPIO_00);
final GpioPinDigitalOutput pin02 = 
        gpio.provisionDigitalOutputPin(RaspiPin.GPIO_02);

// 建立控制直流馬達的L293D物件
L293D l293d = new L293D(pin00, pin02);

使用建立好的物件就可以控制直流馬達的運轉:

l293d.leftForward();
l293d.leftStop();
l293d.leftBackward();

12-4 實作類比數位轉換控制類別(MCP3008)

這個專案使用搖桿模組,控制風扇左、右的方向(步進馬達),搖桿前、後可以控制風扇運轉的方向(直流馬達),按下搖桿可以結束應用程式。搖桿模組根據使用者的操作產生類比訊號,所以需要使用類比數位轉換晶片(MCP3008)讀取。

為了讓應用程式比較方便使用MCP3008讀取類比訊號,依照下列的程式碼設計一個MCP3008包裝類別:

package pifan;

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();
    }

}

12-5 完成主程式類別

完成直流馬達控制與類比數位轉換晶片類別以後,這個專案的主程式就會比較簡單一些,而且這些類別也很容易使用在其它專案,例如自走車可以使用直流馬達控制類別控制車輪的運轉,使用類比數位轉換晶片類別讀取紅外線偵測的障礙物距離。

依照下列的程式碼完成這個專案的完成主程式類別:

package pifan;

import com.pi4j.component.motor.impl.GpioStepperMotorComponent;
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.PinState;
import com.pi4j.io.gpio.RaspiPin;

public class PiFan {

    // 28BYJ-48、四相步進馬達
    // 單步、4 steps
    public static final byte[] SINGLE_STEP = { 
        (byte) 0b0001, 
        (byte) 0b0010, 
        (byte) 0b0100, 
        (byte) 0b1000 };

    // 4-Step sequence: 32 * 63.68395 = 2037.8864 (2038)
    public static final int STEPS_PER_REV = 2038;

    // 搖桿訊號與MCP3008對應的通道
    private static final int Y_CHANNEL = 
            MCP3008.MCP3008Channels.CH_00.getChannel();
    private static final int X_CHANNEL = 
            MCP3008.MCP3008Channels.CH_01.getChannel();
    private static final int BUTTON_CHANNEL = 
            MCP3008.MCP3008Channels.CH_02.getChannel();

    private static final int CENTER = 1023 / 2;

    public static void main(String[] args) {

        System.out.println("PiFan Start...");

        // 建立GPIO控制物件
        final GpioController gpio = GpioFactory.getInstance();

        // 建立控制步進馬達的GPIO輸出物件
        final GpioPinDigitalOutput[] pins = {
                gpio.provisionDigitalOutputPin(RaspiPin.GPIO_03, PinState.LOW),
                gpio.provisionDigitalOutputPin(RaspiPin.GPIO_12, PinState.LOW),
                gpio.provisionDigitalOutputPin(RaspiPin.GPIO_13, PinState.LOW),
                gpio.provisionDigitalOutputPin(RaspiPin.GPIO_14, PinState.LOW)};

        // 建立步進馬達物件
        GpioStepperMotorComponent motor = 
                new GpioStepperMotorComponent(pins);

        // 設定每一步的間隔時間
        motor.setStepInterval(0, 500);

        // 設定運作模式
        motor.setStepSequence(SINGLE_STEP);
        motor.setStepsPerRevolution(STEPS_PER_REV);

        // 建立MCP3008需要的GPIO針腳物件
        final GpioPinDigitalInput serialDataOutput = 
                gpio.provisionDigitalInputPin(RaspiPin.GPIO_04);
        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);

        // 建立控制直流馬達用的GPIO輸出針腳物件
        final GpioPinDigitalOutput pin00 = 
                gpio.provisionDigitalOutputPin(RaspiPin.GPIO_00);
        final GpioPinDigitalOutput pin02 = 
                gpio.provisionDigitalOutputPin(RaspiPin.GPIO_02);

        // 建立控制直流馬達的L293D物件
        L293D l293d = new L293D(pin00, pin02);

        while (true) {

            // 讀取搖桿的值
            int buttonValue = mcp3008.read(BUTTON_CHANNEL);
            int xValue = mcp3008.read(X_CHANNEL);
            int yValue = mcp3008.read(Y_CHANNEL);

            // 判斷搖桿前、後控制直流馬達(風扇)運轉的方向
            if (yValue < 100) {
                l293d.leftStop();
                delay(500);
                l293d.leftForward();
            }
            else if (yValue > 900) {
                l293d.leftStop();
                delay(500);
                l293d.leftBackward();
            }

            // 判斷搖桿左、右控制步進馬達運轉的方向
            if (xValue < (CENTER - 250)) {
                motor.reverse();
            }
            else if (xValue > (CENTER + 250)) {
                motor.forward();
            }
            else {
                motor.stop();
            }

            // 判斷搖桿是否按下按鈕
            if (buttonValue > 1000) {
                l293d.leftStop();
                motor.stop();
                break;
            }

            delay(200);
        }

        System.out.println("PiFan Bye...");
        gpio.shutdown();
    }

    private static void delay(int ms) {
        try {
            Thread.sleep(ms);
        }
        catch (InterruptedException e) {
            System.out.println(e.toString());
        }
    }  

}

完成這個專案了。建議你可以繼續參考這個「藍牙視訊遙控車」專案:

上面影片示範的專案,可以在Android行動電話使用藍牙遙控車子,還可以看到Raspbery Pi傳回的視訊。這個專案所有的原始程式碼與詳細的說明公開在 http://github.com/macdidi5/PiTurtleCar

課程相關的檔案都可以GitHub瀏覽與下載。

http://github.com/macdidi5/JavaEmbedded

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

留言

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

關於作者

張益裕。目前的工作是講師與作者,專長是教育訓練課程規劃、教材編製與課程推廣,技術書籍與專欄寫作。涵蓋的領域有OOAD、Java程式設計、JavaFX、Java Embedded、Android與SQL。已出版電子書Google Play圖書Pubu

熱門論壇文章

熱門技術文章