Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: mbed FastIO FastPWM USBDevice
Fork of Pinscape_Controller by
TSL1410R/tsl1410r.h@40:cc0d9814522b, 2016-02-03 (annotated)
- Committer:
- mjr
- Date:
- Wed Feb 03 22:57:25 2016 +0000
- Revision:
- 40:cc0d9814522b
- Parent:
- 35:e959ffba78fd
- Child:
- 43:7a6364d82a41
Gamma correction option for outputs; work in progress on new config program
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 | 35:e959ffba78fd | 9 | #include "FastAnalogIn.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 | 35:e959ffba78fd | 31 | #define GPIO_PORT_BASE(pin) ((FGPIO_Type *)(FPTA_BASE + ((unsigned int)pin >> PORT_SHIFT) * 0x40)) | 
| mjr | 35:e959ffba78fd | 32 | #define GPIO_PINMASK(pin) (1 << ((pin & 0x7F) >> 2)) | 
| mjr | 2:c174f9ee414a | 33 | |
| mjr | 35:e959ffba78fd | 34 | class TSL1410R | 
| mjr | 2:c174f9ee414a | 35 | { | 
| mjr | 2:c174f9ee414a | 36 | public: | 
| mjr | 35:e959ffba78fd | 37 | TSL1410R(int nPix, PinName siPin, PinName clockPin, PinName ao1Pin, PinName ao2Pin) | 
| mjr | 35:e959ffba78fd | 38 | : nPix(nPix), si(siPin), clock(clockPin), ao1(ao1Pin), ao2(ao2Pin) | 
| mjr | 17:ab3cec0c8bf4 | 39 | { | 
| mjr | 35:e959ffba78fd | 40 | // we're in parallel mode if ao2 is a valid pin | 
| mjr | 35:e959ffba78fd | 41 | parallel = (ao2Pin != NC); | 
| mjr | 35:e959ffba78fd | 42 | |
| mjr | 35:e959ffba78fd | 43 | // remember the clock pin port base and pin mask for fast access | 
| mjr | 35:e959ffba78fd | 44 | clockPort = GPIO_PORT_BASE(clockPin); | 
| mjr | 35:e959ffba78fd | 45 | clockMask = GPIO_PINMASK(clockPin); | 
| mjr | 35:e959ffba78fd | 46 | |
| mjr | 17:ab3cec0c8bf4 | 47 | // disable continuous conversion mode in FastAnalogIn - since we're | 
| mjr | 17:ab3cec0c8bf4 | 48 | // reading discrete pixel values, we want to control when the samples | 
| mjr | 17:ab3cec0c8bf4 | 49 | // are taken rather than continuously averaging over time | 
| mjr | 35:e959ffba78fd | 50 | ao1.disable(); | 
| mjr | 35:e959ffba78fd | 51 | if (parallel) ao2.disable(); | 
| mjr | 17:ab3cec0c8bf4 | 52 | |
| mjr | 17:ab3cec0c8bf4 | 53 | // clear out power-on noise by clocking through all pixels twice | 
| mjr | 17:ab3cec0c8bf4 | 54 | clear(); | 
| mjr | 17:ab3cec0c8bf4 | 55 | clear(); | 
| mjr | 17:ab3cec0c8bf4 | 56 | } | 
| mjr | 2:c174f9ee414a | 57 | |
| mjr | 17:ab3cec0c8bf4 | 58 | // Read the pixels. | 
| mjr | 17:ab3cec0c8bf4 | 59 | // | 
| mjr | 17:ab3cec0c8bf4 | 60 | // 'n' specifies the number of pixels to sample, and is the size of | 
| mjr | 17:ab3cec0c8bf4 | 61 | // the output array 'pix'. This can be less than the full number | 
| mjr | 17:ab3cec0c8bf4 | 62 | // of pixels on the physical device; if it is, we'll spread the | 
| mjr | 17:ab3cec0c8bf4 | 63 | // sample evenly across the full length of the device by skipping | 
| mjr | 17:ab3cec0c8bf4 | 64 | // one or more pixels between each sampled pixel to pad out the | 
| mjr | 17:ab3cec0c8bf4 | 65 | // difference between the sample size and the physical CCD size. | 
| mjr | 17:ab3cec0c8bf4 | 66 | // For example, if the physical sensor has 1280 pixels, and 'n' is | 
| mjr | 17:ab3cec0c8bf4 | 67 | // 640, we'll read every other pixel and skip every other pixel. | 
| mjr | 17:ab3cec0c8bf4 | 68 | // If 'n' is 160, we'll read every 8th pixel and skip 7 between | 
| mjr | 17:ab3cec0c8bf4 | 69 | // each sample. | 
| mjr | 17:ab3cec0c8bf4 | 70 | // | 
| mjr | 17:ab3cec0c8bf4 | 71 | // The reason that we provide this subset mode (where 'n' is less | 
| mjr | 17:ab3cec0c8bf4 | 72 | // than the physical pixel count) is that reading a pixel is the most | 
| mjr | 17:ab3cec0c8bf4 | 73 | // time-consuming part of the scan. For each pixel we read, we have | 
| mjr | 17:ab3cec0c8bf4 | 74 | // to wait for the pixel's charge to transfer from its internal smapling | 
| mjr | 17:ab3cec0c8bf4 | 75 | // capacitor to the CCD's output pin, for that charge to transfer to | 
| mjr | 17:ab3cec0c8bf4 | 76 | // the KL25Z input pin, and for the KL25Z ADC to get a stable reading. | 
| mjr | 17:ab3cec0c8bf4 | 77 | // This all takes on the order of 20us per pixel. Skipping a pixel | 
| mjr | 17:ab3cec0c8bf4 | 78 | // only requires a clock pulse, which takes about 350ns. So we can | 
| mjr | 17:ab3cec0c8bf4 | 79 | // skip 60 pixels in the time it takes to sample 1 pixel. | 
| mjr | 2:c174f9ee414a | 80 | // | 
| mjr | 2:c174f9ee414a | 81 | // We clock an SI pulse at the beginning of the read. This starts the | 
| mjr | 2:c174f9ee414a | 82 | // next integration cycle: the pixel array will reset on the SI, and | 
| mjr | 17:ab3cec0c8bf4 | 83 | // the integration starts 18 clocks later. So by the time this method | 
| mjr | 17:ab3cec0c8bf4 | 84 | // returns, the next sample will have been integrating for npix-18 clocks. | 
| mjr | 17:ab3cec0c8bf4 | 85 | // That's usually enough time to allow immediately reading the next | 
| mjr | 17:ab3cec0c8bf4 | 86 | // sample. If more integration time is required, the caller can simply | 
| mjr | 2:c174f9ee414a | 87 | // sleep/spin for the desired additional time, or can do other work that | 
| mjr | 17:ab3cec0c8bf4 | 88 | // takes the desired additional time. | 
| mjr | 2:c174f9ee414a | 89 | // | 
| mjr | 2:c174f9ee414a | 90 | // If the caller has other work to tend to that takes longer than the | 
| mjr | 2:c174f9ee414a | 91 | // desired maximum integration time, it can call clear() to clock out | 
| mjr | 2:c174f9ee414a | 92 | // the current pixels and start a fresh integration cycle. | 
| mjr | 18:5e890ebd0023 | 93 | void read(uint16_t *pix, int n) | 
| mjr | 17:ab3cec0c8bf4 | 94 | { | 
| mjr | 35:e959ffba78fd | 95 | // get the clock pin pointers into local variables for fast access | 
| mjr | 35:e959ffba78fd | 96 | register FGPIO_Type *clockPort = this->clockPort; | 
| mjr | 35:e959ffba78fd | 97 | register uint32_t clockMask = this->clockMask; | 
| mjr | 35:e959ffba78fd | 98 | |
| mjr | 17:ab3cec0c8bf4 | 99 | // start the next integration cycle by pulsing SI and one clock | 
| mjr | 17:ab3cec0c8bf4 | 100 | si = 1; | 
| mjr | 35:e959ffba78fd | 101 | clockPort->PSOR |= clockMask; // turn the clock pin on (clock = 1) | 
| mjr | 17:ab3cec0c8bf4 | 102 | si = 0; | 
| mjr | 35:e959ffba78fd | 103 | clockPort->PCOR |= clockMask; // turn the clock pin off (clock = 0) | 
| mjr | 17:ab3cec0c8bf4 | 104 | |
| mjr | 17:ab3cec0c8bf4 | 105 | // figure how many pixels to skip on each read | 
| mjr | 17:ab3cec0c8bf4 | 106 | int skip = nPix/n - 1; | 
| mjr | 17:ab3cec0c8bf4 | 107 | |
| mjr | 17:ab3cec0c8bf4 | 108 | // read all of the pixels | 
| mjr | 35:e959ffba78fd | 109 | if (parallel) | 
| mjr | 17:ab3cec0c8bf4 | 110 | { | 
| mjr | 35:e959ffba78fd | 111 | // parallel mode - read pixels from each half sensor concurrently | 
| mjr | 35:e959ffba78fd | 112 | int nPixHalf = nPix/2; | 
| mjr | 35:e959ffba78fd | 113 | for (int src = 0, dst = 0 ; src < nPixHalf ; ++src) | 
| mjr | 17:ab3cec0c8bf4 | 114 | { | 
| mjr | 35:e959ffba78fd | 115 | // pulse the clock and start the ADC sampling | 
| mjr | 35:e959ffba78fd | 116 | clockPort->PSOR |= clockMask; | 
| mjr | 35:e959ffba78fd | 117 | ao1.enable(); | 
| mjr | 35:e959ffba78fd | 118 | ao2.enable(); | 
| mjr | 35:e959ffba78fd | 119 | wait_us(1); | 
| mjr | 35:e959ffba78fd | 120 | clockPort->PCOR |= clockMask; | 
| mjr | 35:e959ffba78fd | 121 | |
| mjr | 35:e959ffba78fd | 122 | // wait for the ADCs to stabilize | 
| mjr | 35:e959ffba78fd | 123 | wait_us(11); | 
| mjr | 35:e959ffba78fd | 124 | |
| mjr | 35:e959ffba78fd | 125 | // read the pixels | 
| mjr | 35:e959ffba78fd | 126 | pix[dst] = ao1.read_u16(); | 
| mjr | 35:e959ffba78fd | 127 | pix[dst+n/2] = ao2.read_u16(); | 
| mjr | 35:e959ffba78fd | 128 | |
| mjr | 40:cc0d9814522b | 129 | // turn off the ADC until the next pixel is clocked out | 
| mjr | 35:e959ffba78fd | 130 | ao1.disable(); | 
| mjr | 35:e959ffba78fd | 131 | ao2.disable(); | 
| mjr | 35:e959ffba78fd | 132 | |
| mjr | 35:e959ffba78fd | 133 | // clock skipped pixels | 
| mjr | 35:e959ffba78fd | 134 | for (int i = 0 ; i < skip ; ++i, ++src) | 
| mjr | 35:e959ffba78fd | 135 | { | 
| mjr | 35:e959ffba78fd | 136 | clockPort->PSOR |= clockMask; | 
| mjr | 35:e959ffba78fd | 137 | clockPort->PCOR |= clockMask; | 
| mjr | 35:e959ffba78fd | 138 | } | 
| mjr | 35:e959ffba78fd | 139 | } | 
| mjr | 35:e959ffba78fd | 140 | } | 
| mjr | 35:e959ffba78fd | 141 | else | 
| mjr | 35:e959ffba78fd | 142 | { | 
| mjr | 35:e959ffba78fd | 143 | // serial mode - read all pixels in a single file | 
| mjr | 35:e959ffba78fd | 144 | for (int src = 0, dst = 0 ; src < nPix ; ++src) | 
| mjr | 35:e959ffba78fd | 145 | { | 
| mjr | 35:e959ffba78fd | 146 | // pulse the clock and start the ADC sampling | 
| mjr | 35:e959ffba78fd | 147 | clockPort->PSOR |= clockMask; | 
| mjr | 35:e959ffba78fd | 148 | ao1.enable(); | 
| mjr | 35:e959ffba78fd | 149 | wait_us(1); | 
| mjr | 35:e959ffba78fd | 150 | clockPort->PCOR |= clockMask; | 
| mjr | 35:e959ffba78fd | 151 | |
| mjr | 35:e959ffba78fd | 152 | // wait for the ADC sample to stabilize | 
| mjr | 35:e959ffba78fd | 153 | wait_us(11); | 
| mjr | 35:e959ffba78fd | 154 | |
| mjr | 35:e959ffba78fd | 155 | // read the ADC sample | 
| mjr | 35:e959ffba78fd | 156 | pix[dst++] = ao1.read_u16(); | 
| mjr | 35:e959ffba78fd | 157 | |
| mjr | 35:e959ffba78fd | 158 | // turn off the ADC until the next pixel is ready | 
| mjr | 35:e959ffba78fd | 159 | ao1.disable(); | 
| mjr | 35:e959ffba78fd | 160 | |
| mjr | 35:e959ffba78fd | 161 | // clock skipped pixels | 
| mjr | 35:e959ffba78fd | 162 | for (int i = 0 ; i < skip ; ++i, ++src) | 
| mjr | 35:e959ffba78fd | 163 | { | 
| mjr | 35:e959ffba78fd | 164 | clockPort->PSOR |= clockMask; | 
| mjr | 35:e959ffba78fd | 165 | clockPort->PCOR |= clockMask; | 
| mjr | 35:e959ffba78fd | 166 | } | 
| mjr | 17:ab3cec0c8bf4 | 167 | } | 
| mjr | 17:ab3cec0c8bf4 | 168 | } | 
| mjr | 17:ab3cec0c8bf4 | 169 | |
| mjr | 17:ab3cec0c8bf4 | 170 | // clock out one extra pixel to leave A1 in the high-Z state | 
| mjr | 35:e959ffba78fd | 171 | clockPort->PSOR |= clockMask; | 
| mjr | 35:e959ffba78fd | 172 | clockPort->PCOR |= clockMask; | 
| mjr | 17:ab3cec0c8bf4 | 173 | } | 
| mjr | 2:c174f9ee414a | 174 | |
| mjr | 2:c174f9ee414a | 175 | // Clock through all pixels to clear the array. Pulses SI at the | 
| mjr | 2:c174f9ee414a | 176 | // beginning of the operation, which starts a new integration cycle. | 
| mjr | 2:c174f9ee414a | 177 | // The caller can thus immediately call read() to read the pixels | 
| mjr | 2:c174f9ee414a | 178 | // integrated while the clear() was taking place. | 
| mjr | 17:ab3cec0c8bf4 | 179 | void clear() | 
| mjr | 17:ab3cec0c8bf4 | 180 | { | 
| mjr | 35:e959ffba78fd | 181 | // get the clock pin pointers into local variables for fast access | 
| mjr | 35:e959ffba78fd | 182 | register FGPIO_Type *clockPort = this->clockPort; | 
| mjr | 35:e959ffba78fd | 183 | register uint32_t clockMask = this->clockMask; | 
| mjr | 35:e959ffba78fd | 184 | |
| mjr | 17:ab3cec0c8bf4 | 185 | // clock in an SI pulse | 
| mjr | 17:ab3cec0c8bf4 | 186 | si = 1; | 
| mjr | 35:e959ffba78fd | 187 | clockPort->PSOR |= clockMask; | 
| mjr | 17:ab3cec0c8bf4 | 188 | si = 0; | 
| mjr | 35:e959ffba78fd | 189 | clockPort->PCOR |= clockMask; | 
| mjr | 17:ab3cec0c8bf4 | 190 | |
| mjr | 40:cc0d9814522b | 191 | // if in serial mode, clock all pixels across both sensor halves; | 
| mjr | 40:cc0d9814522b | 192 | // in parallel mode, the pixels are clocked together | 
| mjr | 40:cc0d9814522b | 193 | int n = parallel ? nPix/2 : nPix; | 
| mjr | 40:cc0d9814522b | 194 | |
| mjr | 17:ab3cec0c8bf4 | 195 | // clock out all pixels | 
| mjr | 40:cc0d9814522b | 196 | for (int i = 0 ; i < n + 1 ; ++i) { | 
| mjr | 35:e959ffba78fd | 197 | clockPort->PSOR |= clockMask; | 
| mjr | 35:e959ffba78fd | 198 | clockPort->PCOR |= clockMask; | 
| mjr | 17:ab3cec0c8bf4 | 199 | } | 
| mjr | 17:ab3cec0c8bf4 | 200 | } | 
| mjr | 2:c174f9ee414a | 201 | |
| mjr | 2:c174f9ee414a | 202 | private: | 
| mjr | 35:e959ffba78fd | 203 | int nPix; // number of pixels in physical sensor array | 
| mjr | 40:cc0d9814522b | 204 | DigitalOut si; // GPIO pin for sensor SI (serial data) | 
| mjr | 40:cc0d9814522b | 205 | DigitalOut clock; // GPIO pin for sensor SCLK (serial clock) | 
| mjr | 40:cc0d9814522b | 206 | FGPIO_Type *clockPort; // IOPORT base address for clock pin - cached for fast writes | 
| mjr | 35:e959ffba78fd | 207 | uint32_t clockMask; // IOPORT register bit mask for clock pin | 
| mjr | 40:cc0d9814522b | 208 | FastAnalogIn ao1; // GPIO pin for sensor AO1 (analog output 1) - we read sensor data from this pin | 
| mjr | 40:cc0d9814522b | 209 | FastAnalogIn ao2; // GPIO pin for sensor AO2 (analog output 2) - 2nd sensor data pin, when in parallel mode | 
| mjr | 40:cc0d9814522b | 210 | bool parallel; // true -> running in parallel mode (we read AO1 and AO2 separately on each clock) | 
| mjr | 2:c174f9ee414a | 211 | }; | 
| mjr | 2:c174f9ee414a | 212 | |
| mjr | 2:c174f9ee414a | 213 | #endif /* TSL1410R_H */ | 
