blob: cff1ac1b7a5f80abf11dddc31757efece2c46c00 [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
Philipp Schrader790cb542023-07-05 21:06:52 -07007#include "glog/logging.h"
8
Parker Schuhd3b7a8872018-02-19 16:42:27 -08009#include "frc971/wpilib/ahal/AnalogInput.h"
10#include "frc971/wpilib/ahal/DigitalSource.h"
11#include "frc971/wpilib/ahal/Encoder.h"
Austin Schuhf6b94632019-02-02 22:11:27 -080012#include "hal/HAL.h"
Brian Silvermand49fd782015-01-30 16:43:17 -050013
14// Interface to the roboRIO FPGA's DMA features.
Brian Silverman2aa83d72015-01-24 18:03:11 -050015
16// Like tEncoder::tOutput with the bitfields reversed.
17typedef union {
18 struct {
Parker Schuhd3b7a8872018-02-19 16:42:27 -080019 unsigned Direction : 1;
20 signed Value : 31;
Brian Silverman2aa83d72015-01-24 18:03:11 -050021 };
22 struct {
Parker Schuhd3b7a8872018-02-19 16:42:27 -080023 unsigned value : 32;
Brian Silverman2aa83d72015-01-24 18:03:11 -050024 };
25} t1Output;
26
Brian Silvermane48dbc12017-02-04 20:06:29 -080027static const int32_t kNumHeaders = 10;
Brian Silverman2aa83d72015-01-24 18:03:11 -050028
Austin Schuh91c75562015-12-20 22:23:10 -080029static constexpr ssize_t kChannelSize[20] = {2, 2, 4, 4, 2, 2, 4, 4, 3, 3,
30 2, 1, 4, 4, 4, 4, 4, 4, 4, 4};
Brian Silvermane48dbc12017-02-04 20:06:29 -080031
Brian Silverman2aa83d72015-01-24 18:03:11 -050032enum DMAOffsetConstants {
33 kEnable_AI0_Low = 0,
34 kEnable_AI0_High = 1,
35 kEnable_AIAveraged0_Low = 2,
36 kEnable_AIAveraged0_High = 3,
37 kEnable_AI1_Low = 4,
38 kEnable_AI1_High = 5,
39 kEnable_AIAveraged1_Low = 6,
40 kEnable_AIAveraged1_High = 7,
41 kEnable_Accumulator0 = 8,
42 kEnable_Accumulator1 = 9,
43 kEnable_DI = 10,
44 kEnable_AnalogTriggers = 11,
45 kEnable_Counters_Low = 12,
46 kEnable_Counters_High = 13,
47 kEnable_CounterTimers_Low = 14,
48 kEnable_CounterTimers_High = 15,
Austin Schuh91c75562015-12-20 22:23:10 -080049 kEnable_Encoders_Low = 16,
50 kEnable_Encoders_High = 17,
51 kEnable_EncoderTimers_Low = 18,
52 kEnable_EncoderTimers_High = 19,
Brian Silverman2aa83d72015-01-24 18:03:11 -050053};
54
55DMA::DMA() {
56 tRioStatusCode status = 0;
57 tdma_config_ = tDMA::create(&status);
Austin Schuh58d8cdf2015-02-15 21:04:42 -080058 tdma_config_->writeConfig_ExternalClock(false, &status);
Brian Silvermane48dbc12017-02-04 20:06:29 -080059 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -050060 if (status != 0) {
61 return;
62 }
63 SetRate(1);
64 SetPause(false);
65}
66
67DMA::~DMA() {
68 tRioStatusCode status = 0;
69
70 manager_->stop(&status);
71 delete tdma_config_;
72}
73
74void DMA::SetPause(bool pause) {
75 tRioStatusCode status = 0;
76 tdma_config_->writeConfig_Pause(pause, &status);
Brian Silvermane48dbc12017-02-04 20:06:29 -080077 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -050078}
79
80void DMA::SetRate(uint32_t cycles) {
81 if (cycles < 1) {
82 cycles = 1;
83 }
84 tRioStatusCode status = 0;
85 tdma_config_->writeRate(cycles, &status);
Brian Silvermane48dbc12017-02-04 20:06:29 -080086 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -050087}
88
Parker Schuhd3b7a8872018-02-19 16:42:27 -080089void DMA::Add(frc::Encoder *encoder) {
Brian Silverman2aa83d72015-01-24 18:03:11 -050090 tRioStatusCode status = 0;
91
92 if (manager_) {
93 wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
Parker Schuhd3b7a8872018-02-19 16:42:27 -080094 "DMA::Add() only works before DMA::Start()");
Brian Silverman2aa83d72015-01-24 18:03:11 -050095 return;
96 }
Austin Schuh91c75562015-12-20 22:23:10 -080097 const int index = encoder->GetFPGAIndex();
98
Austin Schuh91c75562015-12-20 22:23:10 -080099 if (index < 4) {
100 // TODO(austin): Encoder uses a Counter for 1x or 2x; quad for 4x...
101 tdma_config_->writeConfig_Enable_Encoders_Low(true, &status);
102 } else if (index < 8) {
103 // TODO(austin): Encoder uses a Counter for 1x or 2x; quad for 4x...
104 tdma_config_->writeConfig_Enable_Encoders_High(true, &status);
105 } else {
106 wpi_setErrorWithContext(
107 NiFpga_Status_InvalidParameter,
108 "FPGA encoder index is not in the 4 that get logged.");
109 return;
110 }
Brian Silverman2aa83d72015-01-24 18:03:11 -0500111
Brian Silvermane48dbc12017-02-04 20:06:29 -0800112 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500113}
114
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800115void DMA::Add(frc::DigitalSource * /*input*/) {
Brian Silverman2aa83d72015-01-24 18:03:11 -0500116 tRioStatusCode status = 0;
117
118 if (manager_) {
119 wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800120 "DMA::Add() only works before DMA::Start()");
Brian Silverman2aa83d72015-01-24 18:03:11 -0500121 return;
122 }
123
124 tdma_config_->writeConfig_Enable_DI(true, &status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800125 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500126}
127
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800128void DMA::Add(frc::AnalogInput *input) {
Brian Silvermand49fd782015-01-30 16:43:17 -0500129 tRioStatusCode status = 0;
130
131 if (manager_) {
132 wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800133 "DMA::Add() only works before DMA::Start()");
Brian Silvermand49fd782015-01-30 16:43:17 -0500134 return;
135 }
136
Brian Silvermand49fd782015-01-30 16:43:17 -0500137 if (input->GetChannel() <= 3) {
138 tdma_config_->writeConfig_Enable_AI0_Low(true, &status);
139 } else {
140 tdma_config_->writeConfig_Enable_AI0_High(true, &status);
141 }
Brian Silvermane48dbc12017-02-04 20:06:29 -0800142 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silvermand49fd782015-01-30 16:43:17 -0500143}
144
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800145void DMA::SetExternalTrigger(frc::DigitalSource *input, bool rising,
146 bool falling) {
Austin Schuh6c053ef2021-09-26 14:32:16 -0700147 CHECK(input);
Brian Silverman2aa83d72015-01-24 18:03:11 -0500148 tRioStatusCode status = 0;
149
150 if (manager_) {
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800151 wpi_setErrorWithContext(
152 NiFpga_Status_InvalidParameter,
Brian Silverman2aa83d72015-01-24 18:03:11 -0500153 "DMA::SetExternalTrigger() only works before DMA::Start()");
154 return;
155 }
156
157 auto index =
158 ::std::find(trigger_channels_.begin(), trigger_channels_.end(), false);
159 if (index == trigger_channels_.end()) {
160 wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800161 "DMA: No channels left");
Brian Silverman2aa83d72015-01-24 18:03:11 -0500162 return;
163 }
164 *index = true;
165
Austin Schuhb19fddb2015-11-22 22:25:29 -0800166 const int channel_index = ::std::distance(trigger_channels_.begin(), index);
Brian Silverman2aa83d72015-01-24 18:03:11 -0500167
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800168 const bool is_external_clock =
169 tdma_config_->readConfig_ExternalClock(&status);
170 if (status == 0) {
171 if (!is_external_clock) {
172 tdma_config_->writeConfig_ExternalClock(true, &status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800173 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800174 if (status != 0) {
175 return;
176 }
177 }
178 } else {
Brian Silvermane48dbc12017-02-04 20:06:29 -0800179 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500180 return;
181 }
182
Austin Schuh3b5b69b2015-10-31 18:55:47 -0700183 nFPGA::nRoboRIO_FPGANamespace::tDMA::tExternalTriggers new_trigger;
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800184
185 new_trigger.FallingEdge = falling;
186 new_trigger.RisingEdge = rising;
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800187 new_trigger.ExternalClockSource_AnalogTrigger = false;
Austin Schuh91c75562015-12-20 22:23:10 -0800188 unsigned char module = 0;
Austin Schuh94f51e92017-10-30 19:25:32 -0700189 uint32_t channel = input->GetChannel();
Austin Schuh91c75562015-12-20 22:23:10 -0800190 if (channel >= kNumHeaders) {
191 module = 1;
192 channel -= kNumHeaders;
193 } else {
194 module = 0;
195 }
196
197 new_trigger.ExternalClockSource_Module = module;
198 new_trigger.ExternalClockSource_Channel = channel;
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800199
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800200 // Configures the trigger to be external, not off the FPGA clock.
Austin Schuh91c75562015-12-20 22:23:10 -0800201 tdma_config_->writeExternalTriggers(channel_index / 4, channel_index % 4,
202 new_trigger, &status);
203 if (status != 0) {
Brian Silvermane48dbc12017-02-04 20:06:29 -0800204 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Austin Schuh91c75562015-12-20 22:23:10 -0800205 return;
206 }
Brian Silverman2aa83d72015-01-24 18:03:11 -0500207}
208
209DMA::ReadStatus DMA::Read(DMASample *sample, uint32_t timeout_ms,
Brian Silvermand49fd782015-01-30 16:43:17 -0500210 size_t *remaining_out) {
Brian Silverman2aa83d72015-01-24 18:03:11 -0500211 tRioStatusCode status = 0;
212 size_t remainingBytes = 0;
Brian Silvermand49fd782015-01-30 16:43:17 -0500213 *remaining_out = 0;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500214
215 if (!manager_.get()) {
216 wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800217 "DMA::Read() only works after DMA::Start()");
Brian Silverman2aa83d72015-01-24 18:03:11 -0500218 return STATUS_ERROR;
219 }
220
Brian Silvermand49fd782015-01-30 16:43:17 -0500221 sample->dma_ = this;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500222 manager_->read(sample->read_buffer_, capture_size_, timeout_ms,
223 &remainingBytes, &status);
Austin Schuh94f51e92017-10-30 19:25:32 -0700224 sample->CalculateTimestamp();
Brian Silverman2aa83d72015-01-24 18:03:11 -0500225
Brian Silverman2aa83d72015-01-24 18:03:11 -0500226 // TODO(jerry): Do this only if status == 0?
Brian Silvermand49fd782015-01-30 16:43:17 -0500227 *remaining_out = remainingBytes / capture_size_;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500228
Brian Silverman2aa83d72015-01-24 18:03:11 -0500229 // TODO(austin): Check that *remainingBytes % capture_size_ == 0 and deal
230 // with it if it isn't. Probably meant that we overflowed?
231 if (status == 0) {
232 return STATUS_OK;
233 } else if (status == NiFpga_Status_FifoTimeout) {
234 return STATUS_TIMEOUT;
235 } else {
Brian Silvermane48dbc12017-02-04 20:06:29 -0800236 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500237 return STATUS_ERROR;
238 }
239}
240
Brian Silvermand49fd782015-01-30 16:43:17 -0500241const char *DMA::NameOfReadStatus(ReadStatus s) {
242 switch (s) {
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800243 case STATUS_OK:
244 return "OK";
245 case STATUS_TIMEOUT:
246 return "TIMEOUT";
247 case STATUS_ERROR:
248 return "ERROR";
249 default:
250 return "(bad ReadStatus code)";
Brian Silvermand49fd782015-01-30 16:43:17 -0500251 }
252}
253
Brian Silverman2aa83d72015-01-24 18:03:11 -0500254void DMA::Start(size_t queue_depth) {
255 tRioStatusCode status = 0;
256 tconfig_ = tdma_config_->readConfig(&status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800257 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500258 if (status != 0) {
259 return;
260 }
261
262 {
263 size_t accum_size = 0;
264#define SET_SIZE(bit) \
265 if (tconfig_.bit) { \
266 channel_offsets_[k##bit] = accum_size; \
267 accum_size += kChannelSize[k##bit]; \
268 } else { \
269 channel_offsets_[k##bit] = -1; \
270 }
271
272 SET_SIZE(Enable_AI0_Low);
273 SET_SIZE(Enable_AI0_High);
274 SET_SIZE(Enable_AIAveraged0_Low);
275 SET_SIZE(Enable_AIAveraged0_High);
276 SET_SIZE(Enable_AI1_Low);
277 SET_SIZE(Enable_AI1_High);
278 SET_SIZE(Enable_AIAveraged1_Low);
279 SET_SIZE(Enable_AIAveraged1_High);
280 SET_SIZE(Enable_Accumulator0);
281 SET_SIZE(Enable_Accumulator1);
282 SET_SIZE(Enable_DI);
283 SET_SIZE(Enable_AnalogTriggers);
284 SET_SIZE(Enable_Counters_Low);
285 SET_SIZE(Enable_Counters_High);
286 SET_SIZE(Enable_CounterTimers_Low);
287 SET_SIZE(Enable_CounterTimers_High);
Austin Schuh91c75562015-12-20 22:23:10 -0800288 SET_SIZE(Enable_Encoders_Low);
289 SET_SIZE(Enable_Encoders_High);
290 SET_SIZE(Enable_EncoderTimers_Low);
291 SET_SIZE(Enable_EncoderTimers_High);
Brian Silverman2aa83d72015-01-24 18:03:11 -0500292#undef SET_SIZE
293 capture_size_ = accum_size + 1;
294 }
295
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800296 manager_.reset(
Austin Schuh94f51e92017-10-30 19:25:32 -0700297 new nFPGA::tDMAManager(1, queue_depth * capture_size_, &status));
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800298
Brian Silvermane48dbc12017-02-04 20:06:29 -0800299 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500300 if (status != 0) {
301 return;
302 }
303 // Start, stop, start to clear the buffer.
304 manager_->start(&status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800305 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500306 if (status != 0) {
307 return;
308 }
309 manager_->stop(&status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800310 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500311 if (status != 0) {
312 return;
313 }
314 manager_->start(&status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800315 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500316 if (status != 0) {
317 return;
318 }
319}
320
James Kuszmaul9776b392023-01-14 14:08:08 -0800321static_assert(::std::is_trivial<DMASample>::value &&
322 ::std::is_standard_layout<DMASample>::value,
323 "DMASample needs to be POD");
Brian Silvermand49fd782015-01-30 16:43:17 -0500324
Austin Schuh94f51e92017-10-30 19:25:32 -0700325ssize_t DMASample::offset(int index) const {
326 return dma_->channel_offsets_[index];
327}
Brian Silverman2aa83d72015-01-24 18:03:11 -0500328
Austin Schuh94f51e92017-10-30 19:25:32 -0700329void DMASample::CalculateTimestamp() {
330 uint32_t lower_sample = read_buffer_[dma_->capture_size_ - 1];
331#if WPILIB2018
332 int32_t status = 0;
333 fpga_timestamp_ = HAL_ExpandFPGATime(lower_sample, &status);
334 assert(status == 0);
335#else
336 fpga_timestamp_ = lower_sample;
337#endif
338}
339
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700340uint64_t DMASample::GetTime() const { return fpga_timestamp_; }
Austin Schuh8f314a92015-11-22 21:35:40 -0800341
Brian Silverman2aa83d72015-01-24 18:03:11 -0500342double DMASample::GetTimestamp() const {
Austin Schuh8f314a92015-11-22 21:35:40 -0800343 return static_cast<double>(GetTime()) * 0.000001;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500344}
345
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800346bool DMASample::Get(frc::DigitalSource *input) const {
Brian Silverman2aa83d72015-01-24 18:03:11 -0500347 if (offset(kEnable_DI) == -1) {
Austin Schuh91c75562015-12-20 22:23:10 -0800348 wpi_setStaticErrorWithContext(
349 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800350 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500351 return false;
352 }
Austin Schuh94f51e92017-10-30 19:25:32 -0700353 const uint32_t channel = input->GetChannel();
Brian Silvermane48dbc12017-02-04 20:06:29 -0800354 if (channel < kNumHeaders) {
355 return (read_buffer_[offset(kEnable_DI)] >> channel) & 0x1;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500356 } else {
Brian Silvermane48dbc12017-02-04 20:06:29 -0800357 return (read_buffer_[offset(kEnable_DI)] >> (channel + 6)) & 0x1;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500358 }
359}
360
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800361int32_t DMASample::GetRaw(frc::Encoder *input) const {
Austin Schuh91c75562015-12-20 22:23:10 -0800362 int index = input->GetFPGAIndex();
363 uint32_t dmaWord = 0;
Austin Schuh91c75562015-12-20 22:23:10 -0800364 if (index < 4) {
365 if (offset(kEnable_Encoders_Low) == -1) {
366 wpi_setStaticErrorWithContext(
367 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800368 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh91c75562015-12-20 22:23:10 -0800369 return -1;
370 }
371 dmaWord = read_buffer_[offset(kEnable_Encoders_Low) + index];
372 } else if (index < 8) {
373 if (offset(kEnable_Encoders_High) == -1) {
374 wpi_setStaticErrorWithContext(
375 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800376 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh91c75562015-12-20 22:23:10 -0800377 return -1;
378 }
379 dmaWord = read_buffer_[offset(kEnable_Encoders_High) + (index - 4)];
380 } else {
381 wpi_setStaticErrorWithContext(
382 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800383 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh91c75562015-12-20 22:23:10 -0800384 return 0;
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800385 }
386
Brian Silverman2aa83d72015-01-24 18:03:11 -0500387 int32_t result = 0;
388
Austin Schuhb19fddb2015-11-22 22:25:29 -0800389 // Extract the 31-bit signed tEncoder::tOutput Value using a struct with the
390 // reverse packed field order of tOutput. This gets Value from the high
391 // order 31 bits of output on little-endian ARM using gcc. This works
392 // even though C/C++ doesn't guarantee bitfield order.
393 t1Output output;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500394
Austin Schuhb19fddb2015-11-22 22:25:29 -0800395 output.value = dmaWord;
396 result = output.Value;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500397
398 return result;
399}
400
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800401int32_t DMASample::Get(frc::Encoder *input) const {
Brian Silverman2aa83d72015-01-24 18:03:11 -0500402 int32_t raw = GetRaw(input);
403
Brian Silvermand49fd782015-01-30 16:43:17 -0500404 return raw / input->GetEncodingScale();
405}
406
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800407uint16_t DMASample::GetValue(frc::AnalogInput *input) const {
Austin Schuh91c75562015-12-20 22:23:10 -0800408 uint32_t channel = input->GetChannel();
409 uint32_t dmaWord;
410 if (channel < 4) {
411 if (offset(kEnable_AI0_Low) == -1) {
412 wpi_setStaticErrorWithContext(
413 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800414 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh91c75562015-12-20 22:23:10 -0800415 return 0xffff;
416 }
417 dmaWord = read_buffer_[offset(kEnable_AI0_Low) + channel / 2];
418 } else if (channel < 8) {
419 if (offset(kEnable_AI0_High) == -1) {
420 wpi_setStaticErrorWithContext(
421 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800422 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh91c75562015-12-20 22:23:10 -0800423 return 0xffff;
424 }
425 dmaWord = read_buffer_[offset(kEnable_AI0_High) + (channel - 4) / 2];
426 } else {
427 wpi_setStaticErrorWithContext(
428 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800429 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800430 return 0xffff;
Brian Silvermand49fd782015-01-30 16:43:17 -0500431 }
Austin Schuhc6cc4102015-02-15 23:19:53 -0800432 if (channel % 2) {
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800433 return (dmaWord >> 16) & 0xffff;
434 } else {
Austin Schuh91c75562015-12-20 22:23:10 -0800435 return dmaWord & 0xffff;
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800436 }
Brian Silvermand49fd782015-01-30 16:43:17 -0500437 return static_cast<int16_t>(dmaWord);
438}
439
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800440float DMASample::GetVoltage(frc::AnalogInput *input) const {
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800441 uint16_t value = GetValue(input);
442 if (value == 0xffff) return 0.0;
Brian Silvermand49fd782015-01-30 16:43:17 -0500443 uint32_t lsb_weight = input->GetLSBWeight();
444 int32_t offset = input->GetOffset();
445 float voltage = lsb_weight * 1.0e-9 * value - offset * 1.0e-9;
446 return voltage;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500447}