├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── githubci.yml ├── .gitignore ├── Adafruit_PWMServoDriver.cpp ├── Adafruit_PWMServoDriver.h ├── README.md ├── examples ├── gpiotest │ └── gpiotest.ino ├── oscillator │ └── oscillator.ino ├── pwmtest │ └── pwmtest.ino └── servo │ └── servo.ino ├── keywords.txt ├── library.properties └── license.txt /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thank you for opening an issue on an Adafruit Arduino library repository. To 2 | improve the speed of resolution please review the following guidelines and 3 | common troubleshooting steps below before creating the issue: 4 | 5 | - **Do not use GitHub issues for troubleshooting projects and issues.** Instead use 6 | the forums at http://forums.adafruit.com to ask questions and troubleshoot why 7 | something isn't working as expected. In many cases the problem is a common issue 8 | that you will more quickly receive help from the forum community. GitHub issues 9 | are meant for known defects in the code. If you don't know if there is a defect 10 | in the code then start with troubleshooting on the forum first. 11 | 12 | - **If following a tutorial or guide be sure you didn't miss a step.** Carefully 13 | check all of the steps and commands to run have been followed. Consult the 14 | forum if you're unsure or have questions about steps in a guide/tutorial. 15 | 16 | - **For Arduino projects check these very common issues to ensure they don't apply**: 17 | 18 | - For uploading sketches or communicating with the board make sure you're using 19 | a **USB data cable** and **not** a **USB charge-only cable**. It is sometimes 20 | very hard to tell the difference between a data and charge cable! Try using the 21 | cable with other devices or swapping to another cable to confirm it is not 22 | the problem. 23 | 24 | - **Be sure you are supplying adequate power to the board.** Check the specs of 25 | your board and plug in an external power supply. In many cases just 26 | plugging a board into your computer is not enough to power it and other 27 | peripherals. 28 | 29 | - **Double check all soldering joints and connections.** Flakey connections 30 | cause many mysterious problems. See the [guide to excellent soldering](https://learn.adafruit.com/adafruit-guide-excellent-soldering/tools) for examples of good solder joints. 31 | 32 | - **Ensure you are using an official Arduino or Adafruit board.** We can't 33 | guarantee a clone board will have the same functionality and work as expected 34 | with this code and don't support them. 35 | 36 | If you're sure this issue is a defect in the code and checked the steps above 37 | please fill in the following fields to provide enough troubleshooting information. 38 | You may delete the guideline and text above to just leave the following details: 39 | 40 | - Arduino board: **INSERT ARDUINO BOARD NAME/TYPE HERE** 41 | 42 | - Arduino IDE version (found in Arduino -> About Arduino menu): **INSERT ARDUINO 43 | VERSION HERE** 44 | 45 | - List the steps to reproduce the problem below (if possible attach a sketch or 46 | copy the sketch code in too): **LIST REPRO STEPS BELOW** 47 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thank you for creating a pull request to contribute to Adafruit's GitHub code! 2 | Before you open the request please review the following guidelines and tips to 3 | help it be more easily integrated: 4 | 5 | - **Describe the scope of your change--i.e. what the change does and what parts 6 | of the code were modified.** This will help us understand any risks of integrating 7 | the code. 8 | 9 | - **Describe any known limitations with your change.** For example if the change 10 | doesn't apply to a supported platform of the library please mention it. 11 | 12 | - **Please run any tests or examples that can exercise your modified code.** We 13 | strive to not break users of the code and running tests/examples helps with this 14 | process. 15 | 16 | Thank you again for contributing! We will try to test and integrate the change 17 | as soon as we can, but be aware we have many GitHub repositories to manage and 18 | can't immediately respond to every request. There is no need to bump or check in 19 | on a pull request (it will clutter the discussion of the request). 20 | 21 | Also don't be worried if the request is closed or not integrated--sometimes the 22 | priorities of Adafruit's GitHub code (education, ease of use) might not match the 23 | priorities of the pull request. Don't fret, the open source community thrives on 24 | forks and GitHub makes it easy to keep your changes in a forked repo. 25 | 26 | After reviewing the guidelines above you can delete this text from the pull request. 27 | -------------------------------------------------------------------------------- /.github/workflows/githubci.yml: -------------------------------------------------------------------------------- 1 | name: Arduino Library CI 2 | 3 | on: [pull_request, push, repository_dispatch] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/setup-python@v4 11 | with: 12 | python-version: '3.x' 13 | - uses: actions/checkout@v3 14 | - uses: actions/checkout@v3 15 | with: 16 | repository: adafruit/ci-arduino 17 | path: ci 18 | 19 | - name: pre-install 20 | run: bash ci/actions_install.sh 21 | 22 | - name: test platforms 23 | run: python3 ci/build_platform.py main_platforms 24 | 25 | - name: clang 26 | run: python3 ci/run-clang-format.py -e "ci/*" -e "bin/*" -r . 27 | 28 | - name: doxygen 29 | env: 30 | GH_REPO_TOKEN: ${{ secrets.GH_REPO_TOKEN }} 31 | PRETTYNAME : "Adafruit PCA9685 PWM Library" 32 | run: bash ci/doxy_gen_and_deploy.sh 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # osx 2 | .DS_Store 3 | 4 | # doxygen 5 | Doxyfile* 6 | doxygen_sqlite3.db 7 | html 8 | *.tmp 9 | -------------------------------------------------------------------------------- /Adafruit_PWMServoDriver.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file Adafruit_PWMServoDriver.cpp 3 | * 4 | * @mainpage Adafruit 16-channel PWM & Servo driver 5 | * 6 | * @section intro_sec Introduction 7 | * 8 | * This is a library for the 16-channel PWM & Servo driver. 9 | * 10 | * Designed specifically to work with the Adafruit PWM & Servo driver. 11 | * 12 | * Pick one up today in the adafruit shop! 13 | * ------> https://www.adafruit.com/product/815 14 | * 15 | * These displays use I2C to communicate, 2 pins are required to interface. 16 | * 17 | * Adafruit invests time and resources providing this open source code, 18 | * please support Adafruit andopen-source hardware by purchasing products 19 | * from Adafruit! 20 | * 21 | * @section author Author 22 | * 23 | * Limor Fried/Ladyada (Adafruit Industries). 24 | * 25 | * @section license License 26 | * 27 | * BSD license, all text above must be included in any redistribution 28 | */ 29 | 30 | #include "Adafruit_PWMServoDriver.h" 31 | 32 | //#define ENABLE_DEBUG_OUTPUT 33 | 34 | /*! 35 | * @brief Instantiates a new PCA9685 PWM driver chip with the I2C address on a 36 | * TwoWire interface 37 | */ 38 | Adafruit_PWMServoDriver::Adafruit_PWMServoDriver() 39 | : _i2caddr(PCA9685_I2C_ADDRESS), _i2c(&Wire) {} 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 | /*! 50 | * @brief Instantiates a new PCA9685 PWM driver chip with the I2C address on a 51 | * TwoWire interface 52 | * @param addr The 7-bit I2C address to locate this chip, default is 0x40 53 | * @param i2c A reference to a 'TwoWire' object that we'll use to communicate 54 | * with 55 | */ 56 | Adafruit_PWMServoDriver::Adafruit_PWMServoDriver(const uint8_t addr, 57 | TwoWire &i2c) 58 | : _i2caddr(addr), _i2c(&i2c) {} 59 | 60 | /*! 61 | * @brief Setups the I2C interface and hardware 62 | * @param prescale 63 | * Sets External Clock (Optional) 64 | * @return true if successful, otherwise false 65 | */ 66 | bool Adafruit_PWMServoDriver::begin(uint8_t prescale) { 67 | if (i2c_dev) 68 | delete i2c_dev; 69 | i2c_dev = new Adafruit_I2CDevice(_i2caddr, _i2c); 70 | if (!i2c_dev->begin()) 71 | return false; 72 | reset(); 73 | 74 | // set the default internal frequency 75 | setOscillatorFrequency(FREQUENCY_OSCILLATOR); 76 | 77 | if (prescale) { 78 | setExtClk(prescale); 79 | } else { 80 | // set a default frequency 81 | setPWMFreq(1000); 82 | } 83 | 84 | return true; 85 | } 86 | 87 | /*! 88 | * @brief Sends a reset command to the PCA9685 chip over I2C 89 | */ 90 | void Adafruit_PWMServoDriver::reset() { 91 | write8(PCA9685_MODE1, MODE1_RESTART); 92 | delay(10); 93 | } 94 | 95 | /*! 96 | * @brief Puts board into sleep mode 97 | */ 98 | void Adafruit_PWMServoDriver::sleep() { 99 | uint8_t awake = read8(PCA9685_MODE1); 100 | uint8_t sleep = awake | MODE1_SLEEP; // set sleep bit high 101 | write8(PCA9685_MODE1, sleep); 102 | delay(5); // wait until cycle ends for sleep to be active 103 | } 104 | 105 | /*! 106 | * @brief Wakes board from sleep 107 | */ 108 | void Adafruit_PWMServoDriver::wakeup() { 109 | uint8_t sleep = read8(PCA9685_MODE1); 110 | uint8_t wakeup = sleep & ~MODE1_SLEEP; // set sleep bit low 111 | write8(PCA9685_MODE1, wakeup); 112 | } 113 | 114 | /*! 115 | * @brief Sets EXTCLK pin to use the external clock 116 | * @param prescale 117 | * Configures the prescale value to be used by the external clock 118 | */ 119 | void Adafruit_PWMServoDriver::setExtClk(uint8_t prescale) { 120 | uint8_t oldmode = read8(PCA9685_MODE1); 121 | uint8_t newmode = (oldmode & ~MODE1_RESTART) | MODE1_SLEEP; // sleep 122 | write8(PCA9685_MODE1, newmode); // go to sleep, turn off internal oscillator 123 | 124 | // This sets both the SLEEP and EXTCLK bits of the MODE1 register to switch to 125 | // use the external clock. 126 | write8(PCA9685_MODE1, (newmode |= MODE1_EXTCLK)); 127 | 128 | write8(PCA9685_PRESCALE, prescale); // set the prescaler 129 | 130 | delay(5); 131 | // clear the SLEEP bit to start 132 | write8(PCA9685_MODE1, (newmode & ~MODE1_SLEEP) | MODE1_RESTART | MODE1_AI); 133 | 134 | #ifdef ENABLE_DEBUG_OUTPUT 135 | Serial.print("Mode now 0x"); 136 | Serial.println(read8(PCA9685_MODE1), HEX); 137 | #endif 138 | } 139 | 140 | /*! 141 | * @brief Sets the PWM frequency for the entire chip, up to ~1.6 KHz 142 | * @param freq Floating point frequency that we will attempt to match 143 | */ 144 | void Adafruit_PWMServoDriver::setPWMFreq(float freq) { 145 | #ifdef ENABLE_DEBUG_OUTPUT 146 | Serial.print("Attempting to set freq "); 147 | Serial.println(freq); 148 | #endif 149 | // Range output modulation frequency is dependant on oscillator 150 | if (freq < 1) 151 | freq = 1; 152 | if (freq > 3500) 153 | freq = 3500; // Datasheet limit is 3052=50MHz/(4*4096) 154 | 155 | float prescaleval = ((_oscillator_freq / (freq * 4096.0)) + 0.5) - 1; 156 | if (prescaleval < PCA9685_PRESCALE_MIN) 157 | prescaleval = PCA9685_PRESCALE_MIN; 158 | if (prescaleval > PCA9685_PRESCALE_MAX) 159 | prescaleval = PCA9685_PRESCALE_MAX; 160 | uint8_t prescale = (uint8_t)prescaleval; 161 | 162 | #ifdef ENABLE_DEBUG_OUTPUT 163 | Serial.print("Final pre-scale: "); 164 | Serial.println(prescale); 165 | #endif 166 | 167 | uint8_t oldmode = read8(PCA9685_MODE1); 168 | uint8_t newmode = (oldmode & ~MODE1_RESTART) | MODE1_SLEEP; // sleep 169 | write8(PCA9685_MODE1, newmode); // go to sleep 170 | write8(PCA9685_PRESCALE, prescale); // set the prescaler 171 | write8(PCA9685_MODE1, oldmode); 172 | delay(5); 173 | // This sets the MODE1 register to turn on auto increment. 174 | write8(PCA9685_MODE1, oldmode | MODE1_RESTART | MODE1_AI); 175 | 176 | #ifdef ENABLE_DEBUG_OUTPUT 177 | Serial.print("Mode now 0x"); 178 | Serial.println(read8(PCA9685_MODE1), HEX); 179 | #endif 180 | } 181 | 182 | /*! 183 | * @brief Sets the output mode of the PCA9685 to either 184 | * open drain or push pull / totempole. 185 | * Warning: LEDs with integrated zener diodes should 186 | * only be driven in open drain mode. 187 | * @param totempole Totempole if true, open drain if false. 188 | */ 189 | void Adafruit_PWMServoDriver::setOutputMode(bool totempole) { 190 | uint8_t oldmode = read8(PCA9685_MODE2); 191 | uint8_t newmode; 192 | if (totempole) { 193 | newmode = oldmode | MODE2_OUTDRV; 194 | } else { 195 | newmode = oldmode & ~MODE2_OUTDRV; 196 | } 197 | write8(PCA9685_MODE2, newmode); 198 | #ifdef ENABLE_DEBUG_OUTPUT 199 | Serial.print("Setting output mode: "); 200 | Serial.print(totempole ? "totempole" : "open drain"); 201 | Serial.print(" by setting MODE2 to "); 202 | Serial.println(newmode); 203 | #endif 204 | } 205 | 206 | /*! 207 | * @brief Reads set Prescale from PCA9685 208 | * @return prescale value 209 | */ 210 | uint8_t Adafruit_PWMServoDriver::readPrescale(void) { 211 | return read8(PCA9685_PRESCALE); 212 | } 213 | 214 | /*! 215 | * @brief Gets the PWM output of one of the PCA9685 pins 216 | * @param num One of the PWM output pins, from 0 to 15 217 | * @param off If true, returns PWM OFF value, otherwise PWM ON 218 | * @return requested PWM output value 219 | */ 220 | uint16_t Adafruit_PWMServoDriver::getPWM(uint8_t num, bool off) { 221 | uint8_t buffer[2] = {uint8_t(PCA9685_LED0_ON_L + 4 * num), 0}; 222 | if (off) 223 | buffer[0] += 2; 224 | i2c_dev->write_then_read(buffer, 1, buffer, 2); 225 | return uint16_t(buffer[0]) | (uint16_t(buffer[1]) << 8); 226 | } 227 | 228 | /*! 229 | * @brief Sets the PWM output of one of the PCA9685 pins 230 | * @param num One of the PWM output pins, from 0 to 15 231 | * @param on At what point in the 4096-part cycle to turn the PWM output ON 232 | * @param off At what point in the 4096-part cycle to turn the PWM output OFF 233 | * @return 0 if successful, otherwise 1 234 | */ 235 | uint8_t Adafruit_PWMServoDriver::setPWM(uint8_t num, uint16_t on, 236 | uint16_t off) { 237 | #ifdef ENABLE_DEBUG_OUTPUT 238 | Serial.print("Setting PWM "); 239 | Serial.print(num); 240 | Serial.print(": "); 241 | Serial.print(on); 242 | Serial.print("->"); 243 | Serial.println(off); 244 | #endif 245 | 246 | uint8_t buffer[5]; 247 | buffer[0] = PCA9685_LED0_ON_L + 4 * num; 248 | buffer[1] = on; 249 | buffer[2] = on >> 8; 250 | buffer[3] = off; 251 | buffer[4] = off >> 8; 252 | 253 | if (i2c_dev->write(buffer, 5)) { 254 | return 0; 255 | } else { 256 | return 1; 257 | } 258 | } 259 | 260 | /*! 261 | * @brief Helper to set pin PWM output. Sets pin without having to deal with 262 | * on/off tick placement and properly handles a zero value as completely off and 263 | * 4095 as completely on. Optional invert parameter supports inverting the 264 | * pulse for sinking to ground. 265 | * @param num One of the PWM output pins, from 0 to 15 266 | * @param val The number of ticks out of 4096 to be active, should be a value 267 | * from 0 to 4095 inclusive. 268 | * @param invert If true, inverts the output, defaults to 'false' 269 | */ 270 | void Adafruit_PWMServoDriver::setPin(uint8_t num, uint16_t val, bool invert) { 271 | // Clamp value between 0 and 4095 inclusive. 272 | val = min(val, (uint16_t)4095); 273 | if (invert) { 274 | if (val == 0) { 275 | // Special value for signal fully on. 276 | setPWM(num, 4096, 0); 277 | } else if (val == 4095) { 278 | // Special value for signal fully off. 279 | setPWM(num, 0, 4096); 280 | } else { 281 | setPWM(num, 0, 4095 - val); 282 | } 283 | } else { 284 | if (val == 4095) { 285 | // Special value for signal fully on. 286 | setPWM(num, 4096, 0); 287 | } else if (val == 0) { 288 | // Special value for signal fully off. 289 | setPWM(num, 0, 4096); 290 | } else { 291 | setPWM(num, 0, val); 292 | } 293 | } 294 | } 295 | 296 | /*! 297 | * @brief Sets the PWM output of one of the PCA9685 pins based on the input 298 | * microseconds, output is not precise 299 | * @param num One of the PWM output pins, from 0 to 15 300 | * @param Microseconds The number of Microseconds to turn the PWM output ON 301 | */ 302 | void Adafruit_PWMServoDriver::writeMicroseconds(uint8_t num, 303 | uint16_t Microseconds) { 304 | #ifdef ENABLE_DEBUG_OUTPUT 305 | Serial.print("Setting PWM Via Microseconds on output"); 306 | Serial.print(num); 307 | Serial.print(": "); 308 | Serial.print(Microseconds); 309 | Serial.println("->"); 310 | #endif 311 | 312 | double pulse = Microseconds; 313 | double pulselength; 314 | pulselength = 1000000; // 1,000,000 us per second 315 | 316 | // Read prescale 317 | uint16_t prescale = readPrescale(); 318 | 319 | #ifdef ENABLE_DEBUG_OUTPUT 320 | Serial.print(prescale); 321 | Serial.println(" PCA9685 chip prescale"); 322 | #endif 323 | 324 | // Calculate the pulse for PWM based on Equation 1 from the datasheet section 325 | // 7.3.5 326 | prescale += 1; 327 | pulselength *= prescale; 328 | pulselength /= _oscillator_freq; 329 | 330 | #ifdef ENABLE_DEBUG_OUTPUT 331 | Serial.print(pulselength); 332 | Serial.println(" us per bit"); 333 | #endif 334 | 335 | pulse /= pulselength; 336 | 337 | #ifdef ENABLE_DEBUG_OUTPUT 338 | Serial.print(pulse); 339 | Serial.println(" pulse for PWM"); 340 | #endif 341 | 342 | setPWM(num, 0, pulse); 343 | } 344 | 345 | /*! 346 | * @brief Getter for the internally tracked oscillator used for freq 347 | * calculations 348 | * @returns The frequency the PCA9685 thinks it is running at (it cannot 349 | * introspect) 350 | */ 351 | uint32_t Adafruit_PWMServoDriver::getOscillatorFrequency(void) { 352 | return _oscillator_freq; 353 | } 354 | 355 | /*! 356 | * @brief Setter for the internally tracked oscillator used for freq 357 | * calculations 358 | * @param freq The frequency the PCA9685 should use for frequency calculations 359 | */ 360 | void Adafruit_PWMServoDriver::setOscillatorFrequency(uint32_t freq) { 361 | _oscillator_freq = freq; 362 | } 363 | 364 | /******************* Low level I2C interface */ 365 | uint8_t Adafruit_PWMServoDriver::read8(uint8_t addr) { 366 | uint8_t buffer[1] = {addr}; 367 | i2c_dev->write_then_read(buffer, 1, buffer, 1); 368 | return buffer[0]; 369 | } 370 | 371 | void Adafruit_PWMServoDriver::write8(uint8_t addr, uint8_t d) { 372 | uint8_t buffer[2] = {addr, d}; 373 | i2c_dev->write(buffer, 2); 374 | } 375 | -------------------------------------------------------------------------------- /Adafruit_PWMServoDriver.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file Adafruit_PWMServoDriver.h 3 | * 4 | * This is a library for our Adafruit 16-channel PWM & Servo driver. 5 | * 6 | * Designed specifically to work with the Adafruit 16-channel PWM & Servo 7 | * driver. 8 | * 9 | * Pick one up today in the adafruit shop! 10 | * ------> https://www.adafruit.com/product/815 11 | * 12 | * These driver use I2C to communicate, 2 pins are required to interface. 13 | * For Arduino UNOs, thats SCL -> Analog 5, SDA -> Analog 4. 14 | * 15 | * Adafruit invests time and resources providing this open source code, 16 | * please support Adafruit andopen-source hardware by purchasing products 17 | * from Adafruit! 18 | * 19 | * Limor Fried/Ladyada (Adafruit Industries). 20 | * 21 | * BSD license, all text above must be included in any redistribution 22 | */ 23 | #ifndef _ADAFRUIT_PWMServoDriver_H 24 | #define _ADAFRUIT_PWMServoDriver_H 25 | 26 | #include 27 | #include 28 | 29 | // REGISTER ADDRESSES 30 | #define PCA9685_MODE1 0x00 /**< Mode Register 1 */ 31 | #define PCA9685_MODE2 0x01 /**< Mode Register 2 */ 32 | #define PCA9685_SUBADR1 0x02 /**< I2C-bus subaddress 1 */ 33 | #define PCA9685_SUBADR2 0x03 /**< I2C-bus subaddress 2 */ 34 | #define PCA9685_SUBADR3 0x04 /**< I2C-bus subaddress 3 */ 35 | #define PCA9685_ALLCALLADR 0x05 /**< LED All Call I2C-bus address */ 36 | #define PCA9685_LED0_ON_L 0x06 /**< LED0 on tick, low byte*/ 37 | #define PCA9685_LED0_ON_H 0x07 /**< LED0 on tick, high byte*/ 38 | #define PCA9685_LED0_OFF_L 0x08 /**< LED0 off tick, low byte */ 39 | #define PCA9685_LED0_OFF_H 0x09 /**< LED0 off tick, high byte */ 40 | // etc all 16: LED15_OFF_H 0x45 41 | #define PCA9685_ALLLED_ON_L 0xFA /**< load all the LEDn_ON registers, low */ 42 | #define PCA9685_ALLLED_ON_H 0xFB /**< load all the LEDn_ON registers, high */ 43 | #define PCA9685_ALLLED_OFF_L 0xFC /**< load all the LEDn_OFF registers, low */ 44 | #define PCA9685_ALLLED_OFF_H 0xFD /**< load all the LEDn_OFF registers,high */ 45 | #define PCA9685_PRESCALE 0xFE /**< Prescaler for PWM output frequency */ 46 | #define PCA9685_TESTMODE 0xFF /**< defines the test mode to be entered */ 47 | 48 | // MODE1 bits 49 | #define MODE1_ALLCAL 0x01 /**< respond to LED All Call I2C-bus address */ 50 | #define MODE1_SUB3 0x02 /**< respond to I2C-bus subaddress 3 */ 51 | #define MODE1_SUB2 0x04 /**< respond to I2C-bus subaddress 2 */ 52 | #define MODE1_SUB1 0x08 /**< respond to I2C-bus subaddress 1 */ 53 | #define MODE1_SLEEP 0x10 /**< Low power mode. Oscillator off */ 54 | #define MODE1_AI 0x20 /**< Auto-Increment enabled */ 55 | #define MODE1_EXTCLK 0x40 /**< Use EXTCLK pin clock */ 56 | #define MODE1_RESTART 0x80 /**< Restart enabled */ 57 | // MODE2 bits 58 | #define MODE2_OUTNE_0 0x01 /**< Active LOW output enable input */ 59 | #define MODE2_OUTNE_1 \ 60 | 0x02 /**< Active LOW output enable input - high impedience */ 61 | #define MODE2_OUTDRV 0x04 /**< totem pole structure vs open-drain */ 62 | #define MODE2_OCH 0x08 /**< Outputs change on ACK vs STOP */ 63 | #define MODE2_INVRT 0x10 /**< Output logic state inverted */ 64 | 65 | #define PCA9685_I2C_ADDRESS 0x40 /**< Default PCA9685 I2C Slave Address */ 66 | #define FREQUENCY_OSCILLATOR 25000000 /**< Int. osc. frequency in datasheet */ 67 | 68 | #define PCA9685_PRESCALE_MIN 3 /**< minimum prescale value */ 69 | #define PCA9685_PRESCALE_MAX 255 /**< maximum prescale value */ 70 | 71 | /*! 72 | * @brief Class that stores state and functions for interacting with PCA9685 73 | * PWM chip 74 | */ 75 | class Adafruit_PWMServoDriver { 76 | public: 77 | Adafruit_PWMServoDriver(); 78 | Adafruit_PWMServoDriver(const uint8_t addr); 79 | Adafruit_PWMServoDriver(const uint8_t addr, TwoWire &i2c); 80 | bool begin(uint8_t prescale = 0); 81 | void reset(); 82 | void sleep(); 83 | void wakeup(); 84 | void setExtClk(uint8_t prescale); 85 | void setPWMFreq(float freq); 86 | void setOutputMode(bool totempole); 87 | uint16_t getPWM(uint8_t num, bool off = false); 88 | uint8_t setPWM(uint8_t num, uint16_t on, uint16_t off); 89 | void setPin(uint8_t num, uint16_t val, bool invert = false); 90 | uint8_t readPrescale(void); 91 | void writeMicroseconds(uint8_t num, uint16_t Microseconds); 92 | 93 | void setOscillatorFrequency(uint32_t freq); 94 | uint32_t getOscillatorFrequency(void); 95 | 96 | private: 97 | uint8_t _i2caddr; 98 | TwoWire *_i2c; 99 | Adafruit_I2CDevice *i2c_dev = NULL; ///< Pointer to I2C bus interface 100 | 101 | uint32_t _oscillator_freq; 102 | uint8_t read8(uint8_t addr); 103 | void write8(uint8_t addr, uint8_t d); 104 | }; 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Adafruit PCA9685 PWM Servo Driver Library ![Build Status](https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library/workflows/Arduino%20Library%20CI/badge.svg) 2 | 3 | 4 | This is a library for our Adafruit 16-channel PWM & Servo driver, shield or FeatherWing 5 | 6 | 7 | 8 | Pick one up today in the adafruit shop! 9 | * https://www.adafruit.com/products/815 10 | * https://www.adafruit.com/product/1411 11 | * https://www.adafruit.com/product/2928 12 | 13 | These drivers use I2C to communicate, 2 pins are required to interface. 14 | 15 | Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! 16 | 17 | Written by Limor Fried/Ladyada for Adafruit Industries. BSD license, check license.txt for more information. 18 | 19 | All text above must be included in any redistribution 20 | -------------------------------------------------------------------------------- /examples/gpiotest/gpiotest.ino: -------------------------------------------------------------------------------- 1 | /*************************************************** 2 | This is an example for our Adafruit 16-channel PWM & Servo driver 3 | GPIO test - this will set a pin high/low 4 | 5 | Pick one up today in the adafruit shop! 6 | ------> http://www.adafruit.com/products/815 7 | 8 | These drivers use I2C to communicate, 2 pins are required to 9 | interface. 10 | 11 | Adafruit invests time and resources providing this open source code, 12 | please support Adafruit and open-source hardware by purchasing 13 | products from Adafruit! 14 | 15 | Written by Limor Fried/Ladyada for Adafruit Industries. 16 | BSD license, all text above must be included in any redistribution 17 | ****************************************************/ 18 | 19 | #include 20 | #include 21 | 22 | // called this way, it uses the default address 0x40 23 | Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(); 24 | // you can also call it with a different address you want 25 | //Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41); 26 | // you can also call it with a different address and I2C interface 27 | //Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40, Wire); 28 | 29 | void setup() { 30 | Serial.begin(9600); 31 | Serial.println("GPIO test!"); 32 | 33 | pwm.begin(); 34 | pwm.setPWMFreq(1000); // Set to whatever you like, we don't use it in this demo! 35 | 36 | // if you want to really speed stuff up, you can go into 'fast 400khz I2C' mode 37 | // some i2c devices dont like this so much so if you're sharing the bus, watch 38 | // out for this! 39 | Wire.setClock(400000); 40 | } 41 | 42 | void loop() { 43 | // Drive each pin in a 'wave' 44 | for (uint8_t pin=0; pin<16; pin++) { 45 | pwm.setPWM(pin, 4096, 0); // turns pin fully on 46 | delay(100); 47 | pwm.setPWM(pin, 0, 4096); // turns pin fully off 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /examples/oscillator/oscillator.ino: -------------------------------------------------------------------------------- 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 24 | #include 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 = interruptCounter + 1; 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 = 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: %4" PRIu32 " ", totalTime); 112 | Serial.printf(" Freq: %4u ", frozenCounter); 113 | Serial.printf(" Counter: %6" PRIu32 " ", totalCounter); 114 | Serial.printf(" calc.osci.freq: %9" PRIu32 "\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 130 | -------------------------------------------------------------------------------- /examples/pwmtest/pwmtest.ino: -------------------------------------------------------------------------------- 1 | /*************************************************** 2 | This is an example for our Adafruit 16-channel PWM & Servo driver 3 | PWM test - this will drive 16 PWMs in a 'wave' 4 | 5 | Pick one up today in the adafruit shop! 6 | ------> http://www.adafruit.com/products/815 7 | 8 | These drivers use I2C to communicate, 2 pins are required to 9 | interface. 10 | 11 | Adafruit invests time and resources providing this open source code, 12 | please support Adafruit and open-source hardware by purchasing 13 | products from Adafruit! 14 | 15 | Written by Limor Fried/Ladyada for Adafruit Industries. 16 | BSD license, all text above must be included in any redistribution 17 | ****************************************************/ 18 | 19 | #include 20 | #include 21 | 22 | // called this way, it uses the default address 0x40 23 | Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(); 24 | // you can also call it with a different address you want 25 | //Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41); 26 | // you can also call it with a different address and I2C interface 27 | //Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40, Wire); 28 | 29 | void setup() { 30 | Serial.begin(9600); 31 | Serial.println("16 channel PWM test!"); 32 | 33 | pwm.begin(); 34 | /* 35 | * In theory the internal oscillator (clock) is 25MHz but it really isn't 36 | * that precise. You can 'calibrate' this by tweaking this number until 37 | * you get the PWM update frequency you're expecting! 38 | * The int.osc. for the PCA9685 chip is a range between about 23-27MHz and 39 | * is used for calculating things like writeMicroseconds() 40 | * Analog servos run at ~50 Hz updates, It is importaint to use an 41 | * oscilloscope in setting the int.osc frequency for the I2C PCA9685 chip. 42 | * 1) Attach the oscilloscope to one of the PWM signal pins and ground on 43 | * the I2C PCA9685 chip you are setting the value for. 44 | * 2) Adjust setOscillatorFrequency() until the PWM update frequency is the 45 | * expected value (50Hz for most ESCs) 46 | * Setting the value here is specific to each individual I2C PCA9685 chip and 47 | * affects the calculations for the PWM update frequency. 48 | * Failure to correctly set the int.osc value will cause unexpected PWM results 49 | */ 50 | pwm.setOscillatorFrequency(27000000); 51 | pwm.setPWMFreq(1600); // This is the maximum PWM frequency 52 | 53 | // if you want to really speed stuff up, you can go into 'fast 400khz I2C' mode 54 | // some i2c devices dont like this so much so if you're sharing the bus, watch 55 | // out for this! 56 | Wire.setClock(400000); 57 | } 58 | 59 | void loop() { 60 | // Drive each PWM in a 'wave' 61 | for (uint16_t i=0; i<4096; i += 8) { 62 | for (uint8_t pwmnum=0; pwmnum < 16; pwmnum++) { 63 | pwm.setPWM(pwmnum, 0, (i + (4096/16)*pwmnum) % 4096 ); 64 | } 65 | #ifdef ESP8266 66 | yield(); // take a breather, required for ESP8266 67 | #endif 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /examples/servo/servo.ino: -------------------------------------------------------------------------------- 1 | /*************************************************** 2 | This is an example for our Adafruit 16-channel PWM & Servo driver 3 | Servo test - this will drive 8 servos, one after the other on the 4 | first 8 pins of the PCA9685 5 | 6 | Pick one up today in the adafruit shop! 7 | ------> http://www.adafruit.com/products/815 8 | 9 | These drivers use I2C to communicate, 2 pins are required to 10 | interface. 11 | 12 | Adafruit invests time and resources providing this open source code, 13 | please support Adafruit and open-source hardware by purchasing 14 | products from Adafruit! 15 | 16 | Written by Limor Fried/Ladyada for Adafruit Industries. 17 | BSD license, all text above must be included in any redistribution 18 | ****************************************************/ 19 | 20 | #include 21 | #include 22 | 23 | // called this way, it uses the default address 0x40 24 | Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(); 25 | // you can also call it with a different address you want 26 | //Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41); 27 | // you can also call it with a different address and I2C interface 28 | //Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40, Wire); 29 | 30 | // Depending on your servo make, the pulse width min and max may vary, you 31 | // want these to be as small/large as possible without hitting the hard stop 32 | // for max range. You'll have to tweak them as necessary to match the servos you 33 | // have! 34 | #define SERVOMIN 150 // This is the 'minimum' pulse length count (out of 4096) 35 | #define SERVOMAX 600 // This is the 'maximum' pulse length count (out of 4096) 36 | #define USMIN 600 // This is the rounded 'minimum' microsecond length based on the minimum pulse of 150 37 | #define USMAX 2400 // This is the rounded 'maximum' microsecond length based on the maximum pulse of 600 38 | #define SERVO_FREQ 50 // Analog servos run at ~50 Hz updates 39 | 40 | // our servo # counter 41 | uint8_t servonum = 0; 42 | 43 | void setup() { 44 | Serial.begin(9600); 45 | Serial.println("8 channel Servo test!"); 46 | 47 | pwm.begin(); 48 | /* 49 | * In theory the internal oscillator (clock) is 25MHz but it really isn't 50 | * that precise. You can 'calibrate' this by tweaking this number until 51 | * you get the PWM update frequency you're expecting! 52 | * The int.osc. for the PCA9685 chip is a range between about 23-27MHz and 53 | * is used for calculating things like writeMicroseconds() 54 | * Analog servos run at ~50 Hz updates, It is importaint to use an 55 | * oscilloscope in setting the int.osc frequency for the I2C PCA9685 chip. 56 | * 1) Attach the oscilloscope to one of the PWM signal pins and ground on 57 | * the I2C PCA9685 chip you are setting the value for. 58 | * 2) Adjust setOscillatorFrequency() until the PWM update frequency is the 59 | * expected value (50Hz for most ESCs) 60 | * Setting the value here is specific to each individual I2C PCA9685 chip and 61 | * affects the calculations for the PWM update frequency. 62 | * Failure to correctly set the int.osc value will cause unexpected PWM results 63 | */ 64 | pwm.setOscillatorFrequency(27000000); 65 | pwm.setPWMFreq(SERVO_FREQ); // Analog servos run at ~50 Hz updates 66 | 67 | delay(10); 68 | } 69 | 70 | // You can use this function if you'd like to set the pulse length in seconds 71 | // e.g. setServoPulse(0, 0.001) is a ~1 millisecond pulse width. It's not precise! 72 | void setServoPulse(uint8_t n, double pulse) { 73 | double pulselength; 74 | 75 | pulselength = 1000000; // 1,000,000 us per second 76 | pulselength /= SERVO_FREQ; // Analog servos run at ~60 Hz updates 77 | Serial.print(pulselength); Serial.println(" us per period"); 78 | pulselength /= 4096; // 12 bits of resolution 79 | Serial.print(pulselength); Serial.println(" us per bit"); 80 | pulse *= 1000000; // convert input seconds to us 81 | pulse /= pulselength; 82 | Serial.println(pulse); 83 | pwm.setPWM(n, 0, pulse); 84 | } 85 | 86 | void loop() { 87 | // Drive each servo one at a time using setPWM() 88 | Serial.println(servonum); 89 | for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++) { 90 | pwm.setPWM(servonum, 0, pulselen); 91 | } 92 | 93 | delay(500); 94 | for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--) { 95 | pwm.setPWM(servonum, 0, pulselen); 96 | } 97 | 98 | delay(500); 99 | 100 | // Drive each servo one at a time using writeMicroseconds(), it's not precise due to calculation rounding! 101 | // The writeMicroseconds() function is used to mimic the Arduino Servo library writeMicroseconds() behavior. 102 | for (uint16_t microsec = USMIN; microsec < USMAX; microsec++) { 103 | pwm.writeMicroseconds(servonum, microsec); 104 | } 105 | 106 | delay(500); 107 | for (uint16_t microsec = USMAX; microsec > USMIN; microsec--) { 108 | pwm.writeMicroseconds(servonum, microsec); 109 | } 110 | 111 | delay(500); 112 | 113 | servonum++; 114 | if (servonum > 7) servonum = 0; // Testing the first 8 servo channels 115 | } 116 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Classes, datatypes (KEYWORD1) 3 | ####################################### 4 | 5 | Adafruit_PWMServoDriver KEYWORD1 6 | 7 | ####################################### 8 | # Methods and Functions (KEYWORD2) 9 | ####################################### 10 | 11 | begin KEYWORD2 12 | reset KEYWORD2 13 | sleep KEYWORD2 14 | wakeup KEYWORD2 15 | setExtClk KEYWORD2 16 | setPWMFreq KEYWORD2 17 | setOutputMode KEYWORD2 18 | getPWM KEYWORD2 19 | setPWM KEYWORD2 20 | setPin KEYWORD2 21 | readPrescale KEYWORD2 22 | writeMicroseconds KEYWORD2 23 | setOscillatorFrequency KEYWORD2 24 | getOscillatorFrequency KEYWORD2 25 | 26 | ####################################### 27 | # Constants (LITERAL1) 28 | ####################################### 29 | FREQUENCY_OSCILLATOR LITERAL1 30 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Adafruit PWM Servo Driver Library 2 | version=3.0.2 3 | author=Adafruit 4 | maintainer=Adafruit 5 | sentence=Adafruit PWM Servo Driver Library 6 | paragraph=Adafruit PWM Servo Driver Library 7 | category=Device Control 8 | url=https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library 9 | architectures=* 10 | depends=Adafruit BusIO 11 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Software License Agreement (BSD License) 2 | 3 | Copyright (c) 2012, Adafruit Industries 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of the copyright holders nor the 14 | names of its contributors may be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY 18 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY 21 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | --------------------------------------------------------------------------------