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
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 };
Generated on Wed Jul 13 2022 03:30:11 by 1.7.2