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傳回的視訊。這個專案所有的原始程式碼與詳細的說明公開在 https://github.com/macdidi5/PiTurtleCar。 課程相關的檔案都可以GitHub瀏覽與下載。 |

Java 學習之路




https://github.com/macdidi5/JavaEmbedded



