Pinscape Controller version 1 fork. This is a fork to allow for ongoing bug fixes to the original controller version, from before the major changes for the expansion board project.
Dependencies: FastIO FastPWM SimpleDMA mbed
Fork of Pinscape_Controller by
Revision 43:7a6364d82a41, committed 2016-02-06
- Comitter:
- mjr
- Date:
- Sat Feb 06 20:21:48 2016 +0000
- Parent:
- 40:cc0d9814522b
- Child:
- 44:b5ac89b9cd5d
- Commit message:
- Before floating point plunger ranging
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AltAnalogIn/AltAnalogIn.h Sat Feb 06 20:21:48 2016 +0000 @@ -0,0 +1,159 @@ +#ifndef ALTANALOGIN_H +#define ALTANALOGIN_H + +// This is a slightly modified version of Scissors's FastAnalogIn. +// +// This version is optimized for reading from multiple inputs. The KL25Z has +// multiple ADC channels, but the multiplexer hardware only allows sampling one +// at a time. The entire sampling process from start to finish is serialized +// in the multiplexer, so we unfortunately can't overlap the sampling times +// for multiple channels - we have to wait in sequence for the sampling period +// on each channel, one after the other. +// +// The base version of FastAnalogIn uses the hardware's continuous conversion +// feature to speed up sampling. When sampling multiple inputs, that feature +// becomes useless, and in fact the way FastAnalogIn uses it creates additional +// overhead for multiple input sampling. But FastAnalogIn still has some speed +// advantages over the base mbed AnalogIn implementation, since it sets all of +// the other conversion settings to the fastest options. This version keeps the +// other speed-ups from FastAnalogIn, but dispenses with the continuous sampling. + +/* + * Includes + */ +#include "mbed.h" +#include "pinmap.h" + +#if !defined TARGET_LPC1768 && !defined TARGET_KLXX && !defined TARGET_LPC408X && !defined TARGET_LPC11UXX && !defined TARGET_K20D5M + #error "Target not supported" +#endif + + /** A class similar to AnalogIn, only faster, for LPC1768, LPC408X and KLxx + * + * AnalogIn does a single conversion when you read a value (actually several conversions and it takes the median of that). + * This library runns the ADC conversion automatically in the background. + * When read is called, it immediatly returns the last sampled value. + * + * LPC1768 / LPC4088 + * Using more ADC pins in continuous mode will decrease the conversion rate (LPC1768:200kHz/LPC4088:400kHz). + * If you need to sample one pin very fast and sometimes also need to do AD conversions on another pin, + * you can disable the continuous conversion on that ADC channel and still read its value. + * + * KLXX + * Multiple Fast instances can be declared of which only ONE can be continuous (all others must be non-continuous). + * + * When continuous conversion is disabled, a read will block until the conversion is complete + * (much like the regular AnalogIn library does). + * Each ADC channel can be enabled/disabled separately. + * + * IMPORTANT : It does not play nicely with regular AnalogIn objects, so either use this library or AnalogIn, not both at the same time!! + * + * Example for the KLxx processors: + * @code + * // Print messages when the AnalogIn is greater than 50% + * + * #include "mbed.h" + * + * AltAnalogIn temperature(PTC2); //Fast continuous sampling on PTC2 + * AltAnalogIn speed(PTB3, 0); //Fast non-continuous sampling on PTB3 + * + * int main() { + * while(1) { + * if(temperature > 0.5) { + * printf("Too hot! (%f) at speed %f", temperature.read(), speed.read()); + * } + * } + * } + * @endcode + * Example for the LPC1768 processor: + * @code + * // Print messages when the AnalogIn is greater than 50% + * + * #include "mbed.h" + * + * AltAnalogIn temperature(p20); + * + * int main() { + * while(1) { + * if(temperature > 0.5) { + * printf("Too hot! (%f)", temperature.read()); + * } + * } + * } + * @endcode +*/ +class AltAnalogIn { + +public: + /** Create an AltAnalogIn, connected to the specified pin + * + * @param pin AnalogIn pin to connect to + * @param enabled Enable the ADC channel (default = true) + */ + AltAnalogIn( PinName pin, bool enabled = true ); + + ~AltAnalogIn( void ) + { + } + + /** Start a sample. This sets the ADC multiplexer to read from + * this input and activates the sampler. + */ + inline void start() + { + // update the MUX bit in the CFG2 register only if necessary + static int lastMux = -1; + if (lastMux != ADCmux) + { + // remember the new register value + lastMux = ADCmux; + + // select the multiplexer for our ADC channel + if (ADCmux) + ADC0->CFG2 |= ADC_CFG2_MUXSEL_MASK; + else + ADC0->CFG2 &= ~ADC_CFG2_MUXSEL_MASK; + } + + // select our ADC channel in the control register - this initiates sampling + // on the channel + ADC0->SC1[0] = startMask; + } + + + + /** Returns the raw value + * + * @param return Unsigned integer with converted value + */ + inline uint16_t read_u16() + { + // wait for the hardware to signal that the sample is completed + while ((ADC0->SC1[0] & ADC_SC1_COCO_MASK) == 0); + + // return the result register value + return (uint16_t)ADC0->R[0] << 4; // convert 12-bit to 16-bit, padding with zeroes + } + + /** Returns the scaled value + * + * @param return Float with scaled converted value to 0.0-1.0 + */ + float read(void) + { + unsigned short value = read_u16(); + return value / 65535.0f; + } + + /** An operator shorthand for read() + */ + operator float() { return read(); } + + +private: + char ADCnumber; // ADC number of our input pin + char ADCmux; // multiplexer for our input pin (0=A, 1=B) + uint32_t startMask; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AltAnalogIn/AltAnalogIn_KL25Z.cpp Sat Feb 06 20:21:48 2016 +0000 @@ -0,0 +1,94 @@ +#if defined(TARGET_KLXX) || defined(TARGET_K20D50M) + +#include "AltAnalogIn.h" +#include "clk_freqs.h" + +// Maximum ADC clock for KL25Z in 12-bit mode. The data sheet says this is +// limited to 18MHz, but we seem to get good results at higher rates. The +// data sheet is actually slightly vague on this because it's only in the +// table for the 16-bit ADC, even though the ADC we're using is a 12-bit ADC, +// which seems to have slightly different properties. So there's room to +// think the data sheet omits the data for the 12-bit ADC. +#define MAX_FADC_12BIT 25000000 + +#define CHANNELS_A_SHIFT 5 // bit position in ADC channel number of A/B mux +#define ADC_CFG1_ADLSMP 0x10 // long sample time mode +#define ADC_SC2_ADLSTS(mode) (mode) // long sample time select - bits 1:0 of CFG2 + +#ifdef TARGET_K20D50M +static const PinMap PinMap_ADC[] = { + {PTC2, ADC0_SE4b, 0}, + {PTD1, ADC0_SE5b, 0}, + {PTD5, ADC0_SE6b, 0}, + {PTD6, ADC0_SE7b, 0}, + {PTB0, ADC0_SE8, 0}, + {PTB1, ADC0_SE9, 0}, + {PTB2, ADC0_SE12, 0}, + {PTB3, ADC0_SE13, 0}, + {PTC0, ADC0_SE14, 0}, + {PTC1, ADC0_SE15, 0}, + {NC, NC, 0} +}; +#endif + +AltAnalogIn::AltAnalogIn(PinName pin, bool enabled) +{ + // do nothing if explicitly not connected + if (pin == NC) + return; + + // figure our ADC number + ADCnumber = (ADCName)pinmap_peripheral(pin, PinMap_ADC); + if (ADCnumber == (ADCName)NC) { + error("ADC pin mapping failed"); + } + + // figure our multiplexer channel (A or B) + ADCmux = (ADCnumber >> CHANNELS_A_SHIFT) ^ 1; + + // enable the ADC0 clock in the system control module + SIM->SCGC6 |= SIM_SCGC6_ADC0_MASK; + + // enable the port clock gate for the port containing our GPIO pin + uint32_t port = (uint32_t)pin >> PORT_SHIFT; + SIM->SCGC5 |= 1 << (SIM_SCGC5_PORTA_SHIFT + port); + + // Figure the maximum clock frequency. In 12-bit mode or less, we can + // run the ADC at up to 18 MHz per the KL25Z data sheet. (16-bit mode + // is limited to 12 MHz.) + int clkdiv = 0; + uint32_t ourfreq = bus_frequency(); + for ( ; ourfreq > MAX_FADC_12BIT ; ourfreq /= 2, clkdiv += 1) ; + + // Set the "high speed" configuration only if we're right at the bus speed + // limit. This bit is somewhat confusingly named, in that it actually + // *slows down* the conversions. "High speed" means that the *other* + // options are set right at the limits of the ADC, so this option adds + // a few extra cycle delays to every conversion to compensate for living + // on the edge. + uint32_t adhsc_bit = (ourfreq == MAX_FADC_12BIT ? ADC_CFG2_ADHSC_MASK : 0); + + printf("ADCnumber=%d, cfg2_muxsel=%d, bus freq=%ld, clkdiv=%d\r\n", ADCnumber, ADCmux, bus_frequency(), clkdiv); + + // set up the ADC control registers + + ADC0->CFG1 = ADC_CFG1_ADIV(clkdiv) // Clock Divide Select (as calculated above) + | ADC_CFG1_MODE(1) // Sample precision = 12-bit + | ADC_CFG1_ADICLK(0); // Input Clock = bus clock + + ADC0->CFG2 = adhsc_bit // High-Speed Configuration, if needed + | ADC_CFG2_ADLSTS(3); // Long sample time mode 3 -> 6 ADCK cycles total + + ADC0->SC2 = ADC_SC2_REFSEL(0); // Default Voltage Reference + + ADC0->SC3 = 0; // Calibration mode off, single sample, averaging disabled + + // map the GPIO pin in the system multiplexer to the ADC + pinmap_pinout(pin, PinMap_ADC); + + // figure our 'start' mask - this is the value we write to the SC1A register + // to initiate a new sample + startMask = ADC_SC1_ADCH(ADCnumber & ~(1 << CHANNELS_A_SHIFT)); +} + +#endif //defined TARGET_KLXX
--- a/FastAnalogIn.lib Wed Feb 03 22:57:25 2016 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -http://mbed.org/users/Sissors/code/FastAnalogIn/#234c5cd2b8de
--- a/Pinscape_Controller.lib Wed Feb 03 22:57:25 2016 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -http://mbed.org/users/mjr/code/Pinscape_Controller/#ed52738445fc
--- a/TSL1410R/tsl1410r.h Wed Feb 03 22:57:25 2016 +0000 +++ b/TSL1410R/tsl1410r.h Sat Feb 06 20:21:48 2016 +0000 @@ -6,7 +6,7 @@ #include "mbed.h" #include "config.h" -#include "FastAnalogIn.h" +#include "AltAnalogIn.h" #ifndef TSL1410R_H #define TSL1410R_H @@ -28,14 +28,15 @@ // to be assigned dynamically at run-time, which we prefer because it allows for // configuration changes to be made on the fly rather than having to recompile // the program. -#define GPIO_PORT_BASE(pin) ((FGPIO_Type *)(FPTA_BASE + ((unsigned int)pin >> PORT_SHIFT) * 0x40)) -#define GPIO_PINMASK(pin) (1 << ((pin & 0x7F) >> 2)) +#define GPIO_PORT(pin) (((unsigned int)(pin)) >> PORT_SHIFT) +#define GPIO_PORT_BASE(pin) ((FGPIO_Type *)(FPTA_BASE + GPIO_PORT(pin) * 0x40)) +#define GPIO_PINMASK(pin) gpio_set(pin) class TSL1410R { public: - TSL1410R(int nPix, PinName siPin, PinName clockPin, PinName ao1Pin, PinName ao2Pin) - : nPix(nPix), si(siPin), clock(clockPin), ao1(ao1Pin), ao2(ao2Pin) + TSL1410R(int nPixSensor, PinName siPin, PinName clockPin, PinName ao1Pin, PinName ao2Pin) + : nPixSensor(nPixSensor), si(siPin), clock(clockPin), ao1(ao1Pin), ao2(ao2Pin) { // we're in parallel mode if ao2 is a valid pin parallel = (ao2Pin != NC); @@ -44,16 +45,14 @@ clockPort = GPIO_PORT_BASE(clockPin); clockMask = GPIO_PINMASK(clockPin); - // disable continuous conversion mode in FastAnalogIn - since we're - // reading discrete pixel values, we want to control when the samples - // are taken rather than continuously averaging over time - ao1.disable(); - if (parallel) ao2.disable(); - - // clear out power-on noise by clocking through all pixels twice + // clear out power-on random data by clocking through all pixels twice clear(); clear(); + + totalTime = 0.0; nRuns = 0; // $$$ } + + float totalTime; int nRuns; // $$$ // Read the pixels. // @@ -90,86 +89,109 @@ // If the caller has other work to tend to that takes longer than the // desired maximum integration time, it can call clear() to clock out // the current pixels and start a fresh integration cycle. - void read(uint16_t *pix, int n) + void read(register uint16_t *pix, int n) { + Timer t; t.start(); // $$$ + // get the clock pin pointers into local variables for fast access - register FGPIO_Type *clockPort = this->clockPort; - register uint32_t clockMask = this->clockMask; + register volatile uint32_t *clockPSOR = &clockPort->PSOR; + register volatile uint32_t *clockPCOR = &clockPort->PCOR; + register const uint32_t clockMask = this->clockMask; // start the next integration cycle by pulsing SI and one clock si = 1; - clockPort->PSOR |= clockMask; // turn the clock pin on (clock = 1) + clock = 1; si = 0; - clockPort->PCOR |= clockMask; // turn the clock pin off (clock = 0) + clock = 0; // figure how many pixels to skip on each read - int skip = nPix/n - 1; + int skip = nPixSensor/n - 1; +///$$$ +static int done=0; +if (done++ == 0) printf("nPixSensor=%d, n=%d, skip=%d, parallel=%d\r\n", nPixSensor, n, skip, parallel); + + // get the clock PSOR and PCOR register addresses for fast access + // read all of the pixels + int dst; if (parallel) { - // parallel mode - read pixels from each half sensor concurrently - int nPixHalf = nPix/2; - for (int src = 0, dst = 0 ; src < nPixHalf ; ++src) + // Parallel mode - read pixels from each half sensor concurrently. + // Divide 'n' (the output pixel count) by 2 to get the loop count, + // since we're going to do 2 pixels on each iteration. + for (n /= 2, dst = 0 ; dst < n ; ++dst) { - // pulse the clock and start the ADC sampling - clockPort->PSOR |= clockMask; - ao1.enable(); - ao2.enable(); - wait_us(1); - clockPort->PCOR |= clockMask; - - // wait for the ADCs to stabilize - wait_us(11); + // Take the clock high. The TSL1410R will connect the next + // pixel pair's hold capacitors to the A01 and AO2 lines + // (respectively) on the clock rising edge. + *clockPSOR = clockMask; + + // Start the ADC sampler for AO1. The TSL1410R sample + // stabilization time per the data sheet is 120ns. This is + // fast enough that we don't need an explicit delay, since + // the instructions to execute this call will take longer + // than that. + ao1.start(); - // read the pixels + // take the clock low while we're waiting for the reading + *clockPCOR = clockMask; + + // Read the first half-sensor pixel from AO1 pix[dst] = ao1.read_u16(); - pix[dst+n/2] = ao2.read_u16(); - // turn off the ADC until the next pixel is clocked out - ao1.disable(); - ao2.disable(); + // Read the second half-sensor pixel from AO2, and store it + // in the destination array at the current index PLUS 'n', + // which you will recall contains half the output pixel count. + // This second pixel is halfway up the sensor from the first + // pixel, so it goes halfway up the output array from the + // current output position. + ao2.start(); + pix[dst + n] = ao2.read_u16(); - // clock skipped pixels - for (int i = 0 ; i < skip ; ++i, ++src) + // Clock through the skipped pixels + for (int i = skip ; i > 0 ; --i) { - clockPort->PSOR |= clockMask; - clockPort->PCOR |= clockMask; + *clockPSOR = clockMask; + *clockPCOR = clockMask; } } } else { // serial mode - read all pixels in a single file - for (int src = 0, dst = 0 ; src < nPix ; ++src) + for (dst = 0 ; dst < n ; ++dst) { - // pulse the clock and start the ADC sampling - clockPort->PSOR |= clockMask; - ao1.enable(); - wait_us(1); - clockPort->PCOR |= clockMask; + // Clock the next pixel onto the sensor A0 line + *clockPSOR = clockMask; - // wait for the ADC sample to stabilize - wait_us(11); + // start the ADC sampler + ao1.start(); - // read the ADC sample - pix[dst++] = ao1.read_u16(); - - // turn off the ADC until the next pixel is ready - ao1.disable(); + // take the clock low while we're waiting for the analog reading + *clockPCOR = clockMask; - // clock skipped pixels - for (int i = 0 ; i < skip ; ++i, ++src) + // wait for and read the ADC sample; plug it into the output + // array, and increment the output pointer to the next position + pix[dst] = ao1.read_u16(); + + // clock through the skipped pixels + for (int i = skip ; i > 0 ; --i) { - clockPort->PSOR |= clockMask; - clockPort->PCOR |= clockMask; + *clockPSOR = clockMask; + *clockPCOR = clockMask; } } } +//$$$ +if (done==1) printf(". done: dst=%d\r\n", dst); + // clock out one extra pixel to leave A1 in the high-Z state - clockPort->PSOR |= clockMask; - clockPort->PCOR |= clockMask; + clock = 1; + clock = 0; + + if (n >= 80) { totalTime += t.read(); nRuns += 1; } // $$$ } // Clock through all pixels to clear the array. Pulses SI at the @@ -184,29 +206,29 @@ // clock in an SI pulse si = 1; - clockPort->PSOR |= clockMask; + clockPort->PSOR = clockMask; si = 0; - clockPort->PCOR |= clockMask; + clockPort->PCOR = clockMask; // if in serial mode, clock all pixels across both sensor halves; // in parallel mode, the pixels are clocked together - int n = parallel ? nPix/2 : nPix; + int n = parallel ? nPixSensor/2 : nPixSensor; // clock out all pixels for (int i = 0 ; i < n + 1 ; ++i) { - clockPort->PSOR |= clockMask; - clockPort->PCOR |= clockMask; + clock = 1; // $$$clockPort->PSOR = clockMask; + clock = 0; // $$$clockPort->PCOR = clockMask; } } private: - int nPix; // number of pixels in physical sensor array + int nPixSensor; // number of pixels in physical sensor array DigitalOut si; // GPIO pin for sensor SI (serial data) DigitalOut clock; // GPIO pin for sensor SCLK (serial clock) FGPIO_Type *clockPort; // IOPORT base address for clock pin - cached for fast writes uint32_t clockMask; // IOPORT register bit mask for clock pin - FastAnalogIn ao1; // GPIO pin for sensor AO1 (analog output 1) - we read sensor data from this pin - FastAnalogIn ao2; // GPIO pin for sensor AO2 (analog output 2) - 2nd sensor data pin, when in parallel mode + AltAnalogIn ao1; // GPIO pin for sensor AO1 (analog output 1) - we read sensor data from this pin + AltAnalogIn ao2; // GPIO pin for sensor AO2 (analog output 2) - 2nd sensor data pin, when in parallel mode bool parallel; // true -> running in parallel mode (we read AO1 and AO2 separately on each clock) };
--- a/TSL1410R/tsl410r.cpp Wed Feb 03 22:57:25 2016 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -// this file is no longer used - the method bodies are no in the header, -// which was necessary because of the change to a template class, which -// itself was necessary because of the use of the FastIO library
--- a/ccdSensor.h Wed Feb 03 22:57:25 2016 +0000 +++ b/ccdSensor.h Sat Feb 06 20:21:48 2016 +0000 @@ -28,8 +28,8 @@ class PlungerSensorCCD: public PlungerSensor { public: - PlungerSensorCCD(int nPix, PinName si, PinName clock, PinName ao1, PinName ao2) - : ccd(nPix, si, clock, ao1, ao2) + PlungerSensorCCD(int nativePix, PinName si, PinName clock, PinName ao1, PinName ao2) + : ccd(nativePix, si, clock, ao1, ao2) { } @@ -45,7 +45,6 @@ virtual bool lowResScan(int &pos) { // read the pixels at low resolution - const int nlpix = 32; uint16_t pix[nlpix]; ccd.read(pix, nlpix); @@ -144,13 +143,17 @@ // send an exposure report to the joystick interface virtual void sendExposureReport(USBJoystick &js) { + // Read a fresh high-res scan, then do another right away. This + // gives us the shortest possible exposure for the sample we report, + // which helps ensure that the user inspecting the data sees something + // close to what we see when we calculate the plunger position. + ccd.read(pix, npix); + ccd.read(pix, npix); + // send reports for all pixels int idx = 0; while (idx < npix) - { js.updateExposure(idx, npix, pix); - wait_ms(1); - } // The pixel dump requires many USB reports, since each report // can only send a few pixel values. An integration cycle has @@ -163,10 +166,16 @@ } protected: - // pixel buffer + // pixel buffer - concrete subclasses must set to a buffer of the + // appropriate size uint16_t *pix; + // number of pixels in low-res scan - concrete subclasses must set + // this to a value that evenly divides the native sensor size + int nlpix; + // the low-level interface to the CCD hardware +public://$$$ TSL1410R ccd; }; @@ -180,11 +189,17 @@ { // This sensor is 1x1280 pixels at 400dpi. Sample every 8th // pixel -> 160 pixels at 50dpi == 0.5mm spatial resolution. - npix = 160; + npix = 320; + + // for the low-res scan, sample every 40th pixel -> 32 pixels + // at 10dpi == 2.54mm spatial resolution. + nlpix = 32; + + // set the pixel buffer pix = pixbuf; } - uint16_t pixbuf[160]; + uint16_t pixbuf[320]; }; // TSL1412R @@ -197,6 +212,12 @@ // This sensor is 1x1536 pixels at 400dpi. Sample every 8th // pixel -> 192 pixels at 50dpi == 0.5mm spatial resolution. npix = 192; + + // for the low-res scan, sample every 48 pixels -> 32 pixels + // at 8.34dpi = 3.05mm spatial resolution + nlpix = 32; + + // set the pixel buffer pix = pixbuf; }
--- a/config.h Wed Feb 03 22:57:25 2016 +0000 +++ b/config.h Sat Feb 06 20:21:48 2016 +0000 @@ -141,6 +141,15 @@ plunger.enabled = false; plunger.sensorType = PlungerType_None; +#if 1 // $$$ + plunger.enabled = true; + plunger.sensorType = PlungerType_TSL1410RS; + plunger.sensorPin[0] = PTE20; // SI + plunger.sensorPin[1] = PTE21; // SCLK + plunger.sensorPin[2] = PTB0; // AO1 = PTB0 = ADC0_SE8 + plunger.sensorPin[3] = PTE22; // AO2 (parallel mode) = PTE22 = ADC0_SE3 +#endif + // assume that there's no calibration button plunger.cal.btn = NC; plunger.cal.led = NC; @@ -153,23 +162,21 @@ plunger.zbLaunchBall.btn = 0; // assume no TV ON switch -#if 1 + TVON.statusPin = NC; + TVON.latchPin = NC; + TVON.relayPin = NC; + TVON.delayTime = 7; +#if 0//$$$ TVON.statusPin = PTD2; TVON.latchPin = PTE0; TVON.relayPin = PTD3; TVON.delayTime = 7; -#else - TVON.statusPin = NC; - TVON.latchPin = NC; - TVON.relayPin = NC; - TVON.delayTime = 0; #endif // assume no TLC5940 chips -#if 1 // $$$ - tlc5940.nchips = 4; -#else tlc5940.nchips = 0; +#if 0 // $$$ + //tlc5940.nchips = 4; #endif // default TLC5940 pin assignments @@ -180,10 +187,9 @@ tlc5940.gsclk = PTA1; // assume no 74HC595 chips -#if 1 // $$$ - hc595.nchips = 1; -#else hc595.nchips = 0; +#if 0 // $$$ + //hc595.nchips = 1; #endif // default 74HC595 pin assignments @@ -249,7 +255,8 @@ #endif -#if 1 // $$$ + +#if 0 // $$$ // CONFIGURE EXPANSION BOARD PORTS // // We have the following hardware attached:
--- a/main.cpp Wed Feb 03 22:57:25 2016 +0000 +++ b/main.cpp Sat Feb 06 20:21:48 2016 +0000 @@ -685,7 +685,11 @@ class LwPwmOut: public LwOut { public: - LwPwmOut(PinName pin) : p(pin) { prv = 0; } + LwPwmOut(PinName pin, uint8_t initVal) : p(pin) + { + prv = initVal ^ 0xFF; + set(initVal); + } virtual void set(uint8_t val) { if (val != prv) @@ -699,7 +703,7 @@ class LwDigOut: public LwOut { public: - LwDigOut(PinName pin) : p(pin) { prv = 0; } + LwDigOut(PinName pin, uint8_t initVal) : p(pin, initVal ? 1 : 0) { prv = initVal; } virtual void set(uint8_t val) { if (val != prv) @@ -759,12 +763,12 @@ { case PortTypeGPIOPWM: // PWM GPIO port - lwp = new LwPwmOut(wirePinName(pin)); + lwp = new LwPwmOut(wirePinName(pin), activeLow ? 255 : 0); break; case PortTypeGPIODig: // Digital GPIO port - lwp = new LwDigOut(wirePinName(pin)); + lwp = new LwDigOut(wirePinName(pin), activeLow ? 255 : 0); break; case PortTypeTLC5940: @@ -814,6 +818,7 @@ break; case PortTypeVirtual: + case PortTypeDisabled: default: // virtual or unknown lwp = new LwVirtualOut(); @@ -2225,10 +2230,6 @@ // there's already a sensor object, we'll delete it. void createPlunger() { - // delete any existing sensor object - if (plungerSensor != 0) - delete plungerSensor; - // create the new sensor object according to the type switch (cfg.plunger.sensorType) { @@ -2674,7 +2675,8 @@ // clear the I2C bus (for the accelerometer) clear_i2c(); - // load the saved configuration + // load the saved configuration (or set factory defaults if no flash + // configuration has ever been saved) loadConfigFromFlash(); // initialize the diagnostic LEDs @@ -2862,6 +2864,8 @@ // start the first CCD integration cycle plungerSensor->init(); + Timer dbgTimer; dbgTimer.start(); // $$$ plunger debug report timer + // we're all set up - now just loop, processing sensor reports and // host requests for (;;) @@ -3476,6 +3480,17 @@ } } } + + // $$$ + if (dbgTimer.read() > 10) { + dbgTimer.reset(); + if (plungerSensor != 0 && (cfg.plunger.sensorType == PlungerType_TSL1410RS || cfg.plunger.sensorType == PlungerType_TSL1410RP)) + { + PlungerSensorTSL1410R *ps = (PlungerSensorTSL1410R *)plungerSensor; + printf("average plunger read time: %f ms (total=%f, n=%d)\r\n", ps->ccd.totalTime*1000.0 / ps->ccd.nRuns, ps->ccd.totalTime, ps->ccd.nRuns); + } + } + // end $$$ // provide a visual status indication on the on-board LED if (calBtnState < 2 && hbTimer.read_ms() > 1000)
--- a/plunger.h Wed Feb 03 22:57:25 2016 +0000 +++ b/plunger.h Sat Feb 06 20:21:48 2016 +0000 @@ -21,21 +21,23 @@ // a pixel coordinate in the image. But it's no longer the right word, // since we support sensor types that have nothing to do with imaging. // Even so, the function this serves is still applicable. Abstractly, - // it represents the physical resolution of the sensor, by giving the - // total number of quanta that the sensor can resolve over the entire - // range of travel of the plunger. For devices that inherently quantize - // the position reading at the physical level, such as imaging sensors - // and quadrature sensors, this should be set to the total number of - // quanta (resolvable position steps) over the range of travel. For - // devices with physically analog outputs, such as potentiometers or - // LVDTs, the reading still has to be digitized for us to be able to - // work with it, but this happens invisibly in the ADC, so the "pixel" - // scale is essentially arbitrary. Analog sensor types should thus - // simply use the maximum joystick report range, since that's the - // final scale we have to convert to - using a different scale would - // have no benefit and would just introduce rounding errors. + // it represents the physical resolution of the sensor in terms of + // the number of quanta over the full range of travel of the plunger. + // For sensors that inherently quantize the position reading at the + // physical level, such as imaging sensors and quadrature sensors, + // this should be set to the total number of position steps over the + // range of travel. For devices with physically analog outputs, such + // as potentiometers or LVDTs, the reading still has to be digitized + // for us to be able to work with it, which means it has to be turned + // into a value that's fundamentally an integer. But this happens in + // the ADC, so the quantization scale is hidden in the mbed libraries. + // The normal KL25Z ADC configuration is 16-bit quantization, so the + // quantization factor is usually 65535. But you might prefer to set + // this to the joystick maximum so that there are no more rounding + // errors in scale conversions after the point of initial conversion. // - // This value MUST be initialized in the constructor. + // IMPORTANT! This value MUST be initialized in the constructor for + // each concrete subclass. int npix; // Initialize the physical sensor device. This is called at startup
--- a/potSensor.h Wed Feb 03 22:57:25 2016 +0000 +++ b/potSensor.h Sat Feb 06 20:21:48 2016 +0000 @@ -1,9 +1,18 @@ // Potentiometer plunger sensor // // This file implements our generic plunger sensor interface for a -// potentiometer. +// 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. +// +// Although this class is nominally for potentiometers, it will also +// work with any other type of sensor that provides a single analog +// voltage level that maps linearly to the position, such as an LVDT. -#include "FastAnalogIn.h" class PlungerSensorPot: public PlungerSensor { @@ -46,9 +55,11 @@ { // Use an average of several readings. Note that even though this // is nominally a "low res" scan, we can still afford to take an - // average. The point of the low res interface is speed, and since - // we only have one analog value to read, we can afford to take - // several samples here even in the low res case. + // average. The point of the low res interface is to speed things + // up for the image sensor types, which have a large number of + // analog samples to read. In our case, we only have the one + // input to sample, so our normal scan is already so fast that + // there's no need to do anything different here. pos = int((pot.read() + pot.read() + pot.read())/3.0 * npix); return true; }