Mirror with some correction

Dependencies:   mbed FastIO FastPWM USBDevice

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

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mjr 104:6e06e0f4b476 1 // Toshiba TCD1103 linear CCD image sensor, 1x1500 pixels.
mjr 100:1ff35c07217c 2 //
mjr 100:1ff35c07217c 3 // This sensor is conceptually similar to the TAOS TSL1410R (the original
mjr 100:1ff35c07217c 4 // Pinscape sensor!). Like the TSL1410R, it has a linear array of optical
mjr 100:1ff35c07217c 5 // sensor pixels that convert incident photons into electrical charge, an
mjr 100:1ff35c07217c 6 // internal shift register connected to the pixel file that acts as an
mjr 100:1ff35c07217c 7 // electronic shutter, and a serial interface that clocks the pixels out
mjr 100:1ff35c07217c 8 // to the host in analog voltage level format.
mjr 100:1ff35c07217c 9 //
mjr 104:6e06e0f4b476 10 // The big physical difference between this sensor and the old TAOS sensors
mjr 104:6e06e0f4b476 11 // is the size. The TAOS sensors were (by some miracle) approximately the
mjr 104:6e06e0f4b476 12 // same size as the plunger travel range, so we were able to take "contact"
mjr 104:6e06e0f4b476 13 // images without any optics, by placing the plunger close to the sensor,
mjr 104:6e06e0f4b476 14 // back-lighting it, and essentially taking a picture of its shadow. The
mjr 104:6e06e0f4b476 15 // Toshiba sensor, in contrast, has a pixel window that's only 8mm long, so
mjr 104:6e06e0f4b476 16 // the contact image approach won't work. Instead, we have to use a lens
mjr 104:6e06e0f4b476 17 // to focus a reduced image (about 1:10 scale) on the sensor. That makes
mjr 104:6e06e0f4b476 18 // the physical setup more complex, but it has the great advantage that we
mjr 104:6e06e0f4b476 19 // get a focused image. The shadow was always fuzzy in the old contact
mjr 104:6e06e0f4b476 20 // image approach, which reduced the effective resolution when determining
mjr 104:6e06e0f4b476 21 // the plunger position. With a focused image, we can get single-pixel
mjr 104:6e06e0f4b476 22 // resolution. With this Toshiba sensor's 1500 pixels, that's about 500
mjr 104:6e06e0f4b476 23 // dpi, which beats every other sensor we've come up with.
mjr 100:1ff35c07217c 24 //
mjr 104:6e06e0f4b476 25 // The electronic interface to this sensor is similar to the TAOS, but it
mjr 104:6e06e0f4b476 26 // has enough differences that we can't share the same code base.
mjr 100:1ff35c07217c 27 //
mjr 104:6e06e0f4b476 28 // As with the 1410R, we have to use DMA for the ADC transfers in order
mjr 100:1ff35c07217c 29 // to keep up with the high data rate without overloading the KL25Z CPU.
mjr 100:1ff35c07217c 30 // With the 1410R, we're able to use the ADC itself as the clock source,
mjr 100:1ff35c07217c 31 // by running the ADC in continous mode and using its "sample ready" signal
mjr 100:1ff35c07217c 32 // to trigger the DMA transfer. We used this to generate the external clock
mjr 100:1ff35c07217c 33 // signal for the sensor by "linking" the ADC's DMA channel to another pair
mjr 100:1ff35c07217c 34 // of DMA channels that generated the clock up/down signal each time an ADC
mjr 100:1ff35c07217c 35 // sample completed. This strategy won't work with the Toshiba sensor,
mjr 100:1ff35c07217c 36 // though, because the Toshiba sensor's timing sequence requires *two* clock
mjr 100:1ff35c07217c 37 // pulses per pixel. I can't come up with a way to accomplish that with the
mjr 104:6e06e0f4b476 38 // linked-DMA approach. Instead, we'll have to generate a true clock signal
mjr 104:6e06e0f4b476 39 // for the sensor, and drive the DMA conversions off of that clock.
mjr 100:1ff35c07217c 40 //
mjr 104:6e06e0f4b476 41 // The obvious (and, as far as I can tell, only) way to generate the clock
mjr 104:6e06e0f4b476 42 // signal with the KL25Z at the high frequency required is to use a TPM -
mjr 104:6e06e0f4b476 43 // the KL25Z module that drives PWM outputs. TPM channels are designed
mjr 100:1ff35c07217c 44 // precisely for this kind of work, so this is the right approach in terms of
mjr 100:1ff35c07217c 45 // suitability, but it has the downside that TPM units are an extremely scarce
mjr 104:6e06e0f4b476 46 // resource on the KL25Z. We only have three of them to work with. Luckily,
mjr 100:1ff35c07217c 47 // the rest of the Pinscape software only requires two of them: one for the
mjr 100:1ff35c07217c 48 // IR transmitter (which uses a TPM channel to generate the 41-48 kHz carrier
mjr 100:1ff35c07217c 49 // wave used by nearly all consumer IR remotes), and one for the TLC5940
mjr 100:1ff35c07217c 50 // driver (which uses it to generate the grayscale clock signal). Note that
mjr 100:1ff35c07217c 51 // we also use PWM channels for feedback device output ports, but those don't
mjr 100:1ff35c07217c 52 // have any dependency on the TPM period - they'll work with whatever period
mjr 100:1ff35c07217c 53 // the underlying TPM is set to use. So the feedback output ports can all
mjr 100:1ff35c07217c 54 // happily use free channels on TPM units claimed by any of the dedicated
mjr 100:1ff35c07217c 55 // users (IR, TLC5940, and us).
mjr 100:1ff35c07217c 56 //
mjr 100:1ff35c07217c 57 // But what do we do about the 2:1 ratio between master clock pulses and ADC
mjr 100:1ff35c07217c 58 // samples? The "right" way would be to allocate a second TPM unit to
mjr 100:1ff35c07217c 59 // generate a second clock signal at half the frequency of the master clock,
mjr 100:1ff35c07217c 60 // and use that as the ADC trigger. But as we just said, we only have three
mjr 100:1ff35c07217c 61 // TPM units in the whole system, and two of them are already claimed for
mjr 104:6e06e0f4b476 62 // other uses, so we only have one unit available for our use here.
mjr 100:1ff35c07217c 63 //
mjr 100:1ff35c07217c 64 // Fortunately, we can make do with one TPM unit, by taking advantage of a
mjr 100:1ff35c07217c 65 // feature/quirk of the KL25Z ADC. The quirk lets us take ADC samples at
mjr 100:1ff35c07217c 66 // exactly half of the master clock rate, in perfect sync. The trick is to
mjr 100:1ff35c07217c 67 // pick a combination of master clock rate and ADC sample mode such that the
mjr 100:1ff35c07217c 68 // ADC conversion time is *almost but not quite* twice as long as the master
mjr 100:1ff35c07217c 69 // clock rate. With that combination of timings, we can trigger the ADC
mjr 100:1ff35c07217c 70 // from the TPM, and we'll get an ADC sample on exactly every other tick of
mjr 100:1ff35c07217c 71 // the master clock. The reason this works is that the KL25Z ADC ignores
mjr 100:1ff35c07217c 72 // hardware triggers (the TPM trigger is a hardware trigger) that occur when
mjr 100:1ff35c07217c 73 // a conversion is already in progress. So if the ADC sampling time is more
mjr 100:1ff35c07217c 74 // than one master clock period, the ADC will always be busy one clock tick
mjr 100:1ff35c07217c 75 // after a sample starts, so it'll ignore that first clock tick. But as
mjr 100:1ff35c07217c 76 // long as the sampling time is less than *two* master clock periods, the
mjr 100:1ff35c07217c 77 // ADC will always be ready again on the second tick. So we'll get one ADC
mjr 100:1ff35c07217c 78 // sample for every two master clock ticks, exactly as we need.
mjr 100:1ff35c07217c 79 //
mjr 104:6e06e0f4b476 80 // This is all possible because the ADC timing is deterministic, and runs on
mjr 104:6e06e0f4b476 81 // the same clock as the TPM. The KL25Z Subfamily Reference Manual explains
mjr 104:6e06e0f4b476 82 // how to calculate the ADC conversion time for a given combination of mode
mjr 104:6e06e0f4b476 83 // bits. So we just have to pick an ADC mode, calculate its conversion time,
mjr 104:6e06e0f4b476 84 // and then select a TPM period that's slightly more than 1/2 of the ADC
mjr 104:6e06e0f4b476 85 // conversion time.
mjr 104:6e06e0f4b476 86 //
mjr 111:42dc75fbe623 87 // I know this sounds like it should be prone to unpredictable timing bugs,
mjr 111:42dc75fbe623 88 // but it's actually 100% deterministic! It's truly deterministic because
mjr 111:42dc75fbe623 89 // the underlying clock for the TPM and ADC is shared. Intuitively, we
mjr 111:42dc75fbe623 90 // think of time-based processes as inherently stochastic because clocks
mjr 111:42dc75fbe623 91 // are never perfect, so if you have two time-based processes based on
mjr 111:42dc75fbe623 92 // separate clocks, the two processes are never perfectly in sync because
mjr 111:42dc75fbe623 93 // their separate clocks will drift slightly relative to one another. But
mjr 111:42dc75fbe623 94 // that's not what's going on here. The time-based processes we're talking
mjr 111:42dc75fbe623 95 // about are tied to the same underlying clock, so there's absolutely no
mjr 111:42dc75fbe623 96 // possibility of clock drift or phase shift or anything else that feeds
mjr 111:42dc75fbe623 97 // into that intuition about stochasticness in time-based processes. In
mjr 111:42dc75fbe623 98 // addition, the key ADC feature we're exploiting for the clock doubling -
mjr 111:42dc75fbe623 99 // that the ADC ignores hardware triggers during an active cycle - isn't
mjr 111:42dc75fbe623 100 // some accidental behavior we observed empirically. It's by design and
mjr 111:42dc75fbe623 101 // it's documented. We can count on it always being the case with this
mjr 111:42dc75fbe623 102 // ADC. Between those two factors (synchronous clock, designed and
mjr 111:42dc75fbe623 103 // documented ADC behavior), we can count on the timing being EXACTLY the
mjr 111:42dc75fbe623 104 // same on EVERY sample. We're not counting on being "lucky".
mjr 111:42dc75fbe623 105 //
mjr 110:bf332f824585 106 // Note that there are several other, similar Toshiba sensors with the same
mjr 110:bf332f824585 107 // electrical interface and almost the same signal timing, but with a 4:1
mjr 110:bf332f824585 108 // ratio between the master clock ticks and the pixel outputs. This code
mjr 110:bf332f824585 109 // could be adapted to those sensors using the same "trick" we use for the
mjr 110:bf332f824585 110 // 2:1 timing ratio, by choosing an ADC mode with a sampling rate that's
mjr 110:bf332f824585 111 // between 3*fM and 4*fM. That will make the ADC ignore the first three
mjr 110:bf332f824585 112 // master clocks in each cycle, triggering a new sample reading on every
mjr 110:bf332f824585 113 // fourth master clock tick, achieving the desired 4:1 ratio. We don't
mjr 110:bf332f824585 114 // provide an option for that because there are no such Toshiba sensors in
mjr 110:bf332f824585 115 // production that are of interest to us as plunger sensors, and because
mjr 110:bf332f824585 116 // the selection of a suitable fM timing and ADC mode are both dependent
mjr 110:bf332f824585 117 // on the constraints of your application, so it's not feasible to automate
mjr 110:bf332f824585 118 // the selection of either based on simple numeric parameters. If you want
mjr 110:bf332f824585 119 // to adapt the code, start by figuring out the range of fM timing you can
mjr 110:bf332f824585 120 // accept, then look at the KL25Z manual to work out the ADC cycle timing
mjr 110:bf332f824585 121 // for various modes with the properties you want. You can then adjust
mjr 110:bf332f824585 122 // either or both the fM timing and ADC settings until you find a suitable
mjr 110:bf332f824585 123 // balance in the timing. The Toshiba sensors can generally accept a wide
mjr 110:bf332f824585 124 // range of fM rates, so you can count both the clock rate and ADC modes as
mjr 110:bf332f824585 125 // free variables, within the constraints of your application in terms of
mjr 110:bf332f824585 126 // required frame rate and ADC sampling quality.
mjr 110:bf332f824585 127 //
mjr 104:6e06e0f4b476 128 //
mjr 104:6e06e0f4b476 129 // Pixel output signal
mjr 104:6e06e0f4b476 130 //
mjr 104:6e06e0f4b476 131 // The pixel output signal from this sensor is an analog voltage level. It's
mjr 104:6e06e0f4b476 132 // inverted from the brightness: higher brightness is represented by lower
mjr 110:bf332f824585 133 // voltage. The dynamic range is only about 1V, with a 1V floor. So dark
mjr 110:bf332f824585 134 // pixels read at about 2V, and saturated pixels read at about 1V.
mjr 110:bf332f824585 135 //
mjr 110:bf332f824585 136 // The output pin from the sensor connects to what is essentially a very
mjr 110:bf332f824585 137 // small capacitor containing a tiny amount of charge. This isn't a good
mjr 110:bf332f824585 138 // source for the ADC to sample, so some additional circuitry is required
mjr 110:bf332f824585 139 // to convert the charge to a low-impedance voltage source suitable for
mjr 110:bf332f824585 140 // connecting to an ADC. The driver circuit recommended in the Toshiba
mjr 110:bf332f824585 141 // data sheet consists of a high-gain PNP transistor and a few resistors.
mjr 110:bf332f824585 142 // See "How to connect to the KL25Z" below for the component types and
mjr 110:bf332f824585 143 // values we've tested successfully.
mjr 104:6e06e0f4b476 144 //
mjr 104:6e06e0f4b476 145 //
mjr 104:6e06e0f4b476 146 // Inverted logic signals
mjr 104:6e06e0f4b476 147 //
mjr 104:6e06e0f4b476 148 // The Toshiba data sheet recommends buffering the logic signal inputs from
mjr 104:6e06e0f4b476 149 // an MCU through a 74HC04 inverter, because the sensor's logic gates have
mjr 104:6e06e0f4b476 150 // relatively high input capacitance that an MCU might not be able to drive
mjr 104:6e06e0f4b476 151 // fast enough directly to keep up with the sensor's timing requirements.
mjr 104:6e06e0f4b476 152 // SH in particular might be a problem because of its 150pF capacitance,
mjr 104:6e06e0f4b476 153 // which implies about a 2us rise/fall time if driven directly by KL25Z
mjr 104:6e06e0f4b476 154 // GPIOs, which is too slow.
mjr 104:6e06e0f4b476 155 //
mjr 110:bf332f824585 156 // The software will work with or without the logic inversion, in case anyone
mjr 104:6e06e0f4b476 157 // wants to try implementing it with direct GPIO drive (not recommended) or
mjr 104:6e06e0f4b476 158 // with a non-inverting buffer in place of the 74HC04. Simply instantiate the
mjr 104:6e06e0f4b476 159 // class with the 'invertedLogicGates' template parameter set to false to use
mjr 104:6e06e0f4b476 160 // non-inverted logic.
mjr 104:6e06e0f4b476 161 //
mjr 104:6e06e0f4b476 162 //
mjr 104:6e06e0f4b476 163 // How to connect to the KL25Z
mjr 104:6e06e0f4b476 164 //
mjr 104:6e06e0f4b476 165 // Follow the "typical drive circuit" presented in the Toshiba data sheet.
mjr 104:6e06e0f4b476 166 // They leave some of the parts unspecified, so here are the specific values
mjr 104:6e06e0f4b476 167 // we used for our reference implementation:
mjr 104:6e06e0f4b476 168 //
mjr 104:6e06e0f4b476 169 // - 3.3V power supply
mjr 104:6e06e0f4b476 170 // - 74HC04N hex inverter for the logic gate inputs (fM, SH, ICG)
mjr 104:6e06e0f4b476 171 // - 0.1uF ceramic + 10uF electrolytic decoupling capacitors (GND to Vcc))
mjr 104:6e06e0f4b476 172 // - BC212A PNP transistor for the output drive (OS), with:
mjr 104:6e06e0f4b476 173 // - 150 ohm resistor on the base
mjr 104:6e06e0f4b476 174 // - 150 ohm resistor between collector and GND
mjr 104:6e06e0f4b476 175 // - 2.2K ohm resistor between emitter and Vcc
mjr 104:6e06e0f4b476 176 //
mjr 100:1ff35c07217c 177
mjr 100:1ff35c07217c 178 #include "config.h"
mjr 100:1ff35c07217c 179 #include "NewPwm.h"
mjr 100:1ff35c07217c 180 #include "AltAnalogIn.h"
mjr 100:1ff35c07217c 181 #include "SimpleDMA.h"
mjr 100:1ff35c07217c 182 #include "DMAChannels.h"
mjr 100:1ff35c07217c 183
mjr 100:1ff35c07217c 184
mjr 100:1ff35c07217c 185 template<bool invertedLogicGates> class TCD1103
mjr 100:1ff35c07217c 186 {
mjr 100:1ff35c07217c 187 public:
mjr 100:1ff35c07217c 188 TCD1103(PinName fmPin, PinName osPin, PinName icgPin, PinName shPin) :
mjr 100:1ff35c07217c 189 fm(fmPin, invertedLogicGates),
mjr 100:1ff35c07217c 190 os(osPin, false, 6, 1), // single sample, 6-cycle long sampling mode, no averaging
mjr 100:1ff35c07217c 191 icg(icgPin),
mjr 100:1ff35c07217c 192 sh(shPin),
mjr 100:1ff35c07217c 193 os_dma(DMAch_TDC_ADC)
mjr 100:1ff35c07217c 194 {
mjr 103:dec22cd65b2a 195 // Idle conditions: SH low, ICG high.
mjr 103:dec22cd65b2a 196 sh = logicLow;
mjr 103:dec22cd65b2a 197 icg = logicHigh;
mjr 103:dec22cd65b2a 198
mjr 103:dec22cd65b2a 199 // Set a zero minimum integration time by default. Note that tIntMin
mjr 103:dec22cd65b2a 200 // has no effect when it's less than the absolute minimum, which is
mjr 103:dec22cd65b2a 201 // the pixel transfer time for one frame (around 3ms). tIntMin only
mjr 103:dec22cd65b2a 202 // kicks in when it goes above that absolute minimum, at which point
mjr 103:dec22cd65b2a 203 // we'll wait for any additional time needed to reach tIntMin before
mjr 103:dec22cd65b2a 204 // starting the next integration cycle.
mjr 103:dec22cd65b2a 205 tIntMin = 0;
mjr 103:dec22cd65b2a 206
mjr 100:1ff35c07217c 207 // Calibrate the ADC for best accuracy
mjr 100:1ff35c07217c 208 os.calibrate();
mjr 104:6e06e0f4b476 209
mjr 100:1ff35c07217c 210 // ADC sample conversion time. This must be calculated based on the
mjr 100:1ff35c07217c 211 // combination of parameters selected for the os() initializer above.
mjr 109:310ac82cbbee 212 // See the KL25 Sub-Family Reference Manual, section 28.4.4.5, for the
mjr 109:310ac82cbbee 213 // formula. We operate in single-sample mode, so when you read the
mjr 109:310ac82cbbee 214 // Reference Manual tables, the sample time value to use is the
mjr 109:310ac82cbbee 215 // "First or Single" value.
mjr 109:310ac82cbbee 216 const float ADC_TIME = 2.1041667e-6f; // 6-cycle long sampling, no averaging
mjr 100:1ff35c07217c 217
mjr 100:1ff35c07217c 218 // Set the TPM cycle time to satisfy our timing constraints:
mjr 100:1ff35c07217c 219 //
mjr 100:1ff35c07217c 220 // Tm + epsilon1 < A < 2*Tm - epsilon2
mjr 100:1ff35c07217c 221 //
mjr 100:1ff35c07217c 222 // where A is the ADC conversion time and Tm is the master clock
mjr 109:310ac82cbbee 223 // period, and the epsilons provide a margin of safety for any
mjr 100:1ff35c07217c 224 // non-deterministic component to the timing of A and Tm. The
mjr 100:1ff35c07217c 225 // epsilons could be zero if the timing of the ADC is perfectly
mjr 100:1ff35c07217c 226 // deterministic; this must be determined empirically.
mjr 100:1ff35c07217c 227 //
mjr 100:1ff35c07217c 228 // The most conservative solution would be to make epsilon as large
mjr 100:1ff35c07217c 229 // as possible, which means bisecting the time window by making
mjr 100:1ff35c07217c 230 // A = 1.5*T, or, equivalently, T = A/1.5 (the latter form being more
mjr 100:1ff35c07217c 231 // useful because T is the free variable here, as we can only control
mjr 100:1ff35c07217c 232 // A to the extent that we can choose the ADC parameters).
mjr 100:1ff35c07217c 233 //
mjr 100:1ff35c07217c 234 // But we'd also like to make T as short as possible while maintaining
mjr 100:1ff35c07217c 235 // reliable operation. Shorter T yields a higher frame rate, and we
mjr 100:1ff35c07217c 236 // want the frame rate to be as high as possible so that we can track
mjr 100:1ff35c07217c 237 // fast plunger motion accurately. Empirically, we can get reliable
mjr 100:1ff35c07217c 238 // results by using half of the ADC time plus a small buffer time.
mjr 100:1ff35c07217c 239 //
mjr 109:310ac82cbbee 240 fm.getUnit()->period(masterClockPeriod = ADC_TIME/2 + 0.25e-6f);
mjr 100:1ff35c07217c 241
mjr 100:1ff35c07217c 242 // Start the master clock running with a 50% duty cycle
mjr 100:1ff35c07217c 243 fm.write(0.5f);
mjr 100:1ff35c07217c 244
mjr 104:6e06e0f4b476 245 // Allocate our double pixel buffers.
mjr 104:6e06e0f4b476 246 pix1 = new uint8_t[nPixAlo * 2];
mjr 104:6e06e0f4b476 247 pix2 = pix1 + nPixAlo;
mjr 100:1ff35c07217c 248
mjr 100:1ff35c07217c 249 // put the first DMA transfer into the first buffer (pix1)
mjr 100:1ff35c07217c 250 pixDMA = 0;
mjr 101:755f44622abc 251 clientOwnsStablePix = false;
mjr 100:1ff35c07217c 252
mjr 100:1ff35c07217c 253 // start the sample timer with an arbitrary epoch of "now"
mjr 100:1ff35c07217c 254 t.start();
mjr 100:1ff35c07217c 255
mjr 100:1ff35c07217c 256 // Set up the ADC transfer DMA channel. This channel transfers
mjr 100:1ff35c07217c 257 // the current analog sampling result from the ADC output register
mjr 100:1ff35c07217c 258 // to our pixel array.
mjr 100:1ff35c07217c 259 os.initDMA(&os_dma);
mjr 100:1ff35c07217c 260
mjr 100:1ff35c07217c 261 // Register an interrupt callback so that we're notified when
mjr 100:1ff35c07217c 262 // the last ADC transfer completes.
mjr 100:1ff35c07217c 263 os_dma.attach(this, &TCD1103::transferDone);
mjr 100:1ff35c07217c 264
mjr 100:1ff35c07217c 265 // Set up the ADC to trigger on the master clock's TPM channel
mjr 100:1ff35c07217c 266 os.setTriggerTPM(fm.getUnitNum());
mjr 100:1ff35c07217c 267
mjr 100:1ff35c07217c 268 // clear the timing statistics
mjr 100:1ff35c07217c 269 totalXferTime = 0.0;
mjr 100:1ff35c07217c 270 maxXferTime = 0;
mjr 100:1ff35c07217c 271 minXferTime = 0xffffffff;
mjr 100:1ff35c07217c 272 nRuns = 0;
mjr 100:1ff35c07217c 273
mjr 101:755f44622abc 274 // start the first transfer
mjr 101:755f44622abc 275 startTransfer();
mjr 100:1ff35c07217c 276 }
mjr 100:1ff35c07217c 277
mjr 100:1ff35c07217c 278 // logic gate levels, based on whether or not the logic gate connections
mjr 100:1ff35c07217c 279 // in the hardware are buffered through inverters
mjr 100:1ff35c07217c 280 static const int logicLow = invertedLogicGates ? 1 : 0;
mjr 100:1ff35c07217c 281 static const bool logicHigh = invertedLogicGates ? 0 : 1;
mjr 100:1ff35c07217c 282
mjr 100:1ff35c07217c 283 // ready to read
mjr 101:755f44622abc 284 bool ready() { return clientOwnsStablePix; }
mjr 103:dec22cd65b2a 285
mjr 100:1ff35c07217c 286 // Get the stable pixel array. This is the image array from the
mjr 100:1ff35c07217c 287 // previous capture. It remains valid until the next startCapture()
mjr 100:1ff35c07217c 288 // call, at which point this buffer will be reused for the new capture.
mjr 100:1ff35c07217c 289 void getPix(uint8_t * &pix, uint32_t &t)
mjr 100:1ff35c07217c 290 {
mjr 104:6e06e0f4b476 291 // Return the pixel array that ISN'T assigned to the DMA.
mjr 100:1ff35c07217c 292 if (pixDMA)
mjr 100:1ff35c07217c 293 {
mjr 100:1ff35c07217c 294 // DMA owns pix2, so the stable array is pix1
mjr 100:1ff35c07217c 295 pix = pix1;
mjr 100:1ff35c07217c 296 t = t1;
mjr 100:1ff35c07217c 297 }
mjr 100:1ff35c07217c 298 else
mjr 100:1ff35c07217c 299 {
mjr 100:1ff35c07217c 300 // DMA owns pix1, so the stable array is pix2
mjr 100:1ff35c07217c 301 pix = pix2;
mjr 100:1ff35c07217c 302 t = t2;
mjr 100:1ff35c07217c 303 }
mjr 100:1ff35c07217c 304 }
mjr 100:1ff35c07217c 305
mjr 101:755f44622abc 306 // release the client's pixel buffer
mjr 101:755f44622abc 307 void releasePix() { clientOwnsStablePix = false; }
mjr 101:755f44622abc 308
mjr 101:755f44622abc 309 // figure the average scan time from the running totals
mjr 101:755f44622abc 310 uint32_t getAvgScanTime() { return static_cast<uint32_t>(totalXferTime / nRuns);}
mjr 101:755f44622abc 311
mjr 101:755f44622abc 312 // Set the requested minimum integration time. If this is less than the
mjr 101:755f44622abc 313 // sensor's physical minimum time, the physical minimum applies.
mjr 101:755f44622abc 314 virtual void setMinIntTime(uint32_t us)
mjr 100:1ff35c07217c 315 {
mjr 101:755f44622abc 316 tIntMin = us;
mjr 101:755f44622abc 317 }
mjr 101:755f44622abc 318
mjr 101:755f44622abc 319 protected:
mjr 100:1ff35c07217c 320 // Start an image capture from the sensor. Waits the previous
mjr 100:1ff35c07217c 321 // capture to finish if it's still running, then starts a new one
mjr 104:6e06e0f4b476 322 // and returns immediately. The new capture proceeds asynchronously
mjr 104:6e06e0f4b476 323 // via DMA hardware transfer, so the client can continue with other
mjr 100:1ff35c07217c 324 // processing during the capture.
mjr 101:755f44622abc 325 void startTransfer()
mjr 100:1ff35c07217c 326 {
mjr 101:755f44622abc 327 // if we own the stable buffer, swap buffers
mjr 101:755f44622abc 328 if (!clientOwnsStablePix)
mjr 100:1ff35c07217c 329 {
mjr 101:755f44622abc 330 // swap buffers
mjr 101:755f44622abc 331 pixDMA ^= 1;
mjr 101:755f44622abc 332
mjr 101:755f44622abc 333 // release the prior DMA buffer to the client
mjr 101:755f44622abc 334 clientOwnsStablePix = true;
mjr 100:1ff35c07217c 335 }
mjr 100:1ff35c07217c 336
mjr 104:6e06e0f4b476 337 // figure our destination buffer
mjr 104:6e06e0f4b476 338 uint8_t *dst = pixDMA ? pix2 : pix1;
mjr 104:6e06e0f4b476 339
mjr 100:1ff35c07217c 340 // Set up the active pixel array as the destination buffer for
mjr 100:1ff35c07217c 341 // the ADC DMA channel.
mjr 104:6e06e0f4b476 342 os_dma.destination(dst, true);
mjr 100:1ff35c07217c 343
mjr 100:1ff35c07217c 344 // Start the read cycle by sending the ICG/SH pulse sequence
mjr 100:1ff35c07217c 345 uint32_t tNewInt = gen_SH_ICG_pulse(true);
mjr 100:1ff35c07217c 346
mjr 100:1ff35c07217c 347 // Set the timestamp for the current active buffer. The ICG/SH
mjr 100:1ff35c07217c 348 // gymnastics we just did transferred the CCD pixels into the sensor's
mjr 100:1ff35c07217c 349 // internal shift register and reset the pixels, starting a new
mjr 100:1ff35c07217c 350 // integration cycle. So the pixels we just shifted started
mjr 100:1ff35c07217c 351 // integrating the *last* time we did that, which we recorded as
mjr 100:1ff35c07217c 352 // tInt at the time. The image we're about to transfer therefore
mjr 100:1ff35c07217c 353 // represents the light collected between tInt and the SH pulse we
mjr 100:1ff35c07217c 354 // just did. The image covers a time range rather than a single
mjr 100:1ff35c07217c 355 // point in time, but we still have to give it a single timestamp.
mjr 100:1ff35c07217c 356 // Use the midpoint of the integration period.
mjr 100:1ff35c07217c 357 uint32_t tmid = (tNewInt + tInt) >> 1;
mjr 100:1ff35c07217c 358 if (pixDMA)
mjr 100:1ff35c07217c 359 t2 = tmid;
mjr 100:1ff35c07217c 360 else
mjr 100:1ff35c07217c 361 t1 = tmid;
mjr 100:1ff35c07217c 362
mjr 100:1ff35c07217c 363 // Record the start time of the currently active integration period
mjr 100:1ff35c07217c 364 tInt = tNewInt;
mjr 100:1ff35c07217c 365 }
mjr 100:1ff35c07217c 366
mjr 101:755f44622abc 367 // End of transfer notification. This runs as an interrupt handler when
mjr 101:755f44622abc 368 // the DMA transfer completes.
mjr 101:755f44622abc 369 void transferDone()
mjr 100:1ff35c07217c 370 {
mjr 104:6e06e0f4b476 371 // stop the ADC triggering
mjr 104:6e06e0f4b476 372 os.stop();
mjr 104:6e06e0f4b476 373
mjr 101:755f44622abc 374 // add this sample to the timing statistics (for diagnostics and
mjr 101:755f44622abc 375 // performance measurement)
mjr 101:755f44622abc 376 uint32_t now = t.read_us();
mjr 101:755f44622abc 377 uint32_t dt = dtPixXfer = static_cast<uint32_t>(now - tXfer);
mjr 101:755f44622abc 378 totalXferTime += dt;
mjr 101:755f44622abc 379 nRuns += 1;
mjr 101:755f44622abc 380
mjr 101:755f44622abc 381 // collect debug statistics
mjr 101:755f44622abc 382 if (dt < minXferTime) minXferTime = dt;
mjr 101:755f44622abc 383 if (dt > maxXferTime) maxXferTime = dt;
mjr 104:6e06e0f4b476 384
mjr 104:6e06e0f4b476 385 // figure how long we've been integrating so far on this cycle
mjr 101:755f44622abc 386 uint32_t dtInt = now - tInt;
mjr 104:6e06e0f4b476 387
mjr 104:6e06e0f4b476 388 // Figure the time to the start of the next transfer. Wait for the
mjr 104:6e06e0f4b476 389 // remainder of the current integration period if we haven't yet
mjr 104:6e06e0f4b476 390 // reached the requested minimum, otherwise just start almost
mjr 104:6e06e0f4b476 391 // immediately. (Not *actually* immediately: we don't want to start
mjr 104:6e06e0f4b476 392 // the new transfer within this interrupt handler, because the DMA
mjr 104:6e06e0f4b476 393 // IRQ doesn't reliably clear if we start a new transfer immediately.)
mjr 104:6e06e0f4b476 394 uint32_t dtNext = dtInt < tIntMin ? tIntMin - dtInt : 1;
mjr 104:6e06e0f4b476 395
mjr 104:6e06e0f4b476 396 // Schedule the next transfer
mjr 104:6e06e0f4b476 397 integrationTimeout.attach_us(this, &TCD1103::startTransfer, dtNext);
mjr 100:1ff35c07217c 398 }
mjr 100:1ff35c07217c 399
mjr 100:1ff35c07217c 400 // Generate an SH/ICG pulse. This transfers the pixel data from the live
mjr 100:1ff35c07217c 401 // sensor photoreceptors into the sensor's internal shift register, clears
mjr 100:1ff35c07217c 402 // the live pixels, and starts a new integration cycle.
mjr 100:1ff35c07217c 403 //
mjr 100:1ff35c07217c 404 // If start_dma_xfer is true, we'll start the DMA transfer for the ADC
mjr 100:1ff35c07217c 405 // pixel data. We handle this here because the sensor starts clocking
mjr 100:1ff35c07217c 406 // out pixels precisely at the end of the ICG pulse, so we have to be
mjr 100:1ff35c07217c 407 // be very careful about the timing.
mjr 100:1ff35c07217c 408 //
mjr 100:1ff35c07217c 409 // Returns the timestamp (relative to our image timer 't') of the end
mjr 100:1ff35c07217c 410 // of the SH pulse, which is the moment the new integration cycle starts.
mjr 100:1ff35c07217c 411 //
mjr 100:1ff35c07217c 412 // Note that we send these pulses synchronously - that is, this routine
mjr 100:1ff35c07217c 413 // blocks until the pulses have been sent. The overall sequence takes
mjr 100:1ff35c07217c 414 // about 2.5us to 3us, so it's not a significant interruption of the
mjr 100:1ff35c07217c 415 // main loop.
mjr 100:1ff35c07217c 416 //
mjr 100:1ff35c07217c 417 uint32_t gen_SH_ICG_pulse(bool start_dma_xfer)
mjr 100:1ff35c07217c 418 {
mjr 109:310ac82cbbee 419 // Make sure the ADC is stopped
mjr 109:310ac82cbbee 420 os.stop();
mjr 109:310ac82cbbee 421
mjr 100:1ff35c07217c 422 // If desired, prepare to start the DMA transfer for the ADC data.
mjr 100:1ff35c07217c 423 // (Set up a dummy location to write in lieu of the DMA register if
mjr 100:1ff35c07217c 424 // DMA initiation isn't required, so that we don't have to take the
mjr 100:1ff35c07217c 425 // time for a conditional when we're ready to start the DMA transfer.
mjr 100:1ff35c07217c 426 // The timing there will be extremely tight, and we can't afford the
mjr 100:1ff35c07217c 427 // extra instructions to test a condition.)
mjr 100:1ff35c07217c 428 uint8_t dma_chcfg_dummy = 0;
mjr 100:1ff35c07217c 429 volatile uint8_t *dma_chcfg = start_dma_xfer ? os_dma.prepare(nPixSensor, true) : &dma_chcfg_dummy;
mjr 100:1ff35c07217c 430
mjr 100:1ff35c07217c 431 // The basic idea is to take ICG low, and while holding ICG low,
mjr 100:1ff35c07217c 432 // pulse SH. The coincidence of the two pulses transfers the charge
mjr 100:1ff35c07217c 433 // from the live pixels into the shift register, which effectively
mjr 100:1ff35c07217c 434 // discharges the live pixels and thereby starts a new integration
mjr 100:1ff35c07217c 435 // cycle.
mjr 100:1ff35c07217c 436 //
mjr 100:1ff35c07217c 437 // The timing of the pulse sequence is rather tightly constrained
mjr 100:1ff35c07217c 438 // per the data sheet, so we have to take some care in executing it:
mjr 100:1ff35c07217c 439 //
mjr 100:1ff35c07217c 440 // ICG -> LOW
mjr 100:1ff35c07217c 441 // 100-1000 ns delay (*)
mjr 100:1ff35c07217c 442 // SH -> HIGH
mjr 100:1ff35c07217c 443 // >1000ns delay
mjr 100:1ff35c07217c 444 // SH -> LOW
mjr 100:1ff35c07217c 445 // >1000ns delay
mjr 100:1ff35c07217c 446 // ICG -> high (**)
mjr 100:1ff35c07217c 447 //
mjr 100:1ff35c07217c 448 // There are two steps here that are tricky:
mjr 100:1ff35c07217c 449 //
mjr 100:1ff35c07217c 450 // (*) is a narrow window that we can't achieve with an mbed
mjr 100:1ff35c07217c 451 // microsecond timer. Instead, we'll do a couple of extra writes
mjr 100:1ff35c07217c 452 // to the ICG register, which take about 60ns each.
mjr 100:1ff35c07217c 453 //
mjr 100:1ff35c07217c 454 // (**) has the rather severe constraint that the transition must
mjr 100:1ff35c07217c 455 // occur AND complete while the master clock is high. Other people
mjr 100:1ff35c07217c 456 // working with similar Toshiba chips in MCU projects have suggested
mjr 100:1ff35c07217c 457 // that this constraint can safely be ignored, so maybe the data
mjr 100:1ff35c07217c 458 // sheet's insistence about it is obsolete advice from past Toshiba
mjr 100:1ff35c07217c 459 // sensors that the doc writers carried forward by copy-and-paste.
mjr 100:1ff35c07217c 460 // Toshiba has been making these sorts of chips for a very long time,
mjr 100:1ff35c07217c 461 // and the data sheets for many of them are obvious copy-and-paste
mjr 100:1ff35c07217c 462 // jobs. But let's take the data sheet at its word and assume that
mjr 100:1ff35c07217c 463 // this is important for proper operation. Our best hope of
mjr 100:1ff35c07217c 464 // satisfying this constraint is to synchronize the start of the
mjr 100:1ff35c07217c 465 // ICG->high transition with the start of a TPM cycle on the master
mjr 100:1ff35c07217c 466 // clock. That guarantees that the ICG transition starts when the
mjr 100:1ff35c07217c 467 // clock signal is high (as each TPM cycle starts out high), and
mjr 100:1ff35c07217c 468 // gives us the longest possible runway for the transition to
mjr 100:1ff35c07217c 469 // complete while the clock is still high, as we get the full
mjr 100:1ff35c07217c 470 // length of the high part of the cycle to work with. To quantify,
mjr 100:1ff35c07217c 471 // it gives us about 600ns. The register write takes about 60ns,
mjr 100:1ff35c07217c 472 // and waitEndCycle() adds several instructions of overhead, perhaps
mjr 100:1ff35c07217c 473 // 200ns, so we get around 300ns for the transition to finish. That
mjr 100:1ff35c07217c 474 // should be a gracious plenty assuming that the hardware is set up
mjr 100:1ff35c07217c 475 // with an inverter to buffer the clock signals. The inverter should
mjr 100:1ff35c07217c 476 // be able to pull up the 35pF on ICG in a "typical" 30ns (rise time
mjr 100:1ff35c07217c 477 // plus propagation delay, per the 74HC04 data sheet) and max 150ns.
mjr 100:1ff35c07217c 478 // This seems to be one place where the inverter might really be
mjr 100:1ff35c07217c 479 // necessary to meet the timing requirements, as the KL25Z GPIO
mjr 100:1ff35c07217c 480 // might need more like 2us to pull that load up.
mjr 100:1ff35c07217c 481 //
mjr 100:1ff35c07217c 482 // There's an additional constraint on the timing at the end of the
mjr 100:1ff35c07217c 483 // ICG pulse. The sensor starts clocking out pixels on the rising
mjr 100:1ff35c07217c 484 // edge of the ICG pulse. So we need the ICG pulse end to align
mjr 100:1ff35c07217c 485 // with the start of an ADC cycle. If we get that wrong, all of our
mjr 100:1ff35c07217c 486 // ADC samples will be off by half a clock, so every sample will be
mjr 100:1ff35c07217c 487 // the average of two adjacent pixels instead of one pixel. That
mjr 109:310ac82cbbee 488 // would have the effect of shifting the image by half a pixel,
mjr 109:310ac82cbbee 489 // which could make our edge detection jitter by one pixel from one
mjr 109:310ac82cbbee 490 // frame to the next. So we definitely want to avoid this.
mjr 100:1ff35c07217c 491 //
mjr 100:1ff35c07217c 492 // The end of the SH pulse triggers the start of a new integration
mjr 100:1ff35c07217c 493 // cycle, so note the time of that pulse for image timestamping
mjr 100:1ff35c07217c 494 // purposes. That will be the start time of the NEXT image we
mjr 100:1ff35c07217c 495 // transfer after we shift out the current sensor pixels, which
mjr 100:1ff35c07217c 496 // represent the pixels from the last time we pulsed SH.
mjr 100:1ff35c07217c 497 //
mjr 100:1ff35c07217c 498 icg = logicLow;
mjr 109:310ac82cbbee 499 icg = logicLow; // for timing, adds about 150ns > min 100ns
mjr 103:dec22cd65b2a 500
mjr 103:dec22cd65b2a 501 sh = logicHigh; // take SH high
mjr 103:dec22cd65b2a 502
mjr 100:1ff35c07217c 503 wait_us(1); // >1000ns delay
mjr 103:dec22cd65b2a 504 sh = logicHigh; // a little more padding to be sure we're over the minimum
mjr 103:dec22cd65b2a 505
mjr 103:dec22cd65b2a 506 sh = logicLow; // take SH low
mjr 103:dec22cd65b2a 507
mjr 103:dec22cd65b2a 508 uint32_t t_sh = t.read_us(); // this is the start time of the NEXT integration
mjr 103:dec22cd65b2a 509
mjr 103:dec22cd65b2a 510 wait_us(3); // >1000ns delay, 5000ns typical; 3us should get us most
mjr 103:dec22cd65b2a 511 // of the way there, considering that we have some more
mjr 103:dec22cd65b2a 512 // work to do before we end the ICG pulse
mjr 100:1ff35c07217c 513
mjr 100:1ff35c07217c 514 // Now the tricky part! We have to end the ICG pulse (take ICG high)
mjr 100:1ff35c07217c 515 // at the start of a master clock cycle, AND at the start of an ADC
mjr 100:1ff35c07217c 516 // sampling cycle. The sensor will start clocking out pixels the
mjr 100:1ff35c07217c 517 // instance ICG goes high, so we have to align our ADC cycle so that
mjr 100:1ff35c07217c 518 // we start a sample at almost exactly the same time we take ICG
mjr 100:1ff35c07217c 519 // high.
mjr 100:1ff35c07217c 520 //
mjr 100:1ff35c07217c 521 // Now, every ADC sampling cycle always starts at a rising edge of
mjr 100:1ff35c07217c 522 // the master clock, since the master clock is the ADC trigger. BUT,
mjr 100:1ff35c07217c 523 // the converse is NOT true: every rising edge of the master clock
mjr 100:1ff35c07217c 524 // is NOT an ADC sample start. Recall that we've contrived the timing
mjr 100:1ff35c07217c 525 // so that every OTHER master clock rising edge starts an ADC sample.
mjr 100:1ff35c07217c 526 //
mjr 100:1ff35c07217c 527 // So how do we detect which part of the clock cycle we're in? We
mjr 100:1ff35c07217c 528 // could conceivably use the COCO bit in the ADC status register to
mjr 100:1ff35c07217c 529 // detect the little window between the end of one sample and the
mjr 100:1ff35c07217c 530 // start of the next. Unfortunately, this doesn't work: the COCO
mjr 100:1ff35c07217c 531 // bit is never actually set for the duration of even a single CPU
mjr 100:1ff35c07217c 532 // instruction in our setup, no matter how loose we make the timing
mjr 100:1ff35c07217c 533 // between the ADC and the fM cycle. I think the reason is the DMA
mjr 100:1ff35c07217c 534 // setup: the COCO bit triggers the DMA, and the DMA controller
mjr 100:1ff35c07217c 535 // reads the ADC result register (the DMA source in our setup),
mjr 100:1ff35c07217c 536 // which has the side effect of clearing COCO. I've experimented
mjr 100:1ff35c07217c 537 // with this using different timing parameters, and the result is
mjr 100:1ff35c07217c 538 // always the same: the CPU *never* sees the COCO bit set. The DMA
mjr 100:1ff35c07217c 539 // trigger timing is evidently deterministic such that the DMA unit
mjr 100:1ff35c07217c 540 // invariably gets its shot at reading ADC0->R before the CPU does.
mjr 100:1ff35c07217c 541 //
mjr 100:1ff35c07217c 542 // The COCO approach would be a little iffy anyway, since we want the
mjr 100:1ff35c07217c 543 // ADC idle time to be as short as possible, which wouldn't give us
mjr 100:1ff35c07217c 544 // much time to do all we have to do in the COCO period, even if
mjr 100:1ff35c07217c 545 // there were one. What we can do instead is seize control of the
mjr 100:1ff35c07217c 546 // ADC cycle timing: rather than trying to detect when the cycle
mjr 100:1ff35c07217c 547 // ends, we can specify when it begins. We can do this by canceling
mjr 100:1ff35c07217c 548 // the TPM->ADC trigger and aborting any conversion in progress, then
mjr 100:1ff35c07217c 549 // reprogramming the TPM->ADC trigger at our leisure. What we *can*
mjr 100:1ff35c07217c 550 // detect reliably is the start of a TPM cycle. So here's our
mjr 100:1ff35c07217c 551 // strategy:
mjr 100:1ff35c07217c 552 //
mjr 100:1ff35c07217c 553 // - Turn off the TPM->ADC trigger and abort the current conversion
mjr 100:1ff35c07217c 554 // - Wait until a new TPM cycle starts
mjr 100:1ff35c07217c 555 // - Reset the TPM->ADC trigger. The first new conversion will
mjr 100:1ff35c07217c 556 // start on the next TPM cycle, so we have the remainder of
mjr 100:1ff35c07217c 557 // the current TPM cycle to make this happen (about 1us, enough
mjr 100:1ff35c07217c 558 // for 16 CPU instructions - plenty for this step)
mjr 100:1ff35c07217c 559 // - Wait for the new TPM cycle
mjr 100:1ff35c07217c 560 // - End the ICG pulse
mjr 100:1ff35c07217c 561 //
mjr 100:1ff35c07217c 562
mjr 100:1ff35c07217c 563 // Enable the DMA controller for the new transfer from the ADC.
mjr 100:1ff35c07217c 564 // The sensor will start clocking out new samples at the ICG rising
mjr 100:1ff35c07217c 565 // edge, so the next ADC sample to complete will represent the first
mjr 100:1ff35c07217c 566 // pixel in the new frame. So we need the DMA ready to go at the
mjr 100:1ff35c07217c 567 // very next sample. Recall that the DMA is triggered by ADC
mjr 100:1ff35c07217c 568 // completion, and ADC is stopped right now, so enabling the DMA
mjr 100:1ff35c07217c 569 // won't have any immediate effect - it just spools it up so that
mjr 100:1ff35c07217c 570 // it's ready to move samples as soon as we resume the ADC.
mjr 100:1ff35c07217c 571 *dma_chcfg |= DMAMUX_CHCFG_ENBL_MASK;
mjr 100:1ff35c07217c 572
mjr 100:1ff35c07217c 573 // wait for the start of a new master clock cycle
mjr 100:1ff35c07217c 574 fm.waitEndCycle();
mjr 100:1ff35c07217c 575
mjr 109:310ac82cbbee 576 // Wait one more cycle to be sure the DMA is ready. Empirically,
mjr 109:310ac82cbbee 577 // this extra wait is actually required; evidently DMA startup has
mjr 109:310ac82cbbee 578 // some non-deterministic timing element or perhaps an asynchronous
mjr 109:310ac82cbbee 579 // external dependency. In any case, *without* this extra wait,
mjr 109:310ac82cbbee 580 // the DMA transfer sporadically (about 20% probability) misses the
mjr 109:310ac82cbbee 581 // very first pixel that the sensor clocks out, so the entire image
mjr 109:310ac82cbbee 582 // is shifted "left" by one pixel. That makes the position sensing
mjr 109:310ac82cbbee 583 // jitter by a pixel from one frame to the next according to whether
mjr 109:310ac82cbbee 584 // or not we had that one-pixel delay in the DMA startup. Happily,
mjr 109:310ac82cbbee 585 // padding the timing by an fM cycle seems to make the DMA startup
mjr 109:310ac82cbbee 586 // perfectly reliable.
mjr 109:310ac82cbbee 587 fm.waitEndCycle();
mjr 109:310ac82cbbee 588
mjr 100:1ff35c07217c 589 // Okay, a master clock cycle just started, so we have about 1us
mjr 100:1ff35c07217c 590 // (about 16 CPU instructions) before the next one begins. Resume
mjr 100:1ff35c07217c 591 // ADC sampling. The first new sample will start with the next
mjr 109:310ac82cbbee 592 // TPM cycle 1us from now. This step itself takes about 3 machine
mjr 109:310ac82cbbee 593 // instructions for 180ns, so we have about 820ns left to go.
mjr 100:1ff35c07217c 594 os.resume();
mjr 100:1ff35c07217c 595
mjr 104:6e06e0f4b476 596 // Eerything is queued up! We just have to fire the starting gun
mjr 104:6e06e0f4b476 597 // on the sensor at the right moment. And that right moment is the
mjr 104:6e06e0f4b476 598 // start of the next TPM cycle. Wait for it...
mjr 100:1ff35c07217c 599 fm.waitEndCycle();
mjr 100:1ff35c07217c 600
mjr 100:1ff35c07217c 601 // And go!
mjr 100:1ff35c07217c 602 icg = logicHigh;
mjr 100:1ff35c07217c 603
mjr 100:1ff35c07217c 604 // note the start time of the transfer
mjr 100:1ff35c07217c 605 tXfer = t.read_us();
mjr 100:1ff35c07217c 606
mjr 100:1ff35c07217c 607 // return the timestamp of the end of the SH pulse - this is the start
mjr 100:1ff35c07217c 608 // of the new integration period that we just initiated
mjr 100:1ff35c07217c 609 return t_sh;
mjr 100:1ff35c07217c 610 }
mjr 100:1ff35c07217c 611
mjr 100:1ff35c07217c 612 // master clock
mjr 100:1ff35c07217c 613 NewPwmOut fm;
mjr 100:1ff35c07217c 614
mjr 100:1ff35c07217c 615 // analog input for reading the pixel voltage level
mjr 100:1ff35c07217c 616 AltAnalogIn_8bit os;
mjr 100:1ff35c07217c 617
mjr 100:1ff35c07217c 618 // Integration Clear Gate output
mjr 100:1ff35c07217c 619 DigitalOut icg;
mjr 100:1ff35c07217c 620
mjr 100:1ff35c07217c 621 // Shift Gate output
mjr 100:1ff35c07217c 622 DigitalOut sh;
mjr 100:1ff35c07217c 623
mjr 100:1ff35c07217c 624 // DMA channel for the analog input
mjr 100:1ff35c07217c 625 SimpleDMA os_dma;
mjr 100:1ff35c07217c 626
mjr 100:1ff35c07217c 627 // Master clock period, in seconds, calculated based on the ADC timing
mjr 100:1ff35c07217c 628 float masterClockPeriod;
mjr 100:1ff35c07217c 629
mjr 100:1ff35c07217c 630 // Number of pixels. The TCD1103 has 1500 image pixels, plus 32 dummy
mjr 100:1ff35c07217c 631 // pixels at the front end (before the first image pixel) and another 14
mjr 100:1ff35c07217c 632 // dummy pixels at the back end. The sensor always transfers the full
mjr 100:1ff35c07217c 633 // file on each read cycle, including the dummies, so we have to make
mjr 100:1ff35c07217c 634 // room for the dummy pixels during each read.
mjr 100:1ff35c07217c 635 static const int nPixSensor = 1546;
mjr 100:1ff35c07217c 636
mjr 104:6e06e0f4b476 637 // Figure the number of pixels to allocate per pixel buffer. Round
mjr 104:6e06e0f4b476 638 // up to the next 4-byte boundary, so that the buffers are both DWORD-
mjr 104:6e06e0f4b476 639 // aligned. (This allows using DWORD pointers into the buffer to
mjr 104:6e06e0f4b476 640 // operate on buffer pixels four at a time, such as in the negative
mjr 104:6e06e0f4b476 641 // image inversion code in the generic PlungerSensorImage base class.)
mjr 104:6e06e0f4b476 642 static const int nPixAlo = (nPixSensor + 3) & ~3;
mjr 104:6e06e0f4b476 643
mjr 100:1ff35c07217c 644 // pixel buffers - we keep two buffers so that we can transfer the
mjr 100:1ff35c07217c 645 // current sensor data into one buffer via DMA while we concurrently
mjr 100:1ff35c07217c 646 // process the last buffer
mjr 100:1ff35c07217c 647 uint8_t *pix1; // pixel array 1
mjr 100:1ff35c07217c 648 uint8_t *pix2; // pixel array 2
mjr 100:1ff35c07217c 649
mjr 100:1ff35c07217c 650 // Timestamps of pix1 and pix2 arrays, in microseconds, in terms of the
mjr 100:1ff35c07217c 651 // sample timer (this->t).
mjr 100:1ff35c07217c 652 uint32_t t1;
mjr 100:1ff35c07217c 653 uint32_t t2;
mjr 100:1ff35c07217c 654
mjr 100:1ff35c07217c 655 // DMA target buffer. This is the buffer for the next DMA transfer.
mjr 100:1ff35c07217c 656 // 0 means pix1, 1 means pix2. The other buffer contains the stable
mjr 100:1ff35c07217c 657 // data from the last transfer.
mjr 100:1ff35c07217c 658 uint8_t pixDMA;
mjr 100:1ff35c07217c 659
mjr 101:755f44622abc 660 // Stable buffer ownership. At any given time, the DMA subsystem owns
mjr 101:755f44622abc 661 // the buffer specified by pixDMA. The other buffer - the "stable" buffer,
mjr 101:755f44622abc 662 // which contains the most recent completed frame, can be owned by EITHER
mjr 101:755f44622abc 663 // the client or by the DMA subsystem. Each time a DMA transfer completes,
mjr 101:755f44622abc 664 // the DMA subsystem looks at the stable buffer owner flag to determine
mjr 101:755f44622abc 665 // what to do:
mjr 101:755f44622abc 666 //
mjr 101:755f44622abc 667 // - If the DMA subsystem owns the stable buffer, it swaps buffers. This
mjr 101:755f44622abc 668 // makes the newly completed DMA buffer the new stable buffer, and makes
mjr 101:755f44622abc 669 // the old stable buffer the new DMA buffer. At this time, the DMA
mjr 101:755f44622abc 670 // subsystem also changes the stable buffer ownership to CLIENT.
mjr 101:755f44622abc 671 //
mjr 101:755f44622abc 672 // - If the CLIENT owns the stable buffer, the DMA subsystem can't swap
mjr 101:755f44622abc 673 // buffers, because the client is still using the stable buffer. It
mjr 101:755f44622abc 674 // simply leaves things as they are.
mjr 101:755f44622abc 675 //
mjr 101:755f44622abc 676 // In either case, the DMA system starts a new transfer at this point.
mjr 101:755f44622abc 677 //
mjr 101:755f44622abc 678 // The client, meanwhile, is free to access the stable buffer when it has
mjr 101:755f44622abc 679 // ownership. If the client *doesn't* have ownership, it must wait for
mjr 101:755f44622abc 680 // the ownership to be transferred, which can only be done by the DMA
mjr 101:755f44622abc 681 // subsystem on completing a transfer.
mjr 101:755f44622abc 682 //
mjr 101:755f44622abc 683 // When the client is done with the stable buffer, it transfers ownership
mjr 101:755f44622abc 684 // back to the DMA subsystem.
mjr 101:755f44622abc 685 //
mjr 101:755f44622abc 686 // Transfers of ownership from DMA to CLIENT are done only by DMA.
mjr 101:755f44622abc 687 // Transfers from CLIENT to DMA are done only by CLIENT. So whoever has
mjr 101:755f44622abc 688 // ownership now is responsible for transferring ownership.
mjr 101:755f44622abc 689 //
mjr 101:755f44622abc 690 volatile bool clientOwnsStablePix;
mjr 101:755f44622abc 691
mjr 101:755f44622abc 692 // Minimum requested integration time, in microseconds
mjr 101:755f44622abc 693 uint32_t tIntMin;
mjr 101:755f44622abc 694
mjr 101:755f44622abc 695 // Timeout for generating an interrupt at the end of the integration period
mjr 101:755f44622abc 696 Timeout integrationTimeout;
mjr 101:755f44622abc 697
mjr 100:1ff35c07217c 698 // timing statistics
mjr 100:1ff35c07217c 699 Timer t; // sample timer
mjr 100:1ff35c07217c 700 uint32_t tInt; // start time (us) of current integration period
mjr 100:1ff35c07217c 701 uint32_t tXfer; // start time (us) of current pixel transfer
mjr 100:1ff35c07217c 702 uint32_t dtPixXfer; // pixel transfer time of last frame
mjr 100:1ff35c07217c 703 uint64_t totalXferTime; // total time consumed by all reads so far
mjr 100:1ff35c07217c 704 uint32_t nRuns; // number of runs so far
mjr 100:1ff35c07217c 705
mjr 100:1ff35c07217c 706 // debugging - min/max transfer time statistics
mjr 100:1ff35c07217c 707 uint32_t minXferTime;
mjr 100:1ff35c07217c 708 uint32_t maxXferTime;
mjr 100:1ff35c07217c 709 };