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