#pragma GCC diagnostic ignored "-Wmissing-field-initializers" #include "ctre/PCM.h" #include "FRC_NetworkCommunication/CANSessionMux.h" #include // memset #include // usleep /* This can be a constant, as long as nobody needs to update solenoids within 1/50 of a second. */ static const INT32 kCANPeriod = 20; #define STATUS_1 0x9041400 #define STATUS_SOL_FAULTS 0x9041440 #define STATUS_DEBUG 0x9041480 #define EXPECTED_RESPONSE_TIMEOUT_MS (50) #define GET_PCM_STATUS() CtreCanNode::recMsg rx = GetRx (STATUS_1|GetDeviceNumber(),EXPECTED_RESPONSE_TIMEOUT_MS) #define GET_PCM_SOL_FAULTS() CtreCanNode::recMsg rx = GetRx (STATUS_SOL_FAULTS|GetDeviceNumber(),EXPECTED_RESPONSE_TIMEOUT_MS) #define GET_PCM_DEBUG() CtreCanNode::recMsg rx = GetRx (STATUS_DEBUG|GetDeviceNumber(),EXPECTED_RESPONSE_TIMEOUT_MS) #define CONTROL_1 0x09041C00 /* PCM_Control */ #define CONTROL_2 0x09041C40 /* PCM_SupplemControl */ #define CONTROL_3 0x09041C80 /* PcmControlSetOneShotDur_t */ /* encoder/decoders */ typedef struct _PcmStatus_t{ /* Byte 0 */ unsigned SolenoidBits:8; /* Byte 1 */ unsigned compressorOn:1; unsigned stickyFaultFuseTripped:1; unsigned stickyFaultCompCurrentTooHigh:1; unsigned faultFuseTripped:1; unsigned faultCompCurrentTooHigh:1; unsigned faultHardwareFailure:1; unsigned isCloseloopEnabled:1; unsigned pressureSwitchEn:1; /* Byte 2*/ unsigned battVoltage:8; /* Byte 3 */ unsigned solenoidVoltageTop8:8; /* Byte 4 */ unsigned compressorCurrentTop6:6; unsigned solenoidVoltageBtm2:2; /* Byte 5 */ unsigned StickyFault_dItooHigh :1; unsigned Fault_dItooHigh :1; unsigned moduleEnabled:1; unsigned closedLoopOutput:1; unsigned compressorCurrentBtm4:4; /* Byte 6 */ unsigned tokenSeedTop8:8; /* Byte 7 */ unsigned tokenSeedBtm8:8; }PcmStatus_t; typedef struct _PcmControl_t{ /* Byte 0 */ unsigned tokenTop8:8; /* Byte 1 */ unsigned tokenBtm8:8; /* Byte 2 */ unsigned solenoidBits:8; /* Byte 3*/ unsigned reserved:4; unsigned closeLoopOutput:1; unsigned compressorOn:1; unsigned closedLoopEnable:1; unsigned clearStickyFaults:1; /* Byte 4 */ unsigned OneShotField_h8:8; /* Byte 5 */ unsigned OneShotField_l8:8; }PcmControl_t; typedef struct _PcmControlSetOneShotDur_t{ uint8_t sol10MsPerUnit[8]; }PcmControlSetOneShotDur_t; typedef struct _PcmStatusFault_t{ /* Byte 0 */ unsigned SolenoidBlacklist:8; /* Byte 1 */ unsigned reserved_bit0 :1; unsigned reserved_bit1 :1; unsigned reserved_bit2 :1; unsigned reserved_bit3 :1; unsigned StickyFault_CompNoCurrent :1; unsigned Fault_CompNoCurrent :1; unsigned StickyFault_SolenoidJumper :1; unsigned Fault_SolenoidJumper :1; }PcmStatusFault_t; typedef struct _PcmDebug_t{ unsigned tokFailsTop8:8; unsigned tokFailsBtm8:8; unsigned lastFailedTokTop8:8; unsigned lastFailedTokBtm8:8; unsigned tokSuccessTop8:8; unsigned tokSuccessBtm8:8; }PcmDebug_t; /* PCM Constructor - Clears all vars, establishes default settings, starts PCM background process * * @Return - void * * @Param - deviceNumber - Device ID of PCM to be controlled */ PCM::PCM(UINT8 deviceNumber): CtreCanNode(deviceNumber) { RegisterRx(STATUS_1 | deviceNumber ); RegisterRx(STATUS_SOL_FAULTS | deviceNumber ); RegisterRx(STATUS_DEBUG | deviceNumber ); RegisterTx(CONTROL_1 | deviceNumber, kCANPeriod); /* enable close loop */ SetClosedLoopControl(1); } /* PCM D'tor */ PCM::~PCM() { } /* Set PCM solenoid state * * @Return - CTR_Code - Error code (if any) for setting solenoid * * @Param - idx - ID of solenoid (0-7) * @Param - en - Enable / Disable identified solenoid */ CTR_Code PCM::SetSolenoid(unsigned char idx, bool en) { CtreCanNode::txTask toFill = GetTx(CONTROL_1 | GetDeviceNumber()); if(toFill.IsEmpty())return CTR_UnexpectedArbId; if (en) toFill->solenoidBits |= (1ul << (idx)); else toFill->solenoidBits &= ~(1ul << (idx)); FlushTx(toFill); return CTR_OKAY; } /* Clears PCM sticky faults (indicators of past faults * * @Return - CTR_Code - Error code (if any) for setting solenoid * * @Param - clr - Clear / do not clear faults */ CTR_Code PCM::ClearStickyFaults() { int32_t status = 0; uint8_t pcmSupplemControl[] = { 0, 0, 0, 0x80 }; /* only bit set is ClearStickyFaults */ FRC_NetworkCommunication_CANSessionMux_sendMessage(CONTROL_2 | GetDeviceNumber(), pcmSupplemControl, sizeof(pcmSupplemControl), 0, &status); if(status) return CTR_TxFailed; return CTR_OKAY; } /* Enables PCM Closed Loop Control of Compressor via pressure switch * * @Return - CTR_Code - Error code (if any) for setting solenoid * * @Param - en - Enable / Disable Closed Loop Control */ CTR_Code PCM::SetClosedLoopControl(bool en) { CtreCanNode::txTask toFill = GetTx(CONTROL_1 | GetDeviceNumber()); if(toFill.IsEmpty())return CTR_UnexpectedArbId; toFill->closedLoopEnable = en; FlushTx(toFill); return CTR_OKAY; } /* Get solenoid Blacklist status * @Return - CTR_Code - Error code (if any) * @Param - idx - ID of solenoid [0,7] to fire one shot pulse. */ CTR_Code PCM::FireOneShotSolenoid(UINT8 idx) { CtreCanNode::txTask toFill = GetTx(CONTROL_1 | GetDeviceNumber()); if(toFill.IsEmpty())return CTR_UnexpectedArbId; /* grab field as it is now */ uint16_t oneShotField; oneShotField = toFill->OneShotField_h8; oneShotField <<= 8; oneShotField |= toFill->OneShotField_l8; /* get the caller's channel */ uint16_t shift = 2*idx; uint16_t mask = 3; /* two bits wide */ uint8_t chBits = (oneShotField >> shift) & mask; /* flip it */ chBits = (chBits)%3 + 1; /* clear out 2bits for this channel*/ oneShotField &= ~(mask << shift); /* put new field in */ oneShotField |= chBits << shift; /* apply field as it is now */ toFill->OneShotField_h8 = oneShotField >> 8; toFill->OneShotField_l8 = oneShotField; FlushTx(toFill); return CTR_OKAY; } /* Configure the pulse width of a solenoid channel for one-shot pulse. * Preprogrammed pulsewidth is 10ms resolute and can be between 20ms and 5.1s. * @Return - CTR_Code - Error code (if any) * @Param - idx - ID of solenoid [0,7] to configure. * @Param - durMs - pulse width in ms. */ CTR_Code PCM::SetOneShotDurationMs(UINT8 idx,uint32_t durMs) { /* sanity check caller's param */ if(idx > 7) return CTR_InvalidParamValue; /* get latest tx frame */ CtreCanNode::txTask toFill = GetTx(CONTROL_3 | GetDeviceNumber()); if(toFill.IsEmpty()){ /* only send this out if caller wants to do one-shots */ RegisterTx(CONTROL_3 | _deviceNumber, kCANPeriod); /* grab it */ toFill = GetTx(CONTROL_3 | GetDeviceNumber()); } toFill->sol10MsPerUnit[idx] = std::min(durMs/10,(uint32_t)0xFF); /* apply the new data bytes */ FlushTx(toFill); return CTR_OKAY; } /* Get solenoid state * * @Return - True/False - True if solenoid enabled, false otherwise * * @Param - idx - ID of solenoid (0-7) to return status of */ CTR_Code PCM::GetSolenoid(UINT8 idx, bool &status) { GET_PCM_STATUS(); status = (rx->SolenoidBits & (1ul<<(idx)) ) ? 1 : 0; return rx.err; } /* Get solenoid state for all solenoids on the PCM * * @Return - Bitfield of solenoid states */ CTR_Code PCM::GetAllSolenoids(UINT8 &status) { GET_PCM_STATUS(); status = rx->SolenoidBits; return rx.err; } /* Get pressure switch state * * @Return - True/False - True if pressure adequate, false if low */ CTR_Code PCM::GetPressure(bool &status) { GET_PCM_STATUS(); status = (rx->pressureSwitchEn ) ? 1 : 0; return rx.err; } /* Get compressor state * * @Return - True/False - True if enabled, false if otherwise */ CTR_Code PCM::GetCompressor(bool &status) { GET_PCM_STATUS(); status = (rx->compressorOn); return rx.err; } /* Get closed loop control state * * @Return - True/False - True if closed loop enabled, false if otherwise */ CTR_Code PCM::GetClosedLoopControl(bool &status) { GET_PCM_STATUS(); status = (rx->isCloseloopEnabled); return rx.err; } /* Get compressor current draw * * @Return - Amperes - Compressor current */ CTR_Code PCM::GetCompressorCurrent(float &status) { GET_PCM_STATUS(); uint32_t temp =(rx->compressorCurrentTop6); temp <<= 4; temp |= rx->compressorCurrentBtm4; status = temp * 0.03125; /* 5.5 fixed pt value in Amps */ return rx.err; } /* Get voltage across solenoid rail * * @Return - Volts - Voltage across solenoid rail */ CTR_Code PCM::GetSolenoidVoltage(float &status) { GET_PCM_STATUS(); uint32_t raw =(rx->solenoidVoltageTop8); raw <<= 2; raw |= rx->solenoidVoltageBtm2; status = (double) raw * 0.03125; /* 5.5 fixed pt value in Volts */ return rx.err; } /* Get hardware fault value * * @Return - True/False - True if hardware failure detected, false if otherwise */ CTR_Code PCM::GetHardwareFault(bool &status) { GET_PCM_STATUS(); status = rx->faultHardwareFailure; return rx.err; } /* Get compressor fault value * * @Return - True/False - True if shorted compressor detected, false if otherwise */ CTR_Code PCM::GetCompressorCurrentTooHighFault(bool &status) { GET_PCM_STATUS(); status = rx->faultCompCurrentTooHigh; return rx.err; } CTR_Code PCM::GetCompressorShortedStickyFault(bool &status) { GET_PCM_STATUS(); status = rx->StickyFault_dItooHigh; return rx.err; } CTR_Code PCM::GetCompressorShortedFault(bool &status) { GET_PCM_STATUS(); status = rx->Fault_dItooHigh; return rx.err; } CTR_Code PCM::GetCompressorNotConnectedStickyFault(bool &status) { GET_PCM_SOL_FAULTS(); status = rx->StickyFault_CompNoCurrent; return rx.err; } CTR_Code PCM::GetCompressorNotConnectedFault(bool &status) { GET_PCM_SOL_FAULTS(); status = rx->Fault_CompNoCurrent; return rx.err; } /* Get solenoid fault value * * @Return - True/False - True if shorted solenoid detected, false if otherwise */ CTR_Code PCM::GetSolenoidFault(bool &status) { GET_PCM_STATUS(); status = rx->faultFuseTripped; return rx.err; } /* Get compressor sticky fault value * * @Return - True/False - True if solenoid had previously been shorted * (and sticky fault was not cleared), false if otherwise */ CTR_Code PCM::GetCompressorCurrentTooHighStickyFault(bool &status) { GET_PCM_STATUS(); status = rx->stickyFaultCompCurrentTooHigh; return rx.err; } /* Get solenoid sticky fault value * * @Return - True/False - True if compressor had previously been shorted * (and sticky fault was not cleared), false if otherwise */ CTR_Code PCM::GetSolenoidStickyFault(bool &status) { GET_PCM_STATUS(); status = rx->stickyFaultFuseTripped; return rx.err; } /* Get battery voltage * * @Return - Volts - Voltage across PCM power ports */ CTR_Code PCM::GetBatteryVoltage(float &status) { GET_PCM_STATUS(); status = (float)rx->battVoltage * 0.05 + 4.0; /* 50mV per unit plus 4V. */ return rx.err; } /* Return status of module enable/disable * * @Return - bool - Returns TRUE if PCM is enabled, FALSE if disabled */ CTR_Code PCM::isModuleEnabled(bool &status) { GET_PCM_STATUS(); status = rx->moduleEnabled; return rx.err; } /* Get number of total failed PCM Control Frame * * @Return - Failed Control Frames - Number of failed control frames (tokenization fails) * * @WARNING - Return only valid if [SeekDebugFrames] is enabled * See function SeekDebugFrames * See function EnableSeekDebugFrames */ CTR_Code PCM::GetNumberOfFailedControlFrames(UINT16 &status) { GET_PCM_DEBUG(); status = rx->tokFailsTop8; status <<= 8; status |= rx->tokFailsBtm8; return rx.err; } /* Get raw Solenoid Blacklist * * @Return - BINARY - Raw binary breakdown of Solenoid Blacklist * BIT7 = Solenoid 1, BIT6 = Solenoid 2, etc. * * @WARNING - Return only valid if [SeekStatusFaultFrames] is enabled * See function SeekStatusFaultFrames * See function EnableSeekStatusFaultFrames */ CTR_Code PCM::GetSolenoidBlackList(UINT8 &status) { GET_PCM_SOL_FAULTS(); status = rx->SolenoidBlacklist; return rx.err; } /* Get solenoid Blacklist status * - Blacklisted solenoids cannot be enabled until PCM is power cycled * * @Return - True/False - True if Solenoid is blacklisted, false if otherwise * * @Param - idx - ID of solenoid [0,7] * * @WARNING - Return only valid if [SeekStatusFaultFrames] is enabled * See function SeekStatusFaultFrames * See function EnableSeekStatusFaultFrames */ CTR_Code PCM::IsSolenoidBlacklisted(UINT8 idx, bool &status) { GET_PCM_SOL_FAULTS(); status = (rx->SolenoidBlacklist & (1ul<<(idx)) )? 1 : 0; return rx.err; } //------------------ C interface --------------------------------------------// extern "C" { void * c_PCM_Init(void) { return new PCM(); } CTR_Code c_SetSolenoid(void * handle, unsigned char idx, INT8 param) { return ((PCM*) handle)->SetSolenoid(idx, param); } CTR_Code c_SetClosedLoopControl(void * handle, INT8 param) { return ((PCM*) handle)->SetClosedLoopControl(param); } CTR_Code c_ClearStickyFaults(void * handle, INT8 param) { return ((PCM*) handle)->ClearStickyFaults(); } CTR_Code c_GetSolenoid(void * handle, UINT8 idx, INT8 * status) { bool bstatus; CTR_Code retval = ((PCM*) handle)->GetSolenoid(idx, bstatus); *status = bstatus; return retval; } CTR_Code c_GetAllSolenoids(void * handle, UINT8 * status) { return ((PCM*) handle)->GetAllSolenoids(*status); } CTR_Code c_GetPressure(void * handle, INT8 * status) { bool bstatus; CTR_Code retval = ((PCM*) handle)->GetPressure(bstatus); *status = bstatus; return retval; } CTR_Code c_GetCompressor(void * handle, INT8 * status) { bool bstatus; CTR_Code retval = ((PCM*) handle)->GetCompressor(bstatus); *status = bstatus; return retval; } CTR_Code c_GetClosedLoopControl(void * handle, INT8 * status) { bool bstatus; CTR_Code retval = ((PCM*) handle)->GetClosedLoopControl(bstatus); *status = bstatus; return retval; } CTR_Code c_GetCompressorCurrent(void * handle, float * status) { CTR_Code retval = ((PCM*) handle)->GetCompressorCurrent(*status); return retval; } CTR_Code c_GetSolenoidVoltage(void * handle, float*status) { return ((PCM*) handle)->GetSolenoidVoltage(*status); } CTR_Code c_GetHardwareFault(void * handle, INT8*status) { bool bstatus; CTR_Code retval = ((PCM*) handle)->GetHardwareFault(bstatus); *status = bstatus; return retval; } CTR_Code c_GetCompressorFault(void * handle, INT8*status) { bool bstatus; CTR_Code retval = ((PCM*) handle)->GetCompressorCurrentTooHighFault(bstatus); *status = bstatus; return retval; } CTR_Code c_GetSolenoidFault(void * handle, INT8*status) { bool bstatus; CTR_Code retval = ((PCM*) handle)->GetSolenoidFault(bstatus); *status = bstatus; return retval; } CTR_Code c_GetCompressorStickyFault(void * handle, INT8*status) { bool bstatus; CTR_Code retval = ((PCM*) handle)->GetCompressorCurrentTooHighStickyFault(bstatus); *status = bstatus; return retval; } CTR_Code c_GetSolenoidStickyFault(void * handle, INT8*status) { bool bstatus; CTR_Code retval = ((PCM*) handle)->GetSolenoidStickyFault(bstatus); *status = bstatus; return retval; } CTR_Code c_GetBatteryVoltage(void * handle, float*status) { CTR_Code retval = ((PCM*) handle)->GetBatteryVoltage(*status); return retval; } void c_SetDeviceNumber_PCM(void * handle, UINT8 deviceNumber) { } CTR_Code c_GetNumberOfFailedControlFrames(void * handle, UINT16*status) { return ((PCM*) handle)->GetNumberOfFailedControlFrames(*status); } CTR_Code c_GetSolenoidBlackList(void * handle, UINT8 *status) { return ((PCM*) handle)->GetSolenoidBlackList(*status); } CTR_Code c_IsSolenoidBlacklisted(void * handle, UINT8 idx, INT8*status) { bool bstatus; CTR_Code retval = ((PCM*) handle)->IsSolenoidBlacklisted(idx, bstatus); *status = bstatus; return retval; } }