blob: 432b77b37853bafb715061ecaa5f4c2aabcd634d [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 Schuhf6b94632019-02-02 22:11:27 -080010#include "hal/HAL.h"
Brian Silvermand49fd782015-01-30 16:43:17 -050011
12// Interface to the roboRIO FPGA's DMA features.
Brian Silverman2aa83d72015-01-24 18:03:11 -050013
14// Like tEncoder::tOutput with the bitfields reversed.
15typedef union {
16 struct {
Parker Schuhd3b7a8872018-02-19 16:42:27 -080017 unsigned Direction : 1;
18 signed Value : 31;
Brian Silverman2aa83d72015-01-24 18:03:11 -050019 };
20 struct {
Parker Schuhd3b7a8872018-02-19 16:42:27 -080021 unsigned value : 32;
Brian Silverman2aa83d72015-01-24 18:03:11 -050022 };
23} t1Output;
24
Brian Silvermane48dbc12017-02-04 20:06:29 -080025static const int32_t kNumHeaders = 10;
Brian Silverman2aa83d72015-01-24 18:03:11 -050026
Austin Schuh91c75562015-12-20 22:23:10 -080027static constexpr ssize_t kChannelSize[20] = {2, 2, 4, 4, 2, 2, 4, 4, 3, 3,
28 2, 1, 4, 4, 4, 4, 4, 4, 4, 4};
Brian Silvermane48dbc12017-02-04 20:06:29 -080029
Brian Silverman2aa83d72015-01-24 18:03:11 -050030enum DMAOffsetConstants {
31 kEnable_AI0_Low = 0,
32 kEnable_AI0_High = 1,
33 kEnable_AIAveraged0_Low = 2,
34 kEnable_AIAveraged0_High = 3,
35 kEnable_AI1_Low = 4,
36 kEnable_AI1_High = 5,
37 kEnable_AIAveraged1_Low = 6,
38 kEnable_AIAveraged1_High = 7,
39 kEnable_Accumulator0 = 8,
40 kEnable_Accumulator1 = 9,
41 kEnable_DI = 10,
42 kEnable_AnalogTriggers = 11,
43 kEnable_Counters_Low = 12,
44 kEnable_Counters_High = 13,
45 kEnable_CounterTimers_Low = 14,
46 kEnable_CounterTimers_High = 15,
Austin Schuh91c75562015-12-20 22:23:10 -080047 kEnable_Encoders_Low = 16,
48 kEnable_Encoders_High = 17,
49 kEnable_EncoderTimers_Low = 18,
50 kEnable_EncoderTimers_High = 19,
Brian Silverman2aa83d72015-01-24 18:03:11 -050051};
52
53DMA::DMA() {
54 tRioStatusCode status = 0;
55 tdma_config_ = tDMA::create(&status);
Austin Schuh58d8cdf2015-02-15 21:04:42 -080056 tdma_config_->writeConfig_ExternalClock(false, &status);
Brian Silvermane48dbc12017-02-04 20:06:29 -080057 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -050058 if (status != 0) {
59 return;
60 }
61 SetRate(1);
62 SetPause(false);
63}
64
65DMA::~DMA() {
66 tRioStatusCode status = 0;
67
68 manager_->stop(&status);
69 delete tdma_config_;
70}
71
72void DMA::SetPause(bool pause) {
73 tRioStatusCode status = 0;
74 tdma_config_->writeConfig_Pause(pause, &status);
Brian Silvermane48dbc12017-02-04 20:06:29 -080075 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -050076}
77
78void DMA::SetRate(uint32_t cycles) {
79 if (cycles < 1) {
80 cycles = 1;
81 }
82 tRioStatusCode status = 0;
83 tdma_config_->writeRate(cycles, &status);
Brian Silvermane48dbc12017-02-04 20:06:29 -080084 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -050085}
86
Parker Schuhd3b7a8872018-02-19 16:42:27 -080087void DMA::Add(frc::Encoder *encoder) {
Brian Silverman2aa83d72015-01-24 18:03:11 -050088 tRioStatusCode status = 0;
89
90 if (manager_) {
91 wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
Parker Schuhd3b7a8872018-02-19 16:42:27 -080092 "DMA::Add() only works before DMA::Start()");
Brian Silverman2aa83d72015-01-24 18:03:11 -050093 return;
94 }
Austin Schuh91c75562015-12-20 22:23:10 -080095 const int index = encoder->GetFPGAIndex();
96
Austin Schuh91c75562015-12-20 22:23:10 -080097 if (index < 4) {
98 // TODO(austin): Encoder uses a Counter for 1x or 2x; quad for 4x...
99 tdma_config_->writeConfig_Enable_Encoders_Low(true, &status);
100 } else if (index < 8) {
101 // TODO(austin): Encoder uses a Counter for 1x or 2x; quad for 4x...
102 tdma_config_->writeConfig_Enable_Encoders_High(true, &status);
103 } else {
104 wpi_setErrorWithContext(
105 NiFpga_Status_InvalidParameter,
106 "FPGA encoder index is not in the 4 that get logged.");
107 return;
108 }
Brian Silverman2aa83d72015-01-24 18:03:11 -0500109
Brian Silvermane48dbc12017-02-04 20:06:29 -0800110 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500111}
112
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800113void DMA::Add(frc::DigitalSource * /*input*/) {
Brian Silverman2aa83d72015-01-24 18:03:11 -0500114 tRioStatusCode status = 0;
115
116 if (manager_) {
117 wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800118 "DMA::Add() only works before DMA::Start()");
Brian Silverman2aa83d72015-01-24 18:03:11 -0500119 return;
120 }
121
122 tdma_config_->writeConfig_Enable_DI(true, &status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800123 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500124}
125
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800126void DMA::Add(frc::AnalogInput *input) {
Brian Silvermand49fd782015-01-30 16:43:17 -0500127 tRioStatusCode status = 0;
128
129 if (manager_) {
130 wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800131 "DMA::Add() only works before DMA::Start()");
Brian Silvermand49fd782015-01-30 16:43:17 -0500132 return;
133 }
134
Brian Silvermand49fd782015-01-30 16:43:17 -0500135 if (input->GetChannel() <= 3) {
136 tdma_config_->writeConfig_Enable_AI0_Low(true, &status);
137 } else {
138 tdma_config_->writeConfig_Enable_AI0_High(true, &status);
139 }
Brian Silvermane48dbc12017-02-04 20:06:29 -0800140 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silvermand49fd782015-01-30 16:43:17 -0500141}
142
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800143void DMA::SetExternalTrigger(frc::DigitalSource *input, bool rising,
144 bool falling) {
Brian Silverman2aa83d72015-01-24 18:03:11 -0500145 tRioStatusCode status = 0;
146
147 if (manager_) {
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800148 wpi_setErrorWithContext(
149 NiFpga_Status_InvalidParameter,
Brian Silverman2aa83d72015-01-24 18:03:11 -0500150 "DMA::SetExternalTrigger() only works before DMA::Start()");
151 return;
152 }
153
154 auto index =
155 ::std::find(trigger_channels_.begin(), trigger_channels_.end(), false);
156 if (index == trigger_channels_.end()) {
157 wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800158 "DMA: No channels left");
Brian Silverman2aa83d72015-01-24 18:03:11 -0500159 return;
160 }
161 *index = true;
162
Austin Schuhb19fddb2015-11-22 22:25:29 -0800163 const int channel_index = ::std::distance(trigger_channels_.begin(), index);
Brian Silverman2aa83d72015-01-24 18:03:11 -0500164
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800165 const bool is_external_clock =
166 tdma_config_->readConfig_ExternalClock(&status);
167 if (status == 0) {
168 if (!is_external_clock) {
169 tdma_config_->writeConfig_ExternalClock(true, &status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800170 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800171 if (status != 0) {
172 return;
173 }
174 }
175 } else {
Brian Silvermane48dbc12017-02-04 20:06:29 -0800176 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500177 return;
178 }
179
Austin Schuh3b5b69b2015-10-31 18:55:47 -0700180 nFPGA::nRoboRIO_FPGANamespace::tDMA::tExternalTriggers new_trigger;
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800181
182 new_trigger.FallingEdge = falling;
183 new_trigger.RisingEdge = rising;
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800184 new_trigger.ExternalClockSource_AnalogTrigger = false;
Austin Schuh91c75562015-12-20 22:23:10 -0800185 unsigned char module = 0;
Austin Schuh94f51e92017-10-30 19:25:32 -0700186 uint32_t channel = input->GetChannel();
Austin Schuh91c75562015-12-20 22:23:10 -0800187 if (channel >= kNumHeaders) {
188 module = 1;
189 channel -= kNumHeaders;
190 } else {
191 module = 0;
192 }
193
194 new_trigger.ExternalClockSource_Module = module;
195 new_trigger.ExternalClockSource_Channel = channel;
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800196
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800197 // Configures the trigger to be external, not off the FPGA clock.
Austin Schuh91c75562015-12-20 22:23:10 -0800198 tdma_config_->writeExternalTriggers(channel_index / 4, channel_index % 4,
199 new_trigger, &status);
200 if (status != 0) {
Brian Silvermane48dbc12017-02-04 20:06:29 -0800201 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Austin Schuh91c75562015-12-20 22:23:10 -0800202 return;
203 }
Brian Silverman2aa83d72015-01-24 18:03:11 -0500204}
205
206DMA::ReadStatus DMA::Read(DMASample *sample, uint32_t timeout_ms,
Brian Silvermand49fd782015-01-30 16:43:17 -0500207 size_t *remaining_out) {
Brian Silverman2aa83d72015-01-24 18:03:11 -0500208 tRioStatusCode status = 0;
209 size_t remainingBytes = 0;
Brian Silvermand49fd782015-01-30 16:43:17 -0500210 *remaining_out = 0;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500211
212 if (!manager_.get()) {
213 wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800214 "DMA::Read() only works after DMA::Start()");
Brian Silverman2aa83d72015-01-24 18:03:11 -0500215 return STATUS_ERROR;
216 }
217
Brian Silvermand49fd782015-01-30 16:43:17 -0500218 sample->dma_ = this;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500219 manager_->read(sample->read_buffer_, capture_size_, timeout_ms,
220 &remainingBytes, &status);
Austin Schuh94f51e92017-10-30 19:25:32 -0700221 sample->CalculateTimestamp();
Brian Silverman2aa83d72015-01-24 18:03:11 -0500222
Brian Silverman2aa83d72015-01-24 18:03:11 -0500223 // TODO(jerry): Do this only if status == 0?
Brian Silvermand49fd782015-01-30 16:43:17 -0500224 *remaining_out = remainingBytes / capture_size_;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500225
Brian Silverman2aa83d72015-01-24 18:03:11 -0500226 // TODO(austin): Check that *remainingBytes % capture_size_ == 0 and deal
227 // with it if it isn't. Probably meant that we overflowed?
228 if (status == 0) {
229 return STATUS_OK;
230 } else if (status == NiFpga_Status_FifoTimeout) {
231 return STATUS_TIMEOUT;
232 } else {
Brian Silvermane48dbc12017-02-04 20:06:29 -0800233 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500234 return STATUS_ERROR;
235 }
236}
237
Brian Silvermand49fd782015-01-30 16:43:17 -0500238const char *DMA::NameOfReadStatus(ReadStatus s) {
239 switch (s) {
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800240 case STATUS_OK:
241 return "OK";
242 case STATUS_TIMEOUT:
243 return "TIMEOUT";
244 case STATUS_ERROR:
245 return "ERROR";
246 default:
247 return "(bad ReadStatus code)";
Brian Silvermand49fd782015-01-30 16:43:17 -0500248 }
249}
250
Brian Silverman2aa83d72015-01-24 18:03:11 -0500251void DMA::Start(size_t queue_depth) {
252 tRioStatusCode status = 0;
253 tconfig_ = tdma_config_->readConfig(&status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800254 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500255 if (status != 0) {
256 return;
257 }
258
259 {
260 size_t accum_size = 0;
261#define SET_SIZE(bit) \
262 if (tconfig_.bit) { \
263 channel_offsets_[k##bit] = accum_size; \
264 accum_size += kChannelSize[k##bit]; \
265 } else { \
266 channel_offsets_[k##bit] = -1; \
267 }
268
269 SET_SIZE(Enable_AI0_Low);
270 SET_SIZE(Enable_AI0_High);
271 SET_SIZE(Enable_AIAveraged0_Low);
272 SET_SIZE(Enable_AIAveraged0_High);
273 SET_SIZE(Enable_AI1_Low);
274 SET_SIZE(Enable_AI1_High);
275 SET_SIZE(Enable_AIAveraged1_Low);
276 SET_SIZE(Enable_AIAveraged1_High);
277 SET_SIZE(Enable_Accumulator0);
278 SET_SIZE(Enable_Accumulator1);
279 SET_SIZE(Enable_DI);
280 SET_SIZE(Enable_AnalogTriggers);
281 SET_SIZE(Enable_Counters_Low);
282 SET_SIZE(Enable_Counters_High);
283 SET_SIZE(Enable_CounterTimers_Low);
284 SET_SIZE(Enable_CounterTimers_High);
Austin Schuh91c75562015-12-20 22:23:10 -0800285 SET_SIZE(Enable_Encoders_Low);
286 SET_SIZE(Enable_Encoders_High);
287 SET_SIZE(Enable_EncoderTimers_Low);
288 SET_SIZE(Enable_EncoderTimers_High);
Brian Silverman2aa83d72015-01-24 18:03:11 -0500289#undef SET_SIZE
290 capture_size_ = accum_size + 1;
291 }
292
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800293 manager_.reset(
Austin Schuh94f51e92017-10-30 19:25:32 -0700294 new nFPGA::tDMAManager(1, queue_depth * capture_size_, &status));
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800295
Brian Silvermane48dbc12017-02-04 20:06:29 -0800296 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500297 if (status != 0) {
298 return;
299 }
300 // Start, stop, start to clear the buffer.
301 manager_->start(&status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800302 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500303 if (status != 0) {
304 return;
305 }
306 manager_->stop(&status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800307 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500308 if (status != 0) {
309 return;
310 }
311 manager_->start(&status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800312 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500313 if (status != 0) {
314 return;
315 }
316}
317
Brian Silvermand49fd782015-01-30 16:43:17 -0500318static_assert(::std::is_pod<DMASample>::value, "DMASample needs to be POD");
319
Austin Schuh94f51e92017-10-30 19:25:32 -0700320ssize_t DMASample::offset(int index) const {
321 return dma_->channel_offsets_[index];
322}
Brian Silverman2aa83d72015-01-24 18:03:11 -0500323
Austin Schuh94f51e92017-10-30 19:25:32 -0700324void DMASample::CalculateTimestamp() {
325 uint32_t lower_sample = read_buffer_[dma_->capture_size_ - 1];
326#if WPILIB2018
327 int32_t status = 0;
328 fpga_timestamp_ = HAL_ExpandFPGATime(lower_sample, &status);
329 assert(status == 0);
330#else
331 fpga_timestamp_ = lower_sample;
332#endif
333}
334
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700335uint64_t DMASample::GetTime() const { return fpga_timestamp_; }
Austin Schuh8f314a92015-11-22 21:35:40 -0800336
Brian Silverman2aa83d72015-01-24 18:03:11 -0500337double DMASample::GetTimestamp() const {
Austin Schuh8f314a92015-11-22 21:35:40 -0800338 return static_cast<double>(GetTime()) * 0.000001;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500339}
340
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800341bool DMASample::Get(frc::DigitalSource *input) const {
Brian Silverman2aa83d72015-01-24 18:03:11 -0500342 if (offset(kEnable_DI) == -1) {
Austin Schuh91c75562015-12-20 22:23:10 -0800343 wpi_setStaticErrorWithContext(
344 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800345 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500346 return false;
347 }
Austin Schuh94f51e92017-10-30 19:25:32 -0700348 const uint32_t channel = input->GetChannel();
Brian Silvermane48dbc12017-02-04 20:06:29 -0800349 if (channel < kNumHeaders) {
350 return (read_buffer_[offset(kEnable_DI)] >> channel) & 0x1;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500351 } else {
Brian Silvermane48dbc12017-02-04 20:06:29 -0800352 return (read_buffer_[offset(kEnable_DI)] >> (channel + 6)) & 0x1;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500353 }
354}
355
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800356int32_t DMASample::GetRaw(frc::Encoder *input) const {
Austin Schuh91c75562015-12-20 22:23:10 -0800357 int index = input->GetFPGAIndex();
358 uint32_t dmaWord = 0;
Austin Schuh91c75562015-12-20 22:23:10 -0800359 if (index < 4) {
360 if (offset(kEnable_Encoders_Low) == -1) {
361 wpi_setStaticErrorWithContext(
362 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800363 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh91c75562015-12-20 22:23:10 -0800364 return -1;
365 }
366 dmaWord = read_buffer_[offset(kEnable_Encoders_Low) + index];
367 } else if (index < 8) {
368 if (offset(kEnable_Encoders_High) == -1) {
369 wpi_setStaticErrorWithContext(
370 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800371 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh91c75562015-12-20 22:23:10 -0800372 return -1;
373 }
374 dmaWord = read_buffer_[offset(kEnable_Encoders_High) + (index - 4)];
375 } else {
376 wpi_setStaticErrorWithContext(
377 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800378 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh91c75562015-12-20 22:23:10 -0800379 return 0;
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800380 }
381
Brian Silverman2aa83d72015-01-24 18:03:11 -0500382 int32_t result = 0;
383
Austin Schuhb19fddb2015-11-22 22:25:29 -0800384 // Extract the 31-bit signed tEncoder::tOutput Value using a struct with the
385 // reverse packed field order of tOutput. This gets Value from the high
386 // order 31 bits of output on little-endian ARM using gcc. This works
387 // even though C/C++ doesn't guarantee bitfield order.
388 t1Output output;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500389
Austin Schuhb19fddb2015-11-22 22:25:29 -0800390 output.value = dmaWord;
391 result = output.Value;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500392
393 return result;
394}
395
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800396int32_t DMASample::Get(frc::Encoder *input) const {
Brian Silverman2aa83d72015-01-24 18:03:11 -0500397 int32_t raw = GetRaw(input);
398
Brian Silvermand49fd782015-01-30 16:43:17 -0500399 return raw / input->GetEncodingScale();
400}
401
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800402uint16_t DMASample::GetValue(frc::AnalogInput *input) const {
Austin Schuh91c75562015-12-20 22:23:10 -0800403 uint32_t channel = input->GetChannel();
404 uint32_t dmaWord;
405 if (channel < 4) {
406 if (offset(kEnable_AI0_Low) == -1) {
407 wpi_setStaticErrorWithContext(
408 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800409 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh91c75562015-12-20 22:23:10 -0800410 return 0xffff;
411 }
412 dmaWord = read_buffer_[offset(kEnable_AI0_Low) + channel / 2];
413 } else if (channel < 8) {
414 if (offset(kEnable_AI0_High) == -1) {
415 wpi_setStaticErrorWithContext(
416 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800417 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh91c75562015-12-20 22:23:10 -0800418 return 0xffff;
419 }
420 dmaWord = read_buffer_[offset(kEnable_AI0_High) + (channel - 4) / 2];
421 } else {
422 wpi_setStaticErrorWithContext(
423 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800424 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800425 return 0xffff;
Brian Silvermand49fd782015-01-30 16:43:17 -0500426 }
Austin Schuhc6cc4102015-02-15 23:19:53 -0800427 if (channel % 2) {
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800428 return (dmaWord >> 16) & 0xffff;
429 } else {
Austin Schuh91c75562015-12-20 22:23:10 -0800430 return dmaWord & 0xffff;
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800431 }
Brian Silvermand49fd782015-01-30 16:43:17 -0500432 return static_cast<int16_t>(dmaWord);
433}
434
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800435float DMASample::GetVoltage(frc::AnalogInput *input) const {
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800436 uint16_t value = GetValue(input);
437 if (value == 0xffff) return 0.0;
Brian Silvermand49fd782015-01-30 16:43:17 -0500438 uint32_t lsb_weight = input->GetLSBWeight();
439 int32_t offset = input->GetOffset();
440 float voltage = lsb_weight * 1.0e-9 * value - offset * 1.0e-9;
441 return voltage;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500442}