Mike R / Mbed 2 deprecated Pinscape_Controller_V2

Dependencies:   mbed FastIO FastPWM USBDevice

Fork of Pinscape_Controller by Mike R

Committer:
mjr
Date:
Fri Nov 29 05:38:07 2019 +0000
Revision:
101:755f44622abc
Parent:
100:1ff35c07217c
Child:
103:dec22cd65b2a
Use continuous asynchronous frame transfers in image sensors

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mjr 100:1ff35c07217c 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 100:1ff35c07217c 10 // Mechanically, this sensor has an entirely different size scale vs the
mjr 100:1ff35c07217c 11 // TSL1410R. The 1410R's sensor window is about the same size as a standard
mjr 100:1ff35c07217c 12 // plunger's travel range (about 80mm), so the mechanical setup we use with
mjr 100:1ff35c07217c 13 // sensor is to situate the sensor adjacent to the plunger, with the pixel
mjr 100:1ff35c07217c 14 // window aligned with the plunger's axis of motion, so that the plunger
mjr 100:1ff35c07217c 15 // casts a shadow on the sensor at 1:1 scale. The TCD1103, in contrast, is
mjr 100:1ff35c07217c 16 // a tiny little thing, with about an 8mm window. That means that we have
mjr 100:1ff35c07217c 17 // to reduce the plunger shadow image by about 10X to fit the sensor, so an
mjr 100:1ff35c07217c 18 // optical lens is required. This makes it more complicated to set up, but
mjr 100:1ff35c07217c 19 // it also adds the advantage of allowing us to focus the image, for a more
mjr 100:1ff35c07217c 20 // precise reading. The shadow in the lens-less 1410R setup is usually about
mjr 100:1ff35c07217c 21 // four of five pixels wide, so we lose a lot of the sensor's native
mjr 100:1ff35c07217c 22 // precision to the poor optics - we only get about 1/50" resolution as a
mjr 100:1ff35c07217c 23 // result. With a focusing lens, we could potentially get single-pixel
mjr 100:1ff35c07217c 24 // resolution, which would be about 1/500" resolution. The reality will
mjr 100:1ff35c07217c 25 // be somewhat lower, depending on how hard we want to work at the optics,
mjr 100:1ff35c07217c 26 // but it should be possible to do much better than the unfocused 1410R.
mjr 100:1ff35c07217c 27 //
mjr 100:1ff35c07217c 28 // The electronic interface to this sensor has some fairly tight timing
mjr 100:1ff35c07217c 29 // requirements, per the data sheet. The sensor requires the host to
mjr 100:1ff35c07217c 30 // provide a master clock that runs at 0.4 MHz to 4 MHz. The data sheet's
mjr 100:1ff35c07217c 31 // timing diagrams imply that the master clock runs continuously, although
mjr 100:1ff35c07217c 32 // it's probably like the 1410R, where the clock is only needed when you
mjr 100:1ff35c07217c 33 // want to run the shift register and can be stopped at other times.
mjr 100:1ff35c07217c 34 //
mjr 100:1ff35c07217c 35 // As with the 1410R, we'll have to use DMA for the ADC transfers in order
mjr 100:1ff35c07217c 36 // to keep up with the high data rate without overloading the KL25Z CPU.
mjr 100:1ff35c07217c 37 // With the 1410R, we're able to use the ADC itself as the clock source,
mjr 100:1ff35c07217c 38 // by running the ADC in continous mode and using its "sample ready" signal
mjr 100:1ff35c07217c 39 // to trigger the DMA transfer. We used this to generate the external clock
mjr 100:1ff35c07217c 40 // signal for the sensor by "linking" the ADC's DMA channel to another pair
mjr 100:1ff35c07217c 41 // of DMA channels that generated the clock up/down signal each time an ADC
mjr 100:1ff35c07217c 42 // sample completed. This strategy won't work with the Toshiba sensor,
mjr 100:1ff35c07217c 43 // though, because the Toshiba sensor's timing sequence requires *two* clock
mjr 100:1ff35c07217c 44 // pulses per pixel. I can't come up with a way to accomplish that with the
mjr 100:1ff35c07217c 45 // linked-DMA approach. (I've tried!)
mjr 100:1ff35c07217c 46 //
mjr 100:1ff35c07217c 47 // So instead, we'll have to generate a true clock signal for the sensor.
mjr 100:1ff35c07217c 48 // The obvious way to do this (and the only way, as far as I can come up with)
mjr 100:1ff35c07217c 49 // is to use a TPM channel - that is, a PWM output. TPM channels are designed
mjr 100:1ff35c07217c 50 // precisely for this kind of work, so this is the right approach in terms of
mjr 100:1ff35c07217c 51 // suitability, but it has the downside that TPM units are an extremely scarce
mjr 100:1ff35c07217c 52 // resource on the KL25Z. We only have three of them to work with. Luckily
mjr 100:1ff35c07217c 53 // the rest of the Pinscape software only requires two of them: one for the
mjr 100:1ff35c07217c 54 // IR transmitter (which uses a TPM channel to generate the 41-48 kHz carrier
mjr 100:1ff35c07217c 55 // wave used by nearly all consumer IR remotes), and one for the TLC5940
mjr 100:1ff35c07217c 56 // driver (which uses it to generate the grayscale clock signal). Note that
mjr 100:1ff35c07217c 57 // we also use PWM channels for feedback device output ports, but those don't
mjr 100:1ff35c07217c 58 // have any dependency on the TPM period - they'll work with whatever period
mjr 100:1ff35c07217c 59 // the underlying TPM is set to use. So the feedback output ports can all
mjr 100:1ff35c07217c 60 // happily use free channels on TPM units claimed by any of the dedicated
mjr 100:1ff35c07217c 61 // users (IR, TLC5940, and us).
mjr 100:1ff35c07217c 62 //
mjr 100:1ff35c07217c 63 // But what do we do about the 2:1 ratio between master clock pulses and ADC
mjr 100:1ff35c07217c 64 // samples? The "right" way would be to allocate a second TPM unit to
mjr 100:1ff35c07217c 65 // generate a second clock signal at half the frequency of the master clock,
mjr 100:1ff35c07217c 66 // and use that as the ADC trigger. But as we just said, we only have three
mjr 100:1ff35c07217c 67 // TPM units in the whole system, and two of them are already claimed for
mjr 100:1ff35c07217c 68 // other uses, so we only have one unit to use here.
mjr 100:1ff35c07217c 69 //
mjr 100:1ff35c07217c 70 // Fortunately, we can make do with one TPM unit, by taking advantage of a
mjr 100:1ff35c07217c 71 // feature/quirk of the KL25Z ADC. The quirk lets us take ADC samples at
mjr 100:1ff35c07217c 72 // exactly half of the master clock rate, in perfect sync. The trick is to
mjr 100:1ff35c07217c 73 // pick a combination of master clock rate and ADC sample mode such that the
mjr 100:1ff35c07217c 74 // ADC conversion time is *almost but not quite* twice as long as the master
mjr 100:1ff35c07217c 75 // clock rate. With that combination of timings, we can trigger the ADC
mjr 100:1ff35c07217c 76 // from the TPM, and we'll get an ADC sample on exactly every other tick of
mjr 100:1ff35c07217c 77 // the master clock. The reason this works is that the KL25Z ADC ignores
mjr 100:1ff35c07217c 78 // hardware triggers (the TPM trigger is a hardware trigger) that occur when
mjr 100:1ff35c07217c 79 // a conversion is already in progress. So if the ADC sampling time is more
mjr 100:1ff35c07217c 80 // than one master clock period, the ADC will always be busy one clock tick
mjr 100:1ff35c07217c 81 // after a sample starts, so it'll ignore that first clock tick. But as
mjr 100:1ff35c07217c 82 // long as the sampling time is less than *two* master clock periods, the
mjr 100:1ff35c07217c 83 // ADC will always be ready again on the second tick. So we'll get one ADC
mjr 100:1ff35c07217c 84 // sample for every two master clock ticks, exactly as we need.
mjr 100:1ff35c07217c 85 //
mjr 100:1ff35c07217c 86
mjr 100:1ff35c07217c 87 #include "config.h"
mjr 100:1ff35c07217c 88 #include "NewPwm.h"
mjr 100:1ff35c07217c 89 #include "AltAnalogIn.h"
mjr 100:1ff35c07217c 90 #include "SimpleDMA.h"
mjr 100:1ff35c07217c 91 #include "DMAChannels.h"
mjr 100:1ff35c07217c 92
mjr 100:1ff35c07217c 93
mjr 100:1ff35c07217c 94 // Logic Gate Inverters: if invertedLogicGates is true, it means that the
mjr 100:1ff35c07217c 95 // hardware is buffering all of the logic signals (fM, ICG, SH) through an
mjr 100:1ff35c07217c 96 // inverter. The data sheet recommends using a 74HC04 between the host MCU
mjr 100:1ff35c07217c 97 // and the chip logic gates because of the high capacitive load on some of
mjr 100:1ff35c07217c 98 // the gates (particularly SH, 150pF).
mjr 100:1ff35c07217c 99
mjr 100:1ff35c07217c 100 template<bool invertedLogicGates> class TCD1103
mjr 100:1ff35c07217c 101 {
mjr 100:1ff35c07217c 102 public:
mjr 100:1ff35c07217c 103 TCD1103(PinName fmPin, PinName osPin, PinName icgPin, PinName shPin) :
mjr 100:1ff35c07217c 104 fm(fmPin, invertedLogicGates),
mjr 100:1ff35c07217c 105 os(osPin, false, 6, 1), // single sample, 6-cycle long sampling mode, no averaging
mjr 100:1ff35c07217c 106 icg(icgPin),
mjr 100:1ff35c07217c 107 sh(shPin),
mjr 100:1ff35c07217c 108 os_dma(DMAch_TDC_ADC)
mjr 100:1ff35c07217c 109 {
mjr 100:1ff35c07217c 110 // Calibrate the ADC for best accuracy
mjr 100:1ff35c07217c 111 os.calibrate();
mjr 100:1ff35c07217c 112
mjr 100:1ff35c07217c 113 // Idle conditions: SH low, ICG high.
mjr 100:1ff35c07217c 114 sh = logicLow;
mjr 100:1ff35c07217c 115 icg = logicHigh;
mjr 100:1ff35c07217c 116
mjr 100:1ff35c07217c 117 // ADC sample conversion time. This must be calculated based on the
mjr 100:1ff35c07217c 118 // combination of parameters selected for the os() initializer above.
mjr 100:1ff35c07217c 119 // See the KL25 Sub-Family Reference Manual, section 28.4.45, for the
mjr 100:1ff35c07217c 120 // formula.
mjr 100:1ff35c07217c 121 const float ADC_TIME = 2.2083333e-6f; // 6-cycle long sampling, no averaging
mjr 100:1ff35c07217c 122
mjr 100:1ff35c07217c 123 // Set the TPM cycle time to satisfy our timing constraints:
mjr 100:1ff35c07217c 124 //
mjr 100:1ff35c07217c 125 // Tm + epsilon1 < A < 2*Tm - epsilon2
mjr 100:1ff35c07217c 126 //
mjr 100:1ff35c07217c 127 // where A is the ADC conversion time and Tm is the master clock
mjr 100:1ff35c07217c 128 // period, and the epsilons are a margin of safety for any
mjr 100:1ff35c07217c 129 // non-deterministic component to the timing of A and Tm. The
mjr 100:1ff35c07217c 130 // epsilons could be zero if the timing of the ADC is perfectly
mjr 100:1ff35c07217c 131 // deterministic; this must be determined empirically.
mjr 100:1ff35c07217c 132 //
mjr 100:1ff35c07217c 133 // The most conservative solution would be to make epsilon as large
mjr 100:1ff35c07217c 134 // as possible, which means bisecting the time window by making
mjr 100:1ff35c07217c 135 // A = 1.5*T, or, equivalently, T = A/1.5 (the latter form being more
mjr 100:1ff35c07217c 136 // useful because T is the free variable here, as we can only control
mjr 100:1ff35c07217c 137 // A to the extent that we can choose the ADC parameters).
mjr 100:1ff35c07217c 138 //
mjr 100:1ff35c07217c 139 // But we'd also like to make T as short as possible while maintaining
mjr 100:1ff35c07217c 140 // reliable operation. Shorter T yields a higher frame rate, and we
mjr 100:1ff35c07217c 141 // want the frame rate to be as high as possible so that we can track
mjr 100:1ff35c07217c 142 // fast plunger motion accurately. Empirically, we can get reliable
mjr 100:1ff35c07217c 143 // results by using half of the ADC time plus a small buffer time.
mjr 100:1ff35c07217c 144 //
mjr 100:1ff35c07217c 145 fm.getUnit()->period(masterClockPeriod = ADC_TIME/2 + 0.1e-6f);
mjr 100:1ff35c07217c 146 printf("TCD1103 master clock period = %g\r\n", masterClockPeriod);
mjr 100:1ff35c07217c 147
mjr 100:1ff35c07217c 148 // Start the master clock running with a 50% duty cycle
mjr 100:1ff35c07217c 149 fm.write(0.5f);
mjr 100:1ff35c07217c 150
mjr 100:1ff35c07217c 151 // allocate our double pixel buffers
mjr 100:1ff35c07217c 152 pix1 = new uint8_t[nPixSensor*2];
mjr 100:1ff35c07217c 153 pix2 = pix1 + nPixSensor;
mjr 100:1ff35c07217c 154
mjr 100:1ff35c07217c 155 // put the first DMA transfer into the first buffer (pix1)
mjr 101:755f44622abc 156 tIntMin = 0;
mjr 100:1ff35c07217c 157 pixDMA = 0;
mjr 101:755f44622abc 158 clientOwnsStablePix = false;
mjr 100:1ff35c07217c 159
mjr 100:1ff35c07217c 160 // start the sample timer with an arbitrary epoch of "now"
mjr 100:1ff35c07217c 161 t.start();
mjr 100:1ff35c07217c 162
mjr 100:1ff35c07217c 163 // Set up the ADC transfer DMA channel. This channel transfers
mjr 100:1ff35c07217c 164 // the current analog sampling result from the ADC output register
mjr 100:1ff35c07217c 165 // to our pixel array.
mjr 100:1ff35c07217c 166 os.initDMA(&os_dma);
mjr 100:1ff35c07217c 167
mjr 100:1ff35c07217c 168 // Register an interrupt callback so that we're notified when
mjr 100:1ff35c07217c 169 // the last ADC transfer completes.
mjr 100:1ff35c07217c 170 os_dma.attach(this, &TCD1103::transferDone);
mjr 100:1ff35c07217c 171
mjr 100:1ff35c07217c 172 // Set up the ADC to trigger on the master clock's TPM channel
mjr 100:1ff35c07217c 173 os.setTriggerTPM(fm.getUnitNum());
mjr 100:1ff35c07217c 174
mjr 100:1ff35c07217c 175 // clear the timing statistics
mjr 100:1ff35c07217c 176 totalXferTime = 0.0;
mjr 100:1ff35c07217c 177 maxXferTime = 0;
mjr 100:1ff35c07217c 178 minXferTime = 0xffffffff;
mjr 100:1ff35c07217c 179 nRuns = 0;
mjr 100:1ff35c07217c 180
mjr 100:1ff35c07217c 181 // clear random power-up data by clocking through all pixels twice
mjr 100:1ff35c07217c 182 clear();
mjr 100:1ff35c07217c 183 clear();
mjr 101:755f44622abc 184
mjr 101:755f44622abc 185 // start the first transfer
mjr 101:755f44622abc 186 startTransfer();
mjr 100:1ff35c07217c 187 }
mjr 100:1ff35c07217c 188
mjr 100:1ff35c07217c 189 // logic gate levels, based on whether or not the logic gate connections
mjr 100:1ff35c07217c 190 // in the hardware are buffered through inverters
mjr 100:1ff35c07217c 191 static const int logicLow = invertedLogicGates ? 1 : 0;
mjr 100:1ff35c07217c 192 static const bool logicHigh = invertedLogicGates ? 0 : 1;
mjr 100:1ff35c07217c 193
mjr 100:1ff35c07217c 194 // ready to read
mjr 101:755f44622abc 195 bool ready() { return clientOwnsStablePix; }
mjr 100:1ff35c07217c 196
mjr 101:755f44622abc 197 // wait for the DMA subsystem to release a buffer to the client
mjr 101:755f44622abc 198 void wait() { while (!clientOwnsStablePix) ; }
mjr 100:1ff35c07217c 199
mjr 100:1ff35c07217c 200 // Get the stable pixel array. This is the image array from the
mjr 100:1ff35c07217c 201 // previous capture. It remains valid until the next startCapture()
mjr 100:1ff35c07217c 202 // call, at which point this buffer will be reused for the new capture.
mjr 100:1ff35c07217c 203 void getPix(uint8_t * &pix, uint32_t &t)
mjr 100:1ff35c07217c 204 {
mjr 100:1ff35c07217c 205 // return the pixel array that ISN'T assigned to the DMA
mjr 100:1ff35c07217c 206 if (pixDMA)
mjr 100:1ff35c07217c 207 {
mjr 100:1ff35c07217c 208 // DMA owns pix2, so the stable array is pix1
mjr 100:1ff35c07217c 209 pix = pix1;
mjr 100:1ff35c07217c 210 t = t1;
mjr 100:1ff35c07217c 211 }
mjr 100:1ff35c07217c 212 else
mjr 100:1ff35c07217c 213 {
mjr 100:1ff35c07217c 214 // DMA owns pix1, so the stable array is pix2
mjr 100:1ff35c07217c 215 pix = pix2;
mjr 100:1ff35c07217c 216 t = t2;
mjr 100:1ff35c07217c 217 }
mjr 100:1ff35c07217c 218
mjr 100:1ff35c07217c 219 // debugging - print out the pixel transfer time stats periodically
mjr 100:1ff35c07217c 220 static int n;
mjr 100:1ff35c07217c 221 ++n;
mjr 100:1ff35c07217c 222 if (n > 1000)
mjr 100:1ff35c07217c 223 {
mjr 100:1ff35c07217c 224 printf("TCD1103 scan last=%d, min=%d, max=%d (us)\r\n", dtPixXfer, minXferTime, maxXferTime);
mjr 100:1ff35c07217c 225 n = 0;
mjr 100:1ff35c07217c 226 }
mjr 100:1ff35c07217c 227 }
mjr 100:1ff35c07217c 228
mjr 101:755f44622abc 229 // release the client's pixel buffer
mjr 101:755f44622abc 230 void releasePix() { clientOwnsStablePix = false; }
mjr 101:755f44622abc 231
mjr 101:755f44622abc 232 // figure the average scan time from the running totals
mjr 101:755f44622abc 233 uint32_t getAvgScanTime() { return static_cast<uint32_t>(totalXferTime / nRuns);}
mjr 101:755f44622abc 234
mjr 101:755f44622abc 235 // Set the requested minimum integration time. If this is less than the
mjr 101:755f44622abc 236 // sensor's physical minimum time, the physical minimum applies.
mjr 101:755f44622abc 237 virtual void setMinIntTime(uint32_t us)
mjr 100:1ff35c07217c 238 {
mjr 101:755f44622abc 239 tIntMin = us;
mjr 101:755f44622abc 240 }
mjr 101:755f44622abc 241
mjr 101:755f44622abc 242 protected:
mjr 101:755f44622abc 243 // clear the sensor pixels
mjr 101:755f44622abc 244 void clear()
mjr 101:755f44622abc 245 {
mjr 101:755f44622abc 246 // send an SH/ICG pulse sequence to start an integration cycle
mjr 101:755f44622abc 247 // (without initiating a DMA transfer, as we just want to discard
mjr 101:755f44622abc 248 // the incoming samples for a "clear")
mjr 101:755f44622abc 249 tInt = gen_SH_ICG_pulse(false);
mjr 100:1ff35c07217c 250
mjr 101:755f44622abc 251 // wait for one full readout cycle, plus a little extra for padding
mjr 101:755f44622abc 252 ::wait(nPixSensor*masterClockPeriod*2 + 4.0e-6f);
mjr 101:755f44622abc 253 }
mjr 101:755f44622abc 254
mjr 100:1ff35c07217c 255 // Start an image capture from the sensor. Waits the previous
mjr 100:1ff35c07217c 256 // capture to finish if it's still running, then starts a new one
mjr 100:1ff35c07217c 257 // and returns immediately. The new capture proceeds autonomously
mjr 100:1ff35c07217c 258 // via the DMA hardware, so the caller can continue with other
mjr 100:1ff35c07217c 259 // processing during the capture.
mjr 101:755f44622abc 260 void startTransfer()
mjr 100:1ff35c07217c 261 {
mjr 101:755f44622abc 262 // if we own the stable buffer, swap buffers
mjr 101:755f44622abc 263 if (!clientOwnsStablePix)
mjr 100:1ff35c07217c 264 {
mjr 101:755f44622abc 265 // swap buffers
mjr 101:755f44622abc 266 pixDMA ^= 1;
mjr 101:755f44622abc 267
mjr 101:755f44622abc 268 // release the prior DMA buffer to the client
mjr 101:755f44622abc 269 clientOwnsStablePix = true;
mjr 100:1ff35c07217c 270 }
mjr 100:1ff35c07217c 271
mjr 100:1ff35c07217c 272 // Set up the active pixel array as the destination buffer for
mjr 100:1ff35c07217c 273 // the ADC DMA channel.
mjr 100:1ff35c07217c 274 os_dma.destination(pixDMA ? pix2 : pix1, true);
mjr 100:1ff35c07217c 275
mjr 100:1ff35c07217c 276 // Start the read cycle by sending the ICG/SH pulse sequence
mjr 100:1ff35c07217c 277 uint32_t tNewInt = gen_SH_ICG_pulse(true);
mjr 100:1ff35c07217c 278
mjr 100:1ff35c07217c 279 // Set the timestamp for the current active buffer. The ICG/SH
mjr 100:1ff35c07217c 280 // gymnastics we just did transferred the CCD pixels into the sensor's
mjr 100:1ff35c07217c 281 // internal shift register and reset the pixels, starting a new
mjr 100:1ff35c07217c 282 // integration cycle. So the pixels we just shifted started
mjr 100:1ff35c07217c 283 // integrating the *last* time we did that, which we recorded as
mjr 100:1ff35c07217c 284 // tInt at the time. The image we're about to transfer therefore
mjr 100:1ff35c07217c 285 // represents the light collected between tInt and the SH pulse we
mjr 100:1ff35c07217c 286 // just did. The image covers a time range rather than a single
mjr 100:1ff35c07217c 287 // point in time, but we still have to give it a single timestamp.
mjr 100:1ff35c07217c 288 // Use the midpoint of the integration period.
mjr 100:1ff35c07217c 289 uint32_t tmid = (tNewInt + tInt) >> 1;
mjr 100:1ff35c07217c 290 if (pixDMA)
mjr 100:1ff35c07217c 291 t2 = tmid;
mjr 100:1ff35c07217c 292 else
mjr 100:1ff35c07217c 293 t1 = tmid;
mjr 100:1ff35c07217c 294
mjr 100:1ff35c07217c 295 // Record the start time of the currently active integration period
mjr 100:1ff35c07217c 296 tInt = tNewInt;
mjr 100:1ff35c07217c 297 }
mjr 100:1ff35c07217c 298
mjr 101:755f44622abc 299 // End of transfer notification. This runs as an interrupt handler when
mjr 101:755f44622abc 300 // the DMA transfer completes.
mjr 101:755f44622abc 301 void transferDone()
mjr 100:1ff35c07217c 302 {
mjr 101:755f44622abc 303 // add this sample to the timing statistics (for diagnostics and
mjr 101:755f44622abc 304 // performance measurement)
mjr 101:755f44622abc 305 uint32_t now = t.read_us();
mjr 101:755f44622abc 306 uint32_t dt = dtPixXfer = static_cast<uint32_t>(now - tXfer);
mjr 101:755f44622abc 307 totalXferTime += dt;
mjr 101:755f44622abc 308 nRuns += 1;
mjr 101:755f44622abc 309
mjr 101:755f44622abc 310 // collect debug statistics
mjr 101:755f44622abc 311 if (dt < minXferTime) minXferTime = dt;
mjr 101:755f44622abc 312 if (dt > maxXferTime) maxXferTime = dt;
mjr 100:1ff35c07217c 313
mjr 101:755f44622abc 314 // check if there's still time left before we reach the minimum
mjr 101:755f44622abc 315 // requested integration period
mjr 101:755f44622abc 316 uint32_t dtInt = now - tInt;
mjr 101:755f44622abc 317 if (tIntMin > dtInt)
mjr 101:755f44622abc 318 {
mjr 101:755f44622abc 319 // wait for the remaining interval before starting the next
mjr 101:755f44622abc 320 // integration
mjr 101:755f44622abc 321 integrationTimeout.attach(this, &TCD1103::startTransfer, tInt - dtInt);
mjr 101:755f44622abc 322 }
mjr 101:755f44622abc 323 else
mjr 101:755f44622abc 324 {
mjr 101:755f44622abc 325 // we've already reached the minimum integration time - start
mjr 101:755f44622abc 326 // the next transfer immediately
mjr 101:755f44622abc 327 startTransfer();
mjr 101:755f44622abc 328 }
mjr 100:1ff35c07217c 329 }
mjr 100:1ff35c07217c 330
mjr 100:1ff35c07217c 331 // Generate an SH/ICG pulse. This transfers the pixel data from the live
mjr 100:1ff35c07217c 332 // sensor photoreceptors into the sensor's internal shift register, clears
mjr 100:1ff35c07217c 333 // the live pixels, and starts a new integration cycle.
mjr 100:1ff35c07217c 334 //
mjr 100:1ff35c07217c 335 // If start_dma_xfer is true, we'll start the DMA transfer for the ADC
mjr 100:1ff35c07217c 336 // pixel data. We handle this here because the sensor starts clocking
mjr 100:1ff35c07217c 337 // out pixels precisely at the end of the ICG pulse, so we have to be
mjr 100:1ff35c07217c 338 // be very careful about the timing.
mjr 100:1ff35c07217c 339 //
mjr 100:1ff35c07217c 340 // Returns the timestamp (relative to our image timer 't') of the end
mjr 100:1ff35c07217c 341 // of the SH pulse, which is the moment the new integration cycle starts.
mjr 100:1ff35c07217c 342 //
mjr 100:1ff35c07217c 343 // Note that we send these pulses synchronously - that is, this routine
mjr 100:1ff35c07217c 344 // blocks until the pulses have been sent. The overall sequence takes
mjr 100:1ff35c07217c 345 // about 2.5us to 3us, so it's not a significant interruption of the
mjr 100:1ff35c07217c 346 // main loop.
mjr 100:1ff35c07217c 347 //
mjr 100:1ff35c07217c 348 uint32_t gen_SH_ICG_pulse(bool start_dma_xfer)
mjr 100:1ff35c07217c 349 {
mjr 100:1ff35c07217c 350 // If desired, prepare to start the DMA transfer for the ADC data.
mjr 100:1ff35c07217c 351 // (Set up a dummy location to write in lieu of the DMA register if
mjr 100:1ff35c07217c 352 // DMA initiation isn't required, so that we don't have to take the
mjr 100:1ff35c07217c 353 // time for a conditional when we're ready to start the DMA transfer.
mjr 100:1ff35c07217c 354 // The timing there will be extremely tight, and we can't afford the
mjr 100:1ff35c07217c 355 // extra instructions to test a condition.)
mjr 100:1ff35c07217c 356 uint8_t dma_chcfg_dummy = 0;
mjr 100:1ff35c07217c 357 volatile uint8_t *dma_chcfg = start_dma_xfer ? os_dma.prepare(nPixSensor, true) : &dma_chcfg_dummy;
mjr 100:1ff35c07217c 358
mjr 100:1ff35c07217c 359 // The basic idea is to take ICG low, and while holding ICG low,
mjr 100:1ff35c07217c 360 // pulse SH. The coincidence of the two pulses transfers the charge
mjr 100:1ff35c07217c 361 // from the live pixels into the shift register, which effectively
mjr 100:1ff35c07217c 362 // discharges the live pixels and thereby starts a new integration
mjr 100:1ff35c07217c 363 // cycle.
mjr 100:1ff35c07217c 364 //
mjr 100:1ff35c07217c 365 // The timing of the pulse sequence is rather tightly constrained
mjr 100:1ff35c07217c 366 // per the data sheet, so we have to take some care in executing it:
mjr 100:1ff35c07217c 367 //
mjr 100:1ff35c07217c 368 // ICG -> LOW
mjr 100:1ff35c07217c 369 // 100-1000 ns delay (*)
mjr 100:1ff35c07217c 370 // SH -> HIGH
mjr 100:1ff35c07217c 371 // >1000ns delay
mjr 100:1ff35c07217c 372 // SH -> LOW
mjr 100:1ff35c07217c 373 // >1000ns delay
mjr 100:1ff35c07217c 374 // ICG -> high (**)
mjr 100:1ff35c07217c 375 //
mjr 100:1ff35c07217c 376 // There are two steps here that are tricky:
mjr 100:1ff35c07217c 377 //
mjr 100:1ff35c07217c 378 // (*) is a narrow window that we can't achieve with an mbed
mjr 100:1ff35c07217c 379 // microsecond timer. Instead, we'll do a couple of extra writes
mjr 100:1ff35c07217c 380 // to the ICG register, which take about 60ns each.
mjr 100:1ff35c07217c 381 //
mjr 100:1ff35c07217c 382 // (**) has the rather severe constraint that the transition must
mjr 100:1ff35c07217c 383 // occur AND complete while the master clock is high. Other people
mjr 100:1ff35c07217c 384 // working with similar Toshiba chips in MCU projects have suggested
mjr 100:1ff35c07217c 385 // that this constraint can safely be ignored, so maybe the data
mjr 100:1ff35c07217c 386 // sheet's insistence about it is obsolete advice from past Toshiba
mjr 100:1ff35c07217c 387 // sensors that the doc writers carried forward by copy-and-paste.
mjr 100:1ff35c07217c 388 // Toshiba has been making these sorts of chips for a very long time,
mjr 100:1ff35c07217c 389 // and the data sheets for many of them are obvious copy-and-paste
mjr 100:1ff35c07217c 390 // jobs. But let's take the data sheet at its word and assume that
mjr 100:1ff35c07217c 391 // this is important for proper operation. Our best hope of
mjr 100:1ff35c07217c 392 // satisfying this constraint is to synchronize the start of the
mjr 100:1ff35c07217c 393 // ICG->high transition with the start of a TPM cycle on the master
mjr 100:1ff35c07217c 394 // clock. That guarantees that the ICG transition starts when the
mjr 100:1ff35c07217c 395 // clock signal is high (as each TPM cycle starts out high), and
mjr 100:1ff35c07217c 396 // gives us the longest possible runway for the transition to
mjr 100:1ff35c07217c 397 // complete while the clock is still high, as we get the full
mjr 100:1ff35c07217c 398 // length of the high part of the cycle to work with. To quantify,
mjr 100:1ff35c07217c 399 // it gives us about 600ns. The register write takes about 60ns,
mjr 100:1ff35c07217c 400 // and waitEndCycle() adds several instructions of overhead, perhaps
mjr 100:1ff35c07217c 401 // 200ns, so we get around 300ns for the transition to finish. That
mjr 100:1ff35c07217c 402 // should be a gracious plenty assuming that the hardware is set up
mjr 100:1ff35c07217c 403 // with an inverter to buffer the clock signals. The inverter should
mjr 100:1ff35c07217c 404 // be able to pull up the 35pF on ICG in a "typical" 30ns (rise time
mjr 100:1ff35c07217c 405 // plus propagation delay, per the 74HC04 data sheet) and max 150ns.
mjr 100:1ff35c07217c 406 // This seems to be one place where the inverter might really be
mjr 100:1ff35c07217c 407 // necessary to meet the timing requirements, as the KL25Z GPIO
mjr 100:1ff35c07217c 408 // might need more like 2us to pull that load up.
mjr 100:1ff35c07217c 409 //
mjr 100:1ff35c07217c 410 // There's an additional constraint on the timing at the end of the
mjr 100:1ff35c07217c 411 // ICG pulse. The sensor starts clocking out pixels on the rising
mjr 100:1ff35c07217c 412 // edge of the ICG pulse. So we need the ICG pulse end to align
mjr 100:1ff35c07217c 413 // with the start of an ADC cycle. If we get that wrong, all of our
mjr 100:1ff35c07217c 414 // ADC samples will be off by half a clock, so every sample will be
mjr 100:1ff35c07217c 415 // the average of two adjacent pixels instead of one pixel. That
mjr 100:1ff35c07217c 416 // would lose half of the image resolution, which would obviously
mjr 100:1ff35c07217c 417 // be bad. So make certain we're at the tail end of an ADC cycle
mjr 100:1ff35c07217c 418 // by waiting for the ADC "ready" bit to be set.
mjr 100:1ff35c07217c 419 //
mjr 100:1ff35c07217c 420 // The end of the SH pulse triggers the start of a new integration
mjr 100:1ff35c07217c 421 // cycle, so note the time of that pulse for image timestamping
mjr 100:1ff35c07217c 422 // purposes. That will be the start time of the NEXT image we
mjr 100:1ff35c07217c 423 // transfer after we shift out the current sensor pixels, which
mjr 100:1ff35c07217c 424 // represent the pixels from the last time we pulsed SH.
mjr 100:1ff35c07217c 425 //
mjr 100:1ff35c07217c 426 icg = logicLow;
mjr 100:1ff35c07217c 427 icg = logicLow; // for timing, adds about 60ns
mjr 100:1ff35c07217c 428 icg = logicLow; // ditto, another 60ns, total is now 120ns > min 100ns
mjr 100:1ff35c07217c 429 sh = logicHigh;
mjr 100:1ff35c07217c 430 wait_us(1); // >1000ns delay
mjr 100:1ff35c07217c 431 sh = logicLow;
mjr 100:1ff35c07217c 432 uint32_t t_sh = t.read_us(); // this is the start time of the NEXT image
mjr 100:1ff35c07217c 433 wait_us(1); // >1000ns delay
mjr 100:1ff35c07217c 434
mjr 100:1ff35c07217c 435 // Now the tricky part! We have to end the ICG pulse (take ICG high)
mjr 100:1ff35c07217c 436 // at the start of a master clock cycle, AND at the start of an ADC
mjr 100:1ff35c07217c 437 // sampling cycle. The sensor will start clocking out pixels the
mjr 100:1ff35c07217c 438 // instance ICG goes high, so we have to align our ADC cycle so that
mjr 100:1ff35c07217c 439 // we start a sample at almost exactly the same time we take ICG
mjr 100:1ff35c07217c 440 // high.
mjr 100:1ff35c07217c 441 //
mjr 100:1ff35c07217c 442 // Now, every ADC sampling cycle always starts at a rising edge of
mjr 100:1ff35c07217c 443 // the master clock, since the master clock is the ADC trigger. BUT,
mjr 100:1ff35c07217c 444 // the converse is NOT true: every rising edge of the master clock
mjr 100:1ff35c07217c 445 // is NOT an ADC sample start. Recall that we've contrived the timing
mjr 100:1ff35c07217c 446 // so that every OTHER master clock rising edge starts an ADC sample.
mjr 100:1ff35c07217c 447 //
mjr 100:1ff35c07217c 448 // So how do we detect which part of the clock cycle we're in? We
mjr 100:1ff35c07217c 449 // could conceivably use the COCO bit in the ADC status register to
mjr 100:1ff35c07217c 450 // detect the little window between the end of one sample and the
mjr 100:1ff35c07217c 451 // start of the next. Unfortunately, this doesn't work: the COCO
mjr 100:1ff35c07217c 452 // bit is never actually set for the duration of even a single CPU
mjr 100:1ff35c07217c 453 // instruction in our setup, no matter how loose we make the timing
mjr 100:1ff35c07217c 454 // between the ADC and the fM cycle. I think the reason is the DMA
mjr 100:1ff35c07217c 455 // setup: the COCO bit triggers the DMA, and the DMA controller
mjr 100:1ff35c07217c 456 // reads the ADC result register (the DMA source in our setup),
mjr 100:1ff35c07217c 457 // which has the side effect of clearing COCO. I've experimented
mjr 100:1ff35c07217c 458 // with this using different timing parameters, and the result is
mjr 100:1ff35c07217c 459 // always the same: the CPU *never* sees the COCO bit set. The DMA
mjr 100:1ff35c07217c 460 // trigger timing is evidently deterministic such that the DMA unit
mjr 100:1ff35c07217c 461 // invariably gets its shot at reading ADC0->R before the CPU does.
mjr 100:1ff35c07217c 462 //
mjr 100:1ff35c07217c 463 // The COCO approach would be a little iffy anyway, since we want the
mjr 100:1ff35c07217c 464 // ADC idle time to be as short as possible, which wouldn't give us
mjr 100:1ff35c07217c 465 // much time to do all we have to do in the COCO period, even if
mjr 100:1ff35c07217c 466 // there were one. What we can do instead is seize control of the
mjr 100:1ff35c07217c 467 // ADC cycle timing: rather than trying to detect when the cycle
mjr 100:1ff35c07217c 468 // ends, we can specify when it begins. We can do this by canceling
mjr 100:1ff35c07217c 469 // the TPM->ADC trigger and aborting any conversion in progress, then
mjr 100:1ff35c07217c 470 // reprogramming the TPM->ADC trigger at our leisure. What we *can*
mjr 100:1ff35c07217c 471 // detect reliably is the start of a TPM cycle. So here's our
mjr 100:1ff35c07217c 472 // strategy:
mjr 100:1ff35c07217c 473 //
mjr 100:1ff35c07217c 474 // - Turn off the TPM->ADC trigger and abort the current conversion
mjr 100:1ff35c07217c 475 // - Wait until a new TPM cycle starts
mjr 100:1ff35c07217c 476 // - Reset the TPM->ADC trigger. The first new conversion will
mjr 100:1ff35c07217c 477 // start on the next TPM cycle, so we have the remainder of
mjr 100:1ff35c07217c 478 // the current TPM cycle to make this happen (about 1us, enough
mjr 100:1ff35c07217c 479 // for 16 CPU instructions - plenty for this step)
mjr 100:1ff35c07217c 480 // - Wait for the new TPM cycle
mjr 100:1ff35c07217c 481 // - End the ICG pulse
mjr 100:1ff35c07217c 482 //
mjr 100:1ff35c07217c 483
mjr 100:1ff35c07217c 484 // The timing is so tight here that we want to be sure we're not
mjr 100:1ff35c07217c 485 // interrupted by other tasks - disable interrupts.
mjr 100:1ff35c07217c 486 __disable_irq();
mjr 100:1ff35c07217c 487
mjr 100:1ff35c07217c 488 // disable the TPM->ADC trigger and abort the current conversion
mjr 100:1ff35c07217c 489 os.stop();
mjr 100:1ff35c07217c 490
mjr 100:1ff35c07217c 491 // Enable the DMA controller for the new transfer from the ADC.
mjr 100:1ff35c07217c 492 // The sensor will start clocking out new samples at the ICG rising
mjr 100:1ff35c07217c 493 // edge, so the next ADC sample to complete will represent the first
mjr 100:1ff35c07217c 494 // pixel in the new frame. So we need the DMA ready to go at the
mjr 100:1ff35c07217c 495 // very next sample. Recall that the DMA is triggered by ADC
mjr 100:1ff35c07217c 496 // completion, and ADC is stopped right now, so enabling the DMA
mjr 100:1ff35c07217c 497 // won't have any immediate effect - it just spools it up so that
mjr 100:1ff35c07217c 498 // it's ready to move samples as soon as we resume the ADC.
mjr 100:1ff35c07217c 499 *dma_chcfg |= DMAMUX_CHCFG_ENBL_MASK;
mjr 100:1ff35c07217c 500
mjr 100:1ff35c07217c 501 // wait for the start of a new master clock cycle
mjr 100:1ff35c07217c 502 fm.waitEndCycle();
mjr 100:1ff35c07217c 503
mjr 100:1ff35c07217c 504 // Okay, a master clock cycle just started, so we have about 1us
mjr 100:1ff35c07217c 505 // (about 16 CPU instructions) before the next one begins. Resume
mjr 100:1ff35c07217c 506 // ADC sampling. The first new sample will start with the next
mjr 100:1ff35c07217c 507 // TPM cycle 1us from now. (This step takes about 3 instructions.)
mjr 100:1ff35c07217c 508 os.resume();
mjr 100:1ff35c07217c 509
mjr 100:1ff35c07217c 510 // Okay, everything is queued up! We just have to fire the starting
mjr 100:1ff35c07217c 511 // pistol on the sensor at the right moment. And that right moment
mjr 100:1ff35c07217c 512 // is the start of the next TPM cycle. Wait for it...
mjr 100:1ff35c07217c 513 fm.waitEndCycle();
mjr 100:1ff35c07217c 514
mjr 100:1ff35c07217c 515 // And go!
mjr 100:1ff35c07217c 516 icg = logicHigh;
mjr 100:1ff35c07217c 517
mjr 100:1ff35c07217c 518 // note the start time of the transfer
mjr 100:1ff35c07217c 519 tXfer = t.read_us();
mjr 100:1ff35c07217c 520
mjr 100:1ff35c07217c 521 // done with the critical timing section
mjr 100:1ff35c07217c 522 __enable_irq();
mjr 100:1ff35c07217c 523
mjr 100:1ff35c07217c 524 // return the timestamp of the end of the SH pulse - this is the start
mjr 100:1ff35c07217c 525 // of the new integration period that we just initiated
mjr 100:1ff35c07217c 526 return t_sh;
mjr 100:1ff35c07217c 527 }
mjr 100:1ff35c07217c 528
mjr 100:1ff35c07217c 529 // master clock
mjr 100:1ff35c07217c 530 NewPwmOut fm;
mjr 100:1ff35c07217c 531
mjr 100:1ff35c07217c 532 // analog input for reading the pixel voltage level
mjr 100:1ff35c07217c 533 AltAnalogIn_8bit os;
mjr 100:1ff35c07217c 534
mjr 100:1ff35c07217c 535 // Integration Clear Gate output
mjr 100:1ff35c07217c 536 DigitalOut icg;
mjr 100:1ff35c07217c 537
mjr 100:1ff35c07217c 538 // Shift Gate output
mjr 100:1ff35c07217c 539 DigitalOut sh;
mjr 100:1ff35c07217c 540
mjr 100:1ff35c07217c 541 // DMA channel for the analog input
mjr 100:1ff35c07217c 542 SimpleDMA os_dma;
mjr 100:1ff35c07217c 543
mjr 100:1ff35c07217c 544 // Master clock period, in seconds, calculated based on the ADC timing
mjr 100:1ff35c07217c 545 float masterClockPeriod;
mjr 100:1ff35c07217c 546
mjr 100:1ff35c07217c 547 // Number of pixels. The TCD1103 has 1500 image pixels, plus 32 dummy
mjr 100:1ff35c07217c 548 // pixels at the front end (before the first image pixel) and another 14
mjr 100:1ff35c07217c 549 // dummy pixels at the back end. The sensor always transfers the full
mjr 100:1ff35c07217c 550 // file on each read cycle, including the dummies, so we have to make
mjr 100:1ff35c07217c 551 // room for the dummy pixels during each read.
mjr 100:1ff35c07217c 552 static const int nPixSensor = 1546;
mjr 100:1ff35c07217c 553
mjr 100:1ff35c07217c 554 // pixel buffers - we keep two buffers so that we can transfer the
mjr 100:1ff35c07217c 555 // current sensor data into one buffer via DMA while we concurrently
mjr 100:1ff35c07217c 556 // process the last buffer
mjr 100:1ff35c07217c 557 uint8_t *pix1; // pixel array 1
mjr 100:1ff35c07217c 558 uint8_t *pix2; // pixel array 2
mjr 100:1ff35c07217c 559
mjr 100:1ff35c07217c 560 // Timestamps of pix1 and pix2 arrays, in microseconds, in terms of the
mjr 100:1ff35c07217c 561 // sample timer (this->t).
mjr 100:1ff35c07217c 562 uint32_t t1;
mjr 100:1ff35c07217c 563 uint32_t t2;
mjr 100:1ff35c07217c 564
mjr 100:1ff35c07217c 565 // DMA target buffer. This is the buffer for the next DMA transfer.
mjr 100:1ff35c07217c 566 // 0 means pix1, 1 means pix2. The other buffer contains the stable
mjr 100:1ff35c07217c 567 // data from the last transfer.
mjr 100:1ff35c07217c 568 uint8_t pixDMA;
mjr 100:1ff35c07217c 569
mjr 101:755f44622abc 570 // Stable buffer ownership. At any given time, the DMA subsystem owns
mjr 101:755f44622abc 571 // the buffer specified by pixDMA. The other buffer - the "stable" buffer,
mjr 101:755f44622abc 572 // which contains the most recent completed frame, can be owned by EITHER
mjr 101:755f44622abc 573 // the client or by the DMA subsystem. Each time a DMA transfer completes,
mjr 101:755f44622abc 574 // the DMA subsystem looks at the stable buffer owner flag to determine
mjr 101:755f44622abc 575 // what to do:
mjr 101:755f44622abc 576 //
mjr 101:755f44622abc 577 // - If the DMA subsystem owns the stable buffer, it swaps buffers. This
mjr 101:755f44622abc 578 // makes the newly completed DMA buffer the new stable buffer, and makes
mjr 101:755f44622abc 579 // the old stable buffer the new DMA buffer. At this time, the DMA
mjr 101:755f44622abc 580 // subsystem also changes the stable buffer ownership to CLIENT.
mjr 101:755f44622abc 581 //
mjr 101:755f44622abc 582 // - If the CLIENT owns the stable buffer, the DMA subsystem can't swap
mjr 101:755f44622abc 583 // buffers, because the client is still using the stable buffer. It
mjr 101:755f44622abc 584 // simply leaves things as they are.
mjr 101:755f44622abc 585 //
mjr 101:755f44622abc 586 // In either case, the DMA system starts a new transfer at this point.
mjr 101:755f44622abc 587 //
mjr 101:755f44622abc 588 // The client, meanwhile, is free to access the stable buffer when it has
mjr 101:755f44622abc 589 // ownership. If the client *doesn't* have ownership, it must wait for
mjr 101:755f44622abc 590 // the ownership to be transferred, which can only be done by the DMA
mjr 101:755f44622abc 591 // subsystem on completing a transfer.
mjr 101:755f44622abc 592 //
mjr 101:755f44622abc 593 // When the client is done with the stable buffer, it transfers ownership
mjr 101:755f44622abc 594 // back to the DMA subsystem.
mjr 101:755f44622abc 595 //
mjr 101:755f44622abc 596 // Transfers of ownership from DMA to CLIENT are done only by DMA.
mjr 101:755f44622abc 597 // Transfers from CLIENT to DMA are done only by CLIENT. So whoever has
mjr 101:755f44622abc 598 // ownership now is responsible for transferring ownership.
mjr 101:755f44622abc 599 //
mjr 101:755f44622abc 600 volatile bool clientOwnsStablePix;
mjr 101:755f44622abc 601
mjr 101:755f44622abc 602 // Minimum requested integration time, in microseconds
mjr 101:755f44622abc 603 uint32_t tIntMin;
mjr 101:755f44622abc 604
mjr 101:755f44622abc 605 // Timeout for generating an interrupt at the end of the integration period
mjr 101:755f44622abc 606 Timeout integrationTimeout;
mjr 101:755f44622abc 607
mjr 100:1ff35c07217c 608 // timing statistics
mjr 100:1ff35c07217c 609 Timer t; // sample timer
mjr 100:1ff35c07217c 610 uint32_t tInt; // start time (us) of current integration period
mjr 100:1ff35c07217c 611 uint32_t tXfer; // start time (us) of current pixel transfer
mjr 100:1ff35c07217c 612 uint32_t dtPixXfer; // pixel transfer time of last frame
mjr 100:1ff35c07217c 613 uint64_t totalXferTime; // total time consumed by all reads so far
mjr 100:1ff35c07217c 614 uint32_t nRuns; // number of runs so far
mjr 100:1ff35c07217c 615
mjr 100:1ff35c07217c 616 // debugging - min/max transfer time statistics
mjr 100:1ff35c07217c 617 uint32_t minXferTime;
mjr 100:1ff35c07217c 618 uint32_t maxXferTime;
mjr 100:1ff35c07217c 619 };