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