blob: f0af9e7f7b1d136f140e9eac687b35f1be43925d [file] [log] [blame]
Brian Silverman26e4e522015-12-17 01:56:40 -05001/*----------------------------------------------------------------------------*/
Brian Silverman1a675112016-02-20 20:42:49 -05002/* Copyright (c) FIRST 2008-2016. All Rights Reserved. */
Brian Silverman26e4e522015-12-17 01:56:40 -05003/* Open Source Software - may be modified and shared by FRC teams. The code */
Brian Silverman1a675112016-02-20 20:42:49 -05004/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
Brian Silverman26e4e522015-12-17 01:56:40 -05006/*----------------------------------------------------------------------------*/
7
8#include "SPI.h"
9
10#include "WPIErrors.h"
11#include "HAL/Digital.hpp"
12
13#include <string.h>
14
15/**
16 * Constructor
17 *
18 * @param SPIport the physical SPI port
19 */
20SPI::SPI(Port SPIport) {
21 m_port = SPIport;
22 int32_t status = 0;
23 spiInitialize(m_port, &status);
24 wpi_setErrorWithContext(status, getHALErrorMessage(status));
25
26 static int32_t instances = 0;
27 instances++;
28 HALReport(HALUsageReporting::kResourceType_SPI, instances);
29}
30
31/**
32 * Destructor.
33 */
34SPI::~SPI() { spiClose(m_port); }
35
36/**
37 * Configure the rate of the generated clock signal.
38 *
39 * The default value is 500,000Hz.
40 * The maximum value is 4,000,000Hz.
41 *
42 * @param hz The clock rate in Hertz.
43 */
44void SPI::SetClockRate(double hz) { spiSetSpeed(m_port, hz); }
45
46/**
47 * Configure the order that bits are sent and received on the wire
48 * to be most significant bit first.
49 */
50void SPI::SetMSBFirst() {
51 m_msbFirst = true;
52 spiSetOpts(m_port, (int)m_msbFirst, (int)m_sampleOnTrailing,
53 (int)m_clk_idle_high);
54}
55
56/**
57 * Configure the order that bits are sent and received on the wire
58 * to be least significant bit first.
59 */
60void SPI::SetLSBFirst() {
61 m_msbFirst = false;
62 spiSetOpts(m_port, (int)m_msbFirst, (int)m_sampleOnTrailing,
63 (int)m_clk_idle_high);
64}
65
66/**
67 * Configure that the data is stable on the falling edge and the data
68 * changes on the rising edge.
69 */
70void SPI::SetSampleDataOnFalling() {
71 m_sampleOnTrailing = true;
72 spiSetOpts(m_port, (int)m_msbFirst, (int)m_sampleOnTrailing,
73 (int)m_clk_idle_high);
74}
75
76/**
77 * Configure that the data is stable on the rising edge and the data
78 * changes on the falling edge.
79 */
80void SPI::SetSampleDataOnRising() {
81 m_sampleOnTrailing = false;
82 spiSetOpts(m_port, (int)m_msbFirst, (int)m_sampleOnTrailing,
83 (int)m_clk_idle_high);
84}
85
86/**
87 * Configure the clock output line to be active low.
88 * This is sometimes called clock polarity high or clock idle high.
89 */
90void SPI::SetClockActiveLow() {
91 m_clk_idle_high = true;
92 spiSetOpts(m_port, (int)m_msbFirst, (int)m_sampleOnTrailing,
93 (int)m_clk_idle_high);
94}
95
96/**
97 * Configure the clock output line to be active high.
98 * This is sometimes called clock polarity low or clock idle low.
99 */
100void SPI::SetClockActiveHigh() {
101 m_clk_idle_high = false;
102 spiSetOpts(m_port, (int)m_msbFirst, (int)m_sampleOnTrailing,
103 (int)m_clk_idle_high);
104}
105
106/**
107 * Configure the chip select line to be active high.
108 */
109void SPI::SetChipSelectActiveHigh() {
110 int32_t status = 0;
111 spiSetChipSelectActiveHigh(m_port, &status);
112 wpi_setErrorWithContext(status, getHALErrorMessage(status));
113}
114
115/**
116 * Configure the chip select line to be active low.
117 */
118void SPI::SetChipSelectActiveLow() {
119 int32_t status = 0;
120 spiSetChipSelectActiveLow(m_port, &status);
121 wpi_setErrorWithContext(status, getHALErrorMessage(status));
122}
123
124/**
125 * Write data to the slave device. Blocks until there is space in the
126 * output FIFO.
127 *
128 * If not running in output only mode, also saves the data received
129 * on the MISO input during the transfer into the receive FIFO.
130 */
131int32_t SPI::Write(uint8_t* data, uint8_t size) {
132 int32_t retVal = 0;
133 retVal = spiWrite(m_port, data, size);
134 return retVal;
135}
136
137/**
138 * Read a word from the receive FIFO.
139 *
140 * Waits for the current transfer to complete if the receive FIFO is empty.
141 *
142 * If the receive FIFO is empty, there is no active transfer, and initiate
143 * is false, errors.
144 *
145 * @param initiate If true, this function pushes "0" into the
146 * transmit buffer and initiates a transfer.
147 * If false, this function assumes that data is
148 * already in the receive FIFO from a previous
149 * write.
150 */
151int32_t SPI::Read(bool initiate, uint8_t* dataReceived, uint8_t size) {
152 int32_t retVal = 0;
153 if (initiate) {
154 auto dataToSend = new uint8_t[size];
155 memset(dataToSend, 0, size);
156 retVal = spiTransaction(m_port, dataToSend, dataReceived, size);
157 } else
158 retVal = spiRead(m_port, dataReceived, size);
159 return retVal;
160}
161
162/**
163 * Perform a simultaneous read/write transaction with the device
164 *
165 * @param dataToSend The data to be written out to the device
166 * @param dataReceived Buffer to receive data from the device
167 * @param size The length of the transaction, in bytes
168 */
169int32_t SPI::Transaction(uint8_t* dataToSend, uint8_t* dataReceived,
170 uint8_t size) {
171 int32_t retVal = 0;
172 retVal = spiTransaction(m_port, dataToSend, dataReceived, size);
173 return retVal;
174}
175
176/**
177 * Initialize the accumulator.
178 *
179 * @param period Time between reads
180 * @param cmd SPI command to send to request data
181 * @param xfer_size SPI transfer size, in bytes
182 * @param valid_mask Mask to apply to received data for validity checking
183 * @param valid_data After valid_mask is applied, required matching value for
184 * validity checking
185 * @param data_shift Bit shift to apply to received data to get actual data
186 * value
187 * @param data_size Size (in bits) of data field
188 * @param is_signed Is data field signed?
189 * @param big_endian Is device big endian?
190 */
191void SPI::InitAccumulator(double period, uint32_t cmd, uint8_t xfer_size,
192 uint32_t valid_mask, uint32_t valid_value,
193 uint8_t data_shift, uint8_t data_size, bool is_signed,
194 bool big_endian) {
195 int32_t status = 0;
196 spiInitAccumulator(m_port, (uint32_t)(period * 1e6), cmd, xfer_size,
197 valid_mask, valid_value, data_shift, data_size, is_signed,
198 big_endian, &status);
199 wpi_setErrorWithContext(status, getHALErrorMessage(status));
200}
201
202/**
203 * Frees the accumulator.
204 */
205void SPI::FreeAccumulator() {
206 int32_t status = 0;
207 spiFreeAccumulator(m_port, &status);
208 wpi_setErrorWithContext(status, getHALErrorMessage(status));
209}
210
211/**
212 * Resets the accumulator to zero.
213 */
214void SPI::ResetAccumulator() {
215 int32_t status = 0;
216 spiResetAccumulator(m_port, &status);
217 wpi_setErrorWithContext(status, getHALErrorMessage(status));
218}
219
220/**
221 * Set the center value of the accumulator.
222 *
223 * The center value is subtracted from each value before it is added to the accumulator. This
224 * is used for the center value of devices like gyros and accelerometers to make integration work
225 * and to take the device offset into account when integrating.
226 */
227void SPI::SetAccumulatorCenter(int32_t center) {
228 int32_t status = 0;
229 spiSetAccumulatorCenter(m_port, center, &status);
230 wpi_setErrorWithContext(status, getHALErrorMessage(status));
231}
232
233/**
234 * Set the accumulator's deadband.
235 */
236void SPI::SetAccumulatorDeadband(int32_t deadband) {
237 int32_t status = 0;
238 spiSetAccumulatorDeadband(m_port, deadband, &status);
239 wpi_setErrorWithContext(status, getHALErrorMessage(status));
240}
241
242/**
243 * Read the last value read by the accumulator engine.
244 */
245int32_t SPI::GetAccumulatorLastValue() const {
246 int32_t status = 0;
247 int32_t retVal = spiGetAccumulatorLastValue(m_port, &status);
248 wpi_setErrorWithContext(status, getHALErrorMessage(status));
249 return retVal;
250}
251
252/**
253 * Read the accumulated value.
254 *
255 * @return The 64-bit value accumulated since the last Reset().
256 */
257int64_t SPI::GetAccumulatorValue() const {
258 int32_t status = 0;
259 int64_t retVal = spiGetAccumulatorValue(m_port, &status);
260 wpi_setErrorWithContext(status, getHALErrorMessage(status));
261 return retVal;
262}
263
264/**
265 * Read the number of accumulated values.
266 *
267 * Read the count of the accumulated values since the accumulator was last Reset().
268 *
269 * @return The number of times samples from the channel were accumulated.
270 */
271uint32_t SPI::GetAccumulatorCount() const {
272 int32_t status = 0;
273 uint32_t retVal = spiGetAccumulatorCount(m_port, &status);
274 wpi_setErrorWithContext(status, getHALErrorMessage(status));
275 return retVal;
276}
277
278/**
279 * Read the average of the accumulated value.
280 *
281 * @return The accumulated average value (value / count).
282 */
283double SPI::GetAccumulatorAverage() const {
284 int32_t status = 0;
285 double retVal = spiGetAccumulatorAverage(m_port, &status);
286 wpi_setErrorWithContext(status, getHALErrorMessage(status));
287 return retVal;
288}
289
290/**
291 * Read the accumulated value and the number of accumulated values atomically.
292 *
293 * This function reads the value and count atomically.
294 * This can be used for averaging.
295 *
296 * @param value Pointer to the 64-bit accumulated output.
297 * @param count Pointer to the number of accumulation cycles.
298 */
299void SPI::GetAccumulatorOutput(int64_t &value, uint32_t &count) const {
300 int32_t status = 0;
301 spiGetAccumulatorOutput(m_port, &value, &count, &status);
302 wpi_setErrorWithContext(status, getHALErrorMessage(status));
303}