blob: 6b41adb4b7c4b69ece2836acefb786bd761de821 [file] [log] [blame]
Brian Silverman41cdd3e2019-01-19 19:48:58 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) 2008-2018 FIRST. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "frc/SPI.h"
9
10#include <cstring>
11#include <utility>
12
13#include <hal/HAL.h>
14#include <hal/SPI.h>
15#include <wpi/SmallVector.h>
16#include <wpi/mutex.h>
17
18#include "frc/DigitalSource.h"
19#include "frc/Notifier.h"
20#include "frc/WPIErrors.h"
21
22using namespace frc;
23
24static constexpr int kAccumulateDepth = 2048;
25
26class SPI::Accumulator {
27 public:
28 Accumulator(HAL_SPIPort port, int xferSize, int validMask, int validValue,
29 int dataShift, int dataSize, bool isSigned, bool bigEndian)
30 : m_notifier([=]() {
31 std::lock_guard<wpi::mutex> lock(m_mutex);
32 Update();
33 }),
34 m_buf(new uint32_t[(xferSize + 1) * kAccumulateDepth]),
35 m_validMask(validMask),
36 m_validValue(validValue),
37 m_dataMax(1 << dataSize),
38 m_dataMsbMask(1 << (dataSize - 1)),
39 m_dataShift(dataShift),
40 m_xferSize(xferSize + 1), // +1 for timestamp
41 m_isSigned(isSigned),
42 m_bigEndian(bigEndian),
43 m_port(port) {}
44 ~Accumulator() { delete[] m_buf; }
45
46 void Update();
47
48 Notifier m_notifier;
49 uint32_t* m_buf;
50 wpi::mutex m_mutex;
51
52 int64_t m_value = 0;
53 uint32_t m_count = 0;
54 int32_t m_lastValue = 0;
55 uint32_t m_lastTimestamp = 0;
56 double m_integratedValue = 0;
57
58 int32_t m_center = 0;
59 int32_t m_deadband = 0;
60 double m_integratedCenter = 0;
61
62 int32_t m_validMask;
63 int32_t m_validValue;
64 int32_t m_dataMax; // one more than max data value
65 int32_t m_dataMsbMask; // data field MSB mask (for signed)
66 uint8_t m_dataShift; // data field shift right amount, in bits
67 int32_t m_xferSize; // SPI transfer size, in bytes
68 bool m_isSigned; // is data field signed?
69 bool m_bigEndian; // is response big endian?
70 HAL_SPIPort m_port;
71};
72
73void SPI::Accumulator::Update() {
74 bool done;
75 do {
76 done = true;
77 int32_t status = 0;
78
79 // get amount of data available
80 int32_t numToRead =
81 HAL_ReadSPIAutoReceivedData(m_port, m_buf, 0, 0, &status);
82 if (status != 0) return; // error reading
83
84 // only get whole responses; +1 is for timestamp
85 numToRead -= numToRead % m_xferSize;
86 if (numToRead > m_xferSize * kAccumulateDepth) {
87 numToRead = m_xferSize * kAccumulateDepth;
88 done = false;
89 }
90 if (numToRead == 0) return; // no samples
91
92 // read buffered data
93 HAL_ReadSPIAutoReceivedData(m_port, m_buf, numToRead, 0, &status);
94 if (status != 0) return; // error reading
95
96 // loop over all responses
97 for (int32_t off = 0; off < numToRead; off += m_xferSize) {
98 // get timestamp from first word
99 uint32_t timestamp = m_buf[off];
100
101 // convert from bytes
102 uint32_t resp = 0;
103 if (m_bigEndian) {
104 for (int32_t i = 1; i < m_xferSize; ++i) {
105 resp <<= 8;
106 resp |= m_buf[off + i] & 0xff;
107 }
108 } else {
109 for (int32_t i = m_xferSize - 1; i >= 1; --i) {
110 resp <<= 8;
111 resp |= m_buf[off + i] & 0xff;
112 }
113 }
114
115 // process response
116 if ((resp & m_validMask) == static_cast<uint32_t>(m_validValue)) {
117 // valid sensor data; extract data field
118 int32_t data = static_cast<int32_t>(resp >> m_dataShift);
119 data &= m_dataMax - 1;
120 // 2s complement conversion if signed MSB is set
121 if (m_isSigned && (data & m_dataMsbMask) != 0) data -= m_dataMax;
122 // center offset
123 int32_t dataNoCenter = data;
124 data -= m_center;
125 // only accumulate if outside deadband
126 if (data < -m_deadband || data > m_deadband) {
127 m_value += data;
128 if (m_count != 0) {
129 // timestamps use the 1us FPGA clock; also handle rollover
130 if (timestamp >= m_lastTimestamp)
131 m_integratedValue +=
132 dataNoCenter *
133 static_cast<int32_t>(timestamp - m_lastTimestamp) * 1e-6 -
134 m_integratedCenter;
135 else
136 m_integratedValue +=
137 dataNoCenter *
138 static_cast<int32_t>((1ULL << 32) - m_lastTimestamp +
139 timestamp) *
140 1e-6 -
141 m_integratedCenter;
142 }
143 }
144 ++m_count;
145 m_lastValue = data;
146 } else {
147 // no data from the sensor; just clear the last value
148 m_lastValue = 0;
149 }
150 m_lastTimestamp = timestamp;
151 }
152 } while (!done);
153}
154
155SPI::SPI(Port port) : m_port(static_cast<HAL_SPIPort>(port)) {
156 int32_t status = 0;
157 HAL_InitializeSPI(m_port, &status);
158 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
159
160 static int instances = 0;
161 instances++;
162 HAL_Report(HALUsageReporting::kResourceType_SPI, instances);
163}
164
165SPI::~SPI() { HAL_CloseSPI(m_port); }
166
167SPI::SPI(SPI&& rhs)
168 : ErrorBase(std::move(rhs)),
169 m_msbFirst(std::move(rhs.m_msbFirst)),
170 m_sampleOnTrailing(std::move(rhs.m_sampleOnTrailing)),
171 m_clockIdleHigh(std::move(rhs.m_clockIdleHigh)),
172 m_accum(std::move(rhs.m_accum)) {
173 std::swap(m_port, rhs.m_port);
174}
175
176SPI& SPI::operator=(SPI&& rhs) {
177 ErrorBase::operator=(std::move(rhs));
178
179 std::swap(m_port, rhs.m_port);
180 m_msbFirst = std::move(rhs.m_msbFirst);
181 m_sampleOnTrailing = std::move(rhs.m_sampleOnTrailing);
182 m_clockIdleHigh = std::move(rhs.m_clockIdleHigh);
183 m_accum = std::move(rhs.m_accum);
184
185 return *this;
186}
187
188void SPI::SetClockRate(double hz) { HAL_SetSPISpeed(m_port, hz); }
189
190void SPI::SetMSBFirst() {
191 m_msbFirst = true;
192 HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
193}
194
195void SPI::SetLSBFirst() {
196 m_msbFirst = false;
197 HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
198}
199
200void SPI::SetSampleDataOnLeadingEdge() {
201 m_sampleOnTrailing = false;
202 HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
203}
204
205void SPI::SetSampleDataOnTrailingEdge() {
206 m_sampleOnTrailing = true;
207 HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
208}
209
210void SPI::SetSampleDataOnFalling() {
211 m_sampleOnTrailing = true;
212 HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
213}
214
215void SPI::SetSampleDataOnRising() {
216 m_sampleOnTrailing = false;
217 HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
218}
219
220void SPI::SetClockActiveLow() {
221 m_clockIdleHigh = true;
222 HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
223}
224
225void SPI::SetClockActiveHigh() {
226 m_clockIdleHigh = false;
227 HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clockIdleHigh);
228}
229
230void SPI::SetChipSelectActiveHigh() {
231 int32_t status = 0;
232 HAL_SetSPIChipSelectActiveHigh(m_port, &status);
233 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
234}
235
236void SPI::SetChipSelectActiveLow() {
237 int32_t status = 0;
238 HAL_SetSPIChipSelectActiveLow(m_port, &status);
239 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
240}
241
242int SPI::Write(uint8_t* data, int size) {
243 int retVal = 0;
244 retVal = HAL_WriteSPI(m_port, data, size);
245 return retVal;
246}
247
248int SPI::Read(bool initiate, uint8_t* dataReceived, int size) {
249 int retVal = 0;
250 if (initiate) {
251 wpi::SmallVector<uint8_t, 32> dataToSend;
252 dataToSend.resize(size);
253 retVal = HAL_TransactionSPI(m_port, dataToSend.data(), dataReceived, size);
254 } else {
255 retVal = HAL_ReadSPI(m_port, dataReceived, size);
256 }
257 return retVal;
258}
259
260int SPI::Transaction(uint8_t* dataToSend, uint8_t* dataReceived, int size) {
261 int retVal = 0;
262 retVal = HAL_TransactionSPI(m_port, dataToSend, dataReceived, size);
263 return retVal;
264}
265
266void SPI::InitAuto(int bufferSize) {
267 int32_t status = 0;
268 HAL_InitSPIAuto(m_port, bufferSize, &status);
269 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
270}
271
272void SPI::FreeAuto() {
273 int32_t status = 0;
274 HAL_FreeSPIAuto(m_port, &status);
275 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
276}
277
278void SPI::SetAutoTransmitData(wpi::ArrayRef<uint8_t> dataToSend, int zeroSize) {
279 int32_t status = 0;
280 HAL_SetSPIAutoTransmitData(m_port, dataToSend.data(), dataToSend.size(),
281 zeroSize, &status);
282 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
283}
284
285void SPI::StartAutoRate(double period) {
286 int32_t status = 0;
287 HAL_StartSPIAutoRate(m_port, period, &status);
288 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
289}
290
291void SPI::StartAutoTrigger(DigitalSource& source, bool rising, bool falling) {
292 int32_t status = 0;
293 HAL_StartSPIAutoTrigger(
294 m_port, source.GetPortHandleForRouting(),
295 (HAL_AnalogTriggerType)source.GetAnalogTriggerTypeForRouting(), rising,
296 falling, &status);
297 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
298}
299
300void SPI::StopAuto() {
301 int32_t status = 0;
302 HAL_StopSPIAuto(m_port, &status);
303 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
304}
305
306void SPI::ForceAutoRead() {
307 int32_t status = 0;
308 HAL_ForceSPIAutoRead(m_port, &status);
309 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
310}
311
312int SPI::ReadAutoReceivedData(uint32_t* buffer, int numToRead, double timeout) {
313 int32_t status = 0;
314 int32_t val =
315 HAL_ReadSPIAutoReceivedData(m_port, buffer, numToRead, timeout, &status);
316 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
317 return val;
318}
319
320int SPI::GetAutoDroppedCount() {
321 int32_t status = 0;
322 int32_t val = HAL_GetSPIAutoDroppedCount(m_port, &status);
323 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
324 return val;
325}
326
327void SPI::InitAccumulator(double period, int cmd, int xferSize, int validMask,
328 int validValue, int dataShift, int dataSize,
329 bool isSigned, bool bigEndian) {
330 InitAuto(xferSize * kAccumulateDepth);
331 uint8_t cmdBytes[4] = {0, 0, 0, 0};
332 if (bigEndian) {
333 for (int32_t i = xferSize - 1; i >= 0; --i) {
334 cmdBytes[i] = cmd & 0xff;
335 cmd >>= 8;
336 }
337 } else {
338 cmdBytes[0] = cmd & 0xff;
339 cmd >>= 8;
340 cmdBytes[1] = cmd & 0xff;
341 cmd >>= 8;
342 cmdBytes[2] = cmd & 0xff;
343 cmd >>= 8;
344 cmdBytes[3] = cmd & 0xff;
345 }
346 SetAutoTransmitData(cmdBytes, xferSize - 4);
347 StartAutoRate(period);
348
349 m_accum.reset(new Accumulator(m_port, xferSize, validMask, validValue,
350 dataShift, dataSize, isSigned, bigEndian));
351 m_accum->m_notifier.StartPeriodic(period * kAccumulateDepth / 2);
352}
353
354void SPI::FreeAccumulator() {
355 m_accum.reset(nullptr);
356 FreeAuto();
357}
358
359void SPI::ResetAccumulator() {
360 if (!m_accum) return;
361 std::lock_guard<wpi::mutex> lock(m_accum->m_mutex);
362 m_accum->m_value = 0;
363 m_accum->m_count = 0;
364 m_accum->m_lastValue = 0;
365 m_accum->m_lastTimestamp = 0;
366 m_accum->m_integratedValue = 0;
367}
368
369void SPI::SetAccumulatorCenter(int center) {
370 if (!m_accum) return;
371 std::lock_guard<wpi::mutex> lock(m_accum->m_mutex);
372 m_accum->m_center = center;
373}
374
375void SPI::SetAccumulatorDeadband(int deadband) {
376 if (!m_accum) return;
377 std::lock_guard<wpi::mutex> lock(m_accum->m_mutex);
378 m_accum->m_deadband = deadband;
379}
380
381int SPI::GetAccumulatorLastValue() const {
382 if (!m_accum) return 0;
383 std::lock_guard<wpi::mutex> lock(m_accum->m_mutex);
384 m_accum->Update();
385 return m_accum->m_lastValue;
386}
387
388int64_t SPI::GetAccumulatorValue() const {
389 if (!m_accum) return 0;
390 std::lock_guard<wpi::mutex> lock(m_accum->m_mutex);
391 m_accum->Update();
392 return m_accum->m_value;
393}
394
395int64_t SPI::GetAccumulatorCount() const {
396 if (!m_accum) return 0;
397 std::lock_guard<wpi::mutex> lock(m_accum->m_mutex);
398 m_accum->Update();
399 return m_accum->m_count;
400}
401
402double SPI::GetAccumulatorAverage() const {
403 if (!m_accum) return 0;
404 std::lock_guard<wpi::mutex> lock(m_accum->m_mutex);
405 m_accum->Update();
406 if (m_accum->m_count == 0) return 0.0;
407 return static_cast<double>(m_accum->m_value) / m_accum->m_count;
408}
409
410void SPI::GetAccumulatorOutput(int64_t& value, int64_t& count) const {
411 if (!m_accum) {
412 value = 0;
413 count = 0;
414 return;
415 }
416 std::lock_guard<wpi::mutex> lock(m_accum->m_mutex);
417 m_accum->Update();
418 value = m_accum->m_value;
419 count = m_accum->m_count;
420}
421
422void SPI::SetAccumulatorIntegratedCenter(double center) {
423 if (!m_accum) return;
424 std::lock_guard<wpi::mutex> lock(m_accum->m_mutex);
425 m_accum->m_integratedCenter = center;
426}
427
428double SPI::GetAccumulatorIntegratedValue() const {
429 if (!m_accum) return 0;
430 std::lock_guard<wpi::mutex> lock(m_accum->m_mutex);
431 m_accum->Update();
432 return m_accum->m_integratedValue;
433}
434
435double SPI::GetAccumulatorIntegratedAverage() const {
436 if (!m_accum) return 0;
437 std::lock_guard<wpi::mutex> lock(m_accum->m_mutex);
438 m_accum->Update();
439 if (m_accum->m_count <= 1) return 0.0;
440 // count-1 due to not integrating the first value received
441 return m_accum->m_integratedValue / (m_accum->m_count - 1);
442}