2013-12-15 18:30:16 -05:00
|
|
|
/*----------------------------------------------------------------------------*/
|
2017-08-23 22:06:13 -07:00
|
|
|
/* Copyright (c) 2008-2017 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
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
#include "SPI.h"
|
|
|
|
|
|
2016-06-05 07:33:37 -07:00
|
|
|
#include <cstring>
|
2013-12-15 18:30:16 -05:00
|
|
|
|
2017-08-27 00:11:52 -07:00
|
|
|
#include <HAL/HAL.h>
|
|
|
|
|
#include <HAL/SPI.h>
|
|
|
|
|
#include <llvm/SmallVector.h>
|
|
|
|
|
|
2016-05-25 22:38:11 -07:00
|
|
|
#include "WPIErrors.h"
|
|
|
|
|
|
2016-11-01 22:33:12 -07:00
|
|
|
using namespace frc;
|
|
|
|
|
|
2013-12-15 18:30:16 -05:00
|
|
|
/**
|
2014-07-16 16:24:44 -04:00
|
|
|
* Constructor
|
2013-12-15 18:30:16 -05:00
|
|
|
*
|
2017-05-09 12:12:46 -07:00
|
|
|
* @param port the physical SPI port
|
2013-12-15 18:30:16 -05:00
|
|
|
*/
|
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
|
|
|
|
2016-09-06 00:01:45 -07:00
|
|
|
static int instances = 0;
|
2015-06-25 15:07:55 -04:00
|
|
|
instances++;
|
2016-07-09 00:24:26 -07:00
|
|
|
HAL_Report(HALUsageReporting::kResourceType_SPI, instances);
|
2013-12-15 18:30:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2014-07-16 16:24:44 -04:00
|
|
|
* Destructor.
|
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
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Configure the rate of the generated clock signal.
|
2015-06-25 15:07:55 -04:00
|
|
|
*
|
2014-12-29 14:09:37 -05:00
|
|
|
* The default value is 500,000Hz.
|
|
|
|
|
* The maximum value is 4,000,000Hz.
|
2013-12-15 18:30:16 -05:00
|
|
|
*
|
2016-05-20 17:30:37 -07:00
|
|
|
* @param hz The clock rate in Hertz.
|
2013-12-15 18:30:16 -05:00
|
|
|
*/
|
2016-07-09 01:12:37 -07:00
|
|
|
void SPI::SetClockRate(double hz) { HAL_SetSPISpeed(m_port, hz); }
|
2013-12-15 18:30:16 -05:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Configure the order that bits are sent and received on the wire
|
|
|
|
|
* to be most significant bit first.
|
|
|
|
|
*/
|
2015-06-25 15:07:55 -04:00
|
|
|
void SPI::SetMSBFirst() {
|
|
|
|
|
m_msbFirst = true;
|
2016-07-12 10:45:14 -07:00
|
|
|
HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clk_idle_high);
|
2013-12-15 18:30:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Configure the order that bits are sent and received on the wire
|
|
|
|
|
* to be least significant bit first.
|
|
|
|
|
*/
|
2015-06-25 15:07:55 -04:00
|
|
|
void SPI::SetLSBFirst() {
|
|
|
|
|
m_msbFirst = false;
|
2016-07-12 10:45:14 -07:00
|
|
|
HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clk_idle_high);
|
2013-12-15 18:30:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Configure that the data is stable on the falling edge and the data
|
|
|
|
|
* changes on the rising edge.
|
|
|
|
|
*/
|
2015-06-25 15:07:55 -04:00
|
|
|
void SPI::SetSampleDataOnFalling() {
|
|
|
|
|
m_sampleOnTrailing = true;
|
2016-07-12 10:45:14 -07:00
|
|
|
HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clk_idle_high);
|
2013-12-15 18:30:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Configure that the data is stable on the rising edge and the data
|
|
|
|
|
* changes on the falling edge.
|
|
|
|
|
*/
|
2015-06-25 15:07:55 -04:00
|
|
|
void SPI::SetSampleDataOnRising() {
|
|
|
|
|
m_sampleOnTrailing = false;
|
2016-07-12 10:45:14 -07:00
|
|
|
HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clk_idle_high);
|
2013-12-15 18:30:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Configure the clock output line to be active low.
|
2014-07-16 16:24:44 -04:00
|
|
|
* This is sometimes called clock polarity high or clock idle high.
|
2013-12-15 18:30:16 -05:00
|
|
|
*/
|
2015-06-25 15:07:55 -04:00
|
|
|
void SPI::SetClockActiveLow() {
|
|
|
|
|
m_clk_idle_high = true;
|
2016-07-12 10:45:14 -07:00
|
|
|
HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clk_idle_high);
|
2013-12-15 18:30:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Configure the clock output line to be active high.
|
2014-07-16 16:24:44 -04:00
|
|
|
* This is sometimes called clock polarity low or clock idle low.
|
2013-12-15 18:30:16 -05:00
|
|
|
*/
|
2015-06-25 15:07:55 -04:00
|
|
|
void SPI::SetClockActiveHigh() {
|
|
|
|
|
m_clk_idle_high = false;
|
2016-07-12 10:45:14 -07:00
|
|
|
HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clk_idle_high);
|
2013-12-15 18:30:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2014-07-16 16:24:44 -04:00
|
|
|
* Configure the chip select line to be active high.
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2014-07-16 16:24:44 -04:00
|
|
|
* Configure the chip select line to be active low.
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2014-07-16 16:24:44 -04:00
|
|
|
* Write data to the slave device. Blocks until there is space in the
|
2013-12-15 18:30:16 -05:00
|
|
|
* output FIFO.
|
|
|
|
|
*
|
|
|
|
|
* If not running in output only mode, also saves the data received
|
|
|
|
|
* on the MISO input during the transfer into the receive FIFO.
|
|
|
|
|
*/
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Read a word from the receive FIFO.
|
|
|
|
|
*
|
|
|
|
|
* Waits for the current transfer to complete if the receive FIFO is empty.
|
|
|
|
|
*
|
|
|
|
|
* If the receive FIFO is empty, there is no active transfer, and initiate
|
|
|
|
|
* is false, errors.
|
|
|
|
|
*
|
2016-05-20 17:30:37 -07:00
|
|
|
* @param initiate If true, this function pushes "0" into the transmit buffer
|
|
|
|
|
* and initiates a transfer. If false, this function assumes
|
|
|
|
|
* that data is already in the receive FIFO from a previous
|
|
|
|
|
* write.
|
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) {
|
2017-02-14 01:14:08 -08:00
|
|
|
llvm::SmallVector<uint8_t, 32> dataToSend;
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2014-07-16 16:24:44 -04:00
|
|
|
* Perform a simultaneous read/write transaction with the device
|
|
|
|
|
*
|
2016-05-20 17:30:37 -07:00
|
|
|
* @param dataToSend The data to be written out to the device
|
2014-07-16 16:24:44 -04:00
|
|
|
* @param dataReceived Buffer to receive data from the device
|
2016-05-20 17:30:37 -07:00
|
|
|
* @param size The length of the transaction, in bytes
|
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
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Initialize the accumulator.
|
|
|
|
|
*
|
2017-11-22 17:06:57 -08:00
|
|
|
* @param period Time between reads
|
|
|
|
|
* @param cmd SPI command to send to request data
|
|
|
|
|
* @param xferSize SPI transfer size, in bytes
|
|
|
|
|
* @param validMask Mask to apply to received data for validity checking
|
|
|
|
|
* @param validData After valid_mask is applied, required matching value for
|
|
|
|
|
* validity checking
|
|
|
|
|
* @param dataShift Bit shift to apply to received data to get actual data
|
|
|
|
|
* value
|
|
|
|
|
* @param dataSize Size (in bits) of data field
|
|
|
|
|
* @param isSigned Is data field signed?
|
|
|
|
|
* @param bigEndian Is device big endian?
|
2015-11-22 11:50:49 -08:00
|
|
|
*/
|
2017-11-22 17:06:57 -08:00
|
|
|
void SPI::InitAccumulator(double period, int cmd, int xferSize, int validMask,
|
|
|
|
|
int validValue, int dataShift, int dataSize,
|
|
|
|
|
bool isSigned, bool bigEndian) {
|
2015-11-22 11:50:49 -08:00
|
|
|
int32_t status = 0;
|
2016-09-06 00:01:45 -07:00
|
|
|
HAL_InitSPIAccumulator(m_port, static_cast<int32_t>(period * 1e6), cmd,
|
2017-11-22 17:06:57 -08:00
|
|
|
xferSize, validMask, validValue, dataShift, dataSize,
|
|
|
|
|
isSigned, bigEndian, &status);
|
2016-07-09 00:24:26 -07:00
|
|
|
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Frees the accumulator.
|
|
|
|
|
*/
|
|
|
|
|
void SPI::FreeAccumulator() {
|
|
|
|
|
int32_t status = 0;
|
2016-07-09 01:12:37 -07:00
|
|
|
HAL_FreeSPIAccumulator(m_port, &status);
|
2016-07-09 00:24:26 -07:00
|
|
|
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Resets the accumulator to zero.
|
|
|
|
|
*/
|
|
|
|
|
void SPI::ResetAccumulator() {
|
|
|
|
|
int32_t status = 0;
|
2016-07-09 01:12:37 -07:00
|
|
|
HAL_ResetSPIAccumulator(m_port, &status);
|
2016-07-09 00:24:26 -07:00
|
|
|
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the center value of the accumulator.
|
|
|
|
|
*
|
2016-05-20 17:30:37 -07:00
|
|
|
* The center value is subtracted from each value before it is added to the
|
|
|
|
|
* accumulator. This is used for the center value of devices like gyros and
|
|
|
|
|
* accelerometers to make integration work and to take the device offset into
|
|
|
|
|
* account when integrating.
|
2015-11-22 11:50:49 -08:00
|
|
|
*/
|
2016-09-06 00:01:45 -07:00
|
|
|
void SPI::SetAccumulatorCenter(int center) {
|
2015-11-22 11:50:49 -08:00
|
|
|
int32_t status = 0;
|
2016-07-09 01:12:37 -07:00
|
|
|
HAL_SetSPIAccumulatorCenter(m_port, center, &status);
|
2016-07-09 00:24:26 -07:00
|
|
|
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the accumulator's deadband.
|
|
|
|
|
*/
|
2016-09-06 00:01:45 -07:00
|
|
|
void SPI::SetAccumulatorDeadband(int deadband) {
|
2015-11-22 11:50:49 -08:00
|
|
|
int32_t status = 0;
|
2016-07-09 01:12:37 -07:00
|
|
|
HAL_SetSPIAccumulatorDeadband(m_port, deadband, &status);
|
2016-07-09 00:24:26 -07:00
|
|
|
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Read the last value read by the accumulator engine.
|
|
|
|
|
*/
|
2016-09-06 00:01:45 -07:00
|
|
|
int SPI::GetAccumulatorLastValue() const {
|
2015-11-22 11:50:49 -08:00
|
|
|
int32_t status = 0;
|
2016-09-06 00:01:45 -07:00
|
|
|
int retVal = HAL_GetSPIAccumulatorLastValue(m_port, &status);
|
2016-07-09 00:24:26 -07:00
|
|
|
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
|
2015-11-22 11:50:49 -08:00
|
|
|
return retVal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Read the accumulated value.
|
|
|
|
|
*
|
|
|
|
|
* @return The 64-bit value accumulated since the last Reset().
|
|
|
|
|
*/
|
|
|
|
|
int64_t SPI::GetAccumulatorValue() const {
|
|
|
|
|
int32_t status = 0;
|
2016-07-09 01:12:37 -07:00
|
|
|
int64_t retVal = HAL_GetSPIAccumulatorValue(m_port, &status);
|
2016-07-09 00:24:26 -07:00
|
|
|
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
|
2015-11-22 11:50:49 -08:00
|
|
|
return retVal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Read the number of accumulated values.
|
|
|
|
|
*
|
2016-05-20 17:30:37 -07:00
|
|
|
* Read the count of the accumulated values since the accumulator was last
|
|
|
|
|
* Reset().
|
2015-11-22 11:50:49 -08:00
|
|
|
*
|
|
|
|
|
* @return The number of times samples from the channel were accumulated.
|
|
|
|
|
*/
|
2016-07-12 10:45:14 -07:00
|
|
|
int64_t SPI::GetAccumulatorCount() const {
|
2015-11-22 11:50:49 -08:00
|
|
|
int32_t status = 0;
|
2016-07-12 10:45:14 -07:00
|
|
|
int64_t retVal = HAL_GetSPIAccumulatorCount(m_port, &status);
|
2016-07-09 00:24:26 -07:00
|
|
|
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
|
2015-11-22 11:50:49 -08:00
|
|
|
return retVal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Read the average of the accumulated value.
|
|
|
|
|
*
|
|
|
|
|
* @return The accumulated average value (value / count).
|
|
|
|
|
*/
|
|
|
|
|
double SPI::GetAccumulatorAverage() const {
|
|
|
|
|
int32_t status = 0;
|
2016-07-09 01:12:37 -07:00
|
|
|
double retVal = HAL_GetSPIAccumulatorAverage(m_port, &status);
|
2016-07-09 00:24:26 -07:00
|
|
|
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
|
2015-11-22 11:50:49 -08:00
|
|
|
return retVal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Read the accumulated value and the number of accumulated values atomically.
|
|
|
|
|
*
|
|
|
|
|
* This function reads the value and count atomically.
|
|
|
|
|
* This can be used for averaging.
|
|
|
|
|
*
|
|
|
|
|
* @param value Pointer to the 64-bit accumulated output.
|
|
|
|
|
* @param count Pointer to the number of accumulation cycles.
|
|
|
|
|
*/
|
2016-07-12 10:45:14 -07:00
|
|
|
void SPI::GetAccumulatorOutput(int64_t& value, int64_t& count) const {
|
2015-11-22 11:50:49 -08:00
|
|
|
int32_t status = 0;
|
2016-07-09 01:12:37 -07:00
|
|
|
HAL_GetSPIAccumulatorOutput(m_port, &value, &count, &status);
|
2016-07-09 00:24:26 -07:00
|
|
|
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|