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