blob: 9e716d14294d6cbdb642a95cb6c7f4234be46edd [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
Austin Schuh99f7c6a2024-06-25 22:07:44 -07007#include "absl/log/check.h"
8#include "absl/log/log.h"
Philipp Schrader790cb542023-07-05 21:06:52 -07009
Parker Schuhd3b7a8872018-02-19 16:42:27 -080010#include "frc971/wpilib/ahal/AnalogInput.h"
11#include "frc971/wpilib/ahal/DigitalSource.h"
12#include "frc971/wpilib/ahal/Encoder.h"
Austin Schuhf6b94632019-02-02 22:11:27 -080013#include "hal/HAL.h"
Brian Silvermand49fd782015-01-30 16:43:17 -050014
15// Interface to the roboRIO FPGA's DMA features.
Brian Silverman2aa83d72015-01-24 18:03:11 -050016
17// Like tEncoder::tOutput with the bitfields reversed.
18typedef union {
19 struct {
Parker Schuhd3b7a8872018-02-19 16:42:27 -080020 unsigned Direction : 1;
21 signed Value : 31;
Brian Silverman2aa83d72015-01-24 18:03:11 -050022 };
23 struct {
Parker Schuhd3b7a8872018-02-19 16:42:27 -080024 unsigned value : 32;
Brian Silverman2aa83d72015-01-24 18:03:11 -050025 };
26} t1Output;
27
Brian Silvermane48dbc12017-02-04 20:06:29 -080028static const int32_t kNumHeaders = 10;
Brian Silverman2aa83d72015-01-24 18:03:11 -050029
Austin Schuh91c75562015-12-20 22:23:10 -080030static constexpr ssize_t kChannelSize[20] = {2, 2, 4, 4, 2, 2, 4, 4, 3, 3,
31 2, 1, 4, 4, 4, 4, 4, 4, 4, 4};
Brian Silvermane48dbc12017-02-04 20:06:29 -080032
Brian Silverman2aa83d72015-01-24 18:03:11 -050033enum DMAOffsetConstants {
34 kEnable_AI0_Low = 0,
35 kEnable_AI0_High = 1,
36 kEnable_AIAveraged0_Low = 2,
37 kEnable_AIAveraged0_High = 3,
38 kEnable_AI1_Low = 4,
39 kEnable_AI1_High = 5,
40 kEnable_AIAveraged1_Low = 6,
41 kEnable_AIAveraged1_High = 7,
42 kEnable_Accumulator0 = 8,
43 kEnable_Accumulator1 = 9,
44 kEnable_DI = 10,
45 kEnable_AnalogTriggers = 11,
46 kEnable_Counters_Low = 12,
47 kEnable_Counters_High = 13,
48 kEnable_CounterTimers_Low = 14,
49 kEnable_CounterTimers_High = 15,
Austin Schuh91c75562015-12-20 22:23:10 -080050 kEnable_Encoders_Low = 16,
51 kEnable_Encoders_High = 17,
52 kEnable_EncoderTimers_Low = 18,
53 kEnable_EncoderTimers_High = 19,
Brian Silverman2aa83d72015-01-24 18:03:11 -050054};
55
56DMA::DMA() {
57 tRioStatusCode status = 0;
58 tdma_config_ = tDMA::create(&status);
Austin Schuh58d8cdf2015-02-15 21:04:42 -080059 tdma_config_->writeConfig_ExternalClock(false, &status);
Brian Silvermane48dbc12017-02-04 20:06:29 -080060 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -050061 if (status != 0) {
62 return;
63 }
64 SetRate(1);
65 SetPause(false);
66}
67
68DMA::~DMA() {
69 tRioStatusCode status = 0;
70
71 manager_->stop(&status);
72 delete tdma_config_;
73}
74
75void DMA::SetPause(bool pause) {
76 tRioStatusCode status = 0;
77 tdma_config_->writeConfig_Pause(pause, &status);
Brian Silvermane48dbc12017-02-04 20:06:29 -080078 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -050079}
80
81void DMA::SetRate(uint32_t cycles) {
82 if (cycles < 1) {
83 cycles = 1;
84 }
85 tRioStatusCode status = 0;
86 tdma_config_->writeRate(cycles, &status);
Brian Silvermane48dbc12017-02-04 20:06:29 -080087 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -050088}
89
Parker Schuhd3b7a8872018-02-19 16:42:27 -080090void DMA::Add(frc::Encoder *encoder) {
Brian Silverman2aa83d72015-01-24 18:03:11 -050091 tRioStatusCode status = 0;
92
93 if (manager_) {
94 wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
Parker Schuhd3b7a8872018-02-19 16:42:27 -080095 "DMA::Add() only works before DMA::Start()");
Brian Silverman2aa83d72015-01-24 18:03:11 -050096 return;
97 }
Austin Schuh91c75562015-12-20 22:23:10 -080098 const int index = encoder->GetFPGAIndex();
99
Austin Schuh91c75562015-12-20 22:23:10 -0800100 if (index < 4) {
101 // TODO(austin): Encoder uses a Counter for 1x or 2x; quad for 4x...
102 tdma_config_->writeConfig_Enable_Encoders_Low(true, &status);
103 } else if (index < 8) {
104 // TODO(austin): Encoder uses a Counter for 1x or 2x; quad for 4x...
105 tdma_config_->writeConfig_Enable_Encoders_High(true, &status);
106 } else {
107 wpi_setErrorWithContext(
108 NiFpga_Status_InvalidParameter,
109 "FPGA encoder index is not in the 4 that get logged.");
110 return;
111 }
Brian Silverman2aa83d72015-01-24 18:03:11 -0500112
Brian Silvermane48dbc12017-02-04 20:06:29 -0800113 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500114}
115
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800116void DMA::Add(frc::DigitalSource * /*input*/) {
Brian Silverman2aa83d72015-01-24 18:03:11 -0500117 tRioStatusCode status = 0;
118
119 if (manager_) {
120 wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800121 "DMA::Add() only works before DMA::Start()");
Brian Silverman2aa83d72015-01-24 18:03:11 -0500122 return;
123 }
124
125 tdma_config_->writeConfig_Enable_DI(true, &status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800126 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500127}
128
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800129void DMA::Add(frc::AnalogInput *input) {
Brian Silvermand49fd782015-01-30 16:43:17 -0500130 tRioStatusCode status = 0;
131
132 if (manager_) {
133 wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800134 "DMA::Add() only works before DMA::Start()");
Brian Silvermand49fd782015-01-30 16:43:17 -0500135 return;
136 }
137
Brian Silvermand49fd782015-01-30 16:43:17 -0500138 if (input->GetChannel() <= 3) {
139 tdma_config_->writeConfig_Enable_AI0_Low(true, &status);
140 } else {
141 tdma_config_->writeConfig_Enable_AI0_High(true, &status);
142 }
Brian Silvermane48dbc12017-02-04 20:06:29 -0800143 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silvermand49fd782015-01-30 16:43:17 -0500144}
145
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800146void DMA::SetExternalTrigger(frc::DigitalSource *input, bool rising,
147 bool falling) {
Austin Schuh6c053ef2021-09-26 14:32:16 -0700148 CHECK(input);
Brian Silverman2aa83d72015-01-24 18:03:11 -0500149 tRioStatusCode status = 0;
150
151 if (manager_) {
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800152 wpi_setErrorWithContext(
153 NiFpga_Status_InvalidParameter,
Brian Silverman2aa83d72015-01-24 18:03:11 -0500154 "DMA::SetExternalTrigger() only works before DMA::Start()");
155 return;
156 }
157
158 auto index =
159 ::std::find(trigger_channels_.begin(), trigger_channels_.end(), false);
160 if (index == trigger_channels_.end()) {
161 wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800162 "DMA: No channels left");
Brian Silverman2aa83d72015-01-24 18:03:11 -0500163 return;
164 }
165 *index = true;
166
Austin Schuhb19fddb2015-11-22 22:25:29 -0800167 const int channel_index = ::std::distance(trigger_channels_.begin(), index);
Brian Silverman2aa83d72015-01-24 18:03:11 -0500168
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800169 const bool is_external_clock =
170 tdma_config_->readConfig_ExternalClock(&status);
171 if (status == 0) {
172 if (!is_external_clock) {
173 tdma_config_->writeConfig_ExternalClock(true, &status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800174 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800175 if (status != 0) {
176 return;
177 }
178 }
179 } else {
Brian Silvermane48dbc12017-02-04 20:06:29 -0800180 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500181 return;
182 }
183
Austin Schuh3b5b69b2015-10-31 18:55:47 -0700184 nFPGA::nRoboRIO_FPGANamespace::tDMA::tExternalTriggers new_trigger;
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800185
186 new_trigger.FallingEdge = falling;
187 new_trigger.RisingEdge = rising;
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800188 new_trigger.ExternalClockSource_AnalogTrigger = false;
Austin Schuh91c75562015-12-20 22:23:10 -0800189 unsigned char module = 0;
Austin Schuh94f51e92017-10-30 19:25:32 -0700190 uint32_t channel = input->GetChannel();
Austin Schuh91c75562015-12-20 22:23:10 -0800191 if (channel >= kNumHeaders) {
192 module = 1;
193 channel -= kNumHeaders;
194 } else {
195 module = 0;
196 }
197
198 new_trigger.ExternalClockSource_Module = module;
199 new_trigger.ExternalClockSource_Channel = channel;
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800200
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800201 // Configures the trigger to be external, not off the FPGA clock.
Austin Schuh91c75562015-12-20 22:23:10 -0800202 tdma_config_->writeExternalTriggers(channel_index / 4, channel_index % 4,
203 new_trigger, &status);
204 if (status != 0) {
Brian Silvermane48dbc12017-02-04 20:06:29 -0800205 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Austin Schuh91c75562015-12-20 22:23:10 -0800206 return;
207 }
Brian Silverman2aa83d72015-01-24 18:03:11 -0500208}
209
210DMA::ReadStatus DMA::Read(DMASample *sample, uint32_t timeout_ms,
Brian Silvermand49fd782015-01-30 16:43:17 -0500211 size_t *remaining_out) {
Brian Silverman2aa83d72015-01-24 18:03:11 -0500212 tRioStatusCode status = 0;
213 size_t remainingBytes = 0;
Brian Silvermand49fd782015-01-30 16:43:17 -0500214 *remaining_out = 0;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500215
216 if (!manager_.get()) {
217 wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800218 "DMA::Read() only works after DMA::Start()");
Brian Silverman2aa83d72015-01-24 18:03:11 -0500219 return STATUS_ERROR;
220 }
221
Brian Silvermand49fd782015-01-30 16:43:17 -0500222 sample->dma_ = this;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500223 manager_->read(sample->read_buffer_, capture_size_, timeout_ms,
224 &remainingBytes, &status);
Austin Schuh94f51e92017-10-30 19:25:32 -0700225 sample->CalculateTimestamp();
Brian Silverman2aa83d72015-01-24 18:03:11 -0500226
Brian Silverman2aa83d72015-01-24 18:03:11 -0500227 // TODO(jerry): Do this only if status == 0?
Brian Silvermand49fd782015-01-30 16:43:17 -0500228 *remaining_out = remainingBytes / capture_size_;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500229
Brian Silverman2aa83d72015-01-24 18:03:11 -0500230 // TODO(austin): Check that *remainingBytes % capture_size_ == 0 and deal
231 // with it if it isn't. Probably meant that we overflowed?
232 if (status == 0) {
233 return STATUS_OK;
234 } else if (status == NiFpga_Status_FifoTimeout) {
235 return STATUS_TIMEOUT;
236 } else {
Brian Silvermane48dbc12017-02-04 20:06:29 -0800237 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500238 return STATUS_ERROR;
239 }
240}
241
Brian Silvermand49fd782015-01-30 16:43:17 -0500242const char *DMA::NameOfReadStatus(ReadStatus s) {
243 switch (s) {
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800244 case STATUS_OK:
245 return "OK";
246 case STATUS_TIMEOUT:
247 return "TIMEOUT";
248 case STATUS_ERROR:
249 return "ERROR";
250 default:
251 return "(bad ReadStatus code)";
Brian Silvermand49fd782015-01-30 16:43:17 -0500252 }
253}
254
Brian Silverman2aa83d72015-01-24 18:03:11 -0500255void DMA::Start(size_t queue_depth) {
256 tRioStatusCode status = 0;
257 tconfig_ = tdma_config_->readConfig(&status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800258 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500259 if (status != 0) {
260 return;
261 }
262
263 {
264 size_t accum_size = 0;
265#define SET_SIZE(bit) \
266 if (tconfig_.bit) { \
267 channel_offsets_[k##bit] = accum_size; \
268 accum_size += kChannelSize[k##bit]; \
269 } else { \
270 channel_offsets_[k##bit] = -1; \
271 }
272
273 SET_SIZE(Enable_AI0_Low);
274 SET_SIZE(Enable_AI0_High);
275 SET_SIZE(Enable_AIAveraged0_Low);
276 SET_SIZE(Enable_AIAveraged0_High);
277 SET_SIZE(Enable_AI1_Low);
278 SET_SIZE(Enable_AI1_High);
279 SET_SIZE(Enable_AIAveraged1_Low);
280 SET_SIZE(Enable_AIAveraged1_High);
281 SET_SIZE(Enable_Accumulator0);
282 SET_SIZE(Enable_Accumulator1);
283 SET_SIZE(Enable_DI);
284 SET_SIZE(Enable_AnalogTriggers);
285 SET_SIZE(Enable_Counters_Low);
286 SET_SIZE(Enable_Counters_High);
287 SET_SIZE(Enable_CounterTimers_Low);
288 SET_SIZE(Enable_CounterTimers_High);
Austin Schuh91c75562015-12-20 22:23:10 -0800289 SET_SIZE(Enable_Encoders_Low);
290 SET_SIZE(Enable_Encoders_High);
291 SET_SIZE(Enable_EncoderTimers_Low);
292 SET_SIZE(Enable_EncoderTimers_High);
Brian Silverman2aa83d72015-01-24 18:03:11 -0500293#undef SET_SIZE
Maxwell Henderson9b268002024-01-21 12:46:41 -0800294 capture_size_ = accum_size + 2;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500295 }
296
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800297 manager_.reset(
Austin Schuh94f51e92017-10-30 19:25:32 -0700298 new nFPGA::tDMAManager(1, queue_depth * capture_size_, &status));
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800299
Brian Silvermane48dbc12017-02-04 20:06:29 -0800300 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500301 if (status != 0) {
302 return;
303 }
304 // Start, stop, start to clear the buffer.
305 manager_->start(&status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800306 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500307 if (status != 0) {
308 return;
309 }
310 manager_->stop(&status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800311 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500312 if (status != 0) {
313 return;
314 }
315 manager_->start(&status);
Brian Silvermane48dbc12017-02-04 20:06:29 -0800316 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500317 if (status != 0) {
318 return;
319 }
320}
321
James Kuszmaul9776b392023-01-14 14:08:08 -0800322static_assert(::std::is_trivial<DMASample>::value &&
323 ::std::is_standard_layout<DMASample>::value,
324 "DMASample needs to be POD");
Brian Silvermand49fd782015-01-30 16:43:17 -0500325
Austin Schuh94f51e92017-10-30 19:25:32 -0700326ssize_t DMASample::offset(int index) const {
327 return dma_->channel_offsets_[index];
328}
Brian Silverman2aa83d72015-01-24 18:03:11 -0500329
Austin Schuh94f51e92017-10-30 19:25:32 -0700330void DMASample::CalculateTimestamp() {
Maxwell Henderson9b268002024-01-21 12:46:41 -0800331 uint64_t upper_sample = read_buffer_[dma_->capture_size_ - 1];
332 uint64_t lower_sample = read_buffer_[dma_->capture_size_ - 2];
Austin Schuh94f51e92017-10-30 19:25:32 -0700333#if WPILIB2018
334 int32_t status = 0;
335 fpga_timestamp_ = HAL_ExpandFPGATime(lower_sample, &status);
336 assert(status == 0);
337#else
Maxwell Henderson9b268002024-01-21 12:46:41 -0800338 fpga_timestamp_ = (upper_sample << 32) + lower_sample;
Austin Schuh94f51e92017-10-30 19:25:32 -0700339#endif
340}
341
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700342uint64_t DMASample::GetTime() const { return fpga_timestamp_; }
Austin Schuh8f314a92015-11-22 21:35:40 -0800343
Brian Silverman2aa83d72015-01-24 18:03:11 -0500344double DMASample::GetTimestamp() const {
Austin Schuh8f314a92015-11-22 21:35:40 -0800345 return static_cast<double>(GetTime()) * 0.000001;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500346}
347
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800348bool DMASample::Get(frc::DigitalSource *input) const {
Brian Silverman2aa83d72015-01-24 18:03:11 -0500349 if (offset(kEnable_DI) == -1) {
Austin Schuh91c75562015-12-20 22:23:10 -0800350 wpi_setStaticErrorWithContext(
351 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800352 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Brian Silverman2aa83d72015-01-24 18:03:11 -0500353 return false;
354 }
Austin Schuh94f51e92017-10-30 19:25:32 -0700355 const uint32_t channel = input->GetChannel();
Brian Silvermane48dbc12017-02-04 20:06:29 -0800356 if (channel < kNumHeaders) {
357 return (read_buffer_[offset(kEnable_DI)] >> channel) & 0x1;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500358 } else {
Brian Silvermane48dbc12017-02-04 20:06:29 -0800359 return (read_buffer_[offset(kEnable_DI)] >> (channel + 6)) & 0x1;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500360 }
361}
362
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800363int32_t DMASample::GetRaw(frc::Encoder *input) const {
Austin Schuh91c75562015-12-20 22:23:10 -0800364 int index = input->GetFPGAIndex();
365 uint32_t dmaWord = 0;
Austin Schuh91c75562015-12-20 22:23:10 -0800366 if (index < 4) {
367 if (offset(kEnable_Encoders_Low) == -1) {
368 wpi_setStaticErrorWithContext(
369 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800370 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh91c75562015-12-20 22:23:10 -0800371 return -1;
372 }
373 dmaWord = read_buffer_[offset(kEnable_Encoders_Low) + index];
374 } else if (index < 8) {
375 if (offset(kEnable_Encoders_High) == -1) {
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 -1;
380 }
381 dmaWord = read_buffer_[offset(kEnable_Encoders_High) + (index - 4)];
382 } else {
383 wpi_setStaticErrorWithContext(
384 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800385 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh91c75562015-12-20 22:23:10 -0800386 return 0;
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800387 }
388
Brian Silverman2aa83d72015-01-24 18:03:11 -0500389 int32_t result = 0;
390
Austin Schuhb19fddb2015-11-22 22:25:29 -0800391 // Extract the 31-bit signed tEncoder::tOutput Value using a struct with the
392 // reverse packed field order of tOutput. This gets Value from the high
393 // order 31 bits of output on little-endian ARM using gcc. This works
394 // even though C/C++ doesn't guarantee bitfield order.
395 t1Output output;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500396
Austin Schuhb19fddb2015-11-22 22:25:29 -0800397 output.value = dmaWord;
398 result = output.Value;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500399
400 return result;
401}
402
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800403int32_t DMASample::Get(frc::Encoder *input) const {
Brian Silverman2aa83d72015-01-24 18:03:11 -0500404 int32_t raw = GetRaw(input);
405
Brian Silvermand49fd782015-01-30 16:43:17 -0500406 return raw / input->GetEncodingScale();
407}
408
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800409uint16_t DMASample::GetValue(frc::AnalogInput *input) const {
Austin Schuh91c75562015-12-20 22:23:10 -0800410 uint32_t channel = input->GetChannel();
411 uint32_t dmaWord;
412 if (channel < 4) {
413 if (offset(kEnable_AI0_Low) == -1) {
414 wpi_setStaticErrorWithContext(
415 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800416 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh91c75562015-12-20 22:23:10 -0800417 return 0xffff;
418 }
419 dmaWord = read_buffer_[offset(kEnable_AI0_Low) + channel / 2];
420 } else if (channel < 8) {
421 if (offset(kEnable_AI0_High) == -1) {
422 wpi_setStaticErrorWithContext(
423 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800424 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh91c75562015-12-20 22:23:10 -0800425 return 0xffff;
426 }
427 dmaWord = read_buffer_[offset(kEnable_AI0_High) + (channel - 4) / 2];
428 } else {
429 wpi_setStaticErrorWithContext(
430 dma_, NiFpga_Status_ResourceNotFound,
Brian Silvermane48dbc12017-02-04 20:06:29 -0800431 HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800432 return 0xffff;
Brian Silvermand49fd782015-01-30 16:43:17 -0500433 }
Austin Schuhc6cc4102015-02-15 23:19:53 -0800434 if (channel % 2) {
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800435 return (dmaWord >> 16) & 0xffff;
436 } else {
Austin Schuh91c75562015-12-20 22:23:10 -0800437 return dmaWord & 0xffff;
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800438 }
Brian Silvermand49fd782015-01-30 16:43:17 -0500439 return static_cast<int16_t>(dmaWord);
440}
441
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800442float DMASample::GetVoltage(frc::AnalogInput *input) const {
Austin Schuh58d8cdf2015-02-15 21:04:42 -0800443 uint16_t value = GetValue(input);
444 if (value == 0xffff) return 0.0;
Brian Silvermand49fd782015-01-30 16:43:17 -0500445 uint32_t lsb_weight = input->GetLSBWeight();
446 int32_t offset = input->GetOffset();
447 float voltage = lsb_weight * 1.0e-9 * value - offset * 1.0e-9;
448 return voltage;
Brian Silverman2aa83d72015-01-24 18:03:11 -0500449}