jerrym | f157933 | 2013-02-07 01:56:28 +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 "AnalogModule.h"
|
| 8 | #include "Synchronized.h"
|
| 9 | #include "Timer.h"
|
| 10 | #include "WPIErrors.h"
|
| 11 | #include "NetworkCommunication/AICalibration.h"
|
| 12 |
|
| 13 | const long AnalogModule::kTimebase; ///< 40 MHz clock
|
| 14 | const long AnalogModule::kDefaultOversampleBits;
|
| 15 | const long AnalogModule::kDefaultAverageBits;
|
| 16 | const float AnalogModule::kDefaultSampleRate;
|
| 17 | SEM_ID AnalogModule::m_registerWindowSemaphore = NULL;
|
| 18 |
|
| 19 | /**
|
| 20 | * Get an instance of an Analog Module.
|
| 21 | *
|
| 22 | * Singleton analog module creation where a module is allocated on the first use
|
| 23 | * and the same module is returned on subsequent uses.
|
| 24 | *
|
| 25 | * @param moduleNumber The analog module to get (1 or 2).
|
| 26 | * @return A pointer to the AnalogModule.
|
| 27 | */
|
| 28 | AnalogModule* AnalogModule::GetInstance(UINT8 moduleNumber)
|
| 29 | {
|
| 30 | if (CheckAnalogModule(moduleNumber))
|
| 31 | {
|
| 32 | return (AnalogModule*)GetModule(nLoadOut::kModuleType_Analog, moduleNumber);
|
| 33 | }
|
| 34 |
|
| 35 | // If this wasn't caught before now, make sure we say what's wrong before we crash
|
| 36 | char buf[64];
|
| 37 | snprintf(buf, 64, "Analog Module %d", moduleNumber);
|
| 38 | wpi_setGlobalWPIErrorWithContext(ModuleIndexOutOfRange, buf);
|
| 39 |
|
| 40 | return NULL;
|
| 41 | }
|
| 42 |
|
| 43 | /**
|
| 44 | * Create a new instance of an analog module.
|
| 45 | *
|
| 46 | * Create an instance of the analog module object. Initialize all the parameters
|
| 47 | * to reasonable values on start.
|
| 48 | * Setting a global value on an analog module can be done only once unless subsequent
|
| 49 | * values are set the previously set value.
|
| 50 | * Analog modules are a singleton, so the constructor is never called outside of this class.
|
| 51 | *
|
| 52 | * @param moduleNumber The analog module to create (1 or 2).
|
| 53 | */
|
| 54 | AnalogModule::AnalogModule(UINT8 moduleNumber)
|
| 55 | : Module(nLoadOut::kModuleType_Analog, moduleNumber)
|
| 56 | , m_module (NULL)
|
| 57 | , m_sampleRateSet (false)
|
| 58 | , m_numChannelsToActivate (0)
|
| 59 | {
|
| 60 | AddToSingletonList();
|
| 61 | tRioStatusCode localStatus = NiFpga_Status_Success;
|
| 62 | m_module = tAI::create(m_moduleNumber - 1, &localStatus);
|
| 63 | wpi_setError(localStatus);
|
| 64 | SetNumChannelsToActivate(kAnalogChannels);
|
| 65 | SetSampleRate(kDefaultSampleRate);
|
| 66 |
|
| 67 | for (UINT32 i = 0; i < kAnalogChannels; i++)
|
| 68 | {
|
| 69 | m_module->writeScanList(i, i, &localStatus);
|
| 70 | wpi_setError(localStatus);
|
| 71 | SetAverageBits(i + 1, kDefaultAverageBits);
|
| 72 | SetOversampleBits(i + 1, kDefaultOversampleBits);
|
| 73 | }
|
| 74 |
|
| 75 | if (m_registerWindowSemaphore == NULL)
|
| 76 | {
|
| 77 | // Needs to be global since the protected resource spans both module singletons.
|
| 78 | m_registerWindowSemaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
|
| 79 | }
|
| 80 | }
|
| 81 |
|
| 82 | /**
|
| 83 | * Destructor for AnalogModule.
|
| 84 | */
|
| 85 | AnalogModule::~AnalogModule()
|
| 86 | {
|
| 87 | delete m_module;
|
| 88 | }
|
| 89 |
|
| 90 | /**
|
| 91 | * Set the sample rate on the module.
|
| 92 | *
|
| 93 | * This is a global setting for the module and effects all channels.
|
| 94 | *
|
| 95 | * @param samplesPerSecond The number of samples per channel per second.
|
| 96 | */
|
| 97 | void AnalogModule::SetSampleRate(float samplesPerSecond)
|
| 98 | {
|
| 99 | // TODO: This will change when variable size scan lists are implemented.
|
| 100 | // TODO: Need float comparison with epsilon.
|
| 101 | //wpi_assert(!sampleRateSet || GetSampleRate() == samplesPerSecond);
|
| 102 | m_sampleRateSet = true;
|
| 103 |
|
| 104 | // Compute the convert rate
|
| 105 | UINT32 ticksPerSample = (UINT32)((float)kTimebase / samplesPerSecond);
|
| 106 | UINT32 ticksPerConversion = ticksPerSample / GetNumChannelsToActivate();
|
| 107 | // ticksPerConversion must be at least 80
|
| 108 | if (ticksPerConversion < 80)
|
| 109 | {
|
| 110 | wpi_setWPIError(SampleRateTooHigh);
|
| 111 | ticksPerConversion = 80;
|
| 112 | }
|
| 113 |
|
| 114 | // Atomically set the scan size and the convert rate so that the sample rate is constant
|
| 115 | tAI::tConfig config;
|
| 116 | config.ScanSize = GetNumChannelsToActivate();
|
| 117 | config.ConvertRate = ticksPerConversion;
|
| 118 | tRioStatusCode localStatus = NiFpga_Status_Success;
|
| 119 | m_module->writeConfig(config, &localStatus);
|
| 120 | wpi_setError(localStatus);
|
| 121 |
|
| 122 | // Indicate that the scan size has been commited to hardware.
|
| 123 | SetNumChannelsToActivate(0);
|
| 124 | }
|
| 125 |
|
| 126 | /**
|
| 127 | * Get the current sample rate on the module.
|
| 128 | *
|
| 129 | * This assumes one entry in the scan list.
|
| 130 | * This is a global setting for the module and effects all channels.
|
| 131 | *
|
| 132 | * @return Sample rate.
|
| 133 | */
|
| 134 | float AnalogModule::GetSampleRate()
|
| 135 | {
|
| 136 | tRioStatusCode localStatus = NiFpga_Status_Success;
|
| 137 | UINT32 ticksPerConversion = m_module->readLoopTiming(&localStatus);
|
| 138 | wpi_setError(localStatus);
|
| 139 | UINT32 ticksPerSample = ticksPerConversion * GetNumActiveChannels();
|
| 140 | return (float)kTimebase / (float)ticksPerSample;
|
| 141 | }
|
| 142 |
|
| 143 | /**
|
| 144 | * Return the number of channels on the module in use.
|
| 145 | *
|
| 146 | * @return Active channels.
|
| 147 | */
|
| 148 | UINT32 AnalogModule::GetNumActiveChannels()
|
| 149 | {
|
| 150 | tRioStatusCode localStatus = NiFpga_Status_Success;
|
| 151 | UINT32 scanSize = m_module->readConfig_ScanSize(&localStatus);
|
| 152 | wpi_setError(localStatus);
|
| 153 | if (scanSize == 0)
|
| 154 | return 8;
|
| 155 | return scanSize;
|
| 156 | }
|
| 157 |
|
| 158 | /**
|
| 159 | * Get the number of active channels.
|
| 160 | *
|
| 161 | * This is an internal function to allow the atomic update of both the
|
| 162 | * number of active channels and the sample rate.
|
| 163 | *
|
| 164 | * When the number of channels changes, use the new value. Otherwise,
|
| 165 | * return the curent value.
|
| 166 | *
|
| 167 | * @return Value to write to the active channels field.
|
| 168 | */
|
| 169 | UINT32 AnalogModule::GetNumChannelsToActivate()
|
| 170 | {
|
| 171 | if(m_numChannelsToActivate == 0) return GetNumActiveChannels();
|
| 172 | return m_numChannelsToActivate;
|
| 173 | }
|
| 174 |
|
| 175 | /**
|
| 176 | * Set the number of active channels.
|
| 177 | *
|
| 178 | * Store the number of active channels to set. Don't actually commit to hardware
|
| 179 | * until SetSampleRate().
|
| 180 | *
|
| 181 | * @param channels Number of active channels.
|
| 182 | */
|
| 183 | void AnalogModule::SetNumChannelsToActivate(UINT32 channels)
|
| 184 | {
|
| 185 | m_numChannelsToActivate = channels;
|
| 186 | }
|
| 187 |
|
| 188 | /**
|
| 189 | * Set the number of averaging bits.
|
| 190 | *
|
| 191 | * This sets the number of averaging bits. The actual number of averaged samples is 2**bits.
|
| 192 | * Use averaging to improve the stability of your measurement at the expense of sampling rate.
|
| 193 | * The averaging is done automatically in the FPGA.
|
| 194 | *
|
| 195 | * @param channel Analog channel to configure.
|
| 196 | * @param bits Number of bits to average.
|
| 197 | */
|
| 198 | void AnalogModule::SetAverageBits(UINT32 channel, UINT32 bits)
|
| 199 | {
|
| 200 | tRioStatusCode localStatus = NiFpga_Status_Success;
|
| 201 | m_module->writeAverageBits(channel - 1, bits, &localStatus);
|
| 202 | wpi_setError(localStatus);
|
| 203 | }
|
| 204 |
|
| 205 | /**
|
| 206 | * Get the number of averaging bits.
|
| 207 | *
|
| 208 | * This gets the number of averaging bits from the FPGA. The actual number of averaged samples is 2**bits.
|
| 209 | * The averaging is done automatically in the FPGA.
|
| 210 | *
|
| 211 | * @param channel Channel to address.
|
| 212 | * @return Bits to average.
|
| 213 | */
|
| 214 | UINT32 AnalogModule::GetAverageBits(UINT32 channel)
|
| 215 | {
|
| 216 | tRioStatusCode localStatus = NiFpga_Status_Success;
|
| 217 | UINT32 result = m_module->readAverageBits(channel - 1, &localStatus);
|
| 218 | wpi_setError(localStatus);
|
| 219 | return result;
|
| 220 | }
|
| 221 |
|
| 222 | /**
|
| 223 | * Set the number of oversample bits.
|
| 224 | *
|
| 225 | * This sets the number of oversample bits. The actual number of oversampled values is 2**bits.
|
| 226 | * Use oversampling to improve the resolution of your measurements at the expense of sampling rate.
|
| 227 | * The oversampling is done automatically in the FPGA.
|
| 228 | *
|
| 229 | * @param channel Analog channel to configure.
|
| 230 | * @param bits Number of bits to oversample.
|
| 231 | */
|
| 232 | void AnalogModule::SetOversampleBits(UINT32 channel, UINT32 bits)
|
| 233 | {
|
| 234 | tRioStatusCode localStatus = NiFpga_Status_Success;
|
| 235 | m_module->writeOversampleBits(channel - 1, bits, &localStatus);
|
| 236 | wpi_setError(localStatus);
|
| 237 | }
|
| 238 |
|
| 239 | /**
|
| 240 | * Get the number of oversample bits.
|
| 241 | *
|
| 242 | * This gets the number of oversample bits from the FPGA. The actual number of oversampled values is
|
| 243 | * 2**bits. The oversampling is done automatically in the FPGA.
|
| 244 | *
|
| 245 | * @param channel Channel to address.
|
| 246 | * @return Bits to oversample.
|
| 247 | */
|
| 248 | UINT32 AnalogModule::GetOversampleBits(UINT32 channel)
|
| 249 | {
|
| 250 | tRioStatusCode localStatus = NiFpga_Status_Success;
|
| 251 | UINT32 result = m_module->readOversampleBits(channel - 1, &localStatus);
|
| 252 | wpi_setError(localStatus);
|
| 253 | return result;
|
| 254 | }
|
| 255 |
|
| 256 | /**
|
| 257 | * Get a sample straight from the channel on this module.
|
| 258 | *
|
| 259 | * The sample is a 12-bit value representing the -10V to 10V range of the A/D converter in the module.
|
| 260 | * The units are in A/D converter codes. Use GetVoltage() to get the analog value in calibrated units.
|
| 261 | *
|
| 262 | * @return A sample straight from the channel on this module.
|
| 263 | */
|
| 264 | INT16 AnalogModule::GetValue(UINT32 channel)
|
| 265 | {
|
| 266 | INT16 value;
|
| 267 | CheckAnalogChannel(channel);
|
| 268 |
|
| 269 | tAI::tReadSelect readSelect;
|
| 270 | readSelect.Channel = channel - 1;
|
| 271 | readSelect.Module = m_moduleNumber - 1;
|
| 272 | readSelect.Averaged = false;
|
| 273 | tRioStatusCode localStatus = NiFpga_Status_Success;
|
| 274 |
|
| 275 | {
|
| 276 | Synchronized sync(m_registerWindowSemaphore);
|
| 277 | m_module->writeReadSelect(readSelect, &localStatus);
|
| 278 | m_module->strobeLatchOutput(&localStatus);
|
| 279 | value = (INT16) m_module->readOutput(&localStatus);
|
| 280 | }
|
| 281 |
|
| 282 | wpi_setError(localStatus);
|
| 283 | return value;
|
| 284 | }
|
| 285 |
|
| 286 | /**
|
| 287 | * Get a sample from the output of the oversample and average engine for the channel.
|
| 288 | *
|
| 289 | * The sample is 12-bit + the value configured in SetOversampleBits().
|
| 290 | * The value configured in SetAverageBits() will cause this value to be averaged 2**bits number of samples.
|
| 291 | * This is not a sliding window. The sample will not change until 2**(OversamplBits + AverageBits) samples
|
| 292 | * have been acquired from the module on this channel.
|
| 293 | * Use GetAverageVoltage() to get the analog value in calibrated units.
|
| 294 | *
|
| 295 | * @param channel Channel number to read.
|
| 296 | * @return A sample from the oversample and average engine for the channel.
|
| 297 | */
|
| 298 | INT32 AnalogModule::GetAverageValue(UINT32 channel)
|
| 299 | {
|
| 300 | INT32 value;
|
| 301 | CheckAnalogChannel(channel);
|
| 302 |
|
| 303 | tAI::tReadSelect readSelect;
|
| 304 | readSelect.Channel = channel - 1;
|
| 305 | readSelect.Module = m_moduleNumber - 1;
|
| 306 | readSelect.Averaged = true;
|
| 307 | tRioStatusCode localStatus = NiFpga_Status_Success;
|
| 308 |
|
| 309 | {
|
| 310 | Synchronized sync(m_registerWindowSemaphore);
|
| 311 | m_module->writeReadSelect(readSelect, &localStatus);
|
| 312 | m_module->strobeLatchOutput(&localStatus);
|
| 313 | value = m_module->readOutput(&localStatus);
|
| 314 | }
|
| 315 |
|
| 316 | wpi_setError(localStatus);
|
| 317 | return value;
|
| 318 | }
|
| 319 |
|
| 320 | /**
|
| 321 | * Convert a voltage to a raw value for a specified channel.
|
| 322 | *
|
| 323 | * This process depends on the calibration of each channel, so the channel
|
| 324 | * must be specified.
|
| 325 | *
|
| 326 | * @todo This assumes raw values. Oversampling not supported as is.
|
| 327 | *
|
| 328 | * @param channel The channel to convert for.
|
| 329 | * @param voltage The voltage to convert.
|
| 330 | * @return The raw value for the channel.
|
| 331 | */
|
| 332 | INT32 AnalogModule::VoltsToValue(INT32 channel, float voltage)
|
| 333 | {
|
| 334 | if (voltage > 10.0)
|
| 335 | {
|
| 336 | voltage = 10.0;
|
| 337 | wpi_setWPIError(VoltageOutOfRange);
|
| 338 | }
|
| 339 | if (voltage < -10.0)
|
| 340 | {
|
| 341 | voltage = -10.0;
|
| 342 | wpi_setWPIError(VoltageOutOfRange);
|
| 343 | }
|
| 344 | UINT32 LSBWeight = GetLSBWeight(channel);
|
| 345 | INT32 offset = GetOffset(channel);
|
| 346 | INT32 value = (INT32) ((voltage + offset * 1.0e-9) / (LSBWeight * 1.0e-9));
|
| 347 | return value;
|
| 348 | }
|
| 349 |
|
| 350 | /**
|
| 351 | * Get a scaled sample straight from the channel on this module.
|
| 352 | *
|
| 353 | * The value is scaled to units of Volts using the calibrated scaling data from GetLSBWeight() and GetOffset().
|
| 354 | *
|
| 355 | * @param channel The channel to read.
|
| 356 | * @return A scaled sample straight from the channel on this module.
|
| 357 | */
|
| 358 | float AnalogModule::GetVoltage(UINT32 channel)
|
| 359 | {
|
| 360 | INT16 value = GetValue(channel);
|
| 361 | UINT32 LSBWeight = GetLSBWeight(channel);
|
| 362 | INT32 offset = GetOffset(channel);
|
| 363 | float voltage = LSBWeight * 1.0e-9 * value - offset * 1.0e-9;
|
| 364 | return voltage;
|
| 365 | }
|
| 366 |
|
| 367 | /**
|
| 368 | * Get a scaled sample from the output of the oversample and average engine for the channel.
|
| 369 | *
|
| 370 | * The value is scaled to units of Volts using the calibrated scaling data from GetLSBWeight() and GetOffset().
|
| 371 | * Using oversampling will cause this value to be higher resolution, but it will update more slowly.
|
| 372 | * Using averaging will cause this value to be more stable, but it will update more slowly.
|
| 373 | *
|
| 374 | * @param channel The channel to read.
|
| 375 | * @return A scaled sample from the output of the oversample and average engine for the channel.
|
| 376 | */
|
| 377 | float AnalogModule::GetAverageVoltage(UINT32 channel)
|
| 378 | {
|
| 379 | INT32 value = GetAverageValue(channel);
|
| 380 | UINT32 LSBWeight = GetLSBWeight(channel);
|
| 381 | INT32 offset = GetOffset(channel);
|
| 382 | UINT32 oversampleBits = GetOversampleBits(channel);
|
| 383 | float voltage = ((LSBWeight * 1.0e-9 * value) / (float)(1 << oversampleBits)) - offset * 1.0e-9;
|
| 384 | return voltage;
|
| 385 | }
|
| 386 |
|
| 387 | /**
|
| 388 | * Get the factory scaling least significant bit weight constant.
|
| 389 | * The least significant bit weight constant for the channel that was calibrated in
|
| 390 | * manufacturing and stored in an eeprom in the module.
|
| 391 | *
|
| 392 | * Volts = ((LSB_Weight * 1e-9) * raw) - (Offset * 1e-9)
|
| 393 | *
|
| 394 | * @param channel The channel to get calibration data for.
|
| 395 | * @return Least significant bit weight.
|
| 396 | */
|
| 397 | UINT32 AnalogModule::GetLSBWeight(UINT32 channel)
|
| 398 | {
|
| 399 | tRioStatusCode localStatus = NiFpga_Status_Success;
|
| 400 | UINT32 lsbWeight = FRC_NetworkCommunication_nAICalibration_getLSBWeight(m_module->getSystemIndex(), channel - 1, (INT32*)&localStatus);
|
| 401 | wpi_setError(localStatus);
|
| 402 | return lsbWeight;
|
| 403 | }
|
| 404 |
|
| 405 | /**
|
| 406 | * Get the factory scaling offset constant.
|
| 407 | * The offset constant for the channel that was calibrated in manufacturing and stored
|
| 408 | * in an eeprom in the module.
|
| 409 | *
|
| 410 | * Volts = ((LSB_Weight * 1e-9) * raw) - (Offset * 1e-9)
|
| 411 | *
|
| 412 | * @param channel The channel to get calibration data for.
|
| 413 | * @return Offset constant.
|
| 414 | */
|
| 415 | INT32 AnalogModule::GetOffset(UINT32 channel)
|
| 416 | {
|
| 417 | tRioStatusCode localStatus = NiFpga_Status_Success;
|
| 418 | INT32 offset = FRC_NetworkCommunication_nAICalibration_getOffset(m_module->getSystemIndex(), channel - 1, (INT32*)&localStatus);
|
| 419 | wpi_setError(localStatus);
|
| 420 | return offset;
|
| 421 | }
|
| 422 |
|
| 423 |
|