Mike R / Mbed 2 deprecated Pinscape_Controller_V2

Dependencies:   mbed FastIO FastPWM USBDevice

Fork of Pinscape_Controller by Mike R

Committer:
mjr
Date:
Fri Feb 26 18:42:03 2016 +0000
Revision:
48:058ace2aed1d
Parent:
47:df7a88cd249c
Child:
53:9b2611964afc
New plunger processing 1

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mjr 2:c174f9ee414a 1 /*
mjr 47:df7a88cd249c 2 * TSL1410R/TSL1412R interface class.
mjr 47:df7a88cd249c 3 *
mjr 47:df7a88cd249c 4 * This provides a high-level interface for the Taos TSL1410R and
mjr 47:df7a88cd249c 5 * TSL1412R linear CCD array sensors. (The 1410R and 1412R are
mjr 47:df7a88cd249c 6 * identical except for the pixel array size, which the caller
mjr 47:df7a88cd249c 7 * provides as a parameter when instantiating the class.)
mjr 47:df7a88cd249c 8 *
mjr 48:058ace2aed1d 9 * The TSL141xR sensors can take images very quickly. The minimum
mjr 48:058ace2aed1d 10 * integration time (the time it takes to collect photoelectric
mjr 48:058ace2aed1d 11 * charge on the pixels - roughly equal to the shutter speed for
mjr 48:058ace2aed1d 12 * a film camera) is less than a millisecond, per the data sheet.
mjr 48:058ace2aed1d 13 * The sensor is flexible about timing, though, and allows for
mjr 48:058ace2aed1d 14 * much longer integration periods than the minimum. It simply
mjr 48:058ace2aed1d 15 * gathers more light (more photoelectric charge) as the integration
mjr 48:058ace2aed1d 16 * period increases. However, for our purposes in the Pinscape
mjr 48:058ace2aed1d 17 * Controller, we want the highest possible frame rate, as we're
mjr 48:058ace2aed1d 18 * trying to capture the motion of a fast-moving object (the plunger).
mjr 48:058ace2aed1d 19 * The KL25Z can't actually keep up with the fastest frame rate the
mjr 48:058ace2aed1d 20 * sensor can achieve, the limiting factor being its ADC. The
mjr 48:058ace2aed1d 21 * sensor transfers pixels to the MCU serially, and each pixel is
mjr 48:058ace2aed1d 22 * transferred as an analog voltage level, so we have to collect
mjr 48:058ace2aed1d 23 * one ADC sample per pixel. Our maximum frame rate is therefore
mjr 48:058ace2aed1d 24 * determined by the product of the minimum ADC sample time and
mjr 48:058ace2aed1d 25 * the number of pixels.
mjr 47:df7a88cd249c 26 *
mjr 48:058ace2aed1d 27 * The fastest operating mode for the KL25Z ADC is its "continuous"
mjr 48:058ace2aed1d 28 * mode, where it automatically starts taking a new sample every time
mjr 48:058ace2aed1d 29 * it completes the previous one. The fastest way to transfer the
mjr 48:058ace2aed1d 30 * samples to memory in this mode is via the hardware DMA controller.
mjr 48:058ace2aed1d 31 *
mjr 48:058ace2aed1d 32 * It takes a pretty tricky setup to make this work. I don't like
mjr 48:058ace2aed1d 33 * tricky setups - I prefer something easy to understand - but in
mjr 48:058ace2aed1d 34 * this case it's justified because of the dramatic speed improvement
mjr 48:058ace2aed1d 35 * and the importance to the application of maximizing the speed.
mjr 48:058ace2aed1d 36 * I'm pretty sure there's no other way to even get close to the
mjr 48:058ace2aed1d 37 * speed we can achieve with the continuous ADC/DMA combination.
mjr 48:058ace2aed1d 38 * The ADC/DMA mode gives us pixel read times of about 2us, vs a
mjr 48:058ace2aed1d 39 * minimum of about 14us for the next best method I've found.
mjr 48:058ace2aed1d 40 * Using this mode, we can read TSL1410R's 1280 pixels at full
mjr 48:058ace2aed1d 41 * resolution in about 2.5ms. That's a frame rate of 400 frames
mjr 48:058ace2aed1d 42 * per second, which is fast enough to capture a fast-moving
mjr 48:058ace2aed1d 43 * plunger with minimal motion blur.
mjr 48:058ace2aed1d 44 *
mjr 48:058ace2aed1d 45 * (Note that the TSL141xR sensors have a "parallel" that lets
mjr 48:058ace2aed1d 46 * them physically deliver two pixels at once to the MCU. This
mjr 48:058ace2aed1d 47 * could potentially provide a 2x speedup by halving all of the
mjr 48:058ace2aed1d 48 * clock counts. Unfortunately, the KL25Z can't take advantage
mjr 48:058ace2aed1d 49 * of this, because its ADC hardware is only capable of taking one
mjr 48:058ace2aed1d 50 * sample at a time. The ADC has multiple channels that can connect
mjr 48:058ace2aed1d 51 * to multiple GPIO pins, but internally it has only one physical
mjr 48:058ace2aed1d 52 * sampler. Using the parallel mode would paradoxically be slower
mjr 48:058ace2aed1d 53 * than our continuous ADC/DMA method because it would preclude use
mjr 48:058ace2aed1d 54 * of the continuous sampling mode.)
mjr 48:058ace2aed1d 55 *
mjr 48:058ace2aed1d 56 * Here's the tricky approach we use:
mjr 47:df7a88cd249c 57 *
mjr 47:df7a88cd249c 58 * First, we put the analog input port (the ADC == Analog-to-Digital
mjr 47:df7a88cd249c 59 * Converter) in "continuous" mode, at the highest clock speed we can
mjr 47:df7a88cd249c 60 * program with the available clocks and the fastest read cycle
mjr 47:df7a88cd249c 61 * available in the ADC hardware. (The analog input port is the
mjr 47:df7a88cd249c 62 * GPIO pin attached to the sensor's AO == Analog Output pin, where
mjr 47:df7a88cd249c 63 * it outputs each pixel's value, one at a time, as an analog voltage
mjr 47:df7a88cd249c 64 * level.) In continuous mode, every time the ADC finishes taking a
mjr 47:df7a88cd249c 65 * sample, it stores the result value in its output register and then
mjr 47:df7a88cd249c 66 * immediately starts taking a new sample. This means that no MCU
mjr 47:df7a88cd249c 67 * (or even DMA) action is required to start each new sample. This
mjr 47:df7a88cd249c 68 * is where most of the speedup comes from, since it takes significant
mjr 47:df7a88cd249c 69 * time (multiple microseconds) to move data through the peripheral
mjr 47:df7a88cd249c 70 * registers, and it takes more time (also multiple microseconds) for
mjr 47:df7a88cd249c 71 * the ADC to spin up for each new sample when in single-sample mode.
mjr 47:df7a88cd249c 72 * We cut out about 7us this way and get the time per sample down to
mjr 47:df7a88cd249c 73 * about 2us. This is close to the documented maximum speed for the
mjr 47:df7a88cd249c 74 * ADC hardware.
mjr 47:df7a88cd249c 75 *
mjr 47:df7a88cd249c 76 * Second, we use the DMA controller to read the ADC result register
mjr 47:df7a88cd249c 77 * and store each sample in a memory array for processing. The ADC
mjr 47:df7a88cd249c 78 * hardware is designed to work with the DMA controller by signaling
mjr 47:df7a88cd249c 79 * the DMA controller when a new sample is ready; this allows DMA to
mjr 47:df7a88cd249c 80 * move each sample immediately when it's available without any CPU
mjr 47:df7a88cd249c 81 * involvement.
mjr 2:c174f9ee414a 82 *
mjr 47:df7a88cd249c 83 * Third - and this is where it really gets tricky - we use two
mjr 47:df7a88cd249c 84 * additional "linked" DMA channels to generate the clock signal
mjr 47:df7a88cd249c 85 * to the CCD sensor. The clock signal is how we tell the CCD when
mjr 47:df7a88cd249c 86 * to place the next pixel voltage on its AO pin, so the clock has
mjr 47:df7a88cd249c 87 * to be generated in lock step with the ADC sampling cycle. The
mjr 47:df7a88cd249c 88 * ADC timing isn't perfectly uniform or predictable, so we can't
mjr 47:df7a88cd249c 89 * just generate the pixel clock with a *real* clock. We have to
mjr 47:df7a88cd249c 90 * time the signal exactly with the ADC, which means that we have
mjr 47:df7a88cd249c 91 * to generate it from the ADC "sample is ready" signal. Fortunately,
mjr 47:df7a88cd249c 92 * there is just such a signal, and in fact we're already using it,
mjr 47:df7a88cd249c 93 * as described above, to tell the DMA when to move each result from
mjr 47:df7a88cd249c 94 * the ADC output register to our memory array. So how do we use this
mjr 47:df7a88cd249c 95 * to generate the CCD clock? The answer lies in the DMA controller's
mjr 47:df7a88cd249c 96 * channel linking feature. This allows one DMA channel to trigger a
mjr 47:df7a88cd249c 97 * second DMA channel each time the first channel completes one
mjr 47:df7a88cd249c 98 * transfer. And we can use DMA to control our clock GPIO pin by
mjr 47:df7a88cd249c 99 * using the pin's GPIO IPORT register as the DMA destination address.
mjr 47:df7a88cd249c 100 * Specifically, we can take the clock high by writing our pin's bit
mjr 47:df7a88cd249c 101 * pattern to the PSOR ("set output") register, and we can take the
mjr 47:df7a88cd249c 102 * clock low by writing to the PCOR ("clear output") register. We
mjr 47:df7a88cd249c 103 * use one DMA channel for each of these operations.
mjr 47:df7a88cd249c 104 *
mjr 47:df7a88cd249c 105 * Putting it all together, the cascade of linked DMA channels
mjr 47:df7a88cd249c 106 * works like this:
mjr 47:df7a88cd249c 107 *
mjr 48:058ace2aed1d 108 * - We kick off the first ADC sample.
mjr 48:058ace2aed1d 109 *
mjr 48:058ace2aed1d 110 * - When the ADC sample completes, the ADC DMA trigger fires,
mjr 48:058ace2aed1d 111 * which triggers channel 1, the "Clock Up" channel. This
mjr 48:058ace2aed1d 112 * performs one transfer of the clock GPIO bit to the clock
mjr 48:058ace2aed1d 113 * PSOR register, taking the clock high, which causes the CCD
mjr 48:058ace2aed1d 114 * to move the next pixel onto AO.
mjr 47:df7a88cd249c 115 *
mjr 47:df7a88cd249c 116 * - After the Clock Up channel does its transfer, it triggers
mjr 47:df7a88cd249c 117 * its link to channel 2, the ADC transfer channel. This
mjr 47:df7a88cd249c 118 * channel moves the ADC output register value to our memory
mjr 47:df7a88cd249c 119 * array.
mjr 47:df7a88cd249c 120 *
mjr 47:df7a88cd249c 121 * - After the ADC channel does its transfer, it triggers channel
mjr 47:df7a88cd249c 122 * 3, the "Clock Down" channel. This performs one transfer of
mjr 47:df7a88cd249c 123 * the clock GPIO bit to the clock PCOR register, taking the
mjr 47:df7a88cd249c 124 * clock low.
mjr 47:df7a88cd249c 125 *
mjr 47:df7a88cd249c 126 * Note that the order of the channels - Clock Up, ADC, Clock Down -
mjr 48:058ace2aed1d 127 * is important. It ensures that we don't toggle the clock line
mjr 48:058ace2aed1d 128 * too quickly. The CCD has a minimum pulse duration of 50ns for
mjr 48:058ace2aed1d 129 * the clock signal. The DMA controller is so fast that it might
mjr 47:df7a88cd249c 130 * toggle the clock faster than this limit if we did the Up and
mjr 48:058ace2aed1d 131 * Down transfers back-to-back.
mjr 47:df7a88cd249c 132 *
mjr 48:058ace2aed1d 133 * Note also that it's important for Clock Up to be the very first
mjr 48:058ace2aed1d 134 * operation after the DMA trigger. The ADC is in continuous mode,
mjr 48:058ace2aed1d 135 * meaning that it starts taking a new sample immediately upon
mjr 48:058ace2aed1d 136 * finishing the previous one. So when the ADC DMA signal fires,
mjr 48:058ace2aed1d 137 * the new sample is already starting. We therefore have to get
mjr 48:058ace2aed1d 138 * the next pixel onto the sampling pin immediately, or as close
mjr 48:058ace2aed1d 139 * to immediately as possible. The sensor's "analog output
mjr 48:058ace2aed1d 140 * settling time" is 120ns - this is the time for a new pixel
mjr 48:058ace2aed1d 141 * voltage to stabilize on AO after a clock rising edge. So
mjr 48:058ace2aed1d 142 * assuming that the ADC raises the DMA signal immediately on
mjr 48:058ace2aed1d 143 * sample completion, and the DMA controller responds within a
mjr 48:058ace2aed1d 144 * couple of MCU clock cycles, we should have the new pixel voltage
mjr 48:058ace2aed1d 145 * stable on the sampling pin by about 200ns after the new ADC
mjr 48:058ace2aed1d 146 * sample cycle starts. The sampling cycle with our current
mjr 48:058ace2aed1d 147 * parameters is about 2us, so the voltage level is stable for
mjr 48:058ace2aed1d 148 * 90% of the cycle.
mjr 47:df7a88cd249c 149 *
mjr 48:058ace2aed1d 150 * Also, note that it's okay that the ADC sample transfer doesn't
mjr 48:058ace2aed1d 151 * happen until after the Clock Up DMA transfer. The ADC output
mjr 48:058ace2aed1d 152 * register holds the last result until the next sample completes,
mjr 48:058ace2aed1d 153 * so we have about 2us to grab it. The first Clock Up DMA
mjr 48:058ace2aed1d 154 * transfer only takes a couple of clocks - order of 100ns - so
mjr 48:058ace2aed1d 155 * we get to it with time to spare.
mjr 47:df7a88cd249c 156 *
mjr 48:058ace2aed1d 157 * (Note that it would nicer to handle the clock with a single DMA
mjr 48:058ace2aed1d 158 * channel, since DMA channels are a limited resource. We could
mjr 48:058ace2aed1d 159 * conceivably consolidate the clock generator one DMA channel by
mjr 48:058ace2aed1d 160 * switching the DMA destination to the PTOR "toggle" register, and
mjr 48:058ace2aed1d 161 * writing *two* times per trigger - once to toggle the clock up,
mjr 48:058ace2aed1d 162 * and a second time to toggle it down. But I haven't found a way
mjr 48:058ace2aed1d 163 * to make this work. The obstacle is that the DMA controller can
mjr 48:058ace2aed1d 164 * only do one transfer per trigger in the fully autonomous mode
mjr 48:058ace2aed1d 165 * we're using, and to make this toggle scheme work, we'd have to do
mjr 48:058ace2aed1d 166 * two writes per trigger. Maybe even three or four: I think we'd
mjr 48:058ace2aed1d 167 * have to throw in one or two no-op writes (of all zeroes) between
mjr 48:058ace2aed1d 168 * the two toggles, to pad the timing to ensure that the clock pulse
mjr 48:058ace2aed1d 169 * width is over the sensor's 50ns minimum. But it's the same issue
mjr 48:058ace2aed1d 170 * whether it's two writes or four. The DMA controller does have a
mjr 48:058ace2aed1d 171 * "continuous" mode that does an entire transfer on a single trigger,
mjr 48:058ace2aed1d 172 * but it can't reset itself after such a transfer; CPU intervention
mjr 48:058ace2aed1d 173 * is required to do that, which means we'd have to service an
mjr 48:058ace2aed1d 174 * interrupt on every ADC cycle to set up the next clock write.
mjr 48:058ace2aed1d 175 * Given the 2us cycle time, an interrupt would create a ton of CPU
mjr 48:058ace2aed1d 176 * load, and I don't think the CPU is fast enough to reliably complete
mjr 48:058ace2aed1d 177 * the work we'd have to do on each 2us cycle. Fortunately, at
mjr 48:058ace2aed1d 178 * the moment we can afford to dedicate three channels to this
mjr 48:058ace2aed1d 179 * module. We only have one other module using the DMA at all
mjr 48:058ace2aed1d 180 * (the TLC5940 PWM controller interface), and it only needs one
mjr 48:058ace2aed1d 181 * channel. So the KL25Z's complement of four DMA channels is just
mjr 48:058ace2aed1d 182 * enough for all of our needs for the moment.)
mjr 2:c174f9ee414a 183 */
mjr 2:c174f9ee414a 184
mjr 35:e959ffba78fd 185 #include "mbed.h"
mjr 35:e959ffba78fd 186 #include "config.h"
mjr 43:7a6364d82a41 187 #include "AltAnalogIn.h"
mjr 45:c42166b2878c 188 #include "SimpleDMA.h"
mjr 47:df7a88cd249c 189 #include "DMAChannels.h"
mjr 2:c174f9ee414a 190
mjr 35:e959ffba78fd 191 #ifndef TSL1410R_H
mjr 35:e959ffba78fd 192 #define TSL1410R_H
mjr 47:df7a88cd249c 193
mjr 35:e959ffba78fd 194
mjr 47:df7a88cd249c 195 // To allow DMA access to the clock pin, we need to point the DMA
mjr 47:df7a88cd249c 196 // controller to the IOPORT registers that control the pin. PORT_BASE()
mjr 47:df7a88cd249c 197 // gives us the address of the register group for the 32 GPIO pins with
mjr 47:df7a88cd249c 198 // the same letter name as our target pin (e.g., PTA0 through PTA31),
mjr 47:df7a88cd249c 199 // and PINMASK gives us the bit pattern to write to those registers to
mjr 47:df7a88cd249c 200 // access our single GPIO pin. Each register group has three special
mjr 47:df7a88cd249c 201 // registers that update the pin in particular ways: PSOR ("set output
mjr 47:df7a88cd249c 202 // register") turns pins on, PCOR ("clear output register") turns pins
mjr 47:df7a88cd249c 203 // off, and PTOR ("toggle output register") toggle pins to the opposite
mjr 47:df7a88cd249c 204 // of their current values. These registers have special semantics:
mjr 47:df7a88cd249c 205 // writing a bit as 0 has no effect on the corresponding pin, while
mjr 47:df7a88cd249c 206 // writing a bit as 1 performs the register's action on the pin. This
mjr 47:df7a88cd249c 207 // allows a single GPIO pin to be set, cleared, or toggled with a
mjr 47:df7a88cd249c 208 // 32-bit write to one of these registers, without affecting any of the
mjr 47:df7a88cd249c 209 // other pins addressed by the register. (It also allows changing any
mjr 47:df7a88cd249c 210 // group of pins with a single write, although we don't use that
mjr 47:df7a88cd249c 211 // feature here.)
mjr 35:e959ffba78fd 212 //
mjr 47:df7a88cd249c 213 // - To turn a pin ON: PORT_BASE(pin)->PSOR = PINMASK(pin)
mjr 47:df7a88cd249c 214 // - To turn a pin OFF: PORT_BASE(pin)->PCOR = PINMASK(pin)
mjr 47:df7a88cd249c 215 // - To toggle a pin: PORT_BASE(pin)->PTOR = PINMASK(pin)
mjr 47:df7a88cd249c 216 //
mjr 43:7a6364d82a41 217 #define GPIO_PORT(pin) (((unsigned int)(pin)) >> PORT_SHIFT)
mjr 47:df7a88cd249c 218 #define GPIO_PORT_BASE(pin) ((GPIO_Type *)(PTA_BASE + GPIO_PORT(pin) * 0x40))
mjr 43:7a6364d82a41 219 #define GPIO_PINMASK(pin) gpio_set(pin)
mjr 2:c174f9ee414a 220
mjr 35:e959ffba78fd 221 class TSL1410R
mjr 2:c174f9ee414a 222 {
mjr 2:c174f9ee414a 223 public:
mjr 47:df7a88cd249c 224 TSL1410R(int nPixSensor, PinName siPin, PinName clockPin, PinName ao1Pin, PinName /*ao2Pin*/)
mjr 47:df7a88cd249c 225 : adc_dma(DMAch_ADC),
mjr 47:df7a88cd249c 226 clkUp_dma(DMAch_CLKUP),
mjr 47:df7a88cd249c 227 clkDn_dma(DMAch_CLKDN),
mjr 47:df7a88cd249c 228 si(siPin),
mjr 47:df7a88cd249c 229 clock(clockPin),
mjr 47:df7a88cd249c 230 ao1(ao1Pin, true),
mjr 47:df7a88cd249c 231 nPixSensor(nPixSensor)
mjr 17:ab3cec0c8bf4 232 {
mjr 48:058ace2aed1d 233 // start the sample timer with an arbitrary zero point of 'now'
mjr 48:058ace2aed1d 234 t.start();
mjr 48:058ace2aed1d 235
mjr 47:df7a88cd249c 236 // allocate our double pixel buffers
mjr 47:df7a88cd249c 237 pix1 = new uint8_t[nPixSensor*2];
mjr 47:df7a88cd249c 238 pix2 = pix1 + nPixSensor;
mjr 35:e959ffba78fd 239
mjr 47:df7a88cd249c 240 // put the first DMA transfer into the first buffer (pix1)
mjr 47:df7a88cd249c 241 pixDMA = 0;
mjr 48:058ace2aed1d 242 running = false;
mjr 47:df7a88cd249c 243
mjr 35:e959ffba78fd 244 // remember the clock pin port base and pin mask for fast access
mjr 35:e959ffba78fd 245 clockPort = GPIO_PORT_BASE(clockPin);
mjr 35:e959ffba78fd 246 clockMask = GPIO_PINMASK(clockPin);
mjr 35:e959ffba78fd 247
mjr 43:7a6364d82a41 248 // clear out power-on random data by clocking through all pixels twice
mjr 17:ab3cec0c8bf4 249 clear();
mjr 17:ab3cec0c8bf4 250 clear();
mjr 43:7a6364d82a41 251
mjr 47:df7a88cd249c 252 // Set up the Clock Up DMA channel. This channel takes the
mjr 47:df7a88cd249c 253 // clock high by writing the clock bit to the PSOR (set output)
mjr 47:df7a88cd249c 254 // register for the clock pin.
mjr 47:df7a88cd249c 255 clkUp_dma.source(&clockMask, false, 32);
mjr 47:df7a88cd249c 256 clkUp_dma.destination(&clockPort->PSOR, false, 32);
mjr 47:df7a88cd249c 257
mjr 47:df7a88cd249c 258 // Set up the Clock Down DMA channel. This channel takes the
mjr 47:df7a88cd249c 259 // clock low by writing the clock bit to the PCOR (clear output)
mjr 47:df7a88cd249c 260 // register for the clock pin.
mjr 47:df7a88cd249c 261 clkDn_dma.source(&clockMask, false, 32);
mjr 47:df7a88cd249c 262 clkDn_dma.destination(&clockPort->PCOR, false, 32);
mjr 45:c42166b2878c 263
mjr 47:df7a88cd249c 264 // Set up the ADC transfer DMA channel. This channel transfers
mjr 47:df7a88cd249c 265 // the current analog sampling result from the ADC output register
mjr 47:df7a88cd249c 266 // to our pixel array.
mjr 47:df7a88cd249c 267 ao1.initDMA(&adc_dma);
mjr 47:df7a88cd249c 268
mjr 47:df7a88cd249c 269 // Set up our chain of linked DMA channel:
mjr 47:df7a88cd249c 270 // ADC sample completion triggers Clock Up
mjr 47:df7a88cd249c 271 // ...which triggers the ADC transfer
mjr 47:df7a88cd249c 272 // ... which triggers Clock Down
mjr 47:df7a88cd249c 273 clkUp_dma.trigger(Trigger_ADC0);
mjr 47:df7a88cd249c 274 clkUp_dma.link(adc_dma);
mjr 47:df7a88cd249c 275 adc_dma.link(clkDn_dma, false);
mjr 45:c42166b2878c 276
mjr 47:df7a88cd249c 277 // Set the trigger on the downstream links to NONE - these are
mjr 47:df7a88cd249c 278 // triggered by their upstream links, so they don't need separate
mjr 47:df7a88cd249c 279 // peripheral or software triggers.
mjr 47:df7a88cd249c 280 adc_dma.trigger(Trigger_NONE);
mjr 47:df7a88cd249c 281 clkDn_dma.trigger(Trigger_NONE);
mjr 47:df7a88cd249c 282
mjr 47:df7a88cd249c 283 // Register an interrupt callback so that we're notified when
mjr 47:df7a88cd249c 284 // the last transfer completes.
mjr 47:df7a88cd249c 285 clkDn_dma.attach(this, &TSL1410R::transferDone);
mjr 47:df7a88cd249c 286
mjr 47:df7a88cd249c 287 // clear the timing statistics
mjr 47:df7a88cd249c 288 totalTime = 0.0;
mjr 47:df7a88cd249c 289 nRuns = 0;
mjr 17:ab3cec0c8bf4 290 }
mjr 43:7a6364d82a41 291
mjr 47:df7a88cd249c 292 // end of transfer notification
mjr 47:df7a88cd249c 293 void transferDone()
mjr 47:df7a88cd249c 294 {
mjr 47:df7a88cd249c 295 // stop the ADC sampler
mjr 47:df7a88cd249c 296 ao1.stop();
mjr 47:df7a88cd249c 297
mjr 47:df7a88cd249c 298 // clock out one extra pixel to leave A1 in the high-Z state
mjr 47:df7a88cd249c 299 clock = 1;
mjr 47:df7a88cd249c 300 clock = 0;
mjr 48:058ace2aed1d 301
mjr 48:058ace2aed1d 302 // add this sample to the timing statistics (we collect the data
mjr 48:058ace2aed1d 303 // merely to report to the config tool, for diagnostic purposes)
mjr 48:058ace2aed1d 304 totalTime += (t.read_us() - t0);
mjr 48:058ace2aed1d 305 nRuns += 1;
mjr 47:df7a88cd249c 306
mjr 48:058ace2aed1d 307 // the sampler is no long running
mjr 48:058ace2aed1d 308 running = false;
mjr 47:df7a88cd249c 309 }
mjr 47:df7a88cd249c 310
mjr 47:df7a88cd249c 311 // Get the stable pixel array. This is the image array from the
mjr 47:df7a88cd249c 312 // previous capture. It remains valid until the next startCapture()
mjr 47:df7a88cd249c 313 // call, at which point this buffer will be reused for the new capture.
mjr 48:058ace2aed1d 314 void getPix(uint8_t * &pix, int &n, uint32_t &t)
mjr 17:ab3cec0c8bf4 315 {
mjr 47:df7a88cd249c 316 // return the pixel array that ISN'T assigned to the DMA
mjr 48:058ace2aed1d 317 if (pixDMA)
mjr 48:058ace2aed1d 318 {
mjr 48:058ace2aed1d 319 // DMA owns pix2, so the stable array is pix1
mjr 48:058ace2aed1d 320 pix = pix1;
mjr 48:058ace2aed1d 321 t = t1;
mjr 48:058ace2aed1d 322 }
mjr 48:058ace2aed1d 323 else
mjr 48:058ace2aed1d 324 {
mjr 48:058ace2aed1d 325 // DMA owns pix1, so the stable array is pix2
mjr 48:058ace2aed1d 326 pix = pix2;
mjr 48:058ace2aed1d 327 t = t2;
mjr 48:058ace2aed1d 328 }
mjr 48:058ace2aed1d 329
mjr 48:058ace2aed1d 330 // return the pixel count
mjr 47:df7a88cd249c 331 n = nPixSensor;
mjr 47:df7a88cd249c 332 }
mjr 47:df7a88cd249c 333
mjr 48:058ace2aed1d 334 // Start an image capture from the sensor. Waits the previous
mjr 48:058ace2aed1d 335 // capture to finish if it's still running, then starts a new one
mjr 48:058ace2aed1d 336 // and returns immediately. The new capture proceeds autonomously
mjr 48:058ace2aed1d 337 // via the DMA hardware, so the caller can continue with other
mjr 48:058ace2aed1d 338 // processing during the capture.
mjr 47:df7a88cd249c 339 void startCapture()
mjr 47:df7a88cd249c 340 {
mjr 48:058ace2aed1d 341 // wait for the current capture to finish
mjr 48:058ace2aed1d 342 while (running) { }
mjr 43:7a6364d82a41 343
mjr 48:058ace2aed1d 344 // swap to the other DMA buffer for reading the new pixel samples
mjr 47:df7a88cd249c 345 pixDMA ^= 1;
mjr 47:df7a88cd249c 346
mjr 48:058ace2aed1d 347 // note the start time of this transfer
mjr 48:058ace2aed1d 348 t0 = t.read_us();
mjr 35:e959ffba78fd 349
mjr 48:058ace2aed1d 350 // Set up the active pixel array as the destination buffer for
mjr 48:058ace2aed1d 351 // the ADC DMA channel.
mjr 47:df7a88cd249c 352 adc_dma.destination(pixDMA ? pix2 : pix1, true);
mjr 47:df7a88cd249c 353
mjr 47:df7a88cd249c 354 // start the DMA transfers
mjr 47:df7a88cd249c 355 clkDn_dma.start(nPixSensor*4);
mjr 47:df7a88cd249c 356 adc_dma.start(nPixSensor);
mjr 47:df7a88cd249c 357 clkUp_dma.start(nPixSensor*4);
mjr 47:df7a88cd249c 358
mjr 17:ab3cec0c8bf4 359 // start the next integration cycle by pulsing SI and one clock
mjr 17:ab3cec0c8bf4 360 si = 1;
mjr 43:7a6364d82a41 361 clock = 1;
mjr 17:ab3cec0c8bf4 362 si = 0;
mjr 43:7a6364d82a41 363 clock = 0;
mjr 17:ab3cec0c8bf4 364
mjr 48:058ace2aed1d 365 // Set the timestamp for the current active buffer. The SI pulse
mjr 48:058ace2aed1d 366 // we just did performed the HOLD operation, which transfers the
mjr 48:058ace2aed1d 367 // current integration cycle's pixel charges to the output
mjr 48:058ace2aed1d 368 // capacitors in the sensor. We noted the start of the current
mjr 48:058ace2aed1d 369 // integration cycle in tInt when we started it during the previous
mjr 48:058ace2aed1d 370 // scan. The image we're about to transfer therefore represents
mjr 48:058ace2aed1d 371 // the photons collected between tInt and right now (actually, the
mjr 48:058ace2aed1d 372 // SI pulse above, but close enough). Set the timestamp to the
mjr 48:058ace2aed1d 373 // midpoint between tInt and now.
mjr 48:058ace2aed1d 374 uint32_t tmid = tInt + (t0 - tInt)/2;
mjr 48:058ace2aed1d 375 if (pixDMA)
mjr 48:058ace2aed1d 376 t2 = tmid;
mjr 48:058ace2aed1d 377 else
mjr 48:058ace2aed1d 378 t1 = tmid;
mjr 48:058ace2aed1d 379
mjr 48:058ace2aed1d 380 // pad the timing slightly
mjr 48:058ace2aed1d 381 clock = 0;
mjr 48:058ace2aed1d 382
mjr 47:df7a88cd249c 383 // clock in the first pixel
mjr 47:df7a88cd249c 384 clock = 1;
mjr 47:df7a88cd249c 385 clock = 0;
mjr 43:7a6364d82a41 386
mjr 47:df7a88cd249c 387 // Start the ADC sampler. The ADC will read samples continuously
mjr 47:df7a88cd249c 388 // until we tell it to stop. Each sample completion will trigger
mjr 47:df7a88cd249c 389 // our linked DMA channel, which will store the next sample in our
mjr 47:df7a88cd249c 390 // pixel array and pulse the CCD serial data clock to load the next
mjr 47:df7a88cd249c 391 // pixel onto the analog sampler pin. This will all happen without
mjr 47:df7a88cd249c 392 // any CPU involvement, so we can continue with other work.
mjr 48:058ace2aed1d 393 running = true;
mjr 47:df7a88cd249c 394 ao1.start();
mjr 48:058ace2aed1d 395
mjr 48:058ace2aed1d 396 // The new integration cycle starts with the 19th clock pulse
mjr 48:058ace2aed1d 397 // after the SI pulse. We offload all of the transfer work (including
mjr 48:058ace2aed1d 398 // the clock pulse generation) to the DMA controller, so we won't
mjr 48:058ace2aed1d 399 // be notified of exactly when that 19th clock occurs. To keep things
mjr 48:058ace2aed1d 400 // simple, aproximate it as now plus 19 2us sample times. This isn't
mjr 48:058ace2aed1d 401 // exact, since it will vary according to the ADC spin-up time and the
mjr 48:058ace2aed1d 402 // actual sampling time, but 19*2us is close enough given that the
mjr 48:058ace2aed1d 403 // overall integration time we're measuring will be about 64x longer
mjr 48:058ace2aed1d 404 // (around 2.5ms), so even if the 19*2us estimate is off by 100%, our
mjr 48:058ace2aed1d 405 // overall time estimate will still be accurate to about 1.5%.
mjr 48:058ace2aed1d 406 tInt = t.read_us() + 38;
mjr 48:058ace2aed1d 407 }
mjr 48:058ace2aed1d 408
mjr 48:058ace2aed1d 409 // Wait for the current capture to finish
mjr 48:058ace2aed1d 410 void wait()
mjr 48:058ace2aed1d 411 {
mjr 48:058ace2aed1d 412 while (running) { }
mjr 17:ab3cec0c8bf4 413 }
mjr 47:df7a88cd249c 414
mjr 2:c174f9ee414a 415 // Clock through all pixels to clear the array. Pulses SI at the
mjr 2:c174f9ee414a 416 // beginning of the operation, which starts a new integration cycle.
mjr 2:c174f9ee414a 417 // The caller can thus immediately call read() to read the pixels
mjr 2:c174f9ee414a 418 // integrated while the clear() was taking place.
mjr 17:ab3cec0c8bf4 419 void clear()
mjr 17:ab3cec0c8bf4 420 {
mjr 48:058ace2aed1d 421 // get the clock toggle register
mjr 48:058ace2aed1d 422 volatile uint32_t *ptor = &clockPort->PTOR;
mjr 48:058ace2aed1d 423
mjr 17:ab3cec0c8bf4 424 // clock in an SI pulse
mjr 17:ab3cec0c8bf4 425 si = 1;
mjr 48:058ace2aed1d 426 *ptor = clockMask;
mjr 43:7a6364d82a41 427 clockPort->PSOR = clockMask;
mjr 17:ab3cec0c8bf4 428 si = 0;
mjr 48:058ace2aed1d 429 *ptor = clockMask;
mjr 48:058ace2aed1d 430
mjr 48:058ace2aed1d 431 // This starts a new integration period. Or more precisely, the
mjr 48:058ace2aed1d 432 // 19th clock pulse will start the new integration period. We're
mjr 48:058ace2aed1d 433 // going to blast the clock signal as fast as we can, at about
mjr 48:058ace2aed1d 434 // 100ns intervals (50ns up and 50ns down), so the 19th clock
mjr 48:058ace2aed1d 435 // will be about 2us from now.
mjr 48:058ace2aed1d 436 tInt = t.read_us() + 2;
mjr 17:ab3cec0c8bf4 437
mjr 48:058ace2aed1d 438 // clock out all pixels, plus an extra one to clock past the last
mjr 48:058ace2aed1d 439 // pixel and reset the last pixel's internal sampling switch in
mjr 48:058ace2aed1d 440 // the sensor
mjr 48:058ace2aed1d 441 for (int i = 0 ; i < nPixSensor + 1 ; )
mjr 47:df7a88cd249c 442 {
mjr 48:058ace2aed1d 443 // toggle the clock to take it high
mjr 48:058ace2aed1d 444 *ptor = clockMask;
mjr 48:058ace2aed1d 445
mjr 48:058ace2aed1d 446 // increment our loop variable here to pad the timing, to
mjr 48:058ace2aed1d 447 // keep our pulse width long enough for the sensor
mjr 48:058ace2aed1d 448 ++i;
mjr 48:058ace2aed1d 449
mjr 48:058ace2aed1d 450 // toggle the clock to take it low
mjr 48:058ace2aed1d 451 *ptor = clockMask;
mjr 17:ab3cec0c8bf4 452 }
mjr 17:ab3cec0c8bf4 453 }
mjr 47:df7a88cd249c 454
mjr 48:058ace2aed1d 455 // get the timing statistics - sum of scan time for all scans so far
mjr 48:058ace2aed1d 456 // in microseconds, and total number of scans so far
mjr 48:058ace2aed1d 457 void getTimingStats(uint64_t &totalTime, uint32_t &nRuns) const
mjr 47:df7a88cd249c 458 {
mjr 47:df7a88cd249c 459 totalTime = this->totalTime;
mjr 47:df7a88cd249c 460 nRuns = this->nRuns;
mjr 47:df7a88cd249c 461 }
mjr 48:058ace2aed1d 462
mjr 48:058ace2aed1d 463 // get the average scan time in microseconds
mjr 48:058ace2aed1d 464 uint32_t getAvgScanTime() const
mjr 48:058ace2aed1d 465 {
mjr 48:058ace2aed1d 466 return uint32_t(totalTime / nRuns);
mjr 48:058ace2aed1d 467 }
mjr 2:c174f9ee414a 468
mjr 2:c174f9ee414a 469 private:
mjr 47:df7a88cd249c 470 // DMA controller interfaces
mjr 47:df7a88cd249c 471 SimpleDMA adc_dma; // DMA channel for reading the analog input
mjr 47:df7a88cd249c 472 SimpleDMA clkUp_dma; // "Clock Up" channel
mjr 47:df7a88cd249c 473 SimpleDMA clkDn_dma; // "Clock Down" channel
mjr 47:df7a88cd249c 474
mjr 47:df7a88cd249c 475 // Sensor interface pins
mjr 40:cc0d9814522b 476 DigitalOut si; // GPIO pin for sensor SI (serial data)
mjr 40:cc0d9814522b 477 DigitalOut clock; // GPIO pin for sensor SCLK (serial clock)
mjr 47:df7a88cd249c 478 GPIO_Type *clockPort; // IOPORT base address for clock pin - cached for DMA writes
mjr 35:e959ffba78fd 479 uint32_t clockMask; // IOPORT register bit mask for clock pin
mjr 47:df7a88cd249c 480 AltAnalogIn ao1; // GPIO pin for sensor AO (analog output)
mjr 47:df7a88cd249c 481
mjr 47:df7a88cd249c 482 // number of pixels in the physical sensor array
mjr 47:df7a88cd249c 483 int nPixSensor; // number of pixels in physical sensor array
mjr 47:df7a88cd249c 484
mjr 47:df7a88cd249c 485 // pixel buffers - we keep two buffers so that we can transfer the
mjr 47:df7a88cd249c 486 // current sensor data into one buffer via DMA while we concurrently
mjr 47:df7a88cd249c 487 // process the last buffer
mjr 47:df7a88cd249c 488 uint8_t *pix1; // pixel array 1
mjr 47:df7a88cd249c 489 uint8_t *pix2; // pixel array 2
mjr 47:df7a88cd249c 490
mjr 48:058ace2aed1d 491 // Timestamps of pix1 and pix2 arrays, in microseconds, in terms of the
mjr 48:058ace2aed1d 492 // ample timer (this->t).
mjr 48:058ace2aed1d 493 uint32_t t1;
mjr 48:058ace2aed1d 494 uint32_t t2;
mjr 48:058ace2aed1d 495
mjr 47:df7a88cd249c 496 // DMA target buffer. This is the buffer for the next DMA transfer.
mjr 47:df7a88cd249c 497 // 0 means pix1, 1 means pix2. The other buffer contains the stable
mjr 47:df7a88cd249c 498 // data from the last transfer.
mjr 47:df7a88cd249c 499 uint8_t pixDMA;
mjr 48:058ace2aed1d 500
mjr 48:058ace2aed1d 501 // flag: sample is running
mjr 48:058ace2aed1d 502 volatile bool running;
mjr 47:df7a88cd249c 503
mjr 47:df7a88cd249c 504 // timing statistics
mjr 48:058ace2aed1d 505 Timer t; // sample timer
mjr 48:058ace2aed1d 506 uint32_t t0; // start time (us) of current sample
mjr 48:058ace2aed1d 507 uint32_t tInt; // start time (us) of current integration period
mjr 48:058ace2aed1d 508 uint64_t totalTime; // total time consumed by all reads so far
mjr 47:df7a88cd249c 509 uint32_t nRuns; // number of runs so far
mjr 2:c174f9ee414a 510 };
mjr 2:c174f9ee414a 511
mjr 2:c174f9ee414a 512 #endif /* TSL1410R_H */