An I/O controller for virtual pinball machines: accelerometer nudge sensing, analog plunger input, button input encoding, LedWiz compatible output controls, and more.

Dependencies:   mbed FastIO FastPWM USBDevice

Fork of Pinscape_Controller by Mike R

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers tcd1103Sensor.h Source File

tcd1103Sensor.h

00001 // Toshiba TCD1103 linear image sensors
00002 //
00003 // This sensor is similar to the original TSL1410R in both its electronic
00004 // interface and the theory of operation.  The details of the electronics
00005 // are different enough that we can't reuse the same code at the hardware
00006 // interface level, but the principle of operation is similar: the sensor
00007 // provides a serial interface to a file of pixels transferred as analog
00008 // voltage levels representing the charge collected.  
00009 //
00010 // As with the TSL1410R, we position the sensor so that the pixel row is
00011 // aligned with the plunger axis, and we detect the plunger position by
00012 // looking for a dark/light edge at the end of the plunger.  However, 
00013 // the optics for this sensor are very different because of the sensor's
00014 // size.  The TSL1410R is by some magical coincidence the same size as
00015 // the plunger travel range, so we set that sensor up so that the plunger
00016 // is backlit with respect to the sensor, and simply casts a shadow on
00017 // the sensor.  The TCD1103, in contrast, has a pixel array that's only 
00018 // 8mm long, so we can't use the direct shadow approach.  Instead, we
00019 // have to use a lens to focus an image of the plunger on the sensor.
00020 // With a focused image, we can front-light the plunger and take a picture
00021 // of the plunger itself rather than of an occluded back-light.
00022 //
00023 // Even though we use "edge sensing", this class isn't based on the
00024 // PlungerSensorEdgePos class.  Our sensing algorithm is a little different,
00025 // and much simpler, because we're working with a proper image of the
00026 // plunger, rather than an image of its shadow.  The shadow tends to be
00027 // rather fuzzy, and the TSL14xx sensors were pretty noisy, so we had to
00028 // work fairly hard to distinguish an edge in the image from a noise spike.
00029 // This sensor has very low noise, and the focused image produces a sharp
00030 // edge, so we can use a more straightforward algorithm that just looks
00031 // for the first bright spot.
00032 //
00033 // The TCD1103 uses a negative image: brighter pixels are represented by 
00034 // lower numbers.  The electronics of the sensor are such that the dynamic
00035 // range for the pixel analag voltage signal (which is what our pixel 
00036 // elements represent) is only about 1V, or about 30% of the 3.3V range of
00037 // the ADC.  Dark pixels read at about 2V (about 167 after 8-bit ADC 
00038 // quantization), and saturated pixels read at 1V (78 on the ADC).  So our
00039 // effective dynamic range after quantization is about 100 steps.  That 
00040 // would be pretty terrible if the goal were to take pictures for an art
00041 // gallery, and there are things we could do in the electronic interface 
00042 // to improve it.  In particular, we could use an op-amp to expand the 
00043 // voltage range on the ADC input and remove the DC offset, so that the
00044 // signal going into the ADC covers the ADC's full 0V - 3.3V range.  That
00045 // technique is actually used in some other projects using this sensor
00046 // where the goal is to yield pictures as the end result.  But it's
00047 // pretty complicated to set up and fine-tune to get the voltage range
00048 // expansion just right, and we really don't need it; the edge detection
00049 // works fine with what we get directly from the sensor.
00050 
00051 
00052 
00053 #include "plunger.h"
00054 #include "TCD1103.h"
00055 
00056 template <bool invertedLogicGates>
00057 class PlungerSensorImageInterfaceTCD1103: public PlungerSensorImageInterface
00058 {
00059 public:
00060     PlungerSensorImageInterfaceTCD1103(PinName fm, PinName os, PinName icg, PinName sh)
00061         : PlungerSensorImageInterface(1546), sensor(fm, os, icg, sh)
00062     {
00063     }
00064 
00065     // is the sensor ready?
00066     virtual bool ready() { return sensor.ready(); }
00067     
00068     virtual void init() { }
00069     
00070     // get the average sensor scan time
00071     virtual uint32_t getAvgScanTime() { return sensor.getAvgScanTime(); }
00072     
00073     virtual void readPix(uint8_t* &pix, uint32_t &t)
00074     {        
00075         // get the image array from the last capture
00076         sensor.getPix(pix, t);
00077     }
00078     
00079     virtual void releasePix() { sensor.releasePix(); }
00080     
00081     virtual void setMinIntTime(uint32_t us) { sensor.setMinIntTime(us); }
00082 
00083     // the low-level interface to the TSL14xx sensor
00084     TCD1103<invertedLogicGates> sensor;
00085 };
00086 
00087 template<bool invertedLogicGates>
00088 class PlungerSensorTCD1103: public PlungerSensorImage<int>
00089 {
00090 public:
00091     PlungerSensorTCD1103(PinName fm, PinName os, PinName icg, PinName sh)
00092         : PlungerSensorImage(sensor, 1546, 1545, true), sensor(fm, os, icg, sh)
00093     {
00094     }
00095     
00096 protected:
00097     // Process an image.  This seeks the first dark-to-light edge in the image.
00098     // We assume that the background (open space behind the plunger) has a
00099     // dark (minimally reflective) backdrop, and that the tip of the plunger
00100     // has a bright white strip right at the end.  So the end of the plunger
00101     // should be easily identifiable in the image as the first bright edge
00102     // we see starting at the "far" end.
00103     virtual bool process(const uint8_t *pix, int n, int &pos, int& /*processResult*/)
00104     {
00105         // The TCD1103's pixel file that it reports on the wire has the 
00106         // following internal structure:
00107         //
00108         //   16 dummy elements, fixed at the dark charge level
00109         //   13 light-shielded pixels (live pixels, covered with a shade in the sensor)
00110         //   3 dummy "buffer" pixels (to allow for variation in shade alignment)
00111         //   1500 image pixels
00112         //   14 dummy elements (the data sheet doesn't say exactly what these are physically)
00113         //
00114         // The sensor holds the 16 dummy elements at the dark charge level,
00115         // so they provide a reference point for the darkest reading possible.
00116         // The light-shielded pixels serve essentially the same purpose, in
00117         // that they *also* should read out at the dark charge level.  But
00118         // the shaded pixels can be also used for diagnostics, to distinguish
00119         // between problems in the CCD proper and problems in the interface
00120         // electronics.  If the dummy elements are reading at the dark level
00121         // but the shielded pixels aren't, you have a CCD problem; if the
00122         // dummy pixels aren't reading at the dark level, the interface
00123         // electronics are suspect.
00124         //
00125         // For our purposes, we can simply ignore the dummy pixels at either
00126         // end.  The diagnostic status report for the Config Tool sends the
00127         // full view including the dummy pixels, so any diagnostics that the
00128         // user wants to do using the dummy pixels can be done on the PC side.
00129         //
00130         // Deduct the dummy pixels so that we only scan the true image
00131         // pixels in our search for the plunger edge.
00132         int startOfs = 32;
00133         n -= 32 + 14;
00134         
00135         // Scan the pixel array to determine the actual dynamic range 
00136         // of this image.  That will let us determine what consistutes
00137         // "bright" when we're looking for the bright spot.
00138         uint8_t pixMin = 255, pixMax = 0;
00139         const uint8_t *p = pix + startOfs;
00140         for (int i = n; i != 0; --i)
00141         {
00142             uint8_t c = *p++;
00143             if (c < pixMin) pixMin = c;
00144             if (c > pixMax) pixMax = c;
00145         }
00146         
00147         // Figure the threshold brightness for the bright spot as halfway
00148         // between the min and max.
00149         uint8_t threshold = (pixMin + pixMax)/2;
00150         
00151         // Scan for the first bright-enough pixel.  Remember that we're
00152         // working with a negative image, so "brighter" is "less than".
00153         p = pix + startOfs;
00154         for (int i = n; i != 0; --i, ++p)
00155         {
00156             if (*p < threshold)
00157             {
00158                 // got it - report this position
00159                 pos = p - pix;
00160                 return true;
00161             }
00162         }
00163         
00164         // no edge found - report failure
00165         return false;
00166     }
00167     
00168     // Use a fixed orientation for this sensor.  The shadow-edge sensors
00169     // try to infer the direction by checking which end of the image is
00170     // brighter, which works well for the shadow sensors because the back
00171     // end of the image will always be in shadow.  But for this sensor,
00172     // we're taking an image of the plunger (not its shadow), and the
00173     // back end of the plunger is the part with the spring, which has a
00174     // fuzzy and complex reflectivity pattern because of the spring.
00175     // So for this sensor, it's better to insist that the user sets it
00176     // up in a canonical orientation.  That's a reasaonble expectation
00177     // for this sensor anyway, because the physical installation won't
00178     // be as ad hoc as the TSL1410R setup, which only required that you
00179     // mounted the sensor itself.  In this case, you have to build a
00180     // circuit board and mount a lens on it, so it's reasonable to
00181     // expect that everyone will be using the mounting apparatus plans
00182     // that we'll detail in the build guide.  In any case, we'll just
00183     // make it clear in the instructions that you have to mount the
00184     // sensor in a certain orientation.
00185     virtual int getOrientation() const { return 1; }
00186 
00187     // the hardware sensor interface
00188     PlungerSensorImageInterfaceTCD1103<invertedLogicGates> sensor;
00189 };