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@35:e959ffba78fd, 2015-12-19 (annotated)
- Committer:
- mjr
- Date:
- Sat Dec 19 06:37:19 2015 +0000
- Revision:
- 35:e959ffba78fd
- Parent:
- 25:e22b88bd783a
- Child:
- 40:cc0d9814522b
Keyboard/Media Control interface working, but the extra interface confuses the DOF connector.
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 | 35:e959ffba78fd | 129 | // turn off the ADC until the next pixel is ready |
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 | 17:ab3cec0c8bf4 | 191 | // clock out all pixels |
mjr | 17:ab3cec0c8bf4 | 192 | for (int i = 0 ; i < nPix + 1 ; ++i) { |
mjr | 35:e959ffba78fd | 193 | clockPort->PSOR |= clockMask; |
mjr | 35:e959ffba78fd | 194 | clockPort->PCOR |= clockMask; |
mjr | 17:ab3cec0c8bf4 | 195 | } |
mjr | 17:ab3cec0c8bf4 | 196 | } |
mjr | 2:c174f9ee414a | 197 | |
mjr | 2:c174f9ee414a | 198 | private: |
mjr | 35:e959ffba78fd | 199 | int nPix; // number of pixels in physical sensor array |
mjr | 35:e959ffba78fd | 200 | DigitalOut si; |
mjr | 35:e959ffba78fd | 201 | DigitalOut clock; |
mjr | 35:e959ffba78fd | 202 | FGPIO_Type *clockPort; // IOPORT base address for clock pin, for fast writes |
mjr | 35:e959ffba78fd | 203 | uint32_t clockMask; // IOPORT register bit mask for clock pin |
mjr | 35:e959ffba78fd | 204 | FastAnalogIn ao1; |
mjr | 35:e959ffba78fd | 205 | FastAnalogIn ao2; // valid iff running in parallel mode |
mjr | 35:e959ffba78fd | 206 | bool parallel; // true -> running in parallel mode |
mjr | 2:c174f9ee414a | 207 | }; |
mjr | 2:c174f9ee414a | 208 | |
mjr | 2:c174f9ee414a | 209 | #endif /* TSL1410R_H */ |