Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: mbed FastIO FastPWM USBDevice
Diff: Plunger/tcd1103Sensor.h
- Revision:
- 104:6e06e0f4b476
- Parent:
- 101:755f44622abc
- Child:
- 106:e9e3b46132c1
diff -r dec22cd65b2a -r 6e06e0f4b476 Plunger/tcd1103Sensor.h --- a/Plunger/tcd1103Sensor.h Tue Dec 03 19:10:52 2019 +0000 +++ b/Plunger/tcd1103Sensor.h Fri Dec 27 20:14:23 2019 +0000 @@ -8,37 +8,54 @@ // voltage levels representing the charge collected. // // As with the TSL1410R, we position the sensor so that the pixel row is -// aligned with the plunger axis, with a backlight, and we detect the plunger -// position by looking for an edge between a light area (where the backlight -// is unobstructed) and a dark area (where the plunger rod is blocking the -// backlight). The optical sensor area of the TSL1410R is large enough to -// cover the entire plunger travel distance, so the physical setup for that -// sensor is a simple matter of placing the sensor near the plunger, so that -// the plunger casts a shadow on the sensor. The TCD1103, in contrast, has a -// small optical sensor area, about 8mm long, so in this case we have to use -// a lens to reduce the image of the plunger by about 10X (from the 80mm -// plunger travel distance to the 8mm sensor size). This makes the physical -// setup more complex, but it has the advantage of giving us a focused image, -// allowing for better precision in detecting the edge. With the unfocused -// image used in the TSL1410R setup, the shadow was blurry over about 1/50". -// With a lens to focus the image, we could potentially get as good as -// single-pixel resolution, which would give us about 1/500" resolution on -// this 1500-pixel sensor. +// aligned with the plunger axis, and we detect the plunger position by +// looking for a dark/light edge at the end of the plunger. However, +// the optics for this sensor are very different because of the sensor's +// size. The TSL1410R is by some magical coincidence the same size as +// the plunger travel range, so we set that sensor up so that the plunger +// is backlit with respect to the sensor, and simply casts a shadow on +// the sensor. The TCD1103, in contrast, has a pixel array that's only +// 8mm long, so we can't use the direct shadow approach. Instead, we +// have to use a lens to focus an image of the plunger on the sensor. +// With a focused image, we can front-light the plunger and take a picture +// of the plunger itself rather than of an occluded back-light. // +// Even though we use "edge sensing", this class isn't based on the +// PlungerSensorEdgePos class. Our sensing algorithm is a little different, +// and much simpler, because we're working with a proper image of the +// plunger, rather than an image of its shadow. The shadow tends to be +// rather fuzzy, and the TSL14xx sensors were pretty noisy, so we had to +// work fairly hard to distinguish an edge in the image from a noise spike. +// This sensor has very low noise, and the focused image produces a sharp +// edge, so we can use a more straightforward algorithm that just looks +// for the first bright spot. +// +// The TCD1103 uses a negative image: brighter pixels are represented by +// lower numbers. The electronics of the sensor are such that the dynamic +// range for the pixel analag voltage signal (which is what our pixel +// elements represent) is only about 1V, or about 30% of the 3.3V range of +// the ADC. Dark pixels read at about 2V (about 167 after 8-bit ADC +// quantization), and saturated pixels read at 1V (78 on the ADC). So our +// effective dynamic range after quantization is about 100 steps. That +// would be pretty terrible if the goal were to take pictures for an art +// gallery, and there are things we could do in the electronic interface +// to improve it; in particular, we could use an op-amp to expand the +// voltage range on the ADC input and remove the DC offset, so that the +// signal going into the ADC covers the ADC's full 0V - 3.3V range. But +// there's need for that for our purposes here; 100 steps is perfectly +// adequate for our edge sensing, especially given the sensor's low +// noise level. -#include "edgeSensor.h" + +#include "plunger.h" #include "TCD1103.h" template <bool invertedLogicGates> class PlungerSensorImageInterfaceTCD1103: public PlungerSensorImageInterface { public: - // Note that the TCD1103 has 1500 actual image pixels, but the serial - // interface provides 32 dummy elements on the front end (before the - // first image pixel) and 14 dummy elements on the back end (after the - // last image pixel), for a total of 1546 serial outputs. PlungerSensorImageInterfaceTCD1103(PinName fm, PinName os, PinName icg, PinName sh) - : PlungerSensorImageInterface(1546), sensor(fm, os, icg, sh) + : PlungerSensorImageInterface(1500), sensor(fm, os, icg, sh) { } @@ -53,7 +70,7 @@ virtual void readPix(uint8_t* &pix, uint32_t &t) { // get the image array from the last capture - sensor.getPix(pix, t); + sensor.getPix(pix, t); } virtual void releasePix() { sensor.releasePix(); } @@ -65,18 +82,75 @@ }; template<bool invertedLogicGates> -class PlungerSensorTCD1103: public PlungerSensorEdgePos +class PlungerSensorTCD1103: public PlungerSensorImage<int> { public: - // Note that the TCD1103 has 1500 actual image pixels, but the serial - // interface provides 32 dummy elements on the front end (before the - // first image pixel) and 14 dummy elements on the back end (after the - // last image pixel), for a total of 1546 serial outputs. PlungerSensorTCD1103(PinName fm, PinName os, PinName icg, PinName sh) - : PlungerSensorEdgePos(sensor, 1546), sensor(fm, os, icg, sh) + : PlungerSensorImage(sensor, 1500, 1499, true), sensor(fm, os, icg, sh) { } protected: + // Process an image. This seeks the first dark-to-light edge in the image. + // We assume that the background (open space behind the plunger) has a + // dark (minimally reflective) backdrop, and that the tip of the plunger + // has a bright white strip right at the end. So the end of the plunger + // should be easily identifiable in the image as the first bright edge + // we see starting at the "far" end. + virtual bool process(const uint8_t *pix, int n, int &pos, int& /*processResult*/) + { + // Scan the pixel array to determine the actual dynamic range + // of this image. That will let us determine what consistutes + // "bright" when we're looking for the bright spot. + uint8_t pixMin = 255, pixMax = 0; + const uint8_t *p = pix; + for (int i = n; i != 0; --i) + { + uint8_t c = *p++; + if (c < pixMin) pixMin = c; + if (c > pixMax) pixMax = c; + } + + // Figure the threshold brightness for the bright spot as halfway + // between the min and max. + uint8_t threshold = (pixMin + pixMax)/2; + + // Scan for the first bright-enough pixel. Remember that we're + // working with a negative image, so "brighter" is "less than". + p = pix; + for (int i = n; i != 0; --i, ++p) + { + if (*p < threshold) + { + // got it - report this position + pos = p - pix; + return true; + } + } + + // no edge found - report failure + return false; + } + + // Use a fixed orientation for this sensor. The shadow-edge sensors + // try to infer the direction by checking which end of the image is + // brighter, which works well for the shadow sensors because the back + // end of the image will always be in shadow. But for this sensor, + // we're taking an image of the plunger (not its shadow), and the + // back end of the plunger is the part with the spring, which has a + // fuzzy and complex reflectivity pattern because of the spring. + // So for this sensor, it's better to insist that the user sets it + // up in a canonical orientation. That's a reasaonble expectation + // for this sensor anyway, because the physical installation won't + // be as ad hoc as the TSL1410R setup, which only required that you + // mounted the sensor itself. In this case, you have to build a + // circuit board and mount a lens on it, so it's reasonable to + // expect that everyone will be using the mounting apparatus plans + // that we'll detail in the build guide. In any case, we'll just + // make it clear in the instructions that you have to mount the + // sensor in a certain orientation. + virtual int getOrientation() const { return 1; } + + // the hardware sensor interface PlungerSensorImageInterfaceTCD1103<invertedLogicGates> sensor; };