diff --git a/INF/Beispiele.zip b/INF/Beispiele.zip new file mode 100644 index 0000000..c52ebf2 Binary files /dev/null and b/INF/Beispiele.zip differ diff --git a/INF/libraries/PCA9685/LICENSE b/INF/libraries/PCA9685/LICENSE new file mode 100644 index 0000000..ee80abc --- /dev/null +++ b/INF/libraries/PCA9685/LICENSE @@ -0,0 +1,32 @@ +License Agreement +(3-clause BSD License) +Janelia Research Campus Software Copyright 1.1 + +Copyright (c) 2014, Howard Hughes Medical Institute +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the Howard Hughes Medical Institute nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/INF/libraries/PCA9685/README.org b/INF/libraries/PCA9685/README.org new file mode 100644 index 0000000..e1b2904 --- /dev/null +++ b/INF/libraries/PCA9685/README.org @@ -0,0 +1,16 @@ +#+TITLE: PCA9685 +#+AUTHOR: Peter Polidoro +#+EMAIL: peterpolidoro@gmail.com + +* Library Information + - Name :: PCA9685 + - Version :: 2.1.4 + - License :: BSD + - URL :: https://github.com/janelia-arduino/PCA9685 + - Author :: Peter Polidoro + - Email :: peterpolidoro@gmail.com + + The PCA9685 is a 16-channel 12-bit PWM controller. All channels operate at a + programmable frequency between roughly 25 to 1600 Hz with 16 independent + 12-bit duty cycles from 0 to 100%. Supports using a single device (for up to + 16 channels) or up to 55 devices (for up to 880 channels). diff --git a/INF/libraries/PCA9685/examples/FrequencySweep/Constants.cpp b/INF/libraries/PCA9685/examples/FrequencySweep/Constants.cpp new file mode 100644 index 0000000..07c120a --- /dev/null +++ b/INF/libraries/PCA9685/examples/FrequencySweep/Constants.cpp @@ -0,0 +1,18 @@ +// ---------------------------------------------------------------------------- +// Constants.cpp +// +// +// Authors: +// Peter Polidoro peterpolidoro@gmail.com +// ---------------------------------------------------------------------------- +#include "Constants.h" + + +namespace constants +{ +const uint8_t device_address = 0x40; +const size_t output_enable_pin = 2; + +const size_t loop_delay = 100; +const uint16_t frequency_increment = 10; +} diff --git a/INF/libraries/PCA9685/examples/FrequencySweep/Constants.h b/INF/libraries/PCA9685/examples/FrequencySweep/Constants.h new file mode 100644 index 0000000..6734e39 --- /dev/null +++ b/INF/libraries/PCA9685/examples/FrequencySweep/Constants.h @@ -0,0 +1,21 @@ +// ---------------------------------------------------------------------------- +// Constants.h +// +// +// Authors: +// Peter Polidoro peterpolidoro@gmail.com +// ---------------------------------------------------------------------------- +#ifndef CONSTANTS_H +#define CONSTANTS_H +#include + + +namespace constants +{ +extern const uint8_t device_address; +extern const size_t output_enable_pin; + +extern const size_t loop_delay; +extern const uint16_t frequency_increment; +} +#endif diff --git a/INF/libraries/PCA9685/examples/FrequencySweep/FrequencySweep.ino b/INF/libraries/PCA9685/examples/FrequencySweep/FrequencySweep.ino new file mode 100644 index 0000000..f60b801 --- /dev/null +++ b/INF/libraries/PCA9685/examples/FrequencySweep/FrequencySweep.ino @@ -0,0 +1,41 @@ +#include +#include + +#include "Constants.h" + + +PCA9685 pca9685; + +uint16_t frequency_min; +uint16_t frequency_max; +uint16_t frequency; + +void setup() +{ + pca9685.setupSingleDevice(Wire,constants::device_address); + + pca9685.setupOutputEnablePin(constants::output_enable_pin); + pca9685.enableOutputs(constants::output_enable_pin); + + pca9685.setOutputsNotInverted(); + + uint16_t time_min = pca9685.getTimeMin(); + uint16_t time_max = pca9685.getTimeMax(); + pca9685.setAllChannelsOnAndOffTime(time_min,time_max/4); + + frequency_min = pca9685.getFrequencyMin(); + frequency_max = pca9685.getFrequencyMax(); + + frequency = frequency_min; +} + +void loop() +{ + if (frequency > frequency_max) + { + frequency = frequency_min; + } + pca9685.setToFrequency(frequency); + frequency += constants::frequency_increment; + delay(constants::loop_delay); +} diff --git a/INF/libraries/PCA9685/examples/OffTimeSweepSingleDevice/Constants.cpp b/INF/libraries/PCA9685/examples/OffTimeSweepSingleDevice/Constants.cpp new file mode 100644 index 0000000..d0c27cc --- /dev/null +++ b/INF/libraries/PCA9685/examples/OffTimeSweepSingleDevice/Constants.cpp @@ -0,0 +1,21 @@ +// ---------------------------------------------------------------------------- +// Constants.cpp +// +// +// Authors: +// Peter Polidoro peterpolidoro@gmail.com +// ---------------------------------------------------------------------------- +#include "Constants.h" + + +namespace constants +{ +const uint8_t device_address = 0x40; +const size_t output_enable_pin = 2; + +const size_t loop_delay = 100; +const uint16_t frequency = 200; +const uint16_t time_increment = 100; + +const uint8_t channel = 0; +} diff --git a/INF/libraries/PCA9685/examples/OffTimeSweepSingleDevice/Constants.h b/INF/libraries/PCA9685/examples/OffTimeSweepSingleDevice/Constants.h new file mode 100644 index 0000000..c4146ba --- /dev/null +++ b/INF/libraries/PCA9685/examples/OffTimeSweepSingleDevice/Constants.h @@ -0,0 +1,24 @@ +// ---------------------------------------------------------------------------- +// Constants.h +// +// +// Authors: +// Peter Polidoro peterpolidoro@gmail.com +// ---------------------------------------------------------------------------- +#ifndef CONSTANTS_H +#define CONSTANTS_H +#include + + +namespace constants +{ +extern const uint8_t device_address; +extern const size_t output_enable_pin; + +extern const size_t loop_delay; +extern const uint16_t frequency; +extern const uint16_t time_increment; + +extern const uint8_t channel; +} +#endif diff --git a/INF/libraries/PCA9685/examples/OffTimeSweepSingleDevice/OffTimeSweepSingleDevice.ino b/INF/libraries/PCA9685/examples/OffTimeSweepSingleDevice/OffTimeSweepSingleDevice.ino new file mode 100644 index 0000000..ffe0f59 --- /dev/null +++ b/INF/libraries/PCA9685/examples/OffTimeSweepSingleDevice/OffTimeSweepSingleDevice.ino @@ -0,0 +1,41 @@ +#include +#include + +#include "Constants.h" + + +PCA9685 pca9685; + +uint16_t time_min; +uint16_t time_max; +uint16_t off_time; + +void setup() +{ + pca9685.setupSingleDevice(Wire,constants::device_address); + + pca9685.setupOutputEnablePin(constants::output_enable_pin); + pca9685.enableOutputs(constants::output_enable_pin); + + pca9685.setOutputsNotInverted(); + + pca9685.setToFrequency(constants::frequency); + + time_min = pca9685.getTimeMin(); + time_max = pca9685.getTimeMax(); + + off_time = time_min; + + pca9685.setChannelOnTime(constants::channel,time_min); +} + +void loop() +{ + if (off_time > time_max) + { + off_time = time_min; + } + pca9685.setChannelOffTime(constants::channel,off_time); + off_time += constants::time_increment; + delay(constants::loop_delay); +} diff --git a/INF/libraries/PCA9685/examples/OnTimeSweepMultipleDevices/Constants.cpp b/INF/libraries/PCA9685/examples/OnTimeSweepMultipleDevices/Constants.cpp new file mode 100644 index 0000000..55aad84 --- /dev/null +++ b/INF/libraries/PCA9685/examples/OnTimeSweepMultipleDevices/Constants.cpp @@ -0,0 +1,28 @@ +// ---------------------------------------------------------------------------- +// Constants.cpp +// +// +// Authors: +// Peter Polidoro peterpolidoro@gmail.com +// ---------------------------------------------------------------------------- +#include "Constants.h" + + +namespace constants +{ +const uint8_t device_addresses[DEVICE_COUNT] = +{ + 0x40, + 0x41, + 0x42 +}; +const uint8_t device_index = 0; + +const size_t output_enable_pin = 2; + +const size_t loop_delay = 100; +const uint16_t frequency = 200; +const uint16_t time_increment = 100; + +const uint8_t channel = 0; +} diff --git a/INF/libraries/PCA9685/examples/OnTimeSweepMultipleDevices/Constants.h b/INF/libraries/PCA9685/examples/OnTimeSweepMultipleDevices/Constants.h new file mode 100644 index 0000000..b89390d --- /dev/null +++ b/INF/libraries/PCA9685/examples/OnTimeSweepMultipleDevices/Constants.h @@ -0,0 +1,27 @@ +// ---------------------------------------------------------------------------- +// Constants.h +// +// +// Authors: +// Peter Polidoro peterpolidoro@gmail.com +// ---------------------------------------------------------------------------- +#ifndef CONSTANTS_H +#define CONSTANTS_H +#include + + +namespace constants +{ +enum{DEVICE_COUNT=3}; +extern const uint8_t device_addresses[DEVICE_COUNT]; +extern const uint8_t device_index; + +extern const size_t output_enable_pin; + +extern const size_t loop_delay; +extern const uint16_t frequency; +extern const uint16_t time_increment; + +extern const uint8_t channel; +} +#endif diff --git a/INF/libraries/PCA9685/examples/OnTimeSweepMultipleDevices/OnTimeSweepMultipleDevices.ino b/INF/libraries/PCA9685/examples/OnTimeSweepMultipleDevices/OnTimeSweepMultipleDevices.ino new file mode 100644 index 0000000..e00c6ee --- /dev/null +++ b/INF/libraries/PCA9685/examples/OnTimeSweepMultipleDevices/OnTimeSweepMultipleDevices.ino @@ -0,0 +1,46 @@ +#include +#include + +#include "Constants.h" + + +PCA9685 pca9685; + +uint16_t time_min; +uint16_t time_max; +uint16_t on_time; + +void setup() +{ + pca9685.setWire(Wire); + for (uint8_t device_index=0; device_index time_max) + { + on_time = time_min; + } + pca9685.setAllDeviceChannelsOnTime(constants::device_addresses[constants::device_index],on_time); + on_time += constants::time_increment; + delay(constants::loop_delay); +} diff --git a/INF/libraries/PCA9685/examples/ServoController/Constants.cpp b/INF/libraries/PCA9685/examples/ServoController/Constants.cpp new file mode 100644 index 0000000..3c45087 --- /dev/null +++ b/INF/libraries/PCA9685/examples/ServoController/Constants.cpp @@ -0,0 +1,23 @@ +// ---------------------------------------------------------------------------- +// Constants.cpp +// +// +// Authors: +// Peter Polidoro peterpolidoro@gmail.com +// ---------------------------------------------------------------------------- +#include "Constants.h" + + +namespace constants +{ +const uint8_t device_address = 0x40; +const size_t output_enable_pin = 2; + +const size_t loop_delay = 100; + +const uint8_t channel = 0; + +const uint16_t servo_pulse_duration_min = 900; +const uint16_t servo_pulse_duration_max = 2100; +const uint16_t servo_pulse_duration_increment = 100; +} diff --git a/INF/libraries/PCA9685/examples/ServoController/Constants.h b/INF/libraries/PCA9685/examples/ServoController/Constants.h new file mode 100644 index 0000000..ffeb5f6 --- /dev/null +++ b/INF/libraries/PCA9685/examples/ServoController/Constants.h @@ -0,0 +1,26 @@ +// ---------------------------------------------------------------------------- +// Constants.h +// +// +// Authors: +// Peter Polidoro peterpolidoro@gmail.com +// ---------------------------------------------------------------------------- +#ifndef CONSTANTS_H +#define CONSTANTS_H +#include + + +namespace constants +{ +extern const uint8_t device_address; +extern const size_t output_enable_pin; + +extern const size_t loop_delay; + +extern const uint8_t channel; + +extern const uint16_t servo_pulse_duration_min; +extern const uint16_t servo_pulse_duration_max; +extern const uint16_t servo_pulse_duration_increment; +} +#endif diff --git a/INF/libraries/PCA9685/examples/ServoController/ServoController.ino b/INF/libraries/PCA9685/examples/ServoController/ServoController.ino new file mode 100644 index 0000000..2c907bc --- /dev/null +++ b/INF/libraries/PCA9685/examples/ServoController/ServoController.ino @@ -0,0 +1,32 @@ +#include +#include + +#include "Constants.h" + + +PCA9685 pca9685; + +uint16_t servo_pulse_duration; + +void setup() +{ + pca9685.setupSingleDevice(Wire,constants::device_address); + + pca9685.setupOutputEnablePin(constants::output_enable_pin); + pca9685.enableOutputs(constants::output_enable_pin); + + pca9685.setToServoFrequency(); + + servo_pulse_duration = constants::servo_pulse_duration_min; +} + +void loop() +{ + if (servo_pulse_duration > constants::servo_pulse_duration_max) + { + servo_pulse_duration = constants::servo_pulse_duration_min; + } + pca9685.setChannelServoPulseDuration(constants::channel,servo_pulse_duration); + servo_pulse_duration += constants::servo_pulse_duration_increment; + delay(constants::loop_delay); +} diff --git a/INF/libraries/PCA9685/examples/SetChannelDutyCycle/Constants.cpp b/INF/libraries/PCA9685/examples/SetChannelDutyCycle/Constants.cpp new file mode 100644 index 0000000..4242f9e --- /dev/null +++ b/INF/libraries/PCA9685/examples/SetChannelDutyCycle/Constants.cpp @@ -0,0 +1,41 @@ +// ---------------------------------------------------------------------------- +// Constants.cpp +// +// +// Authors: +// Peter Polidoro peterpolidoro@gmail.com +// ---------------------------------------------------------------------------- +#include "Constants.h" + + +namespace constants +{ +const uint8_t device_address = 0x40; +const size_t output_enable_pin = 2; + +const size_t loop_delay = 8000; +const uint16_t frequency = 200; +const uint8_t channel = 0; + +const Example examples[EXAMPLE_COUNT] = +{ + { + 20.0, // DUTY_CYCLE + 10.0, // PERCENT_DELAY + }, + { + 90.0, // DUTY_CYCLE + 90.0, // PERCENT_DELAY + }, + { + // Always ON + 100.0, // DUTY_CYCLE + 0.0, // PERCENT_DELAY + }, + { + // Always OFF + 0.0, // DUTY_CYCLE + 0.0, // PERCENT_DELAY + } +}; +} diff --git a/INF/libraries/PCA9685/examples/SetChannelDutyCycle/Constants.h b/INF/libraries/PCA9685/examples/SetChannelDutyCycle/Constants.h new file mode 100644 index 0000000..31dbc87 --- /dev/null +++ b/INF/libraries/PCA9685/examples/SetChannelDutyCycle/Constants.h @@ -0,0 +1,32 @@ +// ---------------------------------------------------------------------------- +// Constants.h +// +// +// Authors: +// Peter Polidoro peterpolidoro@gmail.com +// ---------------------------------------------------------------------------- +#ifndef CONSTANTS_H +#define CONSTANTS_H +#include + + +namespace constants +{ +extern const uint8_t device_address; +extern const size_t output_enable_pin; + +extern const size_t loop_delay; +extern const uint16_t frequency; +extern const uint8_t channel; + +enum{EXAMPLE_COUNT=4}; + +struct Example +{ + double duty_cycle; + double percent_delay; +}; + +extern const Example examples[EXAMPLE_COUNT]; +} +#endif diff --git a/INF/libraries/PCA9685/examples/SetChannelDutyCycle/SetChannelDutyCycle.ino b/INF/libraries/PCA9685/examples/SetChannelDutyCycle/SetChannelDutyCycle.ino new file mode 100644 index 0000000..3fca393 --- /dev/null +++ b/INF/libraries/PCA9685/examples/SetChannelDutyCycle/SetChannelDutyCycle.ino @@ -0,0 +1,33 @@ +#include +#include + +#include "Constants.h" + + +PCA9685 pca9685; + +uint8_t example_index; + +void setup() +{ + pca9685.setupSingleDevice(Wire,constants::device_address); + + pca9685.setupOutputEnablePin(constants::output_enable_pin); + pca9685.enableOutputs(constants::output_enable_pin); + + pca9685.setToFrequency(constants::frequency); + + example_index = 0; +} + +void loop() +{ + delay(constants::loop_delay); + if (example_index >= constants::EXAMPLE_COUNT) + { + example_index = 0; + } + constants::Example example = constants::examples[example_index++]; + + pca9685.setChannelDutyCycle(constants::channel,example.duty_cycle,example.percent_delay); +} diff --git a/INF/libraries/PCA9685/examples/SetChannelOnAndOffTime/Constants.cpp b/INF/libraries/PCA9685/examples/SetChannelOnAndOffTime/Constants.cpp new file mode 100644 index 0000000..eb25a6c --- /dev/null +++ b/INF/libraries/PCA9685/examples/SetChannelOnAndOffTime/Constants.cpp @@ -0,0 +1,43 @@ +// ---------------------------------------------------------------------------- +// Constants.cpp +// +// +// Authors: +// Peter Polidoro peterpolidoro@gmail.com +// ---------------------------------------------------------------------------- +#include "Constants.h" + + +namespace constants +{ +const uint8_t device_address = 0x40; +const size_t output_enable_pin = 2; + +const size_t loop_delay = 8000; +const uint16_t frequency = 200; +const uint8_t channel = 0; + +const Example examples[EXAMPLE_COUNT] = +{ + { + // ON_TIME < OFF_TIME + 511, // ON_TIME + 3071, // OFF_TIME + }, + { + // ON_TIME < OFF_TIME + 2047, // ON_TIME + 767, // OFF_TIME + }, + { + // Always ON + 4096, // ON_TIME + 0, // OFF_TIME + }, + { + // Always OFF + 0, // ON_TIME + 4096, // OFF_TIME + } +}; +} diff --git a/INF/libraries/PCA9685/examples/SetChannelOnAndOffTime/Constants.h b/INF/libraries/PCA9685/examples/SetChannelOnAndOffTime/Constants.h new file mode 100644 index 0000000..6dca96e --- /dev/null +++ b/INF/libraries/PCA9685/examples/SetChannelOnAndOffTime/Constants.h @@ -0,0 +1,32 @@ +// ---------------------------------------------------------------------------- +// Constants.h +// +// +// Authors: +// Peter Polidoro peterpolidoro@gmail.com +// ---------------------------------------------------------------------------- +#ifndef CONSTANTS_H +#define CONSTANTS_H +#include + + +namespace constants +{ +extern const uint8_t device_address; +extern const size_t output_enable_pin; + +extern const size_t loop_delay; +extern const uint16_t frequency; +extern const uint8_t channel; + +enum{EXAMPLE_COUNT=4}; + +struct Example +{ + uint16_t on_time; + uint16_t off_time; +}; + +extern const Example examples[EXAMPLE_COUNT]; +} +#endif diff --git a/INF/libraries/PCA9685/examples/SetChannelOnAndOffTime/SetChannelOnAndOffTime.ino b/INF/libraries/PCA9685/examples/SetChannelOnAndOffTime/SetChannelOnAndOffTime.ino new file mode 100644 index 0000000..b5d3ac4 --- /dev/null +++ b/INF/libraries/PCA9685/examples/SetChannelOnAndOffTime/SetChannelOnAndOffTime.ino @@ -0,0 +1,33 @@ +#include +#include + +#include "Constants.h" + + +PCA9685 pca9685; + +uint8_t example_index; + +void setup() +{ + pca9685.setupSingleDevice(Wire,constants::device_address); + + pca9685.setupOutputEnablePin(constants::output_enable_pin); + pca9685.enableOutputs(constants::output_enable_pin); + + pca9685.setToFrequency(constants::frequency); + + example_index = 0; +} + +void loop() +{ + delay(constants::loop_delay); + if (example_index >= constants::EXAMPLE_COUNT) + { + example_index = 0; + } + constants::Example example = constants::examples[example_index++]; + + pca9685.setChannelOnAndOffTime(constants::channel,example.on_time,example.off_time); +} diff --git a/INF/libraries/PCA9685/examples/SetChannelPulseWidth/Constants.cpp b/INF/libraries/PCA9685/examples/SetChannelPulseWidth/Constants.cpp new file mode 100644 index 0000000..ccf4c83 --- /dev/null +++ b/INF/libraries/PCA9685/examples/SetChannelPulseWidth/Constants.cpp @@ -0,0 +1,41 @@ +// ---------------------------------------------------------------------------- +// Constants.cpp +// +// +// Authors: +// Peter Polidoro peterpolidoro@gmail.com +// ---------------------------------------------------------------------------- +#include "Constants.h" + + +namespace constants +{ +const uint8_t device_address = 0x40; +const size_t output_enable_pin = 2; + +const size_t loop_delay = 8000; +const uint16_t frequency = 200; +const uint8_t channel = 0; + +const Example examples[EXAMPLE_COUNT] = +{ + { + 819, // PULSE_WIDTH + 409, // PHASE_SHIFT + }, + { + 3686, // PULSE_WIDTH + 3685, // PHASE_SHIFT + }, + { + // Always ON + 4096, // PULSE_WIDTH + 0, // PHASE_SHIFT + }, + { + // Always OFF + 0, // PULSE_WIDTH + 0, // PHASE_SHIFT + } +}; +} diff --git a/INF/libraries/PCA9685/examples/SetChannelPulseWidth/Constants.h b/INF/libraries/PCA9685/examples/SetChannelPulseWidth/Constants.h new file mode 100644 index 0000000..c23a555 --- /dev/null +++ b/INF/libraries/PCA9685/examples/SetChannelPulseWidth/Constants.h @@ -0,0 +1,32 @@ +// ---------------------------------------------------------------------------- +// Constants.h +// +// +// Authors: +// Peter Polidoro peterpolidoro@gmail.com +// ---------------------------------------------------------------------------- +#ifndef CONSTANTS_H +#define CONSTANTS_H +#include + + +namespace constants +{ +extern const uint8_t device_address; +extern const size_t output_enable_pin; + +extern const size_t loop_delay; +extern const uint16_t frequency; +extern const uint8_t channel; + +enum{EXAMPLE_COUNT=4}; + +struct Example +{ + uint16_t pulse_width; + uint16_t phase_shift; +}; + +extern const Example examples[EXAMPLE_COUNT]; +} +#endif diff --git a/INF/libraries/PCA9685/examples/SetChannelPulseWidth/SetChannelPulseWidth.ino b/INF/libraries/PCA9685/examples/SetChannelPulseWidth/SetChannelPulseWidth.ino new file mode 100644 index 0000000..e34c107 --- /dev/null +++ b/INF/libraries/PCA9685/examples/SetChannelPulseWidth/SetChannelPulseWidth.ino @@ -0,0 +1,33 @@ +#include +#include + +#include "Constants.h" + + +PCA9685 pca9685; + +uint8_t example_index; + +void setup() +{ + pca9685.setupSingleDevice(Wire,constants::device_address); + + pca9685.setupOutputEnablePin(constants::output_enable_pin); + pca9685.enableOutputs(constants::output_enable_pin); + + pca9685.setToFrequency(constants::frequency); + + example_index = 0; +} + +void loop() +{ + delay(constants::loop_delay); + if (example_index >= constants::EXAMPLE_COUNT) + { + example_index = 0; + } + constants::Example example = constants::examples[example_index++]; + + pca9685.setChannelPulseWidth(constants::channel,example.pulse_width,example.phase_shift); +} diff --git a/INF/libraries/PCA9685/examples/TestSetAndGet/Constants.cpp b/INF/libraries/PCA9685/examples/TestSetAndGet/Constants.cpp new file mode 100644 index 0000000..8aeca1e --- /dev/null +++ b/INF/libraries/PCA9685/examples/TestSetAndGet/Constants.cpp @@ -0,0 +1,22 @@ +// ---------------------------------------------------------------------------- +// Constants.cpp +// +// +// Authors: +// Peter Polidoro peterpolidoro@gmail.com +// ---------------------------------------------------------------------------- +#include "Constants.h" + + +namespace constants +{ +const uint8_t device_address = 0x40; +const size_t output_enable_pin = 2; + +const long baud = 115200; + +const size_t loop_delay = 100; +const uint16_t frequency = 200; +const uint16_t time_increment = 400; +const double epsilon = 0.001; +} diff --git a/INF/libraries/PCA9685/examples/TestSetAndGet/Constants.h b/INF/libraries/PCA9685/examples/TestSetAndGet/Constants.h new file mode 100644 index 0000000..6f5ae82 --- /dev/null +++ b/INF/libraries/PCA9685/examples/TestSetAndGet/Constants.h @@ -0,0 +1,24 @@ +// ---------------------------------------------------------------------------- +// Constants.h +// +// +// Authors: +// Peter Polidoro peterpolidoro@gmail.com +// ---------------------------------------------------------------------------- +#ifndef CONSTANTS_H +#define CONSTANTS_H +#include + + +namespace constants +{ +extern const uint8_t device_address; +extern const size_t output_enable_pin; + +extern const long baud; +extern const size_t loop_delay; +extern const uint16_t frequency; +extern const uint16_t time_increment; +extern const double epsilon; +} +#endif diff --git a/INF/libraries/PCA9685/examples/TestSetAndGet/TestSetAndGet.ino b/INF/libraries/PCA9685/examples/TestSetAndGet/TestSetAndGet.ino new file mode 100644 index 0000000..af6a09e --- /dev/null +++ b/INF/libraries/PCA9685/examples/TestSetAndGet/TestSetAndGet.ino @@ -0,0 +1,105 @@ +#include +#include +#include + +#include "Constants.h" + + +PCA9685 pca9685; + +uint16_t time_min; +uint16_t time_max; + +uint8_t channel; + +uint16_t on_time_set; +uint16_t off_time_set; +uint16_t on_time_get; +uint16_t off_time_get; + +uint16_t pulse_width_set; +uint16_t phase_shift_set; +uint16_t pulse_width_get; +uint16_t phase_shift_get; + +double duty_cycle_set; +double percent_delay_set; +double duty_cycle_get; +double percent_delay_get; + +void setup() +{ + Serial.begin(constants::baud); + + pca9685.setupSingleDevice(Wire,constants::device_address); + + pca9685.setupOutputEnablePin(constants::output_enable_pin); + pca9685.enableOutputs(constants::output_enable_pin); + + pca9685.setOutputsNotInverted(); + + pca9685.setToServoFrequency(); + + time_min = pca9685.getTimeMin(); + time_max = pca9685.getTimeMax(); + + channel = 0; + on_time_set = time_min; + off_time_set = time_max; +} + +void checkMatch(const char * s, bool match) +{ + if (match) + { + Serial << s << " get matches set.\n"; + } + else + { + Serial << s << " get does not match set!\n"; + } +} + +void loop() +{ + if (on_time_set > off_time_set) + { + on_time_set = time_min; + off_time_set = time_max; + } + for (uint8_t channel=0; channel < pca9685.getChannelCount(); ++channel) + { + Serial << "frequency: " << pca9685.getFrequency() << "\n"; + Serial << "channel: " << channel << ", on_time_set: " << on_time_set << ", off_time_set: " << off_time_set << "\n"; + pca9685.setChannelOnAndOffTime(channel,on_time_set,off_time_set); + pca9685.getChannelOnAndOffTime(channel,on_time_get,off_time_get); + checkMatch("ChannelOnAndOffTime",((on_time_set == on_time_get) && (off_time_set == off_time_get))); + + pca9685.setChannelOnTime(channel,on_time_set); + pca9685.getChannelOnTime(channel,on_time_get); + checkMatch("ChannelOnTime",(on_time_set == on_time_get)); + + pca9685.setChannelOffTime(channel,off_time_set); + pca9685.getChannelOffTime(channel,off_time_get); + checkMatch("ChannelOffTime",(off_time_set == off_time_get)); + + pulse_width_set = off_time_set - on_time_set; + phase_shift_set = on_time_set; + Serial << "channel: " << channel << ", pulse_width_set: " << pulse_width_set << ", phase_shift_set: " << phase_shift_set << "\n"; + pca9685.setChannelPulseWidth(channel,pulse_width_set,phase_shift_set); + pca9685.getChannelPulseWidth(channel,pulse_width_get,phase_shift_get); + checkMatch("ChannelPulseWidth",((pulse_width_set == pulse_width_get) && (phase_shift_set == phase_shift_get))); + + duty_cycle_set = (pulse_width_set * pca9685.getDutyCycleMax()) / pca9685.getTimeMax(); + percent_delay_set = (phase_shift_set * pca9685.getPercentDelayMax()) / pca9685.getTimeMax(); + Serial << "channel: " << channel << ", duty_cycle_set: " << duty_cycle_set << ", percent_delay_set: " << percent_delay_set << "\n"; + pca9685.setChannelDutyCycle(channel,duty_cycle_set,percent_delay_set); + pca9685.getChannelDutyCycle(channel,duty_cycle_get,percent_delay_get); + checkMatch("ChannelDutyCycle",((abs(duty_cycle_set - duty_cycle_get) < constants::epsilon) && (abs(percent_delay_set - percent_delay_get) < constants::epsilon))); + + Serial << "\n"; + delay(constants::loop_delay); + } + on_time_set += constants::time_increment; + off_time_set -= constants::time_increment; +} diff --git a/INF/libraries/PCA9685/library.properties b/INF/libraries/PCA9685/library.properties new file mode 100644 index 0000000..3f954db --- /dev/null +++ b/INF/libraries/PCA9685/library.properties @@ -0,0 +1,9 @@ +name=PCA9685 +version=2.1.4 +author=Peter Polidoro +maintainer=Peter Polidoro +sentence=PCA9685 16-channel 12-bit PWM controller. +paragraph=Like this project? Please star it on GitHub! +category=Device Control +url=https://github.com/janelia-arduino/PCA9685.git +architectures=* diff --git a/INF/libraries/PCA9685/src/PCA9685.h b/INF/libraries/PCA9685/src/PCA9685.h new file mode 100644 index 0000000..434b5ac --- /dev/null +++ b/INF/libraries/PCA9685/src/PCA9685.h @@ -0,0 +1,363 @@ +// ---------------------------------------------------------------------------- +// PCA9685.h +// +// Authors: +// Peter Polidoro peterpolidoro@gmail.com +// ---------------------------------------------------------------------------- +#ifndef PCA9685_H +#define PCA9685_H +#include +#include + + +class PCA9685 +{ +public: + PCA9685(); + + const static uint8_t CHANNELS_PER_DEVICE = 16; + enum {DEVICE_COUNT_MAX=55}; + + // Convenience method when using a single device + void setupSingleDevice(TwoWire & wire=Wire, + uint8_t device_address=0x40, + bool fast_mode_plus=false); + + // Methods for using a single device or multiple devices + void setupOutputEnablePin(size_t output_enable_pin); + void enableOutputs(size_t output_enable_pin); + void disableOutputs(size_t output_enable_pin); + + uint16_t getFrequencyMin(); + uint16_t getFrequencyMax(); + void setToFrequency(uint16_t frequency); + uint16_t getFrequency(); + void setToServoFrequency(); + uint16_t getServoFrequency(); + + uint8_t getChannelCount(); + + double getDutyCycleMin(); + double getDutyCycleMax(); + double getPercentDelayMin(); + double getPercentDelayMax(); + void setChannelDutyCycle(uint8_t channel, + double duty_cycle, + double percent_delay=0); + void getChannelDutyCycle(uint8_t channel, + double & duty_cycle, + double & percent_delay); + void setAllChannelsDutyCycle(double duty_cycle, + double percent_delay=0); + + uint16_t getPulseWidthMin(); + uint16_t getPulseWidthMax(); + uint16_t getPhaseShiftMin(); + uint16_t getPhaseShiftMax(); + void setChannelPulseWidth(uint8_t channel, + uint16_t pulse_width, + uint16_t phase_shift=0); + void getChannelPulseWidth(uint8_t channel, + uint16_t & pulse_width, + uint16_t & phase_shift); + void setAllChannelsPulseWidth(uint16_t pulse_width, + uint16_t phase_shift=0); + + void setChannelServoPulseDuration(uint8_t channel, + uint16_t pulse_duration_microseconds); + void getChannelServoPulseDuration(uint8_t channel, + uint16_t & pulse_duration_microseconds); + void setAllChannelsServoPulseDuration(uint16_t pulse_duration_microseconds); + + uint16_t getTimeMin(); + uint16_t getTimeMax(); + void setChannelOnAndOffTime(uint8_t channel, + uint16_t on_time, + uint16_t off_time); + void getChannelOnAndOffTime(uint8_t channel, + uint16_t & on_time, + uint16_t & off_time); + void setAllChannelsOnAndOffTime(uint16_t on_time, + uint16_t off_time); + void setChannelOnTime(uint8_t channel, + uint16_t on_time); + void getChannelOnTime(uint8_t channel, + uint16_t & on_time); + void setAllChannelsOnTime(uint16_t on_time); + void setChannelOffTime(uint8_t channel, + uint16_t off_time); + void getChannelOffTime(uint8_t channel, + uint16_t & off_time); + void setAllChannelsOffTime(uint16_t off_time); + + void setOutputsInverted(); + void setOutputsNotInverted(); + void setOutputsToTotemPole(); + void setOutputsToOpenDrain(); + void setOutputsLowWhenDisabled(); + void setOutputsHighWhenDisabled(); + void setOutputsHighImpedanceWhenDisabled(); + + // Methods for using multiple devices + // Take care when using fast_mode_plus with non-PCA9685 devices + void setWire(TwoWire & wire=Wire, + bool fast_mode_plus=false); + + // device_address=0x40 when all device address + // hardware select lines are low + // cannot use reserved addresses + void addDevice(uint8_t device_address); + void resetAllDevices(); + + void addDeviceToGroup0(uint8_t device_address); + void removeDeviceFromGroup0(uint8_t device_address); + void addDeviceToGroup1(uint8_t device_address); + void removeDeviceFromGroup1(uint8_t device_address); + void addDeviceToGroup2(uint8_t device_address); + void removeDeviceFromGroup2(uint8_t device_address); + + void setSingleDeviceToFrequency(uint8_t device_address, + uint16_t frequency); + uint16_t getSingleDeviceFrequency(uint8_t device_address); + void setAllDevicesToFrequency(uint16_t frequency); + void setSingleDeviceToServoFrequency(uint8_t device_address); + uint16_t getSingleDeviceServoFrequency(uint8_t device_address); + void setAllDevicesToServoFrequency(); + + uint8_t getDeviceChannelCount(); + + // Use these device address to set more than one device at a time + // with the methods below + const static uint8_t DEVICE_ADDRESS_ALL = 0x70; + const static uint8_t DEVICE_ADDRESS_GROUP0 = 0x71; + const static uint8_t DEVICE_ADDRESS_GROUP1 = 0x72; + const static uint8_t DEVICE_ADDRESS_GROUP2 = 0x73; + + void setDeviceChannelDutyCycle(uint8_t device_address, + uint8_t device_channel, + double duty_cycle, + double percent_delay=0); + void setAllDeviceChannelsDutyCycle(uint8_t device_address, + double duty_cycle, + double percent_delay=0); + + void setDeviceChannelPulseWidth(uint8_t device_address, + uint8_t device_channel, + uint16_t pulse_width, + uint16_t phase_shift=0); + void setAllDeviceChannelsPulseWidth(uint8_t device_address, + uint16_t pulse_width, + uint16_t phase_shift=0); + + void setDeviceChannelServoPulseDuration(uint8_t device_address, + uint8_t device_channel, + uint16_t pulse_duration_microseconds); + void setAllDeviceChannelsServoPulseDuration(uint8_t device_address, + uint16_t pulse_duration_microseconds); + + void setDeviceChannelOnAndOffTime(uint8_t device_address, + uint8_t device_channel, + uint16_t on_time, + uint16_t off_time); + void setAllDeviceChannelsOnAndOffTime(uint8_t device_address, + uint16_t on_time, + uint16_t off_time); + void setDeviceChannelOnTime(uint8_t device_address, + uint8_t device_channel, + uint16_t on_time); + void setAllDeviceChannelsOnTime(uint8_t device_address, + uint16_t on_time); + void setDeviceChannelOffTime(uint8_t device_address, + uint8_t device_channel, + uint16_t off_time); + void setAllDeviceChannelsOffTime(uint8_t device_address, + uint16_t off_time); + + void setSingleDeviceOutputsInverted(uint8_t device_address); + void setAllDevicesOutputsInverted(); + void setSingleDeviceOutputsNotInverted(uint8_t device_address); + void setAllDevicesOutputsNotInverted(); + void setSingleDeviceOutputsToTotemPole(uint8_t device_address); + void setAllDevicesOutputsToTotemPole(); + void setSingleDeviceOutputsToOpenDrain(uint8_t device_address); + void setAllDevicesOutputsToOpenDrain(); + void setSingleDeviceOutputsLowWhenDisabled(uint8_t device_address); + void setAllDevicesOutputsLowWhenDisabled(); + void setSingleDeviceOutputsHighWhenDisabled(uint8_t device_address); + void setAllDevicesOutputsHighWhenDisabled(); + void setSingleDeviceOutputsHighImpedanceWhenDisabled(uint8_t device_address); + void setAllDevicesOutputsHighImpedanceWhenDisabled(); + +private: + const static uint8_t DEVICE_ADDRESS_MIN = 0x40; + const static uint8_t DEVICE_ADDRESS_MAX = 0x7B; + uint8_t device_count_; + uint8_t device_addresses_[DEVICE_COUNT_MAX]; + + TwoWire * wire_ptr_; + const static long FAST_MODE_PLUS_CLOCK_FREQUENCY = 1000000; + + const static int NO_OUTPUT_ENABLE_PIN = -1; + + const static int DEVICE_INDEX_NONE = -1; + const static int DEVICE_INDEX_ALL = -2; + const static int DEVICE_INDEX_GROUP0 = -3; + const static int DEVICE_INDEX_GROUP1 = -4; + const static int DEVICE_INDEX_GROUP2 = -5; + int deviceAddressToDeviceIndex(uint8_t device_address); + + const static uint8_t GENERAL_CALL_DEVICE_ADDRESS = 0x00; + const static uint8_t SWRST = 0b110; + + // Can write to one or more device at a time + // so use address rather than index + template + void write(uint8_t device_address, + uint8_t register_address, + T data); + // Can only read from one device at a time + // so use index rather than address + template + void read(uint8_t device_index, + uint8_t register_address, + T & data); + + const static uint8_t MODE1_REGISTER_ADDRESS = 0x00; + union Mode1Register + { + struct + { + uint8_t allcall : 1; + uint8_t sub3 : 1; + uint8_t sub2 : 1; + uint8_t sub1 : 1; + uint8_t sleep : 1; + uint8_t ai : 1; + uint8_t extclk : 1; + uint8_t restart : 1; + } fields; + uint8_t data; + }; + Mode1Register readMode1Register(uint8_t device_index); + + const static uint8_t MODE2_REGISTER_ADDRESS = 0x01; + union Mode2Register + { + struct + { + uint8_t outne : 2; + uint8_t outdrv : 1; + uint8_t och : 1; + uint8_t invrt : 1; + uint8_t space : 3; + } fields; + uint8_t data; + }; + Mode2Register readMode2Register(uint8_t device_index); + + void sleep(uint8_t device_index); + void wake(uint8_t device_index); + void wakeAll(); + + void setPrescale(uint8_t device_index, + uint8_t prescale); + void getPrescale(uint8_t device_index, + uint8_t & prescale); + uint8_t frequencyToPrescale(uint16_t frequency); + uint16_t prescaleToFrequency(uint8_t prescale); + + uint8_t channelToDeviceIndex(uint8_t channel); + uint8_t channelToDeviceChannel(uint8_t channel); + + void dutyCycleAndPercentDelayToPulseWidthAndPhaseShift(double duty_cycle, + double percent_delay, + uint16_t & pulse_width, + uint16_t & phase_shift); + void pulseWidthAndPhaseShiftToDutyCycleAndPercentDelay(uint16_t pulse_width, + uint16_t phase_shift, + double & duty_cycle, + double & percent_delay); + + void pulseWidthAndPhaseShiftToOnTimeAndOffTime(uint16_t pulse_width, + uint16_t phase_shift, + uint16_t & on_time, + uint16_t & off_time); + void onTimeAndOffTimeToPulseWidthAndPhaseShift(uint16_t on_time, + uint16_t off_time, + uint16_t & pulse_width, + uint16_t & phase_shift); + + void servoPulseDurationToPulseWidthAndPhaseShift(uint16_t pulse_duration_microseconds, + uint16_t & pulse_width, + uint16_t & phase_shift); + void pulseWidthAndPhaseShiftToServoPulseDuration(uint16_t pulse_width, + uint16_t phase_shift, + uint16_t & pulse_duration_microseconds); + + void setOutputsInverted(uint8_t device_index); + void setOutputsNotInverted(uint8_t device_index); + void setOutputsToTotemPole(uint8_t device_index); + void setOutputsToOpenDrain(uint8_t device_index); + void setOutputsLowWhenDisabled(uint8_t device_index); + void setOutputsHighWhenDisabled(uint8_t device_index); + void setOutputsHighImpedanceWhenDisabled(uint8_t device_index); + + const static uint8_t DOES_NOT_RESPOND = 0; + const static uint8_t DOES_RESPOND = 1; + const static uint8_t SUBADR1_REGISTER_ADDRESS = 0x02; + const static uint8_t SUBADR2_REGISTER_ADDRESS = 0x03; + const static uint8_t SUBADR3_REGISTER_ADDRESS = 0x04; + const static uint8_t ALLCALLADR_REGISTER_ADDRESS = 0x05; + + const static uint8_t LED0_ON_L_REGISTER_ADDRESS = 0x06; + const static uint8_t LED0_OFF_L_REGISTER_ADDRESS = 0x08; + const static uint8_t ALL_LED_ON_L_REGISTER_ADDRESS = 0xFA; + const static uint8_t ALL_LED_OFF_L_REGISTER_ADDRESS = 0xFC; + const static uint8_t LED_REGISTERS_SIZE = 4; + const static uint8_t BITS_PER_BYTE = 8; + const static uint8_t BITS_PER_TWO_BYTES = 16; + const static uint8_t BYTE_MAX = 0xFF; + const static uint16_t TWO_BYTE_MAX = 0xFFFF; + + const static uint8_t PRE_SCALE_REGISTER_ADDRESS = 0xFE; + const static uint8_t PRE_SCALE_MIN = 0x03; + const static uint8_t PRE_SCALE_MAX = 0xFF; + // Use period instead of frequency to calculate prescale since it is linear + // Measured 1620 Hz at prescale value 0x03, 1E6/1620=617 + // Datasheet says it should be 1526 Hz, 1E6/1526=655 + const static uint16_t PWM_PERIOD_MIN_US = 617; + // Measured 25.3 Hz at prescale value 0xFF, 1E6/25.3=39525 + // Datasheet says it should be 24 Hz, 1E6/24=41666 + const static uint16_t PWM_PERIOD_MAX_US = 39525; + const static uint32_t MICROSECONDS_PER_SECOND = 1000000; + + const static uint8_t OUTPUTS_INVERTED = 1; + const static uint8_t OUTPUTS_NOT_INVERTED = 0; + const static uint8_t OUTPUTS_TOTEM_POLE = 1; + const static uint8_t OUTPUTS_OPEN_DRAIN = 0; + const static uint8_t OUTPUTS_LOW_WHEN_DISABLED = 0b00; + const static uint8_t OUTPUTS_HIGH_WHEN_DISABLED = 0b01; + const static uint8_t OUTPUTS_HIGH_IMPEDANCE_WHEN_DISABLED = 0b10; + + const static uint8_t SLEEP = 1; + const static uint8_t WAKE = 0; + const static uint8_t AUTO_INCREMENT_ENABLED = 1; + const static uint8_t AUTO_INCREMENT_DISABLED = 0; + const static uint8_t RESTART_ENABLED = 1; + const static uint8_t RESTART_DISABLED = 0; + const static uint8_t RESTART_CLEAR = 1; + + const static uint16_t TIME_MIN = 0; + const static uint16_t TIME_MAX = 4096; + + const static uint8_t PERCENT_MIN = 0; + const static uint8_t PERCENT_MAX = 100; + + const static uint16_t SERVO_FREQUENCY = 50; + const static uint16_t SERVO_PERIOD_MICROSECONDS = 20000; + +}; + +#include "PCA9685/PCA9685Definitions.h" + +#endif diff --git a/INF/libraries/PCA9685/src/PCA9685/PCA9685.cpp b/INF/libraries/PCA9685/src/PCA9685/PCA9685.cpp new file mode 100644 index 0000000..15761d5 --- /dev/null +++ b/INF/libraries/PCA9685/src/PCA9685/PCA9685.cpp @@ -0,0 +1,1005 @@ +// ---------------------------------------------------------------------------- +// PCA9685.cpp +// +// Authors: +// Peter Polidoro peterpolidoro@gmail.com +// ---------------------------------------------------------------------------- +#include "PCA9685.h" + + +PCA9685::PCA9685() +{ + device_count_ = 0; + for (uint8_t device_index=0; device_index 0) + { + frequency = getSingleDeviceFrequency(device_addresses_[0]); + } + return frequency; +} + +void PCA9685::setToServoFrequency() +{ + setAllDevicesToServoFrequency(); +} + +uint16_t PCA9685::getServoFrequency() +{ + return SERVO_FREQUENCY; +} + +uint8_t PCA9685::getChannelCount() +{ + return CHANNELS_PER_DEVICE * device_count_; +} + +double PCA9685::getDutyCycleMin() +{ + return PERCENT_MIN; +} + +double PCA9685::getDutyCycleMax() +{ + return PERCENT_MAX; +} + +double PCA9685::getPercentDelayMin() +{ + return PERCENT_MIN; +} + +double PCA9685::getPercentDelayMax() +{ + return PERCENT_MAX; +} + +void PCA9685::setChannelDutyCycle(uint8_t channel, + double duty_cycle, + double percent_delay) +{ + uint16_t pulse_width; + uint16_t phase_shift; + dutyCycleAndPercentDelayToPulseWidthAndPhaseShift(duty_cycle,percent_delay,pulse_width,phase_shift); + setChannelPulseWidth(channel,pulse_width,phase_shift); +} + +void PCA9685::getChannelDutyCycle(uint8_t channel, + double & duty_cycle, + double & percent_delay) +{ + uint16_t pulse_width; + uint16_t phase_shift; + getChannelPulseWidth(channel,pulse_width,phase_shift); + pulseWidthAndPhaseShiftToDutyCycleAndPercentDelay(pulse_width,phase_shift,duty_cycle,percent_delay); +} + +void PCA9685::setAllChannelsDutyCycle(double duty_cycle, + double percent_delay) +{ + setAllDeviceChannelsDutyCycle(DEVICE_ADDRESS_ALL,duty_cycle,percent_delay); +} + +uint16_t PCA9685::getPulseWidthMin() +{ + return TIME_MIN; +} + +uint16_t PCA9685::getPulseWidthMax() +{ + return TIME_MAX; +} + +uint16_t PCA9685::getPhaseShiftMin() +{ + return TIME_MIN; +} + +uint16_t PCA9685::getPhaseShiftMax() +{ + return 0xFFFF; +} + +void PCA9685::setChannelPulseWidth(uint8_t channel, + uint16_t pulse_width, + uint16_t phase_shift) +{ + uint16_t on_time; + uint16_t off_time; + pulseWidthAndPhaseShiftToOnTimeAndOffTime(pulse_width,phase_shift,on_time,off_time); + setChannelOnAndOffTime(channel,on_time,off_time); +} + +void PCA9685::getChannelPulseWidth(uint8_t channel, + uint16_t & pulse_width, + uint16_t & phase_shift) +{ + uint16_t on_time = 0; + uint16_t off_time = 0; + getChannelOnAndOffTime(channel,on_time,off_time); + onTimeAndOffTimeToPulseWidthAndPhaseShift(on_time,off_time,pulse_width,phase_shift); +} + +void PCA9685::setAllChannelsPulseWidth(uint16_t pulse_width, + uint16_t phase_shift) +{ + setAllDeviceChannelsPulseWidth(DEVICE_ADDRESS_ALL,pulse_width,phase_shift); +} + +void PCA9685::setChannelServoPulseDuration(uint8_t channel, + uint16_t pulse_duration_microseconds) +{ + uint16_t pulse_width; + uint16_t phase_shift; + servoPulseDurationToPulseWidthAndPhaseShift(pulse_duration_microseconds,pulse_width,phase_shift); + setChannelPulseWidth(channel,pulse_width,phase_shift); +} + +void PCA9685::getChannelServoPulseDuration(uint8_t channel, + uint16_t & pulse_duration_microseconds) +{ + uint16_t pulse_width; + uint16_t phase_shift; + getChannelPulseWidth(channel,pulse_width,phase_shift); + pulseWidthAndPhaseShiftToServoPulseDuration(pulse_width,phase_shift,pulse_duration_microseconds); +} + +void PCA9685::setAllChannelsServoPulseDuration(uint16_t pulse_duration_microseconds) +{ + setAllDeviceChannelsServoPulseDuration(DEVICE_ADDRESS_ALL,pulse_duration_microseconds); +} + +uint16_t PCA9685::getTimeMin() +{ + return TIME_MIN; +} + +uint16_t PCA9685::getTimeMax() +{ + return TIME_MAX; +} + +void PCA9685::setChannelOnAndOffTime(uint8_t channel, + uint16_t on_time, + uint16_t off_time) +{ + if (channel >= getChannelCount()) + { + return; + } + uint8_t device_index = channelToDeviceIndex(channel); + uint8_t device_channel = channelToDeviceChannel(channel); + uint8_t register_address = LED0_ON_L_REGISTER_ADDRESS + LED_REGISTERS_SIZE * device_channel; + uint32_t data = (off_time << BITS_PER_TWO_BYTES) | on_time; + write(device_addresses_[device_index],register_address,data); +} + +void PCA9685::getChannelOnAndOffTime(uint8_t channel, + uint16_t & on_time, + uint16_t & off_time) +{ + if (channel >= getChannelCount()) + { + return; + } + uint8_t device_index = channelToDeviceIndex(channel); + uint8_t device_channel = channelToDeviceChannel(channel); + uint8_t register_address = LED0_ON_L_REGISTER_ADDRESS + LED_REGISTERS_SIZE * device_channel; + uint32_t data; + read(device_index,register_address,data); + on_time = data & TWO_BYTE_MAX; + off_time = (data >> BITS_PER_TWO_BYTES) & TWO_BYTE_MAX; +} + +void PCA9685::setAllChannelsOnAndOffTime(uint16_t on_time, + uint16_t off_time) +{ + setAllDeviceChannelsOnAndOffTime(DEVICE_ADDRESS_ALL,on_time,off_time); +} + +void PCA9685::setChannelOnTime(uint8_t channel, + uint16_t on_time) +{ + if (channel >= getChannelCount()) + { + return; + } + uint8_t device_index = channelToDeviceIndex(channel); + uint8_t device_channel = channelToDeviceChannel(channel); + uint8_t register_address = LED0_ON_L_REGISTER_ADDRESS + LED_REGISTERS_SIZE * device_channel; + write(device_addresses_[device_index],register_address,on_time); +} + +void PCA9685::getChannelOnTime(uint8_t channel, + uint16_t & on_time) +{ + if (channel >= getChannelCount()) + { + return; + } + uint8_t device_index = channelToDeviceIndex(channel); + uint8_t device_channel = channelToDeviceChannel(channel); + uint8_t register_address = LED0_ON_L_REGISTER_ADDRESS + LED_REGISTERS_SIZE * device_channel; + read(device_index,register_address,on_time); +} + +void PCA9685::setAllChannelsOnTime(uint16_t on_time) +{ + setAllDeviceChannelsOnTime(DEVICE_ADDRESS_ALL,on_time); +} + +void PCA9685::setChannelOffTime(uint8_t channel, + uint16_t off_time) +{ + if (channel >= getChannelCount()) + { + return; + } + uint8_t device_index = channelToDeviceIndex(channel); + uint8_t device_channel = channelToDeviceChannel(channel); + uint8_t register_address = LED0_OFF_L_REGISTER_ADDRESS + LED_REGISTERS_SIZE * device_channel; + write(device_addresses_[device_index],register_address,off_time); +} + +void PCA9685::getChannelOffTime(uint8_t channel, + uint16_t & off_time) +{ + if (channel >= getChannelCount()) + { + return; + } + uint8_t device_index = channelToDeviceIndex(channel); + uint8_t device_channel = channelToDeviceChannel(channel); + uint8_t register_address = LED0_OFF_L_REGISTER_ADDRESS + LED_REGISTERS_SIZE * device_channel; + read(device_index,register_address,off_time); +} + +void PCA9685::setAllChannelsOffTime(uint16_t off_time) +{ + setAllDeviceChannelsOffTime(DEVICE_ADDRESS_ALL,off_time); +} + +void PCA9685::setOutputsInverted() +{ + setAllDevicesOutputsInverted(); +} + +void PCA9685::setOutputsNotInverted() +{ + setAllDevicesOutputsNotInverted(); +} + +void PCA9685::setOutputsToTotemPole() +{ + setAllDevicesOutputsToTotemPole(); +} + +void PCA9685::setOutputsToOpenDrain() +{ + setAllDevicesOutputsToOpenDrain(); +} + +void PCA9685::setOutputsLowWhenDisabled() +{ + setAllDevicesOutputsLowWhenDisabled(); +} + +void PCA9685::setOutputsHighWhenDisabled() +{ + setAllDevicesOutputsHighWhenDisabled(); +} + +void PCA9685::setOutputsHighImpedanceWhenDisabled() +{ + setAllDevicesOutputsHighImpedanceWhenDisabled(); +} + +void PCA9685::setWire(TwoWire & wire, + bool fast_mode_plus) +{ + wire_ptr_ = &wire; + wire_ptr_->begin(); + if (fast_mode_plus) + { + wire_ptr_->setClock(FAST_MODE_PLUS_CLOCK_FREQUENCY); + } + +} + +void PCA9685::addDevice(uint8_t device_address) +{ + if ((device_count_ >= DEVICE_COUNT_MAX) || + (device_address < DEVICE_ADDRESS_MIN) || + (device_address > DEVICE_ADDRESS_MAX)) + { + return; + } + int device_index = deviceAddressToDeviceIndex(device_address); + if (device_index != DEVICE_INDEX_NONE) + { + return; + } + device_addresses_[device_count_++] = device_address; +} + +void PCA9685::resetAllDevices() +{ + wire_ptr_->beginTransmission(GENERAL_CALL_DEVICE_ADDRESS); + wire_ptr_->write(SWRST); + wire_ptr_->endTransmission(); + delay(10); + wakeAll(); +} + +void PCA9685::addDeviceToGroup0(uint8_t device_address) +{ + int device_index = deviceAddressToDeviceIndex(device_address); + if (device_index < 0) + { + return; + } + Mode1Register mode1_register = readMode1Register(device_index); + mode1_register.fields.sub1 = DOES_RESPOND; + write(device_address,MODE1_REGISTER_ADDRESS,mode1_register.data); +} + +void PCA9685::removeDeviceFromGroup0(uint8_t device_address) +{ + int device_index = deviceAddressToDeviceIndex(device_address); + if (device_index < 0) + { + return; + } + Mode1Register mode1_register = readMode1Register(device_index); + mode1_register.fields.sub1 = DOES_NOT_RESPOND; + write(device_address,MODE1_REGISTER_ADDRESS,mode1_register.data); +} + +void PCA9685::addDeviceToGroup1(uint8_t device_address) +{ + int device_index = deviceAddressToDeviceIndex(device_address); + if (device_index < 0) + { + return; + } + Mode1Register mode1_register = readMode1Register(device_index); + mode1_register.fields.sub2 = DOES_RESPOND; + write(device_address,MODE1_REGISTER_ADDRESS,mode1_register.data); +} + +void PCA9685::removeDeviceFromGroup1(uint8_t device_address) +{ + int device_index = deviceAddressToDeviceIndex(device_address); + if (device_index < 0) + { + return; + } + Mode1Register mode1_register = readMode1Register(device_index); + mode1_register.fields.sub2 = DOES_NOT_RESPOND; + write(device_address,MODE1_REGISTER_ADDRESS,mode1_register.data); +} + +void PCA9685::addDeviceToGroup2(uint8_t device_address) +{ + int device_index = deviceAddressToDeviceIndex(device_address); + if (device_index < 0) + { + return; + } + Mode1Register mode1_register = readMode1Register(device_index); + mode1_register.fields.sub3 = DOES_RESPOND; + write(device_address,MODE1_REGISTER_ADDRESS,mode1_register.data); +} + +void PCA9685::removeDeviceFromGroup2(uint8_t device_address) +{ + int device_index = deviceAddressToDeviceIndex(device_address); + if (device_index < 0) + { + return; + } + Mode1Register mode1_register = readMode1Register(device_index); + mode1_register.fields.sub3 = DOES_NOT_RESPOND; + write(device_address,MODE1_REGISTER_ADDRESS,mode1_register.data); +} + +void PCA9685::setSingleDeviceToFrequency(uint8_t device_address, + uint16_t frequency) +{ + int device_index = deviceAddressToDeviceIndex(device_address); + if (device_index < 0) + { + return; + } + uint8_t prescale = frequencyToPrescale(frequency); + setPrescale(device_index,prescale); +} + +uint16_t PCA9685::getSingleDeviceFrequency(uint8_t device_address) +{ + uint16_t frequency = 0; + int device_index = deviceAddressToDeviceIndex(device_address); + if (device_index >= 0) + { + uint8_t prescale; + getPrescale(device_index,prescale); + frequency = prescaleToFrequency(prescale); + } + return frequency; +} + +void PCA9685::setAllDevicesToFrequency(uint16_t frequency) +{ + uint8_t prescale = frequencyToPrescale(frequency); + for (uint8_t device_index=0; device_index= getDeviceChannelCount()) + { + return; + } + uint8_t register_address = LED0_ON_L_REGISTER_ADDRESS + LED_REGISTERS_SIZE * device_channel; + uint32_t data = (off_time << BITS_PER_TWO_BYTES) | on_time; + write(device_address,register_address,data); +} + +void PCA9685::setAllDeviceChannelsOnAndOffTime(uint8_t device_address, + uint16_t on_time, + uint16_t off_time) +{ + uint8_t register_address = ALL_LED_ON_L_REGISTER_ADDRESS; + uint32_t data = (off_time << BITS_PER_TWO_BYTES) | on_time; + write(device_address,register_address,data); +} + +void PCA9685::setDeviceChannelOnTime(uint8_t device_address, + uint8_t device_channel, + uint16_t on_time) +{ + if (device_channel >= getDeviceChannelCount()) + { + return; + } + uint8_t register_address = LED0_ON_L_REGISTER_ADDRESS + LED_REGISTERS_SIZE * device_channel; + write(device_address,register_address,on_time); +} + +void PCA9685::setAllDeviceChannelsOnTime(uint8_t device_address, + uint16_t on_time) +{ + uint8_t register_address = ALL_LED_ON_L_REGISTER_ADDRESS; + write(device_address,register_address,on_time); +} + +void PCA9685::setDeviceChannelOffTime(uint8_t device_address, + uint8_t device_channel, + uint16_t off_time) +{ + if (device_channel >= getDeviceChannelCount()) + { + return; + } + uint8_t register_address = LED0_OFF_L_REGISTER_ADDRESS + LED_REGISTERS_SIZE * device_channel; + write(device_address,register_address,off_time); +} + +void PCA9685::setAllDeviceChannelsOffTime(uint8_t device_address, + uint16_t off_time) +{ + uint8_t register_address = ALL_LED_OFF_L_REGISTER_ADDRESS; + write(device_address,register_address,off_time); +} + +void PCA9685::setSingleDeviceOutputsInverted(uint8_t device_address) +{ + int device_index = deviceAddressToDeviceIndex(device_address); + if (device_index < 0) + { + return; + } + setOutputsInverted(device_index); +} + +void PCA9685::setAllDevicesOutputsInverted() +{ + for (uint8_t device_index=0; device_index 0) + { + frequency = round((double)MICROSECONDS_PER_SECOND / (double)period_us); + } + return frequency; +} + +uint8_t PCA9685::channelToDeviceIndex(uint8_t channel) +{ + return channel / CHANNELS_PER_DEVICE; +} + +uint8_t PCA9685::channelToDeviceChannel(uint8_t channel) +{ + return channel % CHANNELS_PER_DEVICE; +} + +void PCA9685::dutyCycleAndPercentDelayToPulseWidthAndPhaseShift(double duty_cycle, + double percent_delay, + uint16_t & pulse_width, + uint16_t & phase_shift) +{ + pulse_width = round(((double)TIME_MAX * duty_cycle) / (double)PERCENT_MAX); + phase_shift = round(((double)TIME_MAX * percent_delay) / (double)PERCENT_MAX); +} + +void PCA9685::pulseWidthAndPhaseShiftToDutyCycleAndPercentDelay(uint16_t pulse_width, + uint16_t phase_shift, + double & duty_cycle, + double & percent_delay) +{ + duty_cycle = (double)(pulse_width * PERCENT_MAX) / (double)TIME_MAX; + percent_delay = (double)(phase_shift * PERCENT_MAX) / (double)TIME_MAX; +} + +void PCA9685::pulseWidthAndPhaseShiftToOnTimeAndOffTime(uint16_t pulse_width, + uint16_t phase_shift, + uint16_t & on_time, + uint16_t & off_time) +{ + if (pulse_width == TIME_MIN) + { + on_time = TIME_MIN; + off_time = TIME_MAX; + return; + } + if (pulse_width >= TIME_MAX) + { + on_time = TIME_MAX; + off_time = TIME_MIN; + return; + } + on_time = phase_shift % TIME_MAX; + off_time = (on_time + pulse_width) % TIME_MAX; +} + +void PCA9685::onTimeAndOffTimeToPulseWidthAndPhaseShift(uint16_t on_time, + uint16_t off_time, + uint16_t & pulse_width, + uint16_t & phase_shift) +{ + if (on_time == TIME_MAX) + { + pulse_width = TIME_MAX; + phase_shift = TIME_MIN; + return; + } + if (off_time == TIME_MAX) + { + pulse_width = TIME_MIN; + phase_shift = TIME_MIN; + return; + } + if (on_time > off_time) + { + pulse_width = TIME_MAX - (on_time - off_time); + phase_shift = on_time; + return; + } + pulse_width = off_time - on_time; + phase_shift = on_time; +} + +void PCA9685::servoPulseDurationToPulseWidthAndPhaseShift(uint16_t pulse_duration_microseconds, + uint16_t & pulse_width, + uint16_t & phase_shift) +{ + phase_shift = 0; + pulse_width = (pulse_duration_microseconds * TIME_MAX) / SERVO_PERIOD_MICROSECONDS; +} + +void PCA9685::pulseWidthAndPhaseShiftToServoPulseDuration(uint16_t pulse_width, + uint16_t phase_shift, + uint16_t & pulse_duration_microseconds) +{ + uint16_t period_us = SERVO_PERIOD_MICROSECONDS; + pulse_duration_microseconds = (pulse_width * period_us) / TIME_MAX; +} + +void PCA9685::setOutputsInverted(uint8_t device_index) +{ + Mode2Register mode2_register = readMode2Register(device_index); + mode2_register.fields.invrt = OUTPUTS_INVERTED; + write(device_addresses_[device_index],MODE2_REGISTER_ADDRESS,mode2_register.data); +} + +void PCA9685::setOutputsNotInverted(uint8_t device_index) +{ + Mode2Register mode2_register = readMode2Register(device_index); + mode2_register.fields.invrt = OUTPUTS_NOT_INVERTED; + write(device_addresses_[device_index],MODE2_REGISTER_ADDRESS,mode2_register.data); +} + +void PCA9685::setOutputsToTotemPole(uint8_t device_index) +{ + Mode2Register mode2_register = readMode2Register(device_index); + mode2_register.fields.outdrv = OUTPUTS_TOTEM_POLE; + write(device_addresses_[device_index],MODE2_REGISTER_ADDRESS,mode2_register.data); +} + +void PCA9685::setOutputsToOpenDrain(uint8_t device_index) +{ + Mode2Register mode2_register = readMode2Register(device_index); + mode2_register.fields.outdrv = OUTPUTS_OPEN_DRAIN; + write(device_addresses_[device_index],MODE2_REGISTER_ADDRESS,mode2_register.data); +} + +void PCA9685::setOutputsLowWhenDisabled(uint8_t device_index) +{ + Mode2Register mode2_register = readMode2Register(device_index); + mode2_register.fields.outne = OUTPUTS_LOW_WHEN_DISABLED; + write(device_addresses_[device_index],MODE2_REGISTER_ADDRESS,mode2_register.data); +} + +void PCA9685::setOutputsHighWhenDisabled(uint8_t device_index) +{ + Mode2Register mode2_register = readMode2Register(device_index); + mode2_register.fields.outne = OUTPUTS_HIGH_WHEN_DISABLED; + write(device_addresses_[device_index],MODE2_REGISTER_ADDRESS,mode2_register.data); +} + +void PCA9685::setOutputsHighImpedanceWhenDisabled(uint8_t device_index) +{ + Mode2Register mode2_register = readMode2Register(device_index); + mode2_register.fields.outne = OUTPUTS_HIGH_IMPEDANCE_WHEN_DISABLED; + write(device_addresses_[device_index],MODE2_REGISTER_ADDRESS,mode2_register.data); +} diff --git a/INF/libraries/PCA9685/src/PCA9685/PCA9685Definitions.h b/INF/libraries/PCA9685/src/PCA9685/PCA9685Definitions.h new file mode 100644 index 0000000..be8cb60 --- /dev/null +++ b/INF/libraries/PCA9685/src/PCA9685/PCA9685Definitions.h @@ -0,0 +1,46 @@ +// ---------------------------------------------------------------------------- +// PCA9685.h +// +// +// Authors: +// Peter Polidoro peterpolidoro@gmail.com +// ---------------------------------------------------------------------------- +#ifndef PCA9685_DEFINITIONS_H +#define PCA9685_DEFINITIONS_H + + +template +void PCA9685::write(uint8_t device_address, + uint8_t register_address, + T data) +{ + int byte_count = sizeof(data); + wire_ptr_->beginTransmission(device_address); + wire_ptr_->write(register_address); + for (int byte_n=0; byte_nwrite((data >> (BITS_PER_BYTE * byte_n)) & BYTE_MAX); + } + wire_ptr_->endTransmission(); +} + +template +void PCA9685::read(uint8_t device_index, + uint8_t register_address, + T & data) +{ + int byte_count = sizeof(data); + int device_address = device_addresses_[device_index]; + wire_ptr_->beginTransmission(device_address); + wire_ptr_->write(register_address); + wire_ptr_->endTransmission(); + + wire_ptr_->requestFrom(device_address,byte_count); + data = 0; + for (int byte_n=0; byte_nread()) << (BITS_PER_BYTE * byte_n); + } +} + +#endif