blob: 79c1ae39833bb1d582dd6fc089489c90534a4433 [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.
4
5#include "hal/CTREPCM.h"
6
7#include <fmt/format.h>
8
9#include "HALInitializer.h"
10#include "HALInternal.h"
11#include "PortsInternal.h"
12#include "hal/CANAPI.h"
13#include "hal/Errors.h"
14#include "hal/handles/IndexedHandleResource.h"
15
16using namespace hal;
17
18static constexpr HAL_CANManufacturer manufacturer =
19 HAL_CANManufacturer::HAL_CAN_Man_kCTRE;
20
21static constexpr HAL_CANDeviceType deviceType =
22 HAL_CANDeviceType::HAL_CAN_Dev_kPneumatics;
23
24static constexpr int32_t Status1 = 0x50;
25static constexpr int32_t StatusSolFaults = 0x51;
26static constexpr int32_t StatusDebug = 0x52;
27
28static constexpr int32_t Control1 = 0x70;
29static constexpr int32_t Control2 = 0x71;
30static constexpr int32_t Control3 = 0x72;
31
32static constexpr int32_t TimeoutMs = 100;
33static constexpr int32_t SendPeriod = 20;
34
35union PcmStatus {
36 uint8_t data[8];
37 struct Bits {
38 /* Byte 0 */
39 unsigned SolenoidBits : 8;
40 /* Byte 1 */
41 unsigned compressorOn : 1;
42 unsigned stickyFaultFuseTripped : 1;
43 unsigned stickyFaultCompCurrentTooHigh : 1;
44 unsigned faultFuseTripped : 1;
45 unsigned faultCompCurrentTooHigh : 1;
46 unsigned faultHardwareFailure : 1;
47 unsigned isCloseloopEnabled : 1;
48 unsigned pressureSwitchEn : 1;
49 /* Byte 2*/
50 unsigned battVoltage : 8;
51 /* Byte 3 */
52 unsigned solenoidVoltageTop8 : 8;
53 /* Byte 4 */
54 unsigned compressorCurrentTop6 : 6;
55 unsigned solenoidVoltageBtm2 : 2;
56 /* Byte 5 */
57 unsigned StickyFault_dItooHigh : 1;
58 unsigned Fault_dItooHigh : 1;
59 unsigned moduleEnabled : 1;
60 unsigned closedLoopOutput : 1;
61 unsigned compressorCurrentBtm4 : 4;
62 /* Byte 6 */
63 unsigned tokenSeedTop8 : 8;
64 /* Byte 7 */
65 unsigned tokenSeedBtm8 : 8;
66 } bits;
67};
68
69union PcmControl {
70 uint8_t data[8];
71 struct Bits {
72 /* Byte 0 */
73 unsigned tokenTop8 : 8;
74 /* Byte 1 */
75 unsigned tokenBtm8 : 8;
76 /* Byte 2 */
77 unsigned solenoidBits : 8;
78 /* Byte 3*/
79 unsigned reserved : 4;
80 unsigned closeLoopOutput : 1;
81 unsigned compressorOn : 1;
82 unsigned closedLoopEnable : 1;
83 unsigned clearStickyFaults : 1;
84 /* Byte 4 */
85 unsigned OneShotField_h8 : 8;
86 /* Byte 5 */
87 unsigned OneShotField_l8 : 8;
88 } bits;
89};
90
91struct PcmControlSetOneShotDur {
92 uint8_t sol10MsPerUnit[8];
93};
94
95union PcmStatusFault {
96 uint8_t data[8];
97 struct Bits {
98 /* Byte 0 */
99 unsigned SolenoidDisabledList : 8;
100 /* Byte 1 */
101 unsigned reserved_bit0 : 1;
102 unsigned reserved_bit1 : 1;
103 unsigned reserved_bit2 : 1;
104 unsigned reserved_bit3 : 1;
105 unsigned StickyFault_CompNoCurrent : 1;
106 unsigned Fault_CompNoCurrent : 1;
107 unsigned StickyFault_SolenoidJumper : 1;
108 unsigned Fault_SolenoidJumper : 1;
109 } bits;
110};
111
112union PcmDebug {
113 uint8_t data[8];
114 struct Bits {
115 unsigned tokFailsTop8 : 8;
116 unsigned tokFailsBtm8 : 8;
117 unsigned lastFailedTokTop8 : 8;
118 unsigned lastFailedTokBtm8 : 8;
119 unsigned tokSuccessTop8 : 8;
120 unsigned tokSuccessBtm8 : 8;
121 } bits;
122};
123
124namespace {
125struct PCM {
126 HAL_CANHandle canHandle;
127 wpi::mutex lock;
128 std::string previousAllocation;
129 PcmControl control;
130 PcmControlSetOneShotDur oneShot;
131};
132} // namespace
133
134static IndexedHandleResource<HAL_CTREPCMHandle, PCM, kNumCTREPCMModules,
135 HAL_HandleEnum::CTREPCM>* pcmHandles;
136
137namespace hal::init {
138void InitializeCTREPCM() {
139 static IndexedHandleResource<HAL_CTREPCMHandle, PCM, kNumCTREPCMModules,
140 HAL_HandleEnum::CTREPCM>
141 pH;
142 pcmHandles = &pH;
143}
144} // namespace hal::init
145
146#define READ_PACKET(type, frame, failureValue) \
147 auto pcm = pcmHandles->Get(handle); \
148 if (pcm == nullptr) { \
149 *status = HAL_HANDLE_ERROR; \
150 return failureValue; \
151 } \
152 type pcmStatus; \
153 int32_t length = 0; \
154 uint64_t receivedTimestamp = 0; \
155 HAL_ReadCANPacketTimeout(pcm->canHandle, frame, pcmStatus.data, &length, \
156 &receivedTimestamp, TimeoutMs, status); \
157 if (*status != 0) { \
158 return failureValue; \
159 }
160
161#define READ_STATUS(failureValue) READ_PACKET(PcmStatus, Status1, failureValue)
162#define READ_SOL_FAULTS(failureValue) \
163 READ_PACKET(PcmStatusFault, StatusSolFaults, failureValue)
164
165static void SendControl(PCM* pcm, int32_t* status) {
166 HAL_WriteCANPacketRepeating(pcm->canHandle, pcm->control.data, 8, Control1,
167 SendPeriod, status);
168}
169
170extern "C" {
171
172HAL_CTREPCMHandle HAL_InitializeCTREPCM(int32_t module,
173 const char* allocationLocation,
174 int32_t* status) {
175 hal::init::CheckInit();
176
177 HAL_CTREPCMHandle handle;
178 auto pcm = pcmHandles->Allocate(module, &handle, status);
179
180 if (*status != 0) {
181 if (pcm) {
182 hal::SetLastErrorPreviouslyAllocated(status, "CTRE PCM", module,
183 pcm->previousAllocation);
184 } else {
185 hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for CTRE PCM", 0,
186 kNumCTREPCMModules, module);
187 }
188 return HAL_kInvalidHandle; // failed to allocate. Pass error back.
189 }
190
191 pcm->canHandle = HAL_InitializeCAN(manufacturer, module, deviceType, status);
192 if (*status != 0) {
193 pcmHandles->Free(handle);
194 return HAL_kInvalidHandle;
195 }
196
197 std::memset(&pcm->oneShot, 0, sizeof(pcm->oneShot));
198 std::memset(&pcm->control, 0, sizeof(pcm->control));
199
200 pcm->previousAllocation = allocationLocation ? allocationLocation : "";
201
202 // Enable closed loop control
203 HAL_SetCTREPCMClosedLoopControl(handle, true, status);
204 if (*status != 0) {
205 HAL_FreeCTREPCM(handle);
206 return HAL_kInvalidHandle;
207 }
208 return handle;
209}
210
211void HAL_FreeCTREPCM(HAL_CTREPCMHandle handle) {
212 auto pcm = pcmHandles->Get(handle);
213 if (pcm) {
214 HAL_CleanCAN(pcm->canHandle);
215 }
216 pcmHandles->Free(handle);
217}
218
219HAL_Bool HAL_CheckCTREPCMSolenoidChannel(int32_t channel) {
220 return channel < kNumCTRESolenoidChannels && channel >= 0;
221}
222
223HAL_Bool HAL_GetCTREPCMCompressor(HAL_CTREPCMHandle handle, int32_t* status) {
224 READ_STATUS(false);
225 return pcmStatus.bits.compressorOn;
226}
227
228void HAL_SetCTREPCMClosedLoopControl(HAL_CTREPCMHandle handle, HAL_Bool enabled,
229 int32_t* status) {
230 auto pcm = pcmHandles->Get(handle);
231 if (pcm == nullptr) {
232 *status = HAL_HANDLE_ERROR;
233 return;
234 }
235
James Kuszmaulcf324122023-01-14 14:07:17 -0800236 int32_t can_status = 0;
237
Austin Schuh812d0d12021-11-04 20:16:48 -0700238 std::scoped_lock lock{pcm->lock};
239 pcm->control.bits.closedLoopEnable = enabled ? 1 : 0;
James Kuszmaulcf324122023-01-14 14:07:17 -0800240 SendControl(pcm.get(), &can_status);
Austin Schuh812d0d12021-11-04 20:16:48 -0700241}
242
243HAL_Bool HAL_GetCTREPCMClosedLoopControl(HAL_CTREPCMHandle handle,
244 int32_t* status) {
245 READ_STATUS(false);
246 return pcmStatus.bits.isCloseloopEnabled;
247}
248
249HAL_Bool HAL_GetCTREPCMPressureSwitch(HAL_CTREPCMHandle handle,
250 int32_t* status) {
251 READ_STATUS(false);
252 return pcmStatus.bits.pressureSwitchEn;
253}
254
255double HAL_GetCTREPCMCompressorCurrent(HAL_CTREPCMHandle handle,
256 int32_t* status) {
257 READ_STATUS(0);
258 uint32_t result = pcmStatus.bits.compressorCurrentTop6;
259 result <<= 4;
260 result |= pcmStatus.bits.compressorCurrentBtm4;
261 return result * 0.03125; /* 5.5 fixed pt value in Amps */
262}
263
264HAL_Bool HAL_GetCTREPCMCompressorCurrentTooHighFault(HAL_CTREPCMHandle handle,
265 int32_t* status) {
266 READ_STATUS(false);
267 return pcmStatus.bits.faultCompCurrentTooHigh;
268}
269
270HAL_Bool HAL_GetCTREPCMCompressorCurrentTooHighStickyFault(
271 HAL_CTREPCMHandle handle, int32_t* status) {
272 READ_STATUS(false);
273 return pcmStatus.bits.stickyFaultCompCurrentTooHigh;
274}
275HAL_Bool HAL_GetCTREPCMCompressorShortedStickyFault(HAL_CTREPCMHandle handle,
276 int32_t* status) {
277 READ_STATUS(false);
278 return pcmStatus.bits.Fault_dItooHigh;
279}
280HAL_Bool HAL_GetCTREPCMCompressorShortedFault(HAL_CTREPCMHandle handle,
281 int32_t* status) {
282 READ_STATUS(false);
283 return pcmStatus.bits.StickyFault_dItooHigh;
284}
285HAL_Bool HAL_GetCTREPCMCompressorNotConnectedStickyFault(
286 HAL_CTREPCMHandle handle, int32_t* status) {
287 READ_SOL_FAULTS(false);
288 return pcmStatus.bits.StickyFault_CompNoCurrent;
289}
290HAL_Bool HAL_GetCTREPCMCompressorNotConnectedFault(HAL_CTREPCMHandle handle,
291 int32_t* status) {
292 READ_SOL_FAULTS(false);
293 return pcmStatus.bits.Fault_CompNoCurrent;
294}
295
296int32_t HAL_GetCTREPCMSolenoids(HAL_CTREPCMHandle handle, int32_t* status) {
297 READ_STATUS(0);
298 return pcmStatus.bits.SolenoidBits & 0xFF;
299}
300
301void HAL_SetCTREPCMSolenoids(HAL_CTREPCMHandle handle, int32_t mask,
302 int32_t values, int32_t* status) {
303 auto pcm = pcmHandles->Get(handle);
304 if (pcm == nullptr) {
305 *status = HAL_HANDLE_ERROR;
306 return;
307 }
308
309 uint8_t smallMask = mask & 0xFF;
310 uint8_t smallValues =
311 (values & 0xFF) & smallMask; // Enforce only masked values are set
312 uint8_t invertMask = ~smallMask;
313
314 std::scoped_lock lock{pcm->lock};
315 uint8_t existingValue = invertMask & pcm->control.bits.solenoidBits;
316 pcm->control.bits.solenoidBits = existingValue | smallValues;
317 SendControl(pcm.get(), status);
318}
319
320int32_t HAL_GetCTREPCMSolenoidDisabledList(HAL_CTREPCMHandle handle,
321 int32_t* status) {
322 READ_SOL_FAULTS(0);
323 return pcmStatus.bits.SolenoidDisabledList;
324}
325
326HAL_Bool HAL_GetCTREPCMSolenoidVoltageStickyFault(HAL_CTREPCMHandle handle,
327 int32_t* status) {
328 READ_STATUS(false);
329 return pcmStatus.bits.stickyFaultFuseTripped;
330}
331
332HAL_Bool HAL_GetCTREPCMSolenoidVoltageFault(HAL_CTREPCMHandle handle,
333 int32_t* status) {
334 READ_STATUS(false);
335 return pcmStatus.bits.faultFuseTripped;
336}
337
338void HAL_ClearAllCTREPCMStickyFaults(HAL_CTREPCMHandle handle,
339 int32_t* status) {
Austin Schuh75263e32022-02-22 18:05:32 -0800340 auto pcm = pcmHandles->Get(handle);
341 if (pcm == nullptr) {
342 *status = HAL_HANDLE_ERROR;
343 return;
344 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700345 uint8_t controlData[] = {0, 0, 0, 0x80};
Austin Schuh75263e32022-02-22 18:05:32 -0800346 HAL_WriteCANPacket(pcm->canHandle, controlData, sizeof(controlData), Control2,
Austin Schuh812d0d12021-11-04 20:16:48 -0700347 status);
348}
349
350void HAL_FireCTREPCMOneShot(HAL_CTREPCMHandle handle, int32_t index,
351 int32_t* status) {
352 if (index > 7 || index < 0) {
353 *status = PARAMETER_OUT_OF_RANGE;
354 hal::SetLastError(
355 status,
356 fmt::format("Only [0-7] are valid index values. Requested {}", index));
357 return;
358 }
359
360 auto pcm = pcmHandles->Get(handle);
361 if (pcm == nullptr) {
362 *status = HAL_HANDLE_ERROR;
363 return;
364 }
365
366 std::scoped_lock lock{pcm->lock};
367 uint16_t oneShotField = pcm->control.bits.OneShotField_h8;
368 oneShotField <<= 8;
369 oneShotField |= pcm->control.bits.OneShotField_l8;
370
371 uint16_t shift = 2 * index;
372 uint16_t mask = 3;
373 uint8_t chBits = (oneShotField >> shift) & mask;
374 chBits = (chBits % 3) + 1;
375 oneShotField &= ~(mask << shift);
376 oneShotField |= (chBits << shift);
377 pcm->control.bits.OneShotField_h8 = oneShotField >> 8;
378 pcm->control.bits.OneShotField_l8 = oneShotField;
379 SendControl(pcm.get(), status);
380}
381
382void HAL_SetCTREPCMOneShotDuration(HAL_CTREPCMHandle handle, int32_t index,
383 int32_t durMs, int32_t* status) {
384 if (index > 7 || index < 0) {
385 *status = PARAMETER_OUT_OF_RANGE;
386 hal::SetLastError(
387 status,
388 fmt::format("Only [0-7] are valid index values. Requested {}", index));
389 return;
390 }
391
392 auto pcm = pcmHandles->Get(handle);
393 if (pcm == nullptr) {
394 *status = HAL_HANDLE_ERROR;
395 return;
396 }
397
398 std::scoped_lock lock{pcm->lock};
James Kuszmaulcf324122023-01-14 14:07:17 -0800399 pcm->oneShot.sol10MsPerUnit[index] = (std::min)(
400 static_cast<uint32_t>(durMs) / 10, static_cast<uint32_t>(0xFF));
Austin Schuh812d0d12021-11-04 20:16:48 -0700401 HAL_WriteCANPacketRepeating(pcm->canHandle, pcm->oneShot.sol10MsPerUnit, 8,
Austin Schuh75263e32022-02-22 18:05:32 -0800402 Control3, SendPeriod, status);
Austin Schuh812d0d12021-11-04 20:16:48 -0700403}
404
405} // extern "C"