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 "hp20x.h"
32 
33 using namespace upm;
34 using namespace std;
35 
36 
HP20X(int bus,uint8_t address)37 HP20X::HP20X(int bus, uint8_t address):
38   m_i2c(bus)
39 {
40   m_addr = address;
41 
42   mraa::Result rv;
43   if ( (rv = m_i2c.address(m_addr)) != mraa::SUCCESS)
44     {
45       throw std::invalid_argument(std::string(__FUNCTION__) +
46                                   ": I2c.address() failed");
47       return;
48     }
49 }
50 
~HP20X()51 HP20X::~HP20X()
52 {
53 }
54 
init(DSR_BITS_T dsr)55 bool HP20X::init(DSR_BITS_T dsr)
56 {
57   // wait for the device to report ready
58   waitforDeviceReady();
59 
60   m_dsr = dsr;
61 
62   // enable compensation?  Datasheet says yes, but a register readback
63   // says no.  Data does seem stable, so....
64   compensationEnable(true);
65 
66   return true;
67 }
68 
isReady()69 bool HP20X::isReady()
70 {
71   uint8_t intsrc = readReg(REG_INT_SRC);
72 
73   if (intsrc & INT_SRC_DEV_RDY)
74     return true;
75 
76   return false;
77 }
78 
waitforDeviceReady()79 bool HP20X::waitforDeviceReady()
80 {
81   const int maxRetries = 20;
82 
83   int retries = 0;
84 
85   while (retries < maxRetries)
86     {
87       if (isReady())
88         return true;
89 
90       usleep(20000);
91       retries++;
92     }
93 
94   throw std::runtime_error(std::string(__FUNCTION__) +
95                            ": timeout waiting for device to become ready");
96 
97   return false;
98 }
99 
writeCmd(uint8_t cmd)100 bool HP20X::writeCmd(uint8_t cmd)
101 {
102   mraa::Result rv;
103   if ((rv = m_i2c.writeByte(cmd)) != mraa::SUCCESS)
104     {
105       throw std::runtime_error(std::string(__FUNCTION__) +
106                                ": I2c.writeByte() failed");
107       return false;
108     }
109 
110   return true;
111 }
112 
writeReg(HP20X_REG_T reg,uint8_t data)113 bool HP20X::writeReg(HP20X_REG_T reg, uint8_t data)
114 {
115   waitforDeviceReady();
116 
117   uint8_t r = CMD_WRITE_REG | reg;
118 
119   mraa::Result rv;
120   if ((rv = m_i2c.writeReg(r, data)) != mraa::SUCCESS)
121     {
122       throw std::runtime_error(std::string(__FUNCTION__) +
123                                ": I2c.writeReg() failed");
124       return false;
125     }
126 
127   return true;
128 }
129 
readReg(HP20X_REG_T reg)130 uint8_t HP20X::readReg(HP20X_REG_T reg)
131 {
132   uint8_t r = CMD_READ_REG | reg;
133 
134   return m_i2c.readReg(r);
135 }
136 
readData()137 int HP20X::readData()
138 {
139   uint8_t buf[3] = {0};
140 
141   if (!m_i2c.read(buf, 3))
142     {
143       throw std::runtime_error(std::string(__FUNCTION__) +
144                                ": I2c.read() failed");
145       return 0;
146     }
147 
148   // handle 24bit sign extension
149   int minus = 1;
150   if (buf[0] & 0x80)
151     {
152       // negative
153       buf[0] &= 0x3f;
154       minus = -1;
155     }
156 
157   return ( minus * ((buf[0] << 16) | (buf[1] << 8) | buf[2]) );
158 }
159 
getTemperature()160 float HP20X::getTemperature()
161 {
162   // wait for the device to report ready
163   waitforDeviceReady();
164 
165   // start conversion, T only
166   uint8_t cmd = CMD_ADC_CVT | (CHNL_T << CHNL_SHIFT) | (m_dsr << DSR_SHIFT);
167   writeCmd(cmd);
168 
169   // wait for the device to report ready
170   waitforDeviceReady();
171 
172   // now read the temperature
173   writeCmd(CMD_READ_T);
174 
175   return ((float)readData() / 100.0);
176 }
177 
getPressure()178 float HP20X::getPressure()
179 {
180   // wait for the device to report ready
181   waitforDeviceReady();
182 
183   // start conversion, PT only
184   uint8_t cmd = CMD_ADC_CVT | (CHNL_PT << CHNL_SHIFT) | (m_dsr << DSR_SHIFT);
185   writeCmd(cmd);
186 
187   // wait for the device to report ready
188   waitforDeviceReady();
189 
190   // now read the pressure
191   writeCmd(CMD_READ_P);
192 
193   return ((float)readData() / 100.0);
194 }
195 
getAltitude()196 float HP20X::getAltitude()
197 {
198   // wait for the device to report ready
199   waitforDeviceReady();
200 
201   // start conversion, PT only
202   uint8_t cmd = CMD_ADC_CVT | (CHNL_PT << CHNL_SHIFT) | (m_dsr << DSR_SHIFT);
203   writeCmd(cmd);
204 
205   // wait for the device to report ready
206   waitforDeviceReady();
207 
208   // now read the pressure
209   writeCmd(CMD_READ_A);
210 
211   return ((float)readData() / 100.0);
212 }
213 
compensationEnable(bool enable)214 void HP20X::compensationEnable(bool enable)
215 {
216   if (enable)
217     writeReg(REG_PARA, PARA_CMPS_EN);
218   else
219     writeReg(REG_PARA, 0);
220 }
221 
setInterruptEnable(uint8_t bits)222 bool HP20X::setInterruptEnable(uint8_t bits)
223 {
224   return writeReg(REG_INT_EN, bits);
225 }
226 
setInterruptConfig(uint8_t bits)227 bool HP20X::setInterruptConfig(uint8_t bits)
228 {
229   return writeReg(REG_INT_CFG, bits);
230 }
231 
getInterruptSource()232 uint8_t HP20X::getInterruptSource()
233 {
234   return readReg(REG_INT_SRC);
235 }
236 
setDSR(DSR_BITS_T dsr)237 void HP20X::setDSR(DSR_BITS_T dsr)
238 {
239   m_dsr = dsr;
240 }
241 
recalibrateInternal()242 void HP20X::recalibrateInternal()
243 {
244   waitforDeviceReady();
245   writeCmd(CMD_ANA_CAL);
246 }
247 
softReset()248 void HP20X::softReset()
249 {
250   waitforDeviceReady();
251   writeCmd(CMD_SOFT_RST);
252   waitforDeviceReady();
253 }
254 
setAltitudeOffset(int16_t off)255 void HP20X::setAltitudeOffset(int16_t off)
256 {
257   writeReg(REG_ALT_OFF_LSB, (off & 0xff));
258   writeReg(REG_ALT_OFF_MSB, ((off >> 8) & 0xff));
259 }
260 
setPAThreshholds(int16_t low,int16_t med,int16_t high)261 void HP20X::setPAThreshholds(int16_t low, int16_t med, int16_t high)
262 {
263   // low
264   writeReg(REG_PA_L_TH_LSB, (low & 0xff));
265   writeReg(REG_PA_L_TH_MSB, ((low >> 8) & 0xff));
266 
267   // medium
268   writeReg(REG_PA_M_TH_LSB, (med & 0xff));
269   writeReg(REG_PA_M_TH_MSB, ((med >> 8) & 0xff));
270 
271   // high
272   writeReg(REG_PA_H_TH_LSB, (high & 0xff));
273   writeReg(REG_PA_H_TH_MSB, ((high >> 8) & 0xff));
274 }
275 
setTemperatureThreshholds(int8_t low,int8_t med,int8_t high)276 void HP20X::setTemperatureThreshholds(int8_t low, int8_t med, int8_t high)
277 {
278   // low
279   writeReg(REG_T_L_TH, low);
280 
281   // medium
282   writeReg(REG_T_M_TH, med);
283 
284   // high
285   writeReg(REG_T_H_TH, high);
286 }
287 
288