001package edu.wpi.first.wpilibj;
002
003import java.nio.ByteOrder;
004import java.nio.IntBuffer;
005import java.nio.ByteBuffer;
006
007import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary.tResourceType;
008import edu.wpi.first.wpilibj.communication.UsageReporting;
009import edu.wpi.first.wpilibj.hal.HALLibrary;
010import edu.wpi.first.wpilibj.hal.HALUtil;
011import edu.wpi.first.wpilibj.hal.SPIJNI;
012
013/**
014 *
015 * Represents a SPI bus port
016 
017 * @author koconnor
018 */
019public class SPI extends SensorBase {
020        
021        public enum Port {
022                kOnboardCS0(0), 
023                kOnboardCS1(1), 
024                kOnboardCS2(2), 
025                kOnboardCS3(3), 
026                kMXP(4);
027                
028                private int value;
029                
030                private Port(int value){
031                        this.value = value;
032                }
033                
034                public int getValue(){
035                        return this.value;
036                }
037        };
038
039        private static int devices = 0;
040        
041        private byte m_port;
042        private int bitOrder;
043        private int clockPolarity;
044        private int dataOnTrailing;
045
046        /**
047         * Constructor
048         *
049         * @param port the physical SPI port
050         */
051    public SPI(Port port) {
052                ByteBuffer status = ByteBuffer.allocateDirect(4);
053                status.order(ByteOrder.LITTLE_ENDIAN);
054                
055                m_port = (byte)port.getValue();
056                devices++;
057                
058        SPIJNI.spiInitialize(m_port, status.asIntBuffer());
059                HALUtil.checkStatus(status.asIntBuffer());
060                
061                UsageReporting.report(tResourceType.kResourceType_SPI, devices);
062    }  
063        
064    /**
065     * Free the resources used by this object
066     */
067    public void free(){
068                SPIJNI.spiClose(m_port);
069    }
070        
071        /**
072         * Configure the rate of the generated clock signal.
073         * The default value is 500,000 Hz.
074         * The maximum value is 4,000,000 Hz.
075         *
076         * @param hz    The clock rate in Hertz.
077         */
078    public final void setClockRate(int hz) {
079        SPIJNI.spiSetSpeed(m_port, hz);
080    }
081        
082        /**
083         * Configure the order that bits are sent and received on the wire
084         * to be most significant bit first.
085         */
086        public final void setMSBFirst() {
087                this.bitOrder = 1;
088                SPIJNI.spiSetOpts(m_port, this.bitOrder, this.dataOnTrailing, this.clockPolarity);
089        }
090        
091        /**
092         * Configure the order that bits are sent and received on the wire
093         * to be least significant bit first.
094         */
095        public final void setLSBFirst() {
096                this.bitOrder = 0;
097                SPIJNI.spiSetOpts(m_port, this.bitOrder, this.dataOnTrailing, this.clockPolarity);
098        }
099
100        /**
101         * Configure the clock output line to be active low.
102         * This is sometimes called clock polarity high or clock idle high.
103         */
104        public final void setClockActiveLow() {
105                this.clockPolarity = 1;
106                SPIJNI.spiSetOpts(m_port, this.bitOrder, this.dataOnTrailing, this.clockPolarity);
107        }
108        
109        /**
110         * Configure the clock output line to be active high.
111         * This is sometimes called clock polarity low or clock idle low.
112         */
113        public final void setClockActiveHigh() {
114                this.clockPolarity = 0;
115                SPIJNI.spiSetOpts(m_port, this.bitOrder, this.dataOnTrailing, this.clockPolarity);
116        }
117
118        /**
119         * Configure that the data is stable on the falling edge and the data
120         * changes on the rising edge.
121         */
122    public final void setSampleDataOnFalling() {
123        this.dataOnTrailing = 1;
124                SPIJNI.spiSetOpts(m_port, this.bitOrder, this.dataOnTrailing, this.clockPolarity);
125    }
126        
127        /**
128         * Configure that the data is stable on the rising edge and the data
129         * changes on the falling edge.
130         */
131        public final void setSampleDataOnRising() {
132        this.dataOnTrailing = 0;
133                SPIJNI.spiSetOpts(m_port, this.bitOrder, this.dataOnTrailing, this.clockPolarity);
134    }
135        
136        /**
137         * Configure the chip select line to be active high.
138         */
139        public final void setChipSelectActiveHigh() {
140                ByteBuffer status = ByteBuffer.allocateDirect(4);
141                status.order(ByteOrder.LITTLE_ENDIAN);
142                
143                SPIJNI.spiSetChipSelectActiveHigh(m_port, status.asIntBuffer());
144        
145                HALUtil.checkStatus(status.asIntBuffer());
146        }
147        
148        /**
149         * Configure the chip select line to be active low.
150         */
151        public final void setChipSelectActiveLow() {
152                ByteBuffer status = ByteBuffer.allocateDirect(4);
153                status.order(ByteOrder.LITTLE_ENDIAN);
154                
155                SPIJNI.spiSetChipSelectActiveLow(m_port, status.asIntBuffer());
156        
157                HALUtil.checkStatus(status.asIntBuffer());
158        }
159        
160         /**
161         * Write data to the slave device.  Blocks until there is space in the
162         * output FIFO.
163         *
164         * If not running in output only mode, also saves the data received
165         * on the MISO input during the transfer into the receive FIFO.
166         */
167        public int write(byte[] dataToSend, int size) {
168                int retVal = 0;
169                ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(size);
170                dataToSendBuffer.put(dataToSend);
171                retVal = SPIJNI.spiWrite(m_port, dataToSendBuffer, (byte) size);
172                return retVal;
173        }
174        
175        /**
176         * Read a word from the receive FIFO.
177         *
178         * Waits for the current transfer to complete if the receive FIFO is empty.
179         *
180         * If the receive FIFO is empty, there is no active transfer, and initiate
181         * is false, errors.
182         *
183         * @param initiate      If true, this function pushes "0" into the
184         *                                  transmit buffer and initiates a transfer.
185         *                                  If false, this function assumes that data is
186         *                                  already in the receive FIFO from a previous write.
187         */
188        public int read(boolean initiate, byte[] dataReceived, int size) {
189                int retVal = 0;
190                ByteBuffer dataReceivedBuffer = ByteBuffer.allocateDirect(size);
191                ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(size);
192                if(initiate)
193                        retVal = SPIJNI.spiTransaction(m_port, dataToSendBuffer, dataReceivedBuffer, (byte) size);
194                else
195                        retVal = SPIJNI.spiRead(m_port, dataReceivedBuffer, (byte) size);
196                dataReceivedBuffer.get(dataReceived);
197                return retVal;
198        }
199
200        /**
201         * Perform a simultaneous read/write transaction with the device
202         *
203         * @param dataToSend The data to be written out to the device
204         * @param dataReceived Buffer to receive data from the device
205         * @param size The length of the transaction, in bytes
206         */
207    public int transaction(byte[] dataToSend, byte[] dataReceived, int size) {
208                int retVal = 0;
209                ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(size);
210                dataToSendBuffer.put(dataToSend);
211                ByteBuffer dataReceivedBuffer = ByteBuffer.allocateDirect(size);
212        retVal = SPIJNI.spiTransaction(m_port, dataToSendBuffer, dataReceivedBuffer, (byte) size);
213                dataReceivedBuffer.get(dataReceived);
214        return retVal;
215    }
216}