001 /*----------------------------------------------------------------------------*/
002 /* Copyright (c) FIRST 2008-2012. All Rights Reserved. */
003 /* Open Source Software - may be modified and shared by FRC teams. The code */
004 /* must be accompanied by the FIRST BSD license file in the root directory of */
005 /* the project. */
006 /*----------------------------------------------------------------------------*/
007
008 package edu.wpi.first.wpilibj;
009
010 import edu.wpi.first.wpilibj.fpga.tAI;
011 import edu.wpi.first.wpilibj.communication.AICalibration;
012 import edu.wpi.first.wpilibj.communication.ModulePresence;
013
014 /**
015 * Analog Module class.
016 * Each module can independently sample its channels at a configurable rate.
017 * There is a 64-bit hardware accumulator associated with channel 1 on each module.
018 * The accumulator is attached to the output of the oversample and average engine so that the center
019 * value can be specified in higher resolution resulting in less error.
020 */
021 public class AnalogModule extends Module {
022
023 /**
024 * The time base used by the module
025 */
026 public static final int kTimebase = 40000000;
027 /**
028 * The default number of Oversample bits to use
029 */
030 public static final int kDefaultOversampleBits = 0;
031 /**
032 * The default number of averaging bits to use
033 */
034 public static final int kDefaultAverageBits = 7;
035 /**
036 * The default sample rate
037 */
038 public static final double kDefaultSampleRate = 50000.0;
039 private tAI m_module;
040 private boolean m_sampleRateSet;
041 private long m_accumulatorOffset;
042 private int m_numChannelsToActivate;
043 private final Object syncRoot = new Object();
044
045 /**
046 * Get an instance of an Analog Module.
047 *
048 * Singleton analog module creation where a module is allocated on the first use
049 * and the same module is returned on subsequent uses.
050 *
051 * @param moduleNumber The index of the analog module to get (1 or 2).
052 * @return The AnalogModule.
053 */
054 public static synchronized AnalogModule getInstance(final int moduleNumber) {
055 checkAnalogModule(moduleNumber);
056 return (AnalogModule) getModule(ModulePresence.ModuleType.kAnalog, moduleNumber);
057 }
058
059 /**
060 * Create a new instance of an analog module.
061 *
062 * Create an instance of the analog module object. Initialize all the parameters
063 * to reasonable values on start.
064 * Setting a global value on an analog module can be done only once unless subsequent
065 * values are set the previously set value.
066 * Analog modules are a singleton, so the constructor is never called outside of this class.
067 *
068 * @param moduleNumber The index of the analog module to create (1 or 2).
069 */
070 protected AnalogModule(final int moduleNumber) {
071 super(ModulePresence.ModuleType.kAnalog, moduleNumber);
072
073 m_module = new tAI(moduleNumber - 1);
074 setNumChannelsToActivate(SensorBase.kAnalogChannels);
075 setSampleRate(AnalogModule.kDefaultSampleRate);
076
077 for (int i = 0; i < SensorBase.kAnalogChannels; i++) {
078 m_module.writeScanList(i, i);
079 setAverageBits(i + 1, kDefaultAverageBits);
080 setOversampleBits(i + 1, kDefaultOversampleBits);
081 }
082 }
083
084 /**
085 * Set the sample rate on the module.
086 *
087 * This is a global setting for the module and effects all channels.
088 *
089 * @param samplesPerSecond
090 * The number of samples per channel per second.
091 */
092 public void setSampleRate(final double samplesPerSecond) {
093 // TODO: This will change when variable size scan lists are implemented.
094 // TODO: Need float comparison with epsilon.
095 // wpi_assert(!sampleRateSet || GetSampleRate() == samplesPerSecond);
096 m_sampleRateSet = true;
097
098 // Compute the convert rate
099 final int ticksPerSample = (int) ((double)AnalogModule.kTimebase / samplesPerSecond);
100 int ticksPerConversion = ticksPerSample / getNumChannelsToActivate();
101 // ticksPerConversion must be at least 80
102 // wpi_assert(ticksPerConversion >= 80);
103 if (ticksPerConversion < 80) {
104 ticksPerConversion = 80;
105 }
106
107 // Atomically set the scan size and the convert rate so that the sample
108 // rate is constant
109 m_module.writeConfig_ScanSize(getNumChannelsToActivate());
110 m_module.writeConfig_ConvertRate(ticksPerConversion);
111
112 // Indicate that the scan size has been committed to hardware.
113 setNumChannelsToActivate(0);
114 }
115
116 /**
117 * Get the current sample rate on the module.
118 *
119 * This assumes one entry in the scan list. This is a global setting for the
120 * module and effects all channels.
121 *
122 * @return Sample rate.
123 */
124 public double getSampleRate() {
125 final long ticksPerConversion = m_module.readLoopTiming();
126 final long ticksPerSample = ticksPerConversion * getNumActiveChannels();
127
128 return (double)AnalogModule.kTimebase / (double)ticksPerSample;
129 }
130
131 /**
132 * Return the number of channels on the module in use.
133 *
134 * @return Active channels.
135 */
136 private int getNumActiveChannels() {
137 final int scanSize = m_module.readConfig_ScanSize();
138 return scanSize == 0 ? 8 : scanSize;
139 }
140
141 /**
142 * Get the number of active channels.
143 *
144 * This is an internal function to allow the atomic update of both the
145 * number of active channels and the sample rate.
146 *
147 * When the number of channels changes, use the new value. Otherwise,
148 * return the curent value.
149 *
150 * @return Value to write to the active channels field.
151 */
152 private int getNumChannelsToActivate() {
153 if (m_numChannelsToActivate == 0) {
154 return getNumActiveChannels();
155 }
156 return m_numChannelsToActivate;
157 }
158
159 /**
160 * Set the number of active channels.
161 *
162 * Store the number of active channels to set. Don't actually commit to hardware
163 * until SetSampleRate().
164 *
165 * @param channels Number of active channels.
166 */
167 private void setNumChannelsToActivate(final int channels) {
168 m_numChannelsToActivate = channels;
169 }
170
171 /**
172 * Set the number of averaging bits.
173 *
174 * This sets the number of averaging bits. The actual number of averaged
175 * samples is 2**bits. The averaging is done automatically in the FPGA.
176 *
177 * @param channel
178 * Analog channel to configure.
179 * @param bits
180 * Number of bits to average.
181 */
182 public void setAverageBits(final int channel, final int bits) {
183 m_module.writeAverageBits(channel - 1, bits);
184 }
185
186 /**
187 * Get the number of averaging bits.
188 *
189 * This gets the number of averaging bits from the FPGA. The actual number
190 * of averaged samples is 2**bits. The averaging is done automatically in
191 * the FPGA.
192 *
193 * @param channel Channel to address.
194 * @return Bits to average.
195 */
196 public int getAverageBits(final int channel) {
197 return m_module.readAverageBits(channel - 1);
198 }
199
200 /**
201 * Set the number of oversample bits.
202 *
203 * This sets the number of oversample bits. The actual number of oversampled values is 2**bits.
204 * Use oversampling to improve the resolution of your measurements at the expense of sampling rate.
205 * The oversampling is done automatically in the FPGA.
206 *
207 * @param channel Analog channel to configure.
208 * @param bits Number of bits to oversample.
209 */
210 public void setOversampleBits(final int channel, final int bits) {
211 m_module.writeOversampleBits(channel - 1, bits);
212 }
213
214 /**
215 * Get the number of oversample bits.
216 *
217 * This gets the number of oversample bits from the FPGA. The actual number
218 * of oversampled values is 2**bits. The oversampling is done automatically
219 * in the FPGA.
220 *
221 * @param channel Channel to address.
222 * @return Bits to oversample.
223 */
224 public int getOversampleBits(final int channel) {
225 return m_module.readOversampleBits(channel - 1);
226 }
227
228 /**
229 * Get the raw analog value.
230 *
231 * Get the analog value as it is returned from the D/A converter.
232 *
233 * @param channel Channel number to read.
234 * @return Raw value.
235 */
236 public int getValue(final int channel) {
237 int value;
238 SensorBase.checkAnalogChannel(channel);
239
240 synchronized (syncRoot) {
241 tAI.writeReadSelect_Channel(channel - 1);
242 tAI.writeReadSelect_Module(m_moduleNumber - 1);
243 tAI.writeReadSelect_Averaged(false);
244
245 tAI.strobeLatchOutput();
246 value = tAI.readOutput();
247 }
248
249 return (short) value;
250 }
251
252 /**
253 * Get the raw averaged and oversampled value.
254 *
255 * @param channel Channel number to read.
256 * @return The averaged and oversampled raw value.
257 */
258 public int getAverageValue(final int channel) {
259 int value;
260 SensorBase.checkAnalogChannel(channel);
261
262 synchronized (syncRoot) {
263 tAI.writeReadSelect_Channel(channel - 1);
264 tAI.writeReadSelect_Module(m_moduleNumber - 1);
265 tAI.writeReadSelect_Averaged(true);
266
267 tAI.strobeLatchOutput();
268 value = tAI.readOutput();
269 }
270
271 return value;
272 }
273
274 /**
275 * Convert a voltage to a raw value for a specified channel.
276 *
277 * This process depends on the calibration of each channel, so the channel
278 * must be specified.
279 *
280 * @todo This assumes raw values. Oversampling not supported as is.
281 *
282 * @param channel
283 * The channel to convert for.
284 * @param voltage
285 * The voltage to convert.
286 * @return The raw value for the channel.
287 */
288 public int voltsToValue(final int channel, final double voltage) {
289 // wpi_assert(voltage >= -10.0 && voltage <= 10.0);
290 final long LSBWeight = getLSBWeight(channel);
291 final int offset = getOffset(channel);
292 final int value = (int) ((voltage + offset * 1.0e-9) / (LSBWeight * 1.0e-9));
293
294 return value;
295 }
296
297 /**
298 * Get the voltage.
299 *
300 * Return the voltage from the A/D converter.
301 *
302 * @param channel The channel to read.
303 * @return The scaled voltage from the channel.
304 */
305 public double getVoltage(final int channel) {
306 final int value = getValue(channel);
307 final long LSBWeight = getLSBWeight(channel);
308 final int offset = getOffset(channel);
309 final double voltage = (LSBWeight * 1.0e-9 * value) - (offset * 1.0e-9);
310
311 return voltage;
312 }
313
314 /**
315 * Get the averaged voltage.
316 *
317 * Return the averaged voltage from the A/D converter.
318 *
319 * @param channel The channel to read.
320 * @return The scaled averaged and oversampled voltage from the channel.
321 */
322 public double getAverageVoltage(final int channel) {
323 final int value = getAverageValue(channel);
324 final long LSBWeight = getLSBWeight(channel);
325 final int offset = getOffset(channel);
326 final int oversampleBits = getOversampleBits(channel);
327 final double voltage = ((LSBWeight * 1.0e-9 * value) / (double)(1 << oversampleBits)) - (offset * 1.0e-9);
328
329 return voltage;
330 }
331
332 /**
333 * Get the LSB Weight portion of the calibration for a channel.
334 *
335 * @param channel The channel to get calibration data for.
336 * @return LSB Weight calibration point.
337 */
338 public long getLSBWeight(final int channel) {
339 // TODO: add scaling to make this worth while.
340 return AICalibration.getLSBWeight(m_module.getSystemIndex(), channel - 1);
341 }
342
343 /**
344 * Get the Offset portion of the calibration for a channel.
345 *
346 * @param channel The channel to get calibration data for.
347 * @return Offset calibration point.
348 */
349 public int getOffset(final int channel) {
350 // TODO: add scaling to make this worth while.
351 // TODO: actually use this function.
352 return AICalibration.getOffset(m_module.getSystemIndex(), channel - 1);
353 }
354 }