blob: 26767432f5596b347cfc5776a6a325b520dc4ca5 [file] [log] [blame]
Brian Silvermanf7f267a2017-02-04 16:16:08 -08001#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
2
3#include "ctre/PCM.h"
4#include "FRC_NetworkCommunication/CANSessionMux.h"
5#include <string.h> // memset
6/* This can be a constant, as long as nobody needs to update solenoids within
7 1/50 of a second. */
8static const INT32 kCANPeriod = 20;
9
10#define STATUS_1 0x9041400
11#define STATUS_SOL_FAULTS 0x9041440
12#define STATUS_DEBUG 0x9041480
13
14#define EXPECTED_RESPONSE_TIMEOUT_MS (50)
15#define GET_PCM_STATUS() CtreCanNode::recMsg<PcmStatus_t> rx = GetRx<PcmStatus_t> (STATUS_1|GetDeviceNumber(),EXPECTED_RESPONSE_TIMEOUT_MS)
16#define GET_PCM_SOL_FAULTS() CtreCanNode::recMsg<PcmStatusFault_t> rx = GetRx<PcmStatusFault_t> (STATUS_SOL_FAULTS|GetDeviceNumber(),EXPECTED_RESPONSE_TIMEOUT_MS)
17#define GET_PCM_DEBUG() CtreCanNode::recMsg<PcmDebug_t> rx = GetRx<PcmDebug_t> (STATUS_DEBUG|GetDeviceNumber(),EXPECTED_RESPONSE_TIMEOUT_MS)
18
19#define CONTROL_1 0x09041C00 /* PCM_Control */
20#define CONTROL_2 0x09041C40 /* PCM_SupplemControl */
21#define CONTROL_3 0x09041C80 /* PcmControlSetOneShotDur_t */
22
23/* encoder/decoders */
24typedef struct _PcmStatus_t{
25 /* Byte 0 */
26 unsigned SolenoidBits:8;
27 /* Byte 1 */
28 unsigned compressorOn:1;
29 unsigned stickyFaultFuseTripped:1;
30 unsigned stickyFaultCompCurrentTooHigh:1;
31 unsigned faultFuseTripped:1;
32 unsigned faultCompCurrentTooHigh:1;
33 unsigned faultHardwareFailure:1;
34 unsigned isCloseloopEnabled:1;
35 unsigned pressureSwitchEn:1;
36 /* Byte 2*/
37 unsigned battVoltage:8;
38 /* Byte 3 */
39 unsigned solenoidVoltageTop8:8;
40 /* Byte 4 */
41 unsigned compressorCurrentTop6:6;
42 unsigned solenoidVoltageBtm2:2;
43 /* Byte 5 */
44 unsigned StickyFault_dItooHigh :1;
45 unsigned Fault_dItooHigh :1;
46 unsigned moduleEnabled:1;
47 unsigned closedLoopOutput:1;
48 unsigned compressorCurrentBtm4:4;
49 /* Byte 6 */
50 unsigned tokenSeedTop8:8;
51 /* Byte 7 */
52 unsigned tokenSeedBtm8:8;
53}PcmStatus_t;
54
55typedef struct _PcmControl_t{
56 /* Byte 0 */
57 unsigned tokenTop8:8;
58 /* Byte 1 */
59 unsigned tokenBtm8:8;
60 /* Byte 2 */
61 unsigned solenoidBits:8;
62 /* Byte 3*/
63 unsigned reserved:4;
64 unsigned closeLoopOutput:1;
65 unsigned compressorOn:1;
66 unsigned closedLoopEnable:1;
67 unsigned clearStickyFaults:1;
68 /* Byte 4 */
69 unsigned OneShotField_h8:8;
70 /* Byte 5 */
71 unsigned OneShotField_l8:8;
72}PcmControl_t;
73
74typedef struct _PcmControlSetOneShotDur_t{
75 uint8_t sol10MsPerUnit[8];
76}PcmControlSetOneShotDur_t;
77
78typedef struct _PcmStatusFault_t{
79 /* Byte 0 */
80 unsigned SolenoidBlacklist:8;
81 /* Byte 1 */
82 unsigned reserved_bit0 :1;
83 unsigned reserved_bit1 :1;
84 unsigned reserved_bit2 :1;
85 unsigned reserved_bit3 :1;
86 unsigned StickyFault_CompNoCurrent :1;
87 unsigned Fault_CompNoCurrent :1;
88 unsigned StickyFault_SolenoidJumper :1;
89 unsigned Fault_SolenoidJumper :1;
90}PcmStatusFault_t;
91
92typedef struct _PcmDebug_t{
93 unsigned tokFailsTop8:8;
94 unsigned tokFailsBtm8:8;
95 unsigned lastFailedTokTop8:8;
96 unsigned lastFailedTokBtm8:8;
97 unsigned tokSuccessTop8:8;
98 unsigned tokSuccessBtm8:8;
99}PcmDebug_t;
100
101
102/* PCM Constructor - Clears all vars, establishes default settings, starts PCM background process
103 *
104 * @Return - void
105 *
106 * @Param - deviceNumber - Device ID of PCM to be controlled
107 */
108PCM::PCM(UINT8 deviceNumber): CtreCanNode(deviceNumber)
109{
110 RegisterRx(STATUS_1 | deviceNumber );
111 RegisterRx(STATUS_SOL_FAULTS | deviceNumber );
112 RegisterRx(STATUS_DEBUG | deviceNumber );
113 RegisterTx(CONTROL_1 | deviceNumber, kCANPeriod);
114 /* enable close loop */
115 SetClosedLoopControl(1);
116}
117/* PCM D'tor
118 */
119PCM::~PCM()
120{
121
122}
123
124/* Set PCM solenoid state
125 *
126 * @Return - CTR_Code - Error code (if any) for setting solenoid
127 *
128 * @Param - idx - ID of solenoid (0-7)
129 * @Param - en - Enable / Disable identified solenoid
130 */
131CTR_Code PCM::SetSolenoid(unsigned char idx, bool en)
132{
133 CtreCanNode::txTask<PcmControl_t> toFill = GetTx<PcmControl_t>(CONTROL_1 | GetDeviceNumber());
134 if(toFill.IsEmpty())return CTR_UnexpectedArbId;
135 if (en)
136 toFill->solenoidBits |= (1ul << (idx));
137 else
138 toFill->solenoidBits &= ~(1ul << (idx));
139 FlushTx(toFill);
140 return CTR_OKAY;
141}
142
143/* Set all PCM solenoid states
144 *
145 * @Return - CTR_Code - Error code (if any) for setting solenoids
146 * @Param - state Bitfield to set all solenoids to
147 */
148CTR_Code PCM::SetAllSolenoids(UINT8 state) {
149 CtreCanNode::txTask<PcmControl_t> toFill = GetTx<PcmControl_t>(CONTROL_1 | GetDeviceNumber());
150 if(toFill.IsEmpty())return CTR_UnexpectedArbId;
151 toFill->solenoidBits = state;
152 FlushTx(toFill);
153 return CTR_OKAY;
154}
155
156/* Clears PCM sticky faults (indicators of past faults
157 *
158 * @Return - CTR_Code - Error code (if any) for setting solenoid
159 *
160 * @Param - clr - Clear / do not clear faults
161 */
162CTR_Code PCM::ClearStickyFaults()
163{
164 int32_t status = 0;
165 uint8_t pcmSupplemControl[] = { 0, 0, 0, 0x80 }; /* only bit set is ClearStickyFaults */
166 FRC_NetworkCommunication_CANSessionMux_sendMessage(CONTROL_2 | GetDeviceNumber(), pcmSupplemControl, sizeof(pcmSupplemControl), 0, &status);
167 if(status)
168 return CTR_TxFailed;
169 return CTR_OKAY;
170}
171
172/* Enables PCM Closed Loop Control of Compressor via pressure switch
173 *
174 * @Return - CTR_Code - Error code (if any) for setting solenoid
175 *
176 * @Param - en - Enable / Disable Closed Loop Control
177 */
178CTR_Code PCM::SetClosedLoopControl(bool en)
179{
180 CtreCanNode::txTask<PcmControl_t> toFill = GetTx<PcmControl_t>(CONTROL_1 | GetDeviceNumber());
181 if(toFill.IsEmpty())return CTR_UnexpectedArbId;
182 toFill->closedLoopEnable = en;
183 FlushTx(toFill);
184 return CTR_OKAY;
185}
186/* Get solenoid Blacklist status
187 * @Return - CTR_Code - Error code (if any)
188 * @Param - idx - ID of solenoid [0,7] to fire one shot pulse.
189 */
190CTR_Code PCM::FireOneShotSolenoid(UINT8 idx)
191{
192 CtreCanNode::txTask<PcmControl_t> toFill = GetTx<PcmControl_t>(CONTROL_1 | GetDeviceNumber());
193 if(toFill.IsEmpty())return CTR_UnexpectedArbId;
194 /* grab field as it is now */
195 uint16_t oneShotField;
196 oneShotField = toFill->OneShotField_h8;
197 oneShotField <<= 8;
198 oneShotField |= toFill->OneShotField_l8;
199 /* get the caller's channel */
200 uint16_t shift = 2*idx;
201 uint16_t mask = 3; /* two bits wide */
202 uint8_t chBits = (oneShotField >> shift) & mask;
203 /* flip it */
204 chBits = (chBits)%3 + 1;
205 /* clear out 2bits for this channel*/
206 oneShotField &= ~(mask << shift);
207 /* put new field in */
208 oneShotField |= chBits << shift;
209 /* apply field as it is now */
210 toFill->OneShotField_h8 = oneShotField >> 8;
211 toFill->OneShotField_l8 = oneShotField;
212 FlushTx(toFill);
213 return CTR_OKAY;
214}
215/* Configure the pulse width of a solenoid channel for one-shot pulse.
216 * Preprogrammed pulsewidth is 10ms resolute and can be between 20ms and 5.1s.
217 * @Return - CTR_Code - Error code (if any)
218 * @Param - idx - ID of solenoid [0,7] to configure.
219 * @Param - durMs - pulse width in ms.
220 */
221CTR_Code PCM::SetOneShotDurationMs(UINT8 idx,uint32_t durMs)
222{
223 /* sanity check caller's param */
224 if(idx > 7)
225 return CTR_InvalidParamValue;
226 /* get latest tx frame */
227 CtreCanNode::txTask<PcmControlSetOneShotDur_t> toFill = GetTx<PcmControlSetOneShotDur_t>(CONTROL_3 | GetDeviceNumber());
228 if(toFill.IsEmpty()){
229 /* only send this out if caller wants to do one-shots */
230 RegisterTx(CONTROL_3 | _deviceNumber, kCANPeriod);
231 /* grab it */
232 toFill = GetTx<PcmControlSetOneShotDur_t>(CONTROL_3 | GetDeviceNumber());
233 }
234 toFill->sol10MsPerUnit[idx] = std::min(durMs/10,(uint32_t)0xFF);
235 /* apply the new data bytes */
236 FlushTx(toFill);
237 return CTR_OKAY;
238}
239
240/* Get solenoid state
241 *
242 * @Return - True/False - True if solenoid enabled, false otherwise
243 *
244 * @Param - idx - ID of solenoid (0-7) to return status of
245 */
246CTR_Code PCM::GetSolenoid(UINT8 idx, bool &status)
247{
248 GET_PCM_STATUS();
249 status = (rx->SolenoidBits & (1ul<<(idx)) ) ? 1 : 0;
250 return rx.err;
251}
252
253/* Get solenoid state for all solenoids on the PCM
254 *
255 * @Return - Bitfield of solenoid states
256 */
257CTR_Code PCM::GetAllSolenoids(UINT8 &status)
258{
259 GET_PCM_STATUS();
260 status = rx->SolenoidBits;
261 return rx.err;
262}
263
264/* Get pressure switch state
265 *
266 * @Return - True/False - True if pressure adequate, false if low
267 */
268CTR_Code PCM::GetPressure(bool &status)
269{
270 GET_PCM_STATUS();
271 status = (rx->pressureSwitchEn ) ? 1 : 0;
272 return rx.err;
273}
274
275/* Get compressor state
276 *
277 * @Return - True/False - True if enabled, false if otherwise
278 */
279CTR_Code PCM::GetCompressor(bool &status)
280{
281 GET_PCM_STATUS();
282 status = (rx->compressorOn);
283 return rx.err;
284}
285
286/* Get closed loop control state
287 *
288 * @Return - True/False - True if closed loop enabled, false if otherwise
289 */
290CTR_Code PCM::GetClosedLoopControl(bool &status)
291{
292 GET_PCM_STATUS();
293 status = (rx->isCloseloopEnabled);
294 return rx.err;
295}
296
297/* Get compressor current draw
298 *
299 * @Return - Amperes - Compressor current
300 */
301CTR_Code PCM::GetCompressorCurrent(float &status)
302{
303 GET_PCM_STATUS();
304 uint32_t temp =(rx->compressorCurrentTop6);
305 temp <<= 4;
306 temp |= rx->compressorCurrentBtm4;
307 status = temp * 0.03125; /* 5.5 fixed pt value in Amps */
308 return rx.err;
309}
310
311/* Get voltage across solenoid rail
312 *
313 * @Return - Volts - Voltage across solenoid rail
314 */
315CTR_Code PCM::GetSolenoidVoltage(float &status)
316{
317 GET_PCM_STATUS();
318 uint32_t raw =(rx->solenoidVoltageTop8);
319 raw <<= 2;
320 raw |= rx->solenoidVoltageBtm2;
321 status = (double) raw * 0.03125; /* 5.5 fixed pt value in Volts */
322 return rx.err;
323}
324
325/* Get hardware fault value
326 *
327 * @Return - True/False - True if hardware failure detected, false if otherwise
328 */
329CTR_Code PCM::GetHardwareFault(bool &status)
330{
331 GET_PCM_STATUS();
332 status = rx->faultHardwareFailure;
333 return rx.err;
334}
335
336/* Get compressor fault value
337 *
338 * @Return - True/False - True if shorted compressor detected, false if otherwise
339 */
340CTR_Code PCM::GetCompressorCurrentTooHighFault(bool &status)
341{
342 GET_PCM_STATUS();
343 status = rx->faultCompCurrentTooHigh;
344 return rx.err;
345}
346CTR_Code PCM::GetCompressorShortedStickyFault(bool &status)
347{
348 GET_PCM_STATUS();
349 status = rx->StickyFault_dItooHigh;
350 return rx.err;
351}
352CTR_Code PCM::GetCompressorShortedFault(bool &status)
353{
354 GET_PCM_STATUS();
355 status = rx->Fault_dItooHigh;
356 return rx.err;
357}
358CTR_Code PCM::GetCompressorNotConnectedStickyFault(bool &status)
359{
360 GET_PCM_SOL_FAULTS();
361 status = rx->StickyFault_CompNoCurrent;
362 return rx.err;
363}
364CTR_Code PCM::GetCompressorNotConnectedFault(bool &status)
365{
366 GET_PCM_SOL_FAULTS();
367 status = rx->Fault_CompNoCurrent;
368 return rx.err;
369}
370
371/* Get solenoid fault value
372 *
373 * @Return - True/False - True if shorted solenoid detected, false if otherwise
374 */
375CTR_Code PCM::GetSolenoidFault(bool &status)
376{
377 GET_PCM_STATUS();
378 status = rx->faultFuseTripped;
379 return rx.err;
380}
381
382/* Get compressor sticky fault value
383 *
384 * @Return - True/False - True if solenoid had previously been shorted
385 * (and sticky fault was not cleared), false if otherwise
386 */
387CTR_Code PCM::GetCompressorCurrentTooHighStickyFault(bool &status)
388{
389 GET_PCM_STATUS();
390 status = rx->stickyFaultCompCurrentTooHigh;
391 return rx.err;
392}
393
394/* Get solenoid sticky fault value
395 *
396 * @Return - True/False - True if compressor had previously been shorted
397 * (and sticky fault was not cleared), false if otherwise
398 */
399CTR_Code PCM::GetSolenoidStickyFault(bool &status)
400{
401 GET_PCM_STATUS();
402 status = rx->stickyFaultFuseTripped;
403 return rx.err;
404}
405/* Get battery voltage
406 *
407 * @Return - Volts - Voltage across PCM power ports
408 */
409CTR_Code PCM::GetBatteryVoltage(float &status)
410{
411 GET_PCM_STATUS();
412 status = (float)rx->battVoltage * 0.05 + 4.0; /* 50mV per unit plus 4V. */
413 return rx.err;
414}
415/* Return status of module enable/disable
416 *
417 * @Return - bool - Returns TRUE if PCM is enabled, FALSE if disabled
418 */
419CTR_Code PCM::isModuleEnabled(bool &status)
420{
421 GET_PCM_STATUS();
422 status = rx->moduleEnabled;
423 return rx.err;
424}
425/* Get number of total failed PCM Control Frame
426 *
427 * @Return - Failed Control Frames - Number of failed control frames (tokenization fails)
428 *
429 * @WARNING - Return only valid if [SeekDebugFrames] is enabled
430 * See function SeekDebugFrames
431 * See function EnableSeekDebugFrames
432 */
433CTR_Code PCM::GetNumberOfFailedControlFrames(UINT16 &status)
434{
435 GET_PCM_DEBUG();
436 status = rx->tokFailsTop8;
437 status <<= 8;
438 status |= rx->tokFailsBtm8;
439 return rx.err;
440}
441/* Get raw Solenoid Blacklist
442 *
443 * @Return - BINARY - Raw binary breakdown of Solenoid Blacklist
444 * BIT7 = Solenoid 1, BIT6 = Solenoid 2, etc.
445 *
446 * @WARNING - Return only valid if [SeekStatusFaultFrames] is enabled
447 * See function SeekStatusFaultFrames
448 * See function EnableSeekStatusFaultFrames
449 */
450CTR_Code PCM::GetSolenoidBlackList(UINT8 &status)
451{
452 GET_PCM_SOL_FAULTS();
453 status = rx->SolenoidBlacklist;
454 return rx.err;
455}
456/* Get solenoid Blacklist status
457 * - Blacklisted solenoids cannot be enabled until PCM is power cycled
458 *
459 * @Return - True/False - True if Solenoid is blacklisted, false if otherwise
460 *
461 * @Param - idx - ID of solenoid [0,7]
462 *
463 * @WARNING - Return only valid if [SeekStatusFaultFrames] is enabled
464 * See function SeekStatusFaultFrames
465 * See function EnableSeekStatusFaultFrames
466 */
467CTR_Code PCM::IsSolenoidBlacklisted(UINT8 idx, bool &status)
468{
469 GET_PCM_SOL_FAULTS();
470 status = (rx->SolenoidBlacklist & (1ul<<(idx)) )? 1 : 0;
471 return rx.err;
472}
473//------------------ C interface --------------------------------------------//
474extern "C" {
475 void * c_PCM_Init(void) {
476 return new PCM();
477 }
478 CTR_Code c_SetSolenoid(void * handle, unsigned char idx, INT8 param) {
479 return ((PCM*) handle)->SetSolenoid(idx, param);
480 }
481 CTR_Code c_SetAllSolenoids(void * handle, UINT8 state) {
482 return ((PCM*) handle)->SetAllSolenoids(state);
483 }
484 CTR_Code c_SetClosedLoopControl(void * handle, INT8 param) {
485 return ((PCM*) handle)->SetClosedLoopControl(param);
486 }
487 CTR_Code c_ClearStickyFaults(void * handle, INT8 param) {
488 return ((PCM*) handle)->ClearStickyFaults();
489 }
490 CTR_Code c_GetSolenoid(void * handle, UINT8 idx, INT8 * status) {
491 bool bstatus;
492 CTR_Code retval = ((PCM*) handle)->GetSolenoid(idx, bstatus);
493 *status = bstatus;
494 return retval;
495 }
496 CTR_Code c_GetAllSolenoids(void * handle, UINT8 * status) {
497 return ((PCM*) handle)->GetAllSolenoids(*status);
498 }
499 CTR_Code c_GetPressure(void * handle, INT8 * status) {
500 bool bstatus;
501 CTR_Code retval = ((PCM*) handle)->GetPressure(bstatus);
502 *status = bstatus;
503 return retval;
504 }
505 CTR_Code c_GetCompressor(void * handle, INT8 * status) {
506 bool bstatus;
507 CTR_Code retval = ((PCM*) handle)->GetCompressor(bstatus);
508 *status = bstatus;
509 return retval;
510 }
511 CTR_Code c_GetClosedLoopControl(void * handle, INT8 * status) {
512 bool bstatus;
513 CTR_Code retval = ((PCM*) handle)->GetClosedLoopControl(bstatus);
514 *status = bstatus;
515 return retval;
516 }
517 CTR_Code c_GetCompressorCurrent(void * handle, float * status) {
518 CTR_Code retval = ((PCM*) handle)->GetCompressorCurrent(*status);
519 return retval;
520 }
521 CTR_Code c_GetSolenoidVoltage(void * handle, float*status) {
522 return ((PCM*) handle)->GetSolenoidVoltage(*status);
523 }
524 CTR_Code c_GetHardwareFault(void * handle, INT8*status) {
525 bool bstatus;
526 CTR_Code retval = ((PCM*) handle)->GetHardwareFault(bstatus);
527 *status = bstatus;
528 return retval;
529 }
530 CTR_Code c_GetCompressorFault(void * handle, INT8*status) {
531 bool bstatus;
532 CTR_Code retval = ((PCM*) handle)->GetCompressorCurrentTooHighFault(bstatus);
533 *status = bstatus;
534 return retval;
535 }
536 CTR_Code c_GetSolenoidFault(void * handle, INT8*status) {
537 bool bstatus;
538 CTR_Code retval = ((PCM*) handle)->GetSolenoidFault(bstatus);
539 *status = bstatus;
540 return retval;
541 }
542 CTR_Code c_GetCompressorStickyFault(void * handle, INT8*status) {
543 bool bstatus;
544 CTR_Code retval = ((PCM*) handle)->GetCompressorCurrentTooHighStickyFault(bstatus);
545 *status = bstatus;
546 return retval;
547 }
548 CTR_Code c_GetSolenoidStickyFault(void * handle, INT8*status) {
549 bool bstatus;
550 CTR_Code retval = ((PCM*) handle)->GetSolenoidStickyFault(bstatus);
551 *status = bstatus;
552 return retval;
553 }
554 CTR_Code c_GetBatteryVoltage(void * handle, float*status) {
555 CTR_Code retval = ((PCM*) handle)->GetBatteryVoltage(*status);
556 return retval;
557 }
558 void c_SetDeviceNumber_PCM(void * handle, UINT8 deviceNumber) {
559 }
560 CTR_Code c_GetNumberOfFailedControlFrames(void * handle, UINT16*status) {
561 return ((PCM*) handle)->GetNumberOfFailedControlFrames(*status);
562 }
563 CTR_Code c_GetSolenoidBlackList(void * handle, UINT8 *status) {
564 return ((PCM*) handle)->GetSolenoidBlackList(*status);
565 }
566 CTR_Code c_IsSolenoidBlacklisted(void * handle, UINT8 idx, INT8*status) {
567 bool bstatus;
568 CTR_Code retval = ((PCM*) handle)->IsSolenoidBlacklisted(idx, bstatus);
569 *status = bstatus;
570 return retval;
571 }
572}