blob: 37faf696f44f5ba60066c80a14e88f2ef0a48460 [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
236 std::scoped_lock lock{pcm->lock};
237 pcm->control.bits.closedLoopEnable = enabled ? 1 : 0;
238 SendControl(pcm.get(), status);
239}
240
241HAL_Bool HAL_GetCTREPCMClosedLoopControl(HAL_CTREPCMHandle handle,
242 int32_t* status) {
243 READ_STATUS(false);
244 return pcmStatus.bits.isCloseloopEnabled;
245}
246
247HAL_Bool HAL_GetCTREPCMPressureSwitch(HAL_CTREPCMHandle handle,
248 int32_t* status) {
249 READ_STATUS(false);
250 return pcmStatus.bits.pressureSwitchEn;
251}
252
253double HAL_GetCTREPCMCompressorCurrent(HAL_CTREPCMHandle handle,
254 int32_t* status) {
255 READ_STATUS(0);
256 uint32_t result = pcmStatus.bits.compressorCurrentTop6;
257 result <<= 4;
258 result |= pcmStatus.bits.compressorCurrentBtm4;
259 return result * 0.03125; /* 5.5 fixed pt value in Amps */
260}
261
262HAL_Bool HAL_GetCTREPCMCompressorCurrentTooHighFault(HAL_CTREPCMHandle handle,
263 int32_t* status) {
264 READ_STATUS(false);
265 return pcmStatus.bits.faultCompCurrentTooHigh;
266}
267
268HAL_Bool HAL_GetCTREPCMCompressorCurrentTooHighStickyFault(
269 HAL_CTREPCMHandle handle, int32_t* status) {
270 READ_STATUS(false);
271 return pcmStatus.bits.stickyFaultCompCurrentTooHigh;
272}
273HAL_Bool HAL_GetCTREPCMCompressorShortedStickyFault(HAL_CTREPCMHandle handle,
274 int32_t* status) {
275 READ_STATUS(false);
276 return pcmStatus.bits.Fault_dItooHigh;
277}
278HAL_Bool HAL_GetCTREPCMCompressorShortedFault(HAL_CTREPCMHandle handle,
279 int32_t* status) {
280 READ_STATUS(false);
281 return pcmStatus.bits.StickyFault_dItooHigh;
282}
283HAL_Bool HAL_GetCTREPCMCompressorNotConnectedStickyFault(
284 HAL_CTREPCMHandle handle, int32_t* status) {
285 READ_SOL_FAULTS(false);
286 return pcmStatus.bits.StickyFault_CompNoCurrent;
287}
288HAL_Bool HAL_GetCTREPCMCompressorNotConnectedFault(HAL_CTREPCMHandle handle,
289 int32_t* status) {
290 READ_SOL_FAULTS(false);
291 return pcmStatus.bits.Fault_CompNoCurrent;
292}
293
294int32_t HAL_GetCTREPCMSolenoids(HAL_CTREPCMHandle handle, int32_t* status) {
295 READ_STATUS(0);
296 return pcmStatus.bits.SolenoidBits & 0xFF;
297}
298
299void HAL_SetCTREPCMSolenoids(HAL_CTREPCMHandle handle, int32_t mask,
300 int32_t values, int32_t* status) {
301 auto pcm = pcmHandles->Get(handle);
302 if (pcm == nullptr) {
303 *status = HAL_HANDLE_ERROR;
304 return;
305 }
306
307 uint8_t smallMask = mask & 0xFF;
308 uint8_t smallValues =
309 (values & 0xFF) & smallMask; // Enforce only masked values are set
310 uint8_t invertMask = ~smallMask;
311
312 std::scoped_lock lock{pcm->lock};
313 uint8_t existingValue = invertMask & pcm->control.bits.solenoidBits;
314 pcm->control.bits.solenoidBits = existingValue | smallValues;
315 SendControl(pcm.get(), status);
316}
317
318int32_t HAL_GetCTREPCMSolenoidDisabledList(HAL_CTREPCMHandle handle,
319 int32_t* status) {
320 READ_SOL_FAULTS(0);
321 return pcmStatus.bits.SolenoidDisabledList;
322}
323
324HAL_Bool HAL_GetCTREPCMSolenoidVoltageStickyFault(HAL_CTREPCMHandle handle,
325 int32_t* status) {
326 READ_STATUS(false);
327 return pcmStatus.bits.stickyFaultFuseTripped;
328}
329
330HAL_Bool HAL_GetCTREPCMSolenoidVoltageFault(HAL_CTREPCMHandle handle,
331 int32_t* status) {
332 READ_STATUS(false);
333 return pcmStatus.bits.faultFuseTripped;
334}
335
336void HAL_ClearAllCTREPCMStickyFaults(HAL_CTREPCMHandle handle,
337 int32_t* status) {
Austin Schuh75263e32022-02-22 18:05:32 -0800338 auto pcm = pcmHandles->Get(handle);
339 if (pcm == nullptr) {
340 *status = HAL_HANDLE_ERROR;
341 return;
342 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700343 uint8_t controlData[] = {0, 0, 0, 0x80};
Austin Schuh75263e32022-02-22 18:05:32 -0800344 HAL_WriteCANPacket(pcm->canHandle, controlData, sizeof(controlData), Control2,
Austin Schuh812d0d12021-11-04 20:16:48 -0700345 status);
346}
347
348void HAL_FireCTREPCMOneShot(HAL_CTREPCMHandle handle, int32_t index,
349 int32_t* status) {
350 if (index > 7 || index < 0) {
351 *status = PARAMETER_OUT_OF_RANGE;
352 hal::SetLastError(
353 status,
354 fmt::format("Only [0-7] are valid index values. Requested {}", index));
355 return;
356 }
357
358 auto pcm = pcmHandles->Get(handle);
359 if (pcm == nullptr) {
360 *status = HAL_HANDLE_ERROR;
361 return;
362 }
363
364 std::scoped_lock lock{pcm->lock};
365 uint16_t oneShotField = pcm->control.bits.OneShotField_h8;
366 oneShotField <<= 8;
367 oneShotField |= pcm->control.bits.OneShotField_l8;
368
369 uint16_t shift = 2 * index;
370 uint16_t mask = 3;
371 uint8_t chBits = (oneShotField >> shift) & mask;
372 chBits = (chBits % 3) + 1;
373 oneShotField &= ~(mask << shift);
374 oneShotField |= (chBits << shift);
375 pcm->control.bits.OneShotField_h8 = oneShotField >> 8;
376 pcm->control.bits.OneShotField_l8 = oneShotField;
377 SendControl(pcm.get(), status);
378}
379
380void HAL_SetCTREPCMOneShotDuration(HAL_CTREPCMHandle handle, int32_t index,
381 int32_t durMs, int32_t* status) {
382 if (index > 7 || index < 0) {
383 *status = PARAMETER_OUT_OF_RANGE;
384 hal::SetLastError(
385 status,
386 fmt::format("Only [0-7] are valid index values. Requested {}", index));
387 return;
388 }
389
390 auto pcm = pcmHandles->Get(handle);
391 if (pcm == nullptr) {
392 *status = HAL_HANDLE_ERROR;
393 return;
394 }
395
396 std::scoped_lock lock{pcm->lock};
397 pcm->oneShot.sol10MsPerUnit[index] =
398 (std::min)(static_cast<uint32_t>(durMs) / 10,
399 static_cast<uint32_t>(0xFF));
400 HAL_WriteCANPacketRepeating(pcm->canHandle, pcm->oneShot.sol10MsPerUnit, 8,
Austin Schuh75263e32022-02-22 18:05:32 -0800401 Control3, SendPeriod, status);
Austin Schuh812d0d12021-11-04 20:16:48 -0700402}
403
404} // extern "C"