Mirror with some correction

Dependencies:   mbed FastIO FastPWM USBDevice

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

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mjr 82:4f6209cb5c33 1 /*
mjr 82:4f6209cb5c33 2 * AMS/TAOS TSL14xx series photodiode array interface class.
mjr 82:4f6209cb5c33 3 *
mjr 82:4f6209cb5c33 4 * This provides a high-level interface for the AMS/TAOS TSLxx series
mjr 82:4f6209cb5c33 5 * of photodiode arrays. This class works with most of the sensors
mjr 82:4f6209cb5c33 6 * in this series, which differ only in pixel array sizes. This code
mjr 82:4f6209cb5c33 7 * has been tested with the following sensors from the series:
mjr 82:4f6209cb5c33 8 *
mjr 82:4f6209cb5c33 9 * TSL1410R - 1280 pixels, 400dpi
mjr 82:4f6209cb5c33 10 * TSL1412S - 1536 pixels, 400dpi
mjr 82:4f6209cb5c33 11 * TSL1401CL - 128 pixels, 400dpi
mjr 82:4f6209cb5c33 12 *
mjr 82:4f6209cb5c33 13 * All of these sensors have the same electrical interface, consisting
mjr 82:4f6209cb5c33 14 * of a clock input (CLK), start pulse input (SI), and analog pixel
mjr 82:4f6209cb5c33 15 * output (AO). The sensors are equipped with hold capacitors and
mjr 82:4f6209cb5c33 16 * shift registers that allow simultaneous sampling of all pixels, and
mjr 82:4f6209cb5c33 17 * serial access to the pixel values.
mjr 82:4f6209cb5c33 18 *
mjr 82:4f6209cb5c33 19 * (Note on the plunger sensor class hierarchy: this class is for the
mjr 82:4f6209cb5c33 20 * sensor only, not for the plunger application. This class is meant
mjr 82:4f6209cb5c33 21 * to be reusable in other contexts that just need to read raw pixel
mjr 104:6e06e0f4b476 22 * data from the sensor. Plunger/tslxxSensor.h implements the
mjr 104:6e06e0f4b476 23 * specializations of the plunger interface class for these sensors.)
mjr 104:6e06e0f4b476 24 *
mjr 82:4f6209cb5c33 25 *
mjr 101:755f44622abc 26 * *** Double buffering ***
mjr 101:755f44622abc 27 *
mjr 82:4f6209cb5c33 28 * Our API is based on a double-buffered asynchronous read. The caller
mjr 82:4f6209cb5c33 29 * can access a completed buffer, containing the pixels from the last image
mjr 82:4f6209cb5c33 30 * frame, while the sensor is transferring data asynchronously (using the
mjr 82:4f6209cb5c33 31 * microcontroller's DMA capability) into the other buffer. Each time a
mjr 82:4f6209cb5c33 32 * new read is started, we swap buffers, making the last completed buffer
mjr 82:4f6209cb5c33 33 * available to the client and handing the other buffer to the DMA
mjr 82:4f6209cb5c33 34 * controller to fill asynchronously.
mjr 101:755f44622abc 35 *
mjr 101:755f44622abc 36 * In a way, there are actually THREE frames in our pipeline at any given
mjr 101:755f44622abc 37 * time:
mjr 101:755f44622abc 38 *
mjr 101:755f44622abc 39 * - a live image integrating light on the photo receptors on the sensor
mjr 101:755f44622abc 40 * - the prior image, held in the sensor's shift register and being
mjr 101:755f44622abc 41 * transferred via DMA into one of our buffers (the "DMA" buffer)
mjr 101:755f44622abc 42 * - the second prior image, in our other buffer (the "stable" buffer),
mjr 101:755f44622abc 43 * available for the client to process
mjr 101:755f44622abc 44 *
mjr 101:755f44622abc 45 * The integration process on the sensor starts when we begin the transfer
mjr 101:755f44622abc 46 * of an image via DMA. That frame's integration period ends when the next
mjr 101:755f44622abc 47 * transfer starts. So the minimum integration time is also the DMA pixel
mjr 101:755f44622abc 48 * transfer time. Longer integration times can be achieved by waiting
mjr 101:755f44622abc 49 * for an additional interval after a DMA transfer finishes, before starting
mjr 101:755f44622abc 50 * the next transfer. We make provision for this added time to allow for
mjr 101:755f44622abc 51 * longer exposure times to optimize image quality.
mjr 101:755f44622abc 52 *
mjr 82:4f6209cb5c33 53 *
mjr 101:755f44622abc 54 * *** Optimizing pixel transfer speed ***
mjr 82:4f6209cb5c33 55 *
mjr 101:755f44622abc 56 * For Pinscape purposes, we want the fastest possible frame rate, as we're
mjr 101:755f44622abc 57 * trying to accurately capture the motion of a fast-moving object (the
mjr 101:755f44622abc 58 * plunger). The TSL14xx sensors can achieve a frame rate up to about
mjr 101:755f44622abc 59 * 1000 frames per second, if everything is clocked at the limits in the
mjr 101:755f44622abc 60 * data sheet. The KL25Z, however, can't achieve that fast a rate. The
mjr 101:755f44622abc 61 * limiting factor is the KL25Z's ADC. We have to take an ADC sample for
mjr 101:755f44622abc 62 * every pixel, and the minimum sampling time for the ADC on the KL25Z is
mjr 101:755f44622abc 63 * about 2us. With the 1280-pixel TSL1410R, that gives us a minimum
mjr 101:755f44622abc 64 * pixel transfer time of about 2.6ms. And it's actually very difficult
mjr 101:755f44622abc 65 * to achieve that speed - my original, naive implementation took more
mjr 101:755f44622abc 66 * like 30ms (!!!) to transfer each frame.
mjr 82:4f6209cb5c33 67 *
mjr 101:755f44622abc 68 * As a rule, I don't like tricky code, because it's hard to understand
mjr 101:755f44622abc 69 * and hard to debug. But in this case it's justified. For good plunger
mjr 101:755f44622abc 70 * tracking, it's critical to achieve a minimum frame rate of around 200
mjr 101:755f44622abc 71 * fps (5ms per frame). I'm pretty sure there's no way to even get close
mjr 101:755f44622abc 72 * to this rate without the complex setup described below.
mjr 101:755f44622abc 73 *
mjr 101:755f44622abc 74 * Here's our approach for fast data transfer:
mjr 82:4f6209cb5c33 75 *
mjr 82:4f6209cb5c33 76 * First, we put the analog input port (the ADC == Analog-to-Digital
mjr 82:4f6209cb5c33 77 * Converter) in "continuous" mode, at the highest clock speed we can
mjr 82:4f6209cb5c33 78 * program with the available clocks and the fastest read cycle
mjr 82:4f6209cb5c33 79 * available in the ADC hardware. (The analog input port is the
mjr 82:4f6209cb5c33 80 * GPIO pin attached to the sensor's AO == Analog Output pin, where
mjr 82:4f6209cb5c33 81 * it outputs each pixel's value, one at a time, as an analog voltage
mjr 82:4f6209cb5c33 82 * level.) In continuous mode, every time the ADC finishes taking a
mjr 82:4f6209cb5c33 83 * sample, it stores the result value in its output register and then
mjr 82:4f6209cb5c33 84 * immediately starts taking a new sample. This means that no MCU
mjr 82:4f6209cb5c33 85 * (or even DMA) action is required to start each new sample. This
mjr 82:4f6209cb5c33 86 * is where most of the speedup comes from, since it takes significant
mjr 82:4f6209cb5c33 87 * time (multiple microseconds) to move data through the peripheral
mjr 82:4f6209cb5c33 88 * registers, and it takes more time (also multiple microseconds) for
mjr 82:4f6209cb5c33 89 * the ADC to spin up for each new sample when in single-sample mode.
mjr 82:4f6209cb5c33 90 * We cut out about 7us this way and get the time per sample down to
mjr 82:4f6209cb5c33 91 * about 2us. This is close to the documented maximum speed for the
mjr 82:4f6209cb5c33 92 * ADC hardware.
mjr 82:4f6209cb5c33 93 *
mjr 82:4f6209cb5c33 94 * Second, we use the DMA controller to read the ADC result register
mjr 82:4f6209cb5c33 95 * and store each sample in a memory array for processing. The ADC
mjr 82:4f6209cb5c33 96 * hardware is designed to work with the DMA controller by signaling
mjr 82:4f6209cb5c33 97 * the DMA controller when a new sample is ready; this allows DMA to
mjr 82:4f6209cb5c33 98 * move each sample immediately when it's available without any CPU
mjr 82:4f6209cb5c33 99 * involvement.
mjr 82:4f6209cb5c33 100 *
mjr 82:4f6209cb5c33 101 * Third - and this is where it really gets tricky - we use two
mjr 82:4f6209cb5c33 102 * additional "linked" DMA channels to generate the clock signal
mjr 82:4f6209cb5c33 103 * to the CCD sensor. The clock signal is how we tell the CCD when
mjr 82:4f6209cb5c33 104 * to place the next pixel voltage on its AO pin, so the clock has
mjr 82:4f6209cb5c33 105 * to be generated in lock step with the ADC sampling cycle. The
mjr 82:4f6209cb5c33 106 * ADC timing isn't perfectly uniform or predictable, so we can't
mjr 82:4f6209cb5c33 107 * just generate the pixel clock with a *real* clock. We have to
mjr 82:4f6209cb5c33 108 * time the signal exactly with the ADC, which means that we have
mjr 82:4f6209cb5c33 109 * to generate it from the ADC "sample is ready" signal. Fortunately,
mjr 82:4f6209cb5c33 110 * there is just such a signal, and in fact we're already using it,
mjr 82:4f6209cb5c33 111 * as described above, to tell the DMA when to move each result from
mjr 82:4f6209cb5c33 112 * the ADC output register to our memory array. So how do we use this
mjr 82:4f6209cb5c33 113 * to generate the CCD clock? The answer lies in the DMA controller's
mjr 82:4f6209cb5c33 114 * channel linking feature. This allows one DMA channel to trigger a
mjr 82:4f6209cb5c33 115 * second DMA channel each time the first channel completes one
mjr 82:4f6209cb5c33 116 * transfer. And we can use DMA to control our clock GPIO pin by
mjr 82:4f6209cb5c33 117 * using the pin's GPIO IPORT register as the DMA destination address.
mjr 82:4f6209cb5c33 118 * Specifically, we can take the clock high by writing our pin's bit
mjr 82:4f6209cb5c33 119 * pattern to the PSOR ("set output") register, and we can take the
mjr 82:4f6209cb5c33 120 * clock low by writing to the PCOR ("clear output") register. We
mjr 82:4f6209cb5c33 121 * use one DMA channel for each of these operations.
mjr 82:4f6209cb5c33 122 *
mjr 82:4f6209cb5c33 123 * Putting it all together, the cascade of linked DMA channels
mjr 82:4f6209cb5c33 124 * works like this:
mjr 82:4f6209cb5c33 125 *
mjr 82:4f6209cb5c33 126 * - We kick off the first ADC sample.
mjr 82:4f6209cb5c33 127 *
mjr 82:4f6209cb5c33 128 * - When the ADC sample completes, the ADC DMA trigger fires,
mjr 82:4f6209cb5c33 129 * which triggers channel 1, the "Clock Up" channel. This
mjr 82:4f6209cb5c33 130 * performs one transfer of the clock GPIO bit to the clock
mjr 82:4f6209cb5c33 131 * PSOR register, taking the clock high, which causes the CCD
mjr 82:4f6209cb5c33 132 * to move the next pixel onto AO.
mjr 82:4f6209cb5c33 133 *
mjr 82:4f6209cb5c33 134 * - After the Clock Up channel does its transfer, it triggers
mjr 82:4f6209cb5c33 135 * its link to channel 2, the ADC transfer channel. This
mjr 82:4f6209cb5c33 136 * channel moves the ADC output register value to our memory
mjr 82:4f6209cb5c33 137 * array.
mjr 82:4f6209cb5c33 138 *
mjr 82:4f6209cb5c33 139 * - After the ADC channel does its transfer, it triggers channel
mjr 82:4f6209cb5c33 140 * 3, the "Clock Down" channel. This performs one transfer of
mjr 82:4f6209cb5c33 141 * the clock GPIO bit to the clock PCOR register, taking the
mjr 82:4f6209cb5c33 142 * clock low.
mjr 82:4f6209cb5c33 143 *
mjr 82:4f6209cb5c33 144 * Note that the order of the channels - Clock Up, ADC, Clock Down -
mjr 82:4f6209cb5c33 145 * is important. It ensures that we don't toggle the clock line
mjr 82:4f6209cb5c33 146 * too quickly. The CCD has a minimum pulse duration of 50ns for
mjr 82:4f6209cb5c33 147 * the clock signal. The DMA controller is so fast that it might
mjr 82:4f6209cb5c33 148 * toggle the clock faster than this limit if we did the Up and
mjr 82:4f6209cb5c33 149 * Down transfers back-to-back.
mjr 82:4f6209cb5c33 150 *
mjr 82:4f6209cb5c33 151 * Note also that it's important for Clock Up to be the very first
mjr 82:4f6209cb5c33 152 * operation after the DMA trigger. The ADC is in continuous mode,
mjr 82:4f6209cb5c33 153 * meaning that it starts taking a new sample immediately upon
mjr 82:4f6209cb5c33 154 * finishing the previous one. So when the ADC DMA signal fires,
mjr 82:4f6209cb5c33 155 * the new sample is already starting. We therefore have to get
mjr 82:4f6209cb5c33 156 * the next pixel onto the sampling pin immediately, or as close
mjr 82:4f6209cb5c33 157 * to immediately as possible. The sensor's "analog output
mjr 82:4f6209cb5c33 158 * settling time" is 120ns - this is the time for a new pixel
mjr 82:4f6209cb5c33 159 * voltage to stabilize on AO after a clock rising edge. So
mjr 82:4f6209cb5c33 160 * assuming that the ADC raises the DMA signal immediately on
mjr 82:4f6209cb5c33 161 * sample completion, and the DMA controller responds within a
mjr 82:4f6209cb5c33 162 * couple of MCU clock cycles, we should have the new pixel voltage
mjr 82:4f6209cb5c33 163 * stable on the sampling pin by about 200ns after the new ADC
mjr 82:4f6209cb5c33 164 * sample cycle starts. The sampling cycle with our current
mjr 82:4f6209cb5c33 165 * parameters is about 2us, so the voltage level is stable for
mjr 82:4f6209cb5c33 166 * 90% of the cycle.
mjr 82:4f6209cb5c33 167 *
mjr 82:4f6209cb5c33 168 * Also, note that it's okay that the ADC sample transfer doesn't
mjr 82:4f6209cb5c33 169 * happen until after the Clock Up DMA transfer. The ADC output
mjr 82:4f6209cb5c33 170 * register holds the last result until the next sample completes,
mjr 82:4f6209cb5c33 171 * so we have about 2us to grab it. The first Clock Up DMA
mjr 82:4f6209cb5c33 172 * transfer only takes a couple of clocks - order of 100ns - so
mjr 82:4f6209cb5c33 173 * we get to it with time to spare.
mjr 82:4f6209cb5c33 174 *
mjr 82:4f6209cb5c33 175 * (Note that it would nicer to handle the clock with a single DMA
mjr 82:4f6209cb5c33 176 * channel, since DMA channels are a limited resource. We could
mjr 82:4f6209cb5c33 177 * conceivably consolidate the clock generator one DMA channel by
mjr 82:4f6209cb5c33 178 * switching the DMA destination to the PTOR "toggle" register, and
mjr 82:4f6209cb5c33 179 * writing *two* times per trigger - once to toggle the clock up,
mjr 82:4f6209cb5c33 180 * and a second time to toggle it down. But I haven't found a way
mjr 82:4f6209cb5c33 181 * to make this work. The obstacle is that the DMA controller can
mjr 82:4f6209cb5c33 182 * only do one transfer per trigger in the fully autonomous mode
mjr 82:4f6209cb5c33 183 * we're using, and to make this toggle scheme work, we'd have to do
mjr 82:4f6209cb5c33 184 * two writes per trigger. Maybe even three or four: I think we'd
mjr 82:4f6209cb5c33 185 * have to throw in one or two no-op writes (of all zeroes) between
mjr 82:4f6209cb5c33 186 * the two toggles, to pad the timing to ensure that the clock pulse
mjr 82:4f6209cb5c33 187 * width is over the sensor's 50ns minimum. But it's the same issue
mjr 82:4f6209cb5c33 188 * whether it's two writes or four. The DMA controller does have a
mjr 82:4f6209cb5c33 189 * "continuous" mode that does an entire transfer on a single trigger,
mjr 82:4f6209cb5c33 190 * but it can't reset itself after such a transfer; CPU intervention
mjr 82:4f6209cb5c33 191 * is required to do that, which means we'd have to service an
mjr 82:4f6209cb5c33 192 * interrupt on every ADC cycle to set up the next clock write.
mjr 82:4f6209cb5c33 193 * Given the 2us cycle time, an interrupt would create a ton of CPU
mjr 82:4f6209cb5c33 194 * load, and I don't think the CPU is fast enough to reliably complete
mjr 82:4f6209cb5c33 195 * the work we'd have to do on each 2us cycle. Fortunately, at
mjr 82:4f6209cb5c33 196 * the moment we can afford to dedicate three channels to this
mjr 82:4f6209cb5c33 197 * module. We only have one other module using the DMA at all
mjr 82:4f6209cb5c33 198 * (the TLC5940 PWM controller interface), and it only needs one
mjr 82:4f6209cb5c33 199 * channel. So the KL25Z's complement of four DMA channels is just
mjr 82:4f6209cb5c33 200 * enough for all of our needs for the moment.)
mjr 101:755f44622abc 201 *
mjr 101:755f44622abc 202 * Note that some of the sensors in this series (TSL1410R, TSL1412S)
mjr 101:755f44622abc 203 * have a "parallel" readout mode that lets them physically deliver
mjr 101:755f44622abc 204 * two pixels at once the MCU, via separate physical connections. This
mjr 101:755f44622abc 205 * could provide a 2X speedup on an MCU equipped with two independent
mjr 101:755f44622abc 206 * ADC samplers. Unfortunately, the KL25Z is not so equipped; even
mjr 101:755f44622abc 207 * though it might appear at first glance to support multiple ADC
mjr 101:755f44622abc 208 * "channels", all of the channels internally multiplex into a single
mjr 101:755f44622abc 209 * converter unit, so the hardware can ultimately perform only one
mjr 101:755f44622abc 210 * conversion at a time. Paradoxically, using the sensor's parallel
mjr 101:755f44622abc 211 * mode is actually *slower* with a KL25Z than using its serial mode,
mjr 101:755f44622abc 212 * because we can only maintain the higher throughput of the KL25Z ADC's
mjr 101:755f44622abc 213 * continuous sampling mode by reading all samples thorugh a single
mjr 101:755f44622abc 214 * channel. Switching channels on alternating samples involves a
mjr 101:755f44622abc 215 * bunch of setup overhead within the ADC hardware that adds lots of
mjr 101:755f44622abc 216 * clocks compared to single-channel continuous mode.
mjr 82:4f6209cb5c33 217 */
mjr 82:4f6209cb5c33 218
mjr 82:4f6209cb5c33 219 #include "mbed.h"
mjr 82:4f6209cb5c33 220 #include "config.h"
mjr 82:4f6209cb5c33 221 #include "AltAnalogIn.h"
mjr 82:4f6209cb5c33 222 #include "SimpleDMA.h"
mjr 82:4f6209cb5c33 223 #include "DMAChannels.h"
mjr 82:4f6209cb5c33 224
mjr 82:4f6209cb5c33 225 #ifndef TSL14XX_H
mjr 82:4f6209cb5c33 226 #define TSL14XX_H
mjr 82:4f6209cb5c33 227
mjr 82:4f6209cb5c33 228
mjr 82:4f6209cb5c33 229 // To allow DMA access to the clock pin, we need to point the DMA
mjr 82:4f6209cb5c33 230 // controller to the IOPORT registers that control the pin. PORT_BASE()
mjr 82:4f6209cb5c33 231 // gives us the address of the register group for the 32 GPIO pins with
mjr 82:4f6209cb5c33 232 // the same letter name as our target pin (e.g., PTA0 through PTA31),
mjr 82:4f6209cb5c33 233 // and PINMASK gives us the bit pattern to write to those registers to
mjr 82:4f6209cb5c33 234 // access our single GPIO pin. Each register group has three special
mjr 82:4f6209cb5c33 235 // registers that update the pin in particular ways: PSOR ("set output
mjr 82:4f6209cb5c33 236 // register") turns pins on, PCOR ("clear output register") turns pins
mjr 82:4f6209cb5c33 237 // off, and PTOR ("toggle output register") toggle pins to the opposite
mjr 82:4f6209cb5c33 238 // of their current values. These registers have special semantics:
mjr 82:4f6209cb5c33 239 // writing a bit as 0 has no effect on the corresponding pin, while
mjr 82:4f6209cb5c33 240 // writing a bit as 1 performs the register's action on the pin. This
mjr 82:4f6209cb5c33 241 // allows a single GPIO pin to be set, cleared, or toggled with a
mjr 82:4f6209cb5c33 242 // 32-bit write to one of these registers, without affecting any of the
mjr 82:4f6209cb5c33 243 // other pins addressed by the register. (It also allows changing any
mjr 82:4f6209cb5c33 244 // group of pins with a single write, although we don't use that
mjr 82:4f6209cb5c33 245 // feature here.)
mjr 82:4f6209cb5c33 246 //
mjr 82:4f6209cb5c33 247 // - To turn a pin ON: PORT_BASE(pin)->PSOR = PINMASK(pin)
mjr 82:4f6209cb5c33 248 // - To turn a pin OFF: PORT_BASE(pin)->PCOR = PINMASK(pin)
mjr 82:4f6209cb5c33 249 // - To toggle a pin: PORT_BASE(pin)->PTOR = PINMASK(pin)
mjr 82:4f6209cb5c33 250 //
mjr 82:4f6209cb5c33 251 #define GPIO_PORT(pin) (((unsigned int)(pin)) >> PORT_SHIFT)
mjr 82:4f6209cb5c33 252 #define GPIO_PORT_BASE(pin) ((GPIO_Type *)(PTA_BASE + GPIO_PORT(pin) * 0x40))
mjr 82:4f6209cb5c33 253 #define GPIO_PINMASK(pin) gpio_set(pin)
mjr 82:4f6209cb5c33 254
mjr 82:4f6209cb5c33 255 IF_DIAG(
mjr 82:4f6209cb5c33 256 extern uint64_t mainLoopIterCheckpt[];
mjr 82:4f6209cb5c33 257 extern Timer mainLoopTimer;)
mjr 82:4f6209cb5c33 258
mjr 82:4f6209cb5c33 259 class TSL14xx
mjr 82:4f6209cb5c33 260 {
mjr 82:4f6209cb5c33 261 public:
mjr 82:4f6209cb5c33 262 // Set up the interface.
mjr 82:4f6209cb5c33 263 //
mjr 82:4f6209cb5c33 264 // nPixSensor = native number of pixels on sensor
mjr 82:4f6209cb5c33 265 // siPin = SI pin (GPIO, digital out)
mjr 82:4f6209cb5c33 266 // clockPin = CLK pin (GPIO, digital out)
mjr 82:4f6209cb5c33 267 // aoPin = AO pin (GPIO, analog in - must be ADC-capable)
mjr 82:4f6209cb5c33 268 TSL14xx(int nPixSensor, PinName siPin, PinName clockPin, PinName aoPin)
mjr 100:1ff35c07217c 269 : adc_dma(DMAch_TSL_ADC),
mjr 100:1ff35c07217c 270 clkUp_dma(DMAch_TSL_CLKUP),
mjr 100:1ff35c07217c 271 clkDn_dma(DMAch_TSL_CLKDN),
mjr 82:4f6209cb5c33 272 si(siPin),
mjr 82:4f6209cb5c33 273 clock(clockPin),
mjr 100:1ff35c07217c 274 ao(aoPin, true, 0), // continuous sampling, fast sampling mode
mjr 82:4f6209cb5c33 275 nPixSensor(nPixSensor)
mjr 82:4f6209cb5c33 276 {
mjr 100:1ff35c07217c 277 // Calibrate the ADC for best accuracy
mjr 100:1ff35c07217c 278 ao.calibrate();
mjr 100:1ff35c07217c 279
mjr 82:4f6209cb5c33 280 // start the sample timer with an arbitrary zero point of 'now'
mjr 82:4f6209cb5c33 281 t.start();
mjr 82:4f6209cb5c33 282
mjr 101:755f44622abc 283 // start with no minimum integration time
mjr 101:755f44622abc 284 tIntMin = 0;
mjr 101:755f44622abc 285
mjr 82:4f6209cb5c33 286 // allocate our double pixel buffers
mjr 82:4f6209cb5c33 287 pix1 = new uint8_t[nPixSensor*2];
mjr 82:4f6209cb5c33 288 pix2 = pix1 + nPixSensor;
mjr 82:4f6209cb5c33 289
mjr 82:4f6209cb5c33 290 // put the first DMA transfer into the first buffer (pix1)
mjr 82:4f6209cb5c33 291 pixDMA = 0;
mjr 101:755f44622abc 292
mjr 101:755f44622abc 293 // DMA owns both buffers until the first transfer completes
mjr 101:755f44622abc 294 clientOwnsStablePix = true;
mjr 82:4f6209cb5c33 295
mjr 82:4f6209cb5c33 296 // remember the clock pin port base and pin mask for fast access
mjr 82:4f6209cb5c33 297 clockPort = GPIO_PORT_BASE(clockPin);
mjr 82:4f6209cb5c33 298 clockMask = GPIO_PINMASK(clockPin);
mjr 82:4f6209cb5c33 299
mjr 82:4f6209cb5c33 300 // clear out power-on random data by clocking through all pixels twice
mjr 82:4f6209cb5c33 301 clear();
mjr 82:4f6209cb5c33 302 clear();
mjr 82:4f6209cb5c33 303
mjr 82:4f6209cb5c33 304 // Set up the Clock Up DMA channel. This channel takes the
mjr 82:4f6209cb5c33 305 // clock high by writing the clock bit to the PSOR (set output)
mjr 82:4f6209cb5c33 306 // register for the clock pin.
mjr 82:4f6209cb5c33 307 clkUp_dma.source(&clockMask, false, 32);
mjr 82:4f6209cb5c33 308 clkUp_dma.destination(&clockPort->PSOR, false, 32);
mjr 82:4f6209cb5c33 309
mjr 82:4f6209cb5c33 310 // Set up the Clock Down DMA channel. This channel takes the
mjr 82:4f6209cb5c33 311 // clock low by writing the clock bit to the PCOR (clear output)
mjr 82:4f6209cb5c33 312 // register for the clock pin.
mjr 82:4f6209cb5c33 313 clkDn_dma.source(&clockMask, false, 32);
mjr 82:4f6209cb5c33 314 clkDn_dma.destination(&clockPort->PCOR, false, 32);
mjr 82:4f6209cb5c33 315
mjr 82:4f6209cb5c33 316 // Set up the ADC transfer DMA channel. This channel transfers
mjr 82:4f6209cb5c33 317 // the current analog sampling result from the ADC output register
mjr 82:4f6209cb5c33 318 // to our pixel array.
mjr 82:4f6209cb5c33 319 ao.initDMA(&adc_dma);
mjr 82:4f6209cb5c33 320
mjr 82:4f6209cb5c33 321 // Set up our chain of linked DMA channel:
mjr 82:4f6209cb5c33 322 //
mjr 82:4f6209cb5c33 323 // ADC sample completion triggers Clock Up
mjr 82:4f6209cb5c33 324 // ...which triggers the ADC transfer
mjr 82:4f6209cb5c33 325 // ...which triggers Clock Down
mjr 82:4f6209cb5c33 326 //
mjr 82:4f6209cb5c33 327 // We operate the ADC in "continuous mode", meaning that it starts
mjr 82:4f6209cb5c33 328 // a new sample immediately after the last one completes. This is
mjr 82:4f6209cb5c33 329 // what keeps the cycle going after the Clock Down, since the Clock
mjr 82:4f6209cb5c33 330 // Down transfer itself doesn't trigger another DMA operation.
mjr 82:4f6209cb5c33 331 clkUp_dma.trigger(Trigger_ADC0);
mjr 82:4f6209cb5c33 332 clkUp_dma.link(adc_dma);
mjr 82:4f6209cb5c33 333 adc_dma.link(clkDn_dma, false);
mjr 82:4f6209cb5c33 334
mjr 82:4f6209cb5c33 335 // Set the trigger on the downstream links to NONE - these are
mjr 82:4f6209cb5c33 336 // triggered by their upstream links, so they don't need separate
mjr 82:4f6209cb5c33 337 // peripheral or software triggers.
mjr 82:4f6209cb5c33 338 adc_dma.trigger(Trigger_NONE);
mjr 82:4f6209cb5c33 339 clkDn_dma.trigger(Trigger_NONE);
mjr 82:4f6209cb5c33 340
mjr 82:4f6209cb5c33 341 // Register an interrupt callback so that we're notified when
mjr 82:4f6209cb5c33 342 // the last transfer completes.
mjr 82:4f6209cb5c33 343 clkDn_dma.attach(this, &TSL14xx::transferDone);
mjr 82:4f6209cb5c33 344
mjr 82:4f6209cb5c33 345 // clear the timing statistics
mjr 82:4f6209cb5c33 346 totalTime = 0.0;
mjr 82:4f6209cb5c33 347 nRuns = 0;
mjr 101:755f44622abc 348
mjr 101:755f44622abc 349 // start the first transfer
mjr 101:755f44622abc 350 startTransfer();
mjr 82:4f6209cb5c33 351 }
mjr 82:4f6209cb5c33 352
mjr 82:4f6209cb5c33 353 // Get the stable pixel array. This is the image array from the
mjr 82:4f6209cb5c33 354 // previous capture. It remains valid until the next startCapture()
mjr 82:4f6209cb5c33 355 // call, at which point this buffer will be reused for the new capture.
mjr 82:4f6209cb5c33 356 void getPix(uint8_t * &pix, uint32_t &t)
mjr 82:4f6209cb5c33 357 {
mjr 82:4f6209cb5c33 358 // return the pixel array that ISN'T assigned to the DMA
mjr 82:4f6209cb5c33 359 if (pixDMA)
mjr 82:4f6209cb5c33 360 {
mjr 82:4f6209cb5c33 361 // DMA owns pix2, so the stable array is pix1
mjr 82:4f6209cb5c33 362 pix = pix1;
mjr 82:4f6209cb5c33 363 t = t1;
mjr 82:4f6209cb5c33 364 }
mjr 82:4f6209cb5c33 365 else
mjr 82:4f6209cb5c33 366 {
mjr 82:4f6209cb5c33 367 // DMA owns pix1, so the stable array is pix2
mjr 82:4f6209cb5c33 368 pix = pix2;
mjr 82:4f6209cb5c33 369 t = t2;
mjr 82:4f6209cb5c33 370 }
mjr 82:4f6209cb5c33 371 }
mjr 82:4f6209cb5c33 372
mjr 86:e30a1f60f783 373 // Wait for the current DMA transfer to finish, and retrieve its
mjr 86:e30a1f60f783 374 // pixel array buffer. This provides access to the latest image
mjr 86:e30a1f60f783 375 // without starting a new transfer. These pixels are valid throughout
mjr 86:e30a1f60f783 376 // the next transfer (started via startCapture()) and remain valid
mjr 86:e30a1f60f783 377 // until the next transfer after that.
mjr 86:e30a1f60f783 378 void waitPix(uint8_t * &pix, uint32_t &t)
mjr 86:e30a1f60f783 379 {
mjr 101:755f44622abc 380 // wait for stable buffer ownership to transfer to the client
mjr 86:e30a1f60f783 381 wait();
mjr 86:e30a1f60f783 382
mjr 86:e30a1f60f783 383 // Return the pixel array that IS assigned to DMA, since this
mjr 86:e30a1f60f783 384 // is the latest buffer filled. This buffer is stable, even
mjr 86:e30a1f60f783 385 // though it's assigned to DMA, because the last transfer is
mjr 86:e30a1f60f783 386 // already finished and thus DMA is no longer accessing the
mjr 86:e30a1f60f783 387 // buffer.
mjr 86:e30a1f60f783 388 if (pixDMA)
mjr 86:e30a1f60f783 389 {
mjr 86:e30a1f60f783 390 // DMA owns pix2
mjr 86:e30a1f60f783 391 pix = pix2;
mjr 86:e30a1f60f783 392 t = t2;
mjr 86:e30a1f60f783 393 }
mjr 86:e30a1f60f783 394 else
mjr 86:e30a1f60f783 395 {
mjr 86:e30a1f60f783 396 // DMA owns pix1
mjr 86:e30a1f60f783 397 pix = pix1;
mjr 86:e30a1f60f783 398 t = t1;
mjr 86:e30a1f60f783 399 }
mjr 86:e30a1f60f783 400 }
mjr 86:e30a1f60f783 401
mjr 101:755f44622abc 402 // Set the requested minimum integration time. If this is less than the
mjr 101:755f44622abc 403 // sensor's physical minimum time, the physical minimum applies.
mjr 101:755f44622abc 404 virtual void setMinIntTime(uint32_t us)
mjr 82:4f6209cb5c33 405 {
mjr 101:755f44622abc 406 tIntMin = us;
mjr 101:755f44622abc 407 }
mjr 101:755f44622abc 408
mjr 101:755f44622abc 409 // Wait for the stable buffer ownership to transfer to the client
mjr 101:755f44622abc 410 void wait() { while (!clientOwnsStablePix) ; }
mjr 101:755f44622abc 411
mjr 101:755f44622abc 412 // Is a buffer available?
mjr 101:755f44622abc 413 bool ready() const { return clientOwnsStablePix; }
mjr 101:755f44622abc 414
mjr 101:755f44622abc 415 // Release the client DMA buffer. The client must call this when it's
mjr 101:755f44622abc 416 // done with the current image frame to release the frame back to the
mjr 101:755f44622abc 417 // DMA subsystem, so that it can hand us the next frame.
mjr 101:755f44622abc 418 void releasePix() { clientOwnsStablePix = false; }
mjr 82:4f6209cb5c33 419
mjr 101:755f44622abc 420 // get the timing statistics - sum of scan time for all scans so far
mjr 101:755f44622abc 421 // in microseconds, and total number of scans so far
mjr 101:755f44622abc 422 void getTimingStats(uint64_t &totalTime, uint32_t &nRuns) const
mjr 101:755f44622abc 423 {
mjr 101:755f44622abc 424 totalTime = this->totalTime;
mjr 101:755f44622abc 425 nRuns = this->nRuns;
mjr 101:755f44622abc 426 }
mjr 101:755f44622abc 427
mjr 101:755f44622abc 428 // get the average scan time in microseconds
mjr 101:755f44622abc 429 uint32_t getAvgScanTime() const
mjr 101:755f44622abc 430 {
mjr 101:755f44622abc 431 return uint32_t(totalTime / nRuns);
mjr 101:755f44622abc 432 }
mjr 101:755f44622abc 433
mjr 101:755f44622abc 434 private:
mjr 101:755f44622abc 435 // Start a new transfer. We call this at the end of each integration
mjr 101:755f44622abc 436 // cycle, in interrupt mode. This can be called directly by the interrupt
mjr 101:755f44622abc 437 // handler invoked when the DMA transfer completes, or by a timeout. In
mjr 101:755f44622abc 438 // either case, we're in interrupt mode.
mjr 101:755f44622abc 439 void startTransfer()
mjr 101:755f44622abc 440 {
mjr 101:755f44622abc 441 // If we own the stable buffer, swap buffers: hand ownership of the
mjr 101:755f44622abc 442 // old DMA buffer to the client, and take control of the old client
mjr 101:755f44622abc 443 // buffer (which the client must be done with if we own it) as our
mjr 101:755f44622abc 444 // new DMA buffer.
mjr 101:755f44622abc 445 //
mjr 101:755f44622abc 446 // If the client owns the stable buffer, we can't swap buffers,
mjr 101:755f44622abc 447 // because the client is still working on the stable one. So we
mjr 101:755f44622abc 448 // must start the new transfer using the existing DMA buffer.
mjr 101:755f44622abc 449 if (!clientOwnsStablePix)
mjr 82:4f6209cb5c33 450 {
mjr 101:755f44622abc 451 // swap buffers
mjr 101:755f44622abc 452 pixDMA ^= 1;
mjr 101:755f44622abc 453
mjr 101:755f44622abc 454 // release the prior DMA buffer to the client
mjr 101:755f44622abc 455 clientOwnsStablePix = true;
mjr 82:4f6209cb5c33 456 }
mjr 82:4f6209cb5c33 457
mjr 82:4f6209cb5c33 458 // Set up the active pixel array as the destination buffer for
mjr 82:4f6209cb5c33 459 // the ADC DMA channel.
mjr 82:4f6209cb5c33 460 adc_dma.destination(pixDMA ? pix2 : pix1, true);
mjr 82:4f6209cb5c33 461
mjr 82:4f6209cb5c33 462 // start the DMA transfers
mjr 82:4f6209cb5c33 463 clkDn_dma.start(nPixSensor*4, true);
mjr 82:4f6209cb5c33 464 adc_dma.start(nPixSensor, true);
mjr 82:4f6209cb5c33 465 clkUp_dma.start(nPixSensor*4, true);
mjr 82:4f6209cb5c33 466
mjr 82:4f6209cb5c33 467 // note the start time of this transfer
mjr 82:4f6209cb5c33 468 t0 = t.read_us();
mjr 82:4f6209cb5c33 469
mjr 82:4f6209cb5c33 470 // start the next integration cycle by pulsing SI and one clock
mjr 82:4f6209cb5c33 471 si = 1;
mjr 82:4f6209cb5c33 472 clock = 1;
mjr 82:4f6209cb5c33 473 si = 0;
mjr 82:4f6209cb5c33 474 clock = 0;
mjr 82:4f6209cb5c33 475
mjr 82:4f6209cb5c33 476 // Set the timestamp for the current active buffer. The SI pulse
mjr 86:e30a1f60f783 477 // we just did performed the HOLD operation, which takes a snapshot
mjr 86:e30a1f60f783 478 // of the photo receptors and stores it in the sensor's shift
mjr 86:e30a1f60f783 479 // register. We noted the start of the current integration cycle
mjr 86:e30a1f60f783 480 // in tInt when we started it during the previous scan. The image
mjr 86:e30a1f60f783 481 // we're about to transfer therefore represents the light collected
mjr 86:e30a1f60f783 482 // between tInt and right now (actually, the SI pulse above, but
mjr 86:e30a1f60f783 483 // close enough). The image covers a time range rather than a
mjr 86:e30a1f60f783 484 // single point in time, but we still have to give it a single
mjr 86:e30a1f60f783 485 // timestamp. Use the midpoint of the integration period.
mjr 82:4f6209cb5c33 486 uint32_t tmid = (t0 + tInt) >> 1;
mjr 82:4f6209cb5c33 487 if (pixDMA)
mjr 82:4f6209cb5c33 488 t2 = tmid;
mjr 82:4f6209cb5c33 489 else
mjr 82:4f6209cb5c33 490 t1 = tmid;
mjr 82:4f6209cb5c33 491
mjr 82:4f6209cb5c33 492 // Start the ADC sampler. The ADC will read samples continuously
mjr 82:4f6209cb5c33 493 // until we tell it to stop. Each sample completion will trigger
mjr 82:4f6209cb5c33 494 // our linked DMA channel, which will store the next sample in our
mjr 82:4f6209cb5c33 495 // pixel array and pulse the CCD serial data clock to load the next
mjr 82:4f6209cb5c33 496 // pixel onto the analog sampler pin. This will all happen without
mjr 82:4f6209cb5c33 497 // any CPU involvement, so we can continue with other work.
mjr 82:4f6209cb5c33 498 ao.start();
mjr 82:4f6209cb5c33 499
mjr 82:4f6209cb5c33 500 // The new integration cycle starts with the 19th clock pulse
mjr 82:4f6209cb5c33 501 // after the SI pulse. We offload all of the transfer work (including
mjr 86:e30a1f60f783 502 // the clock pulse generation) to the DMA controller, which doesn't
mjr 86:e30a1f60f783 503 // notify when that 19th pulse occurs, so we have to approximate.
mjr 86:e30a1f60f783 504 // Based on empirical measurements, each pixel transfer in our DMA
mjr 86:e30a1f60f783 505 // setup takes about 2us, so clocking 19 pixels takes about 38us.
mjr 86:e30a1f60f783 506 // In addition, the ADC takes about 4us extra for the first read.
mjr 86:e30a1f60f783 507 tInt = t.read_us() + 19*2 + 4;
mjr 82:4f6209cb5c33 508 }
mjr 82:4f6209cb5c33 509
mjr 101:755f44622abc 510 // End of transfer notification. This is called as an interrupt
mjr 101:755f44622abc 511 // handler when the DMA transfer completes.
mjr 101:755f44622abc 512 void transferDone()
mjr 82:4f6209cb5c33 513 {
mjr 101:755f44622abc 514 // stop the ADC sampler
mjr 101:755f44622abc 515 ao.stop();
mjr 101:755f44622abc 516
mjr 101:755f44622abc 517 // clock out one extra pixel to leave the analog out pin on
mjr 101:755f44622abc 518 // the sensor in the high-Z state
mjr 101:755f44622abc 519 clock = 1;
mjr 101:755f44622abc 520 clock = 0;
mjr 101:755f44622abc 521
mjr 101:755f44622abc 522 // add this sample to the timing statistics (for diagnostics and
mjr 101:755f44622abc 523 // performance measurement)
mjr 101:755f44622abc 524 uint32_t now = t.read_us();
mjr 101:755f44622abc 525 totalTime += uint32_t(now - t0);
mjr 101:755f44622abc 526 nRuns += 1;
mjr 101:755f44622abc 527
mjr 101:755f44622abc 528 // note the ending time of the transfer
mjr 101:755f44622abc 529 tDone = now;
mjr 101:755f44622abc 530
mjr 101:755f44622abc 531 // Figure the time remaining to reach the minimum requested
mjr 101:755f44622abc 532 // integration time for the next cycle. The sensor is currently
mjr 101:755f44622abc 533 // working on an integration cycle that started at tInt, and that
mjr 101:755f44622abc 534 // cycle will end when we start the next cycle. We therefore want
mjr 101:755f44622abc 535 // to wait to start the next cycle until we've reached the desired
mjr 101:755f44622abc 536 // total integration time.
mjr 101:755f44622abc 537 uint32_t dt = now - tInt;
mjr 104:6e06e0f4b476 538
mjr 104:6e06e0f4b476 539 // Figure the time to the start of the next transfer. Wait for the
mjr 104:6e06e0f4b476 540 // remainder of the current integration period if we haven't yet
mjr 104:6e06e0f4b476 541 // reached the requested minimum, otherwise just start almost
mjr 104:6e06e0f4b476 542 // immediately. (Not *actually* immediately: we don't want to start
mjr 104:6e06e0f4b476 543 // the new transfer within this interrupt handler, because the DMA
mjr 104:6e06e0f4b476 544 // IRQ doesn't reliably clear if we start a new transfer immediately.)
mjr 104:6e06e0f4b476 545 uint32_t dtNext = dt < tIntMin ? tIntMin - dt : 1;
mjr 104:6e06e0f4b476 546
mjr 104:6e06e0f4b476 547 // Schedule the next transfer
mjr 104:6e06e0f4b476 548 integrationTimeout.attach_us(this, &TSL14xx::startTransfer, dtNext);
mjr 82:4f6209cb5c33 549 }
mjr 101:755f44622abc 550
mjr 101:755f44622abc 551 // Clear the sensor shift register. Clocks in all of the pixels from
mjr 101:755f44622abc 552 // the sensor without bothering to read them on the ADC. Pulses SI
mjr 101:755f44622abc 553 // at the beginning of the operation, which starts a new integration
mjr 101:755f44622abc 554 // cycle.
mjr 82:4f6209cb5c33 555 void clear()
mjr 82:4f6209cb5c33 556 {
mjr 82:4f6209cb5c33 557 // get the clock toggle register
mjr 82:4f6209cb5c33 558 volatile uint32_t *ptor = &clockPort->PTOR;
mjr 82:4f6209cb5c33 559
mjr 82:4f6209cb5c33 560 // make sure any DMA run is completed
mjr 82:4f6209cb5c33 561 wait();
mjr 82:4f6209cb5c33 562
mjr 82:4f6209cb5c33 563 // clock in an SI pulse
mjr 82:4f6209cb5c33 564 si = 1;
mjr 82:4f6209cb5c33 565 *ptor = clockMask;
mjr 82:4f6209cb5c33 566 clockPort->PSOR = clockMask;
mjr 82:4f6209cb5c33 567 si = 0;
mjr 82:4f6209cb5c33 568 *ptor = clockMask;
mjr 82:4f6209cb5c33 569
mjr 82:4f6209cb5c33 570 // This starts a new integration period. Or more precisely, the
mjr 82:4f6209cb5c33 571 // 19th clock pulse will start the new integration period. We're
mjr 82:4f6209cb5c33 572 // going to blast the clock signal as fast as we can, at about
mjr 82:4f6209cb5c33 573 // 100ns intervals (50ns up and 50ns down), so the 19th clock
mjr 82:4f6209cb5c33 574 // will be about 2us from now.
mjr 82:4f6209cb5c33 575 tInt = t.read_us() + 2;
mjr 82:4f6209cb5c33 576
mjr 82:4f6209cb5c33 577 // clock out all pixels, plus an extra one to clock past the last
mjr 82:4f6209cb5c33 578 // pixel and reset the last pixel's internal sampling switch in
mjr 82:4f6209cb5c33 579 // the sensor
mjr 82:4f6209cb5c33 580 for (int i = 0 ; i < nPixSensor + 1 ; )
mjr 82:4f6209cb5c33 581 {
mjr 82:4f6209cb5c33 582 // toggle the clock to take it high
mjr 82:4f6209cb5c33 583 *ptor = clockMask;
mjr 82:4f6209cb5c33 584
mjr 82:4f6209cb5c33 585 // increment our loop variable here to pad the timing, to
mjr 82:4f6209cb5c33 586 // keep our pulse width long enough for the sensor
mjr 82:4f6209cb5c33 587 ++i;
mjr 82:4f6209cb5c33 588
mjr 82:4f6209cb5c33 589 // toggle the clock to take it low
mjr 82:4f6209cb5c33 590 *ptor = clockMask;
mjr 82:4f6209cb5c33 591 }
mjr 82:4f6209cb5c33 592 }
mjr 86:e30a1f60f783 593
mjr 82:4f6209cb5c33 594 // DMA controller interfaces
mjr 82:4f6209cb5c33 595 SimpleDMA adc_dma; // DMA channel for reading the analog input
mjr 82:4f6209cb5c33 596 SimpleDMA clkUp_dma; // "Clock Up" channel
mjr 82:4f6209cb5c33 597 SimpleDMA clkDn_dma; // "Clock Down" channel
mjr 82:4f6209cb5c33 598
mjr 82:4f6209cb5c33 599 // Sensor interface pins
mjr 82:4f6209cb5c33 600 DigitalOut si; // GPIO pin for sensor SI (serial data)
mjr 82:4f6209cb5c33 601 DigitalOut clock; // GPIO pin for sensor SCLK (serial clock)
mjr 82:4f6209cb5c33 602 GPIO_Type *clockPort; // IOPORT base address for clock pin - cached for DMA writes
mjr 82:4f6209cb5c33 603 uint32_t clockMask; // IOPORT register bit mask for clock pin
mjr 100:1ff35c07217c 604 AltAnalogIn_8bit ao; // GPIO pin for sensor AO (analog output)
mjr 82:4f6209cb5c33 605
mjr 82:4f6209cb5c33 606 // number of pixels in the physical sensor array
mjr 82:4f6209cb5c33 607 int nPixSensor; // number of pixels in physical sensor array
mjr 82:4f6209cb5c33 608
mjr 82:4f6209cb5c33 609 // pixel buffers - we keep two buffers so that we can transfer the
mjr 82:4f6209cb5c33 610 // current sensor data into one buffer via DMA while we concurrently
mjr 82:4f6209cb5c33 611 // process the last buffer
mjr 82:4f6209cb5c33 612 uint8_t *pix1; // pixel array 1
mjr 82:4f6209cb5c33 613 uint8_t *pix2; // pixel array 2
mjr 82:4f6209cb5c33 614
mjr 82:4f6209cb5c33 615 // Timestamps of pix1 and pix2 arrays, in microseconds, in terms of the
mjr 100:1ff35c07217c 616 // sample timer (this->t).
mjr 82:4f6209cb5c33 617 uint32_t t1;
mjr 82:4f6209cb5c33 618 uint32_t t2;
mjr 82:4f6209cb5c33 619
mjr 82:4f6209cb5c33 620 // DMA target buffer. This is the buffer for the next DMA transfer.
mjr 82:4f6209cb5c33 621 // 0 means pix1, 1 means pix2. The other buffer contains the stable
mjr 82:4f6209cb5c33 622 // data from the last transfer.
mjr 82:4f6209cb5c33 623 uint8_t pixDMA;
mjr 82:4f6209cb5c33 624
mjr 101:755f44622abc 625 // Stable buffer ownership. At any given time, the DMA subsystem owns
mjr 101:755f44622abc 626 // the buffer specified by pixDMA. The other buffer - the "stable" buffer,
mjr 101:755f44622abc 627 // which contains the most recent completed frame, can be owned by EITHER
mjr 101:755f44622abc 628 // the client or by the DMA subsystem. Each time a DMA transfer completes,
mjr 101:755f44622abc 629 // the DMA subsystem looks at the stable buffer owner flag to determine
mjr 101:755f44622abc 630 // what to do:
mjr 101:755f44622abc 631 //
mjr 101:755f44622abc 632 // - If the DMA subsystem owns the stable buffer, it swaps buffers. This
mjr 101:755f44622abc 633 // makes the newly completed DMA buffer the new stable buffer, and makes
mjr 101:755f44622abc 634 // the old stable buffer the new DMA buffer. At this time, the DMA
mjr 101:755f44622abc 635 // subsystem also changes the stable buffer ownership to CLIENT.
mjr 101:755f44622abc 636 //
mjr 101:755f44622abc 637 // - If the CLIENT owns the stable buffer, the DMA subsystem can't swap
mjr 101:755f44622abc 638 // buffers, because the client is still using the stable buffer. It
mjr 101:755f44622abc 639 // simply leaves things as they are.
mjr 101:755f44622abc 640 //
mjr 101:755f44622abc 641 // In either case, the DMA system starts a new transfer at this point.
mjr 101:755f44622abc 642 //
mjr 101:755f44622abc 643 // The client, meanwhile, is free to access the stable buffer when it has
mjr 101:755f44622abc 644 // ownership. If the client *doesn't* have ownership, it must wait for
mjr 101:755f44622abc 645 // the ownership to be transferred, which can only be done by the DMA
mjr 101:755f44622abc 646 // subsystem on completing a transfer.
mjr 101:755f44622abc 647 //
mjr 101:755f44622abc 648 // When the client is done with the stable buffer, it transfers ownership
mjr 101:755f44622abc 649 // back to the DMA subsystem.
mjr 101:755f44622abc 650 //
mjr 101:755f44622abc 651 // Transfers of ownership from DMA to CLIENT are done only by DMA.
mjr 101:755f44622abc 652 // Transfers from CLIENT to DMA are done only by CLIENT. So whoever has
mjr 101:755f44622abc 653 // ownership now is responsible for transferring ownership.
mjr 101:755f44622abc 654 //
mjr 101:755f44622abc 655 volatile bool clientOwnsStablePix;
mjr 101:755f44622abc 656
mjr 101:755f44622abc 657 // End-of-integration timeout handler. This lets us fire an interrupt
mjr 101:755f44622abc 658 // when the current integration cycle is done, so that we can start the
mjr 101:755f44622abc 659 // next cycle.
mjr 101:755f44622abc 660 Timeout integrationTimeout;
mjr 101:755f44622abc 661
mjr 101:755f44622abc 662 // Requested minimum integration time, in micoseconds. The client can use
mjr 101:755f44622abc 663 // this to control the exposure level, by increasing it for a longer
mjr 101:755f44622abc 664 // exposure and thus more light-gathering in low-light conditions. Note
mjr 101:755f44622abc 665 // that the physical limit on the minimum integration time is roughly equal
mjr 101:755f44622abc 666 // to the pixel file transfer time, because the integration cycle is
mjr 101:755f44622abc 667 // initiated and ended by transfer starts. It's thus impossible to make
mjr 101:755f44622abc 668 // the integration time less than the time for one full pixel file
mjr 101:755f44622abc 669 // transfer.
mjr 101:755f44622abc 670 uint32_t tIntMin;
mjr 101:755f44622abc 671
mjr 82:4f6209cb5c33 672 // timing statistics
mjr 82:4f6209cb5c33 673 Timer t; // sample timer
mjr 82:4f6209cb5c33 674 uint32_t t0; // start time (us) of current sample
mjr 82:4f6209cb5c33 675 uint32_t tInt; // start time (us) of current integration period
mjr 86:e30a1f60f783 676 uint32_t tDone; // end time of latest finished transfer
mjr 82:4f6209cb5c33 677 uint64_t totalTime; // total time consumed by all reads so far
mjr 82:4f6209cb5c33 678 uint32_t nRuns; // number of runs so far
mjr 82:4f6209cb5c33 679 };
mjr 82:4f6209cb5c33 680
mjr 82:4f6209cb5c33 681 #endif /* TSL14XX_H */