Skip to content

Commit 0d635e6

Browse files
committed
0.7.3 PCA9685_RT
1 parent 893c141 commit 0d635e6

File tree

20 files changed

+183
-41
lines changed

20 files changed

+183
-41
lines changed

libraries/PCA9685_RT/.arduino-ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ compile:
2020
# - due
2121
# - zero
2222
# - leonardo
23-
- m4
23+
# - m4
2424
- esp32
2525
# - esp8266
2626
# - mega2560

libraries/PCA9685_RT/.github/workflows/arduino-lint.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ jobs:
66
runs-on: ubuntu-latest
77
timeout-minutes: 5
88
steps:
9-
- uses: actions/checkout@v4
10-
- uses: arduino/arduino-lint-action@v1
9+
- uses: actions/checkout@v5
10+
- uses: arduino/arduino-lint-action@v2
1111
with:
1212
library-manager: update
1313
compliance: strict

libraries/PCA9685_RT/.github/workflows/arduino_test_runner.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
timeout-minutes: 20
99

1010
steps:
11-
- uses: actions/checkout@v4
11+
- uses: actions/checkout@v5
1212
- uses: ruby/setup-ruby@v1
1313
with:
1414
ruby-version: 2.6

libraries/PCA9685_RT/.github/workflows/jsoncheck.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ on:
55
paths:
66
- '**.json'
77
pull_request:
8+
paths:
9+
- '**.json'
810

911
jobs:
1012
test:
1113
runs-on: ubuntu-latest
1214
timeout-minutes: 5
1315
steps:
14-
- uses: actions/checkout@v4
16+
- uses: actions/checkout@v5
1517
- name: json-syntax-check
1618
uses: limitusus/json-syntax-check@v2
1719
with:

libraries/PCA9685_RT/CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

88

9+
## [0.7.3] - 2025-11-13
10+
- fix #29, add read1()
11+
- fix mask in setPWM()
12+
- refactored getPWM()
13+
- update GitHub actions
14+
- made some functions explicit deprecated.
15+
- update examples
16+
- update readme.md
17+
- update keywords.txt
18+
- minor edits
19+
20+
921
## [0.7.2] - 2025-05-08
1022
- fix #30, Sync with PCA9634
1123
- fix OutputEnable pin code

libraries/PCA9685_RT/PCA9685.cpp

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// FILE: PCA9685.cpp
33
// AUTHOR: Rob Tillaart
44
// DATE: 24-apr-2016
5-
// VERSION: 0.7.2
5+
// VERSION: 0.7.3
66
// PURPOSE: Arduino library for PCA9685 I2C LED driver, 16 channel PWM, 12 bit.
77
// URL: https://github.com/RobTillaart/PCA9685_RT
88

@@ -139,9 +139,10 @@ uint8_t PCA9685::setPWM(uint8_t channel, uint16_t onTime, uint16_t offTime)
139139
_error = PCA9685_ERR_CHANNEL;
140140
return _error;
141141
}
142-
offTime &= 0x0FFFF; // non-doc feature - to easy set figure 8 P.17
143142
uint8_t reg = PCA9685_CHANNEL(channel);
144-
return writeRegister2(reg, onTime, offTime);
143+
// mask both onTime and offTime to be in range 0..4095
144+
// allows to set the FULL_ON / FULL OFF bits.
145+
return writeRegister2(reg, onTime & 0x1FFF, offTime & 0x1FFF);
145146
}
146147

147148

@@ -153,6 +154,7 @@ uint8_t PCA9685::setPWM(uint8_t channel, uint16_t offTime)
153154

154155

155156
// read value from single PWM channel
157+
// may include FULL_ON or FULL_OFF bit.
156158
uint8_t PCA9685::getPWM(uint8_t channel, uint16_t* onTime, uint16_t* offTime)
157159
{
158160
_error = PCA9685_OK;
@@ -162,19 +164,7 @@ uint8_t PCA9685::getPWM(uint8_t channel, uint16_t* onTime, uint16_t* offTime)
162164
return _error;
163165
}
164166
uint8_t reg = PCA9685_CHANNEL(channel);
165-
_wire->beginTransmission(_address);
166-
_wire->write(reg);
167-
_error = _wire->endTransmission();
168-
if (_wire->requestFrom(_address, (uint8_t)4) != 4)
169-
{
170-
_error = PCA9685_ERR_I2C;
171-
return _error;
172-
}
173-
uint16_t _data = _wire->read();
174-
*onTime = (_wire->read() * 256) + _data;
175-
_data = _wire->read();
176-
*offTime = (_wire->read() * 256) + _data;
177-
return _error;
167+
return readRegister2(reg, onTime, offTime);
178168
}
179169

180170

@@ -199,8 +189,8 @@ uint8_t PCA9685::setFrequency(uint16_t freq, int offset)
199189
}
200190

201191

202-
// returns the actual used frequency.
203-
// therefore it does not use offset
192+
// cache == false returns the actual used frequency.
193+
// cache == true returns the frequency without pre-scaler (offset).
204194
uint16_t PCA9685::getFrequency(bool cache)
205195
{
206196
_error = PCA9685_OK;
@@ -230,6 +220,31 @@ uint8_t PCA9685::write1(uint8_t channel, uint8_t mode)
230220
}
231221

232222

223+
// See #29 for discussion.
224+
uint8_t PCA9685::read1(uint8_t channel)
225+
{
226+
_error = PCA9685_OK;
227+
if (channel >= _channelCount)
228+
{
229+
_error = PCA9685_ERR_CHANNEL;
230+
return _error;
231+
}
232+
233+
uint8_t reg = PCA9685_CHANNEL(channel);
234+
235+
uint16_t on, off;
236+
if (readRegister2(reg, &on, &off) != PCA9685_OK) return 2;
237+
if (on & 0x1000) return HIGH; // FULL_ON
238+
if (off & 0x1000) return LOW; // FULL_OFF
239+
240+
uint16_t duty = (off - on) & 0x0FFF;
241+
if (duty == 4095) return HIGH; // PWM = 100%
242+
if (duty == 0) return LOW; // PWM = 0%
243+
244+
return 2; // not HIGH not LOW => use getPWM() to get PWM value.
245+
}
246+
247+
233248
uint8_t PCA9685::allOFF()
234249
{
235250
_error = PCA9685_OK;
@@ -479,6 +494,7 @@ uint8_t PCA9685::readRegister(uint8_t reg)
479494
_wire->beginTransmission(_address);
480495
_wire->write(reg);
481496
_error = _wire->endTransmission();
497+
if (_error != PCA9685_OK) return 0;
482498

483499
if (_wire->requestFrom(_address, (uint8_t)1) != 1)
484500
{
@@ -490,5 +506,26 @@ uint8_t PCA9685::readRegister(uint8_t reg)
490506
}
491507

492508

509+
int PCA9685::readRegister2(uint8_t reg, uint16_t * v1, uint16_t * v2)
510+
{
511+
_wire->beginTransmission(_address);
512+
_wire->write(reg);
513+
_error = _wire->endTransmission();
514+
if (_error != PCA9685_OK) return _error;
515+
516+
if (_wire->requestFrom(_address, (uint8_t)4) != 4)
517+
{
518+
_error = PCA9685_ERR_I2C;
519+
return _error;
520+
}
521+
uint16_t data = _wire->read();
522+
*v1 = (_wire->read() * 256) + data;
523+
data = _wire->read();
524+
*v2 = (_wire->read() * 256) + data;
525+
_error = PCA9685_OK;
526+
return _error;
527+
}
528+
529+
493530
// -- END OF FILE --
494531

libraries/PCA9685_RT/PCA9685.h

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// FILE: PCA9685.h
44
// AUTHOR: Rob Tillaart
55
// DATE: 24-apr-2016
6-
// VERSION: 0.7.2
6+
// VERSION: 0.7.3
77
// PURPOSE: Arduino library for PCA9685 I2C LED driver, 16 channel PWM, 12 bit.
88
// URL: https://github.com/RobTillaart/PCA9685_RT
99

@@ -12,7 +12,7 @@
1212
#include "Wire.h"
1313

1414

15-
#define PCA9685_LIB_VERSION (F("0.7.2"))
15+
#define PCA9685_LIB_VERSION (F("0.7.3"))
1616

1717
// ERROR CODES
1818
#define PCA9685_OK 0x00
@@ -111,6 +111,7 @@ class PCA9685
111111
// onTime = 0..4095, offTime = 0..4095
112112
// allows shifted PWM's e.g. 2 servo's that do not start at same time.
113113
// this will distribute the (peak) load
114+
// a value of 4096 sets the FULL_ON or FULL_OFF bit.
114115
uint8_t setPWM(uint8_t channel, uint16_t onTime, uint16_t offTime);
115116
uint8_t getPWM(uint8_t channel, uint16_t* onTime, uint16_t* offTime);
116117

@@ -123,19 +124,21 @@ class PCA9685
123124
// note: as the frequency is converted to an 8 bit pre-scaler
124125
// the frequency set will seldom be exact, but best effort.
125126
uint8_t setFrequency(uint16_t freq, int offset = 0);
127+
// cache == false returns the actual used frequency (with offset).
128+
// cache == true returns the set frequency without offset.
126129
uint16_t getFrequency(bool cache = true);
127130

128131

129132
/////////////////////////////////////////////////////
130133
//
131134
// WRITE
132135
//
133-
// set channel HIGH or LOW (effectively no PWM)
136+
// set channel mode == HIGH or LOW (effectively no PWM)
134137
uint8_t write1(uint8_t channel, uint8_t mode);
135-
136-
// for backwards compatibility; will be removed in future
137-
uint8_t setON(uint8_t channel) { return write1(channel, HIGH); };
138-
uint8_t setOFF(uint8_t channel) { return write1(channel, LOW); };
138+
// returns {HIGH, LOW, 2},
139+
// 2 => PWM value set; use getPWM() if needed.
140+
// check getError() to see if an I2C error occurred.
141+
uint8_t read1(uint8_t channel);
139142

140143
uint8_t allOFF();
141144

@@ -192,14 +195,22 @@ class PCA9685
192195
//
193196
[[deprecated("use setMode1(value) or setMode2(value) instead")]]
194197
uint8_t writeMode(uint8_t reg, uint8_t value);
198+
[[deprecated("use getMode1() or getMode2() instead")]]
195199
uint8_t readMode(uint8_t reg);
196200

201+
// for backwards compatibility; will be removed in future
202+
[[deprecated("use write1(uint8_t channel, uint8_t mode) instead")]]
203+
uint8_t setON(uint8_t channel) { return write1(channel, HIGH); };
204+
[[deprecated("use write1(uint8_t channel, uint8_t mode) instead")]]
205+
uint8_t setOFF(uint8_t channel) { return write1(channel, LOW); };
206+
197207

198208
private:
199209
// DIRECT CONTROL
200210
uint8_t writeRegister(uint8_t reg, uint8_t value);
201211
uint8_t writeRegister2(uint8_t reg, uint16_t a, uint16_t b);
202212
uint8_t readRegister(uint8_t reg);
213+
int readRegister2(uint8_t reg, uint16_t * v1, uint16_t * v2);
203214

204215
uint8_t _address;
205216
TwoWire * _wire;

libraries/PCA9685_RT/README.md

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ Obsolete in near future, use functions above as those are less error prone.
112112

113113
### Constants for mode registers
114114

115-
| Name | Value | Description |
115+
| MODE1 names | Value | Description |
116116
|:--------------------------|:-------:|:-------------------------------------|
117117
| PCA9685_MODE1_RESTART | 0x80 | 0 = disable 1 = enable |
118118
| PCA9685_MODE1_EXTCLK | 0x40 | 0 = internal 1 = external |
@@ -123,7 +123,10 @@ Obsolete in near future, use functions above as those are less error prone.
123123
| PCA9685_MODE1_SUB3 | 0x02 | 0 = disable 1 = enable |
124124
| PCA9685_MODE1_ALLCALL | 0x01 | 0 = disable 1 = enable |
125125
| PCA9685_MODE1_NONE | 0x00 | |
126-
| ---- | | |
126+
127+
128+
| MODE2 names | Value | Description |
129+
|:--------------------------|:-------:|:-------------------------------------|
127130
| PCA9685_MODE2_INVERT | 0x10 | 0 = normal 1 = inverted |
128131
| PCA9685_MODE2_STOP | 0x08 | 0 = on STOP 1 = on ACK |
129132
| PCA9685_MODE2_TOTEMPOLE | 0x04 | 0 = open drain 1 = totem-pole |
@@ -157,17 +160,23 @@ The signal is divided in 4096 steps, 0..4095.
157160
The pulse can begin **onTime** on any step and it can stop on any step **offTime**.
158161
This allows e.g. to distribute the power over the 16 channels, e.g. the
159162
channels do not need to start at the same moment with HIGH.
163+
The values 4096 allows to set the FULL_ON or FULL OFF bit.
160164
- **uint8_t setPWM(uint8_t channel, offTime)** simple PWM that always start on **onTime = 0**.
161165
- **uint8_t getPWM(uint8_t channel, uint16_t \* onTime, uint16_t \* offTime)**
162166
read back the configuration of the channel.
167+
Note the values may include the FULL ON / FULL OFF bit (0x1000).
163168
- **uint8_t allOFF()** switches all PWM channels OFF. **Experimental** in 0.3.0.
164169
To "undo" the allOFF one can call the **reset()** function and set all
165170
PWM channels again.
166-
- **uint8_t write1(channel, mode)** mode = HIGH or LOW, just use the PCA9685 as
167-
a digital pin, write 1 bit.
168-
This single function replaces the setON() and setOFF() that will become
171+
- **uint8_t write1(uint8_t channel, uint8_t mode)** mode = HIGH or LOW, just use the PCA9685 as
172+
a digital pin, write 1 bit. Returns PCA9685_OK or error code.
173+
This single write1() function replaces the setON() and setOFF() that will become
169174
obsolete in the future.
170175

176+
**fix #29 experimental**
177+
- **uint8_t read1(uint8_t channel)** reads the status of the digital pin.
178+
Can return { LOW = 0, HIGH = 1, other = 2 } or an error code.
179+
171180

172181
### Frequency
173182

@@ -185,7 +194,7 @@ After changing the frequency, one must set all channels (again),
185194
so one should set the frequency in **setup()**
186195

187196
The parameter offset can be used to tune the **preScaler** to get a frequency
188-
closer to the requested value. See **PCA9685_setFrequency_offset** example.
197+
closer to the requested value. See **PCA9685_setFrequency_offset.ino** example.
189198
Default the offset = 0. As the **preScaler** is smaller at higher frequencies
190199
higher frequencies are less accurate.
191200
Making offset too large can result in very incorrect frequencies.
@@ -205,6 +214,8 @@ When using offset, the **getFrequency(false)** will return the adjusted **preSca
205214
| PCA9685_ERR_MODE | 0xFD | Invalid mode
206215
| PCA9685_ERR_I2C | 0xFC | I2C communication error
207216

217+
Be sure to check **lastError()** for possible errors if you want robustness.
218+
208219

209220
## SUB CALL and ALL CALL
210221

@@ -233,12 +244,16 @@ Typically there is only one such group but one can configure more of them by app
233244

234245
The functions to enable all/sub-addresses are straightforward:
235246

247+
SubCall functions:
248+
236249
- **bool enableSubCall(uint8_t nr)** nr = 1,2,3
237250
- **bool disableSubCall(uint8_t nr)** nr = 1,2,3
238251
- **bool isEnabledSubCall(uint8_t nr)** nr = 1,2,3
239252
- **bool setSubCallAddress(uint8_t nr, uint8_t address)**
240253
- **uint8_t getSubCallAddress(uint8_t nr)**
241254

255+
AllCall functions:
256+
242257
- **bool enableAllCall()**
243258
- **bool disableAllCall()**
244259
- **bool isEnabledAllCall()**
@@ -310,14 +325,16 @@ For further details of the development, see - #10 (PCA9634 repo)
310325
- improve error handling
311326
- return values etc.
312327
- documentation.
313-
- #defines ==> const int?
328+
- #defines ==> const uint16_t ? (not possible for all defines)
329+
314330

315331
#### Could
316332

317333
- default values for functions.
318334
- unit tests
319335
- investigate int vs uint16_t ?
320336
- **setFrequency(), getFrequency(), \_freq**
337+
- need for an **allON()** function?
321338

322339
#### Wont
323340

libraries/PCA9685_RT/examples/PCA9685_OE_control/PCA9685_OE_control.ino

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ PCA9685 ledArray(0x20);
1414
void setup()
1515
{
1616
Serial.begin(115200);
17+
Serial.println();
1718
Serial.println(__FILE__);
1819
Serial.print("PCA9685_LIB_VERSION: ");
1920
Serial.println(PCA9685_LIB_VERSION);

libraries/PCA9685_RT/examples/PCA9685_allOFF_test/PCA9685_allOFF_test.ino

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const uint8_t PIN = 2;
1919
void setup()
2020
{
2121
Serial.begin(115200);
22+
Serial.println();
2223
Serial.println(__FILE__);
2324
Serial.print("PCA9685_LIB_VERSION: ");
2425
Serial.println(PCA9685_LIB_VERSION);

0 commit comments

Comments
 (0)