2013-12-15 18:30:16 -05:00
|
|
|
/*----------------------------------------------------------------------------*/
|
2019-06-11 13:57:36 +10:00
|
|
|
/* Copyright (c) 2008-2019 FIRST. All Rights Reserved. */
|
2013-12-15 18:30:16 -05:00
|
|
|
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
2016-01-02 03:02:34 -08:00
|
|
|
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
|
|
|
/* the project. */
|
2013-12-15 18:30:16 -05:00
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
2018-07-20 00:03:45 -07:00
|
|
|
#include "frc/SPI.h"
|
2013-12-15 18:30:16 -05:00
|
|
|
|
2016-06-05 07:33:37 -07:00
|
|
|
#include <cstring>
|
2018-09-24 00:08:25 -07:00
|
|
|
#include <utility>
|
2013-12-15 18:30:16 -05:00
|
|
|
|
2018-07-20 00:03:45 -07:00
|
|
|
#include <hal/HAL.h>
|
|
|
|
|
#include <hal/SPI.h>
|
2018-04-29 23:33:19 -07:00
|
|
|
#include <wpi/SmallVector.h>
|
|
|
|
|
#include <wpi/mutex.h>
|
2017-08-27 00:11:52 -07:00
|
|
|
|
2018-07-20 00:03:45 -07:00
|
|
|
#include "frc/DigitalSource.h"
|
|
|
|
|
#include "frc/Notifier.h"
|
|
|
|
|
#include "frc/WPIErrors.h"
|
2016-05-25 22:38:11 -07:00
|
|
|
|
2016-11-01 22:33:12 -07:00
|
|
|
using namespace frc;
|
|
|
|
|
|
2017-12-13 23:41:37 -08:00
|
|
|
static constexpr int kAccumulateDepth = 2048;
|
|
|
|
|
|
|
|
|
|
class SPI::Accumulator {
|
|
|
|
|
public:
|
|
|
|
|
Accumulator(HAL_SPIPort port, int xferSize, int validMask, int validValue,
|
|
|
|
|
int dataShift, int dataSize, bool isSigned, bool bigEndian)
|
|
|
|
|
: m_notifier([=]() {
|
2019-07-08 22:58:39 -07:00
|
|
|
std::scoped_lock lock(m_mutex);
|
2017-12-13 23:41:37 -08:00
|
|
|
Update();
|
|
|
|
|
}),
|
2018-12-06 22:29:20 -08:00
|
|
|
m_buf(new uint32_t[(xferSize + 1) * kAccumulateDepth]),
|
2017-12-13 23:41:37 -08:00
|
|
|
m_validMask(validMask),
|
|
|
|
|
m_validValue(validValue),
|
|
|
|
|
m_dataMax(1 << dataSize),
|
|
|
|
|
m_dataMsbMask(1 << (dataSize - 1)),
|
|
|
|
|
m_dataShift(dataShift),
|
2018-12-06 22:29:20 -08:00
|
|
|
m_xferSize(xferSize + 1), // +1 for timestamp
|
2017-12-13 23:41:37 -08:00
|
|
|
m_isSigned(isSigned),
|
|
|
|
|
m_bigEndian(bigEndian),
|
|
|
|
|
m_port(port) {}
|
|
|
|
|
~Accumulator() { delete[] m_buf; }
|
|
|
|
|
|
|
|
|
|
void Update();
|
|
|
|
|
|
|
|
|
|
Notifier m_notifier;
|
2018-12-06 22:29:20 -08:00
|
|
|
uint32_t* m_buf;
|
2017-12-13 23:41:37 -08:00
|
|
|
wpi::mutex m_mutex;
|
|
|
|
|
|
|
|
|
|
int64_t m_value = 0;
|
|
|
|
|
uint32_t m_count = 0;
|
|
|
|
|
int32_t m_lastValue = 0;
|
2018-12-06 22:29:20 -08:00
|
|
|
uint32_t m_lastTimestamp = 0;
|
|
|
|
|
double m_integratedValue = 0;
|
2017-12-13 23:41:37 -08:00
|
|
|
|
|
|
|
|
int32_t m_center = 0;
|
|
|
|
|
int32_t m_deadband = 0;
|
2018-12-06 22:29:20 -08:00
|
|
|
double m_integratedCenter = 0;
|
2017-12-13 23:41:37 -08:00
|
|
|
|
|
|
|
|
int32_t m_validMask;
|
|
|
|
|
int32_t m_validValue;
|
|
|
|
|
int32_t m_dataMax; // one more than max data value
|
|
|
|
|
int32_t m_dataMsbMask; // data field MSB mask (for signed)
|
|
|
|
|
uint8_t m_dataShift; // data field shift right amount, in bits
|
|
|
|
|
int32_t m_xferSize; // SPI transfer size, in bytes
|
|
|
|
|
bool m_isSigned; // is data field signed?
|
|
|
|
|
bool m_bigEndian; // is response big endian?
|
|
|
|
|
HAL_SPIPort m_port;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void SPI::Accumulator::Update() {
|
|
|
|
|
bool done;
|
|
|
|
|
do {
|
|
|
|
|
done = true;
|
|
|
|
|
int32_t status = 0;
|
|
|
|
|
|
|
|
|
|
// get amount of data available
|
|
|
|
|
int32_t numToRead =
|
|
|
|
|
HAL_ReadSPIAutoReceivedData(m_port, m_buf, 0, 0, &status);
|
|
|
|
|
if (status != 0) return; // error reading
|
|
|
|
|
|
2018-12-06 22:29:20 -08:00
|
|
|
// only get whole responses; +1 is for timestamp
|
2017-12-13 23:41:37 -08:00
|
|
|
numToRead -= numToRead % m_xferSize;
|
|
|
|
|
if (numToRead > m_xferSize * kAccumulateDepth) {
|
|
|
|
|
numToRead = m_xferSize * kAccumulateDepth;
|
|
|
|
|
done = false;
|
|
|
|
|
}
|
|
|
|
|
if (numToRead == 0) return; // no samples
|
|
|
|
|
|
|
|
|
|
// read buffered data
|
|
|
|
|
HAL_ReadSPIAutoReceivedData(m_port, m_buf, numToRead, 0, &status);
|
|
|
|
|
if (status != 0) return; // error reading
|
|
|
|
|
|
|
|
|
|
// loop over all responses
|
|
|
|
|
for (int32_t off = 0; off < numToRead; off += m_xferSize) {
|
2018-12-06 22:29:20 -08:00
|
|
|
// get timestamp from first word
|
|
|
|
|
uint32_t timestamp = m_buf[off];
|
|
|
|
|
|
2017-12-13 23:41:37 -08:00
|
|
|
// convert from bytes
|
|
|
|
|
uint32_t resp = 0;
|
|
|
|
|
if (m_bigEndian) {
|
2018-12-06 22:29:20 -08:00
|
|
|
for (int32_t i = 1; i < m_xferSize; ++i) {
|
2017-12-13 23:41:37 -08:00
|
|
|
resp <<= 8;
|
|
|
|
|
resp |= m_buf[off + i] & 0xff;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2018-12-06 22:29:20 -08:00
|
|
|
for (int32_t i = m_xferSize - 1; i >= 1; --i) {
|
2017-12-13 23:41:37 -08:00
|
|
|
resp <<= 8;
|
|
|
|
|
resp |= m_buf[off + i] & 0xff;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// process response
|
|
|
|
|
if ((resp & m_validMask) == static_cast<uint32_t>(m_validValue)) {
|
|
|
|
|
// valid sensor data; extract data field
|
|
|
|
|
int32_t data = static_cast<int32_t>(resp >> m_dataShift);
|
|
|
|
|
data &= m_dataMax - 1;
|
|
|
|
|
// 2s complement conversion if signed MSB is set
|
|
|
|
|
if (m_isSigned && (data & m_dataMsbMask) != 0) data -= m_dataMax;
|
|
|
|
|
// center offset
|
2018-12-06 22:29:20 -08:00
|
|
|
int32_t dataNoCenter = data;
|
2017-12-13 23:41:37 -08:00
|
|
|
data -= m_center;
|
|
|
|
|
// only accumulate if outside deadband
|
2018-12-06 22:29:20 -08:00
|
|
|
if (data < -m_deadband || data > m_deadband) {
|
|
|
|
|
m_value += data;
|
|
|
|
|
if (m_count != 0) {
|
|
|
|
|
// timestamps use the 1us FPGA clock; also handle rollover
|
|
|
|
|
if (timestamp >= m_lastTimestamp)
|
|
|
|
|
m_integratedValue +=
|
|
|
|
|
dataNoCenter *
|
|
|
|
|
static_cast<int32_t>(timestamp - m_lastTimestamp) * 1e-6 -
|
|
|
|
|
m_integratedCenter;
|
|
|
|
|
else
|
|
|
|
|
m_integratedValue +=
|
|
|
|
|
dataNoCenter *
|
|
|
|
|
static_cast<int32_t>((1ULL << 32) - m_lastTimestamp +
|
|
|
|
|
timestamp) *
|
|
|
|
|
1e-6 -
|
|
|
|
|
m_integratedCenter;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-12-13 23:41:37 -08:00
|
|
|
++m_count;
|
|
|
|
|
m_lastValue = data;
|
|
|
|
|
} else {
|
|
|
|
|
// no data from the sensor; just clear the last value
|
|
|
|
|
m_lastValue = 0;
|
|
|
|
|
}
|
2018-12-06 22:29:20 -08:00
|
|
|
m_lastTimestamp = timestamp;
|
2017-12-13 23:41:37 -08:00
|
|
|
}
|
|
|
|
|
} while (!done);
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-09 12:12:46 -07:00
|
|
|
SPI::SPI(Port port) : m_port(static_cast<HAL_SPIPort>(port)) {
|
2015-06-25 15:07:55 -04:00
|
|
|
int32_t status = 0;
|
2016-07-09 01:12:37 -07:00
|
|
|
HAL_InitializeSPI(m_port, &status);
|
2016-07-09 00:24:26 -07:00
|
|
|
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
|
2015-06-25 15:07:55 -04:00
|
|
|
|
2019-10-29 21:34:10 -07:00
|
|
|
HAL_Report(HALUsageReporting::kResourceType_SPI,
|
|
|
|
|
static_cast<uint8_t>(port) + 1);
|
2013-12-15 18:30:16 -05:00
|
|
|
}
|
|
|
|
|
|
2016-07-09 01:12:37 -07:00
|
|
|
SPI::~SPI() { HAL_CloseSPI(m_port); }
|
2013-12-15 18:30:16 -05:00
|
|
|
|
2019-06-12 23:04:52 +10:00
|
|
|
void SPI::SetClockRate(int hz) { HAL_SetSPISpeed(m_port, hz); }
|
2013-12-15 18:30:16 -05:00
|
|
|
|
2015-06-25 15:07:55 -04:00
|
|
|
void SPI::SetMSBFirst() {
|
|
|
|
|
m_msbFirst = true;
|
2018-09-23 18:26:02 -07:00
|
|
|
HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
|
2013-12-15 18:30:16 -05:00
|
|
|
}
|
|
|
|
|
|
2015-06-25 15:07:55 -04:00
|
|
|
void SPI::SetLSBFirst() {
|
|
|
|
|
m_msbFirst = false;
|
2018-09-23 18:26:02 -07:00
|
|
|
HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
|
2013-12-15 18:30:16 -05:00
|
|
|
}
|
|
|
|
|
|
2018-05-14 18:16:36 -07:00
|
|
|
void SPI::SetSampleDataOnLeadingEdge() {
|
|
|
|
|
m_sampleOnTrailing = false;
|
2018-09-23 18:26:02 -07:00
|
|
|
HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
|
2018-05-14 18:16:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SPI::SetSampleDataOnTrailingEdge() {
|
|
|
|
|
m_sampleOnTrailing = true;
|
2018-09-23 18:26:02 -07:00
|
|
|
HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
|
2018-05-14 18:16:36 -07:00
|
|
|
}
|
|
|
|
|
|
2015-06-25 15:07:55 -04:00
|
|
|
void SPI::SetSampleDataOnFalling() {
|
|
|
|
|
m_sampleOnTrailing = true;
|
2018-09-23 18:26:02 -07:00
|
|
|
HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
|
2013-12-15 18:30:16 -05:00
|
|
|
}
|
|
|
|
|
|
2015-06-25 15:07:55 -04:00
|
|
|
void SPI::SetSampleDataOnRising() {
|
|
|
|
|
m_sampleOnTrailing = false;
|
2018-09-23 18:26:02 -07:00
|
|
|
HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
|
2013-12-15 18:30:16 -05:00
|
|
|
}
|
|
|
|
|
|
2015-06-25 15:07:55 -04:00
|
|
|
void SPI::SetClockActiveLow() {
|
2018-09-23 18:26:02 -07:00
|
|
|
m_clockIdleHigh = true;
|
|
|
|
|
HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
|
2013-12-15 18:30:16 -05:00
|
|
|
}
|
|
|
|
|
|
2015-06-25 15:07:55 -04:00
|
|
|
void SPI::SetClockActiveHigh() {
|
2018-09-23 18:26:02 -07:00
|
|
|
m_clockIdleHigh = false;
|
|
|
|
|
HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
|
2013-12-15 18:30:16 -05:00
|
|
|
}
|
|
|
|
|
|
2015-06-25 15:07:55 -04:00
|
|
|
void SPI::SetChipSelectActiveHigh() {
|
|
|
|
|
int32_t status = 0;
|
2016-07-09 01:12:37 -07:00
|
|
|
HAL_SetSPIChipSelectActiveHigh(m_port, &status);
|
2016-07-09 00:24:26 -07:00
|
|
|
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
|
2013-12-15 18:30:16 -05:00
|
|
|
}
|
|
|
|
|
|
2015-06-25 15:07:55 -04:00
|
|
|
void SPI::SetChipSelectActiveLow() {
|
|
|
|
|
int32_t status = 0;
|
2016-07-09 01:12:37 -07:00
|
|
|
HAL_SetSPIChipSelectActiveLow(m_port, &status);
|
2016-07-09 00:24:26 -07:00
|
|
|
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
|
2013-12-15 18:30:16 -05:00
|
|
|
}
|
|
|
|
|
|
2016-09-06 00:01:45 -07:00
|
|
|
int SPI::Write(uint8_t* data, int size) {
|
|
|
|
|
int retVal = 0;
|
2016-07-09 01:12:37 -07:00
|
|
|
retVal = HAL_WriteSPI(m_port, data, size);
|
2015-06-25 15:07:55 -04:00
|
|
|
return retVal;
|
2013-12-15 18:30:16 -05:00
|
|
|
}
|
|
|
|
|
|
2016-09-06 00:01:45 -07:00
|
|
|
int SPI::Read(bool initiate, uint8_t* dataReceived, int size) {
|
|
|
|
|
int retVal = 0;
|
2015-06-25 15:07:55 -04:00
|
|
|
if (initiate) {
|
2018-04-29 23:33:19 -07:00
|
|
|
wpi::SmallVector<uint8_t, 32> dataToSend;
|
2017-02-14 01:14:08 -08:00
|
|
|
dataToSend.resize(size);
|
|
|
|
|
retVal = HAL_TransactionSPI(m_port, dataToSend.data(), dataReceived, size);
|
2016-07-10 17:47:44 -07:00
|
|
|
} else {
|
2016-07-09 01:12:37 -07:00
|
|
|
retVal = HAL_ReadSPI(m_port, dataReceived, size);
|
2016-07-10 17:47:44 -07:00
|
|
|
}
|
2015-06-25 15:07:55 -04:00
|
|
|
return retVal;
|
2013-12-15 18:30:16 -05:00
|
|
|
}
|
|
|
|
|
|
2016-09-06 00:01:45 -07:00
|
|
|
int SPI::Transaction(uint8_t* dataToSend, uint8_t* dataReceived, int size) {
|
|
|
|
|
int retVal = 0;
|
2016-07-09 01:12:37 -07:00
|
|
|
retVal = HAL_TransactionSPI(m_port, dataToSend, dataReceived, size);
|
2015-06-25 15:07:55 -04:00
|
|
|
return retVal;
|
2013-12-15 18:30:16 -05:00
|
|
|
}
|
2015-11-22 11:50:49 -08:00
|
|
|
|
2017-12-13 23:41:37 -08:00
|
|
|
void SPI::InitAuto(int bufferSize) {
|
|
|
|
|
int32_t status = 0;
|
|
|
|
|
HAL_InitSPIAuto(m_port, bufferSize, &status);
|
|
|
|
|
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SPI::FreeAuto() {
|
|
|
|
|
int32_t status = 0;
|
|
|
|
|
HAL_FreeSPIAuto(m_port, &status);
|
|
|
|
|
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-13 17:09:56 -07:00
|
|
|
void SPI::SetAutoTransmitData(wpi::ArrayRef<uint8_t> dataToSend, int zeroSize) {
|
2017-12-13 23:41:37 -08:00
|
|
|
int32_t status = 0;
|
|
|
|
|
HAL_SetSPIAutoTransmitData(m_port, dataToSend.data(), dataToSend.size(),
|
|
|
|
|
zeroSize, &status);
|
|
|
|
|
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-24 21:28:20 -07:00
|
|
|
void SPI::StartAutoRate(units::second_t period) {
|
2017-12-13 23:41:37 -08:00
|
|
|
int32_t status = 0;
|
2019-08-24 21:28:20 -07:00
|
|
|
HAL_StartSPIAutoRate(m_port, period.to<double>(), &status);
|
2017-12-13 23:41:37 -08:00
|
|
|
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-24 21:28:20 -07:00
|
|
|
void SPI::StartAutoRate(double period) {
|
|
|
|
|
StartAutoRate(units::second_t(period));
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-13 23:41:37 -08:00
|
|
|
void SPI::StartAutoTrigger(DigitalSource& source, bool rising, bool falling) {
|
|
|
|
|
int32_t status = 0;
|
|
|
|
|
HAL_StartSPIAutoTrigger(
|
|
|
|
|
m_port, source.GetPortHandleForRouting(),
|
|
|
|
|
(HAL_AnalogTriggerType)source.GetAnalogTriggerTypeForRouting(), rising,
|
|
|
|
|
falling, &status);
|
|
|
|
|
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SPI::StopAuto() {
|
|
|
|
|
int32_t status = 0;
|
|
|
|
|
HAL_StopSPIAuto(m_port, &status);
|
|
|
|
|
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SPI::ForceAutoRead() {
|
|
|
|
|
int32_t status = 0;
|
|
|
|
|
HAL_ForceSPIAutoRead(m_port, &status);
|
|
|
|
|
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-24 21:28:20 -07:00
|
|
|
int SPI::ReadAutoReceivedData(uint32_t* buffer, int numToRead,
|
|
|
|
|
units::second_t timeout) {
|
2017-12-13 23:41:37 -08:00
|
|
|
int32_t status = 0;
|
2019-08-24 21:28:20 -07:00
|
|
|
int32_t val = HAL_ReadSPIAutoReceivedData(m_port, buffer, numToRead,
|
|
|
|
|
timeout.to<double>(), &status);
|
2017-12-13 23:41:37 -08:00
|
|
|
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
|
|
|
|
|
return val;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-24 21:28:20 -07:00
|
|
|
int SPI::ReadAutoReceivedData(uint32_t* buffer, int numToRead, double timeout) {
|
|
|
|
|
return ReadAutoReceivedData(buffer, numToRead, units::second_t(timeout));
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-13 23:41:37 -08:00
|
|
|
int SPI::GetAutoDroppedCount() {
|
|
|
|
|
int32_t status = 0;
|
|
|
|
|
int32_t val = HAL_GetSPIAutoDroppedCount(m_port, &status);
|
|
|
|
|
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
|
|
|
|
|
return val;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-24 21:28:20 -07:00
|
|
|
void SPI::InitAccumulator(units::second_t period, int cmd, int xferSize,
|
|
|
|
|
int validMask, int validValue, int dataShift,
|
|
|
|
|
int dataSize, bool isSigned, bool bigEndian) {
|
2017-12-13 23:41:37 -08:00
|
|
|
InitAuto(xferSize * kAccumulateDepth);
|
|
|
|
|
uint8_t cmdBytes[4] = {0, 0, 0, 0};
|
|
|
|
|
if (bigEndian) {
|
|
|
|
|
for (int32_t i = xferSize - 1; i >= 0; --i) {
|
|
|
|
|
cmdBytes[i] = cmd & 0xff;
|
|
|
|
|
cmd >>= 8;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
cmdBytes[0] = cmd & 0xff;
|
|
|
|
|
cmd >>= 8;
|
|
|
|
|
cmdBytes[1] = cmd & 0xff;
|
|
|
|
|
cmd >>= 8;
|
|
|
|
|
cmdBytes[2] = cmd & 0xff;
|
|
|
|
|
cmd >>= 8;
|
|
|
|
|
cmdBytes[3] = cmd & 0xff;
|
|
|
|
|
}
|
|
|
|
|
SetAutoTransmitData(cmdBytes, xferSize - 4);
|
|
|
|
|
StartAutoRate(period);
|
|
|
|
|
|
|
|
|
|
m_accum.reset(new Accumulator(m_port, xferSize, validMask, validValue,
|
|
|
|
|
dataShift, dataSize, isSigned, bigEndian));
|
|
|
|
|
m_accum->m_notifier.StartPeriodic(period * kAccumulateDepth / 2);
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
2019-08-24 21:28:20 -07:00
|
|
|
void SPI::InitAccumulator(double period, int cmd, int xferSize, int validMask,
|
|
|
|
|
int validValue, int dataShift, int dataSize,
|
|
|
|
|
bool isSigned, bool bigEndian) {
|
|
|
|
|
InitAccumulator(units::second_t(period), cmd, xferSize, validMask, validValue,
|
|
|
|
|
dataShift, dataSize, isSigned, bigEndian);
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-22 11:50:49 -08:00
|
|
|
void SPI::FreeAccumulator() {
|
2017-12-13 23:41:37 -08:00
|
|
|
m_accum.reset(nullptr);
|
|
|
|
|
FreeAuto();
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SPI::ResetAccumulator() {
|
2017-12-13 23:41:37 -08:00
|
|
|
if (!m_accum) return;
|
2019-07-08 22:58:39 -07:00
|
|
|
std::scoped_lock lock(m_accum->m_mutex);
|
2017-12-13 23:41:37 -08:00
|
|
|
m_accum->m_value = 0;
|
|
|
|
|
m_accum->m_count = 0;
|
|
|
|
|
m_accum->m_lastValue = 0;
|
2018-12-06 22:29:20 -08:00
|
|
|
m_accum->m_lastTimestamp = 0;
|
|
|
|
|
m_accum->m_integratedValue = 0;
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
2016-09-06 00:01:45 -07:00
|
|
|
void SPI::SetAccumulatorCenter(int center) {
|
2017-12-13 23:41:37 -08:00
|
|
|
if (!m_accum) return;
|
2019-07-08 22:58:39 -07:00
|
|
|
std::scoped_lock lock(m_accum->m_mutex);
|
2017-12-13 23:41:37 -08:00
|
|
|
m_accum->m_center = center;
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
2016-09-06 00:01:45 -07:00
|
|
|
void SPI::SetAccumulatorDeadband(int deadband) {
|
2017-12-13 23:41:37 -08:00
|
|
|
if (!m_accum) return;
|
2019-07-08 22:58:39 -07:00
|
|
|
std::scoped_lock lock(m_accum->m_mutex);
|
2017-12-13 23:41:37 -08:00
|
|
|
m_accum->m_deadband = deadband;
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
2016-09-06 00:01:45 -07:00
|
|
|
int SPI::GetAccumulatorLastValue() const {
|
2017-12-13 23:41:37 -08:00
|
|
|
if (!m_accum) return 0;
|
2019-07-08 22:58:39 -07:00
|
|
|
std::scoped_lock lock(m_accum->m_mutex);
|
2017-12-13 23:41:37 -08:00
|
|
|
m_accum->Update();
|
|
|
|
|
return m_accum->m_lastValue;
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int64_t SPI::GetAccumulatorValue() const {
|
2017-12-13 23:41:37 -08:00
|
|
|
if (!m_accum) return 0;
|
2019-07-08 22:58:39 -07:00
|
|
|
std::scoped_lock lock(m_accum->m_mutex);
|
2017-12-13 23:41:37 -08:00
|
|
|
m_accum->Update();
|
|
|
|
|
return m_accum->m_value;
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
2016-07-12 10:45:14 -07:00
|
|
|
int64_t SPI::GetAccumulatorCount() const {
|
2017-12-13 23:41:37 -08:00
|
|
|
if (!m_accum) return 0;
|
2019-07-08 22:58:39 -07:00
|
|
|
std::scoped_lock lock(m_accum->m_mutex);
|
2017-12-13 23:41:37 -08:00
|
|
|
m_accum->Update();
|
|
|
|
|
return m_accum->m_count;
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double SPI::GetAccumulatorAverage() const {
|
2017-12-13 23:41:37 -08:00
|
|
|
if (!m_accum) return 0;
|
2019-07-08 22:58:39 -07:00
|
|
|
std::scoped_lock lock(m_accum->m_mutex);
|
2017-12-13 23:41:37 -08:00
|
|
|
m_accum->Update();
|
|
|
|
|
if (m_accum->m_count == 0) return 0.0;
|
|
|
|
|
return static_cast<double>(m_accum->m_value) / m_accum->m_count;
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
2016-07-12 10:45:14 -07:00
|
|
|
void SPI::GetAccumulatorOutput(int64_t& value, int64_t& count) const {
|
2017-12-13 23:41:37 -08:00
|
|
|
if (!m_accum) {
|
|
|
|
|
value = 0;
|
|
|
|
|
count = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-07-08 22:58:39 -07:00
|
|
|
std::scoped_lock lock(m_accum->m_mutex);
|
2017-12-13 23:41:37 -08:00
|
|
|
m_accum->Update();
|
|
|
|
|
value = m_accum->m_value;
|
|
|
|
|
count = m_accum->m_count;
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
2018-12-06 22:29:20 -08:00
|
|
|
|
|
|
|
|
void SPI::SetAccumulatorIntegratedCenter(double center) {
|
|
|
|
|
if (!m_accum) return;
|
2019-07-08 22:58:39 -07:00
|
|
|
std::scoped_lock lock(m_accum->m_mutex);
|
2018-12-06 22:29:20 -08:00
|
|
|
m_accum->m_integratedCenter = center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double SPI::GetAccumulatorIntegratedValue() const {
|
|
|
|
|
if (!m_accum) return 0;
|
2019-07-08 22:58:39 -07:00
|
|
|
std::scoped_lock lock(m_accum->m_mutex);
|
2018-12-06 22:29:20 -08:00
|
|
|
m_accum->Update();
|
|
|
|
|
return m_accum->m_integratedValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double SPI::GetAccumulatorIntegratedAverage() const {
|
|
|
|
|
if (!m_accum) return 0;
|
2019-07-08 22:58:39 -07:00
|
|
|
std::scoped_lock lock(m_accum->m_mutex);
|
2018-12-06 22:29:20 -08:00
|
|
|
m_accum->Update();
|
|
|
|
|
if (m_accum->m_count <= 1) return 0.0;
|
|
|
|
|
// count-1 due to not integrating the first value received
|
|
|
|
|
return m_accum->m_integratedValue / (m_accum->m_count - 1);
|
|
|
|
|
}
|