Mirror with some correction

Dependencies:   mbed FastIO FastPWM USBDevice

Committer:
arnoz
Date:
Fri Oct 01 08:19:46 2021 +0000
Revision:
116:7a67265d7c19
Parent:
104:6e06e0f4b476
- Correct information regarding your last merge

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mjr 43:7a6364d82a41 1 #ifndef ALTANALOGIN_H
mjr 43:7a6364d82a41 2 #define ALTANALOGIN_H
mjr 43:7a6364d82a41 3
mjr 48:058ace2aed1d 4 // This is a modified version of Scissors's FastAnalogIn, customized
mjr 104:6e06e0f4b476 5 // for the needs of the Pinscape linear image sensor interfaces. This
mjr 104:6e06e0f4b476 6 // class has a bunch of features to make it even faster than FastAnalogIn,
mjr 104:6e06e0f4b476 7 // including support for 8-bit and 12-bit resolution modes, continuous
mjr 104:6e06e0f4b476 8 // sampling mode, coordination with DMA to move samples into memory
mjr 104:6e06e0f4b476 9 // asynchronously, and client selection of the ADC timing modes.
mjr 48:058ace2aed1d 10 //
mjr 104:6e06e0f4b476 11 // We need all of this special ADC handling because the image sensors
mjr 104:6e06e0f4b476 12 // have special timing requirements that we can only meet with the
mjr 104:6e06e0f4b476 13 // fastest modes offered by the KL25Z ADC. The image sensors all
mjr 104:6e06e0f4b476 14 // operate by sending pixel data as a serial stream of analog samples,
mjr 104:6e06e0f4b476 15 // so the minimum time to read a frame is approximately <number of
mjr 104:6e06e0f4b476 16 // pixels in the frame> times <ADC sampling time per sample>. The
mjr 104:6e06e0f4b476 17 // sensors we currently support vary from 1280 to 1546 pixels per frame.
mjr 104:6e06e0f4b476 18 // With the fastest KL25Z modes, that works out to about 3ms per frame,
mjr 104:6e06e0f4b476 19 // which is just fast enough for our purposes. Using only the default
mjr 104:6e06e0f4b476 20 // modes in the mbed libraries, frame times are around 30ms, which is
mjr 104:6e06e0f4b476 21 // much too slow to accurately track a fast-moving plunger.
mjr 104:6e06e0f4b476 22 //
mjr 104:6e06e0f4b476 23 // This class works ONLY with the KL25Z.
mjr 43:7a6364d82a41 24 //
mjr 100:1ff35c07217c 25 // Important! This class can't coexist at run-time with the standard
mjr 104:6e06e0f4b476 26 // mbed library version of AnalogIn, or with the original version of
mjr 100:1ff35c07217c 27 // FastAnalogIn. All of these classes program the ADC configuration
mjr 100:1ff35c07217c 28 // registers with their own custom settings. These registers are a
mjr 100:1ff35c07217c 29 // global resource, and the different classes all assume they have
mjr 100:1ff35c07217c 30 // exclusive control, so they don't try to coordinate with anyone else
mjr 100:1ff35c07217c 31 // programming the registers. A program that uses AltAnalogIn in one
mjr 100:1ff35c07217c 32 // place will have to use AltAnalogIn exclusively throughout the
mjr 100:1ff35c07217c 33 // program for all ADC interaction. (It *is* okay to statically link
mjr 100:1ff35c07217c 34 // the different classes, as long as only one is actually used at
mjr 100:1ff35c07217c 35 // run-time. The Pinscape software does this, and selects the one to
mjr 100:1ff35c07217c 36 // use at run-time according to which plunger class is selected.)
mjr 43:7a6364d82a41 37
mjr 43:7a6364d82a41 38 /*
mjr 43:7a6364d82a41 39 * Includes
mjr 43:7a6364d82a41 40 */
mjr 43:7a6364d82a41 41 #include "mbed.h"
mjr 43:7a6364d82a41 42 #include "pinmap.h"
mjr 45:c42166b2878c 43 #include "SimpleDMA.h"
mjr 45:c42166b2878c 44
mjr 45:c42166b2878c 45 // KL25Z definitions
mjr 45:c42166b2878c 46 #if defined TARGET_KLXX
mjr 45:c42166b2878c 47
mjr 100:1ff35c07217c 48 // Maximum ADC clock for KL25Z in <= 12-bit mode - 18 MHz per the data sheet
mjr 45:c42166b2878c 49 #define MAX_FADC_12BIT 18000000
mjr 45:c42166b2878c 50
mjr 100:1ff35c07217c 51 // Maximum ADC clock for KL25Z in 16-bit mode - 12 MHz per the data sheet
mjr 100:1ff35c07217c 52 #define MAX_FADC_16BIT 12000000
mjr 100:1ff35c07217c 53
mjr 45:c42166b2878c 54 #define CHANNELS_A_SHIFT 5 // bit position in ADC channel number of A/B mux
mjr 45:c42166b2878c 55 #define ADC_CFG1_ADLSMP 0x10 // long sample time mode
mjr 45:c42166b2878c 56 #define ADC_SC1_AIEN 0x40 // interrupt enable
mjr 45:c42166b2878c 57 #define ADC_SC2_ADLSTS(mode) (mode) // long sample time select - bits 1:0 of CFG2
mjr 45:c42166b2878c 58 #define ADC_SC2_DMAEN 0x04 // DMA enable
mjr 100:1ff35c07217c 59 #define ADC_SC2_ADTRG 0x40 // Hardware conversion trigger
mjr 45:c42166b2878c 60 #define ADC_SC3_CONTINUOUS 0x08 // continuous conversion mode
mjr 100:1ff35c07217c 61 #define ADC_SC3_AVGE 0x04 // averaging enabled
mjr 100:1ff35c07217c 62 #define ADC_SC3_AVGS_4 0x00 // 4-sample averaging
mjr 100:1ff35c07217c 63 #define ADC_SC3_AVGS_8 0x01 // 8-sample averaging
mjr 100:1ff35c07217c 64 #define ADC_SC3_AVGS_16 0x02 // 16-sample averaging
mjr 100:1ff35c07217c 65 #define ADC_SC3_AVGS_32 0x03 // 32-sample averaging
mjr 100:1ff35c07217c 66 #define ADC_SC3_CAL 0x80 // calibration - set to begin calibration
mjr 100:1ff35c07217c 67 #define ADC_SC3_CALF 0x40 // calibration failed flag
mjr 45:c42166b2878c 68
mjr 47:df7a88cd249c 69 #define ADC_8BIT 0 // 8-bit resolution
mjr 47:df7a88cd249c 70 #define ADC_12BIT 1 // 12-bit resolution
mjr 47:df7a88cd249c 71 #define ADC_10BIT 2 // 10-bit resolution
mjr 47:df7a88cd249c 72 #define ADC_16BIT 3 // 16-bit resolution
mjr 47:df7a88cd249c 73
mjr 100:1ff35c07217c 74 // SIM_SOPT7 - enable alternative conversion triggers
mjr 100:1ff35c07217c 75 #define ADC0ALTTRGEN 0x80
mjr 100:1ff35c07217c 76
mjr 100:1ff35c07217c 77 // SIM_SOPT7 ADC0TRGSEL bits for TPMn, n = 0..2
mjr 100:1ff35c07217c 78 #define ADC0TRGSEL_TPM(n) (0x08 | (n)) // select TPMn overflow
mjr 100:1ff35c07217c 79
mjr 100:1ff35c07217c 80
mjr 45:c42166b2878c 81 #else
mjr 45:c42166b2878c 82 #error "This target is not currently supported"
mjr 45:c42166b2878c 83 #endif
mjr 43:7a6364d82a41 84
mjr 43:7a6364d82a41 85 #if !defined TARGET_LPC1768 && !defined TARGET_KLXX && !defined TARGET_LPC408X && !defined TARGET_LPC11UXX && !defined TARGET_K20D5M
mjr 43:7a6364d82a41 86 #error "Target not supported"
mjr 43:7a6364d82a41 87 #endif
mjr 43:7a6364d82a41 88
mjr 48:058ace2aed1d 89
mjr 43:7a6364d82a41 90 class AltAnalogIn {
mjr 43:7a6364d82a41 91
mjr 43:7a6364d82a41 92 public:
mjr 43:7a6364d82a41 93 /** Create an AltAnalogIn, connected to the specified pin
mjr 43:7a6364d82a41 94 *
mjr 43:7a6364d82a41 95 * @param pin AnalogIn pin to connect to
mjr 100:1ff35c07217c 96 * @param continuous true to enable continue sampling mode
mjr 100:1ff35c07217c 97 * @param long_sample_clocks long sample mode: 0 to disable, ADC clock count to enable (6, 10, 16, or 24)
mjr 100:1ff35c07217c 98 * @param averaging number of averaging cycles (1, 4, 8, 16, 32)
mjr 100:1ff35c07217c 99 * @param sample_bits sample size in bits (8, 10, 12, 16)
mjr 43:7a6364d82a41 100 */
mjr 100:1ff35c07217c 101 AltAnalogIn(PinName pin, bool continuous = false, int long_sample_clocks = 0, int averaging = 1, int sample_bits = 8);
mjr 43:7a6364d82a41 102
mjr 43:7a6364d82a41 103 ~AltAnalogIn( void )
mjr 43:7a6364d82a41 104 {
mjr 43:7a6364d82a41 105 }
mjr 43:7a6364d82a41 106
mjr 100:1ff35c07217c 107 // Calibrate the ADC. Per the KL25Z reference manual, this should be
mjr 100:1ff35c07217c 108 // done after each CPU reset to get the best accuracy from the ADC.
mjr 100:1ff35c07217c 109 //
mjr 100:1ff35c07217c 110 // The calibration process runs synchronously (blocking) and takes
mjr 100:1ff35c07217c 111 // about 2ms. Per the reference manual guidelines, we calibrate
mjr 100:1ff35c07217c 112 // using the same timing parameters configured in the constructor,
mjr 100:1ff35c07217c 113 // but we use the maximum averaging rounds.
mjr 45:c42166b2878c 114 //
mjr 100:1ff35c07217c 115 // The calibration depends on the timing parameters, so if multiple
mjr 100:1ff35c07217c 116 // AltAnalogIn objects will be used in the same application, the
mjr 100:1ff35c07217c 117 // configuration established for one object might not be ideal for
mjr 100:1ff35c07217c 118 // another. The advice in the reference manual is to calibrate once
mjr 100:1ff35c07217c 119 // at the settings where the highest accuracy will be needed. It's
mjr 100:1ff35c07217c 120 // also possible to capture the configuration data from the ADC
mjr 100:1ff35c07217c 121 // registers after a configuration and restore them later by writing
mjr 100:1ff35c07217c 122 // the same values back to the registers, for relatively fast switching
mjr 100:1ff35c07217c 123 // between calibration sets, but that's beyond the scope of this class.
mjr 100:1ff35c07217c 124 void calibrate();
mjr 100:1ff35c07217c 125
mjr 100:1ff35c07217c 126 // Initialize DMA. This connects the ADC port to the given DMA
mjr 100:1ff35c07217c 127 // channel. This doesn't actually initiate a transfer; this just
mjr 100:1ff35c07217c 128 // connects the ADC to the DMA channel for later transfers. Use
mjr 100:1ff35c07217c 129 // the DMA object to set up a transfer, and use one of the trigger
mjr 100:1ff35c07217c 130 // modes (e.g., start() for software triggering) to initiate a
mjr 100:1ff35c07217c 131 // sample.
mjr 45:c42166b2878c 132 void initDMA(SimpleDMA *dma);
mjr 100:1ff35c07217c 133
mjr 100:1ff35c07217c 134 // Enable interrupts. This doesn't actually set up a handler; the
mjr 100:1ff35c07217c 135 // caller is responsible for that. This merely sets the ADC registers
mjr 100:1ff35c07217c 136 // so that the ADC generates an ADC0_IRQ interrupt request each time
mjr 100:1ff35c07217c 137 // the sample completes.
mjr 100:1ff35c07217c 138 //
mjr 100:1ff35c07217c 139 // Note that the interrupt handler must read from ADC0->R[0] before
mjr 100:1ff35c07217c 140 // returning, which has the side effect of clearning the COCO (conversion
mjr 100:1ff35c07217c 141 // complete) flag in the ADC registers. When interrupts are enabled,
mjr 100:1ff35c07217c 142 // the ADC asserts the ADC0_IRQ interrupt continuously as long as the
mjr 100:1ff35c07217c 143 // COCO flag is set, so if the ISR doesn't explicitly clear COCO before
mjr 100:1ff35c07217c 144 // it returns, another ADC0_IRQ interrupt will immediate occur as soon
mjr 100:1ff35c07217c 145 // as the ISR returns, so we'll be stuck in an infinite loop of calling
mjr 100:1ff35c07217c 146 // the ISR over and over.
mjr 100:1ff35c07217c 147 void enableInterrupts();
mjr 45:c42166b2878c 148
mjr 100:1ff35c07217c 149 // Start a sample. This sets the ADC multiplexer to read from
mjr 100:1ff35c07217c 150 // this input and activates the sampler.
mjr 43:7a6364d82a41 151 inline void start()
mjr 43:7a6364d82a41 152 {
mjr 100:1ff35c07217c 153 // select my channel
mjr 100:1ff35c07217c 154 selectChannel();
mjr 100:1ff35c07217c 155
mjr 100:1ff35c07217c 156 // set our SC1 bits - this initiates the sample
mjr 100:1ff35c07217c 157 ADC0->SC1[1] = sc1;
mjr 100:1ff35c07217c 158 ADC0->SC1[0] = sc1;
mjr 100:1ff35c07217c 159 }
mjr 100:1ff35c07217c 160
mjr 100:1ff35c07217c 161 // Set the ADC to trigger on a TPM channel, and start sampling on
mjr 100:1ff35c07217c 162 // the trigger. This can be used to start ADC samples in sync with a
mjr 100:1ff35c07217c 163 // clock signal we're generating via a TPM. The ADC is triggered each
mjr 100:1ff35c07217c 164 // time the TPM counter overflows, which makes it trigger at the start
mjr 100:1ff35c07217c 165 // of each PWM period on the unit.
mjr 100:1ff35c07217c 166 void setTriggerTPM(int tpmUnitNumber);
mjr 100:1ff35c07217c 167
mjr 100:1ff35c07217c 168 // stop sampling
mjr 100:1ff35c07217c 169 void stop()
mjr 100:1ff35c07217c 170 {
mjr 100:1ff35c07217c 171 // set the channel bits to binary 11111 to disable sampling
mjr 100:1ff35c07217c 172 ADC0->SC1[0] = 0x1F;
mjr 100:1ff35c07217c 173 }
mjr 100:1ff35c07217c 174
mjr 100:1ff35c07217c 175 // Resume sampling after a pause.
mjr 100:1ff35c07217c 176 inline void resume()
mjr 100:1ff35c07217c 177 {
mjr 100:1ff35c07217c 178 // restore our SC1 bits
mjr 100:1ff35c07217c 179 ADC0->SC1[1] = sc1;
mjr 100:1ff35c07217c 180 ADC0->SC1[0] = sc1;
mjr 100:1ff35c07217c 181 }
mjr 100:1ff35c07217c 182
mjr 100:1ff35c07217c 183 // Wait for the current sample to complete.
mjr 100:1ff35c07217c 184 //
mjr 100:1ff35c07217c 185 // IMPORTANT! DO NOT use this if DMA is enabled on the ADC. It'll
mjr 100:1ff35c07217c 186 // always gets stuck in an infinite loop, because the CPU will never
mjr 100:1ff35c07217c 187 // be able to observe the COCO bit being set when DMA is enabled. The
mjr 100:1ff35c07217c 188 // reason is that the DMA controller always reads its configured source
mjr 100:1ff35c07217c 189 // address when triggered. The DMA source address for the ADC is the
mjr 100:1ff35c07217c 190 // ADC result register ADC0->R[0], and reading that register by any
mjr 100:1ff35c07217c 191 // means clears COCO. And the DMA controller ALWAYS gets to it first,
mjr 100:1ff35c07217c 192 // so the CPU will never see COCO set when DMA is enabled. It doesn't
mjr 100:1ff35c07217c 193 // matter whether or not a DMA transfer is actually running, either -
mjr 100:1ff35c07217c 194 // it's enough to merely enable DMA on the ADC.
mjr 100:1ff35c07217c 195 inline void wait()
mjr 100:1ff35c07217c 196 {
mjr 100:1ff35c07217c 197 while (!isReady()) ;
mjr 100:1ff35c07217c 198 }
mjr 100:1ff35c07217c 199
mjr 100:1ff35c07217c 200 // Is the sample ready?
mjr 100:1ff35c07217c 201 //
mjr 100:1ff35c07217c 202 // NOTE: As with wait(), the CPU will NEVER observe the COCO bit being
mjr 100:1ff35c07217c 203 // set if DMA is enabled on the ADC. This will always return false if
mjr 100:1ff35c07217c 204 // DMA is enabled. (Not our choice - it's a hardware feature.)
mjr 100:1ff35c07217c 205 inline bool isReady()
mjr 100:1ff35c07217c 206 {
mjr 100:1ff35c07217c 207 return (ADC0->SC1[0] & ADC_SC1_COCO_MASK) != 0;
mjr 100:1ff35c07217c 208 }
mjr 100:1ff35c07217c 209
mjr 100:1ff35c07217c 210
mjr 100:1ff35c07217c 211 private:
mjr 100:1ff35c07217c 212 uint32_t id; // unique ID
mjr 100:1ff35c07217c 213 SimpleDMA *dma; // DMA controller, if used
mjr 100:1ff35c07217c 214 char ADCnumber; // ADC number of our input pin
mjr 100:1ff35c07217c 215 char ADCmux; // multiplexer for our input pin (0=A, 1=B)
mjr 100:1ff35c07217c 216 uint32_t sc1; // SC1 register settings for this input
mjr 100:1ff35c07217c 217 uint32_t sc1_aien;
mjr 100:1ff35c07217c 218 uint32_t sc2; // SC2 register settings for this input
mjr 100:1ff35c07217c 219 uint32_t sc3; // SC3 register settings for this input
mjr 100:1ff35c07217c 220
mjr 100:1ff35c07217c 221 // Switch to this channel if it's not the currently selected channel.
mjr 100:1ff35c07217c 222 // We do this as part of start() (software triggering) or any hardware
mjr 100:1ff35c07217c 223 // trigger setup.
mjr 100:1ff35c07217c 224 static int lastMux;
mjr 100:1ff35c07217c 225 static uint32_t lastId;
mjr 100:1ff35c07217c 226 void selectChannel()
mjr 100:1ff35c07217c 227 {
mjr 43:7a6364d82a41 228 // update the MUX bit in the CFG2 register only if necessary
mjr 43:7a6364d82a41 229 if (lastMux != ADCmux)
mjr 43:7a6364d82a41 230 {
mjr 43:7a6364d82a41 231 // remember the new register value
mjr 43:7a6364d82a41 232 lastMux = ADCmux;
mjr 43:7a6364d82a41 233
mjr 43:7a6364d82a41 234 // select the multiplexer for our ADC channel
mjr 43:7a6364d82a41 235 if (ADCmux)
mjr 43:7a6364d82a41 236 ADC0->CFG2 |= ADC_CFG2_MUXSEL_MASK;
mjr 43:7a6364d82a41 237 else
mjr 43:7a6364d82a41 238 ADC0->CFG2 &= ~ADC_CFG2_MUXSEL_MASK;
mjr 43:7a6364d82a41 239 }
mjr 43:7a6364d82a41 240
mjr 45:c42166b2878c 241 // update the SC2 and SC3 bits only if we're changing inputs
mjr 100:1ff35c07217c 242 if (id != lastId)
mjr 45:c42166b2878c 243 {
mjr 45:c42166b2878c 244 // set our ADC0 SC2 and SC3 configuration bits
mjr 45:c42166b2878c 245 ADC0->SC2 = sc2;
mjr 45:c42166b2878c 246 ADC0->SC3 = sc3;
mjr 45:c42166b2878c 247
mjr 45:c42166b2878c 248 // we're the active one now
mjr 100:1ff35c07217c 249 lastId = id;
mjr 45:c42166b2878c 250 }
mjr 45:c42166b2878c 251 }
mjr 45:c42166b2878c 252
mjr 100:1ff35c07217c 253 // Unselect the channel. This clears our internal flag for which
mjr 100:1ff35c07217c 254 // configuration was selected last, so that we restore settings on
mjr 100:1ff35c07217c 255 // the next start or trigger operation.
mjr 100:1ff35c07217c 256 void unselectChannel() { lastId = 0; }
mjr 100:1ff35c07217c 257 };
mjr 43:7a6364d82a41 258
mjr 100:1ff35c07217c 259 // 8-bit sampler subclass
mjr 100:1ff35c07217c 260 class AltAnalogIn_8bit : public AltAnalogIn
mjr 100:1ff35c07217c 261 {
mjr 100:1ff35c07217c 262 public:
mjr 100:1ff35c07217c 263 AltAnalogIn_8bit(PinName pin, bool continuous = false, int long_sample_clocks = 0, int averaging = 1) :
mjr 100:1ff35c07217c 264 AltAnalogIn(pin, continuous, long_sample_clocks, averaging, 8) { }
mjr 100:1ff35c07217c 265
mjr 43:7a6364d82a41 266 /** Returns the raw value
mjr 43:7a6364d82a41 267 *
mjr 43:7a6364d82a41 268 * @param return Unsigned integer with converted value
mjr 43:7a6364d82a41 269 */
mjr 43:7a6364d82a41 270 inline uint16_t read_u16()
mjr 43:7a6364d82a41 271 {
mjr 43:7a6364d82a41 272 // wait for the hardware to signal that the sample is completed
mjr 45:c42166b2878c 273 wait();
mjr 43:7a6364d82a41 274
mjr 43:7a6364d82a41 275 // return the result register value
mjr 48:058ace2aed1d 276 return (uint16_t)ADC0->R[0] << 8; // convert 16-bit to 16-bit, padding with zeroes
mjr 43:7a6364d82a41 277 }
mjr 43:7a6364d82a41 278
mjr 43:7a6364d82a41 279 /** Returns the scaled value
mjr 43:7a6364d82a41 280 *
mjr 43:7a6364d82a41 281 * @param return Float with scaled converted value to 0.0-1.0
mjr 43:7a6364d82a41 282 */
mjr 43:7a6364d82a41 283 float read(void)
mjr 43:7a6364d82a41 284 {
mjr 43:7a6364d82a41 285 unsigned short value = read_u16();
mjr 43:7a6364d82a41 286 return value / 65535.0f;
mjr 43:7a6364d82a41 287 }
mjr 43:7a6364d82a41 288
mjr 43:7a6364d82a41 289 /** An operator shorthand for read()
mjr 43:7a6364d82a41 290 */
mjr 43:7a6364d82a41 291 operator float() { return read(); }
mjr 100:1ff35c07217c 292 };
mjr 43:7a6364d82a41 293
mjr 100:1ff35c07217c 294 // 16-bit sampler subclass
mjr 100:1ff35c07217c 295 class AltAnalogIn_16bit : public AltAnalogIn
mjr 100:1ff35c07217c 296 {
mjr 100:1ff35c07217c 297 public:
mjr 100:1ff35c07217c 298 AltAnalogIn_16bit(PinName pin, bool continuous = false, int long_sample_clocks = 0, int averaging = 1) :
mjr 100:1ff35c07217c 299 AltAnalogIn(pin, continuous, long_sample_clocks, averaging, 16) { }
mjr 100:1ff35c07217c 300
mjr 100:1ff35c07217c 301 /** Returns the raw value
mjr 100:1ff35c07217c 302 *
mjr 100:1ff35c07217c 303 * @param return Unsigned integer with converted value
mjr 100:1ff35c07217c 304 */
mjr 100:1ff35c07217c 305 inline uint16_t read_u16()
mjr 100:1ff35c07217c 306 {
mjr 100:1ff35c07217c 307 // wait for the hardware to signal that the sample is completed
mjr 100:1ff35c07217c 308 wait();
mjr 43:7a6364d82a41 309
mjr 100:1ff35c07217c 310 // return the result register value
mjr 100:1ff35c07217c 311 return (uint16_t)ADC0->R[0];
mjr 100:1ff35c07217c 312 }
mjr 100:1ff35c07217c 313
mjr 100:1ff35c07217c 314 /** Returns the scaled value
mjr 100:1ff35c07217c 315 *
mjr 100:1ff35c07217c 316 * @param return Float with scaled converted value to 0.0-1.0
mjr 100:1ff35c07217c 317 */
mjr 100:1ff35c07217c 318 float read(void)
mjr 100:1ff35c07217c 319 {
mjr 100:1ff35c07217c 320 unsigned short value = read_u16();
mjr 100:1ff35c07217c 321 return value / 65535.0f;
mjr 100:1ff35c07217c 322 }
mjr 100:1ff35c07217c 323
mjr 100:1ff35c07217c 324 /** An operator shorthand for read()
mjr 100:1ff35c07217c 325 */
mjr 100:1ff35c07217c 326 operator float() { return read(); }
mjr 43:7a6364d82a41 327 };
mjr 43:7a6364d82a41 328
mjr 43:7a6364d82a41 329 #endif