blob: 0895c9c0846ae467022ae6016cc5df24c171db92 [file] [log] [blame]
Brian Silverman26e4e522015-12-17 01:56:40 -05001#include "HAL/Accelerometer.hpp"
2
3#include "ChipObject.h"
4#include <stdint.h>
5#include <stdio.h>
6#include <assert.h>
7
8// The 7-bit I2C address with a 0 "send" bit
9static const uint8_t kSendAddress = (0x1c << 1) | 0;
10
11// The 7-bit I2C address with a 1 "receive" bit
12static const uint8_t kReceiveAddress = (0x1c << 1) | 1;
13
14static const uint8_t kControlTxRx = 1;
15static const uint8_t kControlStart = 2;
16static const uint8_t kControlStop = 4;
17
18static tAccel *accel = 0;
19static AccelerometerRange accelerometerRange;
20
21// Register addresses
22enum Register {
23 kReg_Status = 0x00,
24 kReg_OutXMSB = 0x01,
25 kReg_OutXLSB = 0x02,
26 kReg_OutYMSB = 0x03,
27 kReg_OutYLSB = 0x04,
28 kReg_OutZMSB = 0x05,
29 kReg_OutZLSB = 0x06,
30 kReg_Sysmod = 0x0B,
31 kReg_IntSource = 0x0C,
32 kReg_WhoAmI = 0x0D,
33 kReg_XYZDataCfg = 0x0E,
34 kReg_HPFilterCutoff = 0x0F,
35 kReg_PLStatus = 0x10,
36 kReg_PLCfg = 0x11,
37 kReg_PLCount = 0x12,
38 kReg_PLBfZcomp = 0x13,
39 kReg_PLThsReg = 0x14,
40 kReg_FFMtCfg = 0x15,
41 kReg_FFMtSrc = 0x16,
42 kReg_FFMtThs = 0x17,
43 kReg_FFMtCount = 0x18,
44 kReg_TransientCfg = 0x1D,
45 kReg_TransientSrc = 0x1E,
46 kReg_TransientThs = 0x1F,
47 kReg_TransientCount = 0x20,
48 kReg_PulseCfg = 0x21,
49 kReg_PulseSrc = 0x22,
50 kReg_PulseThsx = 0x23,
51 kReg_PulseThsy = 0x24,
52 kReg_PulseThsz = 0x25,
53 kReg_PulseTmlt = 0x26,
54 kReg_PulseLtcy = 0x27,
55 kReg_PulseWind = 0x28,
56 kReg_ASlpCount = 0x29,
57 kReg_CtrlReg1 = 0x2A,
58 kReg_CtrlReg2 = 0x2B,
59 kReg_CtrlReg3 = 0x2C,
60 kReg_CtrlReg4 = 0x2D,
61 kReg_CtrlReg5 = 0x2E,
62 kReg_OffX = 0x2F,
63 kReg_OffY = 0x30,
64 kReg_OffZ = 0x31
65};
66
67extern "C" uint32_t getFPGATime(int32_t *status);
68
69static void writeRegister(Register reg, uint8_t data);
70static uint8_t readRegister(Register reg);
71
72/**
73 * Initialize the accelerometer.
74 */
75static void initializeAccelerometer() {
76 int32_t status;
77
78 if(!accel) {
79 accel = tAccel::create(&status);
80
81 // Enable I2C
82 accel->writeCNFG(1, &status);
83
84 // Set the counter to 100 kbps
85 accel->writeCNTR(213, &status);
86
87 // The device identification number should be 0x2a
88 assert(readRegister(kReg_WhoAmI) == 0x2a);
89 }
90}
91
92static void writeRegister(Register reg, uint8_t data) {
93 int32_t status = 0;
94 uint32_t initialTime;
95
96 accel->writeADDR(kSendAddress, &status);
97
98 // Send a start transmit/receive message with the register address
99 accel->writeCNTL(kControlStart | kControlTxRx, &status);
100 accel->writeDATO(reg, &status);
101 accel->strobeGO(&status);
102
103 // Execute and wait until it's done (up to a millisecond)
104 initialTime = getFPGATime(&status);
105 while(accel->readSTAT(&status) & 1) {
106 if(getFPGATime(&status) > initialTime + 1000) break;
107 }
108
109 // Send a stop transmit/receive message with the data
110 accel->writeCNTL(kControlStop | kControlTxRx, &status);
111 accel->writeDATO(data, &status);
112 accel->strobeGO(&status);
113
114 // Execute and wait until it's done (up to a millisecond)
115 initialTime = getFPGATime(&status);
116 while(accel->readSTAT(&status) & 1) {
117 if(getFPGATime(&status) > initialTime + 1000) break;
118 }
119
120 fflush(stdout);
121}
122
123static uint8_t readRegister(Register reg) {
124 int32_t status = 0;
125 uint32_t initialTime;
126
127 // Send a start transmit/receive message with the register address
128 accel->writeADDR(kSendAddress, &status);
129 accel->writeCNTL(kControlStart | kControlTxRx, &status);
130 accel->writeDATO(reg, &status);
131 accel->strobeGO(&status);
132
133 // Execute and wait until it's done (up to a millisecond)
134 initialTime = getFPGATime(&status);
135 while(accel->readSTAT(&status) & 1) {
136 if(getFPGATime(&status) > initialTime + 1000) break;
137 }
138
139 // Receive a message with the data and stop
140 accel->writeADDR(kReceiveAddress, &status);
141 accel->writeCNTL(kControlStart | kControlStop | kControlTxRx, &status);
142 accel->strobeGO(&status);
143
144 // Execute and wait until it's done (up to a millisecond)
145 initialTime = getFPGATime(&status);
146 while(accel->readSTAT(&status) & 1) {
147 if(getFPGATime(&status) > initialTime + 1000) break;
148 }
149
150 fflush(stdout);
151
152 return accel->readDATI(&status);
153}
154
155/**
156 * Convert a 12-bit raw acceleration value into a scaled double in units of
157 * 1 g-force, taking into account the accelerometer range.
158 */
159static double unpackAxis(int16_t raw) {
160 // The raw value is actually 12 bits, not 16, so we need to propogate the
161 // 2's complement sign bit to the unused 4 bits for this to work with
162 // negative numbers.
163 raw <<= 4;
164 raw >>= 4;
165
166 switch(accelerometerRange) {
167 case kRange_2G: return raw / 1024.0;
168 case kRange_4G: return raw / 512.0;
169 case kRange_8G: return raw / 256.0;
170 default: return 0.0;
171 }
172}
173
174/**
175 * Set the accelerometer to active or standby mode. It must be in standby
176 * mode to change any configuration.
177 */
178void setAccelerometerActive(bool active) {
179 initializeAccelerometer();
180
181 uint8_t ctrlReg1 = readRegister(kReg_CtrlReg1);
182 ctrlReg1 &= ~1; // Clear the existing active bit
183 writeRegister(kReg_CtrlReg1, ctrlReg1 | (active? 1 : 0));
184}
185
186/**
187 * Set the range of values that can be measured (either 2, 4, or 8 g-forces).
188 * The accelerometer should be in standby mode when this is called.
189 */
190void setAccelerometerRange(AccelerometerRange range) {
191 initializeAccelerometer();
192
193 accelerometerRange = range;
194
195 uint8_t xyzDataCfg = readRegister(kReg_XYZDataCfg);
196 xyzDataCfg &= ~3; // Clear the existing two range bits
197 writeRegister(kReg_XYZDataCfg, xyzDataCfg | range);
198}
199
200/**
201 * Get the x-axis acceleration
202 *
203 * This is a floating point value in units of 1 g-force
204 */
205double getAccelerometerX() {
206 initializeAccelerometer();
207
208 int raw = (readRegister(kReg_OutXMSB) << 4) | (readRegister(kReg_OutXLSB) >> 4);
209 return unpackAxis(raw);
210}
211
212/**
213 * Get the y-axis acceleration
214 *
215 * This is a floating point value in units of 1 g-force
216 */
217double getAccelerometerY() {
218 initializeAccelerometer();
219
220 int raw = (readRegister(kReg_OutYMSB) << 4) | (readRegister(kReg_OutYLSB) >> 4);
221 return unpackAxis(raw);
222}
223
224/**
225 * Get the z-axis acceleration
226 *
227 * This is a floating point value in units of 1 g-force
228 */
229double getAccelerometerZ() {
230 initializeAccelerometer();
231
232 int raw = (readRegister(kReg_OutZMSB) << 4) | (readRegister(kReg_OutZLSB) >> 4);
233 return unpackAxis(raw);
234}