Aruna
PCA9685.cpp
Go to the documentation of this file.
1 //
2 // Created by noeel on 18-10-20.
3 //
4 
5 #include <cmath>
6 #include "aruna/driver/PCA9685.h"
7 
8 using namespace aruna;
9 using namespace aruna::driver;
10 
11 log::channel_t PCA9685::log("PCA9685");
12 
13 PCA9685::PCA9685(uint8_t led, I2C_master *i2c_bus, uint8_t i2c_address) : led(led), i2c_address(i2c_address) {
14  this->i2c_bus = i2c_bus;
15 
16 // read device
17  err_t i2c_err;
18  uint8_t mode1;
19  i2c_bus->lock(i2c_address);
20  i2c_err = i2c_bus->read(i2c_address, static_cast<uint8_t>(register_address_pointer::MODE1), &mode1, 1);
21  if (i2c_err != err_t::OK)
22  log.error("Error while reading MODE1 0x%X: %s", i2c_address, err_to_char.at(i2c_err));
23 
24 // check to see if device is powered on
25  if (mode1 & (uint8_t) SLEEP::LOW_POWER) {
26  i2c_err = i2c_bus->write(i2c_address, (uint8_t) register_address_pointer::MODE1,
27  reinterpret_cast<uint8_t *>(mode1 | (uint8_t) SLEEP::NORMAL_MODE), 1);
28  if (i2c_err != err_t::OK)
29  log.error("Error while writing MODE1 0x%X: %s", i2c_address, err_to_char.at(i2c_err));
30  }
31  i2c_bus->unlock(i2c_address);
32 }
33 
35  if (frequency_hz > 1526)
37  if (frequency_hz < 25)
39  err_t i2c_err;
40  uint8_t mode1;
41 // only works if using internal clock
42  uint8_t prescale = round(25000000 / (4096 * frequency_hz)) - 1;
44  i2c_err = i2c_bus->read(i2c_address, static_cast<uint8_t>(register_address_pointer::MODE1), &mode1, 1);
45  if (i2c_err != err_t::OK)
46  log.error("Error while reading MODE1 0x%X: %s", i2c_address, err_to_char.at(i2c_err));
48  reinterpret_cast<uint8_t *>(mode1 ^ (uint8_t) SLEEP::NORMAL_MODE), 1);
49  if (i2c_err != err_t::OK)
50  log.error("Error while writing MODE1 0x%X: %s", i2c_address, err_to_char.at(i2c_err));
51  i2c_err = i2c_bus->write(i2c_address, (uint8_t) register_address_pointer::PRE_SCALE, &prescale, 1);
52  if (i2c_err != err_t::OK)
53  log.error("Error while writing PRE_SCALE 0x%X: %s", i2c_address, err_to_char.at(i2c_err));
54  i2c_err = i2c_bus->write(i2c_address, (uint8_t) register_address_pointer::MODE1, &mode1, 1);
56 
57  return i2c_err;
58 }
59 
61  uint8_t prescale = 0;
62  err_t i2c_err;
63  uint32_t freq = 0;
64  i2c_err = i2c_bus->read(i2c_address, (uint8_t) register_address_pointer::PRE_SCALE, &prescale, 1);
65  if (i2c_err != err_t::OK)
66  log.error("Error while reading PRE_SCALE 0x%X: %s", i2c_address, err_to_char.at(i2c_err));
67  freq = round(25000000 / (4096 * prescale)) - 1;
68  return freq;
69 }
70 
72 
73  duty = round(duty / 65535 * 4095);
74  uint16_t on = 0;
75  uint16_t off = duty;
76  return set_duty(on, off);
77 }
78 
79 err_t PCA9685::set_duty(uint16_t on, uint16_t off) {
80  uint8_t reg = (uint8_t) register_address_pointer::LED0_ON_L + 4 * led;
81  uint8_t data[4];
82  data[0] = on;
83  data[1] = on >> 8;
84  data[2] = off;
85  data[3] = off >> 8;
86  return i2c_bus->write(i2c_address, reg, data, 4);
87 }
88 
89 uint16_t PCA9685::get_duty() {
90  uint16_t on, off;
91  err_t err = get_duty(on, off);
92  if (err != err_t::OK)
93  log.error("Error while getting duty cycle 0x%X: %s", i2c_address, err_to_char.at(err));
94 // TODO test conversion
95  uint16_t duty = round(off / 4095 * 65535);
96  return duty;
97 }
98 
99 err_t PCA9685::get_duty(uint16_t &on, uint16_t &off) {
100  uint8_t reg = (uint8_t) register_address_pointer::LED0_ON_L + 4 * led;
101  uint8_t buff[4];
102  err_t i2c_err = i2c_bus->read(i2c_address, reg, buff, 4);
103 // todo test this conversion
104  on = buff[0] | buff[1] >> 8;
105  off = buff[2] | buff[3] >> 8;
106  return i2c_err;
107 }
108 
110  Pwm::set_duty((uint16_t)0);
111 // TODO put PCA9685 to sleep if I'm the last one connected to it.
112 }
113 
115  uint8_t fake_buffer;
116  return i2c_bus->read(i2c_address, 0, fake_buffer);
117 }
Definition: comm.cpp:14
err_t _set_duty(uint16_t duty) override
Definition: PCA9685.cpp:71
uint16_t get_duty()
Definition: PCA9685.cpp:89
const std::map< err_t, char * > err_to_char
Definition: arunaTypes.h:54
err_t read(uint8_t address, uint8_t reg, uint8_t *buffer, size_t buffer_size)
Read data from I²C slave.
Definition: I2C_master.cpp:7
err_t is_connected()
Is the PCA9685 connected to the I²C bus.
Definition: PCA9685.cpp:114
PCA9685(uint8_t led, I2C_master *i2c_bus, uint8_t i2c_address=default_i2c_address)
PCA9685 pwm object for every LED.
Definition: PCA9685.cpp:13
uint16_t duty
Definition: Pwm.h:16
uint32_t get_frequency()
Definition: PCA9685.cpp:60
static aruna::log::channel_t log
Definition: PCA9685.h:18
err_t _set_frequency(uint32_t frequency_hz) override
Definition: PCA9685.cpp:34
err_t set_duty(float duty_percentage)
Set the duty cycle on time in percentage.
Definition: Pwm.cpp:23
const uint8_t led
Definition: PCA9685.h:113
I2C_master * i2c_bus
Definition: PCA9685.h:114
err_t write(uint8_t address, uint8_t reg, uint8_t *data, size_t data_size)
Write data to the I²C bus.
Definition: I2C_master.cpp:15
err_t lock(uint8_t i2c_address)
Definition: I2C_master.cpp:41
const uint8_t i2c_address
Definition: PCA9685.h:113
err_t set_duty(uint16_t on, uint16_t off)
Set the on and off timing of the PCA9685 led.
Definition: PCA9685.cpp:79
int error(const char *format,...)
log error message
Definition: log.cpp:90
err_t unlock(uint8_t i2c_address)
Definition: I2C_master.cpp:51
uint32_t frequency_hz
Definition: Pwm.h:15