Mike R / Mbed 2 deprecated Pinscape_Controller_V2

Dependencies:   mbed FastIO FastPWM USBDevice

Fork of Pinscape_Controller by Mike R

Revision:
45:c42166b2878c
Parent:
43:7a6364d82a41
Child:
47:df7a88cd249c
--- a/TSL1410R/tsl1410r.h	Sun Feb 07 03:07:11 2016 +0000
+++ b/TSL1410R/tsl1410r.h	Mon Feb 15 20:30:32 2016 +0000
@@ -1,3 +1,45 @@
+// DMA VERSION - NOT WORKING
+
+// I'm saving this code for now, since it was somewhat promising but doesn't
+// quite work.  The idea here was to read the ADC via DMA, operating the ADC
+// in continuous mode.  This speeds things up pretty impressively (by about 
+// a factor of 3 vs having the MCU read each result from the ADC sampling 
+// register), but I can't figure out how to get a stable enough signal out of 
+// it.  I think the problem is that the timing isn't precise enough in detecting 
+// when the DMA completes each write.  We have to clock the next pixel onto the 
+// CCD output each time we complete a sample, and we have to do so quickly so 
+// that the next pixel charge is stable at the ADC input pin by the time the 
+// ADC sample interval starts.  I'm seeing a ton of noise, which I think means 
+// that the new pixel isn't ready for the ADC in time. 
+//
+// I've tried a number of approaches, none of which works:
+//
+// - Skip every other sample, so that we can spend one whole sample just 
+// clocking in the next pixel.  We discard the "odds" samples that are taken
+// during pixel changes, and use only the "even" samples where the pixel is
+// stable the entire time.  I'd think the extra sample would give us plenty
+// of time to stabilize the next pixel, but it doesn't seem to work out that
+// way.  I think the problem might be that the latency of the MCU responding
+// to each sample completion is long enough relative to the sampling interval
+// that we can't reliably respond to the ADC done condition fast enough.  I've
+// tried basing the sample completion detection on the DMA byte counter and
+// the ADC interrupt.  The DMA byte counter is updated after the DMA transfer
+// is done, so that's probably just too late in the cycle.  The ADC interrupt
+// should be concurrent with the DMA transfer starting, but in practice it 
+// still doesn't give us good results.
+//
+// - Use DMA, but with the ADC in single-sample mode.  This bypasses the latency
+// problem by ensuring that the ADC doesn't start a new sample until we've
+// definitely finished clocking in the next pixel.  But it defeats the whole
+// purpose by eliminating the speed improvement - the speeds are comparable to
+// doing the transfers via the MCU.  This surprises me because I'd have expected
+// that the DMA would run concurrently with the MCU pixel clocking code, but
+// maybe there's enough bus contention between the MCU and DMA in this case that
+// there's no true overlapping of the operation.  Or maybe the interrupt dispatch
+// adds enough overhead to negate any overlapping.  I haven't actually been able
+// to get good data out of this mode, either, but I gave up early because of the
+// lack of any speed improvement.
+
 /*
  *  TSL1410R interface class.
  *
@@ -7,9 +49,11 @@
 #include "mbed.h"
 #include "config.h"
 #include "AltAnalogIn.h"
+#include "SimpleDMA.h"
  
 #ifndef TSL1410R_H
 #define TSL1410R_H
+#define TSL1410R_DMA
 
 // 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
@@ -49,11 +93,29 @@
         clear();
         clear();
         
+        // set up our DMA channel for reading from our analog in pin
+        ao1.initDMA(&adc_dma);
+        
+        // Set up our DMA channel for writing the sensor SCLK - we use the PTOR
+        // (toggle) register to flip the bit on each write.  To pad the timing
+        // to the rate required by the CCD, do a no-op 0 write to PTOR after
+        // each toggle.  This gives us a 16-byte buffer, which we can make
+        // circular in the DMA controller.
+        static const uint32_t clkseq[] = { clockMask, 0, clockMask, 0 };
+        clk_dma.destination(&clockPort->PTOR, false, 32);
+        clk_dma.source(clkseq, true, 32, 16);   // set up our circular source buffer
+        clk_dma.trigger(Trigger_ADC0);          // software trigger
+        clk_dma.setCycleSteal(false);           // do the entire transfer on each trigger
+        
         totalTime = 0.0; nRuns = 0; // $$$
     }
     
     float totalTime; int nRuns; // $$$
 
+    // ADC interrupt handler - on each ADC event, 
+    static TSL1410R *instance;
+    static void _aiIRQ() { }
+
     // Read the pixels.
     //
     // 'n' specifies the number of pixels to sample, and is the size of
@@ -91,11 +153,10 @@
     // the current pixels and start a fresh integration cycle.
     void read(register uint16_t *pix, int n)
     {
-        Timer t; t.start(); // $$$
+        Timer t; t.start(); //float tDMA, tPix; // $$$
         
         // 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 volatile uint32_t *clockPTOR = &clockPort->PTOR;
         register const uint32_t clockMask = this->clockMask;
         
         // start the next integration cycle by pulsing SI and one clock
@@ -111,8 +172,6 @@
 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)
@@ -125,7 +184,7 @@
                 // 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;
+                *clockPTOR = clockMask;
 
                 // Start the ADC sampler for AO1.  The TSL1410R sample 
                 // stabilization time per the data sheet is 120ns.  This is
@@ -135,7 +194,7 @@
                 ao1.start();
                 
                 // take the clock low while we're waiting for the reading
-                *clockPCOR = clockMask;
+                *clockPTOR = clockMask;
                 
                 // Read the first half-sensor pixel from AO1
                 pix[dst] = ao1.read_u16();
@@ -152,46 +211,61 @@
                 // Clock through the skipped pixels
                 for (int i = skip ; i > 0 ; --i) 
                 {
-                    *clockPSOR = clockMask;
-                    *clockPCOR = clockMask;
+                    *clockPTOR = clockMask;
+                    *clockPTOR = clockMask;
+                    *clockPTOR = 0;         // pad the timing with an extra nop write
                 }
             }
         }
         else
         {
             // serial mode - read all pixels in a single file
-            for (dst = 0 ; dst < n ; ++dst)
+
+            // clock in the first pixel
+            clock = 1;
+            clock = 0;
+            
+            // start the ADC DMA transfer
+            ao1.startDMA(pix, n, true);
+            
+            // We do 4 clock PTOR writes per clocked pixel (the skipped pixels 
+            // plus the pixel we actually want to sample), at 32 bits (4 bytes) 
+            // each, giving 16 bytes per pixel for the overall write.
+            int clk_dma_len = (skip+1)*16;
+            clk_dma.start(clk_dma_len);
+            
+            // start the first sample
+            ao1.start();
+            
+            // read all pixels
+            for (dst = n*2 ; dst > 0 ; dst -= 2)
             {
-                // Clock the next pixel onto the sensor A0 line
-                *clockPSOR = clockMask;
+                // wait for the current ADC sample to finish
+                while (adc_dma.remaining() >= dst) { }
                 
-                // start the ADC sampler
+                // start the next analog read while we're finishing the DMA transfers
                 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;
-                }
+                // re-arm the clock DMA
+                //clk_dma.restart(clk_dma_len);
             }
+            
+            // wait for the DMA transfer to finish
+            while (adc_dma.isBusy()) { }
+            
+            // apply the 12-bit to 16-bit rescaling to all values
+            for (int i = 0 ; i < n ; ++i)
+                pix[i] <<= 4;
         }
         
 //$$$
 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;
+        *clockPTOR = clockMask;
+        *clockPTOR = clockMask;
         
-        if (n >= 80) { totalTime += t.read(); nRuns += 1; } // $$$
+        if (n >= 64) { totalTime += t.read(); nRuns += 1; } // $$$
     }
 
     // Clock through all pixels to clear the array.  Pulses SI at the
@@ -222,6 +296,9 @@
     }
 
 private:
+    SimpleDMA adc_dma;        // DMA controller for reading the analog input
+    SimpleDMA clk_dma;        // DMA controller for the sensor SCLK (writes the PTOR register to toggle the clock bit)
+    char *dmabuf;             // buffer for DMA transfers
     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)
@@ -233,3 +310,4 @@
 };
  
 #endif /* TSL1410R_H */
+