Mike R / Mbed 2 deprecated Pinscape_Controller_V2

Dependencies:   mbed FastIO FastPWM USBDevice

Fork of Pinscape_Controller by Mike R

Committer:
mjr
Date:
Thu Feb 18 07:32:20 2016 +0000
Revision:
47:df7a88cd249c
Parent:
45:c42166b2878c
Child:
48:058ace2aed1d
3-channel linked DMA scheme for CCD image capture working

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 47:df7a88cd249c 9 * For fast reads of the pixel file from the sensor, we use the KL25Z's
mjr 47:df7a88cd249c 10 * DMA capability. The method we use is very specific to the KL25Z
mjr 47:df7a88cd249c 11 * hardware and is pretty tricky. These are attributes I don't normally
mjr 47:df7a88cd249c 12 * like, but the speedup is amazing, enough to justify the complex
mjr 47:df7a88cd249c 13 * design. I don't think there's any other way to even get close to
mjr 47:df7a88cd249c 14 * this kind of read speed for this sensor with this MCU. (And I've
mjr 47:df7a88cd249c 15 * tried!) Reading the sensor quickly is more than just academic,
mjr 47:df7a88cd249c 16 * too: the plunger moves very fast during a release motion, so we
mjr 47:df7a88cd249c 17 * have to be able to take correspondingly fast pictures to get
mjr 47:df7a88cd249c 18 * clean images without motion blur. Before the speedup of this DMA
mjr 47:df7a88cd249c 19 * approach, images captured during release motions had a lot of
mjr 47:df7a88cd249c 20 * motion blur, and even aliasing from the sinusoidal motion when
mjr 47:df7a88cd249c 21 * the plunger bounces back and forth off the springs. The speedup
mjr 47:df7a88cd249c 22 * gets us over the threshold where we can capture images with very
mjr 47:df7a88cd249c 23 * little blur, so we can track the motion much more precisely even
mjr 47:df7a88cd249c 24 * at release speeds. This lets us determine the plunger position
mjr 47:df7a88cd249c 25 * more precisely and more quickly, which improves responsiveness in
mjr 47:df7a88cd249c 26 * the pinball simulator on the PC.
mjr 47:df7a88cd249c 27 *
mjr 47:df7a88cd249c 28 * Here's our approach.
mjr 47:df7a88cd249c 29 *
mjr 47:df7a88cd249c 30 * First, we put the analog input port (the ADC == Analog-to-Digital
mjr 47:df7a88cd249c 31 * Converter) in "continuous" mode, at the highest clock speed we can
mjr 47:df7a88cd249c 32 * program with the available clocks and the fastest read cycle
mjr 47:df7a88cd249c 33 * available in the ADC hardware. (The analog input port is the
mjr 47:df7a88cd249c 34 * GPIO pin attached to the sensor's AO == Analog Output pin, where
mjr 47:df7a88cd249c 35 * it outputs each pixel's value, one at a time, as an analog voltage
mjr 47:df7a88cd249c 36 * level.) In continuous mode, every time the ADC finishes taking a
mjr 47:df7a88cd249c 37 * sample, it stores the result value in its output register and then
mjr 47:df7a88cd249c 38 * immediately starts taking a new sample. This means that no MCU
mjr 47:df7a88cd249c 39 * (or even DMA) action is required to start each new sample. This
mjr 47:df7a88cd249c 40 * is where most of the speedup comes from, since it takes significant
mjr 47:df7a88cd249c 41 * time (multiple microseconds) to move data through the peripheral
mjr 47:df7a88cd249c 42 * registers, and it takes more time (also multiple microseconds) for
mjr 47:df7a88cd249c 43 * the ADC to spin up for each new sample when in single-sample mode.
mjr 47:df7a88cd249c 44 * We cut out about 7us this way and get the time per sample down to
mjr 47:df7a88cd249c 45 * about 2us. This is close to the documented maximum speed for the
mjr 47:df7a88cd249c 46 * ADC hardware.
mjr 47:df7a88cd249c 47 *
mjr 47:df7a88cd249c 48 * Second, we use the DMA controller to read the ADC result register
mjr 47:df7a88cd249c 49 * and store each sample in a memory array for processing. The ADC
mjr 47:df7a88cd249c 50 * hardware is designed to work with the DMA controller by signaling
mjr 47:df7a88cd249c 51 * the DMA controller when a new sample is ready; this allows DMA to
mjr 47:df7a88cd249c 52 * move each sample immediately when it's available without any CPU
mjr 47:df7a88cd249c 53 * involvement.
mjr 2:c174f9ee414a 54 *
mjr 47:df7a88cd249c 55 * Third - and this is where it really gets tricky - we use two
mjr 47:df7a88cd249c 56 * additional "linked" DMA channels to generate the clock signal
mjr 47:df7a88cd249c 57 * to the CCD sensor. The clock signal is how we tell the CCD when
mjr 47:df7a88cd249c 58 * to place the next pixel voltage on its AO pin, so the clock has
mjr 47:df7a88cd249c 59 * to be generated in lock step with the ADC sampling cycle. The
mjr 47:df7a88cd249c 60 * ADC timing isn't perfectly uniform or predictable, so we can't
mjr 47:df7a88cd249c 61 * just generate the pixel clock with a *real* clock. We have to
mjr 47:df7a88cd249c 62 * time the signal exactly with the ADC, which means that we have
mjr 47:df7a88cd249c 63 * to generate it from the ADC "sample is ready" signal. Fortunately,
mjr 47:df7a88cd249c 64 * there is just such a signal, and in fact we're already using it,
mjr 47:df7a88cd249c 65 * as described above, to tell the DMA when to move each result from
mjr 47:df7a88cd249c 66 * the ADC output register to our memory array. So how do we use this
mjr 47:df7a88cd249c 67 * to generate the CCD clock? The answer lies in the DMA controller's
mjr 47:df7a88cd249c 68 * channel linking feature. This allows one DMA channel to trigger a
mjr 47:df7a88cd249c 69 * second DMA channel each time the first channel completes one
mjr 47:df7a88cd249c 70 * transfer. And we can use DMA to control our clock GPIO pin by
mjr 47:df7a88cd249c 71 * using the pin's GPIO IPORT register as the DMA destination address.
mjr 47:df7a88cd249c 72 * Specifically, we can take the clock high by writing our pin's bit
mjr 47:df7a88cd249c 73 * pattern to the PSOR ("set output") register, and we can take the
mjr 47:df7a88cd249c 74 * clock low by writing to the PCOR ("clear output") register. We
mjr 47:df7a88cd249c 75 * use one DMA channel for each of these operations.
mjr 47:df7a88cd249c 76 *
mjr 47:df7a88cd249c 77 * Putting it all together, the cascade of linked DMA channels
mjr 47:df7a88cd249c 78 * works like this:
mjr 47:df7a88cd249c 79 *
mjr 47:df7a88cd249c 80 * - The ADC sample completes, which triggers channel 1, the
mjr 47:df7a88cd249c 81 * "Clock Up" channel. This performs one transfer of the
mjr 47:df7a88cd249c 82 * clock GPIO bit to the clock PSOR register, taking the clock
mjr 47:df7a88cd249c 83 * high, which causes the CCD to move the next pixel onto AO.
mjr 47:df7a88cd249c 84 *
mjr 47:df7a88cd249c 85 * - After the Clock Up channel does its transfer, it triggers
mjr 47:df7a88cd249c 86 * its link to channel 2, the ADC transfer channel. This
mjr 47:df7a88cd249c 87 * channel moves the ADC output register value to our memory
mjr 47:df7a88cd249c 88 * array.
mjr 47:df7a88cd249c 89 *
mjr 47:df7a88cd249c 90 * - After the ADC channel does its transfer, it triggers channel
mjr 47:df7a88cd249c 91 * 3, the "Clock Down" channel. This performs one transfer of
mjr 47:df7a88cd249c 92 * the clock GPIO bit to the clock PCOR register, taking the
mjr 47:df7a88cd249c 93 * clock low.
mjr 47:df7a88cd249c 94 *
mjr 47:df7a88cd249c 95 * Note that the order of the channels - Clock Up, ADC, Clock Down -
mjr 47:df7a88cd249c 96 * is important, because it ensures that we don't toggle the clock
mjr 47:df7a88cd249c 97 * bit too fast. The CCD has a minimum pulse duration of 50ns for
mjr 47:df7a88cd249c 98 * the clock signal. The DMA controller is so fast that we could
mjr 47:df7a88cd249c 99 * toggle the clock faster than this limit if we did the Up and
mjr 47:df7a88cd249c 100 * Down transfers adjacently.
mjr 47:df7a88cd249c 101 *
mjr 47:df7a88cd249c 102 * Note also that it's important that Clock Up be the first operation,
mjr 47:df7a88cd249c 103 * because the ADC is in continuous mode, meaning that it starts
mjr 47:df7a88cd249c 104 * taking a new sample immediately upon finishing the previous one.
mjr 47:df7a88cd249c 105 * So when the ADC DMA signal fires, the new sample is just starting.
mjr 47:df7a88cd249c 106 * We therefore have to get the next pixel onto the sampling pin as
mjr 47:df7a88cd249c 107 * quickly as possible. The CCD sensor's "analog output settling
mjr 47:df7a88cd249c 108 * time" is 120ns - this is the time for a new pixel voltage to
mjr 47:df7a88cd249c 109 * stabilize on AO after a clock rising edge. So assuming that the
mjr 47:df7a88cd249c 110 * ADC raises the DMA signal immediately, and the DMA controller
mjr 47:df7a88cd249c 111 * responds within a couple of MCU clock cycles, we should have the
mjr 47:df7a88cd249c 112 * new pixel voltage stable on the sampling pin by about 200ns after
mjr 47:df7a88cd249c 113 * the new ADC sample cycle starts. The sampling cycle with our
mjr 47:df7a88cd249c 114 * current parameters is about 2us, so the voltage level is stable
mjr 47:df7a88cd249c 115 * for 90% of the cycle.
mjr 47:df7a88cd249c 116 *
mjr 47:df7a88cd249c 117 * Also, it's okay that the ADC sample transfer doesn't happen until
mjr 47:df7a88cd249c 118 * after the Clock Up DMA transfer. The ADC output register holds the
mjr 47:df7a88cd249c 119 * last result until the next sample completes, so we have about 2us
mjr 47:df7a88cd249c 120 * to grab it. The first Clock Up DMA transfer only takes a couple
mjr 47:df7a88cd249c 121 * of clocks - order of 100ns - so we get to it with time to spare.
mjr 47:df7a88cd249c 122 *
mjr 47:df7a88cd249c 123 * (Note that it's tempting to try to handle the clock with a single
mjr 47:df7a88cd249c 124 * DMA channel, by using the PTOR "toggle output" to do TWO writes:
mjr 47:df7a88cd249c 125 * one to toggle the clock up and another to toggle it down. But
mjr 47:df7a88cd249c 126 * I haven't found a good way to do this. The problem is that the
mjr 47:df7a88cd249c 127 * DMA controller can only do one transfer per trigger in the fully
mjr 47:df7a88cd249c 128 * autonomous mode we're using, and we need to do two writes. In
mjr 47:df7a88cd249c 129 * fact, we'd really need to do three or four writes: we'd have to
mjr 47:df7a88cd249c 130 * throw in one or two no-op writes (of all zeroes) between the two
mjr 47:df7a88cd249c 131 * toggles, for time padding to ensure that we meet the minimum 50ns
mjr 47:df7a88cd249c 132 * pulse width for the TSL1410R clock signal. But it's the same
mjr 47:df7a88cd249c 133 * issue whether it's two writes or four. The DMA controller does
mjr 47:df7a88cd249c 134 * have a "continuous" mode that does an entire transfer on a single
mjr 47:df7a88cd249c 135 * trigger, but it can't reset itself after such a transfer, so CPU
mjr 47:df7a88cd249c 136 * intervention would be required on every ADC cycle to set up the
mjr 47:df7a88cd249c 137 * next clock write. We could do that with an interrupt, but given
mjr 47:df7a88cd249c 138 * the 2us cycle time, an interrupt would create a ton of CPU load,
mjr 47:df7a88cd249c 139 * and probably isn't even enough time to reliably complete each
mjr 47:df7a88cd249c 140 * interrupt service call before the next cycle. Fortunately, at
mjr 47:df7a88cd249c 141 * the moment we only have one other module in the whole system
mjr 47:df7a88cd249c 142 * using DMA at all - the TLC5940 PWM controller interface, which
mjr 47:df7a88cd249c 143 * only needs one channel. So with the four available channels in
mjr 47:df7a88cd249c 144 * the hardware, we can afford to use three of them here.)
mjr 2:c174f9ee414a 145 */
mjr 2:c174f9ee414a 146
mjr 35:e959ffba78fd 147 #include "mbed.h"
mjr 35:e959ffba78fd 148 #include "config.h"
mjr 43:7a6364d82a41 149 #include "AltAnalogIn.h"
mjr 45:c42166b2878c 150 #include "SimpleDMA.h"
mjr 47:df7a88cd249c 151 #include "DMAChannels.h"
mjr 2:c174f9ee414a 152
mjr 35:e959ffba78fd 153 #ifndef TSL1410R_H
mjr 35:e959ffba78fd 154 #define TSL1410R_H
mjr 47:df7a88cd249c 155
mjr 35:e959ffba78fd 156
mjr 47:df7a88cd249c 157 // To allow DMA access to the clock pin, we need to point the DMA
mjr 47:df7a88cd249c 158 // controller to the IOPORT registers that control the pin. PORT_BASE()
mjr 47:df7a88cd249c 159 // gives us the address of the register group for the 32 GPIO pins with
mjr 47:df7a88cd249c 160 // the same letter name as our target pin (e.g., PTA0 through PTA31),
mjr 47:df7a88cd249c 161 // and PINMASK gives us the bit pattern to write to those registers to
mjr 47:df7a88cd249c 162 // access our single GPIO pin. Each register group has three special
mjr 47:df7a88cd249c 163 // registers that update the pin in particular ways: PSOR ("set output
mjr 47:df7a88cd249c 164 // register") turns pins on, PCOR ("clear output register") turns pins
mjr 47:df7a88cd249c 165 // off, and PTOR ("toggle output register") toggle pins to the opposite
mjr 47:df7a88cd249c 166 // of their current values. These registers have special semantics:
mjr 47:df7a88cd249c 167 // writing a bit as 0 has no effect on the corresponding pin, while
mjr 47:df7a88cd249c 168 // writing a bit as 1 performs the register's action on the pin. This
mjr 47:df7a88cd249c 169 // allows a single GPIO pin to be set, cleared, or toggled with a
mjr 47:df7a88cd249c 170 // 32-bit write to one of these registers, without affecting any of the
mjr 47:df7a88cd249c 171 // other pins addressed by the register. (It also allows changing any
mjr 47:df7a88cd249c 172 // group of pins with a single write, although we don't use that
mjr 47:df7a88cd249c 173 // feature here.)
mjr 35:e959ffba78fd 174 //
mjr 47:df7a88cd249c 175 // - To turn a pin ON: PORT_BASE(pin)->PSOR = PINMASK(pin)
mjr 47:df7a88cd249c 176 // - To turn a pin OFF: PORT_BASE(pin)->PCOR = PINMASK(pin)
mjr 47:df7a88cd249c 177 // - To toggle a pin: PORT_BASE(pin)->PTOR = PINMASK(pin)
mjr 47:df7a88cd249c 178 //
mjr 43:7a6364d82a41 179 #define GPIO_PORT(pin) (((unsigned int)(pin)) >> PORT_SHIFT)
mjr 47:df7a88cd249c 180 #define GPIO_PORT_BASE(pin) ((GPIO_Type *)(PTA_BASE + GPIO_PORT(pin) * 0x40))
mjr 43:7a6364d82a41 181 #define GPIO_PINMASK(pin) gpio_set(pin)
mjr 2:c174f9ee414a 182
mjr 35:e959ffba78fd 183 class TSL1410R
mjr 2:c174f9ee414a 184 {
mjr 2:c174f9ee414a 185 public:
mjr 47:df7a88cd249c 186 TSL1410R(int nPixSensor, PinName siPin, PinName clockPin, PinName ao1Pin, PinName /*ao2Pin*/)
mjr 47:df7a88cd249c 187 : adc_dma(DMAch_ADC),
mjr 47:df7a88cd249c 188 clkUp_dma(DMAch_CLKUP),
mjr 47:df7a88cd249c 189 clkDn_dma(DMAch_CLKDN),
mjr 47:df7a88cd249c 190 si(siPin),
mjr 47:df7a88cd249c 191 clock(clockPin),
mjr 47:df7a88cd249c 192 ao1(ao1Pin, true),
mjr 47:df7a88cd249c 193 nPixSensor(nPixSensor)
mjr 17:ab3cec0c8bf4 194 {
mjr 47:df7a88cd249c 195 // allocate our double pixel buffers
mjr 47:df7a88cd249c 196 pix1 = new uint8_t[nPixSensor*2];
mjr 47:df7a88cd249c 197 pix2 = pix1 + nPixSensor;
mjr 35:e959ffba78fd 198
mjr 47:df7a88cd249c 199 // put the first DMA transfer into the first buffer (pix1)
mjr 47:df7a88cd249c 200 pixDMA = 0;
mjr 47:df7a88cd249c 201
mjr 35:e959ffba78fd 202 // remember the clock pin port base and pin mask for fast access
mjr 35:e959ffba78fd 203 clockPort = GPIO_PORT_BASE(clockPin);
mjr 35:e959ffba78fd 204 clockMask = GPIO_PINMASK(clockPin);
mjr 35:e959ffba78fd 205
mjr 43:7a6364d82a41 206 // clear out power-on random data by clocking through all pixels twice
mjr 17:ab3cec0c8bf4 207 clear();
mjr 17:ab3cec0c8bf4 208 clear();
mjr 43:7a6364d82a41 209
mjr 47:df7a88cd249c 210 // Set up the Clock Up DMA channel. This channel takes the
mjr 47:df7a88cd249c 211 // clock high by writing the clock bit to the PSOR (set output)
mjr 47:df7a88cd249c 212 // register for the clock pin.
mjr 47:df7a88cd249c 213 clkUp_dma.source(&clockMask, false, 32);
mjr 47:df7a88cd249c 214 clkUp_dma.destination(&clockPort->PSOR, false, 32);
mjr 47:df7a88cd249c 215
mjr 47:df7a88cd249c 216 // Set up the Clock Down DMA channel. This channel takes the
mjr 47:df7a88cd249c 217 // clock low by writing the clock bit to the PCOR (clear output)
mjr 47:df7a88cd249c 218 // register for the clock pin.
mjr 47:df7a88cd249c 219 clkDn_dma.source(&clockMask, false, 32);
mjr 47:df7a88cd249c 220 clkDn_dma.destination(&clockPort->PCOR, false, 32);
mjr 45:c42166b2878c 221
mjr 47:df7a88cd249c 222 // Set up the ADC transfer DMA channel. This channel transfers
mjr 47:df7a88cd249c 223 // the current analog sampling result from the ADC output register
mjr 47:df7a88cd249c 224 // to our pixel array.
mjr 47:df7a88cd249c 225 ao1.initDMA(&adc_dma);
mjr 47:df7a88cd249c 226
mjr 47:df7a88cd249c 227 // Set up our chain of linked DMA channel:
mjr 47:df7a88cd249c 228 // ADC sample completion triggers Clock Up
mjr 47:df7a88cd249c 229 // ...which triggers the ADC transfer
mjr 47:df7a88cd249c 230 // ... which triggers Clock Down
mjr 47:df7a88cd249c 231 clkUp_dma.trigger(Trigger_ADC0);
mjr 47:df7a88cd249c 232 clkUp_dma.link(adc_dma);
mjr 47:df7a88cd249c 233 adc_dma.link(clkDn_dma, false);
mjr 45:c42166b2878c 234
mjr 47:df7a88cd249c 235 // Set the trigger on the downstream links to NONE - these are
mjr 47:df7a88cd249c 236 // triggered by their upstream links, so they don't need separate
mjr 47:df7a88cd249c 237 // peripheral or software triggers.
mjr 47:df7a88cd249c 238 adc_dma.trigger(Trigger_NONE);
mjr 47:df7a88cd249c 239 clkDn_dma.trigger(Trigger_NONE);
mjr 47:df7a88cd249c 240
mjr 47:df7a88cd249c 241 // Register an interrupt callback so that we're notified when
mjr 47:df7a88cd249c 242 // the last transfer completes.
mjr 47:df7a88cd249c 243 clkDn_dma.attach(this, &TSL1410R::transferDone);
mjr 47:df7a88cd249c 244
mjr 47:df7a88cd249c 245 // clear the timing statistics
mjr 47:df7a88cd249c 246 totalTime = 0.0;
mjr 47:df7a88cd249c 247 nRuns = 0;
mjr 17:ab3cec0c8bf4 248 }
mjr 43:7a6364d82a41 249
mjr 47:df7a88cd249c 250 // end of transfer notification
mjr 47:df7a88cd249c 251 void transferDone()
mjr 47:df7a88cd249c 252 {
mjr 47:df7a88cd249c 253 // stop the ADC sampler
mjr 47:df7a88cd249c 254 ao1.stop();
mjr 47:df7a88cd249c 255
mjr 47:df7a88cd249c 256 // clock out one extra pixel to leave A1 in the high-Z state
mjr 47:df7a88cd249c 257 clock = 1;
mjr 47:df7a88cd249c 258 clock = 0;
mjr 45:c42166b2878c 259
mjr 47:df7a88cd249c 260 // stop the clock
mjr 47:df7a88cd249c 261 t.stop();
mjr 47:df7a88cd249c 262
mjr 47:df7a88cd249c 263 // count the statistics
mjr 47:df7a88cd249c 264 totalTime += t.read();
mjr 47:df7a88cd249c 265 nRuns += 1;
mjr 47:df7a88cd249c 266 }
mjr 47:df7a88cd249c 267
mjr 47:df7a88cd249c 268 // Get the stable pixel array. This is the image array from the
mjr 47:df7a88cd249c 269 // previous capture. It remains valid until the next startCapture()
mjr 47:df7a88cd249c 270 // call, at which point this buffer will be reused for the new capture.
mjr 47:df7a88cd249c 271 void getPix(uint8_t * &pix, int &n)
mjr 17:ab3cec0c8bf4 272 {
mjr 47:df7a88cd249c 273 // return the pixel array that ISN'T assigned to the DMA
mjr 47:df7a88cd249c 274 pix = pixDMA ? pix1 : pix2;
mjr 47:df7a88cd249c 275 n = nPixSensor;
mjr 47:df7a88cd249c 276 }
mjr 47:df7a88cd249c 277
mjr 47:df7a88cd249c 278 // Start an image capture from the sensor. This waits for any previous
mjr 47:df7a88cd249c 279 // capture to finish, then starts a new one and returns immediately. The
mjr 47:df7a88cd249c 280 // new capture proceeds autonomously via the DMA hardware, so the caller
mjr 47:df7a88cd249c 281 // can continue with other processing during the capture.
mjr 47:df7a88cd249c 282 void startCapture()
mjr 47:df7a88cd249c 283 {
mjr 47:df7a88cd249c 284 // wait for the previous transfer to finish
mjr 47:df7a88cd249c 285 while (adc_dma.isBusy()) { }
mjr 43:7a6364d82a41 286
mjr 47:df7a88cd249c 287 // swap to the other DMA buffer
mjr 47:df7a88cd249c 288 pixDMA ^= 1;
mjr 47:df7a88cd249c 289
mjr 47:df7a88cd249c 290 // start timing this transfer
mjr 47:df7a88cd249c 291 t.reset();
mjr 47:df7a88cd249c 292 t.start();
mjr 35:e959ffba78fd 293
mjr 47:df7a88cd249c 294 // set up the active pixel array as the destination buffer for
mjr 47:df7a88cd249c 295 // the ADC DMA channel
mjr 47:df7a88cd249c 296 adc_dma.destination(pixDMA ? pix2 : pix1, true);
mjr 47:df7a88cd249c 297
mjr 47:df7a88cd249c 298 // start the DMA transfers
mjr 47:df7a88cd249c 299 clkDn_dma.start(nPixSensor*4);
mjr 47:df7a88cd249c 300 adc_dma.start(nPixSensor);
mjr 47:df7a88cd249c 301 clkUp_dma.start(nPixSensor*4);
mjr 47:df7a88cd249c 302
mjr 17:ab3cec0c8bf4 303 // start the next integration cycle by pulsing SI and one clock
mjr 17:ab3cec0c8bf4 304 si = 1;
mjr 43:7a6364d82a41 305 clock = 1;
mjr 17:ab3cec0c8bf4 306 si = 0;
mjr 43:7a6364d82a41 307 clock = 0;
mjr 17:ab3cec0c8bf4 308
mjr 47:df7a88cd249c 309 // clock in the first pixel
mjr 47:df7a88cd249c 310 clock = 1;
mjr 47:df7a88cd249c 311 clock = 0;
mjr 43:7a6364d82a41 312
mjr 47:df7a88cd249c 313 // Start the ADC sampler. The ADC will read samples continuously
mjr 47:df7a88cd249c 314 // until we tell it to stop. Each sample completion will trigger
mjr 47:df7a88cd249c 315 // our linked DMA channel, which will store the next sample in our
mjr 47:df7a88cd249c 316 // pixel array and pulse the CCD serial data clock to load the next
mjr 47:df7a88cd249c 317 // pixel onto the analog sampler pin. This will all happen without
mjr 47:df7a88cd249c 318 // any CPU involvement, so we can continue with other work.
mjr 47:df7a88cd249c 319 ao1.start();
mjr 17:ab3cec0c8bf4 320 }
mjr 47:df7a88cd249c 321
mjr 2:c174f9ee414a 322 // Clock through all pixels to clear the array. Pulses SI at the
mjr 2:c174f9ee414a 323 // beginning of the operation, which starts a new integration cycle.
mjr 2:c174f9ee414a 324 // The caller can thus immediately call read() to read the pixels
mjr 2:c174f9ee414a 325 // integrated while the clear() was taking place.
mjr 17:ab3cec0c8bf4 326 void clear()
mjr 17:ab3cec0c8bf4 327 {
mjr 17:ab3cec0c8bf4 328 // clock in an SI pulse
mjr 17:ab3cec0c8bf4 329 si = 1;
mjr 43:7a6364d82a41 330 clockPort->PSOR = clockMask;
mjr 17:ab3cec0c8bf4 331 si = 0;
mjr 43:7a6364d82a41 332 clockPort->PCOR = clockMask;
mjr 17:ab3cec0c8bf4 333
mjr 17:ab3cec0c8bf4 334 // clock out all pixels
mjr 47:df7a88cd249c 335 for (int i = 0 ; i < nPixSensor + 1 ; ++i)
mjr 47:df7a88cd249c 336 {
mjr 47:df7a88cd249c 337 clock = 1;
mjr 47:df7a88cd249c 338 clock = 0;
mjr 17:ab3cec0c8bf4 339 }
mjr 17:ab3cec0c8bf4 340 }
mjr 47:df7a88cd249c 341
mjr 47:df7a88cd249c 342 // get the timing statistics
mjr 47:df7a88cd249c 343 void getTimingStats(float &totalTime, uint32_t &nRuns)
mjr 47:df7a88cd249c 344 {
mjr 47:df7a88cd249c 345 totalTime = this->totalTime;
mjr 47:df7a88cd249c 346 nRuns = this->nRuns;
mjr 47:df7a88cd249c 347 }
mjr 2:c174f9ee414a 348
mjr 2:c174f9ee414a 349 private:
mjr 47:df7a88cd249c 350 // DMA controller interfaces
mjr 47:df7a88cd249c 351 SimpleDMA adc_dma; // DMA channel for reading the analog input
mjr 47:df7a88cd249c 352 SimpleDMA clkUp_dma; // "Clock Up" channel
mjr 47:df7a88cd249c 353 SimpleDMA clkDn_dma; // "Clock Down" channel
mjr 47:df7a88cd249c 354
mjr 47:df7a88cd249c 355 // Sensor interface pins
mjr 40:cc0d9814522b 356 DigitalOut si; // GPIO pin for sensor SI (serial data)
mjr 40:cc0d9814522b 357 DigitalOut clock; // GPIO pin for sensor SCLK (serial clock)
mjr 47:df7a88cd249c 358 GPIO_Type *clockPort; // IOPORT base address for clock pin - cached for DMA writes
mjr 35:e959ffba78fd 359 uint32_t clockMask; // IOPORT register bit mask for clock pin
mjr 47:df7a88cd249c 360 AltAnalogIn ao1; // GPIO pin for sensor AO (analog output)
mjr 47:df7a88cd249c 361
mjr 47:df7a88cd249c 362 // number of pixels in the physical sensor array
mjr 47:df7a88cd249c 363 int nPixSensor; // number of pixels in physical sensor array
mjr 47:df7a88cd249c 364
mjr 47:df7a88cd249c 365 // pixel buffers - we keep two buffers so that we can transfer the
mjr 47:df7a88cd249c 366 // current sensor data into one buffer via DMA while we concurrently
mjr 47:df7a88cd249c 367 // process the last buffer
mjr 47:df7a88cd249c 368 uint8_t *pix1; // pixel array 1
mjr 47:df7a88cd249c 369 uint8_t *pix2; // pixel array 2
mjr 47:df7a88cd249c 370
mjr 47:df7a88cd249c 371 // DMA target buffer. This is the buffer for the next DMA transfer.
mjr 47:df7a88cd249c 372 // 0 means pix1, 1 means pix2. The other buffer contains the stable
mjr 47:df7a88cd249c 373 // data from the last transfer.
mjr 47:df7a88cd249c 374 uint8_t pixDMA;
mjr 47:df7a88cd249c 375
mjr 47:df7a88cd249c 376 // timing statistics
mjr 47:df7a88cd249c 377 Timer t; // timer - started when we start a DMA transfer
mjr 47:df7a88cd249c 378 float totalTime; // total time consumed by all reads so far
mjr 47:df7a88cd249c 379 uint32_t nRuns; // number of runs so far
mjr 2:c174f9ee414a 380 };
mjr 2:c174f9ee414a 381
mjr 2:c174f9ee414a 382 #endif /* TSL1410R_H */