001/*----------------------------------------------------------------------------*/ 002/* Copyright (c) FIRST 2016-2017. All Rights Reserved. */ 003/* Open Source Software - may be modified and shared by FRC teams. The code */ 004/* must be accompanied by the FIRST BSD license file in the root directory of */ 005/* the project. */ 006/*----------------------------------------------------------------------------*/ 007 008package edu.wpi.first.wpilibj; 009 010import java.nio.ByteBuffer; 011import java.nio.ByteOrder; 012 013import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType; 014import edu.wpi.first.wpilibj.hal.HAL; 015import edu.wpi.first.wpilibj.hal.SPIJNI; 016 017/** 018 * Represents a SPI bus port. 019 */ 020public class SPI extends SensorBase { 021 022 public enum Port { 023 kOnboardCS0(0), kOnboardCS1(1), kOnboardCS2(2), kOnboardCS3(3), kMXP(4); 024 025 @SuppressWarnings("MemberName") 026 public int value; 027 028 private Port(int value) { 029 this.value = value; 030 } 031 } 032 033 private static int devices = 0; 034 035 private byte m_port; 036 private int m_bitOrder; 037 private int m_clockPolarity; 038 private int m_dataOnTrailing; 039 040 /** 041 * Constructor. 042 * 043 * @param port the physical SPI port 044 */ 045 public SPI(Port port) { 046 m_port = (byte) port.value; 047 devices++; 048 049 SPIJNI.spiInitialize(m_port); 050 051 HAL.report(tResourceType.kResourceType_SPI, devices); 052 } 053 054 /** 055 * Free the resources used by this object. 056 */ 057 public void free() { 058 SPIJNI.spiClose(m_port); 059 } 060 061 /** 062 * Configure the rate of the generated clock signal. The default value is 500,000 Hz. The maximum 063 * value is 4,000,000 Hz. 064 * 065 * @param hz The clock rate in Hertz. 066 */ 067 public final void setClockRate(int hz) { 068 SPIJNI.spiSetSpeed(m_port, hz); 069 } 070 071 /** 072 * Configure the order that bits are sent and received on the wire to be most significant bit 073 * first. 074 */ 075 public final void setMSBFirst() { 076 m_bitOrder = 1; 077 SPIJNI.spiSetOpts(m_port, m_bitOrder, m_dataOnTrailing, m_clockPolarity); 078 } 079 080 /** 081 * Configure the order that bits are sent and received on the wire to be least significant bit 082 * first. 083 */ 084 public final void setLSBFirst() { 085 m_bitOrder = 0; 086 SPIJNI.spiSetOpts(m_port, m_bitOrder, m_dataOnTrailing, m_clockPolarity); 087 } 088 089 /** 090 * Configure the clock output line to be active low. This is sometimes called clock polarity high 091 * or clock idle high. 092 */ 093 public final void setClockActiveLow() { 094 m_clockPolarity = 1; 095 SPIJNI.spiSetOpts(m_port, m_bitOrder, m_dataOnTrailing, m_clockPolarity); 096 } 097 098 /** 099 * Configure the clock output line to be active high. This is sometimes called clock polarity low 100 * or clock idle low. 101 */ 102 public final void setClockActiveHigh() { 103 m_clockPolarity = 0; 104 SPIJNI.spiSetOpts(m_port, m_bitOrder, m_dataOnTrailing, m_clockPolarity); 105 } 106 107 /** 108 * Configure that the data is stable on the falling edge and the data changes on the rising edge. 109 */ 110 public final void setSampleDataOnFalling() { 111 m_dataOnTrailing = 1; 112 SPIJNI.spiSetOpts(m_port, m_bitOrder, m_dataOnTrailing, m_clockPolarity); 113 } 114 115 /** 116 * Configure that the data is stable on the rising edge and the data changes on the falling edge. 117 */ 118 public final void setSampleDataOnRising() { 119 m_dataOnTrailing = 0; 120 SPIJNI.spiSetOpts(m_port, m_bitOrder, m_dataOnTrailing, m_clockPolarity); 121 } 122 123 /** 124 * Configure the chip select line to be active high. 125 */ 126 public final void setChipSelectActiveHigh() { 127 SPIJNI.spiSetChipSelectActiveHigh(m_port); 128 } 129 130 /** 131 * Configure the chip select line to be active low. 132 */ 133 public final void setChipSelectActiveLow() { 134 SPIJNI.spiSetChipSelectActiveLow(m_port); 135 } 136 137 /** 138 * Write data to the slave device. Blocks until there is space in the output FIFO. 139 * 140 * <p>If not running in output only mode, also saves the data received on the MISO input during 141 * the transfer into the receive FIFO. 142 */ 143 public int write(byte[] dataToSend, int size) { 144 ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(size); 145 dataToSendBuffer.put(dataToSend); 146 return SPIJNI.spiWrite(m_port, dataToSendBuffer, (byte) size); 147 } 148 149 /** 150 * Write data to the slave device. Blocks until there is space in the output FIFO. 151 * 152 * <p>If not running in output only mode, also saves the data received on the MISO input during 153 * the transfer into the receive FIFO. 154 * 155 * @param dataToSend The buffer containing the data to send. Must be created using 156 * ByteBuffer.allocateDirect(). 157 */ 158 public int write(ByteBuffer dataToSend, int size) { 159 if (!dataToSend.isDirect()) { 160 throw new IllegalArgumentException("must be a direct buffer"); 161 } 162 if (dataToSend.capacity() < size) { 163 throw new IllegalArgumentException("buffer is too small, must be at least " + size); 164 } 165 return SPIJNI.spiWrite(m_port, dataToSend, (byte) size); 166 } 167 168 /** 169 * Read a word from the receive FIFO. 170 * 171 * <p>Waits for the current transfer to complete if the receive FIFO is empty. 172 * 173 * <p>If the receive FIFO is empty, there is no active transfer, and initiate is false, errors. 174 * 175 * @param initiate If true, this function pushes "0" into the transmit buffer and initiates a 176 * transfer. If false, this function assumes that data is already in the receive 177 * FIFO from a previous write. 178 */ 179 public int read(boolean initiate, byte[] dataReceived, int size) { 180 final int retVal; 181 ByteBuffer dataReceivedBuffer = ByteBuffer.allocateDirect(size); 182 ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(size); 183 if (initiate) { 184 retVal = SPIJNI.spiTransaction(m_port, dataToSendBuffer, dataReceivedBuffer, (byte) size); 185 } else { 186 retVal = SPIJNI.spiRead(m_port, dataReceivedBuffer, (byte) size); 187 } 188 dataReceivedBuffer.get(dataReceived); 189 return retVal; 190 } 191 192 /** 193 * Read a word from the receive FIFO. 194 * 195 * <p>Waits for the current transfer to complete if the receive FIFO is empty. 196 * 197 * <p>If the receive FIFO is empty, there is no active transfer, and initiate is false, errors. 198 * 199 * @param initiate If true, this function pushes "0" into the transmit buffer and initiates 200 * a transfer. If false, this function assumes that data is already in the 201 * receive FIFO from a previous write. 202 * @param dataReceived The buffer to be filled with the received data. Must be created using 203 * ByteBuffer.allocateDirect(). 204 * @param size The length of the transaction, in bytes 205 */ 206 public int read(boolean initiate, ByteBuffer dataReceived, int size) { 207 if (!dataReceived.isDirect()) { 208 throw new IllegalArgumentException("must be a direct buffer"); 209 } 210 if (dataReceived.capacity() < size) { 211 throw new IllegalArgumentException("buffer is too small, must be at least " + size); 212 } 213 if (initiate) { 214 ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(size); 215 return SPIJNI.spiTransaction(m_port, dataToSendBuffer, dataReceived, (byte) size); 216 } 217 return SPIJNI.spiRead(m_port, dataReceived, (byte) size); 218 } 219 220 /** 221 * Perform a simultaneous read/write transaction with the device. 222 * 223 * @param dataToSend The data to be written out to the device 224 * @param dataReceived Buffer to receive data from the device 225 * @param size The length of the transaction, in bytes 226 */ 227 public int transaction(byte[] dataToSend, byte[] dataReceived, int size) { 228 ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(size); 229 dataToSendBuffer.put(dataToSend); 230 ByteBuffer dataReceivedBuffer = ByteBuffer.allocateDirect(size); 231 int retVal = SPIJNI.spiTransaction(m_port, dataToSendBuffer, dataReceivedBuffer, (byte) size); 232 dataReceivedBuffer.get(dataReceived); 233 return retVal; 234 } 235 236 /** 237 * Perform a simultaneous read/write transaction with the device 238 * 239 * @param dataToSend The data to be written out to the device. Must be created using 240 * ByteBuffer.allocateDirect(). 241 * @param dataReceived Buffer to receive data from the device. Must be created using 242 * ByteBuffer.allocateDirect(). 243 * @param size The length of the transaction, in bytes 244 */ 245 public int transaction(ByteBuffer dataToSend, ByteBuffer dataReceived, int size) { 246 if (!dataToSend.isDirect()) { 247 throw new IllegalArgumentException("dataToSend must be a direct buffer"); 248 } 249 if (dataToSend.capacity() < size) { 250 throw new IllegalArgumentException("dataToSend is too small, must be at least " + size); 251 } 252 if (!dataReceived.isDirect()) { 253 throw new IllegalArgumentException("dataReceived must be a direct buffer"); 254 } 255 if (dataReceived.capacity() < size) { 256 throw new IllegalArgumentException("dataReceived is too small, must be at least " + size); 257 } 258 return SPIJNI.spiTransaction(m_port, dataToSend, dataReceived, (byte) size); 259 } 260 261 /** 262 * Initialize the accumulator. 263 * 264 * @param period Time between reads 265 * @param cmd SPI command to send to request data 266 * @param xferSize SPI transfer size, in bytes 267 * @param validMask Mask to apply to received data for validity checking 268 * @param validValue After validMask is applied, required matching value for validity checking 269 * @param dataShift Bit shift to apply to received data to get actual data value 270 * @param dataSize Size (in bits) of data field 271 * @param isSigned Is data field signed? 272 * @param bigEndian Is device big endian? 273 */ 274 public void initAccumulator(double period, int cmd, int xferSize, 275 int validMask, int validValue, 276 int dataShift, int dataSize, 277 boolean isSigned, boolean bigEndian) { 278 SPIJNI.spiInitAccumulator(m_port, (int) (period * 1.0e6), cmd, 279 (byte) xferSize, validMask, validValue, (byte) dataShift, 280 (byte) dataSize, isSigned, bigEndian); 281 } 282 283 /** 284 * Frees the accumulator. 285 */ 286 public void freeAccumulator() { 287 SPIJNI.spiFreeAccumulator(m_port); 288 } 289 290 /** 291 * Resets the accumulator to zero. 292 */ 293 public void resetAccumulator() { 294 SPIJNI.spiResetAccumulator(m_port); 295 } 296 297 /** 298 * Set the center value of the accumulator. 299 * 300 * <p>The center value is subtracted from each value before it is added to the accumulator. This 301 * is used for the center value of devices like gyros and accelerometers to make integration work 302 * and to take the device offset into account when integrating. 303 */ 304 public void setAccumulatorCenter(int center) { 305 SPIJNI.spiSetAccumulatorCenter(m_port, center); 306 } 307 308 /** 309 * Set the accumulator's deadband. 310 */ 311 public void setAccumulatorDeadband(int deadband) { 312 SPIJNI.spiSetAccumulatorDeadband(m_port, deadband); 313 } 314 315 /** 316 * Read the last value read by the accumulator engine. 317 */ 318 public int getAccumulatorLastValue() { 319 return SPIJNI.spiGetAccumulatorLastValue(m_port); 320 } 321 322 /** 323 * Read the accumulated value. 324 * 325 * @return The 64-bit value accumulated since the last Reset(). 326 */ 327 public long getAccumulatorValue() { 328 return SPIJNI.spiGetAccumulatorValue(m_port); 329 } 330 331 /** 332 * Read the number of accumulated values. 333 * 334 * <p>Read the count of the accumulated values since the accumulator was last Reset(). 335 * 336 * @return The number of times samples from the channel were accumulated. 337 */ 338 public int getAccumulatorCount() { 339 return SPIJNI.spiGetAccumulatorCount(m_port); 340 } 341 342 /** 343 * Read the average of the accumulated value. 344 * 345 * @return The accumulated average value (value / count). 346 */ 347 public double getAccumulatorAverage() { 348 return SPIJNI.spiGetAccumulatorAverage(m_port); 349 } 350 351 /** 352 * Read the accumulated value and the number of accumulated values atomically. 353 * 354 * <p>This function reads the value and count atomically. This can be used for averaging. 355 * 356 * @param result AccumulatorResult object to store the results in. 357 */ 358 public void getAccumulatorOutput(AccumulatorResult result) { 359 if (result == null) { 360 throw new IllegalArgumentException("Null parameter `result'"); 361 } 362 ByteBuffer value = ByteBuffer.allocateDirect(8); 363 // set the byte order 364 value.order(ByteOrder.LITTLE_ENDIAN); 365 ByteBuffer count = ByteBuffer.allocateDirect(8); 366 // set the byte order 367 count.order(ByteOrder.LITTLE_ENDIAN); 368 SPIJNI.spiGetAccumulatorOutput(m_port, value.asLongBuffer(), count.asLongBuffer()); 369 result.value = value.asLongBuffer().get(0); 370 result.count = count.asLongBuffer().get(0); 371 } 372}