Mirror with some correction

Dependencies:   mbed FastIO FastPWM USBDevice

Committer:
mjr
Date:
Thu Apr 13 23:20:28 2017 +0000
Revision:
82:4f6209cb5c33
Child:
86:e30a1f60f783
Plunger refactoring; AEDR-8300 added; TSL1401CL in progress; VL6180X added

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