Mirror with some correction

Dependencies:   mbed FastIO FastPWM USBDevice

Revision:
101:755f44622abc
Parent:
100:1ff35c07217c
Child:
103:dec22cd65b2a
--- a/TCD1103/TCD1103.h	Thu Nov 28 23:18:23 2019 +0000
+++ b/TCD1103/TCD1103.h	Fri Nov 29 05:38:07 2019 +0000
@@ -153,8 +153,9 @@
         pix2 = pix1 + nPixSensor;
         
         // put the first DMA transfer into the first buffer (pix1)
+        tIntMin = 0;
         pixDMA = 0;
-        running = false;
+        clientOwnsStablePix = false;
 
         // start the sample timer with an arbitrary epoch of "now"
         t.start();
@@ -180,6 +181,9 @@
         // clear random power-up data by clocking through all pixels twice
         clear();
         clear();
+        
+        // start the first transfer
+        startTransfer();
     }
         
     // logic gate levels, based on whether or not the logic gate connections
@@ -188,13 +192,10 @@
     static const bool logicHigh = invertedLogicGates ? 0 : 1;
     
     // ready to read
-    bool ready() { return !running; }
+    bool ready() { return clientOwnsStablePix; }
     
-    // is the DMA busy?
-    bool dmaBusy() { return running; }
-
-    // wait for the current DMA cycle to finish
-    void wait() { while (running) ; }
+    // wait for the DMA subsystem to release a buffer to the client
+    void wait() { while (!clientOwnsStablePix) ; }
     
     // Get the stable pixel array.  This is the image array from the
     // previous capture.  It remains valid until the next startCapture()
@@ -225,64 +226,49 @@
         }
     }
     
-    // wait for pixels to become ready
-    void waitPix(uint8_t * &pix, uint32_t &t) 
+    // release the client's pixel buffer
+    void releasePix() { clientOwnsStablePix = false; }
+    
+    // figure the average scan time from the running totals
+    uint32_t getAvgScanTime() { return static_cast<uint32_t>(totalXferTime / nRuns);}
+
+    // Set the requested minimum integration time.  If this is less than the
+    // sensor's physical minimum time, the physical minimum applies.
+    virtual void setMinIntTime(uint32_t us)
     {
-        // wait for the current transfer to finish
-        wait();
+        tIntMin = us;
+    }
+    
+protected:
+    // clear the sensor pixels    
+    void clear() 
+    {
+        // send an SH/ICG pulse sequence to start an integration cycle
+        // (without initiating a DMA transfer, as we just want to discard
+        // the incoming samples for a "clear")
+        tInt = gen_SH_ICG_pulse(false);
         
-        // Return the pixel array that IS assigned to DMA, since this
-        // is the latest buffer filled.  This buffer is stable, even
-        // though it's assigned to DMA, because the last transfer is
-        // already finished and thus DMA is no longer accessing the
-        // buffer.
-        if (pixDMA)
-        {
-            // DMA owns pix2
-            pix = pix2;
-            t = t2;
-        }
-        else
-        {
-            // DMA owns pix1
-            pix = pix1;
-            t = t1;
-        }
-   }
-        
+        // wait for one full readout cycle, plus a little extra for padding
+        ::wait(nPixSensor*masterClockPeriod*2 + 4.0e-6f);
+    }
+    
     // Start an image capture from the sensor.  Waits the previous
     // capture to finish if it's still running, then starts a new one
     // and returns immediately.  The new capture proceeds autonomously 
     // via the DMA hardware, so the caller can continue with other 
     // processing during the capture.
-    void startCapture(uint32_t minIntTime_us = 0)
+    void startTransfer()
     {
-        IF_DIAG(uint32_t tDiag0 = mainLoopTimer.read_us();)
-        
-        // wait for the last current capture to finish
-        while (running) { }
-
-        // we're starting a new capture immediately        
-        running = true;
-
-        // collect timing diagnostics
-        IF_DIAG(mainLoopIterCheckpt[8] += uint32_t(mainLoopTimer.read_us() - tDiag0);)
-        
-        // If the elapsed time since the start of the last integration
-        // hasn't reached the specified minimum yet, wait.  This allows
-        // the caller to control the integration time to optimize the
-        // exposure level.
-        uint32_t dt = uint32_t(t.read_us() - tInt);
-        if (dt < minIntTime_us)
+        // if we own the stable buffer, swap buffers
+        if (!clientOwnsStablePix)
         {
-            // we haven't reached the required minimum yet - wait for the 
-            // remaining interval
-            wait_us(minIntTime_us - dt);
+            // swap buffers
+            pixDMA ^= 1;
+            
+            // release the prior DMA buffer to the client
+            clientOwnsStablePix = true;
         }
         
-        // swap to the other DMA buffer for reading the new pixel samples
-        pixDMA ^= 1;
-        
         // Set up the active pixel array as the destination buffer for 
         // the ADC DMA channel. 
         os_dma.destination(pixDMA ? pix2 : pix1, true);
@@ -308,29 +294,40 @@
 
         // Record the start time of the currently active integration period
         tInt = tNewInt;
-        
-        IF_DIAG(mainLoopIterCheckpt[9] += uint32_t(mainLoopTimer.read_us() - tDiag0);)
     }
     
-    // clear the sensor pixels    
-    void clear() 
+    // End of transfer notification.  This runs as an interrupt handler when
+    // the DMA transfer completes.
+    void transferDone()
     {
-        // make sure any DMA run is completed
-        wait();
+        // add this sample to the timing statistics (for diagnostics and
+        // performance measurement)
+        uint32_t now = t.read_us();
+        uint32_t dt = dtPixXfer = static_cast<uint32_t>(now - tXfer);
+        totalXferTime += dt;
+        nRuns += 1;
+        
+        // collect debug statistics
+        if (dt < minXferTime) minXferTime = dt;
+        if (dt > maxXferTime) maxXferTime = dt;
         
-        // send an SH/ICG pulse sequence to start an integration cycle
-        // (without initiating a DMA transfer, as we just want to discard
-        // the incoming samples for a "clear")
-        tInt = gen_SH_ICG_pulse(false);
-        
-        // wait for one full readout cycle, plus a little extra for padding
-        ::wait(nPixSensor*masterClockPeriod*2 + 4.0e-6f);
+        // check if there's still time left before we reach the minimum 
+        // requested integration period
+        uint32_t dtInt = now - tInt;
+        if (tIntMin > dtInt)
+        {
+            // wait for the remaining interval before starting the next
+            // integration
+            integrationTimeout.attach(this, &TCD1103::startTransfer, tInt - dtInt);
+        }
+        else
+        {
+            // we've already reached the minimum integration time - start
+            // the next transfer immediately
+            startTransfer();
+        }
     }
-    
-    // figure the average scan time from the running totals
-    uint32_t getAvgScanTime() { return static_cast<uint32_t>(totalXferTime / nRuns);}
 
-protected:
     // Generate an SH/ICG pulse.  This transfers the pixel data from the live
     // sensor photoreceptors into the sensor's internal shift register, clears
     // the live pixels, and starts a new integration cycle.
@@ -529,23 +526,6 @@
         return t_sh;
     }
 
-    // end of transfer notification
-    void transferDone()
-    {
-        // add this sample to the timing statistics (for diagnostics and
-        // performance measurement)
-        uint32_t dt = dtPixXfer = static_cast<uint32_t>(t.read_us() - tXfer);
-        totalXferTime += dt;
-        nRuns += 1;
-        
-        // collect debug statistics
-        if (dt < minXferTime) minXferTime = dt;
-        if (dt > maxXferTime) maxXferTime = dt;
-        
-        // the sampler is no long running
-        running = false;
-    }
-
     // master clock
     NewPwmOut fm;
     
@@ -587,9 +567,44 @@
     // data from the last transfer.
     uint8_t pixDMA;
     
-    // flag: sample is running
-    volatile bool running;
-
+    // Stable buffer ownership.  At any given time, the DMA subsystem owns
+    // the buffer specified by pixDMA.  The other buffer - the "stable" buffer,
+    // which contains the most recent completed frame, can be owned by EITHER
+    // the client or by the DMA subsystem.  Each time a DMA transfer completes,
+    // the DMA subsystem looks at the stable buffer owner flag to determine 
+    // what to do:
+    //
+    // - If the DMA subsystem owns the stable buffer, it swaps buffers.  This
+    //   makes the newly completed DMA buffer the new stable buffer, and makes
+    //   the old stable buffer the new DMA buffer.  At this time, the DMA 
+    //   subsystem also changes the stable buffer ownership to CLIENT.
+    //
+    // - If the CLIENT owns the stable buffer, the DMA subsystem can't swap
+    //   buffers, because the client is still using the stable buffer.  It
+    //   simply leaves things as they are.
+    //
+    // In either case, the DMA system starts a new transfer at this point.
+    //
+    // The client, meanwhile, is free to access the stable buffer when it has
+    // ownership.  If the client *doesn't* have ownership, it must wait for
+    // the ownership to be transferred, which can only be done by the DMA
+    // subsystem on completing a transfer.
+    //
+    // When the client is done with the stable buffer, it transfers ownership
+    // back to the DMA subsystem.
+    //
+    // Transfers of ownership from DMA to CLIENT are done only by DMA.
+    // Transfers from CLIENT to DMA are done only by CLIENT.  So whoever has
+    // ownership now is responsible for transferring ownership.
+    //
+    volatile bool clientOwnsStablePix;
+    
+    // Minimum requested integration time, in microseconds
+    uint32_t tIntMin;
+    
+    // Timeout for generating an interrupt at the end of the integration period
+    Timeout integrationTimeout;
+        
     // timing statistics
     Timer t;                  // sample timer
     uint32_t tInt;            // start time (us) of current integration period