Skip to content

Commit caa5a89

Browse files
authored
Merge pull request #61 from Bolukan/master
Unbug Prescale calculation and update oscillator frequency handler
2 parents fbe4894 + 6664ce9 commit caa5a89

File tree

6 files changed

+239
-58
lines changed

6 files changed

+239
-58
lines changed

Adafruit_PWMServoDriver.cpp

Lines changed: 62 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,38 @@
3030
#include "Adafruit_PWMServoDriver.h"
3131
#include <Wire.h>
3232

33+
/*!
34+
* @brief Instantiates a new PCA9685 PWM driver chip with the I2C address on a
35+
* TwoWire interface
36+
*/
37+
Adafruit_PWMServoDriver::Adafruit_PWMServoDriver():
38+
_i2caddr(PCA9685_I2C_ADDRESS), _i2c(&Wire) {
39+
}
40+
41+
/*!
42+
* @brief Instantiates a new PCA9685 PWM driver chip with the I2C address on a
43+
* TwoWire interface
44+
* @param addr The 7-bit I2C address to locate this chip, default is 0x40
45+
*/
46+
Adafruit_PWMServoDriver::Adafruit_PWMServoDriver(const uint8_t addr):
47+
_i2caddr(addr), _i2c(&Wire) {
48+
}
49+
3350
/*!
3451
* @brief Instantiates a new PCA9685 PWM driver chip with the I2C address on a
3552
* TwoWire interface
3653
* @param addr The 7-bit I2C address to locate this chip, default is 0x40
37-
* @param i2c A pointer to a 'Wire' compatible object that we'll use to
38-
* communicate with
54+
* @param i2c A reference to a 'TwoWire' object that we'll use to communicate
55+
* with
3956
*/
40-
Adafruit_PWMServoDriver::Adafruit_PWMServoDriver(uint8_t addr, TwoWire *i2c) {
41-
_i2c = i2c;
42-
_i2caddr = addr;
57+
Adafruit_PWMServoDriver::Adafruit_PWMServoDriver(const uint8_t addr, TwoWire& i2c) :
58+
_i2caddr(addr), _i2c(&i2c) {
4359
}
4460

4561
/*!
4662
* @brief Setups the I2C interface and hardware
4763
* @param prescale
4864
* Sets External Clock (Optional)
49-
*
5065
*/
5166
void Adafruit_PWMServoDriver::begin(uint8_t prescale) {
5267
_i2c->begin();
@@ -63,7 +78,7 @@ void Adafruit_PWMServoDriver::begin(uint8_t prescale) {
6378
* @brief Sends a reset command to the PCA9685 chip over I2C
6479
*/
6580
void Adafruit_PWMServoDriver::reset() {
66-
write8(PCA9685_MODE1, 0x80);
81+
write8(PCA9685_MODE1, MODE1_RESTART);
6782
delay(10);
6883
}
6984

@@ -72,7 +87,7 @@ void Adafruit_PWMServoDriver::reset() {
7287
*/
7388
void Adafruit_PWMServoDriver::sleep() {
7489
uint8_t awake = read8(PCA9685_MODE1);
75-
uint8_t sleep = awake | 0x10; // set sleep bit high
90+
uint8_t sleep = awake | MODE1_SLEEP; // set sleep bit high
7691
write8(PCA9685_MODE1, sleep);
7792
delay(5); // wait until cycle ends for sleep to be active
7893
}
@@ -82,31 +97,29 @@ void Adafruit_PWMServoDriver::sleep() {
8297
*/
8398
void Adafruit_PWMServoDriver::wakeup() {
8499
uint8_t sleep = read8(PCA9685_MODE1);
85-
uint8_t wakeup = sleep & ~0x10; // set sleep bit low
100+
uint8_t wakeup = sleep & ~MODE1_SLEEP; // set sleep bit low
86101
write8(PCA9685_MODE1, wakeup);
87102
}
88103

89-
/**************************************************************************/
90104
/*!
91-
@brief Sets EXTCLK pin to use the external clock
92-
@param prescale Configures the prescale value to be used by the external
93-
clock
94-
*/
95-
/**************************************************************************/
105+
* @brief Sets EXTCLK pin to use the external clock
106+
* @param prescale
107+
* Configures the prescale value to be used by the external clock
108+
*/
96109
void Adafruit_PWMServoDriver::setExtClk(uint8_t prescale) {
97110
uint8_t oldmode = read8(PCA9685_MODE1);
98-
uint8_t newmode = (oldmode & 0x7F) | 0x10; // sleep
111+
uint8_t newmode = (oldmode & ~MODE1_RESTART) | MODE1_SLEEP; // sleep
99112
write8(PCA9685_MODE1, newmode); // go to sleep, turn off internal oscillator
100113

101114
// This sets both the SLEEP and EXTCLK bits of the MODE1 register to switch to
102115
// use the external clock.
103-
write8(PCA9685_MODE1, (newmode |= 0x40));
116+
write8(PCA9685_MODE1, (newmode |= MODE1_EXTCLK));
104117

105118
write8(PCA9685_PRESCALE, prescale); // set the prescaler
106119

107120
delay(5);
108-
write8(PCA9685_MODE1,
109-
(newmode & ~(0x10)) | 0xa0); // clear the SLEEP bit to start
121+
// clear the SLEEP bit to start
122+
write8(PCA9685_MODE1, (newmode & ~MODE1_SLEEP) | MODE1_RESTART | MODE1_AI);
110123

111124
#ifdef ENABLE_DEBUG_OUTPUT
112125
Serial.print("Mode now 0x");
@@ -123,34 +136,36 @@ void Adafruit_PWMServoDriver::setPWMFreq(float freq) {
123136
Serial.print("Attempting to set freq ");
124137
Serial.println(freq);
125138
#endif
126-
127-
freq *=
128-
0.9; // Correct for overshoot in the frequency setting (see issue #11).
129-
float prescaleval = 25000000;
139+
// Range output modulation frequency is dependant on oscillator
140+
if (freq < 1) freq = 1;
141+
if (freq > 3500) freq = 3500; // Datasheet limit is 3052=50MHz/(4*4096)
142+
/*
143+
freq *= 0.9; // Correct for overshoot in the frequency setting (see issue #11)
144+
float prescaleval = FREQUENCY_OSCILLATOR;
145+
*/
146+
uint32_t prescaleval = FREQUENCY_CALIBRATED;
147+
prescaleval /= freq; // required output modulation frequency
148+
// rounding to nearest number is equal to adding 0,5 and floor to nearest number
149+
prescaleval += 2048;
130150
prescaleval /= 4096;
131-
prescaleval /= freq;
132151
prescaleval -= 1;
152+
if (prescaleval < PCA9685_PRESCALE_MIN) prescaleval = PCA9685_PRESCALE_MIN;
153+
if (prescaleval > PCA9685_PRESCALE_MAX) prescaleval = PCA9685_PRESCALE_MAX;
154+
uint8_t prescale = (uint8_t) prescaleval;
133155

134-
#ifdef ENABLE_DEBUG_OUTPUT
135-
Serial.print("Estimated pre-scale: ");
136-
Serial.println(prescaleval);
137-
#endif
138-
139-
uint8_t prescale = floor(prescaleval + 0.5);
140156
#ifdef ENABLE_DEBUG_OUTPUT
141157
Serial.print("Final pre-scale: ");
142158
Serial.println(prescale);
143159
#endif
144160

145161
uint8_t oldmode = read8(PCA9685_MODE1);
146-
uint8_t newmode = (oldmode & 0x7F) | 0x10; // sleep
162+
uint8_t newmode = (oldmode & ~MODE1_RESTART) | MODE1_SLEEP; // sleep
147163
write8(PCA9685_MODE1, newmode); // go to sleep
148164
write8(PCA9685_PRESCALE, prescale); // set the prescaler
149165
write8(PCA9685_MODE1, oldmode);
150166
delay(5);
151-
write8(PCA9685_MODE1,
152-
oldmode |
153-
0xa0); // This sets the MODE1 register to turn on auto increment.
167+
// This sets the MODE1 register to turn on auto increment.
168+
write8(PCA9685_MODE1, oldmode | MODE1_RESTART | MODE1_AI);
154169

155170
#ifdef ENABLE_DEBUG_OUTPUT
156171
Serial.print("Mode now 0x");
@@ -169,10 +184,10 @@ void Adafruit_PWMServoDriver::setOutputMode(bool totempole) {
169184
uint8_t oldmode = read8(PCA9685_MODE2);
170185
uint8_t newmode;
171186
if (totempole) {
172-
newmode = (oldmode&0x7F) | 0x04;
187+
newmode = oldmode | MODE2_OUTDRV;
173188
}
174189
else {
175-
newmode = (oldmode&0x7F) & ~0x04;
190+
newmode = oldmode & ~MODE2_OUTDRV;
176191
}
177192
write8(PCA9685_MODE2, newmode);
178193
#ifdef ENABLE_DEBUG_OUTPUT
@@ -183,13 +198,22 @@ void Adafruit_PWMServoDriver::setOutputMode(bool totempole) {
183198
#endif
184199
}
185200

201+
/*!
202+
* @brief Reads set Prescale from PCA9685
203+
* @return prescale value
204+
*/
205+
uint8_t Adafruit_PWMServoDriver::readPrescale(void)
206+
{
207+
return read8(PCA9685_PRESCALE);
208+
}
209+
186210
/*!
187211
* @brief Gets the PWM output of one of the PCA9685 pins
188212
* @param num One of the PWM output pins, from 0 to 15
189213
* @return requested PWM output value
190214
*/
191215
uint8_t Adafruit_PWMServoDriver::getPWM(uint8_t num) {
192-
_i2c->requestFrom((int)_i2caddr, LED0_ON_L + 4 * num, (int)4);
216+
_i2c->requestFrom((int)_i2caddr, PCA9685_LED0_ON_L + 4 * num, (int)4);
193217
return _i2c->read();
194218
}
195219

@@ -210,7 +234,7 @@ void Adafruit_PWMServoDriver::setPWM(uint8_t num, uint16_t on, uint16_t off) {
210234
#endif
211235

212236
_i2c->beginTransmission(_i2caddr);
213-
_i2c->write(LED0_ON_L + 4 * num);
237+
_i2c->write(PCA9685_LED0_ON_L + 4 * num);
214238
_i2c->write(on);
215239
_i2c->write(on >> 8);
216240
_i2c->write(off);

Adafruit_PWMServoDriver.h

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,30 +25,58 @@
2525
#include <Arduino.h>
2626
#include <Wire.h>
2727

28-
#define PCA9685_SUBADR1 0x2 /**< i2c bus address 1 */
29-
#define PCA9685_SUBADR2 0x3 /**< i2c bus address 2 */
30-
#define PCA9685_SUBADR3 0x4 /**< i2c bus address 3 */
28+
// REGISTER ADDRESSES
29+
#define PCA9685_MODE1 0x00 /**< Mode Register 1 */
30+
#define PCA9685_MODE2 0x01 /**< Mode Register 2 */
31+
#define PCA9685_SUBADR1 0x02 /**< I2C-bus subaddress 1 */
32+
#define PCA9685_SUBADR2 0x03 /**< I2C-bus subaddress 2 */
33+
#define PCA9685_SUBADR3 0x04 /**< I2C-bus subaddress 3 */
34+
#define PCA9685_ALLCALLADR 0x05 /**< LED All Call I2C-bus address */
35+
#define PCA9685_LED0_ON_L 0x06 /**< LED0 output and brightness control byte 0 */
36+
#define PCA9685_LED0_ON_H 0x07 /**< LED0 output and brightness control byte 1 */
37+
#define PCA9685_LED0_OFF_L 0x08 /**< LED0 output and brightness control byte 2 */
38+
#define PCA9685_LED0_OFF_H 0x09 /**< LED0 output and brightness control byte 3 */
39+
// etc all 16: LED15_OFF_H 0x45
40+
#define PCA9685_ALLLED_ON_L 0xFA /**< load all the LEDn_ON registers, byte 0 */
41+
#define PCA9685_ALLLED_ON_H 0xFB /**< load all the LEDn_ON registers, byte 1 */
42+
#define PCA9685_ALLLED_OFF_L 0xFC /**< load all the LEDn_OFF registers, byte 0 */
43+
#define PCA9685_ALLLED_OFF_H 0xFD /**< load all the LEDn_OFF registers, byte 1 */
44+
#define PCA9685_PRESCALE 0xFE /**< Prescaler for PWM output frequency */
45+
#define PCA9685_TESTMODE 0xFF /**< defines the test mode to be entered */
3146

32-
#define PCA9685_MODE1 0x0 /**< Mode Register 1 */
33-
#define PCA9685_MODE2 0x1 /**< Mode Register 2 */
34-
#define PCA9685_PRESCALE 0xFE /**< Prescaler for PWM output frequency */
47+
// MODE1 bits
48+
#define MODE1_ALLCAL 0x01 /**< respond to LED All Call I2C-bus address */
49+
#define MODE1_SUB3 0x02 /**< respond to I2C-bus subaddress 3 */
50+
#define MODE1_SUB2 0x04 /**< respond to I2C-bus subaddress 2 */
51+
#define MODE1_SUB1 0x08 /**< respond to I2C-bus subaddress 1 */
52+
#define MODE1_SLEEP 0x10 /**< Low power mode. Oscillator off */
53+
#define MODE1_AI 0x20 /**< Auto-Increment enabled */
54+
#define MODE1_EXTCLK 0x40 /**< Use EXTCLK pin clock */
55+
#define MODE1_RESTART 0x80 /**< Restart enabled */
56+
// MODE2 bits
57+
#define MODE2_OUTNE_0 0x01 /**< Active LOW output enable input */
58+
#define MODE2_OUTNE_1 0x02 /**< Active LOW output enable input - high impedience */
59+
#define MODE2_OUTDRV 0x04 /**< totem pole structure vs open-drain */
60+
#define MODE2_OCH 0x08 /**< Outputs change on ACK vs STOP */
61+
#define MODE2_INVRT 0x10 /**< Output logic state inverted */
3562

36-
#define LED0_ON_L 0x6 /**< LED0 output and brightness control byte 0 */
37-
#define LED0_ON_H 0x7 /**< LED0 output and brightness control byte 1 */
38-
#define LED0_OFF_L 0x8 /**< LED0 output and brightness control byte 2 */
39-
#define LED0_OFF_H 0x9 /**< LED0 output and brightness control byte 3 */
63+
#define PCA9685_I2C_ADDRESS 0x40 /**< Default PCA9685 I2C Slave Address */
4064

41-
#define ALLLED_ON_L 0xFA /**< load all the LEDn_ON registers, byte 0 */
42-
#define ALLLED_ON_H 0xFB /**< load all the LEDn_ON registers, byte 1 */
43-
#define ALLLED_OFF_L 0xFC /**< load all the LEDn_OFF registers, byte 0 */
44-
#define ALLLED_OFF_H 0xFD /**< load all the LEDn_OFF registers, byte 1 */
65+
#define FREQUENCY_OSCILLATOR 25000000 /**< Oscillator frequency cf satasheet */
66+
#define FREQUENCY_CALIBRATED 26075000 /**< Oscillator frequency measured at 104.3% */
67+
#define FREQUENCY_LEGACY 27777778 /**< Oscillator frequency using freq /= 0,9 */
68+
69+
#define PCA9685_PRESCALE_MIN 3 /**< minimum prescale value */
70+
#define PCA9685_PRESCALE_MAX 255 /**< maximum prescale value */
4571

4672
/*!
4773
* @brief Class that stores state and functions for interacting with PCA9685 PWM chip
4874
*/
4975
class Adafruit_PWMServoDriver {
5076
public:
51-
Adafruit_PWMServoDriver(uint8_t addr = 0x40, TwoWire *I2C = &Wire);
77+
Adafruit_PWMServoDriver();
78+
Adafruit_PWMServoDriver(const uint8_t addr);
79+
Adafruit_PWMServoDriver(const uint8_t addr, TwoWire& i2c);
5280
void begin(uint8_t prescale = 0);
5381
void reset();
5482
void sleep();
@@ -59,11 +87,11 @@ class Adafruit_PWMServoDriver {
5987
uint8_t getPWM(uint8_t num);
6088
void setPWM(uint8_t num, uint16_t on, uint16_t off);
6189
void setPin(uint8_t num, uint16_t val, bool invert=false);
90+
uint8_t readPrescale(void);
6291

6392
private:
6493
uint8_t _i2caddr;
65-
66-
TwoWire *_i2c;
94+
TwoWire* _i2c;
6795

6896
uint8_t read8(uint8_t addr);
6997
void write8(uint8_t addr, uint8_t d);

examples/gpiotest/gpiotest.ino

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
2424
// you can also call it with a different address you want
2525
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41);
2626
// you can also call it with a different address and I2C interface
27-
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40, &Wire);
27+
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40, Wire);
2828

2929
void setup() {
3030
Serial.begin(9600);

0 commit comments

Comments
 (0)