brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 1 | /*----------------------------------------------------------------------------*/ |
| 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 | |
| 16 | static Resource *DIOChannels = NULL; |
| 17 | static 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 | */ |
| 26 | DigitalModule* DigitalModule::GetInstance(UINT8 moduleNumber) |
| 27 | { |
| 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 | */ |
| 51 | DigitalModule::DigitalModule(UINT8 moduleNumber) |
| 52 | : 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); |
| 63 | if (m_fpgaDIO->readLoopTiming(&localStatus) != kExpectedLoopTiming) |
| 64 | { |
| 65 | char err[128]; |
| 66 | sprintf(err, "DIO LoopTiming: %d, expecting: %d\n", m_fpgaDIO->readLoopTiming(&localStatus), kExpectedLoopTiming); |
| 67 | wpi_setWPIErrorWithContext(LoopTimingError, err); |
| 68 | } |
| 69 | m_fpgaDIO->writePWMConfig_Period(PWM::kDefaultPwmPeriod, &localStatus); |
| 70 | m_fpgaDIO->writePWMConfig_MinHigh(PWM::kDefaultMinPwmHigh, &localStatus); |
| 71 | |
| 72 | // Ensure that PWM output values are set to OFF |
| 73 | for (UINT32 pwm_index = 1; pwm_index <= kPwmChannels; pwm_index++) |
| 74 | { |
| 75 | SetPWM(pwm_index, PWM::kPwmDisabled); |
| 76 | SetPWMPeriodScale(pwm_index, 3); // Set all to 4x by default. |
| 77 | } |
| 78 | |
| 79 | // Turn off all relay outputs. |
| 80 | m_fpgaDIO->writeSlowValue_RelayFwd(0, &localStatus); |
| 81 | m_fpgaDIO->writeSlowValue_RelayRev(0, &localStatus); |
| 82 | wpi_setError(localStatus); |
| 83 | |
| 84 | // Create a semaphore to protect changes to the digital output values |
| 85 | m_digitalSemaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE); |
| 86 | |
| 87 | // Create a semaphore to protect changes to the relay values |
| 88 | m_relaySemaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE); |
| 89 | |
| 90 | // Create a semaphore to protect changes to the DO PWM config |
| 91 | m_doPwmSemaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE); |
| 92 | |
| 93 | AddToSingletonList(); |
| 94 | } |
| 95 | |
| 96 | DigitalModule::~DigitalModule() |
| 97 | { |
| 98 | semDelete(m_doPwmSemaphore); |
| 99 | m_doPwmSemaphore = NULL; |
| 100 | semDelete(m_relaySemaphore); |
| 101 | m_relaySemaphore = NULL; |
| 102 | semDelete(m_digitalSemaphore); |
| 103 | m_digitalSemaphore = NULL; |
| 104 | delete m_fpgaDIO; |
| 105 | } |
| 106 | |
| 107 | /** |
| 108 | * Set a PWM channel to the desired value. The values range from 0 to 255 and the period is controlled |
| 109 | * by the PWM Period and MinHigh registers. |
| 110 | * |
| 111 | * @param channel The PWM channel to set. |
| 112 | * @param value The PWM value to set. |
| 113 | */ |
| 114 | void DigitalModule::SetPWM(UINT32 channel, UINT8 value) |
| 115 | { |
| 116 | CheckPWMChannel(channel); |
| 117 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 118 | m_fpgaDIO->writePWMValue(channel - 1, value, &localStatus); |
| 119 | wpi_setError(localStatus); |
| 120 | } |
| 121 | |
| 122 | /** |
| 123 | * Get a value from a PWM channel. The values range from 0 to 255. |
| 124 | * |
| 125 | * @param channel The PWM channel to read from. |
| 126 | * @return The raw PWM value. |
| 127 | */ |
| 128 | UINT8 DigitalModule::GetPWM(UINT32 channel) |
| 129 | { |
| 130 | CheckPWMChannel(channel); |
| 131 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 132 | return m_fpgaDIO->readPWMValue(channel - 1, &localStatus); |
| 133 | wpi_setError(localStatus); |
| 134 | } |
| 135 | |
| 136 | /** |
| 137 | * Set how how often the PWM signal is squelched, thus scaling the period. |
| 138 | * |
| 139 | * @param channel The PWM channel to configure. |
| 140 | * @param squelchMask The 2-bit mask of outputs to squelch. |
| 141 | */ |
| 142 | void DigitalModule::SetPWMPeriodScale(UINT32 channel, UINT32 squelchMask) |
| 143 | { |
| 144 | CheckPWMChannel(channel); |
| 145 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 146 | m_fpgaDIO->writePWMPeriodScale(channel - 1, squelchMask, &localStatus); |
| 147 | wpi_setError(localStatus); |
| 148 | } |
| 149 | |
| 150 | /** |
| 151 | * Set the state of a relay. |
| 152 | * Set the state of a relay output to be forward. Relays have two outputs and each is |
| 153 | * independently set to 0v or 12v. |
| 154 | */ |
| 155 | void DigitalModule::SetRelayForward(UINT32 channel, bool on) |
| 156 | { |
| 157 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 158 | CheckRelayChannel(channel); |
| 159 | { |
| 160 | Synchronized sync(m_relaySemaphore); |
| 161 | UINT8 forwardRelays = m_fpgaDIO->readSlowValue_RelayFwd(&localStatus); |
| 162 | if (on) |
| 163 | forwardRelays |= 1 << (channel - 1); |
| 164 | else |
| 165 | forwardRelays &= ~(1 << (channel - 1)); |
| 166 | m_fpgaDIO->writeSlowValue_RelayFwd(forwardRelays, &localStatus); |
| 167 | } |
| 168 | wpi_setError(localStatus); |
| 169 | } |
| 170 | |
| 171 | /** |
| 172 | * Set the state of a relay. |
| 173 | * Set the state of a relay output to be reverse. Relays have two outputs and each is |
| 174 | * independently set to 0v or 12v. |
| 175 | */ |
| 176 | void DigitalModule::SetRelayReverse(UINT32 channel, bool on) |
| 177 | { |
| 178 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 179 | CheckRelayChannel(channel); |
| 180 | { |
| 181 | Synchronized sync(m_relaySemaphore); |
| 182 | UINT8 reverseRelays = m_fpgaDIO->readSlowValue_RelayRev(&localStatus); |
| 183 | if (on) |
| 184 | reverseRelays |= 1 << (channel - 1); |
| 185 | else |
| 186 | reverseRelays &= ~(1 << (channel - 1)); |
| 187 | m_fpgaDIO->writeSlowValue_RelayRev(reverseRelays, &localStatus); |
| 188 | } |
| 189 | wpi_setError(localStatus); |
| 190 | } |
| 191 | |
| 192 | /** |
| 193 | * Get the current state of the forward relay channel |
| 194 | */ |
| 195 | bool DigitalModule::GetRelayForward(UINT32 channel) |
| 196 | { |
| 197 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 198 | UINT8 forwardRelays = m_fpgaDIO->readSlowValue_RelayFwd(&localStatus); |
| 199 | wpi_setError(localStatus); |
| 200 | return (forwardRelays & (1 << (channel - 1))) != 0; |
| 201 | } |
| 202 | |
| 203 | /** |
| 204 | * Get the current state of all of the forward relay channels on this module. |
| 205 | */ |
| 206 | UINT8 DigitalModule::GetRelayForward() |
| 207 | { |
| 208 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 209 | UINT8 forwardRelays = m_fpgaDIO->readSlowValue_RelayFwd(&localStatus); |
| 210 | wpi_setError(localStatus); |
| 211 | return forwardRelays; |
| 212 | } |
| 213 | |
| 214 | /** |
| 215 | * Get the current state of the reverse relay channel |
| 216 | */ |
| 217 | bool DigitalModule::GetRelayReverse(UINT32 channel) |
| 218 | { |
| 219 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 220 | UINT8 reverseRelays = m_fpgaDIO->readSlowValue_RelayRev(&localStatus); |
| 221 | wpi_setError(localStatus); |
| 222 | return (reverseRelays & (1 << (channel - 1))) != 0; |
| 223 | |
| 224 | } |
| 225 | |
| 226 | /** |
| 227 | * Get the current state of all of the reverse relay channels on this module. |
| 228 | */ |
| 229 | UINT8 DigitalModule::GetRelayReverse() |
| 230 | { |
| 231 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 232 | UINT8 reverseRelays = m_fpgaDIO->readSlowValue_RelayRev(&localStatus); |
| 233 | wpi_setError(localStatus); |
| 234 | return reverseRelays; |
| 235 | } |
| 236 | |
| 237 | |
| 238 | /** |
| 239 | * Allocate Digital I/O channels. |
| 240 | * Allocate channels so that they are not accidently reused. Also the direction is set at the |
| 241 | * time of the allocation. |
| 242 | * |
| 243 | * @param channel The Digital I/O channel |
| 244 | * @param input If true open as input; if false open as output |
| 245 | * @return Was successfully allocated |
| 246 | */ |
| 247 | bool DigitalModule::AllocateDIO(UINT32 channel, bool input) |
| 248 | { |
| 249 | char buf[64]; |
| 250 | snprintf(buf, 64, "DIO %d (Module %d)", channel, m_moduleNumber); |
Brian Silverman | 362a7ad | 2013-04-17 15:28:29 -0700 | [diff] [blame^] | 251 | if (DIOChannels->Allocate(kDigitalChannels * (m_moduleNumber - 1) + |
| 252 | channel - 1, buf, this) == ~0ul) return false; |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 253 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 254 | { |
| 255 | Synchronized sync(m_digitalSemaphore); |
| 256 | UINT32 bitToSet = 1 << (RemapDigitalChannel(channel - 1)); |
| 257 | UINT32 outputEnable = m_fpgaDIO->readOutputEnable(&localStatus); |
| 258 | UINT32 outputEnableValue; |
| 259 | if (input) |
| 260 | { |
| 261 | outputEnableValue = outputEnable & (~bitToSet); // clear the bit for read |
| 262 | } |
| 263 | else |
| 264 | { |
| 265 | outputEnableValue = outputEnable | bitToSet; // set the bit for write |
| 266 | } |
| 267 | m_fpgaDIO->writeOutputEnable(outputEnableValue, &localStatus); |
| 268 | } |
| 269 | wpi_setError(localStatus); |
| 270 | return true; |
| 271 | } |
| 272 | |
| 273 | /** |
| 274 | * Free the resource associated with a digital I/O channel. |
| 275 | * |
| 276 | * @param channel The Digital I/O channel to free |
| 277 | */ |
| 278 | void DigitalModule::FreeDIO(UINT32 channel) |
| 279 | { |
Brian Silverman | 362a7ad | 2013-04-17 15:28:29 -0700 | [diff] [blame^] | 280 | DIOChannels->Free(kDigitalChannels * (m_moduleNumber - 1) + channel - 1, |
| 281 | this); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 282 | } |
| 283 | |
| 284 | /** |
| 285 | * Write a digital I/O bit to the FPGA. |
| 286 | * Set a single value on a digital I/O channel. |
| 287 | * |
| 288 | * @param channel The Digital I/O channel |
| 289 | * @param value The state to set the digital channel (if it is configured as an output) |
| 290 | */ |
| 291 | void DigitalModule::SetDIO(UINT32 channel, short value) |
| 292 | { |
| 293 | if (value != 0 && value != 1) |
| 294 | { |
| 295 | wpi_setWPIError(NonBinaryDigitalValue); |
| 296 | if (value != 0) |
| 297 | value = 1; |
| 298 | } |
| 299 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 300 | { |
| 301 | Synchronized sync(m_digitalSemaphore); |
| 302 | UINT16 currentDIO = m_fpgaDIO->readDO(&localStatus); |
| 303 | if(value == 0) |
| 304 | { |
| 305 | currentDIO = currentDIO & ~(1 << RemapDigitalChannel(channel - 1)); |
| 306 | } |
| 307 | else if (value == 1) |
| 308 | { |
| 309 | currentDIO = currentDIO | (1 << RemapDigitalChannel(channel - 1)); |
| 310 | } |
| 311 | m_fpgaDIO->writeDO(currentDIO, &localStatus); |
| 312 | } |
| 313 | wpi_setError(localStatus); |
| 314 | } |
| 315 | |
| 316 | /** |
| 317 | * Read a digital I/O bit from the FPGA. |
| 318 | * Get a single value from a digital I/O channel. |
| 319 | * |
| 320 | * @param channel The digital I/O channel |
| 321 | * @return The state of the specified channel |
| 322 | */ |
| 323 | bool DigitalModule::GetDIO(UINT32 channel) |
| 324 | { |
| 325 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 326 | UINT32 currentDIO = m_fpgaDIO->readDI(&localStatus); |
| 327 | wpi_setError(localStatus); |
| 328 | |
| 329 | //Shift 00000001 over channel-1 places. |
| 330 | //AND it against the currentDIO |
| 331 | //if it == 0, then return false |
| 332 | //else return true |
| 333 | return ((currentDIO >> RemapDigitalChannel(channel - 1)) & 1) != 0; |
| 334 | } |
| 335 | |
| 336 | /** |
| 337 | * Read the state of all the Digital I/O lines from the FPGA |
| 338 | * These are not remapped to logical order. They are still in hardware order. |
| 339 | */ |
| 340 | UINT16 DigitalModule::GetDIO() |
| 341 | { |
| 342 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 343 | UINT32 currentDIO = m_fpgaDIO->readDI(&localStatus); |
| 344 | wpi_setError(localStatus); |
| 345 | return currentDIO; |
| 346 | } |
| 347 | |
| 348 | /** |
| 349 | * Read the direction of a the Digital I/O lines |
| 350 | * A 1 bit means output and a 0 bit means input. |
| 351 | * |
| 352 | * @param channel The digital I/O channel |
| 353 | * @return The direction of the specified channel |
| 354 | */ |
| 355 | bool DigitalModule::GetDIODirection(UINT32 channel) |
| 356 | { |
| 357 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 358 | UINT32 currentOutputEnable = m_fpgaDIO->readOutputEnable(&localStatus); |
| 359 | wpi_setError(localStatus); |
| 360 | |
| 361 | //Shift 00000001 over channel-1 places. |
| 362 | //AND it against the currentOutputEnable |
| 363 | //if it == 0, then return false |
| 364 | //else return true |
| 365 | return ((currentOutputEnable >> RemapDigitalChannel(channel - 1)) & 1) != 0; |
| 366 | } |
| 367 | |
| 368 | /** |
| 369 | * Read the direction of all the Digital I/O lines from the FPGA |
| 370 | * A 1 bit means output and a 0 bit means input. |
| 371 | * These are not remapped to logical order. They are still in hardware order. |
| 372 | */ |
| 373 | UINT16 DigitalModule::GetDIODirection() |
| 374 | { |
| 375 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 376 | UINT32 currentOutputEnable = m_fpgaDIO->readOutputEnable(&localStatus); |
| 377 | wpi_setError(localStatus); |
| 378 | return currentOutputEnable; |
| 379 | } |
| 380 | |
| 381 | /** |
| 382 | * Generate a single pulse. |
| 383 | * Write a pulse to the specified digital output channel. There can only be a single pulse going at any time. |
| 384 | * |
| 385 | * @param channel The Digital Output channel that the pulse should be output on |
| 386 | * @param pulseLength The active length of the pulse (in seconds) |
| 387 | */ |
| 388 | void DigitalModule::Pulse(UINT32 channel, float pulseLength) |
| 389 | { |
| 390 | UINT16 mask = 1 << RemapDigitalChannel(channel - 1); |
| 391 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 392 | m_fpgaDIO->writePulseLength((UINT8)(1.0e9 * pulseLength / (m_fpgaDIO->readLoopTiming(&localStatus) * 25)), &localStatus); |
| 393 | m_fpgaDIO->writePulse(mask, &localStatus); |
| 394 | wpi_setError(localStatus); |
| 395 | } |
| 396 | |
| 397 | /** |
| 398 | * Check a DIO line to see if it is currently generating a pulse. |
| 399 | * |
| 400 | * @return A pulse is in progress |
| 401 | */ |
| 402 | bool DigitalModule::IsPulsing(UINT32 channel) |
| 403 | { |
| 404 | UINT16 mask = 1 << RemapDigitalChannel(channel - 1); |
| 405 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 406 | UINT16 pulseRegister = m_fpgaDIO->readPulse(&localStatus); |
| 407 | wpi_setError(localStatus); |
| 408 | return (pulseRegister & mask) != 0; |
| 409 | } |
| 410 | |
| 411 | /** |
| 412 | * Check if any DIO line is currently generating a pulse. |
| 413 | * |
| 414 | * @return A pulse on some line is in progress |
| 415 | */ |
| 416 | bool DigitalModule::IsPulsing() |
| 417 | { |
| 418 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 419 | UINT16 pulseRegister = m_fpgaDIO->readPulse(&localStatus); |
| 420 | wpi_setError(localStatus); |
| 421 | return pulseRegister != 0; |
| 422 | } |
| 423 | |
| 424 | /** |
| 425 | * Allocate a DO PWM Generator. |
| 426 | * Allocate PWM generators so that they are not accidently reused. |
| 427 | * |
| 428 | * @return PWM Generator refnum |
| 429 | */ |
| 430 | UINT32 DigitalModule::AllocateDO_PWM() |
| 431 | { |
| 432 | char buf[64]; |
| 433 | snprintf(buf, 64, "DO_PWM (Module: %d)", m_moduleNumber); |
Brian Silverman | 362a7ad | 2013-04-17 15:28:29 -0700 | [diff] [blame^] | 434 | return DO_PWMGenerators[(m_moduleNumber - 1)]->Allocate(buf, this); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 435 | } |
| 436 | |
| 437 | /** |
| 438 | * Free the resource associated with a DO PWM generator. |
| 439 | * |
| 440 | * @param pwmGenerator The pwmGen to free that was allocated with AllocateDO_PWM() |
| 441 | */ |
| 442 | void DigitalModule::FreeDO_PWM(UINT32 pwmGenerator) |
| 443 | { |
| 444 | if (pwmGenerator == ~0ul) return; |
Brian Silverman | 362a7ad | 2013-04-17 15:28:29 -0700 | [diff] [blame^] | 445 | DO_PWMGenerators[(m_moduleNumber - 1)]->Free(pwmGenerator, this); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 446 | } |
| 447 | |
| 448 | /** |
| 449 | * Change the frequency of the DO PWM generator. |
| 450 | * |
| 451 | * The valid range is from 0.6 Hz to 19 kHz. The frequency resolution is logarithmic. |
| 452 | * |
| 453 | * @param rate The frequency to output all digital output PWM signals on this module. |
| 454 | */ |
| 455 | void DigitalModule::SetDO_PWMRate(float rate) |
| 456 | { |
| 457 | // Currently rounding in the log rate domain... heavy weight toward picking a higher freq. |
| 458 | // TODO: Round in the linear rate domain. |
| 459 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 460 | UINT8 pwmPeriodPower = (UINT8)(log(1.0 / (m_fpgaDIO->readLoopTiming(&localStatus) * 0.25E-6 * rate))/log(2.0) + 0.5); |
| 461 | m_fpgaDIO->writeDO_PWMConfig_PeriodPower(pwmPeriodPower, &localStatus); |
| 462 | wpi_setError(localStatus); |
| 463 | } |
| 464 | |
| 465 | /** |
| 466 | * Configure which DO channel the PWM siganl is output on |
| 467 | * |
| 468 | * @param pwmGenerator The generator index reserved by AllocateDO_PWM() |
| 469 | * @param channel The Digital Output channel to output on |
| 470 | */ |
| 471 | void DigitalModule::SetDO_PWMOutputChannel(UINT32 pwmGenerator, UINT32 channel) |
| 472 | { |
| 473 | if (pwmGenerator == ~0ul) return; |
| 474 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 475 | switch(pwmGenerator) |
| 476 | { |
| 477 | case 0: |
| 478 | m_fpgaDIO->writeDO_PWMConfig_OutputSelect_0(RemapDigitalChannel(channel - 1), &localStatus); |
| 479 | break; |
| 480 | case 1: |
| 481 | m_fpgaDIO->writeDO_PWMConfig_OutputSelect_1(RemapDigitalChannel(channel - 1), &localStatus); |
| 482 | break; |
| 483 | case 2: |
| 484 | m_fpgaDIO->writeDO_PWMConfig_OutputSelect_2(RemapDigitalChannel(channel - 1), &localStatus); |
| 485 | break; |
| 486 | case 3: |
| 487 | m_fpgaDIO->writeDO_PWMConfig_OutputSelect_3(RemapDigitalChannel(channel - 1), &localStatus); |
| 488 | break; |
| 489 | } |
| 490 | wpi_setError(localStatus); |
| 491 | } |
| 492 | |
| 493 | /** |
| 494 | * Configure the duty-cycle of the PWM generator |
| 495 | * |
| 496 | * @param pwmGenerator The generator index reserved by AllocateDO_PWM() |
| 497 | * @param dutyCycle The percent duty cycle to output [0..1]. |
| 498 | */ |
| 499 | void DigitalModule::SetDO_PWMDutyCycle(UINT32 pwmGenerator, float dutyCycle) |
| 500 | { |
| 501 | if (pwmGenerator == ~0ul) return; |
| 502 | if (dutyCycle > 1.0) dutyCycle = 1.0; |
| 503 | if (dutyCycle < 0.0) dutyCycle = 0.0; |
| 504 | float rawDutyCycle = 256.0 * dutyCycle; |
| 505 | if (rawDutyCycle > 255.5) rawDutyCycle = 255.5; |
| 506 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 507 | { |
| 508 | Synchronized sync(m_doPwmSemaphore); |
| 509 | UINT8 pwmPeriodPower = m_fpgaDIO->readDO_PWMConfig_PeriodPower(&localStatus); |
| 510 | if (pwmPeriodPower < 4) |
| 511 | { |
| 512 | // The resolution of the duty cycle drops close to the highest frequencies. |
| 513 | rawDutyCycle = rawDutyCycle / pow(2.0, 4 - pwmPeriodPower); |
| 514 | } |
| 515 | m_fpgaDIO->writeDO_PWMDutyCycle(pwmGenerator, (UINT8)rawDutyCycle, &localStatus); |
| 516 | } |
| 517 | wpi_setError(localStatus); |
| 518 | } |
| 519 | |
| 520 | /** |
| 521 | * Return a pointer to an I2C object for this digital module |
| 522 | * The caller is responsible for deleting the pointer. |
| 523 | * |
| 524 | * @param address The address of the device on the I2C bus |
| 525 | * @return A pointer to an I2C object to talk to the device at address |
| 526 | */ |
| 527 | I2C* DigitalModule::GetI2C(UINT32 address) |
| 528 | { |
| 529 | return new I2C(this, address); |
| 530 | } |
| 531 | |
| 532 | |