364 lines
12 KiB
C++
364 lines
12 KiB
C++
// ----------------------------------------------------------------------------
|
|
// PCA9685.h
|
|
//
|
|
// Authors:
|
|
// Peter Polidoro peterpolidoro@gmail.com
|
|
// ----------------------------------------------------------------------------
|
|
#ifndef PCA9685_H
|
|
#define PCA9685_H
|
|
#include <Arduino.h>
|
|
#include <Wire.h>
|
|
|
|
|
|
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<typename T>
|
|
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<typename T>
|
|
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
|