Mirror with some correction
Dependencies: mbed FastIO FastPWM USBDevice
Plunger/potSensor.h
- Committer:
- arnoz
- Date:
- 2021-10-01
- Revision:
- 116:7a67265d7c19
- Parent:
- 104:6e06e0f4b476
File content as of revision 116:7a67265d7c19:
// Potentiometer plunger sensor // // This file implements our generic plunger sensor interface for a // potentiometer. The potentiometer resistance must be linear in // position. To connect physically, wire the fixed ends of the // potentiometer to +3.3V and GND (respectively), and connect the // wiper to an ADC-capable GPIO pin on the KL25Z. The wiper voltage // that we read on the ADC will vary linearly with the wiper position. // Mechanically attach the wiper to the plunger so that the wiper moves // in lock step with the plunger. // // In practice, the ADC readings from a potentiometer can be noisy, // varying by around 1% from reading to reading when the slider is // stationary. One way to improve this is to use longer sampling times // in the ADC to improve the accuracy of the sampling. We can tolerate // quite long ADC sampling times because even the slow modes are quite // a lot faster than the result rate we require. Another way to reduce // noise is to apply some low-pass filtering. The simplest low-pass // filter is to average a number of samples together. Since our ADC // sampling rate (even with long conversions) is quite a lot faster than // the needed output rate, we can simply average samples over the time // scale where we need discrete outputs. // // Note: even though this class is specifically for potentiometers, it // could also be used with any other type of sensor that represents its // position reading as a single analog voltage level that varies linearly // with the position, such as an LVDT. Note that linearity is key here: // this code wouldn't work well with a sensor that produces an analog // voltage but has a NON-linear response curve with respect to measured // position. For example, this code wouldn't work well with the old // Sharp reflective IR proximity/distance sensors, since those have // power-law response curves. To work with a non-linear sensor, you'd // have to subclass this class, override readRaw(), and add processing // that translates the non-linear sensor reading to a linear position // measurement. Such processing is obviously a function of the physics // of the particular sensor, so it would have to be crafted for each // such sensor type. // #include "plunger.h" #include "AltAnalogIn.h" class PlungerSensorPot: public PlungerSensor { public: // Our native readings are taken as 16-bit ADC samples, so // our native scale is an unsigned 16-bit int, 0..65535. // // Initialize the ADC to take continuous samples, interrupting us // when each conversion finishes so that we can collect the result // in an ISR. For the sampling mode, use long conversions with // 24 ADCK cycles and 8x averaging; this gives us conversion times // of about 37.33us. // PlungerSensorPot(PinName ao) : PlungerSensor(65535), pot(ao, true, 24, 8) // continuous, 24-cycle long samples, 8x averaging -> 37.33us/sample { // calibrate the ADC for best accuracy pot.calibrate(); // clear the timing statistics totalConversionTime = 0; nSamples = 0; // start with everything zeroed history_write_idx = 0; running_sum = 0; for (int i = 0 ; i < countof(history); ++i) history[i] = 0; // set the initial timestamp to the arbitrary epoch on the timer current_timestamp = 0; // Set up an interrupt handler to collect the ADC results. The // ADC will trigger the interrupt on each completed sample. isrThis = this; NVIC_SetVector(ADC0_IRQn, (uint32_t)&irq_handler_static); NVIC_EnableIRQ(ADC0_IRQn); pot.enableInterrupts(); // Start the first asynchronous ADC sample. The ADC will run // continuously once started, and we'll collect samples in the ISR. pot.start(); timer.start(); } virtual void init() { } // samples are always ready virtual bool ready() { return true; } // read the sensor virtual bool readRaw(PlungerReading &r) { // read the current sample components atomically __disable_irq(); // figure the current average reading over the history window r.pos = running_sum / countof(history); r.t = current_timestamp; // done with the atomic read __enable_irq(); // we always have a result available return true; } // Figure the average scan time in microseconds virtual uint32_t getAvgScanTime() { // The effective time per sample is the raw sampling interval // times the averaging window size. if (nSamples == 0) return 0; else return static_cast<uint32_t>(totalConversionTime/nSamples) * countof(history); } private: // analog input for the pot wiper AltAnalogIn_16bit pot; // timer for input timestamps Timer timer; // total sampling time and number of samples, for computing scan times uint64_t totalConversionTime; uint32_t nSamples; // interrupt handler static PlungerSensorPot *isrThis; static void irq_handler_static(void) { isrThis->irq_handler(); } void irq_handler() { // read the next sample uint16_t sample = pot.read_u16(); // deduct the outgoing sample from the running sum running_sum -= history[history_write_idx]; // add the new sample into the running sum running_sum += sample; // store the new sample in the history history[history_write_idx++] = sample; // wrap the history index at the end of the window if (history_write_idx >= countof(history)) history_write_idx = 0; // calculate the elapsed time since the last sample uint32_t now = timer.read_us(); totalConversionTime += now - current_timestamp; ++nSamples; // update the reading timestamp current_timestamp = now; } // Running sum of readings. This is the sum of the readings in the // rolling 5ms window. uint32_t running_sum; // Rolling window of readings, for the averaging filter. Our // sampling time is about 37.33us; 128 of these add up to about // 4.8ms, which is a good interval between samples for our // internal tracking and sending USB data to the PC. uint16_t history[128]; int history_write_idx; // current average reading and scan time uint32_t current_timestamp; };