/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* Copyright (c) The Processing Foundation 2015 Hardware I/O library developed by Gottfried Haider as part of GSoC 2015 This library 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 2.1 of the License, or (at your option) any later version. This library 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 this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package processing.io; import processing.io.NativeInterface; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Map; /** * @webref */ public class SPI { /** * CPOL=0, CPHA=0, most common */ public static final int MODE0 = 0; /** * CPOL=0, CPHA=1 */ public static final int MODE1 = 1; /** * CPOL=1, CPHA=0 */ public static final int MODE2 = 2; /** * CPOL=1, CPHA=1 */ public static final int MODE3 = 3; /** * most significant bit first, most common */ public static final int MSBFIRST = 0; /** * least significant bit first */ public static final int LSBFIRST = 1; protected int dataOrder = 0; protected String dev; protected int handle; protected int maxSpeed = 500000; protected int mode = 0; protected static Map<String, String> settings = new HashMap<String, String>(); /** * Opens an SPI interface as master * @param dev device name * @see list * @webref */ public SPI(String dev) { NativeInterface.loadLibrary(); this.dev = dev; if (NativeInterface.isSimulated()) { return; } handle = NativeInterface.openDevice("/dev/" + dev); if (handle < 0) { throw new RuntimeException(NativeInterface.getError(handle)); } } /** * Closes the SPI interface * @webref */ public void close() { if (NativeInterface.isSimulated()) { return; } NativeInterface.closeDevice(handle); handle = 0; } protected void finalize() throws Throwable { try { close(); } finally { super.finalize(); } } /** * Lists all available SPI interfaces * @return String array * @webref */ public static String[] list() { if (NativeInterface.isSimulated()) { // as on the Raspberry Pi return new String[]{ "spidev0.0", "spidev0.1" }; } ArrayList<String> devs = new ArrayList<String>(); File dir = new File("/dev"); File[] files = dir.listFiles(); if (files != null) { for (File file : files) { if (file.getName().startsWith("spidev")) { devs.add(file.getName()); } } } // listFiles() does not guarantee ordering String[] tmp = devs.toArray(new String[devs.size()]); Arrays.sort(tmp); return tmp; } /** * Configures the SPI interface * @param maxSpeed maximum transmission rate in Hz, 500000 (500 kHz) is a resonable default * @param dataOrder whether data is send with the first- or least-significant bit first (SPI.MSBFIRST or SPI.LSBFIRST, the former is more common) * @param mode <a href="https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Clock_polarity_and_phase">SPI.MODE0 to SPI.MODE3</a> * @webref */ public void settings(int maxSpeed, int dataOrder, int mode) { this.maxSpeed = maxSpeed; this.dataOrder = dataOrder; this.mode = mode; } /** * Transfers data over the SPI bus * @param out bytes to send * @return bytes read in (array is the same length as out) * @webref */ public byte[] transfer(byte[] out) { if (NativeInterface.isSimulated()) { return new byte[out.length]; } // track the current setting per device across multiple instances String curSettings = maxSpeed + "-" + dataOrder + "-" + mode; if (!curSettings.equals(settings.get(dev))) { int ret = NativeInterface.setSpiSettings(handle, maxSpeed, dataOrder, mode); if (ret < 0) { System.err.println(NativeInterface.getError(handle)); throw new RuntimeException("Error updating device configuration"); } settings.put(dev, curSettings); } byte[] in = new byte[out.length]; int transferred = NativeInterface.transferSpi(handle, out, in); if (transferred < 0) { throw new RuntimeException(NativeInterface.getError(transferred)); } else if (transferred < out.length) { throw new RuntimeException("Fewer bytes transferred than requested: " + transferred); } return in; } /** * Transfers data over the SPI bus * @param out string to send * @return bytes read in (array is the same length as out) */ public byte[] transfer(String out) { return transfer(out.getBytes()); } /** * Transfers data over the SPI bus * @param out single byte to send, e.g. numeric literal (0 to 255, or -128 to 127) * @return bytes read in (array is the same length as out) */ public byte[] transfer(int out) { if (out < -128 || 255 < out) { System.err.println("The transfer function can only operate on a single byte at a time. Call it with a value from 0 to 255, or -128 to 127."); throw new RuntimeException("Argument does not fit into a single byte"); } byte[] tmp = new byte[1]; tmp[0] = (byte)out; return transfer(tmp); } /** * Transfers data over the SPI bus * @param out single byte to send * @return bytes read in (array is the same length as out) */ public byte[] transfer(byte out) { // cast to (unsigned) int return transfer(out & 0xff); } }