Java Embedded (12)整合應用 – PiFan
Java Embedded (11)控制直流馬達 – 使用L293D晶片 << 前情 12-1 PiFan介紹雖然已經瞭解各種不同零件與模組的應用,還是需要把它們整合在一起,才能夠完成實際的專案。以自走車的應用來說,需要整合直流馬達、紅外線測距模組與應用程式,如果要製作藍牙遙控車的話,就還需要搭配藍牙模組與行動裝置應用程式。 這個專案製作一個可以控制風扇的整合應用,你可以先參考示範影片: PiFan需要下列主要的零件: 下列是零件與設備規格列表:
大部份的零件與設備,都已經在之前的內容說明與實作。因為要控制風扇的方向,使用搖桿模組是最方便的,這是它的外觀: 這種搖桿模組的應用很常見,例如遊戲設備的操作裝置。它也是一種類比訊號的模組,所以要搭配類比數位轉換晶片(MCP3008),讀取使用者的操作。 12-2 連接PiFan的零件與線路因為這個專案使用比較多的零件與模組,所以分別說明它們的連接方式。大部份的作法跟之前說明的一樣,不過因為需要連接比較多的GPIO針腳,所以調整模組與晶片連接到Raspberry Pi的針腳。 搖桿模組與類比數位轉換晶片(MCP3008)搖桿模組是一種類比訊號的模組,所以要搭配類比數位轉換晶片(MCP3008),它可以讓使用者上、下、左、右操作搖桿,也可以按下搖桿的按鈕。應用程式可以透過MCP3008讀取使用者操作的資訊。依照下列的線路圖連接搖桿模組與MCP3008: 直流馬達與直流馬達控制晶片(L293D)連接直流馬達與直流馬達控制晶片(L293D),與之前說明的作法一樣,只有調整連接到Raspbery Pi的GPIO針腳。依照下列的線路圖連接直流馬達與控制晶片: 步進馬達與步進馬達控制模組(ULN2003)連接步進馬達與步進馬達控制模組,與之前說明的作法一樣,只有調整連接到Raspbery Pi的GPIO針腳。依照下列的線路圖連接步進馬達與控制模組: 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瀏覽與下載。 |