1 /*
2  * Author: Jon Trulson <jtrulson@ics.com>
3  * Copyright (c) 2015 Intel Corporation.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include <unistd.h>
26 #include <math.h>
27 #include <iostream>
28 #include <string>
29 #include <stdexcept>
30 
31 #include "pca9685.h"
32 
33 using namespace upm;
34 using namespace std;
35 
36 
PCA9685(int bus,uint8_t address,bool raw)37 PCA9685::PCA9685(int bus, uint8_t address, bool raw)
38 {
39   m_addr = address;
40 
41   // setup our i2c link
42   if ( raw )
43     {
44       m_i2c = mraa_i2c_init_raw(bus);
45     }
46   else
47     {
48       m_i2c = mraa_i2c_init(bus);
49     }
50 
51   if ( !m_i2c)
52     {
53       throw std::invalid_argument(std::string(__FUNCTION__) +
54                                   ": mraa_i2c_init() failed");
55       return;
56     }
57 
58   mraa_result_t rv;
59 
60   if ( (rv = mraa_i2c_address(m_i2c, m_addr)) != MRAA_SUCCESS)
61     {
62       throw std::runtime_error(std::string(__FUNCTION__) +
63                                ": mraa_i2c_address() failed");
64       return;
65     }
66 
67   // enable auto-increment mode by default
68   enableAutoIncrement(true);
69 
70   // enable restart by default.
71   enableRestart(true);
72 }
73 
~PCA9685()74 PCA9685::~PCA9685()
75 {
76   setModeSleep(true);
77   mraa_i2c_stop(m_i2c);
78 }
79 
writeByte(uint8_t reg,uint8_t byte)80 bool PCA9685::writeByte(uint8_t reg, uint8_t byte)
81 {
82   mraa_result_t rv = mraa_i2c_write_byte_data(m_i2c, byte, reg);
83 
84   if (rv != MRAA_SUCCESS)
85     {
86       throw std::runtime_error(std::string(__FUNCTION__) +
87                                ": mraa_i2c_write_byte_data() failed");
88       return false;
89     }
90 
91   return true;
92 }
93 
writeWord(uint8_t reg,uint16_t word)94 bool PCA9685::writeWord(uint8_t reg, uint16_t word)
95 {
96   mraa_result_t rv = mraa_i2c_write_word_data(m_i2c, word, reg);
97 
98   if (rv != MRAA_SUCCESS)
99     {
100       throw std::runtime_error(std::string(__FUNCTION__) +
101                                ": mraa_i2c_write_word_data() failed");
102       return false;
103     }
104 
105   return true;
106 }
107 
readByte(uint8_t reg)108 uint8_t PCA9685::readByte(uint8_t reg)
109 {
110   return mraa_i2c_read_byte_data(m_i2c, reg);
111 }
112 
readWord(uint8_t reg)113 uint16_t PCA9685::readWord(uint8_t reg)
114 {
115   return mraa_i2c_read_word_data(m_i2c, reg);
116 }
117 
setModeSleep(bool sleep)118 bool PCA9685::setModeSleep(bool sleep)
119 {
120   uint8_t mode1 = readByte(REG_MODE1);
121   uint8_t restartBit = mode1 & MODE1_RESTART;
122 
123   if (sleep)
124     mode1 |= MODE1_SLEEP;
125   else
126     mode1 &= ~MODE1_SLEEP;
127 
128   // if we are waking up, then preserve but don't write restart bit if set
129   if (!sleep && restartBit)
130     mode1 &= ~MODE1_RESTART;
131 
132   writeByte(REG_MODE1, mode1);
133 
134   // Need a delay of 500us after turning sleep mode off for the oscillator
135   // to stabilize
136   if (!sleep)
137     usleep(500);
138 
139   // now check to see if we want to (and can) restart when waking up
140   if (restartBit && m_restartEnabled && !sleep)
141     {
142       mode1 |= restartBit;
143       writeByte(REG_MODE1, mode1);
144     }
145 
146   return true;
147 }
148 
enableAutoIncrement(bool ai)149 bool PCA9685::enableAutoIncrement(bool ai)
150 {
151   uint8_t mode1 = readByte(REG_MODE1);
152 
153   if (ai)
154     mode1 |= MODE1_AI;
155   else
156     mode1 &= ~MODE1_AI;
157 
158   return writeByte(REG_MODE1, mode1);
159 }
160 
ledFullOn(uint8_t led,bool val)161 bool PCA9685::ledFullOn(uint8_t led, bool val)
162 {
163   if (led > 15 && (led != PCA9685_ALL_LED))
164     {
165       throw std::out_of_range(std::string(__FUNCTION__) +
166                               ": led value must be between 0-15 or " +
167                               "PCA9685_ALL_LED (255)");
168       return false;
169     }
170 
171   // figure out the register offset (*_ON_H)
172   uint8_t regoff;
173 
174   if (led == PCA9685_ALL_LED)
175     regoff = REG_ALL_LED_ON_H;
176   else
177     regoff = REG_LED0_ON_L + (led * 4) + 1;
178 
179   uint8_t bits = readByte(regoff);
180 
181   if (val)
182     bits |= 0x10;
183   else
184     bits &= ~0x10;
185 
186   return writeByte(regoff, bits);
187 }
188 
ledFullOff(uint8_t led,bool val)189 bool PCA9685::ledFullOff(uint8_t led, bool val)
190 {
191   if (led > 15 && (led != PCA9685_ALL_LED))
192     {
193       throw std::out_of_range(std::string(__FUNCTION__) +
194                               ": led value must be between 0-15 or " +
195                               "PCA9685_ALL_LED (255)");
196       return false;
197     }
198 
199   // figure out the register offset (*_OFF_H)
200   uint8_t regoff;
201 
202   if (led == PCA9685_ALL_LED)
203     regoff = REG_ALL_LED_OFF_H;
204   else
205     regoff = REG_LED0_ON_L + (led * 4) + 3;
206 
207   uint8_t bits = readByte(regoff);
208 
209   if (val)
210     bits |= 0x10;
211   else
212     bits &= ~0x10;
213 
214   return writeByte(regoff, bits);
215 }
216 
ledOnTime(uint8_t led,uint16_t time)217 bool PCA9685::ledOnTime(uint8_t led, uint16_t time)
218 {
219   if (led > 15 && (led != PCA9685_ALL_LED))
220     {
221       throw std::out_of_range(std::string(__FUNCTION__) +
222                               ": led value must be between 0-15 or " +
223                               "PCA9685_ALL_LED (255)");
224       return false;
225     }
226 
227   if (time > 4095)
228     {
229       throw std::out_of_range(std::string(__FUNCTION__) +
230                               ": time value must be between 0-4095");
231       return false;
232     }
233 
234   // figure out the register offset (*_ON_L)
235   uint8_t regoff;
236 
237   if (led == PCA9685_ALL_LED)
238     regoff = REG_ALL_LED_ON_L;
239   else
240     regoff = REG_LED0_ON_L + (led * 4);
241 
242   // we need to preserve the full ON bit in *_ON_H
243   uint8_t onbit = (readByte(regoff + 1) & 0x10);
244 
245   time = (time & 0x0fff) | (onbit << 8);
246 
247   return writeWord(regoff, time);
248 }
249 
ledOffTime(uint8_t led,uint16_t time)250 bool PCA9685::ledOffTime(uint8_t led, uint16_t time)
251 {
252   if (led > 15 && (led != PCA9685_ALL_LED))
253     {
254       throw std::out_of_range(std::string(__FUNCTION__) +
255                               ": led value must be between 0-15 or " +
256                               "PCA9685_ALL_LED (255)");
257       return false;
258     }
259 
260   if (time > 4095)
261     {
262       throw std::out_of_range(std::string(__FUNCTION__) +
263                               ": time value must be between 0-4095");
264       return false;
265     }
266 
267   // figure out the register offset (*_OFF_L)
268   uint8_t regoff;
269 
270   if (led == PCA9685_ALL_LED)
271     regoff = REG_ALL_LED_OFF_L;
272   else
273     regoff = REG_LED0_ON_L + (led * 4) + 2;
274 
275   // we need to preserve the full OFF bit in *_OFF_H
276   uint8_t offbit = (readByte(regoff + 1) & 0x10);
277 
278   time = (time & 0x0fff) | (offbit << 8);
279 
280   return writeWord(regoff, time);
281 }
282 
setPrescale(uint8_t prescale)283 bool PCA9685::setPrescale(uint8_t prescale)
284 {
285   // This will be ignored if the device isn't in SLEEP mode
286   return writeByte(REG_PRESCALE, prescale);
287 }
288 
setPrescaleFromHz(float hz,float oscFreq)289 bool PCA9685::setPrescaleFromHz(float hz, float oscFreq)
290 {
291   float prescale = round( oscFreq / (4096.0 * hz) ) - 1;
292 
293   return setPrescale(uint8_t(prescale));
294 }
295