blob: c2844d8017ebdb409474e36d3f9e6898ed7a6f36 [file] [log] [blame]
brians343bc112013-02-10 01:53:46 +00001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2008. 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 $(WIND_BASE)/WPILib. */
5/*----------------------------------------------------------------------------*/
6
7#include "DigitalModule.h"
8#include "I2C.h"
9#include "PWM.h"
10#include "Resource.h"
11#include "Synchronized.h"
12#include "WPIErrors.h"
13#include <math.h>
14#include <taskLib.h>
15
16static Resource *DIOChannels = NULL;
17static Resource *DO_PWMGenerators[tDIO::kNumSystems] = {NULL};
18
19/**
20 * Get an instance of an Digital Module.
21 * Singleton digital module creation where a module is allocated on the first use
22 * and the same module is returned on subsequent uses.
23 *
24 * @param moduleNumber The digital module to get (1 or 2).
25 */
Brian Silverman68749472014-01-05 14:50:00 -080026DigitalModule* DigitalModule::GetInstance(uint8_t moduleNumber)
brians343bc112013-02-10 01:53:46 +000027{
28 if (CheckDigitalModule(moduleNumber))
29 {
30 return (DigitalModule *)GetModule(nLoadOut::kModuleType_Digital, moduleNumber);
31 }
32
33 // If this wasn't caught before now, make sure we say what's wrong before we crash
34 char buf[64];
35 snprintf(buf, 64, "Digital Module %d", moduleNumber);
36 wpi_setGlobalWPIErrorWithContext(ModuleIndexOutOfRange, buf);
37
38 return NULL;
39}
40
41/**
42 * Create a new instance of an digital module.
43 * Create an instance of the digital module object. Initialize all the parameters
44 * to reasonable values on start.
45 * Setting a global value on an digital module can be done only once unless subsequent
46 * values are set the previously set value.
47 * Digital modules are a singleton, so the constructor is never called outside of this class.
48 *
49 * @param moduleNumber The digital module to create (1 or 2).
50 */
Brian Silverman68749472014-01-05 14:50:00 -080051DigitalModule::DigitalModule(uint8_t moduleNumber)
brians343bc112013-02-10 01:53:46 +000052 : Module(nLoadOut::kModuleType_Digital, moduleNumber)
53 , m_fpgaDIO (NULL)
54{
55 Resource::CreateResourceObject(&DIOChannels, tDIO::kNumSystems * kDigitalChannels);
56 Resource::CreateResourceObject(&DO_PWMGenerators[m_moduleNumber - 1], tDIO::kNumDO_PWMDutyCycleElements);
57 tRioStatusCode localStatus = NiFpga_Status_Success;
58 m_fpgaDIO = tDIO::create(m_moduleNumber - 1, &localStatus);
59 wpi_setError(localStatus);
60
61 // Make sure that the 9403 IONode has had a chance to initialize before continuing.
62 while(m_fpgaDIO->readLoopTiming(&localStatus) == 0) taskDelay(1);
Brian Silverman68749472014-01-05 14:50:00 -080063
brians343bc112013-02-10 01:53:46 +000064 if (m_fpgaDIO->readLoopTiming(&localStatus) != kExpectedLoopTiming)
65 {
66 char err[128];
Brian Silverman68749472014-01-05 14:50:00 -080067 sprintf(err, "DIO LoopTiming: %d, expecting: %lu\n", m_fpgaDIO->readLoopTiming(&localStatus), kExpectedLoopTiming);
brians343bc112013-02-10 01:53:46 +000068 wpi_setWPIErrorWithContext(LoopTimingError, err);
69 }
Brian Silverman68749472014-01-05 14:50:00 -080070
71 //Calculate the length, in ms, of one DIO loop
72 double loopTime = m_fpgaDIO->readLoopTiming(&localStatus)/(kSystemClockTicksPerMicrosecond*1e3);
73
74 m_fpgaDIO->writePWMConfig_Period((uint16_t) (PWM::kDefaultPwmPeriod/loopTime + .5), &localStatus);
75 m_fpgaDIO->writePWMConfig_MinHigh((uint16_t) ((PWM::kDefaultPwmCenter-PWM::kDefaultPwmStepsDown*loopTime)/loopTime + .5), &localStatus);
brians343bc112013-02-10 01:53:46 +000076
77 // Ensure that PWM output values are set to OFF
Brian Silverman68749472014-01-05 14:50:00 -080078 for (uint32_t pwm_index = 1; pwm_index <= kPwmChannels; pwm_index++)
brians343bc112013-02-10 01:53:46 +000079 {
80 SetPWM(pwm_index, PWM::kPwmDisabled);
81 SetPWMPeriodScale(pwm_index, 3); // Set all to 4x by default.
82 }
83
84 // Turn off all relay outputs.
85 m_fpgaDIO->writeSlowValue_RelayFwd(0, &localStatus);
86 m_fpgaDIO->writeSlowValue_RelayRev(0, &localStatus);
87 wpi_setError(localStatus);
88
89 // Create a semaphore to protect changes to the digital output values
90 m_digitalSemaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
91
92 // Create a semaphore to protect changes to the relay values
93 m_relaySemaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
94
95 // Create a semaphore to protect changes to the DO PWM config
96 m_doPwmSemaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
97
98 AddToSingletonList();
99}
100
101DigitalModule::~DigitalModule()
102{
103 semDelete(m_doPwmSemaphore);
104 m_doPwmSemaphore = NULL;
105 semDelete(m_relaySemaphore);
106 m_relaySemaphore = NULL;
107 semDelete(m_digitalSemaphore);
108 m_digitalSemaphore = NULL;
109 delete m_fpgaDIO;
110}
111
112/**
113 * Set a PWM channel to the desired value. The values range from 0 to 255 and the period is controlled
114 * by the PWM Period and MinHigh registers.
115 *
116 * @param channel The PWM channel to set.
117 * @param value The PWM value to set.
118 */
Brian Silverman68749472014-01-05 14:50:00 -0800119void DigitalModule::SetPWM(uint32_t channel, uint8_t value)
brians343bc112013-02-10 01:53:46 +0000120{
121 CheckPWMChannel(channel);
122 tRioStatusCode localStatus = NiFpga_Status_Success;
123 m_fpgaDIO->writePWMValue(channel - 1, value, &localStatus);
124 wpi_setError(localStatus);
125}
126
127/**
128 * Get a value from a PWM channel. The values range from 0 to 255.
129 *
130 * @param channel The PWM channel to read from.
131 * @return The raw PWM value.
132 */
Brian Silverman68749472014-01-05 14:50:00 -0800133uint8_t DigitalModule::GetPWM(uint32_t channel)
brians343bc112013-02-10 01:53:46 +0000134{
135 CheckPWMChannel(channel);
136 tRioStatusCode localStatus = NiFpga_Status_Success;
137 return m_fpgaDIO->readPWMValue(channel - 1, &localStatus);
138 wpi_setError(localStatus);
139}
140
141/**
142 * Set how how often the PWM signal is squelched, thus scaling the period.
143 *
144 * @param channel The PWM channel to configure.
145 * @param squelchMask The 2-bit mask of outputs to squelch.
146 */
Brian Silverman68749472014-01-05 14:50:00 -0800147void DigitalModule::SetPWMPeriodScale(uint32_t channel, uint32_t squelchMask)
brians343bc112013-02-10 01:53:46 +0000148{
149 CheckPWMChannel(channel);
150 tRioStatusCode localStatus = NiFpga_Status_Success;
151 m_fpgaDIO->writePWMPeriodScale(channel - 1, squelchMask, &localStatus);
152 wpi_setError(localStatus);
153}
154
155/**
156 * Set the state of a relay.
157 * Set the state of a relay output to be forward. Relays have two outputs and each is
158 * independently set to 0v or 12v.
159 */
Brian Silverman68749472014-01-05 14:50:00 -0800160void DigitalModule::SetRelayForward(uint32_t channel, bool on)
brians343bc112013-02-10 01:53:46 +0000161{
162 tRioStatusCode localStatus = NiFpga_Status_Success;
163 CheckRelayChannel(channel);
164 {
165 Synchronized sync(m_relaySemaphore);
Brian Silverman68749472014-01-05 14:50:00 -0800166 uint8_t forwardRelays = m_fpgaDIO->readSlowValue_RelayFwd(&localStatus);
brians343bc112013-02-10 01:53:46 +0000167 if (on)
168 forwardRelays |= 1 << (channel - 1);
169 else
170 forwardRelays &= ~(1 << (channel - 1));
171 m_fpgaDIO->writeSlowValue_RelayFwd(forwardRelays, &localStatus);
172 }
173 wpi_setError(localStatus);
174}
175
176/**
177 * Set the state of a relay.
178 * Set the state of a relay output to be reverse. Relays have two outputs and each is
179 * independently set to 0v or 12v.
180 */
Brian Silverman68749472014-01-05 14:50:00 -0800181void DigitalModule::SetRelayReverse(uint32_t channel, bool on)
brians343bc112013-02-10 01:53:46 +0000182{
183 tRioStatusCode localStatus = NiFpga_Status_Success;
184 CheckRelayChannel(channel);
185 {
186 Synchronized sync(m_relaySemaphore);
Brian Silverman68749472014-01-05 14:50:00 -0800187 uint8_t reverseRelays = m_fpgaDIO->readSlowValue_RelayRev(&localStatus);
brians343bc112013-02-10 01:53:46 +0000188 if (on)
189 reverseRelays |= 1 << (channel - 1);
190 else
191 reverseRelays &= ~(1 << (channel - 1));
192 m_fpgaDIO->writeSlowValue_RelayRev(reverseRelays, &localStatus);
193 }
194 wpi_setError(localStatus);
195}
196
197/**
198 * Get the current state of the forward relay channel
199 */
Brian Silverman68749472014-01-05 14:50:00 -0800200bool DigitalModule::GetRelayForward(uint32_t channel)
brians343bc112013-02-10 01:53:46 +0000201{
202 tRioStatusCode localStatus = NiFpga_Status_Success;
Brian Silverman68749472014-01-05 14:50:00 -0800203 uint8_t forwardRelays = m_fpgaDIO->readSlowValue_RelayFwd(&localStatus);
brians343bc112013-02-10 01:53:46 +0000204 wpi_setError(localStatus);
205 return (forwardRelays & (1 << (channel - 1))) != 0;
206}
207
208/**
209 * Get the current state of all of the forward relay channels on this module.
210 */
Brian Silverman68749472014-01-05 14:50:00 -0800211uint8_t DigitalModule::GetRelayForward()
brians343bc112013-02-10 01:53:46 +0000212{
213 tRioStatusCode localStatus = NiFpga_Status_Success;
Brian Silverman68749472014-01-05 14:50:00 -0800214 uint8_t forwardRelays = m_fpgaDIO->readSlowValue_RelayFwd(&localStatus);
brians343bc112013-02-10 01:53:46 +0000215 wpi_setError(localStatus);
216 return forwardRelays;
217}
218
219/**
220 * Get the current state of the reverse relay channel
221 */
Brian Silverman68749472014-01-05 14:50:00 -0800222bool DigitalModule::GetRelayReverse(uint32_t channel)
brians343bc112013-02-10 01:53:46 +0000223{
224 tRioStatusCode localStatus = NiFpga_Status_Success;
Brian Silverman68749472014-01-05 14:50:00 -0800225 uint8_t reverseRelays = m_fpgaDIO->readSlowValue_RelayRev(&localStatus);
brians343bc112013-02-10 01:53:46 +0000226 wpi_setError(localStatus);
227 return (reverseRelays & (1 << (channel - 1))) != 0;
228
229}
230
231/**
232 * Get the current state of all of the reverse relay channels on this module.
233 */
Brian Silverman68749472014-01-05 14:50:00 -0800234uint8_t DigitalModule::GetRelayReverse()
brians343bc112013-02-10 01:53:46 +0000235{
236 tRioStatusCode localStatus = NiFpga_Status_Success;
Brian Silverman68749472014-01-05 14:50:00 -0800237 uint8_t reverseRelays = m_fpgaDIO->readSlowValue_RelayRev(&localStatus);
brians343bc112013-02-10 01:53:46 +0000238 wpi_setError(localStatus);
239 return reverseRelays;
240}
241
242
243/**
244 * Allocate Digital I/O channels.
245 * Allocate channels so that they are not accidently reused. Also the direction is set at the
246 * time of the allocation.
247 *
248 * @param channel The Digital I/O channel
249 * @param input If true open as input; if false open as output
250 * @return Was successfully allocated
251 */
Brian Silverman68749472014-01-05 14:50:00 -0800252bool DigitalModule::AllocateDIO(uint32_t channel, bool input)
brians343bc112013-02-10 01:53:46 +0000253{
254 char buf[64];
255 snprintf(buf, 64, "DIO %d (Module %d)", channel, m_moduleNumber);
256 if (DIOChannels->Allocate(kDigitalChannels * (m_moduleNumber - 1) + channel - 1, buf) == ~0ul) return false;
257 tRioStatusCode localStatus = NiFpga_Status_Success;
258 {
259 Synchronized sync(m_digitalSemaphore);
Brian Silverman68749472014-01-05 14:50:00 -0800260 uint32_t bitToSet = 1 << (RemapDigitalChannel(channel - 1));
261 uint32_t outputEnable = m_fpgaDIO->readOutputEnable(&localStatus);
262 uint32_t outputEnableValue;
brians343bc112013-02-10 01:53:46 +0000263 if (input)
264 {
265 outputEnableValue = outputEnable & (~bitToSet); // clear the bit for read
266 }
267 else
268 {
269 outputEnableValue = outputEnable | bitToSet; // set the bit for write
270 }
271 m_fpgaDIO->writeOutputEnable(outputEnableValue, &localStatus);
272 }
273 wpi_setError(localStatus);
274 return true;
275}
276
277/**
278 * Free the resource associated with a digital I/O channel.
279 *
280 * @param channel The Digital I/O channel to free
281 */
Brian Silverman68749472014-01-05 14:50:00 -0800282void DigitalModule::FreeDIO(uint32_t channel)
brians343bc112013-02-10 01:53:46 +0000283{
284 DIOChannels->Free(kDigitalChannels * (m_moduleNumber - 1) + channel - 1);
285}
286
287/**
288 * Write a digital I/O bit to the FPGA.
289 * Set a single value on a digital I/O channel.
290 *
291 * @param channel The Digital I/O channel
292 * @param value The state to set the digital channel (if it is configured as an output)
293 */
Brian Silverman68749472014-01-05 14:50:00 -0800294void DigitalModule::SetDIO(uint32_t channel, short value)
brians343bc112013-02-10 01:53:46 +0000295{
296 if (value != 0 && value != 1)
297 {
298 wpi_setWPIError(NonBinaryDigitalValue);
299 if (value != 0)
300 value = 1;
301 }
302 tRioStatusCode localStatus = NiFpga_Status_Success;
303 {
304 Synchronized sync(m_digitalSemaphore);
Brian Silverman68749472014-01-05 14:50:00 -0800305 uint16_t currentDIO = m_fpgaDIO->readDO(&localStatus);
brians343bc112013-02-10 01:53:46 +0000306 if(value == 0)
307 {
308 currentDIO = currentDIO & ~(1 << RemapDigitalChannel(channel - 1));
309 }
310 else if (value == 1)
311 {
312 currentDIO = currentDIO | (1 << RemapDigitalChannel(channel - 1));
313 }
314 m_fpgaDIO->writeDO(currentDIO, &localStatus);
315 }
316 wpi_setError(localStatus);
317}
318
319/**
320 * Read a digital I/O bit from the FPGA.
321 * Get a single value from a digital I/O channel.
322 *
323 * @param channel The digital I/O channel
324 * @return The state of the specified channel
325 */
Brian Silverman68749472014-01-05 14:50:00 -0800326bool DigitalModule::GetDIO(uint32_t channel)
brians343bc112013-02-10 01:53:46 +0000327{
328 tRioStatusCode localStatus = NiFpga_Status_Success;
Brian Silverman68749472014-01-05 14:50:00 -0800329 uint32_t currentDIO = m_fpgaDIO->readDI(&localStatus);
brians343bc112013-02-10 01:53:46 +0000330 wpi_setError(localStatus);
331
332 //Shift 00000001 over channel-1 places.
333 //AND it against the currentDIO
334 //if it == 0, then return false
335 //else return true
336 return ((currentDIO >> RemapDigitalChannel(channel - 1)) & 1) != 0;
337}
338
339/**
340 * Read the state of all the Digital I/O lines from the FPGA
341 * These are not remapped to logical order. They are still in hardware order.
342 */
Brian Silverman68749472014-01-05 14:50:00 -0800343uint16_t DigitalModule::GetDIO()
brians343bc112013-02-10 01:53:46 +0000344{
345 tRioStatusCode localStatus = NiFpga_Status_Success;
Brian Silverman68749472014-01-05 14:50:00 -0800346 uint32_t currentDIO = m_fpgaDIO->readDI(&localStatus);
brians343bc112013-02-10 01:53:46 +0000347 wpi_setError(localStatus);
348 return currentDIO;
349}
350
351/**
352 * Read the direction of a the Digital I/O lines
353 * A 1 bit means output and a 0 bit means input.
354 *
355 * @param channel The digital I/O channel
356 * @return The direction of the specified channel
357 */
Brian Silverman68749472014-01-05 14:50:00 -0800358bool DigitalModule::GetDIODirection(uint32_t channel)
brians343bc112013-02-10 01:53:46 +0000359{
360 tRioStatusCode localStatus = NiFpga_Status_Success;
Brian Silverman68749472014-01-05 14:50:00 -0800361 uint32_t currentOutputEnable = m_fpgaDIO->readOutputEnable(&localStatus);
brians343bc112013-02-10 01:53:46 +0000362 wpi_setError(localStatus);
363
364 //Shift 00000001 over channel-1 places.
365 //AND it against the currentOutputEnable
366 //if it == 0, then return false
367 //else return true
368 return ((currentOutputEnable >> RemapDigitalChannel(channel - 1)) & 1) != 0;
369}
370
371/**
372 * Read the direction of all the Digital I/O lines from the FPGA
373 * A 1 bit means output and a 0 bit means input.
374 * These are not remapped to logical order. They are still in hardware order.
375 */
Brian Silverman68749472014-01-05 14:50:00 -0800376uint16_t DigitalModule::GetDIODirection()
brians343bc112013-02-10 01:53:46 +0000377{
378 tRioStatusCode localStatus = NiFpga_Status_Success;
Brian Silverman68749472014-01-05 14:50:00 -0800379 uint32_t currentOutputEnable = m_fpgaDIO->readOutputEnable(&localStatus);
brians343bc112013-02-10 01:53:46 +0000380 wpi_setError(localStatus);
381 return currentOutputEnable;
382}
383
384/**
385 * Generate a single pulse.
386 * Write a pulse to the specified digital output channel. There can only be a single pulse going at any time.
387 *
388 * @param channel The Digital Output channel that the pulse should be output on
389 * @param pulseLength The active length of the pulse (in seconds)
390 */
Brian Silverman68749472014-01-05 14:50:00 -0800391void DigitalModule::Pulse(uint32_t channel, float pulseLength)
brians343bc112013-02-10 01:53:46 +0000392{
Brian Silverman68749472014-01-05 14:50:00 -0800393 uint16_t mask = 1 << RemapDigitalChannel(channel - 1);
brians343bc112013-02-10 01:53:46 +0000394 tRioStatusCode localStatus = NiFpga_Status_Success;
Brian Silverman68749472014-01-05 14:50:00 -0800395 m_fpgaDIO->writePulseLength((uint8_t)(1.0e9 * pulseLength / (m_fpgaDIO->readLoopTiming(&localStatus) * 25)), &localStatus);
brians343bc112013-02-10 01:53:46 +0000396 m_fpgaDIO->writePulse(mask, &localStatus);
397 wpi_setError(localStatus);
398}
399
400/**
401 * Check a DIO line to see if it is currently generating a pulse.
402 *
403 * @return A pulse is in progress
404 */
Brian Silverman68749472014-01-05 14:50:00 -0800405bool DigitalModule::IsPulsing(uint32_t channel)
brians343bc112013-02-10 01:53:46 +0000406{
Brian Silverman68749472014-01-05 14:50:00 -0800407 uint16_t mask = 1 << RemapDigitalChannel(channel - 1);
brians343bc112013-02-10 01:53:46 +0000408 tRioStatusCode localStatus = NiFpga_Status_Success;
Brian Silverman68749472014-01-05 14:50:00 -0800409 uint16_t pulseRegister = m_fpgaDIO->readPulse(&localStatus);
brians343bc112013-02-10 01:53:46 +0000410 wpi_setError(localStatus);
411 return (pulseRegister & mask) != 0;
412}
413
414/**
415 * Check if any DIO line is currently generating a pulse.
416 *
417 * @return A pulse on some line is in progress
418 */
419bool DigitalModule::IsPulsing()
420{
421 tRioStatusCode localStatus = NiFpga_Status_Success;
Brian Silverman68749472014-01-05 14:50:00 -0800422 uint16_t pulseRegister = m_fpgaDIO->readPulse(&localStatus);
brians343bc112013-02-10 01:53:46 +0000423 wpi_setError(localStatus);
424 return pulseRegister != 0;
425}
426
427/**
428 * Allocate a DO PWM Generator.
429 * Allocate PWM generators so that they are not accidently reused.
430 *
431 * @return PWM Generator refnum
432 */
Brian Silverman68749472014-01-05 14:50:00 -0800433uint32_t DigitalModule::AllocateDO_PWM()
brians343bc112013-02-10 01:53:46 +0000434{
435 char buf[64];
436 snprintf(buf, 64, "DO_PWM (Module: %d)", m_moduleNumber);
437 return DO_PWMGenerators[(m_moduleNumber - 1)]->Allocate(buf);
438}
439
440/**
441 * Free the resource associated with a DO PWM generator.
442 *
443 * @param pwmGenerator The pwmGen to free that was allocated with AllocateDO_PWM()
444 */
Brian Silverman68749472014-01-05 14:50:00 -0800445void DigitalModule::FreeDO_PWM(uint32_t pwmGenerator)
brians343bc112013-02-10 01:53:46 +0000446{
447 if (pwmGenerator == ~0ul) return;
448 DO_PWMGenerators[(m_moduleNumber - 1)]->Free(pwmGenerator);
449}
450
451/**
452 * Change the frequency of the DO PWM generator.
453 *
454 * The valid range is from 0.6 Hz to 19 kHz. The frequency resolution is logarithmic.
455 *
456 * @param rate The frequency to output all digital output PWM signals on this module.
457 */
458void DigitalModule::SetDO_PWMRate(float rate)
459{
460 // Currently rounding in the log rate domain... heavy weight toward picking a higher freq.
461 // TODO: Round in the linear rate domain.
462 tRioStatusCode localStatus = NiFpga_Status_Success;
Brian Silverman68749472014-01-05 14:50:00 -0800463 uint8_t pwmPeriodPower = (uint8_t)(log(1.0 / (m_fpgaDIO->readLoopTiming(&localStatus) * 0.25E-6 * rate))/log(2.0) + 0.5);
brians343bc112013-02-10 01:53:46 +0000464 m_fpgaDIO->writeDO_PWMConfig_PeriodPower(pwmPeriodPower, &localStatus);
465 wpi_setError(localStatus);
466}
467
468/**
469 * Configure which DO channel the PWM siganl is output on
470 *
471 * @param pwmGenerator The generator index reserved by AllocateDO_PWM()
472 * @param channel The Digital Output channel to output on
473 */
Brian Silverman68749472014-01-05 14:50:00 -0800474void DigitalModule::SetDO_PWMOutputChannel(uint32_t pwmGenerator, uint32_t channel)
brians343bc112013-02-10 01:53:46 +0000475{
476 if (pwmGenerator == ~0ul) return;
477 tRioStatusCode localStatus = NiFpga_Status_Success;
478 switch(pwmGenerator)
479 {
480 case 0:
481 m_fpgaDIO->writeDO_PWMConfig_OutputSelect_0(RemapDigitalChannel(channel - 1), &localStatus);
482 break;
483 case 1:
484 m_fpgaDIO->writeDO_PWMConfig_OutputSelect_1(RemapDigitalChannel(channel - 1), &localStatus);
485 break;
486 case 2:
487 m_fpgaDIO->writeDO_PWMConfig_OutputSelect_2(RemapDigitalChannel(channel - 1), &localStatus);
488 break;
489 case 3:
490 m_fpgaDIO->writeDO_PWMConfig_OutputSelect_3(RemapDigitalChannel(channel - 1), &localStatus);
491 break;
492 }
493 wpi_setError(localStatus);
494}
495
496/**
497 * Configure the duty-cycle of the PWM generator
498 *
499 * @param pwmGenerator The generator index reserved by AllocateDO_PWM()
500 * @param dutyCycle The percent duty cycle to output [0..1].
501 */
Brian Silverman68749472014-01-05 14:50:00 -0800502void DigitalModule::SetDO_PWMDutyCycle(uint32_t pwmGenerator, float dutyCycle)
brians343bc112013-02-10 01:53:46 +0000503{
504 if (pwmGenerator == ~0ul) return;
505 if (dutyCycle > 1.0) dutyCycle = 1.0;
506 if (dutyCycle < 0.0) dutyCycle = 0.0;
507 float rawDutyCycle = 256.0 * dutyCycle;
508 if (rawDutyCycle > 255.5) rawDutyCycle = 255.5;
509 tRioStatusCode localStatus = NiFpga_Status_Success;
510 {
511 Synchronized sync(m_doPwmSemaphore);
Brian Silverman68749472014-01-05 14:50:00 -0800512 uint8_t pwmPeriodPower = m_fpgaDIO->readDO_PWMConfig_PeriodPower(&localStatus);
brians343bc112013-02-10 01:53:46 +0000513 if (pwmPeriodPower < 4)
514 {
515 // The resolution of the duty cycle drops close to the highest frequencies.
516 rawDutyCycle = rawDutyCycle / pow(2.0, 4 - pwmPeriodPower);
517 }
Brian Silverman68749472014-01-05 14:50:00 -0800518 m_fpgaDIO->writeDO_PWMDutyCycle(pwmGenerator, (uint8_t)rawDutyCycle, &localStatus);
brians343bc112013-02-10 01:53:46 +0000519 }
520 wpi_setError(localStatus);
521}
522
523/**
Brian Silverman68749472014-01-05 14:50:00 -0800524 * Get the loop timing of the Digital Module
525 *
526 * @return The loop time
527 */
528uint16_t DigitalModule::GetLoopTiming()
529{
530 tRioStatusCode localStatus = NiFpga_Status_Success;
531 uint16_t timing = m_fpgaDIO->readLoopTiming(&localStatus);
532 wpi_setError(localStatus);
533
534 return timing;
535}
536
537/**
brians343bc112013-02-10 01:53:46 +0000538 * Return a pointer to an I2C object for this digital module
539 * The caller is responsible for deleting the pointer.
540 *
541 * @param address The address of the device on the I2C bus
542 * @return A pointer to an I2C object to talk to the device at address
543 */
Brian Silverman68749472014-01-05 14:50:00 -0800544I2C* DigitalModule::GetI2C(uint32_t address)
brians343bc112013-02-10 01:53:46 +0000545{
546 return new I2C(this, address);
547}
548
549