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