Skip to content

Commit e7a1097

Browse files
committed
Measure oscillator frequency of your device
Change Wire pointer to reference
1 parent afbdf1f commit e7a1097

File tree

6 files changed

+176
-30
lines changed

6 files changed

+176
-30
lines changed

Adafruit_PWMServoDriver.cpp

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,32 @@
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
/*!
@@ -48,7 +64,7 @@ Adafruit_PWMServoDriver::Adafruit_PWMServoDriver(uint8_t addr, TwoWire *i2c) {
4864
* Sets External Clock (Optional)
4965
*/
5066
void Adafruit_PWMServoDriver::begin(uint8_t prescale) {
51-
_i2c->begin();
67+
_i2c.begin();
5268
reset();
5369
if (prescale) {
5470
setExtClk(prescale);
@@ -197,8 +213,8 @@ uint8_t Adafruit_PWMServoDriver::readPrescale(void)
197213
* @return requested PWM output value
198214
*/
199215
uint8_t Adafruit_PWMServoDriver::getPWM(uint8_t num) {
200-
_i2c->requestFrom((int)_i2caddr, PCA9685_LED0_ON_L + 4 * num, (int)4);
201-
return _i2c->read();
216+
_i2c.requestFrom((int)_i2caddr, PCA9685_LED0_ON_L + 4 * num, (int)4);
217+
return _i2c.read();
202218
}
203219

204220
/*!
@@ -217,13 +233,13 @@ void Adafruit_PWMServoDriver::setPWM(uint8_t num, uint16_t on, uint16_t off) {
217233
Serial.println(off);
218234
#endif
219235

220-
_i2c->beginTransmission(_i2caddr);
221-
_i2c->write(PCA9685_LED0_ON_L + 4 * num);
222-
_i2c->write(on);
223-
_i2c->write(on >> 8);
224-
_i2c->write(off);
225-
_i2c->write(off >> 8);
226-
_i2c->endTransmission();
236+
_i2c.beginTransmission(_i2caddr);
237+
_i2c.write(PCA9685_LED0_ON_L + 4 * num);
238+
_i2c.write(on);
239+
_i2c.write(on >> 8);
240+
_i2c.write(off);
241+
_i2c.write(off >> 8);
242+
_i2c.endTransmission();
227243
}
228244

229245
/*!
@@ -263,17 +279,17 @@ void Adafruit_PWMServoDriver::setPin(uint8_t num, uint16_t val, bool invert) {
263279
}
264280

265281
uint8_t Adafruit_PWMServoDriver::read8(uint8_t addr) {
266-
_i2c->beginTransmission(_i2caddr);
267-
_i2c->write(addr);
268-
_i2c->endTransmission();
282+
_i2c.beginTransmission(_i2caddr);
283+
_i2c.write(addr);
284+
_i2c.endTransmission();
269285

270-
_i2c->requestFrom((uint8_t)_i2caddr, (uint8_t)1);
271-
return _i2c->read();
286+
_i2c.requestFrom((uint8_t)_i2caddr, (uint8_t)1);
287+
return _i2c.read();
272288
}
273289

274290
void Adafruit_PWMServoDriver::write8(uint8_t addr, uint8_t d) {
275-
_i2c->beginTransmission(_i2caddr);
276-
_i2c->write(addr);
277-
_i2c->write(d);
278-
_i2c->endTransmission();
291+
_i2c.beginTransmission(_i2caddr);
292+
_i2c.write(addr);
293+
_i2c.write(d);
294+
_i2c.endTransmission();
279295
}

Adafruit_PWMServoDriver.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@
7575
*/
7676
class Adafruit_PWMServoDriver {
7777
public:
78-
Adafruit_PWMServoDriver(uint8_t addr = PCA9685_I2C_ADDRESS, TwoWire *I2C = &Wire);
78+
Adafruit_PWMServoDriver();
79+
Adafruit_PWMServoDriver(const uint8_t addr);
80+
Adafruit_PWMServoDriver(const uint8_t addr, TwoWire& i2c);
7981
void begin(uint8_t prescale = 0);
8082
void reset();
8183
void sleep();
@@ -90,8 +92,7 @@ class Adafruit_PWMServoDriver {
9092

9193
private:
9294
uint8_t _i2caddr;
93-
94-
TwoWire *_i2c;
95+
TwoWire& _i2c;
9596

9697
uint8_t read8(uint8_t addr);
9798
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);

examples/oscillator/oscillator.ino

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/***************************************************
2+
This is an example for our Adafruit 16-channel PWM & Servo driver
3+
to calibrate the frequency of the oscillator clock of the PCA9685.
4+
5+
CAUTION: DO NOT CONNECT ANY VOLTAGE HIGHER THAN THE BOARD LIMITS.
6+
For 3.3V boards, like the ESP8266, remove any 5V input. The setup will
7+
feed the voltage back into the board to measure the frequency.
8+
KABOEM, SMOKE if you use too much VOLTAGE.
9+
10+
Connect the PCA9685 with I2C (Ground, VCC, SCL, SCA) and apply
11+
voltage on V+. See above not higher than board limits.
12+
Connect the signal (yellow pin, PWM) of the PCA9685 to your board:
13+
Default is pin 3, last of first block.
14+
Default is pin 14 (of your ESP8266).
15+
16+
Formula for prescale to get the targetted frequency (=update_rate) is:
17+
prescale = round ( osc_clock / 4096 * update_rate) - 1
18+
rewritten: osc_clock = (prescale + 1) * 4096 * update_rate
19+
We will measure the real update_rate to assert the real osc_clock.
20+
21+
***************************************************/
22+
23+
#include <Wire.h>
24+
#include <Adafruit_PWMServoDriver.h>
25+
26+
// called this way, it uses the default address 0x40
27+
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40, Wire);
28+
// you can also call it with a different address you want
29+
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40);
30+
// you can also call it with a different address and I2C interface
31+
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40, Wire);
32+
33+
#if (defined(ESP8266) || defined(ESP32))
34+
35+
// Applied frequency in the test: can be changed to get the optimal
36+
// oscillator calibration for your targetted frequency.
37+
#define FREQUENCY 50
38+
39+
// CAUTION: ONLY CONNECT server and ESP WITHOUT 5V ON V+ or green breakout supply pins. Use 3.3V on V+
40+
#define PIN_SERVO_FEEDBACK 3 // Connect Yellow PWM pin, 3 = last on first block
41+
#define PIN_BOARD_FEEDBACK 14 // 14 => D5 on NodeMCU
42+
43+
uint8_t prescale = 0;
44+
// loop
45+
#define INTERVAL 1000 // 1 sec
46+
int32_t lastEvaluation = 0;
47+
uint16_t frozenCounter = 0;
48+
uint16_t countDeviations = 0;
49+
50+
uint32_t totalCounter = 0;
51+
uint32_t totalTime = 0; // in millis
52+
uint32_t realOsciFreq = 0;
53+
uint32_t multiplier = 4096;
54+
55+
// interrupt
56+
volatile uint16_t interruptCounter = 0;
57+
58+
ICACHE_RAM_ATTR void handleInterrupt() {
59+
interruptCounter++;
60+
}
61+
62+
void setup() {
63+
Serial.begin(115200);
64+
Serial.println("PCA9685 Oscillator test");
65+
66+
// set PCA9685
67+
pwm.begin();
68+
pwm.setPWMFreq(FREQUENCY); // Set some frequency
69+
pwm.setPWM(PIN_SERVO_FEEDBACK,0,2048); // half of time high, half of time low
70+
prescale = pwm.readPrescale(); // read prescale
71+
Serial.printf("Target frequency: %u\n", FREQUENCY);
72+
Serial.printf("Applied prescale: %u\n", prescale);
73+
74+
// prepare interrupt on ESP pin
75+
pinMode(PIN_BOARD_FEEDBACK, INPUT);
76+
attachInterrupt(digitalPinToInterrupt(PIN_BOARD_FEEDBACK), handleInterrupt, RISING);
77+
78+
// take a breath and reset to zero
79+
delay(10);
80+
interruptCounter = 0;
81+
lastEvaluation = millis();
82+
}
83+
84+
void loop() {
85+
if (millis() - lastEvaluation > INTERVAL)
86+
{
87+
// first freeze counters and adjust for new round
88+
frozenCounter = interruptCounter; // first freeze counter
89+
interruptCounter -= frozenCounter;
90+
lastEvaluation += INTERVAL;
91+
92+
totalCounter += frozenCounter;
93+
totalTime += 1;
94+
95+
// only print deviations from targetted frequency
96+
//if (frozenCounter != FREQUENCY)
97+
{
98+
multiplier = 4096;
99+
realOsciFreq = (prescale + 1) * totalCounter; // first part calcutlation
100+
// now follows an ugly hack to have maximum precision in 32 bits
101+
while (((realOsciFreq & 0x80000000) == 0) && (multiplier != 1))
102+
{
103+
realOsciFreq <<= 1;
104+
multiplier >>= 1;
105+
}
106+
realOsciFreq /= totalTime;
107+
if (multiplier) realOsciFreq *= multiplier;
108+
109+
countDeviations++;
110+
Serial.printf("%4u", countDeviations);
111+
Serial.printf(" Timestamp: %4u ", totalTime);
112+
Serial.printf(" Freq: %4u ", frozenCounter);
113+
Serial.printf(" Counter: %6u ", totalCounter);
114+
Serial.printf(" calc.osci.freq: %9u\n",realOsciFreq);
115+
}
116+
}
117+
118+
}
119+
#else
120+
121+
void setup() {
122+
Serial.begin(115200);
123+
Serial.println("PCA9685 Oscillator test");
124+
Serial.println("yet not available for your board."); // please help adapt the code!
125+
}
126+
127+
void loop() {}
128+
129+
#endif // ESP8266/ESP32

examples/pwmtest/pwmtest.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);

examples/servo/servo.ino

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

3030
// Depending on your servo make, the pulse width min and max may vary, you
3131
// want these to be as small/large as possible without hitting the hard stop

0 commit comments

Comments
 (0)