DUDS
Distributed Update of Data from Something
FXOS8700CQ.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the DUDS project. It is subject to the BSD-style
3  * license terms in the LICENSE file found in the top-level directory of this
4  * distribution and at https://github.com/jjackowski/duds/blob/master/LICENSE.
5  * No part of DUDS, including this file, may be copied, modified, propagated,
6  * or distributed except according to the terms contained in the LICENSE file.
7  *
8  * Copyright (C) 2017 Jeff Jackowski
9  */
13 #include <thread>
14 
15 namespace duds { namespace hardware { namespace devices { namespace instruments {
16 
22 namespace FXOS8700CQ_internal {
23 
30 enum Regs {
57  RegConfig1 = 0x2A, // the answer should have started at zero
74  RegMagOffsetX = 0x3F,
75  RegMagOffsetY = 0x41,
76  RegMagOffsetZ = 0x43,
77  RegMagConfig1 = 0x5B,
80 };
81 
82 }
83 
84 using namespace FXOS8700CQ_internal;
85 
86 // Device uses big-endian data unaligned to address value
87 
88 FXOS8700CQ::FXOS8700CQ(std::unique_ptr<duds::hardware::interface::I2c> &i2ccom) :
89 com(std::move(i2ccom)), datarate(0) {
90  try {
92  // verify ID
93  firstcon.addOutputVector() << (std::int8_t)RegDeviceId;
94  firstcon.addInputVector(1);
95  com->converse(firstcon);
97  std::uint8_t id;
98  ex >> id;
99  if (id != 0xC7) {
102  );
103  }
104  // make the device not active
105  cfg.magnetometer = 1; // in case magnetometer was in use; takes longer
106  suspend();
107  // attempt reset
108  // This reset makes the device stop responding until a power cycle under
109  // certain conditions met by connecting the device to my notebook
110  // computer's I2C bus on its video output. Works fine on a Raspberry Pi.
111  firstcon.clear();
112  firstcon.addOutputVector() << (std::uint8_t)(RegConfig2) <<
113  (std::uint8_t)(0x40);
114  try {
115  // the reset causes the device to not ack the message
116  com->converse(firstcon);
118  // bother; ignore it
119  } catch (...) {
120  // could be a real issue, but should be unlikely to occur
121  throw;
122  }
123  // give the device time to complete the reset
124  std::this_thread::sleep_for(std::chrono::milliseconds(2));
125  } catch (...) {
126  // move the I2C communicator back
127  i2ccom = std::move(com);
128  throw;
129  }
130 }
131 
133  suspend();
134 }
135 
136 // search for double freq if both accelerometer and magnetometer will run
137 static const std::map<float, int> DataRateVals = {
138  { 800.0, 0 },
139  { 400.0, 1 },
140  { 200.0, 2 },
141  { 100.0, 3 },
142  { 50.0, 4 }, // Sleep data rate is limited to 50Hz.
143  { 12.5, 5 }, // Subtract 4 from value to get sleep ODR value.
144  { 6.25, 6 },
145  { 1.5625, 7 },
146 };
147 
149  float freq,
150  Settings settings
151 ) {
152  // one of the instruments must be enabled
153  if (!settings.accelerometer && !settings.magnetometer) {
155  }
156  if ( // low noise mode cannot be used with 8g range
157  (settings.accelLowNoise && (settings.maxMagnitude == Magnitude8g)) ||
158  // only thre valid values in 2 bits
159  (settings.maxMagnitude == 3)
160  ) {
162  }
163  { // sample rate configuration
164  float f = freq;
165  // the update rate is halved when both the accelerometer and
166  // magnetometer are used together
167  if (settings.accelerometer && settings.magnetometer) {
168  f *= 2;
169  }
170  // find the supported rate that is at least as great as the requested
171  // rate
172  std::map<float, int>::const_iterator dv = DataRateVals.lower_bound(f);
173  if (dv == DataRateVals.cend()) {
176  );
177  }
178  drval = dv->second;
179  datarate = dv->first;
180  if (settings.accelerometer && settings.magnetometer) {
181  datarate /= 2;
182  }
183  }
184  // prevent device from doing much prior to changing config
185  suspend(); // ensures it is inactive
186 
187  // the code below could be cleaned up, but until the device gives good
188  // data, the code may be wrong, so don't worry about cleaner or more
189  // optimal code yet
190 
191  // start with magnetometer config
193  std::uint8_t tmpregs[4] = {
194  (std::uint8_t)(settings.oversampleRatio << 2),
195  0x24, // guess for degauss freq
196  (std::uint8_t)(((settings.oversampleRatio/2) << 4) | 0x80),
197  0
198  };
199  if (settings.magnetometer) {
200  tmpregs[0] |= 1;
201  if (settings.accelerometer) {
202  tmpregs[0] |= 2;
203  }
204  }
205  // set the three magnetometer control registers; they have some other stuff
206  conv.addOutputVector() <<
207  (std::uint8_t)(RegMagConfig1) << tmpregs[0] << tmpregs[1] << tmpregs[2];
208  com->converse(conv);
209  conv.clear();
210  // accelerometer control reg
211  tmpregs[0] = settings.maxMagnitude;
212  tmpregs[1] = 0;
213  if (settings.highPassFilter) {
214  tmpregs[0] |= 0x10;
215  // high-pass filter cut-off
216  if (settings.highPassLowCutoff) {
217  tmpregs[1] = 1;
218  }
219  }
220  conv.addOutputVector() <<
221  (std::uint8_t)RegAccelConfig << tmpregs[0] << tmpregs[1];
222  com->converse(conv);
223  conv.clear();
224  // configuration reg 2
225  tmpregs[0] = settings.oversampleMode | (settings.oversampleSleepMode << 3);
226  conv.addOutputVector() <<
227  (std::uint8_t)(RegConfig2) << tmpregs[0];
228  com->converse(conv);
229  /*
230  tmpregs[0] = 0xC0 | (drval << 3);
231  if (settings.accelLowNoise) {
232  tmpregs[0] |= 4;
233  }
234  tmpregs[1] = settings.oversampleMode | (settings.oversampleSleepMode << 3);
235  conv.addOutputVector() <<
236  (std::uint8_t)(RegConfig1) << tmpregs[0] << tmpregs[1];
237  com->converse(conv);
238  */
239  conv.clear();
240 
241  // no FIFO
242  conv.addOutputVector() <<
243  (std::uint8_t)(RegFifoConfig) << (std::uint8_t)0;
244  com->converse(conv);
245 
246  cfg = settings;
247  // make the conversation to read in the device's sample data
248  bufq.clear();
249  if (settings.magnetometer) {
250  bufq.addOutputVector() << (std::uint8_t)RegMagStatus;
251  } else {
252  bufq.addOutputVector() << (std::uint8_t)RegStatus;
253  }
254  bufq.addInputVector(1);
255 
256  input.clear();
257  if (settings.magnetometer) {
258  // prefer to read from the magnetometer side to get the "time aligned"
259  // accelerometer sample, because that sounds nice, like it might mean
260  // something useful
261  input.addOutputVector() << (std::uint8_t)RegMagSample;
262  if (settings.accelerometer) {
263  input.addInputVector(12).bigEndian(true);
264  } else {
265  input.addInputVector(6).bigEndian(true);
266  }
267  } else {
268  input.addOutputVector() << (std::uint8_t)(RegSamples);
269  input.addInputVector(6).bigEndian(true);
270  }
271  /*
272  if (settings.accelerometer) {
273  input.addOutputVector() << (std::uint8_t)(RegSamples);
274  if (settings.magnetometer) {
275  input.addInputVector(12).bigEndian(true);
276  } else {
277  input.addInputVector(6).bigEndian(true);
278  }
279  } else {
280  input.addOutputVector() << (std::uint8_t)(RegMagSample);
281  input.addInputVector(6).bigEndian(true);
282  }
283  */
284 }
285 
288  // configuration reg 1
289  // do this after sending configuration reg 2 so that the device transitions
290  // to its active state after having sent the complete configuration
291  std::uint8_t tmpreg = 0xC1 | (drval << 3);
292  if (cfg.accelLowNoise) {
293  tmpreg |= 4;
294  }
295  // this will cause device to begin sampling
296  // if FIFO is used, it will start to fill, so should start thread to read it
297  // prior to sending it
298  conv.addOutputVector() <<
299  (std::uint8_t)RegConfig1 << tmpreg;
300  com->converse(conv);
301 }
302 
305  conv.addOutputVector() <<
306  // command to start writing at enable register
307  (std::uint8_t)RegConfig1 <<
308  // make it stop
309  (std::uint8_t)(0);
310  com->converse(conv);
311  // using the magnetometer requires more time to suspend; delay here to avoid
312  // adding check to start()
313  if (cfg.magnetometer) {
314  conv.clear();
315  conv.addOutputVector() << (std::int8_t)RegSystemMode;
316  conv.addInputVector(1);
318  std::int8_t modestat;
319  do {
320  std::this_thread::sleep_for(std::chrono::milliseconds(32));
321  com->converse(conv);
322  ex.reset(conv);
323  ex >> modestat;
324  } while (modestat & 3);
325  }
326 }
327 
329  com->converse(bufq);
331  std::int8_t status;
332  ex >> status;
333  // have new samples for all axes?
334  if ((status & 7) == 7) {
335  // read them
336  com->converse(input);
337  // parse input
339  if (cfg.magnetometer) {
340  ex >> magn.x >> magn.y >> magn.z;
341  }
342  if (cfg.accelerometer) {
343  ex >> accl.x >> accl.y >> accl.z;
344  // accelerometer data is read from different registers depending
345  // on the use of the magnetometer
346  if (cfg.magnetometer) {
347  // the 2 MSb's may not be set properly for a 16-bit signed int
348  accl.x = duds::general::SignExtend<14>(accl.x);
349  accl.y = duds::general::SignExtend<14>(accl.y);
350  accl.z = duds::general::SignExtend<14>(accl.z);
351  } else {
352  // the registers hold the number shifted so that the 8 most
353  // significant bits are all in the same byte, leaving 2 bits
354  // in the least significant byte unused; shift them to place
355  // properly in 16-bit ints
356  accl.x >>= 2;
357  accl.y >>= 2;
358  accl.z >>= 2;
359  }
360  }
361  return true;
362  }
363  return false;
364 }
365 
366 
367 } } } }
void start()
Tells the device to start sampling.
Definition: FXOS8700CQ.cpp:286
void configure(float freq, Settings settings)
Configures the device.
Definition: FXOS8700CQ.cpp:148
void clear()
Makes the conversation empty.
The various settings for the device packed into an integer to avoid passing a lot of parameters...
Definition: FXOS8700CQ.hpp:104
bool sample()
Reads sampled data from the device.
Definition: FXOS8700CQ.cpp:328
boost::error_info< struct Info_i2cdevaddr, int > I2cDeviceAddr
Provides the device (slave) address along with an error.
Definition: I2cErrors.hpp:83
ConversationVector & addInputVector(std::size_t len)
Creates a new ConversationVector for fixed length input and initializes it with the given length...
Header for ConversationExtractor; includes all other conversation related header files.
unsigned int accelLowNoise
Use the low-noise mode of the accelerometer.
Definition: FXOS8700CQ.hpp:119
void reset()
Prepares the object to extract another time from the same Conversation object used previously...
unsigned int highPassLowCutoff
Adjusts the cut-off frequency of the high-pass filter to be lower.
Definition: FXOS8700CQ.hpp:127
unsigned int accelerometer
Flag to use the accelerometer.
Definition: FXOS8700CQ.hpp:109
Magnitude maxMagnitude
The maximum magnetude for the accelerometer.
Definition: FXOS8700CQ.hpp:131
boost::error_info< struct Info_UpdateRate, float > RequestedUpdateRate
Definition: FXOS8700CQ.hpp:35
bool bigEndian() const
True if this part is flagged as having data in big-endian form.
STL namespace.
move_impl move(unsigned int c, unsigned int r)
Display stream manipulator that moves the display cursor to the given location.
void suspend()
Suspends operation by putting the device into a low-power mode that discontinues sampling.
Definition: FXOS8700CQ.cpp:303
OversampleMode oversampleSleepMode
The oversample mode to use when in the sleep state.
Definition: FXOS8700CQ.hpp:140
The device did not respond to its address (NACK).
Definition: I2cErrors.hpp:47
SYSMOD, the system operating mode and FIFO error status.
Definition: FXOS8700CQ.cpp:52
std::int8_t drval
The data rate value given to the device.
Definition: FXOS8700CQ.hpp:184
duds::hardware::interface::Conversation input
The conversation used to read in samples from the device.
Definition: FXOS8700CQ.hpp:168
unsigned int magnetometer
Flag to use the magnetometer.
Definition: FXOS8700CQ.hpp:114
duds::hardware::interface::Conversation bufq
Definition: FXOS8700CQ.hpp:168
Settings cfg
The current various configuration options.
Definition: FXOS8700CQ.hpp:180
FXOS8700CQ(std::unique_ptr< duds::hardware::interface::I2c > &i2ccom)
Attempts to identify the device, then suspends the device&#39;s operation.
Definition: FXOS8700CQ.cpp:88
static const std::map< float, int > DataRateVals
Definition: FXOS8700CQ.cpp:137
unsigned int oversampleRatio
Affects how many samples are taken by the magnetometer to produce a single output sample...
Definition: FXOS8700CQ.hpp:147
static std::wstring_convert< std::codecvt_utf8< char32_t >, char32_t > conv
String converter; UTF-8 to/from UTF-32.
Definition: BppFont.cpp:165
The requested maximum magnitude is either unsupported or an invalid value.
Definition: FXOS8700CQ.hpp:28
std::unique_ptr< duds::hardware::interface::I2c > com
The I2C communication interface.
Definition: FXOS8700CQ.hpp:159
float datarate
The currently configured sample rate.
Definition: FXOS8700CQ.hpp:176
The configuration requested that neither the accelerometer or magnetometer be used.
Definition: FXOS8700CQ.hpp:33
An attempt was made to use a device that seems to exist, but the responding device is not the type th...
ConversationVector & addOutputVector()
Creates a new ConversationVector for output and returns it for modification.
unsigned int highPassFilter
Enables the high-pass filter.
Definition: FXOS8700CQ.hpp:123
RawSample accl
The values supplied by the device.
Definition: FXOS8700CQ.hpp:172
Extracts data from a Conversation without modifying the Conversation or copying from it...
#define DUDS_THROW_EXCEPTION(x)
Works like BOOST_THROW_EXCEPTION, but includes a stack trace if DUDS_ERRORS_VERBOSE is defined...
Definition: Errors.hpp:48
The requested data rate is unsupported.
Definition: FXOS8700CQ.hpp:24
OversampleMode oversampleMode
The oversample mode to use when in the active non-sleep state.
Definition: FXOS8700CQ.hpp:135
Represents a two-way conversation with a device.