Pinscape Controller version 1 fork. This is a fork to allow for ongoing bug fixes to the original controller version, from before the major changes for the expansion board project.
Dependencies: FastIO FastPWM SimpleDMA mbed
Fork of Pinscape_Controller by
TSL1410R/tsl1410r.h@43:7a6364d82a41, 2016-02-06 (annotated)
- Committer:
- mjr
- Date:
- Sat Feb 06 20:21:48 2016 +0000
- Revision:
- 43:7a6364d82a41
- Parent:
- 40:cc0d9814522b
Before floating point plunger ranging
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
mjr | 2:c174f9ee414a | 1 | /* |
mjr | 2:c174f9ee414a | 2 | * TSL1410R interface class. |
mjr | 2:c174f9ee414a | 3 | * |
mjr | 2:c174f9ee414a | 4 | * This provides a high-level interface for the Taos TSL1410R linear CCD array sensor. |
mjr | 2:c174f9ee414a | 5 | */ |
mjr | 2:c174f9ee414a | 6 | |
mjr | 35:e959ffba78fd | 7 | #include "mbed.h" |
mjr | 35:e959ffba78fd | 8 | #include "config.h" |
mjr | 43:7a6364d82a41 | 9 | #include "AltAnalogIn.h" |
mjr | 2:c174f9ee414a | 10 | |
mjr | 35:e959ffba78fd | 11 | #ifndef TSL1410R_H |
mjr | 35:e959ffba78fd | 12 | #define TSL1410R_H |
mjr | 35:e959ffba78fd | 13 | |
mjr | 35:e959ffba78fd | 14 | // For faster GPIO on the clock pin, we write the IOPORT registers directly. |
mjr | 35:e959ffba78fd | 15 | // PORT_BASE gives us the memory mapped location of the IOPORT register set |
mjr | 35:e959ffba78fd | 16 | // for a pin; PINMASK gives us the bit pattern to write to the registers. |
mjr | 35:e959ffba78fd | 17 | // |
mjr | 35:e959ffba78fd | 18 | // - To turn a pin ON: PORT_BASE(pin)->PSOR |= PINMASK(pin) |
mjr | 35:e959ffba78fd | 19 | // - To turn a pin OFF: PORT_BASE(pin)->PCOR |= PINMASK(pin) |
mjr | 35:e959ffba78fd | 20 | // - To toggle a pin: PORT_BASE(pin)->PTOR |= PINMASK(pin) |
mjr | 35:e959ffba78fd | 21 | // |
mjr | 35:e959ffba78fd | 22 | // When used in a loop where the port address and pin mask are cached in |
mjr | 35:e959ffba78fd | 23 | // local variables, this runs at the same speed as the FastIO library - about |
mjr | 35:e959ffba78fd | 24 | // 78ns per pin write on the KL25Z. Not surprising since it's doing the same |
mjr | 35:e959ffba78fd | 25 | // thing, and the compiler should be able to reduce a pin write to a single ARM |
mjr | 35:e959ffba78fd | 26 | // instruction when the port address and mask are in local register variables. |
mjr | 35:e959ffba78fd | 27 | // The advantage over the FastIO library is that this approach allows for pins |
mjr | 35:e959ffba78fd | 28 | // to be assigned dynamically at run-time, which we prefer because it allows for |
mjr | 35:e959ffba78fd | 29 | // configuration changes to be made on the fly rather than having to recompile |
mjr | 35:e959ffba78fd | 30 | // the program. |
mjr | 43:7a6364d82a41 | 31 | #define GPIO_PORT(pin) (((unsigned int)(pin)) >> PORT_SHIFT) |
mjr | 43:7a6364d82a41 | 32 | #define GPIO_PORT_BASE(pin) ((FGPIO_Type *)(FPTA_BASE + GPIO_PORT(pin) * 0x40)) |
mjr | 43:7a6364d82a41 | 33 | #define GPIO_PINMASK(pin) gpio_set(pin) |
mjr | 2:c174f9ee414a | 34 | |
mjr | 35:e959ffba78fd | 35 | class TSL1410R |
mjr | 2:c174f9ee414a | 36 | { |
mjr | 2:c174f9ee414a | 37 | public: |
mjr | 43:7a6364d82a41 | 38 | TSL1410R(int nPixSensor, PinName siPin, PinName clockPin, PinName ao1Pin, PinName ao2Pin) |
mjr | 43:7a6364d82a41 | 39 | : nPixSensor(nPixSensor), si(siPin), clock(clockPin), ao1(ao1Pin), ao2(ao2Pin) |
mjr | 17:ab3cec0c8bf4 | 40 | { |
mjr | 35:e959ffba78fd | 41 | // we're in parallel mode if ao2 is a valid pin |
mjr | 35:e959ffba78fd | 42 | parallel = (ao2Pin != NC); |
mjr | 35:e959ffba78fd | 43 | |
mjr | 35:e959ffba78fd | 44 | // remember the clock pin port base and pin mask for fast access |
mjr | 35:e959ffba78fd | 45 | clockPort = GPIO_PORT_BASE(clockPin); |
mjr | 35:e959ffba78fd | 46 | clockMask = GPIO_PINMASK(clockPin); |
mjr | 35:e959ffba78fd | 47 | |
mjr | 43:7a6364d82a41 | 48 | // clear out power-on random data by clocking through all pixels twice |
mjr | 17:ab3cec0c8bf4 | 49 | clear(); |
mjr | 17:ab3cec0c8bf4 | 50 | clear(); |
mjr | 43:7a6364d82a41 | 51 | |
mjr | 43:7a6364d82a41 | 52 | totalTime = 0.0; nRuns = 0; // $$$ |
mjr | 17:ab3cec0c8bf4 | 53 | } |
mjr | 43:7a6364d82a41 | 54 | |
mjr | 43:7a6364d82a41 | 55 | float totalTime; int nRuns; // $$$ |
mjr | 2:c174f9ee414a | 56 | |
mjr | 17:ab3cec0c8bf4 | 57 | // Read the pixels. |
mjr | 17:ab3cec0c8bf4 | 58 | // |
mjr | 17:ab3cec0c8bf4 | 59 | // 'n' specifies the number of pixels to sample, and is the size of |
mjr | 17:ab3cec0c8bf4 | 60 | // the output array 'pix'. This can be less than the full number |
mjr | 17:ab3cec0c8bf4 | 61 | // of pixels on the physical device; if it is, we'll spread the |
mjr | 17:ab3cec0c8bf4 | 62 | // sample evenly across the full length of the device by skipping |
mjr | 17:ab3cec0c8bf4 | 63 | // one or more pixels between each sampled pixel to pad out the |
mjr | 17:ab3cec0c8bf4 | 64 | // difference between the sample size and the physical CCD size. |
mjr | 17:ab3cec0c8bf4 | 65 | // For example, if the physical sensor has 1280 pixels, and 'n' is |
mjr | 17:ab3cec0c8bf4 | 66 | // 640, we'll read every other pixel and skip every other pixel. |
mjr | 17:ab3cec0c8bf4 | 67 | // If 'n' is 160, we'll read every 8th pixel and skip 7 between |
mjr | 17:ab3cec0c8bf4 | 68 | // each sample. |
mjr | 17:ab3cec0c8bf4 | 69 | // |
mjr | 17:ab3cec0c8bf4 | 70 | // The reason that we provide this subset mode (where 'n' is less |
mjr | 17:ab3cec0c8bf4 | 71 | // than the physical pixel count) is that reading a pixel is the most |
mjr | 17:ab3cec0c8bf4 | 72 | // time-consuming part of the scan. For each pixel we read, we have |
mjr | 17:ab3cec0c8bf4 | 73 | // to wait for the pixel's charge to transfer from its internal smapling |
mjr | 17:ab3cec0c8bf4 | 74 | // capacitor to the CCD's output pin, for that charge to transfer to |
mjr | 17:ab3cec0c8bf4 | 75 | // the KL25Z input pin, and for the KL25Z ADC to get a stable reading. |
mjr | 17:ab3cec0c8bf4 | 76 | // This all takes on the order of 20us per pixel. Skipping a pixel |
mjr | 17:ab3cec0c8bf4 | 77 | // only requires a clock pulse, which takes about 350ns. So we can |
mjr | 17:ab3cec0c8bf4 | 78 | // skip 60 pixels in the time it takes to sample 1 pixel. |
mjr | 2:c174f9ee414a | 79 | // |
mjr | 2:c174f9ee414a | 80 | // We clock an SI pulse at the beginning of the read. This starts the |
mjr | 2:c174f9ee414a | 81 | // next integration cycle: the pixel array will reset on the SI, and |
mjr | 17:ab3cec0c8bf4 | 82 | // the integration starts 18 clocks later. So by the time this method |
mjr | 17:ab3cec0c8bf4 | 83 | // returns, the next sample will have been integrating for npix-18 clocks. |
mjr | 17:ab3cec0c8bf4 | 84 | // That's usually enough time to allow immediately reading the next |
mjr | 17:ab3cec0c8bf4 | 85 | // sample. If more integration time is required, the caller can simply |
mjr | 2:c174f9ee414a | 86 | // sleep/spin for the desired additional time, or can do other work that |
mjr | 17:ab3cec0c8bf4 | 87 | // takes the desired additional time. |
mjr | 2:c174f9ee414a | 88 | // |
mjr | 2:c174f9ee414a | 89 | // If the caller has other work to tend to that takes longer than the |
mjr | 2:c174f9ee414a | 90 | // desired maximum integration time, it can call clear() to clock out |
mjr | 2:c174f9ee414a | 91 | // the current pixels and start a fresh integration cycle. |
mjr | 43:7a6364d82a41 | 92 | void read(register uint16_t *pix, int n) |
mjr | 17:ab3cec0c8bf4 | 93 | { |
mjr | 43:7a6364d82a41 | 94 | Timer t; t.start(); // $$$ |
mjr | 43:7a6364d82a41 | 95 | |
mjr | 35:e959ffba78fd | 96 | // get the clock pin pointers into local variables for fast access |
mjr | 43:7a6364d82a41 | 97 | register volatile uint32_t *clockPSOR = &clockPort->PSOR; |
mjr | 43:7a6364d82a41 | 98 | register volatile uint32_t *clockPCOR = &clockPort->PCOR; |
mjr | 43:7a6364d82a41 | 99 | register const uint32_t clockMask = this->clockMask; |
mjr | 35:e959ffba78fd | 100 | |
mjr | 17:ab3cec0c8bf4 | 101 | // start the next integration cycle by pulsing SI and one clock |
mjr | 17:ab3cec0c8bf4 | 102 | si = 1; |
mjr | 43:7a6364d82a41 | 103 | clock = 1; |
mjr | 17:ab3cec0c8bf4 | 104 | si = 0; |
mjr | 43:7a6364d82a41 | 105 | clock = 0; |
mjr | 17:ab3cec0c8bf4 | 106 | |
mjr | 17:ab3cec0c8bf4 | 107 | // figure how many pixels to skip on each read |
mjr | 43:7a6364d82a41 | 108 | int skip = nPixSensor/n - 1; |
mjr | 17:ab3cec0c8bf4 | 109 | |
mjr | 43:7a6364d82a41 | 110 | ///$$$ |
mjr | 43:7a6364d82a41 | 111 | static int done=0; |
mjr | 43:7a6364d82a41 | 112 | if (done++ == 0) printf("nPixSensor=%d, n=%d, skip=%d, parallel=%d\r\n", nPixSensor, n, skip, parallel); |
mjr | 43:7a6364d82a41 | 113 | |
mjr | 43:7a6364d82a41 | 114 | // get the clock PSOR and PCOR register addresses for fast access |
mjr | 43:7a6364d82a41 | 115 | |
mjr | 17:ab3cec0c8bf4 | 116 | // read all of the pixels |
mjr | 43:7a6364d82a41 | 117 | int dst; |
mjr | 35:e959ffba78fd | 118 | if (parallel) |
mjr | 17:ab3cec0c8bf4 | 119 | { |
mjr | 43:7a6364d82a41 | 120 | // Parallel mode - read pixels from each half sensor concurrently. |
mjr | 43:7a6364d82a41 | 121 | // Divide 'n' (the output pixel count) by 2 to get the loop count, |
mjr | 43:7a6364d82a41 | 122 | // since we're going to do 2 pixels on each iteration. |
mjr | 43:7a6364d82a41 | 123 | for (n /= 2, dst = 0 ; dst < n ; ++dst) |
mjr | 17:ab3cec0c8bf4 | 124 | { |
mjr | 43:7a6364d82a41 | 125 | // Take the clock high. The TSL1410R will connect the next |
mjr | 43:7a6364d82a41 | 126 | // pixel pair's hold capacitors to the A01 and AO2 lines |
mjr | 43:7a6364d82a41 | 127 | // (respectively) on the clock rising edge. |
mjr | 43:7a6364d82a41 | 128 | *clockPSOR = clockMask; |
mjr | 43:7a6364d82a41 | 129 | |
mjr | 43:7a6364d82a41 | 130 | // Start the ADC sampler for AO1. The TSL1410R sample |
mjr | 43:7a6364d82a41 | 131 | // stabilization time per the data sheet is 120ns. This is |
mjr | 43:7a6364d82a41 | 132 | // fast enough that we don't need an explicit delay, since |
mjr | 43:7a6364d82a41 | 133 | // the instructions to execute this call will take longer |
mjr | 43:7a6364d82a41 | 134 | // than that. |
mjr | 43:7a6364d82a41 | 135 | ao1.start(); |
mjr | 35:e959ffba78fd | 136 | |
mjr | 43:7a6364d82a41 | 137 | // take the clock low while we're waiting for the reading |
mjr | 43:7a6364d82a41 | 138 | *clockPCOR = clockMask; |
mjr | 43:7a6364d82a41 | 139 | |
mjr | 43:7a6364d82a41 | 140 | // Read the first half-sensor pixel from AO1 |
mjr | 35:e959ffba78fd | 141 | pix[dst] = ao1.read_u16(); |
mjr | 35:e959ffba78fd | 142 | |
mjr | 43:7a6364d82a41 | 143 | // Read the second half-sensor pixel from AO2, and store it |
mjr | 43:7a6364d82a41 | 144 | // in the destination array at the current index PLUS 'n', |
mjr | 43:7a6364d82a41 | 145 | // which you will recall contains half the output pixel count. |
mjr | 43:7a6364d82a41 | 146 | // This second pixel is halfway up the sensor from the first |
mjr | 43:7a6364d82a41 | 147 | // pixel, so it goes halfway up the output array from the |
mjr | 43:7a6364d82a41 | 148 | // current output position. |
mjr | 43:7a6364d82a41 | 149 | ao2.start(); |
mjr | 43:7a6364d82a41 | 150 | pix[dst + n] = ao2.read_u16(); |
mjr | 35:e959ffba78fd | 151 | |
mjr | 43:7a6364d82a41 | 152 | // Clock through the skipped pixels |
mjr | 43:7a6364d82a41 | 153 | for (int i = skip ; i > 0 ; --i) |
mjr | 35:e959ffba78fd | 154 | { |
mjr | 43:7a6364d82a41 | 155 | *clockPSOR = clockMask; |
mjr | 43:7a6364d82a41 | 156 | *clockPCOR = clockMask; |
mjr | 35:e959ffba78fd | 157 | } |
mjr | 35:e959ffba78fd | 158 | } |
mjr | 35:e959ffba78fd | 159 | } |
mjr | 35:e959ffba78fd | 160 | else |
mjr | 35:e959ffba78fd | 161 | { |
mjr | 35:e959ffba78fd | 162 | // serial mode - read all pixels in a single file |
mjr | 43:7a6364d82a41 | 163 | for (dst = 0 ; dst < n ; ++dst) |
mjr | 35:e959ffba78fd | 164 | { |
mjr | 43:7a6364d82a41 | 165 | // Clock the next pixel onto the sensor A0 line |
mjr | 43:7a6364d82a41 | 166 | *clockPSOR = clockMask; |
mjr | 35:e959ffba78fd | 167 | |
mjr | 43:7a6364d82a41 | 168 | // start the ADC sampler |
mjr | 43:7a6364d82a41 | 169 | ao1.start(); |
mjr | 35:e959ffba78fd | 170 | |
mjr | 43:7a6364d82a41 | 171 | // take the clock low while we're waiting for the analog reading |
mjr | 43:7a6364d82a41 | 172 | *clockPCOR = clockMask; |
mjr | 35:e959ffba78fd | 173 | |
mjr | 43:7a6364d82a41 | 174 | // wait for and read the ADC sample; plug it into the output |
mjr | 43:7a6364d82a41 | 175 | // array, and increment the output pointer to the next position |
mjr | 43:7a6364d82a41 | 176 | pix[dst] = ao1.read_u16(); |
mjr | 43:7a6364d82a41 | 177 | |
mjr | 43:7a6364d82a41 | 178 | // clock through the skipped pixels |
mjr | 43:7a6364d82a41 | 179 | for (int i = skip ; i > 0 ; --i) |
mjr | 35:e959ffba78fd | 180 | { |
mjr | 43:7a6364d82a41 | 181 | *clockPSOR = clockMask; |
mjr | 43:7a6364d82a41 | 182 | *clockPCOR = clockMask; |
mjr | 35:e959ffba78fd | 183 | } |
mjr | 17:ab3cec0c8bf4 | 184 | } |
mjr | 17:ab3cec0c8bf4 | 185 | } |
mjr | 17:ab3cec0c8bf4 | 186 | |
mjr | 43:7a6364d82a41 | 187 | //$$$ |
mjr | 43:7a6364d82a41 | 188 | if (done==1) printf(". done: dst=%d\r\n", dst); |
mjr | 43:7a6364d82a41 | 189 | |
mjr | 17:ab3cec0c8bf4 | 190 | // clock out one extra pixel to leave A1 in the high-Z state |
mjr | 43:7a6364d82a41 | 191 | clock = 1; |
mjr | 43:7a6364d82a41 | 192 | clock = 0; |
mjr | 43:7a6364d82a41 | 193 | |
mjr | 43:7a6364d82a41 | 194 | if (n >= 80) { totalTime += t.read(); nRuns += 1; } // $$$ |
mjr | 17:ab3cec0c8bf4 | 195 | } |
mjr | 2:c174f9ee414a | 196 | |
mjr | 2:c174f9ee414a | 197 | // Clock through all pixels to clear the array. Pulses SI at the |
mjr | 2:c174f9ee414a | 198 | // beginning of the operation, which starts a new integration cycle. |
mjr | 2:c174f9ee414a | 199 | // The caller can thus immediately call read() to read the pixels |
mjr | 2:c174f9ee414a | 200 | // integrated while the clear() was taking place. |
mjr | 17:ab3cec0c8bf4 | 201 | void clear() |
mjr | 17:ab3cec0c8bf4 | 202 | { |
mjr | 35:e959ffba78fd | 203 | // get the clock pin pointers into local variables for fast access |
mjr | 35:e959ffba78fd | 204 | register FGPIO_Type *clockPort = this->clockPort; |
mjr | 35:e959ffba78fd | 205 | register uint32_t clockMask = this->clockMask; |
mjr | 35:e959ffba78fd | 206 | |
mjr | 17:ab3cec0c8bf4 | 207 | // clock in an SI pulse |
mjr | 17:ab3cec0c8bf4 | 208 | si = 1; |
mjr | 43:7a6364d82a41 | 209 | clockPort->PSOR = clockMask; |
mjr | 17:ab3cec0c8bf4 | 210 | si = 0; |
mjr | 43:7a6364d82a41 | 211 | clockPort->PCOR = clockMask; |
mjr | 17:ab3cec0c8bf4 | 212 | |
mjr | 40:cc0d9814522b | 213 | // if in serial mode, clock all pixels across both sensor halves; |
mjr | 40:cc0d9814522b | 214 | // in parallel mode, the pixels are clocked together |
mjr | 43:7a6364d82a41 | 215 | int n = parallel ? nPixSensor/2 : nPixSensor; |
mjr | 40:cc0d9814522b | 216 | |
mjr | 17:ab3cec0c8bf4 | 217 | // clock out all pixels |
mjr | 40:cc0d9814522b | 218 | for (int i = 0 ; i < n + 1 ; ++i) { |
mjr | 43:7a6364d82a41 | 219 | clock = 1; // $$$clockPort->PSOR = clockMask; |
mjr | 43:7a6364d82a41 | 220 | clock = 0; // $$$clockPort->PCOR = clockMask; |
mjr | 17:ab3cec0c8bf4 | 221 | } |
mjr | 17:ab3cec0c8bf4 | 222 | } |
mjr | 2:c174f9ee414a | 223 | |
mjr | 2:c174f9ee414a | 224 | private: |
mjr | 43:7a6364d82a41 | 225 | int nPixSensor; // number of pixels in physical sensor array |
mjr | 40:cc0d9814522b | 226 | DigitalOut si; // GPIO pin for sensor SI (serial data) |
mjr | 40:cc0d9814522b | 227 | DigitalOut clock; // GPIO pin for sensor SCLK (serial clock) |
mjr | 40:cc0d9814522b | 228 | FGPIO_Type *clockPort; // IOPORT base address for clock pin - cached for fast writes |
mjr | 35:e959ffba78fd | 229 | uint32_t clockMask; // IOPORT register bit mask for clock pin |
mjr | 43:7a6364d82a41 | 230 | AltAnalogIn ao1; // GPIO pin for sensor AO1 (analog output 1) - we read sensor data from this pin |
mjr | 43:7a6364d82a41 | 231 | AltAnalogIn ao2; // GPIO pin for sensor AO2 (analog output 2) - 2nd sensor data pin, when in parallel mode |
mjr | 40:cc0d9814522b | 232 | bool parallel; // true -> running in parallel mode (we read AO1 and AO2 separately on each clock) |
mjr | 2:c174f9ee414a | 233 | }; |
mjr | 2:c174f9ee414a | 234 | |
mjr | 2:c174f9ee414a | 235 | #endif /* TSL1410R_H */ |