/*
* Copyright 2016 Cel Skeggs
*
* This file is part of the CCRE, the Common Chicken Runtime Engine.
*
* The CCRE is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* The CCRE is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the CCRE. If not, see <http://www.gnu.org/licenses/>.
*/
package ccre.frc;
import java.io.IOException;
import java.nio.ByteBuffer;
import edu.wpi.first.wpilibj.hal.SPIJNI;
class DirectSPI {
public static final byte PORT_CS0 = 0, PORT_CS1 = 1, PORT_CS2 = 2, PORT_CS3 = 3, PORT_MXP = 4;
// TODO: one source says the maximum is 7 bytes... (docs in HAL) - should
// that be included here?
static final int MAX_SPI_LENGTH = 256; // based on size of byte
public static byte portForCS(int n) {
if (n < PORT_CS0 || n > PORT_CS3) {
throw new IllegalArgumentException("Invalid CS port - not in [0, 3]: " + n);
}
return (byte) n;
}
public static void checkPort(byte port) {
if (port < PORT_CS0 || port > PORT_MXP) {
throw new IllegalArgumentException("Invalid SPI port: " + port);
}
}
public static void init(byte port) {
checkPort(port);
SPIJNI.spiInitialize(port);
}
public static void free(byte port) {
checkPort(port);
SPIJNI.spiClose(port);
}
// defaults: hertz = 500,000 Hz, LSB, data on rising, clock active high,
// chip select UNKNOWN
public static void configure(byte port, int hertz, boolean isMSB, boolean dataOnFalling, boolean clockActiveLow, boolean chipSelectActiveLow) {
checkPort(port);
SPIJNI.spiSetSpeed(port, hertz);
SPIJNI.spiSetOpts(port, isMSB ? 1 : 0, dataOnFalling ? 1 : 0, clockActiveLow ? 1 : 0);
if (chipSelectActiveLow) {
SPIJNI.spiSetChipSelectActiveLow(port);
} else {
SPIJNI.spiSetChipSelectActiveHigh(port);
}
}
public static int transact(byte port, ByteBuffer send, ByteBuffer recv, int len) throws IOException {
if (!send.isDirect() || !recv.isDirect()) {
throw new IllegalArgumentException("Buffer must be direct!");
}
if (len >= MAX_SPI_LENGTH) {
throw new IllegalArgumentException("Length too long");
}
if (send.capacity() < len || recv.capacity() < len) {
throw new IllegalArgumentException("Buffer too short");
}
int count = SPIJNI.spiTransaction(port, send, recv, (byte) len);
if (count < 0) {
throw new IOException("SPI write error on " + port);
}
return count;
}
public static int write(byte port, ByteBuffer data, int len) throws IOException {
if (!data.isDirect()) {
throw new IllegalArgumentException("Buffer must be direct!");
}
if (len >= MAX_SPI_LENGTH) {
throw new IllegalArgumentException("Length too long");
}
if (data.capacity() < len) {
throw new IllegalArgumentException("Buffer too short");
}
int count = SPIJNI.spiWrite(port, data, (byte) len);
if (count < 0) {
throw new IOException("SPI write error on " + port);
}
return count;
}
public static int read(byte port, ByteBuffer data, int len) throws IOException {
if (!data.isDirect()) {
throw new IllegalArgumentException("Buffer must be direct!");
}
if (len >= MAX_SPI_LENGTH) {
throw new IllegalArgumentException("Length too long");
}
if (data.capacity() < len) {
throw new IllegalArgumentException("Buffer too short");
}
int count = SPIJNI.spiRead(port, data, (byte) len);
if (count < 0) {
throw new IOException("SPI write error on " + port);
}
return count;
}
// TODO: handle SPI accumulator
}