2020-12-26 14:12:05 -08:00
|
|
|
// Copyright (c) FIRST and other WPILib contributors.
|
|
|
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
|
|
|
// the WPILib BSD license file in the root directory of this project.
|
2016-01-02 03:02:34 -08:00
|
|
|
|
2014-07-16 16:24:44 -04:00
|
|
|
package edu.wpi.first.wpilibj;
|
|
|
|
|
|
2018-09-20 21:59:46 -07:00
|
|
|
import edu.wpi.first.hal.AccumulatorResult;
|
|
|
|
|
import edu.wpi.first.hal.FRCNetComm.tResourceType;
|
|
|
|
|
import edu.wpi.first.hal.HAL;
|
|
|
|
|
import edu.wpi.first.hal.SPIJNI;
|
2020-12-29 22:45:16 -08:00
|
|
|
import java.nio.ByteBuffer;
|
|
|
|
|
import java.nio.ByteOrder;
|
|
|
|
|
import java.nio.IntBuffer;
|
2014-07-16 16:24:44 -04:00
|
|
|
|
2020-12-29 22:45:16 -08:00
|
|
|
/** Represents a SPI bus port. */
|
2018-05-22 23:33:17 -07:00
|
|
|
public class SPI implements AutoCloseable {
|
2015-06-25 15:07:55 -04:00
|
|
|
public enum Port {
|
2022-09-24 00:11:37 -07:00
|
|
|
kOnboardCS0(SPIJNI.ONBOARD_CS0_PORT),
|
|
|
|
|
kOnboardCS1(SPIJNI.ONBOARD_CS0_PORT),
|
|
|
|
|
kOnboardCS2(SPIJNI.ONBOARD_CS0_PORT),
|
|
|
|
|
kOnboardCS3(SPIJNI.ONBOARD_CS0_PORT),
|
|
|
|
|
kMXP(SPIJNI.MXP_PORT);
|
2015-06-25 15:07:55 -04:00
|
|
|
|
2018-07-29 16:47:22 -07:00
|
|
|
public final int value;
|
2015-06-25 15:07:55 -04:00
|
|
|
|
2017-10-17 21:47:55 -07:00
|
|
|
Port(int value) {
|
2016-07-13 23:39:58 -07:00
|
|
|
this.value = value;
|
2014-07-16 16:24:44 -04:00
|
|
|
}
|
2016-05-20 12:07:40 -04:00
|
|
|
}
|
2015-06-25 15:07:55 -04:00
|
|
|
|
2022-09-24 00:11:37 -07:00
|
|
|
public enum Mode {
|
|
|
|
|
kMode0(SPIJNI.SPI_MODE0),
|
|
|
|
|
kMode1(SPIJNI.SPI_MODE1),
|
|
|
|
|
kMode2(SPIJNI.SPI_MODE2),
|
|
|
|
|
kMode3(SPIJNI.SPI_MODE3);
|
|
|
|
|
|
|
|
|
|
public final int value;
|
|
|
|
|
|
|
|
|
|
Mode(int value) {
|
|
|
|
|
this.value = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-09 12:12:46 -07:00
|
|
|
private int m_port;
|
2022-09-24 00:11:37 -07:00
|
|
|
private int m_mode;
|
2015-06-25 15:07:55 -04:00
|
|
|
|
|
|
|
|
/**
|
2016-05-20 12:07:40 -04:00
|
|
|
* Constructor.
|
2015-06-25 15:07:55 -04:00
|
|
|
*
|
|
|
|
|
* @param port the physical SPI port
|
|
|
|
|
*/
|
|
|
|
|
public SPI(Port port) {
|
2022-09-24 00:11:37 -07:00
|
|
|
m_port = port.value;
|
2015-06-25 15:07:55 -04:00
|
|
|
|
2015-11-01 09:11:52 -08:00
|
|
|
SPIJNI.spiInitialize(m_port);
|
2015-06-25 15:07:55 -04:00
|
|
|
|
2022-09-24 00:11:37 -07:00
|
|
|
m_mode = 0;
|
|
|
|
|
SPIJNI.spiSetMode(m_port, m_mode);
|
|
|
|
|
|
2019-10-29 21:34:10 -07:00
|
|
|
HAL.report(tResourceType.kResourceType_SPI, port.value + 1);
|
2015-06-25 15:07:55 -04:00
|
|
|
}
|
|
|
|
|
|
2021-08-21 02:19:59 -04:00
|
|
|
public int getPort() {
|
|
|
|
|
return m_port;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-22 23:33:17 -07:00
|
|
|
@Override
|
|
|
|
|
public void close() {
|
2017-12-13 23:41:37 -08:00
|
|
|
if (m_accum != null) {
|
2018-05-22 23:33:17 -07:00
|
|
|
m_accum.close();
|
2017-12-13 23:41:37 -08:00
|
|
|
m_accum = null;
|
|
|
|
|
}
|
2015-06-25 15:07:55 -04:00
|
|
|
SPIJNI.spiClose(m_port);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2016-05-20 12:07:40 -04:00
|
|
|
* Configure the rate of the generated clock signal. The default value is 500,000 Hz. The maximum
|
|
|
|
|
* value is 4,000,000 Hz.
|
2015-06-25 15:07:55 -04:00
|
|
|
*
|
|
|
|
|
* @param hz The clock rate in Hertz.
|
|
|
|
|
*/
|
|
|
|
|
public final void setClockRate(int hz) {
|
|
|
|
|
SPIJNI.spiSetSpeed(m_port, hz);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2016-05-20 12:07:40 -04:00
|
|
|
* Configure the order that bits are sent and received on the wire to be most significant bit
|
|
|
|
|
* first.
|
2022-09-24 00:11:37 -07:00
|
|
|
*
|
|
|
|
|
* @deprecated Does not work, will be removed.
|
2015-06-25 15:07:55 -04:00
|
|
|
*/
|
2022-09-24 00:11:37 -07:00
|
|
|
@Deprecated(since = "2023", forRemoval = true)
|
2015-06-25 15:07:55 -04:00
|
|
|
public final void setMSBFirst() {
|
2022-09-24 00:11:37 -07:00
|
|
|
DriverStation.reportWarning("setMSBFirst not supported by roboRIO", false);
|
2015-06-25 15:07:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2016-05-20 12:07:40 -04:00
|
|
|
* Configure the order that bits are sent and received on the wire to be least significant bit
|
|
|
|
|
* first.
|
2022-09-24 00:11:37 -07:00
|
|
|
*
|
|
|
|
|
* @deprecated Does not work, will be removed.
|
2015-06-25 15:07:55 -04:00
|
|
|
*/
|
2022-09-24 00:11:37 -07:00
|
|
|
@Deprecated(since = "2023", forRemoval = true)
|
2015-06-25 15:07:55 -04:00
|
|
|
public final void setLSBFirst() {
|
2022-09-24 00:11:37 -07:00
|
|
|
DriverStation.reportWarning("setLSBFirst not supported by roboRIO", false);
|
2015-06-25 15:07:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2016-05-20 12:07:40 -04:00
|
|
|
* Configure the clock output line to be active low. This is sometimes called clock polarity high
|
|
|
|
|
* or clock idle high.
|
2022-09-24 00:11:37 -07:00
|
|
|
*
|
|
|
|
|
* @deprecated Use setMode() instead.
|
2015-06-25 15:07:55 -04:00
|
|
|
*/
|
2022-09-24 00:11:37 -07:00
|
|
|
@Deprecated(since = "2023", forRemoval = true)
|
2015-06-25 15:07:55 -04:00
|
|
|
public final void setClockActiveLow() {
|
2022-09-24 00:11:37 -07:00
|
|
|
m_mode |= 1;
|
|
|
|
|
SPIJNI.spiSetMode(m_port, m_mode);
|
2015-06-25 15:07:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2016-05-20 12:07:40 -04:00
|
|
|
* Configure the clock output line to be active high. This is sometimes called clock polarity low
|
|
|
|
|
* or clock idle low.
|
2022-09-24 00:11:37 -07:00
|
|
|
*
|
|
|
|
|
* @deprecated Use setMode() instead.
|
2015-06-25 15:07:55 -04:00
|
|
|
*/
|
2022-09-24 00:11:37 -07:00
|
|
|
@Deprecated(since = "2023", forRemoval = true)
|
2015-06-25 15:07:55 -04:00
|
|
|
public final void setClockActiveHigh() {
|
2022-09-24 00:11:37 -07:00
|
|
|
m_mode &= 1;
|
|
|
|
|
SPIJNI.spiSetMode(m_port, m_mode);
|
2015-06-25 15:07:55 -04:00
|
|
|
}
|
|
|
|
|
|
2018-05-14 18:16:36 -07:00
|
|
|
/**
|
|
|
|
|
* Configure that the data is stable on the leading edge and the data changes on the trailing
|
|
|
|
|
* edge.
|
2022-09-24 00:11:37 -07:00
|
|
|
*
|
|
|
|
|
* @deprecated Use setMode() instead.
|
2018-05-14 18:16:36 -07:00
|
|
|
*/
|
2022-09-24 00:11:37 -07:00
|
|
|
@Deprecated(since = "2023", forRemoval = true)
|
2018-05-14 18:16:36 -07:00
|
|
|
public final void setSampleDataOnLeadingEdge() {
|
2022-09-24 00:11:37 -07:00
|
|
|
m_mode &= 2;
|
|
|
|
|
SPIJNI.spiSetMode(m_port, m_mode);
|
2018-05-14 18:16:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Configure that the data is stable on the trailing edge and the data changes on the leading
|
|
|
|
|
* edge.
|
2022-09-24 00:11:37 -07:00
|
|
|
*
|
|
|
|
|
* @deprecated Use setMode() instead.
|
2018-05-14 18:16:36 -07:00
|
|
|
*/
|
2022-09-24 00:11:37 -07:00
|
|
|
@Deprecated(since = "2023", forRemoval = true)
|
2018-05-14 18:16:36 -07:00
|
|
|
public final void setSampleDataOnTrailingEdge() {
|
2022-09-24 00:11:37 -07:00
|
|
|
m_mode |= 2;
|
|
|
|
|
SPIJNI.spiSetMode(m_port, m_mode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sets the mode for the SPI device.
|
|
|
|
|
*
|
|
|
|
|
* <p>Mode 0 is Clock idle low, data sampled on rising edge.
|
|
|
|
|
*
|
|
|
|
|
* <p>Mode 1 is Clock idle low, data sampled on falling edge.
|
|
|
|
|
*
|
|
|
|
|
* <p>Mode 2 is Clock idle high, data sampled on falling edge.
|
|
|
|
|
*
|
|
|
|
|
* <p>Mode 3 is Clock idle high, data sampled on rising edge.
|
|
|
|
|
*
|
|
|
|
|
* @param mode The mode to set.
|
|
|
|
|
*/
|
|
|
|
|
public final void setMode(Mode mode) {
|
|
|
|
|
m_mode = mode.value & 0x3;
|
|
|
|
|
SPIJNI.spiSetMode(m_port, m_mode);
|
2015-06-25 15:07:55 -04:00
|
|
|
}
|
|
|
|
|
|
2020-12-29 22:45:16 -08:00
|
|
|
/** Configure the chip select line to be active high. */
|
2015-06-25 15:07:55 -04:00
|
|
|
public final void setChipSelectActiveHigh() {
|
2015-11-01 09:11:52 -08:00
|
|
|
SPIJNI.spiSetChipSelectActiveHigh(m_port);
|
2015-06-25 15:07:55 -04:00
|
|
|
}
|
|
|
|
|
|
2020-12-29 22:45:16 -08:00
|
|
|
/** Configure the chip select line to be active low. */
|
2015-06-25 15:07:55 -04:00
|
|
|
public final void setChipSelectActiveLow() {
|
2015-11-01 09:11:52 -08:00
|
|
|
SPIJNI.spiSetChipSelectActiveLow(m_port);
|
2015-06-25 15:07:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2020-09-19 10:35:38 -07:00
|
|
|
* Write data to the peripheral device. Blocks until there is space in the output FIFO.
|
2015-06-25 15:07:55 -04:00
|
|
|
*
|
2020-09-19 10:35:38 -07:00
|
|
|
* <p>If not running in output only mode, also saves the data received on the CIPO input during
|
2016-05-20 12:07:40 -04:00
|
|
|
* the transfer into the receive FIFO.
|
2021-06-10 20:46:47 -07:00
|
|
|
*
|
|
|
|
|
* @param dataToSend The buffer containing the data to send.
|
|
|
|
|
* @param size The number of bytes to send.
|
|
|
|
|
* @return Number of bytes written or -1 on error.
|
2015-06-25 15:07:55 -04:00
|
|
|
*/
|
|
|
|
|
public int write(byte[] dataToSend, int size) {
|
2017-11-14 00:00:45 -08:00
|
|
|
if (dataToSend.length < size) {
|
|
|
|
|
throw new IllegalArgumentException("buffer is too small, must be at least " + size);
|
|
|
|
|
}
|
|
|
|
|
return SPIJNI.spiWriteB(m_port, dataToSend, (byte) size);
|
2015-11-06 11:12:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2020-09-19 10:35:38 -07:00
|
|
|
* Write data to the peripheral device. Blocks until there is space in the output FIFO.
|
2015-11-06 11:12:00 -08:00
|
|
|
*
|
2020-09-19 10:35:38 -07:00
|
|
|
* <p>If not running in output only mode, also saves the data received on the CIPO input during
|
2016-05-20 12:07:40 -04:00
|
|
|
* the transfer into the receive FIFO.
|
2015-11-06 11:12:00 -08:00
|
|
|
*
|
2017-11-14 00:00:45 -08:00
|
|
|
* @param dataToSend The buffer containing the data to send.
|
2021-06-10 20:46:47 -07:00
|
|
|
* @param size The number of bytes to send.
|
|
|
|
|
* @return Number of bytes written or -1 on error.
|
2015-11-06 11:12:00 -08:00
|
|
|
*/
|
|
|
|
|
public int write(ByteBuffer dataToSend, int size) {
|
2017-11-14 00:00:45 -08:00
|
|
|
if (dataToSend.hasArray()) {
|
|
|
|
|
return write(dataToSend.array(), size);
|
|
|
|
|
}
|
2016-05-20 12:07:40 -04:00
|
|
|
if (!dataToSend.isDirect()) {
|
2015-11-06 11:12:00 -08:00
|
|
|
throw new IllegalArgumentException("must be a direct buffer");
|
2016-05-20 12:07:40 -04:00
|
|
|
}
|
|
|
|
|
if (dataToSend.capacity() < size) {
|
2015-11-06 11:12:00 -08:00
|
|
|
throw new IllegalArgumentException("buffer is too small, must be at least " + size);
|
2016-05-20 12:07:40 -04:00
|
|
|
}
|
2015-11-06 11:12:00 -08:00
|
|
|
return SPIJNI.spiWrite(m_port, dataToSend, (byte) size);
|
2015-06-25 15:07:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Read a word from the receive FIFO.
|
|
|
|
|
*
|
2016-05-20 12:07:40 -04:00
|
|
|
* <p>Waits for the current transfer to complete if the receive FIFO is empty.
|
2015-06-25 15:07:55 -04:00
|
|
|
*
|
2016-05-20 12:07:40 -04:00
|
|
|
* <p>If the receive FIFO is empty, there is no active transfer, and initiate is false, errors.
|
2015-06-25 15:07:55 -04:00
|
|
|
*
|
2016-05-20 12:07:40 -04:00
|
|
|
* @param initiate If true, this function pushes "0" into the transmit buffer and initiates a
|
2020-12-29 22:45:16 -08:00
|
|
|
* transfer. If false, this function assumes that data is already in the receive FIFO from a
|
|
|
|
|
* previous write.
|
2021-06-10 20:46:47 -07:00
|
|
|
* @param dataReceived Buffer in which to store bytes read.
|
|
|
|
|
* @param size Number of bytes to read.
|
|
|
|
|
* @return Number of bytes read or -1 on error.
|
2015-06-25 15:07:55 -04:00
|
|
|
*/
|
|
|
|
|
public int read(boolean initiate, byte[] dataReceived, int size) {
|
2017-11-14 00:00:45 -08:00
|
|
|
if (dataReceived.length < size) {
|
|
|
|
|
throw new IllegalArgumentException("buffer is too small, must be at least " + size);
|
2016-05-20 12:07:40 -04:00
|
|
|
}
|
2017-11-14 00:00:45 -08:00
|
|
|
return SPIJNI.spiReadB(m_port, initiate, dataReceived, (byte) size);
|
2015-06-25 15:07:55 -04:00
|
|
|
}
|
|
|
|
|
|
2015-11-06 11:12:00 -08:00
|
|
|
/**
|
|
|
|
|
* Read a word from the receive FIFO.
|
|
|
|
|
*
|
2016-05-20 12:07:40 -04:00
|
|
|
* <p>Waits for the current transfer to complete if the receive FIFO is empty.
|
2015-11-06 11:12:00 -08:00
|
|
|
*
|
2016-05-20 12:07:40 -04:00
|
|
|
* <p>If the receive FIFO is empty, there is no active transfer, and initiate is false, errors.
|
2015-11-06 11:12:00 -08:00
|
|
|
*
|
2020-12-29 22:45:16 -08: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.
|
2017-11-14 00:00:45 -08:00
|
|
|
* @param dataReceived The buffer to be filled with the received data.
|
2020-12-29 22:45:16 -08:00
|
|
|
* @param size The length of the transaction, in bytes
|
2021-06-10 20:46:47 -07:00
|
|
|
* @return Number of bytes read or -1 on error.
|
2015-11-06 11:12:00 -08:00
|
|
|
*/
|
|
|
|
|
public int read(boolean initiate, ByteBuffer dataReceived, int size) {
|
2017-11-14 00:00:45 -08:00
|
|
|
if (dataReceived.hasArray()) {
|
|
|
|
|
return read(initiate, dataReceived.array(), size);
|
|
|
|
|
}
|
2016-05-20 12:07:40 -04:00
|
|
|
if (!dataReceived.isDirect()) {
|
2015-11-06 11:12:00 -08:00
|
|
|
throw new IllegalArgumentException("must be a direct buffer");
|
2016-05-20 12:07:40 -04:00
|
|
|
}
|
|
|
|
|
if (dataReceived.capacity() < size) {
|
2015-11-06 11:12:00 -08:00
|
|
|
throw new IllegalArgumentException("buffer is too small, must be at least " + size);
|
2016-05-20 12:07:40 -04:00
|
|
|
}
|
2017-11-14 00:00:45 -08:00
|
|
|
return SPIJNI.spiRead(m_port, initiate, dataReceived, (byte) size);
|
2015-11-06 11:12:00 -08:00
|
|
|
}
|
|
|
|
|
|
2015-06-25 15:07:55 -04:00
|
|
|
/**
|
2016-05-20 12:07:40 -04:00
|
|
|
* Perform a simultaneous read/write transaction with the device.
|
2015-06-25 15:07:55 -04:00
|
|
|
*
|
2020-12-29 22:45:16 -08:00
|
|
|
* @param dataToSend The data to be written out to the device
|
2015-06-25 15:07:55 -04:00
|
|
|
* @param dataReceived Buffer to receive data from the device
|
2020-12-29 22:45:16 -08:00
|
|
|
* @param size The length of the transaction, in bytes
|
2021-06-10 20:46:47 -07:00
|
|
|
* @return TODO
|
2015-06-25 15:07:55 -04:00
|
|
|
*/
|
|
|
|
|
public int transaction(byte[] dataToSend, byte[] dataReceived, int size) {
|
2017-11-14 00:00:45 -08:00
|
|
|
if (dataToSend.length < size) {
|
|
|
|
|
throw new IllegalArgumentException("dataToSend is too small, must be at least " + size);
|
|
|
|
|
}
|
|
|
|
|
if (dataReceived.length < size) {
|
|
|
|
|
throw new IllegalArgumentException("dataReceived is too small, must be at least " + size);
|
|
|
|
|
}
|
|
|
|
|
return SPIJNI.spiTransactionB(m_port, dataToSend, dataReceived, (byte) size);
|
2015-06-25 15:07:55 -04:00
|
|
|
}
|
2015-11-06 11:12:00 -08:00
|
|
|
|
|
|
|
|
/**
|
2017-07-28 22:24:05 -07:00
|
|
|
* Perform a simultaneous read/write transaction with the device.
|
2015-11-06 11:12:00 -08:00
|
|
|
*
|
2020-12-29 22:45:16 -08:00
|
|
|
* @param dataToSend The data to be written out to the device.
|
2017-11-14 00:00:45 -08:00
|
|
|
* @param dataReceived Buffer to receive data from the device.
|
2020-12-29 22:45:16 -08:00
|
|
|
* @param size The length of the transaction, in bytes
|
2021-06-10 20:46:47 -07:00
|
|
|
* @return TODO
|
2015-11-06 11:12:00 -08:00
|
|
|
*/
|
|
|
|
|
public int transaction(ByteBuffer dataToSend, ByteBuffer dataReceived, int size) {
|
2017-11-14 00:00:45 -08:00
|
|
|
if (dataToSend.hasArray() && dataReceived.hasArray()) {
|
|
|
|
|
return transaction(dataToSend.array(), dataReceived.array(), size);
|
|
|
|
|
}
|
2016-05-20 12:07:40 -04:00
|
|
|
if (!dataToSend.isDirect()) {
|
2015-11-06 11:12:00 -08:00
|
|
|
throw new IllegalArgumentException("dataToSend must be a direct buffer");
|
2016-05-20 12:07:40 -04:00
|
|
|
}
|
|
|
|
|
if (dataToSend.capacity() < size) {
|
2015-11-06 11:12:00 -08:00
|
|
|
throw new IllegalArgumentException("dataToSend is too small, must be at least " + size);
|
2016-05-20 12:07:40 -04:00
|
|
|
}
|
|
|
|
|
if (!dataReceived.isDirect()) {
|
2015-11-06 11:12:00 -08:00
|
|
|
throw new IllegalArgumentException("dataReceived must be a direct buffer");
|
2016-05-20 12:07:40 -04:00
|
|
|
}
|
|
|
|
|
if (dataReceived.capacity() < size) {
|
2015-11-06 11:12:00 -08:00
|
|
|
throw new IllegalArgumentException("dataReceived is too small, must be at least " + size);
|
2016-05-20 12:07:40 -04:00
|
|
|
}
|
2015-11-06 11:12:00 -08:00
|
|
|
return SPIJNI.spiTransaction(m_port, dataToSend, dataReceived, (byte) size);
|
|
|
|
|
}
|
2015-11-22 11:50:49 -08:00
|
|
|
|
2017-12-13 23:41:37 -08:00
|
|
|
/**
|
|
|
|
|
* Initialize automatic SPI transfer engine.
|
|
|
|
|
*
|
2020-12-29 22:45:16 -08:00
|
|
|
* <p>Only a single engine is available, and use of it blocks use of all other chip select usage
|
|
|
|
|
* on the same physical SPI port while it is running.
|
2017-12-13 23:41:37 -08:00
|
|
|
*
|
|
|
|
|
* @param bufferSize buffer size in bytes
|
|
|
|
|
*/
|
|
|
|
|
public void initAuto(int bufferSize) {
|
|
|
|
|
SPIJNI.spiInitAuto(m_port, bufferSize);
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-29 22:45:16 -08:00
|
|
|
/** Frees the automatic SPI transfer engine. */
|
2017-12-13 23:41:37 -08:00
|
|
|
public void freeAuto() {
|
|
|
|
|
SPIJNI.spiFreeAuto(m_port);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the data to be transmitted by the engine.
|
|
|
|
|
*
|
2020-12-29 22:45:16 -08:00
|
|
|
* <p>Up to 16 bytes are configurable, and may be followed by up to 127 zero bytes.
|
2017-12-13 23:41:37 -08:00
|
|
|
*
|
|
|
|
|
* @param dataToSend data to send (maximum 16 bytes)
|
|
|
|
|
* @param zeroSize number of zeros to send after the data
|
|
|
|
|
*/
|
|
|
|
|
public void setAutoTransmitData(byte[] dataToSend, int zeroSize) {
|
|
|
|
|
SPIJNI.spiSetAutoTransmitData(m_port, dataToSend, zeroSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Start running the automatic SPI transfer engine at a periodic rate.
|
|
|
|
|
*
|
2020-12-29 22:45:16 -08:00
|
|
|
* <p>{@link #initAuto(int)} and {@link #setAutoTransmitData(byte[], int)} must be called before
|
|
|
|
|
* calling this function.
|
2017-12-13 23:41:37 -08:00
|
|
|
*
|
|
|
|
|
* @param period period between transfers, in seconds (us resolution)
|
|
|
|
|
*/
|
|
|
|
|
public void startAutoRate(double period) {
|
|
|
|
|
SPIJNI.spiStartAutoRate(m_port, period);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Start running the automatic SPI transfer engine when a trigger occurs.
|
|
|
|
|
*
|
2020-12-29 22:45:16 -08:00
|
|
|
* <p>{@link #initAuto(int)} and {@link #setAutoTransmitData(byte[], int)} must be called before
|
|
|
|
|
* calling this function.
|
2017-12-13 23:41:37 -08:00
|
|
|
*
|
|
|
|
|
* @param source digital source for the trigger (may be an analog trigger)
|
|
|
|
|
* @param rising trigger on the rising edge
|
|
|
|
|
* @param falling trigger on the falling edge
|
|
|
|
|
*/
|
|
|
|
|
public void startAutoTrigger(DigitalSource source, boolean rising, boolean falling) {
|
2020-12-29 22:45:16 -08:00
|
|
|
SPIJNI.spiStartAutoTrigger(
|
|
|
|
|
m_port,
|
|
|
|
|
source.getPortHandleForRouting(),
|
|
|
|
|
source.getAnalogTriggerTypeForRouting(),
|
|
|
|
|
rising,
|
|
|
|
|
falling);
|
2017-12-13 23:41:37 -08:00
|
|
|
}
|
|
|
|
|
|
2020-12-29 22:45:16 -08:00
|
|
|
/** Stop running the automatic SPI transfer engine. */
|
2017-12-13 23:41:37 -08:00
|
|
|
public void stopAuto() {
|
|
|
|
|
SPIJNI.spiStopAuto(m_port);
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-29 22:45:16 -08:00
|
|
|
/** Force the engine to make a single transfer. */
|
2017-12-13 23:41:37 -08:00
|
|
|
public void forceAutoRead() {
|
|
|
|
|
SPIJNI.spiForceAutoRead(m_port);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Read data that has been transferred by the automatic SPI transfer engine.
|
|
|
|
|
*
|
2020-12-29 22:45:16 -08:00
|
|
|
* <p>Transfers may be made a byte at a time, so it's necessary for the caller to handle cases
|
|
|
|
|
* where an entire transfer has not been completed.
|
2017-12-13 23:41:37 -08:00
|
|
|
*
|
2020-12-29 22:45:16 -08:00
|
|
|
* <p>Each received data sequence consists of a timestamp followed by the received data bytes, one
|
|
|
|
|
* byte per word (in the least significant byte). The length of each received data sequence is the
|
|
|
|
|
* same as the combined size of the data and zeroSize set in setAutoTransmitData().
|
2017-12-13 23:41:37 -08:00
|
|
|
*
|
2020-12-29 22:45:16 -08:00
|
|
|
* <p>Blocks until numToRead words have been read or timeout expires. May be called with
|
|
|
|
|
* numToRead=0 to retrieve how many words are available.
|
2018-12-06 22:29:20 -08:00
|
|
|
*
|
|
|
|
|
* @param buffer buffer where read words are stored
|
|
|
|
|
* @param numToRead number of words to read
|
2017-12-13 23:41:37 -08:00
|
|
|
* @param timeout timeout in seconds (ms resolution)
|
2018-12-06 22:29:20 -08:00
|
|
|
* @return Number of words remaining to be read
|
2017-12-13 23:41:37 -08:00
|
|
|
*/
|
|
|
|
|
public int readAutoReceivedData(ByteBuffer buffer, int numToRead, double timeout) {
|
|
|
|
|
if (!buffer.isDirect()) {
|
|
|
|
|
throw new IllegalArgumentException("must be a direct buffer");
|
|
|
|
|
}
|
2018-12-06 22:29:20 -08:00
|
|
|
if (buffer.capacity() < numToRead * 4) {
|
2020-12-29 22:45:16 -08:00
|
|
|
throw new IllegalArgumentException(
|
|
|
|
|
"buffer is too small, must be at least " + (numToRead * 4));
|
2017-12-13 23:41:37 -08:00
|
|
|
}
|
|
|
|
|
return SPIJNI.spiReadAutoReceivedData(m_port, buffer, numToRead, timeout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Read data that has been transferred by the automatic SPI transfer engine.
|
|
|
|
|
*
|
2020-12-29 22:45:16 -08:00
|
|
|
* <p>Transfers may be made a byte at a time, so it's necessary for the caller to handle cases
|
|
|
|
|
* where an entire transfer has not been completed.
|
2017-12-13 23:41:37 -08:00
|
|
|
*
|
2020-12-29 22:45:16 -08:00
|
|
|
* <p>Each received data sequence consists of a timestamp followed by the received data bytes, one
|
|
|
|
|
* byte per word (in the least significant byte). The length of each received data sequence is the
|
|
|
|
|
* same as the combined size of the data and zeroSize set in setAutoTransmitData().
|
2018-12-06 22:29:20 -08:00
|
|
|
*
|
2020-12-29 22:45:16 -08:00
|
|
|
* <p>Blocks until numToRead words have been read or timeout expires. May be called with
|
|
|
|
|
* numToRead=0 to retrieve how many words are available.
|
2017-12-13 23:41:37 -08:00
|
|
|
*
|
2018-12-06 22:29:20 -08:00
|
|
|
* @param buffer array where read words are stored
|
|
|
|
|
* @param numToRead number of words to read
|
2017-12-13 23:41:37 -08:00
|
|
|
* @param timeout timeout in seconds (ms resolution)
|
2018-12-06 22:29:20 -08:00
|
|
|
* @return Number of words remaining to be read
|
2017-12-13 23:41:37 -08:00
|
|
|
*/
|
2018-12-06 22:29:20 -08:00
|
|
|
public int readAutoReceivedData(int[] buffer, int numToRead, double timeout) {
|
2017-12-13 23:41:37 -08:00
|
|
|
if (buffer.length < numToRead) {
|
|
|
|
|
throw new IllegalArgumentException("buffer is too small, must be at least " + numToRead);
|
|
|
|
|
}
|
|
|
|
|
return SPIJNI.spiReadAutoReceivedData(m_port, buffer, numToRead, timeout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2020-12-29 22:45:16 -08:00
|
|
|
* Get the number of bytes dropped by the automatic SPI transfer engine due to the receive buffer
|
|
|
|
|
* being full.
|
2017-12-13 23:41:37 -08:00
|
|
|
*
|
|
|
|
|
* @return Number of bytes dropped
|
|
|
|
|
*/
|
|
|
|
|
public int getAutoDroppedCount() {
|
|
|
|
|
return SPIJNI.spiGetAutoDroppedCount(m_port);
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-24 10:52:58 -08:00
|
|
|
/**
|
|
|
|
|
* Configure the Auto SPI Stall time between reads.
|
|
|
|
|
*
|
|
|
|
|
* @param csToSclkTicks the number of ticks to wait before asserting the cs pin
|
|
|
|
|
* @param stallTicks the number of ticks to stall for
|
|
|
|
|
* @param pow2BytesPerRead the number of bytes to read before stalling
|
|
|
|
|
*/
|
|
|
|
|
public void configureAutoStall(int csToSclkTicks, int stallTicks, int pow2BytesPerRead) {
|
|
|
|
|
SPIJNI.spiConfigureAutoStall(m_port, csToSclkTicks, stallTicks, pow2BytesPerRead);
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-13 23:41:37 -08:00
|
|
|
private static final int kAccumulateDepth = 2048;
|
|
|
|
|
|
2018-05-22 23:33:17 -07:00
|
|
|
private static class Accumulator implements AutoCloseable {
|
2020-12-29 22:45:16 -08:00
|
|
|
Accumulator(
|
|
|
|
|
int port,
|
|
|
|
|
int xferSize,
|
|
|
|
|
int validMask,
|
|
|
|
|
int validValue,
|
|
|
|
|
int dataShift,
|
|
|
|
|
int dataSize,
|
|
|
|
|
boolean isSigned,
|
|
|
|
|
boolean bigEndian) {
|
2017-12-13 23:41:37 -08:00
|
|
|
m_notifier = new Notifier(this::update);
|
2020-12-29 22:45:16 -08:00
|
|
|
m_buf =
|
|
|
|
|
ByteBuffer.allocateDirect((xferSize + 1) * kAccumulateDepth * 4)
|
|
|
|
|
.order(ByteOrder.nativeOrder());
|
2018-12-06 22:29:20 -08:00
|
|
|
m_intBuf = m_buf.asIntBuffer();
|
2020-12-29 22:45:16 -08:00
|
|
|
m_xferSize = xferSize + 1; // +1 for timestamp
|
2017-12-13 23:41:37 -08:00
|
|
|
m_validMask = validMask;
|
|
|
|
|
m_validValue = validValue;
|
|
|
|
|
m_dataShift = dataShift;
|
|
|
|
|
m_dataMax = 1 << dataSize;
|
|
|
|
|
m_dataMsbMask = 1 << (dataSize - 1);
|
|
|
|
|
m_isSigned = isSigned;
|
|
|
|
|
m_bigEndian = bigEndian;
|
|
|
|
|
m_port = port;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-22 23:33:17 -07:00
|
|
|
@Override
|
|
|
|
|
public void close() {
|
2018-05-24 16:56:29 -07:00
|
|
|
m_notifier.close();
|
2017-12-13 23:41:37 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final Notifier m_notifier;
|
|
|
|
|
final ByteBuffer m_buf;
|
2018-12-06 22:29:20 -08:00
|
|
|
final IntBuffer m_intBuf;
|
2017-12-13 23:41:37 -08:00
|
|
|
final Object m_mutex = new Object();
|
|
|
|
|
|
|
|
|
|
long m_value;
|
|
|
|
|
int m_count;
|
|
|
|
|
int m_lastValue;
|
2018-12-06 22:29:20 -08:00
|
|
|
long m_lastTimestamp;
|
|
|
|
|
double m_integratedValue;
|
2017-12-13 23:41:37 -08:00
|
|
|
|
|
|
|
|
int m_center;
|
|
|
|
|
int m_deadband;
|
2018-12-06 22:29:20 -08:00
|
|
|
double m_integratedCenter;
|
2017-12-13 23:41:37 -08:00
|
|
|
|
|
|
|
|
final int m_validMask;
|
|
|
|
|
final int m_validValue;
|
2020-12-29 22:45:16 -08:00
|
|
|
final int m_dataMax; // one more than max data value
|
|
|
|
|
final int m_dataMsbMask; // data field MSB mask (for signed)
|
|
|
|
|
final int m_dataShift; // data field shift right amount, in bits
|
|
|
|
|
final int m_xferSize; // SPI transfer size, in bytes
|
|
|
|
|
final boolean m_isSigned; // is data field signed?
|
|
|
|
|
final boolean m_bigEndian; // is response big endian?
|
2017-12-13 23:41:37 -08:00
|
|
|
final int m_port;
|
|
|
|
|
|
|
|
|
|
void update() {
|
|
|
|
|
synchronized (m_mutex) {
|
|
|
|
|
boolean done = false;
|
|
|
|
|
while (!done) {
|
|
|
|
|
done = true;
|
|
|
|
|
|
|
|
|
|
// get amount of data available
|
|
|
|
|
int numToRead = SPIJNI.spiReadAutoReceivedData(m_port, m_buf, 0, 0);
|
|
|
|
|
|
|
|
|
|
// only get whole responses
|
|
|
|
|
numToRead -= numToRead % m_xferSize;
|
|
|
|
|
if (numToRead > m_xferSize * kAccumulateDepth) {
|
|
|
|
|
numToRead = m_xferSize * kAccumulateDepth;
|
|
|
|
|
done = false;
|
|
|
|
|
}
|
|
|
|
|
if (numToRead == 0) {
|
2020-12-29 22:45:16 -08:00
|
|
|
return; // no samples
|
2017-12-13 23:41:37 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// read buffered data
|
|
|
|
|
SPIJNI.spiReadAutoReceivedData(m_port, m_buf, numToRead, 0);
|
|
|
|
|
|
|
|
|
|
// loop over all responses
|
|
|
|
|
for (int off = 0; off < numToRead; off += m_xferSize) {
|
2018-12-06 22:29:20 -08:00
|
|
|
// get timestamp from first word
|
|
|
|
|
long timestamp = m_intBuf.get(off) & 0xffffffffL;
|
|
|
|
|
|
2017-12-13 23:41:37 -08:00
|
|
|
// convert from bytes
|
|
|
|
|
int resp = 0;
|
|
|
|
|
if (m_bigEndian) {
|
2018-12-06 22:29:20 -08:00
|
|
|
for (int i = 1; i < m_xferSize; ++i) {
|
2017-12-13 23:41:37 -08:00
|
|
|
resp <<= 8;
|
2018-12-06 22:29:20 -08:00
|
|
|
resp |= m_intBuf.get(off + i) & 0xff;
|
2017-12-13 23:41:37 -08:00
|
|
|
}
|
|
|
|
|
} else {
|
2018-12-06 22:29:20 -08:00
|
|
|
for (int i = m_xferSize - 1; i >= 1; --i) {
|
2017-12-13 23:41:37 -08:00
|
|
|
resp <<= 8;
|
2018-12-06 22:29:20 -08:00
|
|
|
resp |= m_intBuf.get(off + i) & 0xff;
|
2017-12-13 23:41:37 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// process response
|
|
|
|
|
if ((resp & m_validMask) == m_validValue) {
|
|
|
|
|
// valid sensor data; extract data field
|
|
|
|
|
int data = 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
|
|
|
int dataNoCenter = data;
|
2017-12-13 23:41:37 -08:00
|
|
|
data -= m_center;
|
|
|
|
|
// only accumulate if outside deadband
|
|
|
|
|
if (data < -m_deadband || data > m_deadband) {
|
|
|
|
|
m_value += data;
|
2018-12-06 22:29:20 -08:00
|
|
|
if (m_count != 0) {
|
|
|
|
|
// timestamps use the 1us FPGA clock; also handle rollover
|
|
|
|
|
if (timestamp >= m_lastTimestamp) {
|
2020-12-29 22:45:16 -08:00
|
|
|
m_integratedValue +=
|
|
|
|
|
dataNoCenter * (timestamp - m_lastTimestamp) * 1e-6 - m_integratedCenter;
|
2018-12-06 22:29:20 -08:00
|
|
|
} else {
|
2020-12-29 22:45:16 -08:00
|
|
|
m_integratedValue +=
|
|
|
|
|
dataNoCenter * ((1L << 32) - m_lastTimestamp + timestamp) * 1e-6
|
|
|
|
|
- m_integratedCenter;
|
2018-12-06 22:29:20 -08:00
|
|
|
}
|
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-03 10:00:53 -07:00
|
|
|
private Accumulator m_accum;
|
2017-12-13 23:41:37 -08:00
|
|
|
|
2015-11-22 11:50:49 -08:00
|
|
|
/**
|
|
|
|
|
* Initialize the accumulator.
|
|
|
|
|
*
|
2020-12-29 22:45:16 -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
|
2016-05-20 12:07:40 -04:00
|
|
|
* @param validValue After validMask is applied, required matching value for validity checking
|
2020-12-29 22:45:16 -08:00
|
|
|
* @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?
|
|
|
|
|
*/
|
|
|
|
|
public void initAccumulator(
|
|
|
|
|
double period,
|
|
|
|
|
int cmd,
|
|
|
|
|
int xferSize,
|
|
|
|
|
int validMask,
|
|
|
|
|
int validValue,
|
|
|
|
|
int dataShift,
|
|
|
|
|
int dataSize,
|
|
|
|
|
boolean isSigned,
|
|
|
|
|
boolean bigEndian) {
|
2017-12-13 23:41:37 -08:00
|
|
|
initAuto(xferSize * 2048);
|
|
|
|
|
byte[] cmdBytes = new byte[] {0, 0, 0, 0};
|
|
|
|
|
if (bigEndian) {
|
|
|
|
|
for (int i = xferSize - 1; i >= 0; --i) {
|
|
|
|
|
cmdBytes[i] = (byte) (cmd & 0xff);
|
|
|
|
|
cmd >>= 8;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
cmdBytes[0] = (byte) (cmd & 0xff);
|
|
|
|
|
cmd >>= 8;
|
|
|
|
|
cmdBytes[1] = (byte) (cmd & 0xff);
|
|
|
|
|
cmd >>= 8;
|
|
|
|
|
cmdBytes[2] = (byte) (cmd & 0xff);
|
|
|
|
|
cmd >>= 8;
|
|
|
|
|
cmdBytes[3] = (byte) (cmd & 0xff);
|
|
|
|
|
}
|
|
|
|
|
setAutoTransmitData(cmdBytes, xferSize - 4);
|
|
|
|
|
startAutoRate(period);
|
|
|
|
|
|
2020-12-29 22:45:16 -08:00
|
|
|
m_accum =
|
|
|
|
|
new Accumulator(
|
|
|
|
|
m_port, xferSize, validMask, validValue, dataShift, dataSize, isSigned, bigEndian);
|
2017-12-13 23:41:37 -08:00
|
|
|
m_accum.m_notifier.startPeriodic(period * 1024);
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
2020-12-29 22:45:16 -08:00
|
|
|
/** Frees the accumulator. */
|
2015-11-22 11:50:49 -08:00
|
|
|
public void freeAccumulator() {
|
2017-12-25 23:03:22 -05:00
|
|
|
if (m_accum != null) {
|
2018-05-22 23:33:17 -07:00
|
|
|
m_accum.close();
|
2017-12-25 23:03:22 -05:00
|
|
|
m_accum = null;
|
|
|
|
|
}
|
2017-12-13 23:41:37 -08:00
|
|
|
freeAuto();
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
2020-12-29 22:45:16 -08:00
|
|
|
/** Resets the accumulator to zero. */
|
2015-11-22 11:50:49 -08:00
|
|
|
public void resetAccumulator() {
|
2017-12-13 23:41:37 -08:00
|
|
|
if (m_accum == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
synchronized (m_accum.m_mutex) {
|
|
|
|
|
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;
|
2017-12-13 23:41:37 -08:00
|
|
|
}
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the center value of the accumulator.
|
|
|
|
|
*
|
2016-05-20 12:07:40 -04:00
|
|
|
* <p>The center value is subtracted from each value before it is added to the accumulator. This
|
2015-11-22 11:50:49 -08:00
|
|
|
* 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.
|
2021-06-10 20:46:47 -07:00
|
|
|
*
|
|
|
|
|
* @param center The accumulator's center value.
|
2015-11-22 11:50:49 -08:00
|
|
|
*/
|
|
|
|
|
public void setAccumulatorCenter(int center) {
|
2017-12-13 23:41:37 -08:00
|
|
|
if (m_accum == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
synchronized (m_accum.m_mutex) {
|
|
|
|
|
m_accum.m_center = center;
|
|
|
|
|
}
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
2021-06-10 20:46:47 -07:00
|
|
|
/**
|
|
|
|
|
* Set the accumulator's deadband.
|
|
|
|
|
*
|
|
|
|
|
* @param deadband The accumulator's deadband.
|
|
|
|
|
*/
|
2015-11-22 11:50:49 -08:00
|
|
|
public void setAccumulatorDeadband(int deadband) {
|
2017-12-13 23:41:37 -08:00
|
|
|
if (m_accum == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
synchronized (m_accum.m_mutex) {
|
|
|
|
|
m_accum.m_deadband = deadband;
|
|
|
|
|
}
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
2021-06-10 20:46:47 -07:00
|
|
|
/**
|
|
|
|
|
* Read the last value read by the accumulator engine.
|
|
|
|
|
*
|
|
|
|
|
* @return The last value read by the accumulator engine.
|
|
|
|
|
*/
|
2015-11-22 11:50:49 -08:00
|
|
|
public int getAccumulatorLastValue() {
|
2017-12-13 23:41:37 -08:00
|
|
|
if (m_accum == null) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
synchronized (m_accum.m_mutex) {
|
|
|
|
|
m_accum.update();
|
|
|
|
|
return m_accum.m_lastValue;
|
|
|
|
|
}
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Read the accumulated value.
|
|
|
|
|
*
|
|
|
|
|
* @return The 64-bit value accumulated since the last Reset().
|
|
|
|
|
*/
|
|
|
|
|
public long getAccumulatorValue() {
|
2017-12-13 23:41:37 -08:00
|
|
|
if (m_accum == null) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
synchronized (m_accum.m_mutex) {
|
|
|
|
|
m_accum.update();
|
|
|
|
|
return m_accum.m_value;
|
|
|
|
|
}
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Read the number of accumulated values.
|
|
|
|
|
*
|
2016-05-20 12:07:40 -04:00
|
|
|
* <p>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.
|
|
|
|
|
*/
|
|
|
|
|
public int getAccumulatorCount() {
|
2017-12-13 23:41:37 -08:00
|
|
|
if (m_accum == null) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
synchronized (m_accum.m_mutex) {
|
|
|
|
|
m_accum.update();
|
|
|
|
|
return m_accum.m_count;
|
|
|
|
|
}
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Read the average of the accumulated value.
|
|
|
|
|
*
|
|
|
|
|
* @return The accumulated average value (value / count).
|
|
|
|
|
*/
|
|
|
|
|
public double getAccumulatorAverage() {
|
2017-12-13 23:41:37 -08:00
|
|
|
if (m_accum == null) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
synchronized (m_accum.m_mutex) {
|
|
|
|
|
m_accum.update();
|
|
|
|
|
if (m_accum.m_count == 0) {
|
|
|
|
|
return 0.0;
|
|
|
|
|
}
|
|
|
|
|
return ((double) m_accum.m_value) / m_accum.m_count;
|
|
|
|
|
}
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Read the accumulated value and the number of accumulated values atomically.
|
|
|
|
|
*
|
2016-05-20 12:07:40 -04:00
|
|
|
* <p>This function reads the value and count atomically. This can be used for averaging.
|
2015-11-22 11:50:49 -08:00
|
|
|
*
|
|
|
|
|
* @param result AccumulatorResult object to store the results in.
|
|
|
|
|
*/
|
|
|
|
|
public void getAccumulatorOutput(AccumulatorResult result) {
|
|
|
|
|
if (result == null) {
|
|
|
|
|
throw new IllegalArgumentException("Null parameter `result'");
|
|
|
|
|
}
|
2017-12-13 23:41:37 -08:00
|
|
|
if (m_accum == null) {
|
|
|
|
|
result.value = 0;
|
|
|
|
|
result.count = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
synchronized (m_accum.m_mutex) {
|
|
|
|
|
m_accum.update();
|
|
|
|
|
result.value = m_accum.m_value;
|
|
|
|
|
result.count = m_accum.m_count;
|
|
|
|
|
}
|
2015-11-22 11:50:49 -08:00
|
|
|
}
|
2018-12-06 22:29:20 -08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the center value of the accumulator integrator.
|
|
|
|
|
*
|
2020-12-29 22:45:16 -08:00
|
|
|
* <p>The center value is subtracted from each value*dt before it is added to the integrated
|
|
|
|
|
* value. This is used for the center value of devices like gyros and accelerometers to take the
|
|
|
|
|
* device offset into account when integrating.
|
2021-06-10 20:46:47 -07:00
|
|
|
*
|
|
|
|
|
* @param center The accumulator integrator's center value.
|
2018-12-06 22:29:20 -08:00
|
|
|
*/
|
|
|
|
|
public void setAccumulatorIntegratedCenter(double center) {
|
|
|
|
|
if (m_accum == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
synchronized (m_accum.m_mutex) {
|
|
|
|
|
m_accum.m_integratedCenter = center;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2020-12-29 22:45:16 -08:00
|
|
|
* Read the integrated value. This is the sum of (each value * time between values).
|
2018-12-06 22:29:20 -08:00
|
|
|
*
|
|
|
|
|
* @return The integrated value accumulated since the last Reset().
|
|
|
|
|
*/
|
|
|
|
|
public double getAccumulatorIntegratedValue() {
|
|
|
|
|
if (m_accum == null) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
synchronized (m_accum.m_mutex) {
|
|
|
|
|
m_accum.update();
|
|
|
|
|
return m_accum.m_integratedValue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2020-12-29 22:45:16 -08:00
|
|
|
* Read the average of the integrated value. This is the sum of (each value times the time between
|
|
|
|
|
* values), divided by the count.
|
2018-12-06 22:29:20 -08:00
|
|
|
*
|
2020-12-29 22:45:16 -08:00
|
|
|
* @return The average of the integrated value accumulated since the last Reset().
|
2018-12-06 22:29:20 -08:00
|
|
|
*/
|
|
|
|
|
public double getAccumulatorIntegratedAverage() {
|
|
|
|
|
if (m_accum == null) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
synchronized (m_accum.m_mutex) {
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-07-16 16:24:44 -04:00
|
|
|
}
|