Smartcar Shield
GY50.cpp
Go to the documentation of this file.
1 /*
2  L3G4200D implementation based on code by:
3  * http://bildr.org/2011/06/l3g4200d-arduino/
4  * http://www.pieter-jan.com/node/7
5  * https://github.com/sparkfun/Tri-Axis_Gyro_Breakout-L3G4200D
6 */
7 #include "GY50.hpp"
8 #include "../../../utilities/Utilities.hpp"
9 
10 namespace
11 {
12 const uint8_t kGyroAddress = 105;
13 const auto kMeasurementInterval = 100;
14 const float kGyroSensitivity = 0.07F;
15 const int kGyroThreshold = 12; // Smaller changes are to be ignored
16 } // namespace
17 
18 using namespace smartcarlib::utils;
19 using namespace smartcarlib::constants::gy50;
20 
21 GY50::GY50(Runtime& runtime, int offset, unsigned long samplingInterval)
22  : kOffset{ offset }
23  , kSamplingInterval{ samplingInterval }
24  , mRuntime(runtime)
25  , mPreviousSample{ 0 }
26  , mAttached{ false }
27  , mAngularDisplacement{ 0 }
28 {
29 }
30 
32 {
33  // Get the reading from (-180,180) to [0, 360) scale
34  static constexpr auto kFullScaleDegrees = 360;
35  auto normalizedReading = static_cast<int>(mAngularDisplacement) % kFullScaleDegrees;
36 
37  return normalizedReading < 0 ? normalizedReading + kFullScaleDegrees : normalizedReading;
38 }
39 
41 {
42  unsigned long currentTime = mRuntime.currentTimeMillis();
43  unsigned long interval = currentTime - mPreviousSample;
44  if (interval <= kSamplingInterval)
45  {
46  return; // Not the time to read yet
47  }
48 
49  int drift = kOffset - getAngularVelocity();
50 
51  if (getAbsolute(drift) > kGyroThreshold)
52  {
53  float gyroRate = static_cast<float>(drift) * kGyroSensitivity;
54  static constexpr auto kGyroscopeReadingScale = 1000.0F;
55  mAngularDisplacement += gyroRate / (kGyroscopeReadingScale / static_cast<float>(interval));
56  }
57  mPreviousSample = currentTime;
58 }
59 
60 void GY50::attach()
61 {
62  if (mAttached)
63  {
64  return;
65  }
66 
67  mRuntime.i2cInit();
68  // Enable z and turn off power down
69  static constexpr uint8_t kControlRegister1 = 0x20;
70  writeL3G4200DRegister(kControlRegister1, 0b00001100); // NOLINT(readability-magic-numbers)
71  // If you'd like to adjust/use the HPF, you can edit the line below to configure CTRL_REG2
72  static constexpr uint8_t kControlRegister2 = 0x21;
73  writeL3G4200DRegister(kControlRegister2, 0b00000000); // NOLINT(readability-magic-numbers)
74  // Configure CTRL_REG3 to generate data ready interrupt on INT2
75  // No interrupts used on INT1, if you'd like to configure INT1
76  // or INT2 otherwise, consult the datasheet
77  static constexpr uint8_t kControlRegister3 = 0x22;
78  writeL3G4200DRegister(kControlRegister3, 0b00001000); // NOLINT(readability-magic-numbers)
79  // CTRL_REG4 controls the full-scale range, among other things
80  static constexpr uint8_t kControlRegister4 = 0x23;
81  writeL3G4200DRegister(kControlRegister4, 0b00110000); // NOLINT(readability-magic-numbers)
82  // CTRL_REG5 controls high-pass filtering of outputs, use it if you'd like
83  static constexpr uint8_t kControlRegister5 = 0x24;
84  writeL3G4200DRegister(kControlRegister5, 0b00000000); // NOLINT(readability-magic-numbers)
85 
86  mAttached = true;
87 }
88 
89 int GY50::getOffset(int measurements)
90 {
91  if (measurements <= 0)
92  {
93  return kError;
94  }
95 
96  long sum = 0;
97  for (auto i = 0; i < measurements; i++)
98  {
99  sum += getAngularVelocity();
100  mRuntime.delayMillis(kMeasurementInterval);
101  }
102 
103  return static_cast<int>(sum / measurements);
104 }
105 
106 int GY50::getAngularVelocity()
107 {
108  attach();
109 
110  static const uint8_t zAxisFirstByteRegister = 0x2D;
111  static const uint8_t zAxisSecondByteRegister = 0x2C;
112 
113  auto firstByte = readL3G4200DRegister(zAxisFirstByteRegister);
114  auto secondByte = readL3G4200DRegister(zAxisSecondByteRegister);
115 
116  static constexpr auto kBitsInByte = 8;
117 
118  return static_cast<int16_t>((firstByte << kBitsInByte) | secondByte);
119 }
120 
121 int GY50::readL3G4200DRegister(uint8_t registerAddress)
122 {
123  mRuntime.i2cBeginTransmission(kGyroAddress);
124  mRuntime.i2cWrite(registerAddress);
125  mRuntime.i2cEndTransmission();
126  mRuntime.i2cRequestFrom(kGyroAddress, 1);
127 
128  return mRuntime.i2cAvailable() != 0 ? mRuntime.i2cRead() : 0;
129 }
130 
131 void GY50::writeL3G4200DRegister(uint8_t registerAddress, uint8_t value)
132 {
133  mRuntime.i2cBeginTransmission(kGyroAddress);
134  mRuntime.i2cWrite(registerAddress);
135  mRuntime.i2cWrite(value);
136  mRuntime.i2cEndTransmission();
137 }
Runtime::i2cEndTransmission
virtual uint8_t i2cEndTransmission()=0
Ends a transmission to an I2C device equivalent of Wire.endTransmission in Arduino.
Runtime::i2cInit
virtual void i2cInit()=0
Initialize I2C bus as master, equivalent of Wire.begin in Arduino.
Runtime::delayMillis
virtual void delayMillis(unsigned long milliseconds)=0
Block the execution for the specified number of milliseconds, equivalent of delay in Arduino.
GY50::GY50
GY50(Runtime &runtime, int offset, unsigned long samplingInterval=smartcarlib::constants::gy50::kDefaultSamplingInterval)
Constructs a GY50 gyroscope.
Definition: GY50.cpp:21
Runtime
Definition: Runtime.hpp:35
GY50.hpp
smartcarlib::utils::getAbsolute
constexpr AnyNumber getAbsolute(AnyNumber number)
Gets the absolute of the supplied number.
Definition: Utilities.hpp:24
Runtime::i2cBeginTransmission
virtual void i2cBeginTransmission(uint8_t address)=0
Initiate a transmission to the specified I2C slave device, equivalent of Wire.beginTransmission in Ar...
Runtime::i2cRead
virtual int i2cRead()=0
Reads a byte from I2C bus, equivalent of Wire.read in Arduino.
smartcarlib::constants::gy50
Definition: GY50.hpp:24
Runtime::i2cRequestFrom
virtual uint8_t i2cRequestFrom(uint8_t address, uint8_t numberOfBytes)=0
Request a number of bytes from the specified I2C slave, equivalent of Wire.requestFrom in Arduino.
smartcarlib::utils
Definition: Utilities.hpp:9
Runtime::currentTimeMillis
virtual unsigned long currentTimeMillis()=0
Gets the amount of milliseconds since the microcontroller started running, equivalent of millis in Ar...
GY50::getOffset
int getOffset(int measurements=smartcarlib::constants::gy50::kDefaultCalibrationMeasurements)
Get the sensor's offset which is the value the sensor returns when still.
Definition: GY50.cpp:89
Runtime::i2cAvailable
virtual int i2cAvailable()=0
Gets the number of bytes available to be retrieved, equivalent of Wire.availableO in Arduino.
GY50::getHeading
int getHeading() override
Returns the current heading of the vehicle.
Definition: GY50.cpp:31
smartcarlib::constants::sr04::kError
const unsigned int kError
Definition: SR04.hpp:24
GY50::update
void update() override
Updates the sensor's readings.
Definition: GY50.cpp:40
Runtime::i2cWrite
virtual size_t i2cWrite(uint8_t value)=0
Send the specified byte via i2c, equivalent of Wire.write in Arduino.