Mirror with some correction

Dependencies:   mbed FastIO FastPWM USBDevice

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

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mjr 100:1ff35c07217c 1 // Toshiba TCD1103 linear image sensors
mjr 100:1ff35c07217c 2 //
mjr 100:1ff35c07217c 3 // This sensor is similar to the original TSL1410R in both its electronic
mjr 100:1ff35c07217c 4 // interface and the theory of operation. The details of the electronics
mjr 100:1ff35c07217c 5 // are different enough that we can't reuse the same code at the hardware
mjr 100:1ff35c07217c 6 // interface level, but the principle of operation is similar: the sensor
mjr 100:1ff35c07217c 7 // provides a serial interface to a file of pixels transferred as analog
mjr 100:1ff35c07217c 8 // voltage levels representing the charge collected.
mjr 100:1ff35c07217c 9 //
mjr 100:1ff35c07217c 10 // As with the TSL1410R, we position the sensor so that the pixel row is
mjr 104:6e06e0f4b476 11 // aligned with the plunger axis, and we detect the plunger position by
mjr 104:6e06e0f4b476 12 // looking for a dark/light edge at the end of the plunger. However,
mjr 104:6e06e0f4b476 13 // the optics for this sensor are very different because of the sensor's
mjr 104:6e06e0f4b476 14 // size. The TSL1410R is by some magical coincidence the same size as
mjr 104:6e06e0f4b476 15 // the plunger travel range, so we set that sensor up so that the plunger
mjr 104:6e06e0f4b476 16 // is backlit with respect to the sensor, and simply casts a shadow on
mjr 104:6e06e0f4b476 17 // the sensor. The TCD1103, in contrast, has a pixel array that's only
mjr 104:6e06e0f4b476 18 // 8mm long, so we can't use the direct shadow approach. Instead, we
mjr 104:6e06e0f4b476 19 // have to use a lens to focus an image of the plunger on the sensor.
mjr 104:6e06e0f4b476 20 // With a focused image, we can front-light the plunger and take a picture
mjr 104:6e06e0f4b476 21 // of the plunger itself rather than of an occluded back-light.
mjr 100:1ff35c07217c 22 //
mjr 104:6e06e0f4b476 23 // Even though we use "edge sensing", this class isn't based on the
mjr 104:6e06e0f4b476 24 // PlungerSensorEdgePos class. Our sensing algorithm is a little different,
mjr 104:6e06e0f4b476 25 // and much simpler, because we're working with a proper image of the
mjr 104:6e06e0f4b476 26 // plunger, rather than an image of its shadow. The shadow tends to be
mjr 104:6e06e0f4b476 27 // rather fuzzy, and the TSL14xx sensors were pretty noisy, so we had to
mjr 104:6e06e0f4b476 28 // work fairly hard to distinguish an edge in the image from a noise spike.
mjr 104:6e06e0f4b476 29 // This sensor has very low noise, and the focused image produces a sharp
mjr 104:6e06e0f4b476 30 // edge, so we can use a more straightforward algorithm that just looks
mjr 104:6e06e0f4b476 31 // for the first bright spot.
mjr 104:6e06e0f4b476 32 //
mjr 104:6e06e0f4b476 33 // The TCD1103 uses a negative image: brighter pixels are represented by
mjr 104:6e06e0f4b476 34 // lower numbers. The electronics of the sensor are such that the dynamic
mjr 104:6e06e0f4b476 35 // range for the pixel analag voltage signal (which is what our pixel
mjr 104:6e06e0f4b476 36 // elements represent) is only about 1V, or about 30% of the 3.3V range of
mjr 104:6e06e0f4b476 37 // the ADC. Dark pixels read at about 2V (about 167 after 8-bit ADC
mjr 104:6e06e0f4b476 38 // quantization), and saturated pixels read at 1V (78 on the ADC). So our
mjr 104:6e06e0f4b476 39 // effective dynamic range after quantization is about 100 steps. That
mjr 104:6e06e0f4b476 40 // would be pretty terrible if the goal were to take pictures for an art
mjr 104:6e06e0f4b476 41 // gallery, and there are things we could do in the electronic interface
mjr 106:e9e3b46132c1 42 // to improve it. In particular, we could use an op-amp to expand the
mjr 104:6e06e0f4b476 43 // voltage range on the ADC input and remove the DC offset, so that the
mjr 106:e9e3b46132c1 44 // signal going into the ADC covers the ADC's full 0V - 3.3V range. That
mjr 106:e9e3b46132c1 45 // technique is actually used in some other projects using this sensor
mjr 106:e9e3b46132c1 46 // where the goal is to yield pictures as the end result. But it's
mjr 106:e9e3b46132c1 47 // pretty complicated to set up and fine-tune to get the voltage range
mjr 106:e9e3b46132c1 48 // expansion just right, and we really don't need it; the edge detection
mjr 106:e9e3b46132c1 49 // works fine with what we get directly from the sensor.
mjr 106:e9e3b46132c1 50
mjr 100:1ff35c07217c 51
mjr 104:6e06e0f4b476 52
mjr 104:6e06e0f4b476 53 #include "plunger.h"
mjr 100:1ff35c07217c 54 #include "TCD1103.h"
mjr 100:1ff35c07217c 55
mjr 100:1ff35c07217c 56 template <bool invertedLogicGates>
mjr 100:1ff35c07217c 57 class PlungerSensorImageInterfaceTCD1103: public PlungerSensorImageInterface
mjr 100:1ff35c07217c 58 {
mjr 100:1ff35c07217c 59 public:
mjr 100:1ff35c07217c 60 PlungerSensorImageInterfaceTCD1103(PinName fm, PinName os, PinName icg, PinName sh)
mjr 109:310ac82cbbee 61 : PlungerSensorImageInterface(1546), sensor(fm, os, icg, sh)
mjr 100:1ff35c07217c 62 {
mjr 100:1ff35c07217c 63 }
mjr 100:1ff35c07217c 64
mjr 100:1ff35c07217c 65 // is the sensor ready?
mjr 100:1ff35c07217c 66 virtual bool ready() { return sensor.ready(); }
mjr 100:1ff35c07217c 67
mjr 101:755f44622abc 68 virtual void init() { }
mjr 100:1ff35c07217c 69
mjr 100:1ff35c07217c 70 // get the average sensor scan time
mjr 100:1ff35c07217c 71 virtual uint32_t getAvgScanTime() { return sensor.getAvgScanTime(); }
mjr 100:1ff35c07217c 72
mjr 101:755f44622abc 73 virtual void readPix(uint8_t* &pix, uint32_t &t)
mjr 100:1ff35c07217c 74 {
mjr 100:1ff35c07217c 75 // get the image array from the last capture
mjr 104:6e06e0f4b476 76 sensor.getPix(pix, t);
mjr 100:1ff35c07217c 77 }
mjr 100:1ff35c07217c 78
mjr 101:755f44622abc 79 virtual void releasePix() { sensor.releasePix(); }
mjr 101:755f44622abc 80
mjr 101:755f44622abc 81 virtual void setMinIntTime(uint32_t us) { sensor.setMinIntTime(us); }
mjr 100:1ff35c07217c 82
mjr 100:1ff35c07217c 83 // the low-level interface to the TSL14xx sensor
mjr 100:1ff35c07217c 84 TCD1103<invertedLogicGates> sensor;
mjr 100:1ff35c07217c 85 };
mjr 100:1ff35c07217c 86
mjr 100:1ff35c07217c 87 template<bool invertedLogicGates>
mjr 104:6e06e0f4b476 88 class PlungerSensorTCD1103: public PlungerSensorImage<int>
mjr 100:1ff35c07217c 89 {
mjr 100:1ff35c07217c 90 public:
mjr 100:1ff35c07217c 91 PlungerSensorTCD1103(PinName fm, PinName os, PinName icg, PinName sh)
mjr 109:310ac82cbbee 92 : PlungerSensorImage(sensor, 1546, 1545, true), sensor(fm, os, icg, sh)
mjr 100:1ff35c07217c 93 {
mjr 100:1ff35c07217c 94 }
mjr 100:1ff35c07217c 95
mjr 100:1ff35c07217c 96 protected:
mjr 104:6e06e0f4b476 97 // Process an image. This seeks the first dark-to-light edge in the image.
mjr 104:6e06e0f4b476 98 // We assume that the background (open space behind the plunger) has a
mjr 104:6e06e0f4b476 99 // dark (minimally reflective) backdrop, and that the tip of the plunger
mjr 104:6e06e0f4b476 100 // has a bright white strip right at the end. So the end of the plunger
mjr 104:6e06e0f4b476 101 // should be easily identifiable in the image as the first bright edge
mjr 104:6e06e0f4b476 102 // we see starting at the "far" end.
mjr 104:6e06e0f4b476 103 virtual bool process(const uint8_t *pix, int n, int &pos, int& /*processResult*/)
mjr 104:6e06e0f4b476 104 {
mjr 109:310ac82cbbee 105 // The TCD1103's pixel file that it reports on the wire has the
mjr 109:310ac82cbbee 106 // following internal structure:
mjr 109:310ac82cbbee 107 //
mjr 109:310ac82cbbee 108 // 16 dummy elements, fixed at the dark charge level
mjr 109:310ac82cbbee 109 // 13 light-shielded pixels (live pixels, covered with a shade in the sensor)
mjr 109:310ac82cbbee 110 // 3 dummy "buffer" pixels (to allow for variation in shade alignment)
mjr 109:310ac82cbbee 111 // 1500 image pixels
mjr 109:310ac82cbbee 112 // 14 dummy elements (the data sheet doesn't say exactly what these are physically)
mjr 109:310ac82cbbee 113 //
mjr 109:310ac82cbbee 114 // The sensor holds the 16 dummy elements at the dark charge level,
mjr 109:310ac82cbbee 115 // so they provide a reference point for the darkest reading possible.
mjr 109:310ac82cbbee 116 // The light-shielded pixels serve essentially the same purpose, in
mjr 109:310ac82cbbee 117 // that they *also* should read out at the dark charge level. But
mjr 109:310ac82cbbee 118 // the shaded pixels can be also used for diagnostics, to distinguish
mjr 109:310ac82cbbee 119 // between problems in the CCD proper and problems in the interface
mjr 109:310ac82cbbee 120 // electronics. If the dummy elements are reading at the dark level
mjr 109:310ac82cbbee 121 // but the shielded pixels aren't, you have a CCD problem; if the
mjr 109:310ac82cbbee 122 // dummy pixels aren't reading at the dark level, the interface
mjr 109:310ac82cbbee 123 // electronics are suspect.
mjr 109:310ac82cbbee 124 //
mjr 109:310ac82cbbee 125 // For our purposes, we can simply ignore the dummy pixels at either
mjr 109:310ac82cbbee 126 // end. The diagnostic status report for the Config Tool sends the
mjr 109:310ac82cbbee 127 // full view including the dummy pixels, so any diagnostics that the
mjr 109:310ac82cbbee 128 // user wants to do using the dummy pixels can be done on the PC side.
mjr 109:310ac82cbbee 129 //
mjr 109:310ac82cbbee 130 // Deduct the dummy pixels so that we only scan the true image
mjr 109:310ac82cbbee 131 // pixels in our search for the plunger edge.
mjr 109:310ac82cbbee 132 int startOfs = 32;
mjr 109:310ac82cbbee 133 n -= 32 + 14;
mjr 109:310ac82cbbee 134
mjr 104:6e06e0f4b476 135 // Scan the pixel array to determine the actual dynamic range
mjr 104:6e06e0f4b476 136 // of this image. That will let us determine what consistutes
mjr 104:6e06e0f4b476 137 // "bright" when we're looking for the bright spot.
mjr 104:6e06e0f4b476 138 uint8_t pixMin = 255, pixMax = 0;
mjr 109:310ac82cbbee 139 const uint8_t *p = pix + startOfs;
mjr 104:6e06e0f4b476 140 for (int i = n; i != 0; --i)
mjr 104:6e06e0f4b476 141 {
mjr 104:6e06e0f4b476 142 uint8_t c = *p++;
mjr 104:6e06e0f4b476 143 if (c < pixMin) pixMin = c;
mjr 104:6e06e0f4b476 144 if (c > pixMax) pixMax = c;
mjr 104:6e06e0f4b476 145 }
mjr 104:6e06e0f4b476 146
mjr 104:6e06e0f4b476 147 // Figure the threshold brightness for the bright spot as halfway
mjr 104:6e06e0f4b476 148 // between the min and max.
mjr 104:6e06e0f4b476 149 uint8_t threshold = (pixMin + pixMax)/2;
mjr 104:6e06e0f4b476 150
mjr 104:6e06e0f4b476 151 // Scan for the first bright-enough pixel. Remember that we're
mjr 104:6e06e0f4b476 152 // working with a negative image, so "brighter" is "less than".
mjr 109:310ac82cbbee 153 p = pix + startOfs;
mjr 104:6e06e0f4b476 154 for (int i = n; i != 0; --i, ++p)
mjr 104:6e06e0f4b476 155 {
mjr 104:6e06e0f4b476 156 if (*p < threshold)
mjr 104:6e06e0f4b476 157 {
mjr 104:6e06e0f4b476 158 // got it - report this position
mjr 104:6e06e0f4b476 159 pos = p - pix;
mjr 104:6e06e0f4b476 160 return true;
mjr 104:6e06e0f4b476 161 }
mjr 104:6e06e0f4b476 162 }
mjr 104:6e06e0f4b476 163
mjr 104:6e06e0f4b476 164 // no edge found - report failure
mjr 104:6e06e0f4b476 165 return false;
mjr 104:6e06e0f4b476 166 }
mjr 104:6e06e0f4b476 167
mjr 104:6e06e0f4b476 168 // Use a fixed orientation for this sensor. The shadow-edge sensors
mjr 104:6e06e0f4b476 169 // try to infer the direction by checking which end of the image is
mjr 104:6e06e0f4b476 170 // brighter, which works well for the shadow sensors because the back
mjr 104:6e06e0f4b476 171 // end of the image will always be in shadow. But for this sensor,
mjr 104:6e06e0f4b476 172 // we're taking an image of the plunger (not its shadow), and the
mjr 104:6e06e0f4b476 173 // back end of the plunger is the part with the spring, which has a
mjr 104:6e06e0f4b476 174 // fuzzy and complex reflectivity pattern because of the spring.
mjr 104:6e06e0f4b476 175 // So for this sensor, it's better to insist that the user sets it
mjr 104:6e06e0f4b476 176 // up in a canonical orientation. That's a reasaonble expectation
mjr 104:6e06e0f4b476 177 // for this sensor anyway, because the physical installation won't
mjr 104:6e06e0f4b476 178 // be as ad hoc as the TSL1410R setup, which only required that you
mjr 104:6e06e0f4b476 179 // mounted the sensor itself. In this case, you have to build a
mjr 104:6e06e0f4b476 180 // circuit board and mount a lens on it, so it's reasonable to
mjr 104:6e06e0f4b476 181 // expect that everyone will be using the mounting apparatus plans
mjr 104:6e06e0f4b476 182 // that we'll detail in the build guide. In any case, we'll just
mjr 104:6e06e0f4b476 183 // make it clear in the instructions that you have to mount the
mjr 104:6e06e0f4b476 184 // sensor in a certain orientation.
mjr 104:6e06e0f4b476 185 virtual int getOrientation() const { return 1; }
mjr 104:6e06e0f4b476 186
mjr 104:6e06e0f4b476 187 // the hardware sensor interface
mjr 100:1ff35c07217c 188 PlungerSensorImageInterfaceTCD1103<invertedLogicGates> sensor;
mjr 100:1ff35c07217c 189 };