├── datasheet └── PCA9685.pdf ├── .gitignore ├── library.properties ├── examples ├── FrequencySweep │ ├── Constants.cpp │ ├── Constants.h │ └── FrequencySweep.ino ├── OffTimeSweepSingleDevice │ ├── Constants.cpp │ ├── Constants.h │ └── OffTimeSweepSingleDevice.ino ├── TestSetAndGet │ ├── Constants.cpp │ ├── Constants.h │ └── TestSetAndGet.ino ├── OnTimeSweepMultipleDevices │ ├── Constants.cpp │ ├── Constants.h │ └── OnTimeSweepMultipleDevices.ino ├── ServoController │ ├── Constants.cpp │ ├── Constants.h │ └── ServoController.ino ├── SetChannelDutyCycle │ ├── SetChannelDutyCycle.ino │ ├── Constants.h │ └── Constants.cpp ├── SetChannelOnAndOffTime │ ├── SetChannelOnAndOffTime.ino │ ├── Constants.h │ └── Constants.cpp └── SetChannelPulseWidth │ ├── SetChannelPulseWidth.ino │ ├── Constants.h │ └── Constants.cpp ├── README.org ├── src ├── PCA9685 │ ├── PCA9685Definitions.h │ └── PCA9685.cpp └── PCA9685.h └── LICENSE /datasheet/PCA9685.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janelia-arduino/PCA9685/HEAD/datasheet/PCA9685.pdf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.idea 3 | /build 4 | /bin 5 | /lib 6 | /sftp-config.json 7 | .tags 8 | .tags_sorted_by_file 9 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=PCA9685 2 | version=3.0.3 3 | author=Peter Polidoro 4 | maintainer=Peter Polidoro 5 | sentence=PCA9685 16-channel 12-bit PWM controller. 6 | paragraph=Like this project? Please star it on GitHub! 7 | category=Device Control 8 | url=https://github.com/janelia-arduino/PCA9685.git 9 | architectures=* 10 | -------------------------------------------------------------------------------- /examples/FrequencySweep/Constants.cpp: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Constants.cpp 3 | // 4 | // 5 | // Authors: 6 | // Peter Polidoro peter@polidoro.io 7 | // ---------------------------------------------------------------------------- 8 | #include "Constants.h" 9 | 10 | 11 | namespace constants 12 | { 13 | const PCA9685::DeviceAddress device_address = 0x40; 14 | const PCA9685::Pin output_enable_pin = 2; 15 | 16 | const size_t loop_delay = 100; 17 | const PCA9685::Frequency frequency_increment = 10; 18 | } 19 | -------------------------------------------------------------------------------- /examples/FrequencySweep/Constants.h: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Constants.h 3 | // 4 | // 5 | // Authors: 6 | // Peter Polidoro peter@polidoro.io 7 | // ---------------------------------------------------------------------------- 8 | #ifndef CONSTANTS_H 9 | #define CONSTANTS_H 10 | #include 11 | #include 12 | 13 | 14 | namespace constants 15 | { 16 | extern const PCA9685::DeviceAddress device_address; 17 | extern const PCA9685::Pin output_enable_pin; 18 | 19 | extern const size_t loop_delay; 20 | extern const PCA9685::Frequency frequency_increment; 21 | } 22 | #endif 23 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | #+TITLE: PCA9685 2 | #+AUTHOR: Peter Polidoro 3 | #+EMAIL: peter@polidoro.io 4 | 5 | * Library Information 6 | - Name :: PCA9685 7 | - Version :: 3.0.3 8 | - License :: BSD 9 | - URL :: https://github.com/janelia-arduino/PCA9685 10 | - Author :: Peter Polidoro 11 | - Email :: peter@polidoro.io 12 | 13 | ** Description 14 | 15 | The PCA9685 is a 16-channel 12-bit PWM controller. 16 | 17 | All channels operate at a programmable frequency between roughly 25 to 1600 Hz 18 | with 16 independent 12-bit duty cycles from 0 to 100%. 19 | 20 | Supports using a single device (for up to 16 channels) or up to 55 devices 21 | (for up to 880 channels). 22 | -------------------------------------------------------------------------------- /examples/OffTimeSweepSingleDevice/Constants.cpp: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Constants.cpp 3 | // 4 | // 5 | // Authors: 6 | // Peter Polidoro peter@polidoro.io 7 | // ---------------------------------------------------------------------------- 8 | #include "Constants.h" 9 | 10 | 11 | namespace constants 12 | { 13 | const PCA9685::DeviceAddress device_address = 0x40; 14 | const PCA9685::Pin output_enable_pin = 2; 15 | 16 | const size_t loop_delay = 100; 17 | const PCA9685::Frequency frequency = 200; 18 | const PCA9685::Time time_increment = 100; 19 | 20 | const PCA9685::Channel channel = 0; 21 | } 22 | -------------------------------------------------------------------------------- /examples/TestSetAndGet/Constants.cpp: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Constants.cpp 3 | // 4 | // 5 | // Authors: 6 | // Peter Polidoro peter@polidoro.io 7 | // ---------------------------------------------------------------------------- 8 | #include "Constants.h" 9 | 10 | 11 | namespace constants 12 | { 13 | const PCA9685::DeviceAddress device_address = 0x40; 14 | const PCA9685::Pin output_enable_pin = 2; 15 | 16 | const long baud = 115200; 17 | 18 | const size_t loop_delay = 100; 19 | const PCA9685::Frequency frequency = 200; 20 | const PCA9685::Time time_increment = 400; 21 | const PCA9685::Percent epsilon = 0.001; 22 | } 23 | -------------------------------------------------------------------------------- /examples/OffTimeSweepSingleDevice/Constants.h: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Constants.h 3 | // 4 | // 5 | // Authors: 6 | // Peter Polidoro peter@polidoro.io 7 | // ---------------------------------------------------------------------------- 8 | #ifndef CONSTANTS_H 9 | #define CONSTANTS_H 10 | #include 11 | #include 12 | 13 | 14 | namespace constants 15 | { 16 | extern const PCA9685::DeviceAddress device_address; 17 | extern const PCA9685::Pin output_enable_pin; 18 | 19 | extern const size_t loop_delay; 20 | extern const PCA9685::Frequency frequency; 21 | extern const PCA9685::Time time_increment; 22 | 23 | extern const PCA9685::Channel channel; 24 | } 25 | #endif 26 | -------------------------------------------------------------------------------- /examples/TestSetAndGet/Constants.h: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Constants.h 3 | // 4 | // 5 | // Authors: 6 | // Peter Polidoro peter@polidoro.io 7 | // ---------------------------------------------------------------------------- 8 | #ifndef CONSTANTS_H 9 | #define CONSTANTS_H 10 | #include 11 | #include 12 | 13 | 14 | namespace constants 15 | { 16 | extern const PCA9685::DeviceAddress device_address; 17 | extern const PCA9685::Pin output_enable_pin; 18 | 19 | extern const long baud; 20 | extern const size_t loop_delay; 21 | extern const PCA9685::Frequency frequency; 22 | extern const PCA9685::Time time_increment; 23 | extern const PCA9685::Percent epsilon; 24 | } 25 | #endif 26 | -------------------------------------------------------------------------------- /examples/OnTimeSweepMultipleDevices/Constants.cpp: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Constants.cpp 3 | // 4 | // 5 | // Authors: 6 | // Peter Polidoro peter@polidoro.io 7 | // ---------------------------------------------------------------------------- 8 | #include "Constants.h" 9 | 10 | 11 | namespace constants 12 | { 13 | const PCA9685::DeviceAddress device_addresses[DEVICE_COUNT] = 14 | { 15 | 0x40, 16 | 0x41, 17 | 0x42 18 | }; 19 | const PCA9685::DeviceIndex = 0; 20 | 21 | const PCA9685::Pin output_enable_pin = 2; 22 | 23 | const size_t loop_delay = 100; 24 | const PCA9685::Frequency frequency = 200; 25 | const PCA9685::Time time_increment = 100; 26 | 27 | const PCA9685::Channel channel = 0; 28 | } 29 | -------------------------------------------------------------------------------- /examples/ServoController/Constants.cpp: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Constants.cpp 3 | // 4 | // 5 | // Authors: 6 | // Peter Polidoro peter@polidoro.io 7 | // ---------------------------------------------------------------------------- 8 | #include "Constants.h" 9 | 10 | 11 | namespace constants 12 | { 13 | const PCA9685::DeviceAddress device_address = 0x40; 14 | const PCA9685::Pin output_enable_pin = 2; 15 | 16 | const size_t loop_delay = 100; 17 | 18 | const PCA9685::Channel channel = 0; 19 | 20 | const PCA9685::DurationMicroseconds servo_pulse_duration_min = 900; 21 | const PCA9685::DurationMicroseconds servo_pulse_duration_max = 2100; 22 | const PCA9685::DurationMicroseconds servo_pulse_duration_increment = 100; 23 | } 24 | -------------------------------------------------------------------------------- /examples/OnTimeSweepMultipleDevices/Constants.h: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Constants.h 3 | // 4 | // 5 | // Authors: 6 | // Peter Polidoro peter@polidoro.io 7 | // ---------------------------------------------------------------------------- 8 | #ifndef CONSTANTS_H 9 | #define CONSTANTS_H 10 | #include 11 | #include 12 | 13 | 14 | namespace constants 15 | { 16 | enum{DEVICE_COUNT=3}; 17 | extern const PCA9685::DeviceAddress device_addresses[DEVICE_COUNT]; 18 | extern const PCA9685::DeviceIndex; 19 | 20 | extern const PCA9685::Pin output_enable_pin; 21 | 22 | extern const size_t loop_delay; 23 | extern const PCA9685::Frequency frequency; 24 | extern const PCA9685::Time time_increment; 25 | 26 | extern const PCA9685::Channel channel; 27 | } 28 | #endif 29 | -------------------------------------------------------------------------------- /examples/SetChannelDutyCycle/SetChannelDutyCycle.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "Constants.h" 5 | 6 | 7 | PCA9685 pca9685; 8 | 9 | uint8_t example_index; 10 | 11 | void setup() 12 | { 13 | pca9685.setupSingleDevice(Wire,constants::device_address); 14 | 15 | pca9685.setupOutputEnablePin(constants::output_enable_pin); 16 | pca9685.enableOutputs(constants::output_enable_pin); 17 | 18 | pca9685.setToFrequency(constants::frequency); 19 | 20 | example_index = 0; 21 | } 22 | 23 | void loop() 24 | { 25 | delay(constants::loop_delay); 26 | if (example_index >= constants::EXAMPLE_COUNT) 27 | { 28 | example_index = 0; 29 | } 30 | constants::Example example = constants::examples[example_index++]; 31 | 32 | pca9685.setChannelDutyCycle(constants::channel,example.duty_cycle,example.percent_delay); 33 | } 34 | -------------------------------------------------------------------------------- /examples/SetChannelOnAndOffTime/SetChannelOnAndOffTime.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "Constants.h" 5 | 6 | 7 | PCA9685 pca9685; 8 | 9 | uint8_t example_index; 10 | 11 | void setup() 12 | { 13 | pca9685.setupSingleDevice(Wire,constants::device_address); 14 | 15 | pca9685.setupOutputEnablePin(constants::output_enable_pin); 16 | pca9685.enableOutputs(constants::output_enable_pin); 17 | 18 | pca9685.setToFrequency(constants::frequency); 19 | 20 | example_index = 0; 21 | } 22 | 23 | void loop() 24 | { 25 | delay(constants::loop_delay); 26 | if (example_index >= constants::EXAMPLE_COUNT) 27 | { 28 | example_index = 0; 29 | } 30 | constants::Example example = constants::examples[example_index++]; 31 | 32 | pca9685.setChannelOnAndOffTime(constants::channel,example.on_time,example.off_time); 33 | } 34 | -------------------------------------------------------------------------------- /examples/SetChannelPulseWidth/SetChannelPulseWidth.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "Constants.h" 5 | 6 | 7 | PCA9685 pca9685; 8 | 9 | uint8_t example_index; 10 | 11 | void setup() 12 | { 13 | pca9685.setupSingleDevice(Wire,constants::device_address); 14 | 15 | pca9685.setupOutputEnablePin(constants::output_enable_pin); 16 | pca9685.enableOutputs(constants::output_enable_pin); 17 | 18 | pca9685.setToFrequency(constants::frequency); 19 | 20 | example_index = 0; 21 | } 22 | 23 | void loop() 24 | { 25 | delay(constants::loop_delay); 26 | if (example_index >= constants::EXAMPLE_COUNT) 27 | { 28 | example_index = 0; 29 | } 30 | constants::Example example = constants::examples[example_index++]; 31 | 32 | pca9685.setChannelPulseWidth(constants::channel,example.pulse_width,example.phase_shift); 33 | } 34 | -------------------------------------------------------------------------------- /examples/ServoController/Constants.h: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Constants.h 3 | // 4 | // 5 | // Authors: 6 | // Peter Polidoro peter@polidoro.io 7 | // ---------------------------------------------------------------------------- 8 | #ifndef CONSTANTS_H 9 | #define CONSTANTS_H 10 | #include 11 | #include 12 | 13 | 14 | namespace constants 15 | { 16 | extern const PCA9685::DeviceAddress device_address; 17 | extern const PCA9685::Pin output_enable_pin; 18 | 19 | extern const size_t loop_delay; 20 | 21 | extern const PCA9685::Channel channel; 22 | 23 | extern const PCA9685::DurationMicroseconds servo_pulse_duration_min; 24 | extern const PCA9685::DurationMicroseconds servo_pulse_duration_max; 25 | extern const PCA9685::DurationMicroseconds servo_pulse_duration_increment; 26 | } 27 | #endif 28 | -------------------------------------------------------------------------------- /examples/SetChannelOnAndOffTime/Constants.h: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Constants.h 3 | // 4 | // 5 | // Authors: 6 | // Peter Polidoro peter@polidoro.io 7 | // ---------------------------------------------------------------------------- 8 | #ifndef CONSTANTS_H 9 | #define CONSTANTS_H 10 | #include 11 | #include 12 | 13 | 14 | namespace constants 15 | { 16 | extern const PCA9685::DeviceAddress device_address; 17 | extern const PCA9685::Pin output_enable_pin; 18 | 19 | extern const size_t loop_delay; 20 | extern const PCA9685::Frequency frequency; 21 | extern const PCA9685::Channel channel; 22 | 23 | enum{EXAMPLE_COUNT=4}; 24 | 25 | struct Example 26 | { 27 | PCA9685::Time on_time; 28 | PCA9685::Time off_time; 29 | }; 30 | 31 | extern const Example examples[EXAMPLE_COUNT]; 32 | } 33 | #endif 34 | -------------------------------------------------------------------------------- /examples/SetChannelDutyCycle/Constants.h: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Constants.h 3 | // 4 | // 5 | // Authors: 6 | // Peter Polidoro peter@polidoro.io 7 | // ---------------------------------------------------------------------------- 8 | #ifndef CONSTANTS_H 9 | #define CONSTANTS_H 10 | #include 11 | #include 12 | 13 | 14 | namespace constants 15 | { 16 | extern const PCA9685::DeviceAddress device_address; 17 | extern const PCA9685::Pin output_enable_pin; 18 | 19 | extern const size_t loop_delay; 20 | extern const PCA9685::Frequency frequency; 21 | extern const PCA9685::Channel channel; 22 | 23 | enum{EXAMPLE_COUNT=4}; 24 | 25 | struct Example 26 | { 27 | PCA9685::Percent duty_cycle; 28 | PCA9685::Percent percent_delay; 29 | }; 30 | 31 | extern const Example examples[EXAMPLE_COUNT]; 32 | } 33 | #endif 34 | -------------------------------------------------------------------------------- /examples/SetChannelPulseWidth/Constants.h: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Constants.h 3 | // 4 | // 5 | // Authors: 6 | // Peter Polidoro peter@polidoro.io 7 | // ---------------------------------------------------------------------------- 8 | #ifndef CONSTANTS_H 9 | #define CONSTANTS_H 10 | #include 11 | #include 12 | 13 | 14 | namespace constants 15 | { 16 | extern const PCA9685::DeviceAddress device_address; 17 | extern const PCA9685::Pin output_enable_pin; 18 | 19 | extern const size_t loop_delay; 20 | extern const PCA9685::Frequency frequency; 21 | extern const PCA9685::Channel channel; 22 | 23 | enum{EXAMPLE_COUNT=4}; 24 | 25 | struct Example 26 | { 27 | PCA9685::Duration pulse_width; 28 | PCA9685::Duration phase_shift; 29 | }; 30 | 31 | extern const Example examples[EXAMPLE_COUNT]; 32 | } 33 | #endif 34 | -------------------------------------------------------------------------------- /examples/ServoController/ServoController.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "Constants.h" 5 | 6 | 7 | PCA9685 pca9685; 8 | 9 | PCA9685::DurationMicroseconds servo_pulse_duration; 10 | 11 | void setup() 12 | { 13 | pca9685.setupSingleDevice(Wire,constants::device_address); 14 | 15 | pca9685.setupOutputEnablePin(constants::output_enable_pin); 16 | pca9685.enableOutputs(constants::output_enable_pin); 17 | 18 | pca9685.setToServoFrequency(); 19 | 20 | servo_pulse_duration = constants::servo_pulse_duration_min; 21 | } 22 | 23 | void loop() 24 | { 25 | if (servo_pulse_duration > constants::servo_pulse_duration_max) 26 | { 27 | servo_pulse_duration = constants::servo_pulse_duration_min; 28 | } 29 | pca9685.setChannelServoPulseDuration(constants::channel,servo_pulse_duration); 30 | servo_pulse_duration += constants::servo_pulse_duration_increment; 31 | delay(constants::loop_delay); 32 | } 33 | -------------------------------------------------------------------------------- /examples/SetChannelPulseWidth/Constants.cpp: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Constants.cpp 3 | // 4 | // 5 | // Authors: 6 | // Peter Polidoro peter@polidoro.io 7 | // ---------------------------------------------------------------------------- 8 | #include "Constants.h" 9 | 10 | 11 | namespace constants 12 | { 13 | const PCA9685::DeviceAddress device_address = 0x40; 14 | const PCA9685::Pin output_enable_pin = 2; 15 | 16 | const size_t loop_delay = 8000; 17 | const PCA9685::Frequency frequency = 200; 18 | const PCA9685::Channel channel = 0; 19 | 20 | const Example examples[EXAMPLE_COUNT] = 21 | { 22 | { 23 | 819, // PULSE_WIDTH 24 | 409, // PHASE_SHIFT 25 | }, 26 | { 27 | 3686, // PULSE_WIDTH 28 | 3685, // PHASE_SHIFT 29 | }, 30 | { 31 | // Always ON 32 | 4096, // PULSE_WIDTH 33 | 0, // PHASE_SHIFT 34 | }, 35 | { 36 | // Always OFF 37 | 0, // PULSE_WIDTH 38 | 0, // PHASE_SHIFT 39 | } 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /examples/SetChannelDutyCycle/Constants.cpp: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Constants.cpp 3 | // 4 | // 5 | // Authors: 6 | // Peter Polidoro peter@polidoro.io 7 | // ---------------------------------------------------------------------------- 8 | #include "Constants.h" 9 | 10 | 11 | namespace constants 12 | { 13 | const PCA9685::DeviceAddress device_address = 0x40; 14 | const PCA9685::Pin output_enable_pin = 2; 15 | 16 | const size_t loop_delay = 8000; 17 | const PCA9685::Frequency frequency = 200; 18 | const PCA9685::Channel channel = 0; 19 | 20 | const Example examples[EXAMPLE_COUNT] = 21 | { 22 | { 23 | 20.0, // DUTY_CYCLE 24 | 10.0, // PERCENT_DELAY 25 | }, 26 | { 27 | 90.0, // DUTY_CYCLE 28 | 90.0, // PERCENT_DELAY 29 | }, 30 | { 31 | // Always ON 32 | 100.0, // DUTY_CYCLE 33 | 0.0, // PERCENT_DELAY 34 | }, 35 | { 36 | // Always OFF 37 | 0.0, // DUTY_CYCLE 38 | 0.0, // PERCENT_DELAY 39 | } 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /examples/OffTimeSweepSingleDevice/OffTimeSweepSingleDevice.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "Constants.h" 5 | 6 | 7 | PCA9685 pca9685; 8 | 9 | PCA9685::Time time_min; 10 | PCA9685::Time time_max; 11 | PCA9685::Time off_time; 12 | 13 | void setup() 14 | { 15 | pca9685.setupSingleDevice(Wire,constants::device_address); 16 | 17 | pca9685.setupOutputEnablePin(constants::output_enable_pin); 18 | pca9685.enableOutputs(constants::output_enable_pin); 19 | 20 | pca9685.setOutputsNotInverted(); 21 | 22 | pca9685.setToFrequency(constants::frequency); 23 | 24 | time_min = pca9685.getTimeMin(); 25 | time_max = pca9685.getTimeMax(); 26 | 27 | off_time = time_min; 28 | 29 | pca9685.setChannelOnTime(constants::channel,time_min); 30 | } 31 | 32 | void loop() 33 | { 34 | if (off_time > time_max) 35 | { 36 | off_time = time_min; 37 | } 38 | pca9685.setChannelOffTime(constants::channel,off_time); 39 | off_time += constants::time_increment; 40 | delay(constants::loop_delay); 41 | } 42 | -------------------------------------------------------------------------------- /examples/SetChannelOnAndOffTime/Constants.cpp: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Constants.cpp 3 | // 4 | // 5 | // Authors: 6 | // Peter Polidoro peter@polidoro.io 7 | // ---------------------------------------------------------------------------- 8 | #include "Constants.h" 9 | 10 | 11 | namespace constants 12 | { 13 | const PCA9685::DeviceAddress device_address = 0x40; 14 | const PCA9685::Pin output_enable_pin = 2; 15 | 16 | const size_t loop_delay = 8000; 17 | const PCA9685::Frequency frequency = 200; 18 | const PCA9685::Channel channel = 0; 19 | 20 | const Example examples[EXAMPLE_COUNT] = 21 | { 22 | { 23 | // ON_TIME < OFF_TIME 24 | 511, // ON_TIME 25 | 3071, // OFF_TIME 26 | }, 27 | { 28 | // ON_TIME < OFF_TIME 29 | 2047, // ON_TIME 30 | 767, // OFF_TIME 31 | }, 32 | { 33 | // Always ON 34 | 4096, // ON_TIME 35 | 0, // OFF_TIME 36 | }, 37 | { 38 | // Always OFF 39 | 0, // ON_TIME 40 | 4096, // OFF_TIME 41 | } 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /examples/FrequencySweep/FrequencySweep.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "Constants.h" 5 | 6 | 7 | PCA9685 pca9685; 8 | 9 | PCA9685::Frequency frequency_min; 10 | PCA9685::Frequency frequency_max; 11 | PCA9685::Frequency frequency; 12 | 13 | void setup() 14 | { 15 | pca9685.setupSingleDevice(Wire,constants::device_address); 16 | 17 | pca9685.setupOutputEnablePin(constants::output_enable_pin); 18 | pca9685.enableOutputs(constants::output_enable_pin); 19 | 20 | pca9685.setOutputsNotInverted(); 21 | 22 | PCA9685::Time time_min = pca9685.getTimeMin(); 23 | PCA9685::Time time_max = pca9685.getTimeMax(); 24 | pca9685.setAllChannelsOnAndOffTime(time_min,time_max/4); 25 | 26 | frequency_min = pca9685.getFrequencyMin(); 27 | frequency_max = pca9685.getFrequencyMax(); 28 | 29 | frequency = frequency_min; 30 | } 31 | 32 | void loop() 33 | { 34 | if (frequency > frequency_max) 35 | { 36 | frequency = frequency_min; 37 | } 38 | pca9685.setToFrequency(frequency); 39 | frequency += constants::frequency_increment; 40 | delay(constants::loop_delay); 41 | } 42 | -------------------------------------------------------------------------------- /examples/OnTimeSweepMultipleDevices/OnTimeSweepMultipleDevices.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "Constants.h" 5 | 6 | 7 | PCA9685 pca9685; 8 | 9 | PCA9685::Time time_min; 10 | PCA9685::Time time_max; 11 | PCA9685::Time on_time; 12 | 13 | void setup() 14 | { 15 | pca9685.setWire(Wire); 16 | for (PCA9685::DeviceIndex=0; device_index time_max) 40 | { 41 | on_time = time_min; 42 | } 43 | pca9685.setAllDeviceChannelsOnTime(constants::device_addresses[constants::device_index],on_time); 44 | on_time += constants::time_increment; 45 | delay(constants::loop_delay); 46 | } 47 | -------------------------------------------------------------------------------- /src/PCA9685/PCA9685Definitions.h: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // PCA9685.h 3 | // 4 | // 5 | // Authors: 6 | // Peter Polidoro peter@polidoro.io 7 | // ---------------------------------------------------------------------------- 8 | #ifndef PCA9685_DEFINITIONS_H 9 | #define PCA9685_DEFINITIONS_H 10 | 11 | 12 | template 13 | void PCA9685::write(DeviceAddress device_address, 14 | uint8_t register_address, 15 | T data) 16 | { 17 | int byte_count = sizeof(data); 18 | wire_ptr_->beginTransmission(device_address); 19 | wire_ptr_->write(register_address); 20 | uint8_t write_byte; 21 | for (int byte_n=0; byte_n> (BITS_PER_BYTE * byte_n)) & BYTE_MAX; 24 | wire_ptr_->write(write_byte); 25 | } 26 | wire_ptr_->endTransmission(); 27 | } 28 | 29 | template 30 | void PCA9685::read(DeviceIndex device_index, 31 | uint8_t register_address, 32 | T & data) 33 | { 34 | int byte_count = sizeof(data); 35 | int device_address = device_addresses_[device_index]; 36 | wire_ptr_->beginTransmission(device_address); 37 | wire_ptr_->write(register_address); 38 | wire_ptr_->endTransmission(); 39 | 40 | wire_ptr_->requestFrom(device_address,byte_count); 41 | data = 0; 42 | for (int byte_n=0; byte_nread()) << (BITS_PER_BYTE * byte_n); 45 | } 46 | } 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Janelia Open-Source Software 2 | (3-clause BSD License) 3 | 4 | Copyright (c) 2021 Howard Hughes Medical Institute 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, this 13 | list of conditions and the following disclaimer in the documentation and/or 14 | other materials provided with the distribution. 15 | 16 | * Neither the name of HHMI nor the names of its contributors may be used to 17 | endorse or promote products derived from this software without specific prior 18 | written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND 21 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 24 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 27 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /examples/TestSetAndGet/TestSetAndGet.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "Constants.h" 6 | 7 | 8 | PCA9685 pca9685; 9 | 10 | PCA9685::Time time_min; 11 | PCA9685::Time time_max; 12 | 13 | PCA9685::Channel channel; 14 | 15 | PCA9685::Time on_time_set; 16 | PCA9685::Time off_time_set; 17 | PCA9685::Time on_time_get; 18 | PCA9685::Time off_time_get; 19 | 20 | PCA9685::Duration pulse_width_set; 21 | PCA9685::Duration phase_shift_set; 22 | PCA9685::Duration pulse_width_get; 23 | PCA9685::Duration phase_shift_get; 24 | 25 | PCA9685::Percent duty_cycle_set; 26 | PCA9685::Percent percent_delay_set; 27 | PCA9685::Percent duty_cycle_get; 28 | PCA9685::Percent percent_delay_get; 29 | 30 | void setup() 31 | { 32 | Serial.begin(constants::baud); 33 | 34 | pca9685.setupSingleDevice(Wire,constants::device_address); 35 | 36 | pca9685.setupOutputEnablePin(constants::output_enable_pin); 37 | pca9685.enableOutputs(constants::output_enable_pin); 38 | 39 | pca9685.setOutputsNotInverted(); 40 | 41 | pca9685.setToServoFrequency(); 42 | 43 | time_min = pca9685.getTimeMin(); 44 | time_max = pca9685.getTimeMax(); 45 | 46 | channel = 0; 47 | on_time_set = time_min; 48 | off_time_set = time_max; 49 | } 50 | 51 | void checkMatch(const char * s, bool match) 52 | { 53 | if (match) 54 | { 55 | Serial << s << " get matches set.\n"; 56 | } 57 | else 58 | { 59 | Serial << s << " get does not match set!\n"; 60 | } 61 | } 62 | 63 | void loop() 64 | { 65 | if (on_time_set > off_time_set) 66 | { 67 | on_time_set = time_min; 68 | off_time_set = time_max; 69 | } 70 | for (PCA9685::Channel channel=0; channel < pca9685.getChannelCount(); ++channel) 71 | { 72 | Serial << "frequency: " << pca9685.getFrequency() << "\n"; 73 | Serial << "channel: " << channel << ", on_time_set: " << on_time_set << ", off_time_set: " << off_time_set << "\n"; 74 | pca9685.setChannelOnAndOffTime(channel,on_time_set,off_time_set); 75 | pca9685.getChannelOnAndOffTime(channel,on_time_get,off_time_get); 76 | checkMatch("ChannelOnAndOffTime",((on_time_set == on_time_get) && (off_time_set == off_time_get))); 77 | 78 | pca9685.setChannelOnTime(channel,on_time_set); 79 | pca9685.getChannelOnTime(channel,on_time_get); 80 | checkMatch("ChannelOnTime",(on_time_set == on_time_get)); 81 | 82 | pca9685.setChannelOffTime(channel,off_time_set); 83 | pca9685.getChannelOffTime(channel,off_time_get); 84 | checkMatch("ChannelOffTime",(off_time_set == off_time_get)); 85 | 86 | pulse_width_set = off_time_set - on_time_set; 87 | phase_shift_set = on_time_set; 88 | Serial << "channel: " << channel << ", pulse_width_set: " << pulse_width_set << ", phase_shift_set: " << phase_shift_set << "\n"; 89 | pca9685.setChannelPulseWidth(channel,pulse_width_set,phase_shift_set); 90 | pca9685.getChannelPulseWidth(channel,pulse_width_get,phase_shift_get); 91 | checkMatch("ChannelPulseWidth",((pulse_width_set == pulse_width_get) && (phase_shift_set == phase_shift_get))); 92 | 93 | duty_cycle_set = (pulse_width_set * pca9685.getDutyCycleMax()) / pca9685.getTimeMax(); 94 | percent_delay_set = (phase_shift_set * pca9685.getPercentDelayMax()) / pca9685.getTimeMax(); 95 | Serial << "channel: " << channel << ", duty_cycle_set: " << duty_cycle_set << ", percent_delay_set: " << percent_delay_set << "\n"; 96 | pca9685.setChannelDutyCycle(channel,duty_cycle_set,percent_delay_set); 97 | pca9685.getChannelDutyCycle(channel,duty_cycle_get,percent_delay_get); 98 | checkMatch("ChannelDutyCycle",((abs(duty_cycle_set - duty_cycle_get) < constants::epsilon) && (abs(percent_delay_set - percent_delay_get) < constants::epsilon))); 99 | 100 | Serial << "\n"; 101 | delay(constants::loop_delay); 102 | } 103 | on_time_set += constants::time_increment; 104 | off_time_set -= constants::time_increment; 105 | } 106 | -------------------------------------------------------------------------------- /src/PCA9685.h: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // PCA9685.h 3 | // 4 | // Authors: 5 | // Peter Polidoro peter@polidoro.io 6 | // ---------------------------------------------------------------------------- 7 | #ifndef PCA9685_H 8 | #define PCA9685_H 9 | #include 10 | #include 11 | 12 | 13 | class PCA9685 14 | { 15 | public: 16 | PCA9685(); 17 | 18 | typedef uint16_t Channel; 19 | typedef uint8_t ChannelCount; 20 | typedef uint8_t DeviceAddress; 21 | typedef uint8_t DeviceIndex; 22 | typedef size_t Pin; 23 | typedef uint16_t Frequency; 24 | typedef double Percent; 25 | typedef uint16_t Time; 26 | typedef uint16_t Duration; 27 | typedef uint16_t DurationMicroseconds; 28 | 29 | const static Channel CHANNELS_PER_DEVICE = 16; 30 | enum {DEVICE_COUNT_MAX=55}; 31 | 32 | // Convenience method when using a single device 33 | void setupSingleDevice(TwoWire & wire=Wire, 34 | DeviceAddress device_address=0x40, 35 | bool fast_mode_plus=false); 36 | 37 | // Methods for using a single device or multiple devices 38 | void setupOutputEnablePin(Pin output_enable_pin); 39 | void enableOutputs(Pin output_enable_pin); 40 | void disableOutputs(Pin output_enable_pin); 41 | 42 | Frequency getFrequencyMin(); 43 | Frequency getFrequencyMax(); 44 | void setToFrequency(Frequency frequency); 45 | Frequency getFrequency(); 46 | void setToServoFrequency(); 47 | Frequency getServoFrequency(); 48 | 49 | ChannelCount getChannelCount(); 50 | 51 | Percent getDutyCycleMin(); 52 | Percent getDutyCycleMax(); 53 | Percent getPercentDelayMin(); 54 | Percent getPercentDelayMax(); 55 | void setChannelDutyCycle(Channel channel, 56 | Percent duty_cycle, 57 | Percent percent_delay=0); 58 | void getChannelDutyCycle(Channel channel, 59 | Percent & duty_cycle, 60 | Percent & percent_delay); 61 | void setAllChannelsDutyCycle(Percent duty_cycle, 62 | Percent percent_delay=0); 63 | 64 | Duration getPulseWidthMin(); 65 | Duration getPulseWidthMax(); 66 | Time getPhaseShiftMin(); 67 | Time getPhaseShiftMax(); 68 | void setChannelPulseWidth(Channel channel, 69 | Duration pulse_width, 70 | Duration phase_shift=0); 71 | void getChannelPulseWidth(Channel channel, 72 | Duration & pulse_width, 73 | Time & phase_shift); 74 | void setAllChannelsPulseWidth(Duration pulse_width, 75 | Duration phase_shift=0); 76 | 77 | void setChannelServoPulseDuration(Channel channel, 78 | DurationMicroseconds pulse_duration_microseconds); 79 | void getChannelServoPulseDuration(Channel channel, 80 | DurationMicroseconds & pulse_duration_microseconds); 81 | void setAllChannelsServoPulseDuration(DurationMicroseconds pulse_duration_microseconds); 82 | 83 | Time getTimeMin(); 84 | Time getTimeMax(); 85 | void setChannelOnAndOffTime(Channel channel, 86 | Time on_time, 87 | Time off_time); 88 | void getChannelOnAndOffTime(Channel channel, 89 | Time & on_time, 90 | Time & off_time); 91 | void setAllChannelsOnAndOffTime(Time on_time, 92 | Time off_time); 93 | void setChannelOnTime(Channel channel, 94 | Time on_time); 95 | void getChannelOnTime(Channel channel, 96 | Time & on_time); 97 | void setAllChannelsOnTime(Time on_time); 98 | void setChannelOffTime(Channel channel, 99 | Time off_time); 100 | void getChannelOffTime(Channel channel, 101 | Time & off_time); 102 | void setAllChannelsOffTime(Time off_time); 103 | 104 | void setOutputsInverted(); 105 | void setOutputsNotInverted(); 106 | void setOutputsToTotemPole(); 107 | void setOutputsToOpenDrain(); 108 | void setOutputsLowWhenDisabled(); 109 | void setOutputsHighWhenDisabled(); 110 | void setOutputsHighImpedanceWhenDisabled(); 111 | 112 | // Methods for using multiple devices 113 | // Take care when using fast_mode_plus with non-PCA9685 devices 114 | void setWire(TwoWire & wire=Wire, 115 | bool fast_mode_plus=false); 116 | 117 | // device_address=0x40 when all device address 118 | // hardware select lines are low 119 | // cannot use reserved addresses 120 | void addDevice(DeviceAddress device_address); 121 | void resetAllDevices(); 122 | 123 | void addDeviceToGroup0(DeviceAddress device_address); 124 | void removeDeviceFromGroup0(DeviceAddress device_address); 125 | void addDeviceToGroup1(DeviceAddress device_address); 126 | void removeDeviceFromGroup1(DeviceAddress device_address); 127 | void addDeviceToGroup2(DeviceAddress device_address); 128 | void removeDeviceFromGroup2(DeviceAddress device_address); 129 | 130 | void setSingleDeviceToFrequency(DeviceAddress device_address, 131 | Frequency frequency); 132 | Frequency getSingleDeviceFrequency(DeviceAddress device_address); 133 | void setAllDevicesToFrequency(Frequency frequency); 134 | void setSingleDeviceToServoFrequency(DeviceAddress device_address); 135 | Frequency getSingleDeviceServoFrequency(DeviceAddress device_address); 136 | void setAllDevicesToServoFrequency(); 137 | 138 | ChannelCount getDeviceChannelCount(); 139 | 140 | // Use these device address to set more than one device at a time 141 | // with the methods below 142 | const static DeviceAddress DEVICE_ADDRESS_ALL = 0x70; 143 | const static DeviceAddress DEVICE_ADDRESS_GROUP0 = 0x71; 144 | const static DeviceAddress DEVICE_ADDRESS_GROUP1 = 0x72; 145 | const static DeviceAddress DEVICE_ADDRESS_GROUP2 = 0x73; 146 | 147 | void setDeviceChannelDutyCycle(DeviceAddress device_address, 148 | Channel device_channel, 149 | Percent duty_cycle, 150 | Percent percent_delay=0); 151 | void setAllDeviceChannelsDutyCycle(DeviceAddress device_address, 152 | Percent duty_cycle, 153 | Percent percent_delay=0); 154 | 155 | void setDeviceChannelPulseWidth(DeviceAddress device_address, 156 | Channel device_channel, 157 | Duration pulse_width, 158 | Duration phase_shift=0); 159 | void setAllDeviceChannelsPulseWidth(DeviceAddress device_address, 160 | Duration pulse_width, 161 | Duration phase_shift=0); 162 | 163 | void setDeviceChannelServoPulseDuration(DeviceAddress device_address, 164 | Channel device_channel, 165 | DurationMicroseconds pulse_duration_microseconds); 166 | void setAllDeviceChannelsServoPulseDuration(DeviceAddress device_address, 167 | DurationMicroseconds pulse_duration_microseconds); 168 | 169 | void setDeviceChannelOnAndOffTime(DeviceAddress device_address, 170 | Channel device_channel, 171 | Time on_time, 172 | Time off_time); 173 | void setAllDeviceChannelsOnAndOffTime(DeviceAddress device_address, 174 | Time on_time, 175 | Time off_time); 176 | void setDeviceChannelOnTime(DeviceAddress device_address, 177 | Channel device_channel, 178 | Time on_time); 179 | void setAllDeviceChannelsOnTime(DeviceAddress device_address, 180 | Time on_time); 181 | void setDeviceChannelOffTime(DeviceAddress device_address, 182 | Channel device_channel, 183 | Time off_time); 184 | void setAllDeviceChannelsOffTime(DeviceAddress device_address, 185 | Time off_time); 186 | 187 | void setSingleDeviceOutputsInverted(DeviceAddress device_address); 188 | void setAllDevicesOutputsInverted(); 189 | void setSingleDeviceOutputsNotInverted(DeviceAddress device_address); 190 | void setAllDevicesOutputsNotInverted(); 191 | void setSingleDeviceOutputsToTotemPole(DeviceAddress device_address); 192 | void setAllDevicesOutputsToTotemPole(); 193 | void setSingleDeviceOutputsToOpenDrain(DeviceAddress device_address); 194 | void setAllDevicesOutputsToOpenDrain(); 195 | void setSingleDeviceOutputsLowWhenDisabled(DeviceAddress device_address); 196 | void setAllDevicesOutputsLowWhenDisabled(); 197 | void setSingleDeviceOutputsHighWhenDisabled(DeviceAddress device_address); 198 | void setAllDevicesOutputsHighWhenDisabled(); 199 | void setSingleDeviceOutputsHighImpedanceWhenDisabled(DeviceAddress device_address); 200 | void setAllDevicesOutputsHighImpedanceWhenDisabled(); 201 | 202 | private: 203 | const static DeviceAddress DEVICE_ADDRESS_MIN = 0x40; 204 | const static DeviceAddress DEVICE_ADDRESS_MAX = 0x7B; 205 | uint8_t device_count_; 206 | DeviceAddress device_addresses_[DEVICE_COUNT_MAX]; 207 | 208 | TwoWire * wire_ptr_; 209 | const static long FAST_MODE_PLUS_CLOCK_FREQUENCY = 1000000; 210 | 211 | const static int NO_OUTPUT_ENABLE_PIN = -1; 212 | 213 | const static int DEVICE_INDEX_NONE = -1; 214 | const static int DEVICE_INDEX_ALL = -2; 215 | const static int DEVICE_INDEX_GROUP0 = -3; 216 | const static int DEVICE_INDEX_GROUP1 = -4; 217 | const static int DEVICE_INDEX_GROUP2 = -5; 218 | int deviceAddressToDeviceIndex(DeviceAddress device_address); 219 | 220 | const static DeviceAddress GENERAL_CALL_DEVICE_ADDRESS = 0x00; 221 | const static uint8_t SWRST = 0b110; 222 | 223 | // Can write to one or more device at a time 224 | // so use address rather than index 225 | template 226 | void write(DeviceAddress device_address, 227 | uint8_t register_address, 228 | T data); 229 | // Can only read from one device at a time 230 | // so use index rather than address 231 | template 232 | void read(DeviceIndex device_index, 233 | uint8_t register_address, 234 | T & data); 235 | 236 | const static uint8_t MODE1_REGISTER_ADDRESS = 0x00; 237 | union Mode1Register 238 | { 239 | struct 240 | { 241 | uint8_t allcall : 1; 242 | uint8_t sub3 : 1; 243 | uint8_t sub2 : 1; 244 | uint8_t sub1 : 1; 245 | uint8_t sleep : 1; 246 | uint8_t ai : 1; 247 | uint8_t extclk : 1; 248 | uint8_t restart : 1; 249 | } fields; 250 | uint8_t data; 251 | }; 252 | Mode1Register readMode1Register(DeviceIndex device_index); 253 | 254 | const static uint8_t MODE2_REGISTER_ADDRESS = 0x01; 255 | union Mode2Register 256 | { 257 | struct 258 | { 259 | uint8_t outne : 2; 260 | uint8_t outdrv : 1; 261 | uint8_t och : 1; 262 | uint8_t invrt : 1; 263 | uint8_t space : 3; 264 | } fields; 265 | uint8_t data; 266 | }; 267 | Mode2Register readMode2Register(DeviceIndex device_index); 268 | 269 | void sleep(DeviceIndex device_index); 270 | void wake(DeviceIndex device_index); 271 | void wakeAll(); 272 | 273 | void setPrescale(DeviceIndex device_index, 274 | uint8_t prescale); 275 | void getPrescale(DeviceIndex device_index, 276 | uint8_t & prescale); 277 | uint8_t frequencyToPrescale(Frequency frequency); 278 | Frequency prescaleToFrequency(uint8_t prescale); 279 | 280 | uint8_t channelToDeviceIndex(Channel channel); 281 | Channel channelToDeviceChannel(Channel channel); 282 | 283 | void dutyCycleAndPercentDelayToPulseWidthAndPhaseShift(Percent duty_cycle, 284 | Percent percent_delay, 285 | Duration & pulse_width, 286 | Time & phase_shift); 287 | void pulseWidthAndPhaseShiftToDutyCycleAndPercentDelay(Duration pulse_width, 288 | Duration phase_shift, 289 | Percent & duty_cycle, 290 | Percent & percent_delay); 291 | 292 | void pulseWidthAndPhaseShiftToOnTimeAndOffTime(Duration pulse_width, 293 | Duration phase_shift, 294 | Time & on_time, 295 | Time & off_time); 296 | void onTimeAndOffTimeToPulseWidthAndPhaseShift(Time on_time, 297 | Time off_time, 298 | Duration & pulse_width, 299 | Time & phase_shift); 300 | 301 | void servoPulseDurationToPulseWidthAndPhaseShift(DurationMicroseconds pulse_duration_microseconds, 302 | Duration & pulse_width, 303 | Time & phase_shift); 304 | void pulseWidthAndPhaseShiftToServoPulseDuration(Duration pulse_width, 305 | Duration phase_shift, 306 | DurationMicroseconds & pulse_duration_microseconds); 307 | 308 | void setOutputsInverted(DeviceIndex device_index); 309 | void setOutputsNotInverted(DeviceIndex device_index); 310 | void setOutputsToTotemPole(DeviceIndex device_index); 311 | void setOutputsToOpenDrain(DeviceIndex device_index); 312 | void setOutputsLowWhenDisabled(DeviceIndex device_index); 313 | void setOutputsHighWhenDisabled(DeviceIndex device_index); 314 | void setOutputsHighImpedanceWhenDisabled(DeviceIndex device_index); 315 | 316 | const static uint8_t DOES_NOT_RESPOND = 0; 317 | const static uint8_t DOES_RESPOND = 1; 318 | const static uint8_t SUBADR1_REGISTER_ADDRESS = 0x02; 319 | const static uint8_t SUBADR2_REGISTER_ADDRESS = 0x03; 320 | const static uint8_t SUBADR3_REGISTER_ADDRESS = 0x04; 321 | const static uint8_t ALLCALLADR_REGISTER_ADDRESS = 0x05; 322 | 323 | const static uint8_t LED0_ON_L_REGISTER_ADDRESS = 0x06; 324 | const static uint8_t LED0_OFF_L_REGISTER_ADDRESS = 0x08; 325 | const static uint8_t ALL_LED_ON_L_REGISTER_ADDRESS = 0xFA; 326 | const static uint8_t ALL_LED_OFF_L_REGISTER_ADDRESS = 0xFC; 327 | const static uint8_t LED_REGISTERS_SIZE = 4; 328 | const static uint8_t BITS_PER_BYTE = 8; 329 | const static uint8_t BITS_PER_TWO_BYTES = 16; 330 | const static uint8_t BYTE_MAX = 0xFF; 331 | const static uint16_t TWO_BYTE_MAX = 0xFFFF; 332 | 333 | const static uint8_t PRE_SCALE_REGISTER_ADDRESS = 0xFE; 334 | const static uint8_t PRE_SCALE_MIN = 0x03; 335 | const static uint8_t PRE_SCALE_MAX = 0xFF; 336 | // Use period instead of frequency to calculate prescale since it is linear 337 | // Measured 1620 Hz at prescale value 0x03, 1E6/1620=617 338 | // Datasheet says it should be 1526 Hz, 1E6/1526=655 339 | const static DurationMicroseconds PWM_PERIOD_MIN_US = 617; 340 | // Measured 25.3 Hz at prescale value 0xFF, 1E6/25.3=39525 341 | // Datasheet says it should be 24 Hz, 1E6/24=41666 342 | const static DurationMicroseconds PWM_PERIOD_MAX_US = 39525; 343 | const static uint32_t MICROSECONDS_PER_SECOND = 1000000; 344 | 345 | const static uint8_t OUTPUTS_INVERTED = 1; 346 | const static uint8_t OUTPUTS_NOT_INVERTED = 0; 347 | const static uint8_t OUTPUTS_TOTEM_POLE = 1; 348 | const static uint8_t OUTPUTS_OPEN_DRAIN = 0; 349 | const static uint8_t OUTPUTS_LOW_WHEN_DISABLED = 0b00; 350 | const static uint8_t OUTPUTS_HIGH_WHEN_DISABLED = 0b01; 351 | const static uint8_t OUTPUTS_HIGH_IMPEDANCE_WHEN_DISABLED = 0b10; 352 | 353 | const static uint8_t SLEEP = 1; 354 | const static uint8_t WAKE = 0; 355 | const static uint8_t AUTO_INCREMENT_ENABLED = 1; 356 | const static uint8_t AUTO_INCREMENT_DISABLED = 0; 357 | const static uint8_t RESTART_ENABLED = 1; 358 | const static uint8_t RESTART_DISABLED = 0; 359 | const static uint8_t RESTART_CLEAR = 1; 360 | 361 | const static Time TIME_MIN = 0; 362 | const static Time TIME_MAX = 4096; 363 | 364 | const static uint8_t PERCENT_MIN = 0; 365 | const static uint8_t PERCENT_MAX = 100; 366 | 367 | const static Frequency SERVO_FREQUENCY = 50; 368 | const static DurationMicroseconds SERVO_PERIOD_MICROSECONDS = 20000; 369 | 370 | }; 371 | 372 | #include "PCA9685/PCA9685Definitions.h" 373 | 374 | #endif 375 | -------------------------------------------------------------------------------- /src/PCA9685/PCA9685.cpp: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // PCA9685.cpp 3 | // 4 | // Authors: 5 | // Peter Polidoro peter@polidoro.io 6 | // ---------------------------------------------------------------------------- 7 | #include "PCA9685.h" 8 | 9 | 10 | PCA9685::PCA9685() 11 | { 12 | device_count_ = 0; 13 | for (DeviceIndex device_index=0; device_index 0) 63 | { 64 | frequency = getSingleDeviceFrequency(device_addresses_[0]); 65 | } 66 | return frequency; 67 | } 68 | 69 | void PCA9685::setToServoFrequency() 70 | { 71 | setAllDevicesToServoFrequency(); 72 | } 73 | 74 | PCA9685::Frequency PCA9685::getServoFrequency() 75 | { 76 | return SERVO_FREQUENCY; 77 | } 78 | 79 | PCA9685::ChannelCount PCA9685::getChannelCount() 80 | { 81 | return CHANNELS_PER_DEVICE * device_count_; 82 | } 83 | 84 | PCA9685::Percent PCA9685::getDutyCycleMin() 85 | { 86 | return PERCENT_MIN; 87 | } 88 | 89 | PCA9685::Percent PCA9685::getDutyCycleMax() 90 | { 91 | return PERCENT_MAX; 92 | } 93 | 94 | PCA9685::Percent PCA9685::getPercentDelayMin() 95 | { 96 | return PERCENT_MIN; 97 | } 98 | 99 | PCA9685::Percent PCA9685::getPercentDelayMax() 100 | { 101 | return PERCENT_MAX; 102 | } 103 | 104 | void PCA9685::setChannelDutyCycle(Channel channel, 105 | Percent duty_cycle, 106 | Percent percent_delay) 107 | { 108 | Duration pulse_width; 109 | Duration phase_shift; 110 | dutyCycleAndPercentDelayToPulseWidthAndPhaseShift(duty_cycle,percent_delay,pulse_width,phase_shift); 111 | setChannelPulseWidth(channel,pulse_width,phase_shift); 112 | } 113 | 114 | void PCA9685::getChannelDutyCycle(Channel channel, 115 | Percent & duty_cycle, 116 | Percent & percent_delay) 117 | { 118 | Duration pulse_width; 119 | Duration phase_shift; 120 | getChannelPulseWidth(channel,pulse_width,phase_shift); 121 | pulseWidthAndPhaseShiftToDutyCycleAndPercentDelay(pulse_width,phase_shift,duty_cycle,percent_delay); 122 | } 123 | 124 | void PCA9685::setAllChannelsDutyCycle(Percent duty_cycle, 125 | Percent percent_delay) 126 | { 127 | setAllDeviceChannelsDutyCycle(DEVICE_ADDRESS_ALL,duty_cycle,percent_delay); 128 | } 129 | 130 | PCA9685::Duration PCA9685::getPulseWidthMin() 131 | { 132 | return TIME_MIN; 133 | } 134 | 135 | PCA9685::Duration PCA9685::getPulseWidthMax() 136 | { 137 | return TIME_MAX; 138 | } 139 | 140 | PCA9685::Duration PCA9685::getPhaseShiftMin() 141 | { 142 | return TIME_MIN; 143 | } 144 | 145 | PCA9685::Duration PCA9685::getPhaseShiftMax() 146 | { 147 | return 0xFFFF; 148 | } 149 | 150 | void PCA9685::setChannelPulseWidth(Channel channel, 151 | Duration pulse_width, 152 | Duration phase_shift) 153 | { 154 | Time on_time; 155 | Time off_time; 156 | pulseWidthAndPhaseShiftToOnTimeAndOffTime(pulse_width,phase_shift,on_time,off_time); 157 | setChannelOnAndOffTime(channel,on_time,off_time); 158 | } 159 | 160 | void PCA9685::getChannelPulseWidth(Channel channel, 161 | Duration & pulse_width, 162 | Duration & phase_shift) 163 | { 164 | Time on_time = 0; 165 | Time off_time = 0; 166 | getChannelOnAndOffTime(channel,on_time,off_time); 167 | onTimeAndOffTimeToPulseWidthAndPhaseShift(on_time,off_time,pulse_width,phase_shift); 168 | } 169 | 170 | void PCA9685::setAllChannelsPulseWidth(Duration pulse_width, 171 | Duration phase_shift) 172 | { 173 | setAllDeviceChannelsPulseWidth(DEVICE_ADDRESS_ALL,pulse_width,phase_shift); 174 | } 175 | 176 | void PCA9685::setChannelServoPulseDuration(Channel channel, 177 | DurationMicroseconds pulse_duration_microseconds) 178 | { 179 | Duration pulse_width; 180 | Duration phase_shift; 181 | servoPulseDurationToPulseWidthAndPhaseShift(pulse_duration_microseconds,pulse_width,phase_shift); 182 | setChannelPulseWidth(channel,pulse_width,phase_shift); 183 | } 184 | 185 | void PCA9685::getChannelServoPulseDuration(Channel channel, 186 | DurationMicroseconds & pulse_duration_microseconds) 187 | { 188 | Duration pulse_width; 189 | Duration phase_shift; 190 | getChannelPulseWidth(channel,pulse_width,phase_shift); 191 | pulseWidthAndPhaseShiftToServoPulseDuration(pulse_width,phase_shift,pulse_duration_microseconds); 192 | } 193 | 194 | void PCA9685::setAllChannelsServoPulseDuration(DurationMicroseconds pulse_duration_microseconds) 195 | { 196 | setAllDeviceChannelsServoPulseDuration(DEVICE_ADDRESS_ALL,pulse_duration_microseconds); 197 | } 198 | 199 | PCA9685::Time PCA9685::getTimeMin() 200 | { 201 | return TIME_MIN; 202 | } 203 | 204 | PCA9685::Time PCA9685::getTimeMax() 205 | { 206 | return TIME_MAX; 207 | } 208 | 209 | void PCA9685::setChannelOnAndOffTime(Channel channel, 210 | Time on_time, 211 | Time off_time) 212 | { 213 | if (channel >= getChannelCount()) 214 | { 215 | return; 216 | } 217 | DeviceIndex device_index = channelToDeviceIndex(channel); 218 | Channel device_channel = channelToDeviceChannel(channel); 219 | uint8_t register_address = LED0_ON_L_REGISTER_ADDRESS + LED_REGISTERS_SIZE * device_channel; 220 | uint32_t data = (off_time << BITS_PER_TWO_BYTES) | on_time; 221 | write(device_addresses_[device_index],register_address,data); 222 | } 223 | 224 | void PCA9685::getChannelOnAndOffTime(Channel channel, 225 | Time & on_time, 226 | Time & off_time) 227 | { 228 | if (channel >= getChannelCount()) 229 | { 230 | return; 231 | } 232 | DeviceIndex device_index = channelToDeviceIndex(channel); 233 | Channel device_channel = channelToDeviceChannel(channel); 234 | uint8_t register_address = LED0_ON_L_REGISTER_ADDRESS + LED_REGISTERS_SIZE * device_channel; 235 | uint32_t data; 236 | read(device_index,register_address,data); 237 | on_time = data & TWO_BYTE_MAX; 238 | off_time = (data >> BITS_PER_TWO_BYTES) & TWO_BYTE_MAX; 239 | } 240 | 241 | void PCA9685::setAllChannelsOnAndOffTime(Time on_time, 242 | Time off_time) 243 | { 244 | setAllDeviceChannelsOnAndOffTime(DEVICE_ADDRESS_ALL,on_time,off_time); 245 | } 246 | 247 | void PCA9685::setChannelOnTime(Channel channel, 248 | Time on_time) 249 | { 250 | if (channel >= getChannelCount()) 251 | { 252 | return; 253 | } 254 | DeviceIndex device_index = channelToDeviceIndex(channel); 255 | Channel device_channel = channelToDeviceChannel(channel); 256 | uint8_t register_address = LED0_ON_L_REGISTER_ADDRESS + LED_REGISTERS_SIZE * device_channel; 257 | write(device_addresses_[device_index],register_address,on_time); 258 | } 259 | 260 | void PCA9685::getChannelOnTime(Channel channel, 261 | Time & on_time) 262 | { 263 | if (channel >= getChannelCount()) 264 | { 265 | return; 266 | } 267 | DeviceIndex device_index = channelToDeviceIndex(channel); 268 | Channel device_channel = channelToDeviceChannel(channel); 269 | uint8_t register_address = LED0_ON_L_REGISTER_ADDRESS + LED_REGISTERS_SIZE * device_channel; 270 | read(device_index,register_address,on_time); 271 | } 272 | 273 | void PCA9685::setAllChannelsOnTime(Time on_time) 274 | { 275 | setAllDeviceChannelsOnTime(DEVICE_ADDRESS_ALL,on_time); 276 | } 277 | 278 | void PCA9685::setChannelOffTime(Channel channel, 279 | Time off_time) 280 | { 281 | if (channel >= getChannelCount()) 282 | { 283 | return; 284 | } 285 | DeviceIndex device_index = channelToDeviceIndex(channel); 286 | Channel device_channel = channelToDeviceChannel(channel); 287 | uint8_t register_address = LED0_OFF_L_REGISTER_ADDRESS + LED_REGISTERS_SIZE * device_channel; 288 | write(device_addresses_[device_index],register_address,off_time); 289 | } 290 | 291 | void PCA9685::getChannelOffTime(Channel channel, 292 | Time & off_time) 293 | { 294 | if (channel >= getChannelCount()) 295 | { 296 | return; 297 | } 298 | DeviceIndex device_index = channelToDeviceIndex(channel); 299 | Channel device_channel = channelToDeviceChannel(channel); 300 | uint8_t register_address = LED0_OFF_L_REGISTER_ADDRESS + LED_REGISTERS_SIZE * device_channel; 301 | read(device_index,register_address,off_time); 302 | } 303 | 304 | void PCA9685::setAllChannelsOffTime(Time off_time) 305 | { 306 | setAllDeviceChannelsOffTime(DEVICE_ADDRESS_ALL,off_time); 307 | } 308 | 309 | void PCA9685::setOutputsInverted() 310 | { 311 | setAllDevicesOutputsInverted(); 312 | } 313 | 314 | void PCA9685::setOutputsNotInverted() 315 | { 316 | setAllDevicesOutputsNotInverted(); 317 | } 318 | 319 | void PCA9685::setOutputsToTotemPole() 320 | { 321 | setAllDevicesOutputsToTotemPole(); 322 | } 323 | 324 | void PCA9685::setOutputsToOpenDrain() 325 | { 326 | setAllDevicesOutputsToOpenDrain(); 327 | } 328 | 329 | void PCA9685::setOutputsLowWhenDisabled() 330 | { 331 | setAllDevicesOutputsLowWhenDisabled(); 332 | } 333 | 334 | void PCA9685::setOutputsHighWhenDisabled() 335 | { 336 | setAllDevicesOutputsHighWhenDisabled(); 337 | } 338 | 339 | void PCA9685::setOutputsHighImpedanceWhenDisabled() 340 | { 341 | setAllDevicesOutputsHighImpedanceWhenDisabled(); 342 | } 343 | 344 | void PCA9685::setWire(TwoWire & wire, 345 | bool fast_mode_plus) 346 | { 347 | wire_ptr_ = &wire; 348 | wire_ptr_->begin(); 349 | if (fast_mode_plus) 350 | { 351 | wire_ptr_->setClock(FAST_MODE_PLUS_CLOCK_FREQUENCY); 352 | } 353 | 354 | } 355 | 356 | void PCA9685::addDevice(DeviceAddress device_address) 357 | { 358 | if ((device_count_ >= DEVICE_COUNT_MAX) || 359 | (device_address < DEVICE_ADDRESS_MIN) || 360 | (device_address > DEVICE_ADDRESS_MAX)) 361 | { 362 | return; 363 | } 364 | int device_index = deviceAddressToDeviceIndex(device_address); 365 | if (device_index != DEVICE_INDEX_NONE) 366 | { 367 | return; 368 | } 369 | device_addresses_[device_count_++] = device_address; 370 | } 371 | 372 | void PCA9685::resetAllDevices() 373 | { 374 | wire_ptr_->beginTransmission(GENERAL_CALL_DEVICE_ADDRESS); 375 | wire_ptr_->write(SWRST); 376 | wire_ptr_->endTransmission(); 377 | delay(10); 378 | wakeAll(); 379 | } 380 | 381 | void PCA9685::addDeviceToGroup0(DeviceAddress device_address) 382 | { 383 | int device_index = deviceAddressToDeviceIndex(device_address); 384 | if (device_index < 0) 385 | { 386 | return; 387 | } 388 | Mode1Register mode1_register = readMode1Register(device_index); 389 | mode1_register.fields.sub1 = DOES_RESPOND; 390 | write(device_address,MODE1_REGISTER_ADDRESS,mode1_register.data); 391 | } 392 | 393 | void PCA9685::removeDeviceFromGroup0(DeviceAddress device_address) 394 | { 395 | int device_index = deviceAddressToDeviceIndex(device_address); 396 | if (device_index < 0) 397 | { 398 | return; 399 | } 400 | Mode1Register mode1_register = readMode1Register(device_index); 401 | mode1_register.fields.sub1 = DOES_NOT_RESPOND; 402 | write(device_address,MODE1_REGISTER_ADDRESS,mode1_register.data); 403 | } 404 | 405 | void PCA9685::addDeviceToGroup1(DeviceAddress device_address) 406 | { 407 | int device_index = deviceAddressToDeviceIndex(device_address); 408 | if (device_index < 0) 409 | { 410 | return; 411 | } 412 | Mode1Register mode1_register = readMode1Register(device_index); 413 | mode1_register.fields.sub2 = DOES_RESPOND; 414 | write(device_address,MODE1_REGISTER_ADDRESS,mode1_register.data); 415 | } 416 | 417 | void PCA9685::removeDeviceFromGroup1(DeviceAddress device_address) 418 | { 419 | int device_index = deviceAddressToDeviceIndex(device_address); 420 | if (device_index < 0) 421 | { 422 | return; 423 | } 424 | Mode1Register mode1_register = readMode1Register(device_index); 425 | mode1_register.fields.sub2 = DOES_NOT_RESPOND; 426 | write(device_address,MODE1_REGISTER_ADDRESS,mode1_register.data); 427 | } 428 | 429 | void PCA9685::addDeviceToGroup2(DeviceAddress device_address) 430 | { 431 | int device_index = deviceAddressToDeviceIndex(device_address); 432 | if (device_index < 0) 433 | { 434 | return; 435 | } 436 | Mode1Register mode1_register = readMode1Register(device_index); 437 | mode1_register.fields.sub3 = DOES_RESPOND; 438 | write(device_address,MODE1_REGISTER_ADDRESS,mode1_register.data); 439 | } 440 | 441 | void PCA9685::removeDeviceFromGroup2(DeviceAddress device_address) 442 | { 443 | int device_index = deviceAddressToDeviceIndex(device_address); 444 | if (device_index < 0) 445 | { 446 | return; 447 | } 448 | Mode1Register mode1_register = readMode1Register(device_index); 449 | mode1_register.fields.sub3 = DOES_NOT_RESPOND; 450 | write(device_address,MODE1_REGISTER_ADDRESS,mode1_register.data); 451 | } 452 | 453 | void PCA9685::setSingleDeviceToFrequency(DeviceAddress device_address, 454 | Frequency frequency) 455 | { 456 | int device_index = deviceAddressToDeviceIndex(device_address); 457 | if (device_index < 0) 458 | { 459 | return; 460 | } 461 | uint8_t prescale = frequencyToPrescale(frequency); 462 | setPrescale(device_index,prescale); 463 | } 464 | 465 | PCA9685::Frequency PCA9685::getSingleDeviceFrequency(DeviceAddress device_address) 466 | { 467 | Frequency frequency = 0; 468 | int device_index = deviceAddressToDeviceIndex(device_address); 469 | if (device_index >= 0) 470 | { 471 | uint8_t prescale; 472 | getPrescale(device_index,prescale); 473 | frequency = prescaleToFrequency(prescale); 474 | } 475 | return frequency; 476 | } 477 | 478 | void PCA9685::setAllDevicesToFrequency(Frequency frequency) 479 | { 480 | uint8_t prescale = frequencyToPrescale(frequency); 481 | for (DeviceIndex device_index=0; device_index= getDeviceChannelCount()) 574 | { 575 | return; 576 | } 577 | uint8_t register_address = LED0_ON_L_REGISTER_ADDRESS + LED_REGISTERS_SIZE * device_channel; 578 | uint32_t data = (off_time << BITS_PER_TWO_BYTES) | on_time; 579 | write(device_address,register_address,data); 580 | } 581 | 582 | void PCA9685::setAllDeviceChannelsOnAndOffTime(DeviceAddress device_address, 583 | Time on_time, 584 | Time off_time) 585 | { 586 | uint8_t register_address = ALL_LED_ON_L_REGISTER_ADDRESS; 587 | uint32_t data = (off_time << BITS_PER_TWO_BYTES) | on_time; 588 | write(device_address,register_address,data); 589 | } 590 | 591 | void PCA9685::setDeviceChannelOnTime(DeviceAddress device_address, 592 | Channel device_channel, 593 | Time on_time) 594 | { 595 | if (device_channel >= getDeviceChannelCount()) 596 | { 597 | return; 598 | } 599 | uint8_t register_address = LED0_ON_L_REGISTER_ADDRESS + LED_REGISTERS_SIZE * device_channel; 600 | write(device_address,register_address,on_time); 601 | } 602 | 603 | void PCA9685::setAllDeviceChannelsOnTime(DeviceAddress device_address, 604 | Time on_time) 605 | { 606 | uint8_t register_address = ALL_LED_ON_L_REGISTER_ADDRESS; 607 | write(device_address,register_address,on_time); 608 | } 609 | 610 | void PCA9685::setDeviceChannelOffTime(DeviceAddress device_address, 611 | Channel device_channel, 612 | Time off_time) 613 | { 614 | if (device_channel >= getDeviceChannelCount()) 615 | { 616 | return; 617 | } 618 | uint8_t register_address = LED0_OFF_L_REGISTER_ADDRESS + LED_REGISTERS_SIZE * device_channel; 619 | write(device_address,register_address,off_time); 620 | } 621 | 622 | void PCA9685::setAllDeviceChannelsOffTime(DeviceAddress device_address, 623 | Time off_time) 624 | { 625 | uint8_t register_address = ALL_LED_OFF_L_REGISTER_ADDRESS; 626 | write(device_address,register_address,off_time); 627 | } 628 | 629 | void PCA9685::setSingleDeviceOutputsInverted(DeviceAddress device_address) 630 | { 631 | int device_index = deviceAddressToDeviceIndex(device_address); 632 | if (device_index < 0) 633 | { 634 | return; 635 | } 636 | setOutputsInverted(device_index); 637 | } 638 | 639 | void PCA9685::setAllDevicesOutputsInverted() 640 | { 641 | for (DeviceIndex device_index=0; device_index 0) 860 | { 861 | frequency = round((double)MICROSECONDS_PER_SECOND / (double)period_us); 862 | } 863 | return frequency; 864 | } 865 | 866 | uint8_t PCA9685::channelToDeviceIndex(Channel channel) 867 | { 868 | return channel / CHANNELS_PER_DEVICE; 869 | } 870 | 871 | PCA9685::Channel PCA9685::channelToDeviceChannel(Channel channel) 872 | { 873 | return channel % CHANNELS_PER_DEVICE; 874 | } 875 | 876 | void PCA9685::dutyCycleAndPercentDelayToPulseWidthAndPhaseShift(Percent duty_cycle, 877 | Percent percent_delay, 878 | Duration & pulse_width, 879 | Duration & phase_shift) 880 | { 881 | pulse_width = round(((double)TIME_MAX * duty_cycle) / (double)PERCENT_MAX); 882 | phase_shift = round(((double)TIME_MAX * percent_delay) / (double)PERCENT_MAX); 883 | } 884 | 885 | void PCA9685::pulseWidthAndPhaseShiftToDutyCycleAndPercentDelay(Duration pulse_width, 886 | Duration phase_shift, 887 | Percent & duty_cycle, 888 | Percent & percent_delay) 889 | { 890 | duty_cycle = (double)(pulse_width * PERCENT_MAX) / (double)TIME_MAX; 891 | percent_delay = (double)(phase_shift * PERCENT_MAX) / (double)TIME_MAX; 892 | } 893 | 894 | void PCA9685::pulseWidthAndPhaseShiftToOnTimeAndOffTime(Duration pulse_width, 895 | Duration phase_shift, 896 | Time & on_time, 897 | Time & off_time) 898 | { 899 | if (pulse_width == TIME_MIN) 900 | { 901 | on_time = TIME_MIN; 902 | off_time = TIME_MAX; 903 | return; 904 | } 905 | if (pulse_width >= TIME_MAX) 906 | { 907 | on_time = TIME_MAX; 908 | off_time = TIME_MIN; 909 | return; 910 | } 911 | on_time = phase_shift % TIME_MAX; 912 | off_time = (on_time + pulse_width) % TIME_MAX; 913 | } 914 | 915 | void PCA9685::onTimeAndOffTimeToPulseWidthAndPhaseShift(Time on_time, 916 | Time off_time, 917 | Duration & pulse_width, 918 | Duration & phase_shift) 919 | { 920 | if (on_time == TIME_MAX) 921 | { 922 | pulse_width = TIME_MAX; 923 | phase_shift = TIME_MIN; 924 | return; 925 | } 926 | if (off_time == TIME_MAX) 927 | { 928 | pulse_width = TIME_MIN; 929 | phase_shift = TIME_MIN; 930 | return; 931 | } 932 | if (on_time > off_time) 933 | { 934 | pulse_width = TIME_MAX - (on_time - off_time); 935 | phase_shift = on_time; 936 | return; 937 | } 938 | pulse_width = off_time - on_time; 939 | phase_shift = on_time; 940 | } 941 | 942 | void PCA9685::servoPulseDurationToPulseWidthAndPhaseShift(DurationMicroseconds pulse_duration_microseconds, 943 | Duration & pulse_width, 944 | Duration & phase_shift) 945 | { 946 | phase_shift = 0; 947 | pulse_width = (pulse_duration_microseconds * TIME_MAX) / SERVO_PERIOD_MICROSECONDS; 948 | } 949 | 950 | void PCA9685::pulseWidthAndPhaseShiftToServoPulseDuration(Duration pulse_width, 951 | Duration phase_shift, 952 | DurationMicroseconds & pulse_duration_microseconds) 953 | { 954 | DurationMicroseconds period_us = SERVO_PERIOD_MICROSECONDS; 955 | pulse_duration_microseconds = (pulse_width * period_us) / TIME_MAX; 956 | } 957 | 958 | void PCA9685::setOutputsInverted(DeviceIndex device_index) 959 | { 960 | Mode2Register mode2_register = readMode2Register(device_index); 961 | mode2_register.fields.invrt = OUTPUTS_INVERTED; 962 | write(device_addresses_[device_index],MODE2_REGISTER_ADDRESS,mode2_register.data); 963 | } 964 | 965 | void PCA9685::setOutputsNotInverted(DeviceIndex device_index) 966 | { 967 | Mode2Register mode2_register = readMode2Register(device_index); 968 | mode2_register.fields.invrt = OUTPUTS_NOT_INVERTED; 969 | write(device_addresses_[device_index],MODE2_REGISTER_ADDRESS,mode2_register.data); 970 | } 971 | 972 | void PCA9685::setOutputsToTotemPole(DeviceIndex device_index) 973 | { 974 | Mode2Register mode2_register = readMode2Register(device_index); 975 | mode2_register.fields.outdrv = OUTPUTS_TOTEM_POLE; 976 | write(device_addresses_[device_index],MODE2_REGISTER_ADDRESS,mode2_register.data); 977 | } 978 | 979 | void PCA9685::setOutputsToOpenDrain(DeviceIndex device_index) 980 | { 981 | Mode2Register mode2_register = readMode2Register(device_index); 982 | mode2_register.fields.outdrv = OUTPUTS_OPEN_DRAIN; 983 | write(device_addresses_[device_index],MODE2_REGISTER_ADDRESS,mode2_register.data); 984 | } 985 | 986 | void PCA9685::setOutputsLowWhenDisabled(DeviceIndex device_index) 987 | { 988 | Mode2Register mode2_register = readMode2Register(device_index); 989 | mode2_register.fields.outne = OUTPUTS_LOW_WHEN_DISABLED; 990 | write(device_addresses_[device_index],MODE2_REGISTER_ADDRESS,mode2_register.data); 991 | } 992 | 993 | void PCA9685::setOutputsHighWhenDisabled(DeviceIndex device_index) 994 | { 995 | Mode2Register mode2_register = readMode2Register(device_index); 996 | mode2_register.fields.outne = OUTPUTS_HIGH_WHEN_DISABLED; 997 | write(device_addresses_[device_index],MODE2_REGISTER_ADDRESS,mode2_register.data); 998 | } 999 | 1000 | void PCA9685::setOutputsHighImpedanceWhenDisabled(DeviceIndex device_index) 1001 | { 1002 | Mode2Register mode2_register = readMode2Register(device_index); 1003 | mode2_register.fields.outne = OUTPUTS_HIGH_IMPEDANCE_WHEN_DISABLED; 1004 | write(device_addresses_[device_index],MODE2_REGISTER_ADDRESS,mode2_register.data); 1005 | } 1006 | --------------------------------------------------------------------------------