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 Mike R

TSL1410R/tsl1410r.h

Committer:
mjr
Date:
2016-02-06
Revision:
43:7a6364d82a41
Parent:
40:cc0d9814522b

File content as of revision 43:7a6364d82a41:

/*
 *  TSL1410R interface class.
 *
 *  This provides a high-level interface for the Taos TSL1410R linear CCD array sensor.
 */
 
#include "mbed.h"
#include "config.h"
#include "AltAnalogIn.h"
 
#ifndef TSL1410R_H
#define TSL1410R_H

// For faster GPIO on the clock pin, we write the IOPORT registers directly.
// PORT_BASE gives us the memory mapped location of the IOPORT register set
// for a pin; PINMASK gives us the bit pattern to write to the registers.
//
// - To turn a pin ON:  PORT_BASE(pin)->PSOR |= PINMASK(pin)
// - To turn a pin OFF: PORT_BASE(pin)->PCOR |= PINMASK(pin)
// - To toggle a pin:   PORT_BASE(pin)->PTOR |= PINMASK(pin)
//
// When used in a loop where the port address and pin mask are cached in
// local variables, this runs at the same speed as the FastIO library - about 
// 78ns per pin write on the KL25Z.  Not surprising since it's doing the same
// thing, and the compiler should be able to reduce a pin write to a single ARM
// instruction when the port address and mask are in local register variables.
// The advantage over the FastIO library is that this approach allows for pins
// to be assigned dynamically at run-time, which we prefer because it allows for
// configuration changes to be made on the fly rather than having to recompile
// the program.
#define GPIO_PORT(pin)        (((unsigned int)(pin)) >> PORT_SHIFT)
#define GPIO_PORT_BASE(pin)   ((FGPIO_Type *)(FPTA_BASE + GPIO_PORT(pin) * 0x40))
#define GPIO_PINMASK(pin)     gpio_set(pin)
 
class TSL1410R
{
public:
    TSL1410R(int nPixSensor, PinName siPin, PinName clockPin, PinName ao1Pin, PinName ao2Pin) 
        : nPixSensor(nPixSensor), si(siPin), clock(clockPin), ao1(ao1Pin), ao2(ao2Pin)
    {
        // we're in parallel mode if ao2 is a valid pin
        parallel = (ao2Pin != NC);
        
        // remember the clock pin port base and pin mask for fast access
        clockPort = GPIO_PORT_BASE(clockPin);
        clockMask = GPIO_PINMASK(clockPin);
        
        // clear out power-on random data by clocking through all pixels twice
        clear();
        clear();
        
        totalTime = 0.0; nRuns = 0; // $$$
    }
    
    float totalTime; int nRuns; // $$$

    // Read the pixels.
    //
    // 'n' specifies the number of pixels to sample, and is the size of
    // the output array 'pix'.  This can be less than the full number
    // of pixels on the physical device; if it is, we'll spread the
    // sample evenly across the full length of the device by skipping
    // one or more pixels between each sampled pixel to pad out the
    // difference between the sample size and the physical CCD size.
    // For example, if the physical sensor has 1280 pixels, and 'n' is
    // 640, we'll read every other pixel and skip every other pixel.
    // If 'n' is 160, we'll read every 8th pixel and skip 7 between
    // each sample.
    // 
    // The reason that we provide this subset mode (where 'n' is less
    // than the physical pixel count) is that reading a pixel is the most
    // time-consuming part of the scan.  For each pixel we read, we have
    // to wait for the pixel's charge to transfer from its internal smapling
    // capacitor to the CCD's output pin, for that charge to transfer to
    // the KL25Z input pin, and for the KL25Z ADC to get a stable reading.
    // This all takes on the order of 20us per pixel.  Skipping a pixel
    // only requires a clock pulse, which takes about 350ns.  So we can
    // skip 60 pixels in the time it takes to sample 1 pixel.
    //
    // We clock an SI pulse at the beginning of the read.  This starts the
    // next integration cycle: the pixel array will reset on the SI, and 
    // the integration starts 18 clocks later.  So by the time this method
    // returns, the next sample will have been integrating for npix-18 clocks.  
    // That's usually enough time to allow immediately reading the next
    // sample.  If more integration time is required, the caller can simply
    // sleep/spin for the desired additional time, or can do other work that
    // takes the desired additional time.
    //
    // If the caller has other work to tend to that takes longer than the
    // desired maximum integration time, it can call clear() to clock out
    // the current pixels and start a fresh integration cycle.
    void read(register uint16_t *pix, int n)
    {
        Timer t; t.start(); // $$$
        
        // get the clock pin pointers into local variables for fast access
        register volatile uint32_t *clockPSOR = &clockPort->PSOR;
        register volatile uint32_t *clockPCOR = &clockPort->PCOR;
        register const uint32_t clockMask = this->clockMask;
        
        // start the next integration cycle by pulsing SI and one clock
        si = 1;
        clock = 1;
        si = 0;
        clock = 0;
        
        // figure how many pixels to skip on each read
        int skip = nPixSensor/n - 1;
        
///$$$
static int done=0;
if (done++ == 0) printf("nPixSensor=%d, n=%d, skip=%d, parallel=%d\r\n", nPixSensor, n, skip, parallel);

        // get the clock PSOR and PCOR register addresses for fast access

        // read all of the pixels
        int dst;
        if (parallel)
        {
            // Parallel mode - read pixels from each half sensor concurrently.
            // Divide 'n' (the output pixel count) by 2 to get the loop count,
            // since we're going to do 2 pixels on each iteration.
            for (n /= 2, dst = 0 ; dst < n ; ++dst)
            {
                // Take the clock high.  The TSL1410R will connect the next
                // pixel pair's hold capacitors to the A01 and AO2 lines 
                // (respectively) on the clock rising edge.
                *clockPSOR = clockMask;

                // Start the ADC sampler for AO1.  The TSL1410R sample 
                // stabilization time per the data sheet is 120ns.  This is
                // fast enough that we don't need an explicit delay, since
                // the instructions to execute this call will take longer
                // than that.
                ao1.start();
                
                // take the clock low while we're waiting for the reading
                *clockPCOR = clockMask;
                
                // Read the first half-sensor pixel from AO1
                pix[dst] = ao1.read_u16();
                
                // Read the second half-sensor pixel from AO2, and store it
                // in the destination array at the current index PLUS 'n',
                // which you will recall contains half the output pixel count.
                // This second pixel is halfway up the sensor from the first 
                // pixel, so it goes halfway up the output array from the
                // current output position.
                ao2.start();
                pix[dst + n] = ao2.read_u16();
                
                // Clock through the skipped pixels
                for (int i = skip ; i > 0 ; --i) 
                {
                    *clockPSOR = clockMask;
                    *clockPCOR = clockMask;
                }
            }
        }
        else
        {
            // serial mode - read all pixels in a single file
            for (dst = 0 ; dst < n ; ++dst)
            {
                // Clock the next pixel onto the sensor A0 line
                *clockPSOR = clockMask;
                
                // start the ADC sampler
                ao1.start();
                
                // take the clock low while we're waiting for the analog reading
                *clockPCOR = clockMask;
                
                // wait for and read the ADC sample; plug it into the output
                // array, and increment the output pointer to the next position
                pix[dst] = ao1.read_u16();
                
                // clock through the skipped pixels
                for (int i = skip ; i > 0 ; --i) 
                {
                    *clockPSOR = clockMask;
                    *clockPCOR = clockMask;
                }
            }
        }
        
//$$$
if (done==1) printf(". done: dst=%d\r\n", dst);
        
        // clock out one extra pixel to leave A1 in the high-Z state
        clock = 1;
        clock = 0;
        
        if (n >= 80) { totalTime += t.read(); nRuns += 1; } // $$$
    }

    // Clock through all pixels to clear the array.  Pulses SI at the
    // beginning of the operation, which starts a new integration cycle.
    // The caller can thus immediately call read() to read the pixels 
    // integrated while the clear() was taking place.
    void clear()
    {
        // get the clock pin pointers into local variables for fast access
        register FGPIO_Type *clockPort = this->clockPort;
        register uint32_t clockMask = this->clockMask;

        // clock in an SI pulse
        si = 1;
        clockPort->PSOR = clockMask;
        si = 0;
        clockPort->PCOR = clockMask;
        
        // if in serial mode, clock all pixels across both sensor halves;
        // in parallel mode, the pixels are clocked together
        int n = parallel ? nPixSensor/2 : nPixSensor;
        
        // clock out all pixels
        for (int i = 0 ; i < n + 1 ; ++i) {
            clock = 1; // $$$clockPort->PSOR = clockMask;
            clock = 0; // $$$clockPort->PCOR = clockMask;
        }
    }

private:
    int nPixSensor;           // number of pixels in physical sensor array
    DigitalOut si;            // GPIO pin for sensor SI (serial data) 
    DigitalOut clock;         // GPIO pin for sensor SCLK (serial clock)
    FGPIO_Type *clockPort;    // IOPORT base address for clock pin - cached for fast writes
    uint32_t clockMask;       // IOPORT register bit mask for clock pin
    AltAnalogIn ao1;          // GPIO pin for sensor AO1 (analog output 1) - we read sensor data from this pin
    AltAnalogIn ao2;          // GPIO pin for sensor AO2 (analog output 2) - 2nd sensor data pin, when in parallel mode
    bool parallel;            // true -> running in parallel mode (we read AO1 and AO2 separately on each clock)
};
 
#endif /* TSL1410R_H */