PX4 Firmware
PX4 Autopilot Software http://px4.io
pca9685.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2012-2017 PX4 Development Team. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * 3. Neither the name PX4 nor the names of its contributors may be
16  * used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  ****************************************************************************/
33 
34 /**
35  * @file pca9685.cpp
36  *
37  * Driver for the PCA9685 I2C PWM module
38  * The chip is used on the Adafruit I2C/PWM converter https://www.adafruit.com/product/815
39  *
40  * Parts of the code are adapted from the arduino library for the board
41  * https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library
42  * for the license of these parts see the
43  * arduino_Adafruit_PWM_Servo_Driver_Library_license.txt file
44  * see https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library for contributors
45  *
46  * @author Thomas Gubler <thomasgubler@gmail.com>
47  */
48 
49 #include <px4_platform_common/px4_config.h>
50 #include <px4_platform_common/defines.h>
51 
52 #include <drivers/device/i2c.h>
53 
54 #include <sys/types.h>
55 #include <stdint.h>
56 #include <string.h>
57 #include <stdlib.h>
58 #include <stdbool.h>
59 #include <fcntl.h>
60 #include <unistd.h>
61 #include <stdio.h>
62 #include <ctype.h>
63 #include <math.h>
64 #include <px4_platform_common/getopt.h>
65 
66 #include <px4_platform_common/px4_work_queue/ScheduledWorkItem.hpp>
67 #include <nuttx/clock.h>
68 
69 #include <perf/perf_counter.h>
70 #include <systemlib/err.h>
71 
72 #include <uORB/uORB.h>
74 
75 #include <board_config.h>
77 
78 #define PCA9685_SUBADR1 0x2
79 #define PCA9685_SUBADR2 0x3
80 #define PCA9685_SUBADR3 0x4
81 
82 #define PCA9685_MODE1 0x0
83 #define PCA9685_PRESCALE 0xFE
84 
85 #define LED0_ON_L 0x6
86 #define LED0_ON_H 0x7
87 #define LED0_OFF_L 0x8
88 #define LED0_OFF_H 0x9
89 
90 #define ALLLED_ON_L 0xFA
91 #define ALLLED_ON_H 0xFB
92 #define ALLLED_OFF_L 0xFC
93 #define ALLLED_OF
94 
95 #define ADDR 0x40 // I2C adress
96 
97 #define PCA9685_DEVICE_PATH "/dev/pca9685"
98 #define PCA9685_BUS PX4_I2C_BUS_EXPANSION
99 #define PCA9685_PWMFREQ 60.0f
100 #define PCA9685_NCHANS 16 // total amount of pwm outputs
101 
102 #define PCA9685_PWMMIN 150 // this is the 'minimum' pulse length count (out of 4096)
103 #define PCA9685_PWMMAX 600 // this is the 'maximum' pulse length count (out of 4096)_PWMFREQ 60.0f
104 
105 #define PCA9685_PWMCENTER ((PCA9685_PWMMAX + PCA9685_PWMMIN)/2)
106 #define PCA9685_MAXSERVODEG 90.0f /* maximal servo deflection in degrees
107  PCA9685_PWMMIN <--> -PCA9685_MAXSERVODEG
108  PCA9685_PWMMAX <--> PCA9685_MAXSERVODEG
109  */
110 #define PCA9685_SCALE ((PCA9685_PWMMAX - PCA9685_PWMCENTER)/(M_DEG_TO_RAD_F * PCA9685_MAXSERVODEG)) // scales from rad to PWM
111 
112 using namespace time_literals;
113 
114 class PCA9685 : public device::I2C, public px4::ScheduledWorkItem
115 {
116 public:
117  PCA9685(int bus = PCA9685_BUS, uint8_t address = ADDR);
118  virtual ~PCA9685() = default;
119 
120 
121  virtual int init();
122  virtual int ioctl(struct file *filp, int cmd, unsigned long arg);
123  virtual int info();
124  virtual int reset();
125  bool is_running() { return _running; }
126 
127 private:
128 
130  bool _running;
134 
135  uint8_t _msg[6];
136 
138  struct actuator_controls_s _actuator_controls;
139  uint16_t _current_values[actuator_controls_s::NUM_ACTUATOR_CONTROLS]; /**< stores the current pwm output
140  values as sent to the setPin() */
141 
142  bool _mode_on_initialized; /** Set to true after the first call of i2cpwm in mode IOX_MODE_ON */
143 
144  void Run() override;
145 
146  /**
147  * Helper function to set the pwm frequency
148  */
149  int setPWMFreq(float freq);
150 
151  /**
152  * Helper function to set the demanded pwm value
153  * @param num pwm output number
154  */
155  int setPWM(uint8_t num, uint16_t on, uint16_t off);
156 
157  /**
158  * Sets pin without having to deal with on/off tick placement and properly handles
159  * a zero value as completely off. Optional invert parameter supports inverting
160  * the pulse for sinking to ground.
161  * @param num pwm output number
162  * @param val should be a value from 0 to 4095 inclusive.
163  */
164  int setPin(uint8_t num, uint16_t val, bool invert = false);
165 
166 
167  /* Wrapper to read a byte from addr */
168  int read8(uint8_t addr, uint8_t &value);
169 
170  /* Wrapper to wite a byte to addr */
171  int write8(uint8_t addr, uint8_t value);
172 
173 };
174 
175 /* for now, we only support one board */
176 namespace
177 {
178 PCA9685 *g_pca9685;
179 }
180 
181 void pca9685_usage();
182 
183 extern "C" __EXPORT int pca9685_main(int argc, char *argv[]);
184 
185 PCA9685::PCA9685(int bus, uint8_t address) :
186  I2C("pca9685", PCA9685_DEVICE_PATH, bus, address, 100000),
187  ScheduledWorkItem(MODULE_NAME, px4::device_bus_to_wq(get_device_id())),
189  _running(false),
190  _i2cpwm_interval(1_s / 60.0f),
191  _should_run(false),
192  _comms_errors(perf_alloc(PC_COUNT, "pca9685_com_err")),
193  _actuator_controls_sub(-1),
194  _actuator_controls(),
195  _mode_on_initialized(false)
196 {
197  memset(_msg, 0, sizeof(_msg));
198  memset(_current_values, 0, sizeof(_current_values));
199 }
200 
201 int
203 {
204  int ret;
205  ret = I2C::init();
206 
207  if (ret != OK) {
208  return ret;
209  }
210 
211  ret = reset();
212 
213  if (ret != OK) {
214  return ret;
215  }
216 
218 
219  return ret;
220 }
221 
222 int
223 PCA9685::ioctl(struct file *filp, int cmd, unsigned long arg)
224 {
225  int ret = -EINVAL;
226 
227  switch (cmd) {
228 
229  case IOX_SET_MODE:
230 
231  if (_mode != (IOX_MODE)arg) {
232 
233  switch ((IOX_MODE)arg) {
234  case IOX_MODE_OFF:
235  warnx("shutting down");
236  break;
237 
238  case IOX_MODE_ON:
239  warnx("starting");
240  break;
241 
242  case IOX_MODE_TEST_OUT:
243  warnx("test starting");
244  break;
245 
246  default:
247  return -1;
248  }
249 
250  _mode = (IOX_MODE)arg;
251  }
252 
253  // if not active, kick it
254  if (!_running) {
255  _running = true;
256  ScheduleNow();
257  }
258 
259 
260  return OK;
261 
262  default:
263  // see if the parent class can make any use of it
264  ret = CDev::ioctl(filp, cmd, arg);
265  break;
266  }
267 
268  return ret;
269 }
270 
271 int
273 {
274  int ret = OK;
275 
276  if (is_running()) {
277  warnx("Driver is running, mode: %u", _mode);
278 
279  } else {
280  warnx("Driver started but not running");
281  }
282 
283  return ret;
284 }
285 
286 /**
287  * Main loop function
288  */
289 void
291 {
292  if (_mode == IOX_MODE_TEST_OUT) {
294  _should_run = true;
295 
296  } else if (_mode == IOX_MODE_OFF) {
297  _should_run = false;
298 
299  } else {
300  if (!_mode_on_initialized) {
301  /* Subscribe to actuator control 2 (payload group for gimbal) */
302  _actuator_controls_sub = orb_subscribe(ORB_ID(actuator_controls_2));
303  /* set the uorb update interval lower than the driver pwm interval */
305 
306  _mode_on_initialized = true;
307  }
308 
309  /* Read the servo setpoints from the actuator control topics (gimbal) */
310  bool updated;
312 
313  if (updated) {
314  orb_copy(ORB_ID(actuator_controls_2), _actuator_controls_sub, &_actuator_controls);
315 
316  for (int i = 0; i < actuator_controls_s::NUM_ACTUATOR_CONTROLS; i++) {
317  /* Scale the controls to PWM, first multiply by pi to get rad,
318  * the control[i] values are on the range -1 ... 1 */
319  uint16_t new_value = PCA9685_PWMCENTER +
321  DEVICE_DEBUG("%d: current: %u, new %u, control %.2f", i, _current_values[i], new_value,
322  (double)_actuator_controls.control[i]);
323 
324  if (new_value != _current_values[i] &&
325  new_value >= PCA9685_PWMMIN &&
326  new_value <= PCA9685_PWMMAX) {
327  /* This value was updated, send the command to adjust the PWM value */
328  setPin(i, new_value);
329  _current_values[i] = new_value;
330  }
331  }
332  }
333 
334  _should_run = true;
335  }
336 
337  // check if any activity remains, else stop
338  if (!_should_run) {
339  _running = false;
340  return;
341  }
342 
343  // re-queue ourselves to run again later
344  _running = true;
345  ScheduleDelayed(_i2cpwm_interval);
346 }
347 
348 int
349 PCA9685::setPWM(uint8_t num, uint16_t on, uint16_t off)
350 {
351  int ret;
352  /* convert to correct message */
353  _msg[0] = LED0_ON_L + 4 * num;
354  _msg[1] = on;
355  _msg[2] = on >> 8;
356  _msg[3] = off;
357  _msg[4] = off >> 8;
358 
359  /* try i2c transfer */
360  ret = transfer(_msg, 5, nullptr, 0);
361 
362  if (OK != ret) {
364  DEVICE_LOG("i2c::transfer returned %d", ret);
365  }
366 
367  return ret;
368 }
369 
370 int
371 PCA9685::setPin(uint8_t num, uint16_t val, bool invert)
372 {
373  // Clamp value between 0 and 4095 inclusive.
374  if (val > 4095) {
375  val = 4095;
376  }
377 
378  if (invert) {
379  if (val == 0) {
380  // Special value for signal fully on.
381  return setPWM(num, 4096, 0);
382 
383  } else if (val == 4095) {
384  // Special value for signal fully off.
385  return setPWM(num, 0, 4096);
386 
387  } else {
388  return setPWM(num, 0, 4095 - val);
389  }
390 
391  } else {
392  if (val == 4095) {
393  // Special value for signal fully on.
394  return setPWM(num, 4096, 0);
395 
396  } else if (val == 0) {
397  // Special value for signal fully off.
398  return setPWM(num, 0, 4096);
399 
400  } else {
401  return setPWM(num, 0, val);
402  }
403  }
404 
405  return PX4_ERROR;
406 }
407 
408 int
410 {
411  int ret = OK;
412  freq *= 0.9f; /* Correct for overshoot in the frequency setting (see issue
413  https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library/issues/11). */
414  float prescaleval = 25000000;
415  prescaleval /= 4096;
416  prescaleval /= freq;
417  prescaleval -= 1;
418  uint8_t prescale = uint8_t(prescaleval + 0.5f); //implicit floor()
419  uint8_t oldmode;
420  ret = read8(PCA9685_MODE1, oldmode);
421 
422  if (ret != OK) {
423  return ret;
424  }
425 
426  uint8_t newmode = (oldmode & 0x7F) | 0x10; // sleep
427 
428  ret = write8(PCA9685_MODE1, newmode); // go to sleep
429 
430  if (ret != OK) {
431  return ret;
432  }
433 
434  ret = write8(PCA9685_PRESCALE, prescale); // set the prescaler
435 
436  if (ret != OK) {
437  return ret;
438  }
439 
440  ret = write8(PCA9685_MODE1, oldmode);
441 
442  if (ret != OK) {
443  return ret;
444  }
445 
446  usleep(5000); //5ms delay (from arduino driver)
447 
448  ret = write8(PCA9685_MODE1, oldmode | 0xa1); // This sets the MODE1 register to turn on auto increment.
449 
450  if (ret != OK) {
451  return ret;
452  }
453 
454  return ret;
455 }
456 
457 /* Wrapper to read a byte from addr */
458 int
459 PCA9685::read8(uint8_t addr, uint8_t &value)
460 {
461  int ret = OK;
462 
463  /* send addr */
464  ret = transfer(&addr, sizeof(addr), nullptr, 0);
465 
466  if (ret != OK) {
467  goto fail_read;
468  }
469 
470  /* get value */
471  ret = transfer(nullptr, 0, &value, 1);
472 
473  if (ret != OK) {
474  goto fail_read;
475  }
476 
477  return ret;
478 
479 fail_read:
481  DEVICE_LOG("i2c::transfer returned %d", ret);
482 
483  return ret;
484 }
485 
486 int PCA9685::reset(void)
487 {
488  warnx("resetting");
489  return write8(PCA9685_MODE1, 0x0);
490 }
491 
492 /* Wrapper to wite a byte to addr */
493 int
494 PCA9685::write8(uint8_t addr, uint8_t value)
495 {
496  int ret = OK;
497  _msg[0] = addr;
498  _msg[1] = value;
499  /* send addr and value */
500  ret = transfer(_msg, 2, nullptr, 0);
501 
502  if (ret != OK) {
504  DEVICE_LOG("i2c::transfer returned %d", ret);
505  }
506 
507  return ret;
508 }
509 
510 void
512 {
513  warnx("missing command: try 'start', 'test', 'stop', 'info'");
514  warnx("options:");
515  warnx(" -b i2cbus (%d)", PX4_I2C_BUS_EXPANSION);
516  warnx(" -a addr (0x%x)", ADDR);
517 }
518 
519 int
520 pca9685_main(int argc, char *argv[])
521 {
522  int i2cdevice = -1;
523  int i2caddr = ADDR; // 7bit
524 
525  int myoptind = 1;
526  int ch;
527  const char *myoptarg = nullptr;
528 
529 
530  // jump over start/off/etc and look at options first
531  while ((ch = px4_getopt(argc, argv, "a:b:", &myoptind, &myoptarg)) != EOF) {
532  switch (ch) {
533  case 'a':
534  i2caddr = strtol(myoptarg, NULL, 0);
535  break;
536 
537  case 'b':
538  i2cdevice = strtol(myoptarg, NULL, 0);
539  break;
540 
541  default:
542  pca9685_usage();
543  exit(0);
544  }
545  }
546 
547  if (myoptind >= argc) {
548  pca9685_usage();
549  exit(0);
550  }
551 
552  const char *verb = argv[myoptind];
553 
554  int fd;
555  int ret;
556 
557  if (!strcmp(verb, "start")) {
558  if (g_pca9685 != nullptr) {
559  errx(1, "already started");
560  }
561 
562  if (i2cdevice == -1) {
563  // try the external bus first
564  i2cdevice = PX4_I2C_BUS_EXPANSION;
565  g_pca9685 = new PCA9685(PX4_I2C_BUS_EXPANSION, i2caddr);
566 
567  if (g_pca9685 != nullptr && OK != g_pca9685->init()) {
568  delete g_pca9685;
569  g_pca9685 = nullptr;
570  }
571 
572  if (g_pca9685 == nullptr) {
573  errx(1, "init failed");
574  }
575  }
576 
577  if (g_pca9685 == nullptr) {
578  g_pca9685 = new PCA9685(i2cdevice, i2caddr);
579 
580  if (g_pca9685 == nullptr) {
581  errx(1, "new failed");
582  }
583 
584  if (OK != g_pca9685->init()) {
585  delete g_pca9685;
586  g_pca9685 = nullptr;
587  errx(1, "init failed");
588  }
589  }
590 
591  fd = open(PCA9685_DEVICE_PATH, 0);
592 
593  if (fd == -1) {
594  errx(1, "Unable to open " PCA9685_DEVICE_PATH);
595  }
596 
597  ret = ioctl(fd, IOX_SET_MODE, (unsigned long)IOX_MODE_ON);
598  close(fd);
599 
600 
601  exit(0);
602  }
603 
604  // need the driver past this point
605  if (g_pca9685 == nullptr) {
606  warnx("not started, run pca9685 start");
607  exit(1);
608  }
609 
610  if (!strcmp(verb, "info")) {
611  g_pca9685->info();
612  exit(0);
613  }
614 
615  if (!strcmp(verb, "reset")) {
616  g_pca9685->reset();
617  exit(0);
618  }
619 
620 
621  if (!strcmp(verb, "test")) {
622  fd = open(PCA9685_DEVICE_PATH, 0);
623 
624  if (fd == -1) {
625  errx(1, "Unable to open " PCA9685_DEVICE_PATH);
626  }
627 
628  ret = ioctl(fd, IOX_SET_MODE, (unsigned long)IOX_MODE_TEST_OUT);
629 
630  close(fd);
631  exit(ret);
632  }
633 
634  if (!strcmp(verb, "stop")) {
635  fd = open(PCA9685_DEVICE_PATH, 0);
636 
637  if (fd == -1) {
638  errx(1, "Unable to open " PCA9685_DEVICE_PATH);
639  }
640 
641  ret = ioctl(fd, IOX_SET_MODE, (unsigned long)IOX_MODE_OFF);
642  close(fd);
643 
644  // wait until we're not running any more
645  for (unsigned i = 0; i < 15; i++) {
646  if (!g_pca9685->is_running()) {
647  break;
648  }
649 
650  usleep(50000);
651  printf(".");
652  fflush(stdout);
653  }
654 
655  printf("\n");
656  fflush(stdout);
657 
658  if (!g_pca9685->is_running()) {
659  delete g_pca9685;
660  g_pca9685 = nullptr;
661  warnx("stopped, exiting");
662  exit(0);
663 
664  } else {
665  warnx("stop failed.");
666  exit(1);
667  }
668  }
669 
670  pca9685_usage();
671  exit(0);
672 }
__EXPORT int pca9685_main(int argc, char *argv[])
Definition: pca9685.cpp:520
#define PCA9685_PWMCENTER
Definition: pca9685.cpp:105
int orb_copy(const struct orb_metadata *meta, int handle, void *buffer)
Definition: uORB.cpp:90
IO expander device API.
API for the uORB lightweight object broker.
virtual int reset()
Definition: pca9685.cpp:486
int write8(uint8_t addr, uint8_t value)
Definition: pca9685.cpp:494
#define LED0_ON_L
Definition: pca9685.cpp:85
virtual int info()
Definition: pca9685.cpp:272
int orb_set_interval(int handle, unsigned interval)
Definition: uORB.cpp:126
Definition: I2C.hpp:51
static Mode _mode
Definition: motor_ramp.cpp:81
int info()
Print a little info about the driver.
uint8_t _msg[6]
Definition: pca9685.cpp:135
int _actuator_controls_sub
Definition: pca9685.cpp:137
bool _mode_on_initialized
Definition: pca9685.cpp:142
int reset(enum LPS22HB_BUS busid)
Reset the driver.
void Run() override
Set to true after the first call of i2cpwm in mode IOX_MODE_ON.
Definition: pca9685.cpp:290
count the number of times an event occurs
Definition: perf_counter.h:55
#define PCA9685_SCALE
Definition: pca9685.cpp:110
IOX_MODE
#define PCA9685_PWMFREQ
Definition: pca9685.cpp:99
bool _should_run
Definition: pca9685.cpp:132
int setPWM(uint8_t num, uint16_t on, uint16_t off)
Helper function to set the demanded pwm value.
Definition: pca9685.cpp:349
perf_counter_t _comms_errors
Definition: pca9685.cpp:133
#define IOX_SET_MODE
set device mode (non-blocking)
#define PCA9685_PWMMAX
Definition: pca9685.cpp:103
int orb_subscribe(const struct orb_metadata *meta)
Definition: uORB.cpp:75
int setPin(uint8_t num, uint16_t val, bool invert=false)
Sets pin without having to deal with on/off tick placement and properly handles a zero value as compl...
Definition: pca9685.cpp:371
int read8(uint8_t addr, uint8_t &value)
Definition: pca9685.cpp:459
#define ORB_ID(_name)
Generates a pointer to the uORB metadata structure for a given topic.
Definition: uORB.h:87
uint16_t _current_values[actuator_controls_s::NUM_ACTUATOR_CONTROLS]
stores the current pwm output values as sent to the setPin()
Definition: pca9685.cpp:139
void perf_count(perf_counter_t handle)
Count a performance event.
Header common to all counters.
void init()
Activates/configures the hardware registers.
#define PCA9685_PRESCALE
Definition: pca9685.cpp:83
#define DEVICE_LOG(FMT,...)
Definition: Device.hpp:51
#define perf_alloc(a, b)
Definition: px4io.h:59
#define PCA9685_BUS
Definition: pca9685.cpp:98
Vector< float, 6 > f(float t, const Matrix< float, 6, 1 > &, const Matrix< float, 3, 1 > &)
Definition: integration.cpp:8
#define PCA9685_DEVICE_PATH
Definition: pca9685.cpp:97
#define warnx(...)
Definition: err.h:95
uint64_t _i2cpwm_interval
Definition: pca9685.cpp:131
#define PCA9685_PWMMIN
Definition: pca9685.cpp:102
Simple error/warning functions, heavily inspired by the BSD functions of the same names...
#define PCA9685_MODE1
Definition: pca9685.cpp:82
virtual int ioctl(struct file *filp, int cmd, unsigned long arg)
Definition: pca9685.cpp:223
int fd
Definition: dataman.cpp:146
virtual int init()
Definition: pca9685.cpp:202
bool _running
Definition: pca9685.cpp:130
int setPWMFreq(float freq)
Helper function to set the pwm frequency.
Definition: pca9685.cpp:409
enum IOX_MODE _mode
Definition: pca9685.cpp:129
bool is_running()
Definition: pca9685.cpp:125
PCA9685(int bus=PCA9685_BUS, uint8_t address=ADDR)
Definition: pca9685.cpp:185
struct @83::@85::@87 file
int orb_check(int handle, bool *updated)
Definition: uORB.cpp:95
#define errx(eval,...)
Definition: err.h:89
Definition: bst.cpp:62
void pca9685_usage()
Definition: pca9685.cpp:511
#define OK
Definition: uavcan_main.cpp:71
#define M_PI_F
Definition: ashtech.cpp:44
#define DEVICE_DEBUG(FMT,...)
Definition: Device.hpp:52
struct actuator_controls_s _actuator_controls
Definition: pca9685.cpp:138
Performance measuring tools.
#define ADDR
Definition: pca9685.cpp:95
Base class for devices connected via I2C.