blob: 6cc8bf071526e922111cccfbff697dc08ed637b6 [file] [log] [blame]
Brian Silvermanb5b46ca2016-03-13 01:14:17 -05001#include "frc971/wpilib/dma.h"
Brian Silverman2aa83d72015-01-24 18:03:11 -05002
3#include <algorithm>
Tyler Chatowbf0609c2021-07-31 16:13:27 -07004#include <cstring>
Brian Silvermand49fd782015-01-30 16:43:17 -05005#include <type_traits>
6
Parker Schuhd3b7a8872018-02-19 16:42:27 -08007#include "frc971/wpilib/ahal/AnalogInput.h"
8#include "frc971/wpilib/ahal/DigitalSource.h"
9#include "frc971/wpilib/ahal/Encoder.h"
Austin Schuh6c053ef2021-09-26 14:32:16 -070010#include "glog/logging.h"
Austin Schuhf6b94632019-02-02 22:11:27 -080011#include "hal/HAL.h"
Brian Silvermand49fd782015-01-30 16:43:17 -050012
13// Interface to the roboRIO FPGA's DMA features.
Brian Silverman2aa83d72015-01-24 18:03:11 -050014
15// Like tEncoder::tOutput with the bitfields reversed.
16typedef union {
17 struct {
Parker Schuhd3b7a8872018-02-19 16:42:27 -080018 unsigned Direction : 1;
19 signed Value : 31;
Brian Silverman2aa83d72015-01-24 18:03:11 -050020 };
21 struct {
Parker Schuhd3b7a8872018-02-19 16:42:27 -080022 unsigned value : 32;
Brian Silverman2aa83d72015-01-24 18:03:11 -050023 };
24} t1Output;
25
Brian Silvermane48dbc12017-02-04 20:06:29 -080026static const int32_t kNumHeaders = 10;
Brian Silverman2aa83d72015-01-24 18:03:11 -050027
Austin Schuh91c75562015-12-20 22:23:10 -080028static constexpr ssize_t kChannelSize[20] = {2, 2, 4, 4, 2, 2, 4, 4, 3, 3,
29 2, 1, 4, 4, 4, 4, 4, 4, 4, 4};
Brian Silvermane48dbc12017-02-04 20:06:29 -080030
Brian Silverman2aa83d72015-01-24 18:03:11 -050031enum DMAOffsetConstants {
32 kEnable_AI0_Low = 0,
33 kEnable_AI0_High = 1,
34 kEnable_AIAveraged0_Low = 2,
35 kEnable_AIAveraged0_High = 3,
36 kEnable_AI1_Low = 4,
37 kEnable_AI1_High = 5,
38 kEnable_AIAveraged1_Low = 6,
39 kEnable_AIAveraged1_High = 7,
40 kEnable_Accumulator0 = 8,
41 kEnable_Accumulator1 = 9,
42 kEnable_DI = 10,
43 kEnable_AnalogTriggers = 11,
44 kEnable_Counters_Low = 12,
45 kEnable_Counters_High = 13,
46 kEnable_CounterTimers_Low = 14,
47 kEnable_CounterTimers_High = 15,
Austin Schuh91c75562015-12-20 22:23:10 -080048 kEnable_Encoders_Low = 16,
49 kEnable_Encoders_High = 17,
50 kEnable_EncoderTimers_Low = 18,
51 kEnable_EncoderTimers_High = 19,
Brian Silverman2aa83d72015-01-24 18:03:11 -050052};
53
54DMA::DMA() {
55 tRioStatusCode status = 0;
56 tdma_config_ = tDMA::create(&status);
Austin Schuh58d8cdf2015-02-15 21:04:42 -080057 tdma_config_->writeConfig_ExternalClock(false, &status);
Brian Silvermane48dbc12017-02-04 20:06:29 -080058 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -050059 if (status != 0) {
60 return;
61 }
62 SetRate(1);
63 SetPause(false);
64}
65
66DMA::~DMA() {
67 tRioStatusCode status = 0;
68
69 manager_->stop(&status);
70 delete tdma_config_;
71}
72
73void DMA::SetPause(bool pause) {
74 tRioStatusCode status = 0;
75 tdma_config_->writeConfig_Pause(pause, &status);
Brian Silvermane48dbc12017-02-04 20:06:29 -080076 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -050077}
78
79void DMA::SetRate(uint32_t cycles) {
80 if (cycles < 1) {
81 cycles = 1;
82 }
83 tRioStatusCode status = 0;
84 tdma_config_->writeRate(cycles, &status);
Brian Silvermane48dbc12017-02-04 20:06:29 -080085 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -050086}
87
Parker Schuhd3b7a8872018-02-19 16:42:27 -080088void DMA::Add(frc::Encoder *encoder) {
Brian Silverman2aa83d72015-01-24 18:03:11 -050089 tRioStatusCode status = 0;
90
91 if (manager_) {
92 wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
Parker Schuhd3b7a8872018-02-19 16:42:27 -080093 "DMA::Add() only works before DMA::Start()");
Brian Silverman2aa83d72015-01-24 18:03:11 -050094 return;
95 }
Austin Schuh91c75562015-12-20 22:23:10 -080096 const int index = encoder->GetFPGAIndex();
97
Austin Schuh91c75562015-12-20 22:23:10 -080098 if (index < 4) {
99 // TODO(austin): Encoder uses a Counter for 1x or 2x; quad for 4x...
100 tdma_config_->writeConfig_Enable_Encoders_Low(true, &status);
101 } else if (index < 8) {
102 // TODO(austin): Encoder uses a Counter for 1x or 2x; quad for 4x...
103 tdma_config_->writeConfig_Enable_Encoders_High(true, &status);
104 } else {
105 wpi_setErrorWithContext(
106 NiFpga_Status_InvalidParameter,
107 "FPGA encoder index is not in the 4 that get logged.");
108 return;
109 }
Brian Silverman2aa83d72015-01-24 18:03:11 -0500110
Brian Silvermane48dbc12017-02-04 20:06:29 -0800111 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500112}
113
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800114void DMA::Add(frc::DigitalSource * /*input*/) {
Brian Silverman2aa83d72015-01-24 18:03:11 -0500115 tRioStatusCode status = 0;
116
117 if (manager_) {
118 wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800119 "DMA::Add() only works before DMA::Start()");
Brian Silverman2aa83d72015-01-24 18:03:11 -0500120 return;
121 }
122
123 tdma_config_->writeConfig_Enable_DI(true, &status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800124 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500125}
126
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800127void DMA::Add(frc::AnalogInput *input) {
Brian Silvermand49fd782015-01-30 16:43:17 -0500128 tRioStatusCode status = 0;
129
130 if (manager_) {
131 wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800132 "DMA::Add() only works before DMA::Start()");
Brian Silvermand49fd782015-01-30 16:43:17 -0500133 return;
134 }
135
Brian Silvermand49fd782015-01-30 16:43:17 -0500136 if (input->GetChannel() <= 3) {
137 tdma_config_->writeConfig_Enable_AI0_Low(true, &status);
138 } else {
139 tdma_config_->writeConfig_Enable_AI0_High(true, &status);
140 }
Brian Silvermane48dbc12017-02-04 20:06:29 -0800141 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silvermand49fd782015-01-30 16:43:17 -0500142}
143
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800144void DMA::SetExternalTrigger(frc::DigitalSource *input, bool rising,
145 bool falling) {
Austin Schuh6c053ef2021-09-26 14:32:16 -0700146 CHECK(input);
Brian Silverman2aa83d72015-01-24 18:03:11 -0500147 tRioStatusCode status = 0;
148
149 if (manager_) {
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800150 wpi_setErrorWithContext(
151 NiFpga_Status_InvalidParameter,
Brian Silverman2aa83d72015-01-24 18:03:11 -0500152 "DMA::SetExternalTrigger() only works before DMA::Start()");
153 return;
154 }
155
156 auto index =
157 ::std::find(trigger_channels_.begin(), trigger_channels_.end(), false);
158 if (index == trigger_channels_.end()) {
159 wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800160 "DMA: No channels left");
Brian Silverman2aa83d72015-01-24 18:03:11 -0500161 return;
162 }
163 *index = true;
164
Austin Schuhb19fddb2015-11-22 22:25:29 -0800165 const int channel_index = ::std::distance(trigger_channels_.begin(), index);
Brian Silverman2aa83d72015-01-24 18:03:11 -0500166
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800167 const bool is_external_clock =
168 tdma_config_->readConfig_ExternalClock(&status);
169 if (status == 0) {
170 if (!is_external_clock) {
171 tdma_config_->writeConfig_ExternalClock(true, &status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800172 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800173 if (status != 0) {
174 return;
175 }
176 }
177 } else {
Brian Silvermane48dbc12017-02-04 20:06:29 -0800178 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500179 return;
180 }
181
Austin Schuh3b5b69b2015-10-31 18:55:47 -0700182 nFPGA::nRoboRIO_FPGANamespace::tDMA::tExternalTriggers new_trigger;
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800183
184 new_trigger.FallingEdge = falling;
185 new_trigger.RisingEdge = rising;
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800186 new_trigger.ExternalClockSource_AnalogTrigger = false;
Austin Schuh91c75562015-12-20 22:23:10 -0800187 unsigned char module = 0;
Austin Schuh94f51e92017-10-30 19:25:32 -0700188 uint32_t channel = input->GetChannel();
Austin Schuh91c75562015-12-20 22:23:10 -0800189 if (channel >= kNumHeaders) {
190 module = 1;
191 channel -= kNumHeaders;
192 } else {
193 module = 0;
194 }
195
196 new_trigger.ExternalClockSource_Module = module;
197 new_trigger.ExternalClockSource_Channel = channel;
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800198
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800199 // Configures the trigger to be external, not off the FPGA clock.
Austin Schuh91c75562015-12-20 22:23:10 -0800200 tdma_config_->writeExternalTriggers(channel_index / 4, channel_index % 4,
201 new_trigger, &status);
202 if (status != 0) {
Brian Silvermane48dbc12017-02-04 20:06:29 -0800203 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Austin Schuh91c75562015-12-20 22:23:10 -0800204 return;
205 }
Brian Silverman2aa83d72015-01-24 18:03:11 -0500206}
207
208DMA::ReadStatus DMA::Read(DMASample *sample, uint32_t timeout_ms,
Brian Silvermand49fd782015-01-30 16:43:17 -0500209 size_t *remaining_out) {
Brian Silverman2aa83d72015-01-24 18:03:11 -0500210 tRioStatusCode status = 0;
211 size_t remainingBytes = 0;
Brian Silvermand49fd782015-01-30 16:43:17 -0500212 *remaining_out = 0;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500213
214 if (!manager_.get()) {
215 wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800216 "DMA::Read() only works after DMA::Start()");
Brian Silverman2aa83d72015-01-24 18:03:11 -0500217 return STATUS_ERROR;
218 }
219
Brian Silvermand49fd782015-01-30 16:43:17 -0500220 sample->dma_ = this;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500221 manager_->read(sample->read_buffer_, capture_size_, timeout_ms,
222 &remainingBytes, &status);
Austin Schuh94f51e92017-10-30 19:25:32 -0700223 sample->CalculateTimestamp();
Brian Silverman2aa83d72015-01-24 18:03:11 -0500224
Brian Silverman2aa83d72015-01-24 18:03:11 -0500225 // TODO(jerry): Do this only if status == 0?
Brian Silvermand49fd782015-01-30 16:43:17 -0500226 *remaining_out = remainingBytes / capture_size_;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500227
Brian Silverman2aa83d72015-01-24 18:03:11 -0500228 // TODO(austin): Check that *remainingBytes % capture_size_ == 0 and deal
229 // with it if it isn't. Probably meant that we overflowed?
230 if (status == 0) {
231 return STATUS_OK;
232 } else if (status == NiFpga_Status_FifoTimeout) {
233 return STATUS_TIMEOUT;
234 } else {
Brian Silvermane48dbc12017-02-04 20:06:29 -0800235 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500236 return STATUS_ERROR;
237 }
238}
239
Brian Silvermand49fd782015-01-30 16:43:17 -0500240const char *DMA::NameOfReadStatus(ReadStatus s) {
241 switch (s) {
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800242 case STATUS_OK:
243 return "OK";
244 case STATUS_TIMEOUT:
245 return "TIMEOUT";
246 case STATUS_ERROR:
247 return "ERROR";
248 default:
249 return "(bad ReadStatus code)";
Brian Silvermand49fd782015-01-30 16:43:17 -0500250 }
251}
252
Brian Silverman2aa83d72015-01-24 18:03:11 -0500253void DMA::Start(size_t queue_depth) {
254 tRioStatusCode status = 0;
255 tconfig_ = tdma_config_->readConfig(&status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800256 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500257 if (status != 0) {
258 return;
259 }
260
261 {
262 size_t accum_size = 0;
263#define SET_SIZE(bit) \
264 if (tconfig_.bit) { \
265 channel_offsets_[k##bit] = accum_size; \
266 accum_size += kChannelSize[k##bit]; \
267 } else { \
268 channel_offsets_[k##bit] = -1; \
269 }
270
271 SET_SIZE(Enable_AI0_Low);
272 SET_SIZE(Enable_AI0_High);
273 SET_SIZE(Enable_AIAveraged0_Low);
274 SET_SIZE(Enable_AIAveraged0_High);
275 SET_SIZE(Enable_AI1_Low);
276 SET_SIZE(Enable_AI1_High);
277 SET_SIZE(Enable_AIAveraged1_Low);
278 SET_SIZE(Enable_AIAveraged1_High);
279 SET_SIZE(Enable_Accumulator0);
280 SET_SIZE(Enable_Accumulator1);
281 SET_SIZE(Enable_DI);
282 SET_SIZE(Enable_AnalogTriggers);
283 SET_SIZE(Enable_Counters_Low);
284 SET_SIZE(Enable_Counters_High);
285 SET_SIZE(Enable_CounterTimers_Low);
286 SET_SIZE(Enable_CounterTimers_High);
Austin Schuh91c75562015-12-20 22:23:10 -0800287 SET_SIZE(Enable_Encoders_Low);
288 SET_SIZE(Enable_Encoders_High);
289 SET_SIZE(Enable_EncoderTimers_Low);
290 SET_SIZE(Enable_EncoderTimers_High);
Brian Silverman2aa83d72015-01-24 18:03:11 -0500291#undef SET_SIZE
292 capture_size_ = accum_size + 1;
293 }
294
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800295 manager_.reset(
Austin Schuh94f51e92017-10-30 19:25:32 -0700296 new nFPGA::tDMAManager(1, queue_depth * capture_size_, &status));
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800297
Brian Silvermane48dbc12017-02-04 20:06:29 -0800298 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500299 if (status != 0) {
300 return;
301 }
302 // Start, stop, start to clear the buffer.
303 manager_->start(&status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800304 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500305 if (status != 0) {
306 return;
307 }
308 manager_->stop(&status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800309 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500310 if (status != 0) {
311 return;
312 }
313 manager_->start(&status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800314 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500315 if (status != 0) {
316 return;
317 }
318}
319
Brian Silvermand49fd782015-01-30 16:43:17 -0500320static_assert(::std::is_pod<DMASample>::value, "DMASample needs to be POD");
321
Austin Schuh94f51e92017-10-30 19:25:32 -0700322ssize_t DMASample::offset(int index) const {
323 return dma_->channel_offsets_[index];
324}
Brian Silverman2aa83d72015-01-24 18:03:11 -0500325
Austin Schuh94f51e92017-10-30 19:25:32 -0700326void DMASample::CalculateTimestamp() {
327 uint32_t lower_sample = read_buffer_[dma_->capture_size_ - 1];
328#if WPILIB2018
329 int32_t status = 0;
330 fpga_timestamp_ = HAL_ExpandFPGATime(lower_sample, &status);
331 assert(status == 0);
332#else
333 fpga_timestamp_ = lower_sample;
334#endif
335}
336
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700337uint64_t DMASample::GetTime() const { return fpga_timestamp_; }
Austin Schuh8f314a92015-11-22 21:35:40 -0800338
Brian Silverman2aa83d72015-01-24 18:03:11 -0500339double DMASample::GetTimestamp() const {
Austin Schuh8f314a92015-11-22 21:35:40 -0800340 return static_cast<double>(GetTime()) * 0.000001;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500341}
342
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800343bool DMASample::Get(frc::DigitalSource *input) const {
Brian Silverman2aa83d72015-01-24 18:03:11 -0500344 if (offset(kEnable_DI) == -1) {
Austin Schuh91c75562015-12-20 22:23:10 -0800345 wpi_setStaticErrorWithContext(
346 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800347 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500348 return false;
349 }
Austin Schuh94f51e92017-10-30 19:25:32 -0700350 const uint32_t channel = input->GetChannel();
Brian Silvermane48dbc12017-02-04 20:06:29 -0800351 if (channel < kNumHeaders) {
352 return (read_buffer_[offset(kEnable_DI)] >> channel) & 0x1;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500353 } else {
Brian Silvermane48dbc12017-02-04 20:06:29 -0800354 return (read_buffer_[offset(kEnable_DI)] >> (channel + 6)) & 0x1;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500355 }
356}
357
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800358int32_t DMASample::GetRaw(frc::Encoder *input) const {
Austin Schuh91c75562015-12-20 22:23:10 -0800359 int index = input->GetFPGAIndex();
360 uint32_t dmaWord = 0;
Austin Schuh91c75562015-12-20 22:23:10 -0800361 if (index < 4) {
362 if (offset(kEnable_Encoders_Low) == -1) {
363 wpi_setStaticErrorWithContext(
364 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800365 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh91c75562015-12-20 22:23:10 -0800366 return -1;
367 }
368 dmaWord = read_buffer_[offset(kEnable_Encoders_Low) + index];
369 } else if (index < 8) {
370 if (offset(kEnable_Encoders_High) == -1) {
371 wpi_setStaticErrorWithContext(
372 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800373 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh91c75562015-12-20 22:23:10 -0800374 return -1;
375 }
376 dmaWord = read_buffer_[offset(kEnable_Encoders_High) + (index - 4)];
377 } else {
378 wpi_setStaticErrorWithContext(
379 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800380 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh91c75562015-12-20 22:23:10 -0800381 return 0;
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800382 }
383
Brian Silverman2aa83d72015-01-24 18:03:11 -0500384 int32_t result = 0;
385
Austin Schuhb19fddb2015-11-22 22:25:29 -0800386 // Extract the 31-bit signed tEncoder::tOutput Value using a struct with the
387 // reverse packed field order of tOutput. This gets Value from the high
388 // order 31 bits of output on little-endian ARM using gcc. This works
389 // even though C/C++ doesn't guarantee bitfield order.
390 t1Output output;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500391
Austin Schuhb19fddb2015-11-22 22:25:29 -0800392 output.value = dmaWord;
393 result = output.Value;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500394
395 return result;
396}
397
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800398int32_t DMASample::Get(frc::Encoder *input) const {
Brian Silverman2aa83d72015-01-24 18:03:11 -0500399 int32_t raw = GetRaw(input);
400
Brian Silvermand49fd782015-01-30 16:43:17 -0500401 return raw / input->GetEncodingScale();
402}
403
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800404uint16_t DMASample::GetValue(frc::AnalogInput *input) const {
Austin Schuh91c75562015-12-20 22:23:10 -0800405 uint32_t channel = input->GetChannel();
406 uint32_t dmaWord;
407 if (channel < 4) {
408 if (offset(kEnable_AI0_Low) == -1) {
409 wpi_setStaticErrorWithContext(
410 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800411 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh91c75562015-12-20 22:23:10 -0800412 return 0xffff;
413 }
414 dmaWord = read_buffer_[offset(kEnable_AI0_Low) + channel / 2];
415 } else if (channel < 8) {
416 if (offset(kEnable_AI0_High) == -1) {
417 wpi_setStaticErrorWithContext(
418 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800419 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh91c75562015-12-20 22:23:10 -0800420 return 0xffff;
421 }
422 dmaWord = read_buffer_[offset(kEnable_AI0_High) + (channel - 4) / 2];
423 } else {
424 wpi_setStaticErrorWithContext(
425 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800426 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800427 return 0xffff;
Brian Silvermand49fd782015-01-30 16:43:17 -0500428 }
Austin Schuhc6cc4102015-02-15 23:19:53 -0800429 if (channel % 2) {
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800430 return (dmaWord >> 16) & 0xffff;
431 } else {
Austin Schuh91c75562015-12-20 22:23:10 -0800432 return dmaWord & 0xffff;
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800433 }
Brian Silvermand49fd782015-01-30 16:43:17 -0500434 return static_cast<int16_t>(dmaWord);
435}
436
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800437float DMASample::GetVoltage(frc::AnalogInput *input) const {
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800438 uint16_t value = GetValue(input);
439 if (value == 0xffff) return 0.0;
Brian Silvermand49fd782015-01-30 16:43:17 -0500440 uint32_t lsb_weight = input->GetLSBWeight();
441 int32_t offset = input->GetOffset();
442 float voltage = lsb_weight * 1.0e-9 * value - offset * 1.0e-9;
443 return voltage;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500444}