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 | */ |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 26 | DigitalModule* DigitalModule::GetInstance(uint8_t moduleNumber) |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 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 | */ |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 51 | DigitalModule::DigitalModule(uint8_t moduleNumber) |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 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); |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 63 | |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 64 | if (m_fpgaDIO->readLoopTiming(&localStatus) != kExpectedLoopTiming) |
| 65 | { |
| 66 | char err[128]; |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 67 | sprintf(err, "DIO LoopTiming: %d, expecting: %lu\n", m_fpgaDIO->readLoopTiming(&localStatus), kExpectedLoopTiming); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 68 | wpi_setWPIErrorWithContext(LoopTimingError, err); |
| 69 | } |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 70 | |
| 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); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 76 | |
| 77 | // Ensure that PWM output values are set to OFF |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 78 | for (uint32_t pwm_index = 1; pwm_index <= kPwmChannels; pwm_index++) |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 79 | { |
| 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 | |
| 101 | DigitalModule::~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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 119 | void DigitalModule::SetPWM(uint32_t channel, uint8_t value) |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 120 | { |
| 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 133 | uint8_t DigitalModule::GetPWM(uint32_t channel) |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 134 | { |
| 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 147 | void DigitalModule::SetPWMPeriodScale(uint32_t channel, uint32_t squelchMask) |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 148 | { |
| 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 160 | void DigitalModule::SetRelayForward(uint32_t channel, bool on) |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 161 | { |
| 162 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 163 | CheckRelayChannel(channel); |
| 164 | { |
| 165 | Synchronized sync(m_relaySemaphore); |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 166 | uint8_t forwardRelays = m_fpgaDIO->readSlowValue_RelayFwd(&localStatus); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 167 | 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 181 | void DigitalModule::SetRelayReverse(uint32_t channel, bool on) |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 182 | { |
| 183 | tRioStatusCode localStatus = NiFpga_Status_Success; |
| 184 | CheckRelayChannel(channel); |
| 185 | { |
| 186 | Synchronized sync(m_relaySemaphore); |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 187 | uint8_t reverseRelays = m_fpgaDIO->readSlowValue_RelayRev(&localStatus); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 188 | 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 200 | bool DigitalModule::GetRelayForward(uint32_t channel) |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 201 | { |
| 202 | tRioStatusCode localStatus = NiFpga_Status_Success; |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 203 | uint8_t forwardRelays = m_fpgaDIO->readSlowValue_RelayFwd(&localStatus); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 204 | 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 211 | uint8_t DigitalModule::GetRelayForward() |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 212 | { |
| 213 | tRioStatusCode localStatus = NiFpga_Status_Success; |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 214 | uint8_t forwardRelays = m_fpgaDIO->readSlowValue_RelayFwd(&localStatus); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 215 | wpi_setError(localStatus); |
| 216 | return forwardRelays; |
| 217 | } |
| 218 | |
| 219 | /** |
| 220 | * Get the current state of the reverse relay channel |
| 221 | */ |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 222 | bool DigitalModule::GetRelayReverse(uint32_t channel) |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 223 | { |
| 224 | tRioStatusCode localStatus = NiFpga_Status_Success; |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 225 | uint8_t reverseRelays = m_fpgaDIO->readSlowValue_RelayRev(&localStatus); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 226 | 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 234 | uint8_t DigitalModule::GetRelayReverse() |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 235 | { |
| 236 | tRioStatusCode localStatus = NiFpga_Status_Success; |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 237 | uint8_t reverseRelays = m_fpgaDIO->readSlowValue_RelayRev(&localStatus); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 238 | 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 252 | bool DigitalModule::AllocateDIO(uint32_t channel, bool input) |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 253 | { |
| 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 260 | uint32_t bitToSet = 1 << (RemapDigitalChannel(channel - 1)); |
| 261 | uint32_t outputEnable = m_fpgaDIO->readOutputEnable(&localStatus); |
| 262 | uint32_t outputEnableValue; |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 263 | 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 282 | void DigitalModule::FreeDIO(uint32_t channel) |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 283 | { |
| 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 294 | void DigitalModule::SetDIO(uint32_t channel, short value) |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 295 | { |
| 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 305 | uint16_t currentDIO = m_fpgaDIO->readDO(&localStatus); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 306 | 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 326 | bool DigitalModule::GetDIO(uint32_t channel) |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 327 | { |
| 328 | tRioStatusCode localStatus = NiFpga_Status_Success; |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 329 | uint32_t currentDIO = m_fpgaDIO->readDI(&localStatus); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 330 | 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 343 | uint16_t DigitalModule::GetDIO() |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 344 | { |
| 345 | tRioStatusCode localStatus = NiFpga_Status_Success; |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 346 | uint32_t currentDIO = m_fpgaDIO->readDI(&localStatus); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 347 | 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 358 | bool DigitalModule::GetDIODirection(uint32_t channel) |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 359 | { |
| 360 | tRioStatusCode localStatus = NiFpga_Status_Success; |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 361 | uint32_t currentOutputEnable = m_fpgaDIO->readOutputEnable(&localStatus); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 362 | 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 376 | uint16_t DigitalModule::GetDIODirection() |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 377 | { |
| 378 | tRioStatusCode localStatus = NiFpga_Status_Success; |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 379 | uint32_t currentOutputEnable = m_fpgaDIO->readOutputEnable(&localStatus); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 380 | 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 391 | void DigitalModule::Pulse(uint32_t channel, float pulseLength) |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 392 | { |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 393 | uint16_t mask = 1 << RemapDigitalChannel(channel - 1); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 394 | tRioStatusCode localStatus = NiFpga_Status_Success; |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 395 | m_fpgaDIO->writePulseLength((uint8_t)(1.0e9 * pulseLength / (m_fpgaDIO->readLoopTiming(&localStatus) * 25)), &localStatus); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 396 | 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 405 | bool DigitalModule::IsPulsing(uint32_t channel) |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 406 | { |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 407 | uint16_t mask = 1 << RemapDigitalChannel(channel - 1); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 408 | tRioStatusCode localStatus = NiFpga_Status_Success; |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 409 | uint16_t pulseRegister = m_fpgaDIO->readPulse(&localStatus); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 410 | 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 | */ |
| 419 | bool DigitalModule::IsPulsing() |
| 420 | { |
| 421 | tRioStatusCode localStatus = NiFpga_Status_Success; |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 422 | uint16_t pulseRegister = m_fpgaDIO->readPulse(&localStatus); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 423 | 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 433 | uint32_t DigitalModule::AllocateDO_PWM() |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 434 | { |
| 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 445 | void DigitalModule::FreeDO_PWM(uint32_t pwmGenerator) |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 446 | { |
| 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 | */ |
| 458 | void 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 463 | uint8_t pwmPeriodPower = (uint8_t)(log(1.0 / (m_fpgaDIO->readLoopTiming(&localStatus) * 0.25E-6 * rate))/log(2.0) + 0.5); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 464 | 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 474 | void DigitalModule::SetDO_PWMOutputChannel(uint32_t pwmGenerator, uint32_t channel) |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 475 | { |
| 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 502 | void DigitalModule::SetDO_PWMDutyCycle(uint32_t pwmGenerator, float dutyCycle) |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 503 | { |
| 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 512 | uint8_t pwmPeriodPower = m_fpgaDIO->readDO_PWMConfig_PeriodPower(&localStatus); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 513 | 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 518 | m_fpgaDIO->writeDO_PWMDutyCycle(pwmGenerator, (uint8_t)rawDutyCycle, &localStatus); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 519 | } |
| 520 | wpi_setError(localStatus); |
| 521 | } |
| 522 | |
| 523 | /** |
Brian Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 524 | * Get the loop timing of the Digital Module |
| 525 | * |
| 526 | * @return The loop time |
| 527 | */ |
| 528 | uint16_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 | /** |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 538 | * 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 Silverman | 6874947 | 2014-01-05 14:50:00 -0800 | [diff] [blame^] | 544 | I2C* DigitalModule::GetI2C(uint32_t address) |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 545 | { |
| 546 | return new I2C(this, address); |
| 547 | } |
| 548 | |
| 549 | |