001package com.pi4j.io.spi.impl; 002 003/* 004 * #%L 005 * ********************************************************************** 006 * ORGANIZATION : Pi4J 007 * PROJECT : Pi4J :: Java Library (Core) 008 * FILENAME : SpiDeviceImpl.java 009 * 010 * This file is part of the Pi4J project. More information about 011 * this project can be found here: https://www.pi4j.com/ 012 * ********************************************************************** 013 * %% 014 * Copyright (C) 2012 - 2019 Pi4J 015 * %% 016 * This program is free software: you can redistribute it and/or modify 017 * it under the terms of the GNU Lesser General Public License as 018 * published by the Free Software Foundation, either version 3 of the 019 * License, or (at your option) any later version. 020 * 021 * This program is distributed in the hope that it will be useful, 022 * but WITHOUT ANY WARRANTY; without even the implied warranty of 023 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 024 * GNU General Lesser Public License for more details. 025 * 026 * You should have received a copy of the GNU General Lesser Public 027 * License along with this program. If not, see 028 * <http://www.gnu.org/licenses/lgpl-3.0.html>. 029 * #L% 030 */ 031 032 033import com.pi4j.io.spi.SpiChannel; 034import com.pi4j.io.spi.SpiDevice; 035import com.pi4j.io.spi.SpiMode; 036import com.pi4j.wiringpi.Spi; 037 038import java.io.IOException; 039import java.io.InputStream; 040import java.io.OutputStream; 041import java.nio.ByteBuffer; 042import java.nio.charset.Charset; 043 044public class SpiDeviceImpl implements SpiDevice { 045 046 protected final SpiChannel channel; 047 protected final SpiMode mode; 048 049 /** 050 * Creates the SPI Device at the given spi and input channel 051 * 052 * @param channel 053 * spi channel to use 054 * @param speed 055 * spi speed/rate (in Hertz) for channel to communicate at 056 * (range is 500kHz - 32MHz) 057 * @param mode 058 * spi mode (see http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Mode_numbers) 059 * 060 */ 061 public SpiDeviceImpl(SpiChannel channel, int speed, SpiMode mode) throws IOException { 062 this.channel = channel; 063 this.mode = mode; 064 try { 065 int fd; 066 if(mode == SpiMode.MODE_0){ 067 // using this hack because the Odroid port of WiringPi does not currently support 'wiringPiSPISetupMode' 068 fd = Spi.wiringPiSPISetup(channel.getChannel(), speed); 069 } 070 else{ 071 fd = Spi.wiringPiSPISetupMode(channel.getChannel(), speed, mode.getMode()); 072 } 073 if (fd <= -1) { 074 throw new IOException("SPI port setup failed, wiringPiSPISetupMode returned " + fd); 075 } 076 } catch (UnsatisfiedLinkError e) { 077 throw new IOException("SPI port setup failed, no SPI available.", e); 078 } 079 } 080 081 /** 082 * Creates the SPI Device at the given spi and input channel 083 * 084 * @param channel 085 * spi channel to use 086 * @param speed 087 * spi speed/rate (in Hertz) for channel to communicate at 088 * (range is 500kHz - 32MHz) 089 */ 090 public SpiDeviceImpl(SpiChannel channel, int speed) throws IOException { 091 this(channel, speed, DEFAULT_SPI_MODE); 092 } 093 094 /** 095 * Creates the SPI Device at the given SPI and input channel 096 * (A default speed of 1 MHz will be used) 097 * 098 * @param channel 099 * spi channel to use 100 * @param mode 101 * spi mode (see http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Mode_numbers) 102 */ 103 public SpiDeviceImpl(SpiChannel channel, SpiMode mode) throws IOException { 104 this(channel, DEFAULT_SPI_SPEED, mode); 105 } 106 107 /** 108 * Creates the SPI Device at the given SPI and input channel 109 * (A default speed of 1 MHz will be used) 110 * 111 * @param channel 112 * spi channel to use 113 */ 114 public SpiDeviceImpl(SpiChannel channel) throws IOException { 115 this(channel, DEFAULT_SPI_SPEED); 116 } 117 118 @Override 119 public String write(String data, String charset) throws IOException { 120 byte[] buffer = data.getBytes(charset); 121 return new String(write(buffer), charset); 122 } 123 124 @Override 125 public String write(String data, Charset charset) throws IOException { 126 byte[] buffer = data.getBytes(charset); 127 return new String(write(buffer), charset); 128 } 129 130 @Override 131 public ByteBuffer write(ByteBuffer data) throws IOException { 132 return ByteBuffer.wrap(write(data.array())); 133 } 134 135 @Override 136 public byte[] write(InputStream input) throws IOException { 137 138 // ensure bytes are available 139 if(input.available() <= 0){ 140 throw new IOException("No available bytes in input stream to write to SPI channel: " + channel.getChannel()); 141 } 142 else if(input.available() > MAX_SUPPORTED_BYTES){ 143 throw new IOException("Number of bytes in stream exceed the maximum bytes allowed to write SPI channel in a single call"); 144 } 145 146 // create a temporary buffer to store read bytes from stream 147 byte[] buffer = new byte[MAX_SUPPORTED_BYTES]; 148 149 // read maximum number of supported bytes 150 int length = input.read(buffer, 0 , MAX_SUPPORTED_BYTES); 151 152 // write bytes to SPI channel 153 return write(buffer, 0, length); 154 } 155 156 @Override 157 public int write(InputStream input, OutputStream output) throws IOException { 158 // write stream data to SPI device 159 byte[] buffer = write(input); 160 161 //write resulting byte array to output stream 162 output.write(buffer); 163 164 // return data length 165 return buffer.length; 166 } 167 168 @Override 169 public byte[] write(byte... data) throws IOException { 170 return write(data, 0, data.length); 171 } 172 173 @Override 174 public short[] write(short... data) throws IOException { 175 return write(data, 0, data.length); 176 } 177 178 @Override 179 public byte[] write(byte[] data, int start, int length) throws IOException { 180 181 // ensure the length does not exceed the data array 182 length = Math.min(data.length - start, length); 183 184 // validate max length allowed 185 if (length > MAX_SUPPORTED_BYTES) { 186 throw new IOException("Number of bytes in data to write exceed the maximum bytes allowed to write SPI channel in a single call"); 187 } 188 189 // we make a copy of the data argument because we don't want to modify the original source data 190 byte[] buffer = new byte[length]; 191 System.arraycopy(data, start, buffer, 0, length); 192 193 synchronized (channel) { 194 // write the bytes from the temporary buffer to the SPI channel 195 if (Spi.wiringPiSPIDataRW(channel.getChannel(), buffer) <= 0) { 196 throw new IOException("Failed to write data to SPI channel: " + channel.getChannel()); 197 } 198 } 199 // return the updated byte buffer as the SPI read results 200 return buffer; 201 } 202 203 @Override 204 public short[] write(short[] data, int start, int length) throws IOException { 205 206 // ensure the length does not exceed the data array 207 length = Math.min(data.length - start, length); 208 209 // validate max length allowed 210 if (length > MAX_SUPPORTED_BYTES) { 211 throw new IOException("Number of bytes in data to write exceed the maximum bytes allowed to write SPI channel in a single call"); 212 } 213 214 // we make a copy of the data argument because we don't want to modify the original source data 215 short[] buffer = new short[length]; 216 System.arraycopy(data, start, buffer, 0, length); 217 218 synchronized (channel) { 219 // write the bytes from the temporary buffer to the SPI channel 220 if (Spi.wiringPiSPIDataRW(channel.getChannel(), buffer) <= 0) { 221 throw new IOException("Failed to write data to SPI channel: " + channel.getChannel()); 222 } 223 224 // return the updated byte buffer as the SPI read results 225 return buffer; 226 } 227 } 228 229}