mirror of
https://github.com/RobTillaart/Arduino.git
synced 2024-10-03 18:09:02 -04:00
168 lines
5.6 KiB
C++
168 lines
5.6 KiB
C++
#pragma once
|
|
//
|
|
// FILE: PCA9685.h
|
|
// AUTHOR: Rob Tillaart
|
|
// DATE: 24-apr-2016
|
|
// VERSION: 0.4.0
|
|
// PURPOSE: Arduino library for I2C PCA9685 16 channel PWM
|
|
// URL: https://github.com/RobTillaart/PCA9685_RT
|
|
|
|
|
|
#include "Arduino.h"
|
|
#include "Wire.h"
|
|
|
|
|
|
#define PCA9685_LIB_VERSION (F("0.4.0"))
|
|
|
|
// ERROR CODES
|
|
#define PCA9685_OK 0x00
|
|
#define PCA9685_ERROR 0xFF
|
|
#define PCA9685_ERR_CHANNEL 0xFE
|
|
#define PCA9685_ERR_MODE 0xFD
|
|
#define PCA9685_ERR_I2C 0xFC
|
|
|
|
// get/setFrequency()
|
|
#define PCA9685_MIN_FREQ 24
|
|
#define PCA9685_MAX_FREQ 1526
|
|
|
|
|
|
// REGISTERS CONFIGURATION - check datasheet for details
|
|
#define PCA9685_MODE1 0x00
|
|
#define PCA9685_MODE2 0x01
|
|
|
|
// Configuration bits MODE1 register
|
|
#define PCA9685_MODE1_RESTART 0x80 // 0 = disable 1 = enable
|
|
#define PCA9685_MODE1_EXTCLK 0x40 // 0 = internal 1 = external (datasheet)
|
|
#define PCA9685_MODE1_AUTOINCR 0x20 // 0 = disable 1 = enable
|
|
#define PCA9685_MODE1_SLEEP 0x10 // 0 = normal 1 = sleep
|
|
#define PCA9685_MODE1_SUB1 0x08 // 0 = disable 1 = enable
|
|
#define PCA9685_MODE1_SUB2 0x04 // 0 = disable 1 = enable
|
|
#define PCA9685_MODE1_SUB3 0x02 // 0 = disable 1 = enable
|
|
#define PCA9685_MODE1_ALLCALL 0x01 // 0 = disable 1 = enable
|
|
#define PCA9685_MODE1_NONE 0x00
|
|
|
|
// Configuration bits MODE2 register
|
|
#define PCA9685_MODE2_BLINK 0x20 // 0 = dim 1 = blink
|
|
#define PCA9685_MODE2_INVERT 0x10 // 0 = normal 1 = inverted
|
|
#define PCA9685_MODE2_ACK 0x08 // 0 = on STOP 1 = on ACK
|
|
#define PCA9685_MODE2_TOTEMPOLE 0x04 // 0 = open drain 1 = totem-pole
|
|
#define PCA9685_MODE2_OUTNE 0x03 // datasheet
|
|
#define PCA9685_MODE2_NONE 0x00
|
|
|
|
// (since 0.4.0)
|
|
#define PCA9685_SUBADR(x) (0x01 + (x)) // x = 1..3
|
|
#define PCA9685_ALLCALLADR 0x05
|
|
|
|
// REGISTERS - CHANNELS
|
|
// 0x06 + 4*channel is base per channel
|
|
#define PCA9685_CHANNEL_0 0x06
|
|
#define PCA9685_CHANNEL(x) (0x06 + ((x) * 4)) // x = 0..15
|
|
|
|
// REGISTERS - ALL_ON ALL_OFF - partly implemented
|
|
#define PCA9685_ALL_ON_L 0xFA
|
|
#define PCA9685_ALL_ON_H 0xFB
|
|
#define PCA9685_ALL_OFF_L 0xFC
|
|
#define PCA9685_ALL_OFF_H 0xFD // used for allOFF()
|
|
|
|
// REGISTERS - FREQUENCY
|
|
#define PCA9685_PRE_SCALER 0xFE
|
|
|
|
// NOT IMPLEMENTED
|
|
// WARNING: DO NOT USE THIS REGISTER (see datasheet)
|
|
#define PCA9685_TESTMODE 0xFF // do not be use. see datasheet.
|
|
|
|
|
|
|
|
class PCA9685
|
|
{
|
|
public:
|
|
explicit PCA9685(const uint8_t deviceAddress, TwoWire *wire = &Wire);
|
|
|
|
#if defined (ESP8266) || defined(ESP32)
|
|
bool begin(uint8_t sda, uint8_t scl,
|
|
uint8_t mode1_mask = PCA9685_MODE1_AUTOINCR | PCA9685_MODE1_ALLCALL,
|
|
uint8_t mode2_mask = PCA9685_MODE2_TOTEMPOLE);
|
|
#endif
|
|
bool begin(uint8_t mode1_mask = PCA9685_MODE1_AUTOINCR | PCA9685_MODE1_ALLCALL,
|
|
uint8_t mode2_mask = PCA9685_MODE2_TOTEMPOLE);
|
|
void configure(uint8_t mode1_mask, uint8_t mode2_mask);
|
|
bool isConnected();
|
|
|
|
uint8_t channelCount() { return _channelCount; };
|
|
|
|
// reg = 1, 2 check datasheet for values
|
|
uint8_t writeMode(uint8_t reg, uint8_t value);
|
|
uint8_t readMode(uint8_t reg);
|
|
|
|
// convenience wrappers
|
|
uint8_t setMode1(uint8_t value) { return writeMode(PCA9685_MODE1, value); };
|
|
uint8_t setMode2(uint8_t value) { return writeMode(PCA9685_MODE2, value); };
|
|
uint8_t getMode1() { return readMode(PCA9685_MODE1); };
|
|
uint8_t getMode2() { return readMode(PCA9685_MODE2); };
|
|
|
|
|
|
// single PWM setting, channel = 0..15,
|
|
// onTime = 0..4095, offTime = 0..4095
|
|
// allows shifted PWM's e.g. 2 servo's that do not start at same time.
|
|
void setPWM(uint8_t channel, uint16_t onTime, uint16_t offTime);
|
|
void getPWM(uint8_t channel, uint16_t* onTime, uint16_t* offTime);
|
|
|
|
// single PWM setting, channel = 0..15, offTime = 0..4095 (onTime = 0)
|
|
void setPWM(uint8_t channel, uint16_t offTime);
|
|
|
|
|
|
// set update frequency for all channels
|
|
// freq = 24 - 1526 Hz
|
|
// note: as the frequency is converted to an 8 bit pre-scaler
|
|
// the frequency set will seldom be exact, but best effort.
|
|
void setFrequency(uint16_t freq, int offset = 0);
|
|
int getFrequency(bool cache = true);
|
|
|
|
// set channel HIGH or LOW (effectively no PWM)
|
|
void digitalWrite(uint8_t channel, uint8_t mode);
|
|
|
|
// for backwards compatibility; will be removed in future
|
|
void setON(uint8_t channel) { digitalWrite(channel, HIGH); };
|
|
void setOFF(uint8_t channel) { digitalWrite(channel, LOW); };
|
|
|
|
// experimental for 0.3.0
|
|
void allOFF();
|
|
|
|
int lastError();
|
|
|
|
/////////////////////////////////////////////////////
|
|
//
|
|
// SUB CALL - ALL CALL (since 0.4.0)
|
|
//
|
|
// nr = { 1, 2, 3 }
|
|
bool enableSubCall(uint8_t nr);
|
|
bool disableSubCall(uint8_t nr);
|
|
bool isEnabledSubCall(uint8_t nr);
|
|
bool setSubCallAddress(uint8_t nr, uint8_t address);
|
|
uint8_t getSubCallAddress(uint8_t nr);
|
|
|
|
bool enableAllCall();
|
|
bool disableAllCall();
|
|
bool isEnabledAllCall();
|
|
bool setAllCallAddress(uint8_t address);
|
|
uint8_t getAllCallAddress();
|
|
|
|
|
|
private:
|
|
// DIRECT CONTROL
|
|
void writeReg(uint8_t reg, uint8_t value);
|
|
void writeReg2(uint8_t reg, uint16_t a, uint16_t b);
|
|
uint8_t readReg(uint8_t reg);
|
|
|
|
uint8_t _address;
|
|
int _error;
|
|
int _freq = 200; // default PWM frequency - P25 datasheet
|
|
uint8_t _channelCount = 16;
|
|
|
|
TwoWire* _wire;
|
|
};
|
|
|
|
|
|
// -- END OF FILE --
|
|
|