/*
* #%L
* **********************************************************************
* ORGANIZATION : Pi4J
* PROJECT : Pi4J :: Java Examples
* FILENAME : PCA9685GpioExample.java
*
* This file is part of the Pi4J project. More information about
* this project can be found here: http://www.pi4j.com/
* **********************************************************************
* %%
* Copyright (C) 2012 - 2013 Pi4J
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.math.BigDecimal;
import java.util.Scanner;
import com.pi4j.gpio.extension.pca.PCA9685GpioProvider;
import com.pi4j.gpio.extension.pca.PCA9685Pin;
import com.pi4j.io.gpio.GpioController;
import com.pi4j.io.gpio.GpioFactory;
import com.pi4j.io.gpio.GpioPinPwmOutput;
import com.pi4j.io.gpio.Pin;
import com.pi4j.io.i2c.I2CBus;
import com.pi4j.io.i2c.I2CFactory;
/**
* <p>
* This example code demonstrates how to setup a custom GpioProvider
* for GPIO PWM pin control using the PCA9685 16-channel, 12-bit PWM I2C-bus LED/Servo controller.
* </p>
* <p>
* More information about the PCA9685 can be found here:<br>
* <a href="http://www.nxp.com/documents/data_sheet/PCA9685.pdf">PCA9685.pdf</a><br><br>
* ...and especially about the board here:<br>
* <a href="http://www.adafruit.com/products/815">Adafruit 16-Channel 12-bit PWM/Servo Driver</a>
* </p>
*
* @author Christian Wehrli
* @see PCA9685GpioProvider
*/
public class PCA9685GpioExample {
private static final int SERVO_DURATION_MIN = 900;
private static final int SERVO_DURATION_NEUTRAL = 1500;
private static final int SERVO_DURATION_MAX = 2100;
@SuppressWarnings("resource")
public static void main(String args[]) throws Exception {
System.out.println("<--Pi4J--> PCA9685 PWM Example ... started.");
// This would theoretically lead into a resolution of 5 microseconds per step:
// 4096 Steps (12 Bit)
// T = 4096 * 0.000005s = 0.02048s
// f = 1 / T = 48.828125
BigDecimal frequency = new BigDecimal("48.828");
// Correction factor: actualFreq / targetFreq
// e.g. measured actual frequency is: 51.69 Hz
// Calculate correction factor: 51.65 / 48.828 = 1.0578
// --> To measure actual frequency set frequency without correction factor(or set to 1)
BigDecimal frequencyCorrectionFactor = new BigDecimal("1.0578");
// Create custom PCA9685 GPIO provider
I2CBus bus = I2CFactory.getInstance(I2CBus.BUS_1);
final PCA9685GpioProvider gpioProvider = new PCA9685GpioProvider(bus, 0x40, frequency, frequencyCorrectionFactor);
// Define outputs in use for this example
GpioPinPwmOutput[] myOutputs = provisionPwmOutputs(gpioProvider);
// Reset outputs
gpioProvider.reset();
//
// Set various PWM patterns for outputs 0..9
final int offset = 400;
final int pulseDuration = 600;
for (int i = 0; i < 10; i++) {
Pin pin = PCA9685Pin.ALL[i];
int onPosition = checkForOverflow(offset * i);
int offPosition = checkForOverflow(pulseDuration * (i + 1));
gpioProvider.setPwm(pin, onPosition, offPosition);
}
// Set full ON
gpioProvider.setAlwaysOn(PCA9685Pin.PWM_10);
// Set full OFF
gpioProvider.setAlwaysOff(PCA9685Pin.PWM_11);
// Set 0.9ms pulse (R/C Servo minimum position)
gpioProvider.setPwm(PCA9685Pin.PWM_12, SERVO_DURATION_MIN);
// Set 1.5ms pulse (R/C Servo neutral position)
gpioProvider.setPwm(PCA9685Pin.PWM_13, SERVO_DURATION_NEUTRAL);
// Set 2.1ms pulse (R/C Servo maximum position)
gpioProvider.setPwm(PCA9685Pin.PWM_14, SERVO_DURATION_MAX);
//
// Show PWM values for outputs 0..14
for (GpioPinPwmOutput output : myOutputs) {
int[] onOffValues = gpioProvider.getPwmOnOffValues(output.getPin());
System.out.println(output.getPin().getName() + " (" + output.getName() + "): ON value [" + onOffValues[0] + "], OFF value [" + onOffValues[1] + "]");
}
System.out.println("Press <Enter> to terminate...");
new Scanner(System.in).nextLine();
}
private static int checkForOverflow(int position) {
int result = position;
if (position > PCA9685GpioProvider.PWM_STEPS - 1) {
result = position - PCA9685GpioProvider.PWM_STEPS - 1;
}
return result;
}
private static GpioPinPwmOutput[] provisionPwmOutputs(final PCA9685GpioProvider gpioProvider) {
GpioController gpio = GpioFactory.getInstance();
GpioPinPwmOutput myOutputs[] = {
gpio.provisionPwmOutputPin(gpioProvider, PCA9685Pin.PWM_00, "Pulse 00"),
gpio.provisionPwmOutputPin(gpioProvider, PCA9685Pin.PWM_01, "Pulse 01"),
gpio.provisionPwmOutputPin(gpioProvider, PCA9685Pin.PWM_02, "Pulse 02"),
gpio.provisionPwmOutputPin(gpioProvider, PCA9685Pin.PWM_03, "Pulse 03"),
gpio.provisionPwmOutputPin(gpioProvider, PCA9685Pin.PWM_04, "Pulse 04"),
gpio.provisionPwmOutputPin(gpioProvider, PCA9685Pin.PWM_05, "Pulse 05"),
gpio.provisionPwmOutputPin(gpioProvider, PCA9685Pin.PWM_06, "Pulse 06"),
gpio.provisionPwmOutputPin(gpioProvider, PCA9685Pin.PWM_07, "Pulse 07"),
gpio.provisionPwmOutputPin(gpioProvider, PCA9685Pin.PWM_08, "Pulse 08"),
gpio.provisionPwmOutputPin(gpioProvider, PCA9685Pin.PWM_09, "Pulse 09"),
gpio.provisionPwmOutputPin(gpioProvider, PCA9685Pin.PWM_10, "Always ON"),
gpio.provisionPwmOutputPin(gpioProvider, PCA9685Pin.PWM_11, "Always OFF"),
gpio.provisionPwmOutputPin(gpioProvider, PCA9685Pin.PWM_12, "Servo pulse MIN"),
gpio.provisionPwmOutputPin(gpioProvider, PCA9685Pin.PWM_13, "Servo pulse NEUTRAL"),
gpio.provisionPwmOutputPin(gpioProvider, PCA9685Pin.PWM_14, "Servo pulse MAX"),
gpio.provisionPwmOutputPin(gpioProvider, PCA9685Pin.PWM_15, "not used")};
return myOutputs;
}
}