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