DSP program for the Surfboard hardware (PCB to be open sourced) http://www.avbotz.com/ourauv/electrical/signal-processing/

Dependencies:   MODDMA SimpleIOMacros mbed-dsp mbed

Files at this revision

API Documentation at this revision

Comitter:
avbotz
Date:
Fri Aug 02 02:23:36 2013 +0000
Child:
1:f69ec4c889ff
Commit message:
Initial commit. Not working. MODDMA makes DMA setup slow, and the mbed SPI peripheral corrupts data sometimes. I will write my own SPI and DMA classes and see how it goes.

Changed in this revision

MODDMA.lib Show annotated file Show diff for this revision Revisions of this file
MODDMA_cache.cpp Show annotated file Show diff for this revision Revisions of this file
MODDMA_cache.h Show annotated file Show diff for this revision Revisions of this file
SimpleIOMacros.lib Show annotated file Show diff for this revision Revisions of this file
dma.cpp Show annotated file Show diff for this revision Revisions of this file
dma.h Show annotated file Show diff for this revision Revisions of this file
hydro_dsp.cpp Show annotated file Show diff for this revision Revisions of this file
hydro_dsp.h Show annotated file Show diff for this revision Revisions of this file
mbed-dsp.lib Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
surfboard.cpp Show annotated file Show diff for this revision Revisions of this file
surfboard.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MODDMA.lib	Fri Aug 02 02:23:36 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/AjK/code/MODDMA/#2aa6ffab5522
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MODDMA_cache.cpp	Fri Aug 02 02:23:36 2013 +0000
@@ -0,0 +1,606 @@
+#include "MODDMA_cache.h"
+
+// Exists so the IRQ handler can access MODDMA object.
+MODDMA_Cache* moddma_p;
+
+// Setup the DMA controller and then cache some values. they will be used in Reset().
+// does not work; these values do not change. we aren't caching the right ones.
+
+MODDMA_Cache::MODDMA_Cache()
+{
+    moddma_p = this;
+    init();
+}
+
+// A copy of Kirkham's setup function. This caches most values as they are set. 
+uint32_t MODDMA_Cache::Setup(MODDMA_Config* config)
+{
+    LPC_GPDMACH_TypeDef *pChannel = (LPC_GPDMACH_TypeDef *)Channel_p( config->channelNum() );
+    
+    setups[config->channelNum() & 0x7] = config;
+    
+    // Reset the Interrupt status
+    LPC_GPDMA->DMACIntTCClear = DMACIntTCClear = IntTCClear_Ch( config->channelNum() );
+    LPC_GPDMA->DMACIntErrClr  = DMACIntErrClr  = IntErrClr_Ch ( config->channelNum() );
+
+    // Clear DMA configure
+    pChannel->DMACCControl = 0x00;
+    pChannel->DMACCConfig  = 0x00;
+
+    // Assign Linker List Item value 
+    pChannel->DMACCLLI = DMACCLLI = config->dmaLLI();
+
+    // Set value to Channel Control Registers 
+    switch (config->transferType()) {
+    
+        // Memory to memory
+        case m2m:
+            // Assign physical source and destination address
+            pChannel->DMACCSrcAddr  = config->srcMemAddr();
+            pChannel->DMACCDestAddr = config->dstMemAddr();
+            pChannel->DMACCControl
+                = CxControl_TransferSize(config->transferSize()) 
+                | CxControl_SBSize(_32) 
+                | CxControl_DBSize(_32) 
+                | CxControl_SWidth(config->transferWidth()) 
+                | CxControl_DWidth(config->transferWidth()) 
+                | CxControl_SI() 
+                | CxControl_DI() 
+                | CxControl_I();
+            break;
+        
+        // Memory to peripheral
+        case m2p:
+            // Assign physical source
+            pChannel->DMACCSrcAddr = DMACCSrcAddr = config->srcMemAddr();
+            // Assign peripheral destination address
+            pChannel->DMACCDestAddr = DMACCDestAddr = (uint32_t)LUTPerAddr(config->dstConn());
+            pChannel->DMACCControl = DMACCControl
+                = CxControl_TransferSize((uint32_t)config->transferSize()) 
+                | CxControl_SBSize((uint32_t)LUTPerBurst(config->dstConn())) 
+                | CxControl_DBSize((uint32_t)LUTPerBurst(config->dstConn())) 
+                | CxControl_SWidth((uint32_t)LUTPerWid(config->dstConn())) 
+                | CxControl_DWidth((uint32_t)LUTPerWid(config->dstConn())) 
+                | CxControl_SI() 
+                | CxControl_I();
+            break;
+            
+        // Peripheral to memory
+        case p2m:
+            // Assign peripheral source address
+            pChannel->DMACCSrcAddr = DMACCSrcAddr = (uint32_t)LUTPerAddr(config->srcConn());
+            // Assign memory destination address
+            pChannel->DMACCDestAddr = DMACCDestAddr = config->dstMemAddr();
+            pChannel->DMACCControl = DMACCControl
+                = CxControl_TransferSize((uint32_t)config->transferSize()) 
+                | CxControl_SBSize((uint32_t)LUTPerBurst(config->srcConn())) 
+                | CxControl_DBSize((uint32_t)LUTPerBurst(config->srcConn())) 
+                | CxControl_SWidth((uint32_t)LUTPerWid(config->srcConn())) 
+                | CxControl_DWidth((uint32_t)LUTPerWid(config->srcConn())) 
+                | CxControl_DI() 
+                | CxControl_I();
+            break;
+            
+        // Peripheral to peripheral
+        case p2p:
+            // Assign peripheral source address
+            pChannel->DMACCSrcAddr = (uint32_t)LUTPerAddr(config->srcConn());
+            // Assign peripheral destination address
+            pChannel->DMACCDestAddr = (uint32_t)LUTPerAddr(config->dstConn());
+            pChannel->DMACCControl
+                = CxControl_TransferSize((uint32_t)config->transferSize()) 
+                | CxControl_SBSize((uint32_t)LUTPerBurst(config->srcConn())) 
+                | CxControl_DBSize((uint32_t)LUTPerBurst(config->dstConn())) 
+                | CxControl_SWidth((uint32_t)LUTPerWid(config->srcConn())) 
+                | CxControl_DWidth((uint32_t)LUTPerWid(config->dstConn())) 
+                | CxControl_I();
+            break;
+            
+        // GPIO to memory
+        case g2m:
+            // Assign GPIO source address
+            pChannel->DMACCSrcAddr = config->srcMemAddr();
+            // Assign memory destination address
+            pChannel->DMACCDestAddr = config->dstMemAddr();
+            pChannel->DMACCControl
+                = CxControl_TransferSize((uint32_t)config->transferSize()) 
+                | CxControl_SBSize((uint32_t)LUTPerBurst(config->srcConn())) 
+                | CxControl_DBSize((uint32_t)LUTPerBurst(config->srcConn())) 
+                | CxControl_SWidth((uint32_t)LUTPerWid(config->srcConn())) 
+                | CxControl_DWidth((uint32_t)LUTPerWid(config->srcConn())) 
+                | CxControl_DI() 
+                | CxControl_I();
+            break;
+            
+        // Memory to GPIO
+        case m2g:
+            // Assign physical source
+            pChannel->DMACCSrcAddr = config->srcMemAddr();
+            // Assign peripheral destination address
+            pChannel->DMACCDestAddr = config->dstMemAddr();
+            pChannel->DMACCControl
+                = CxControl_TransferSize((uint32_t)config->transferSize()) 
+                | CxControl_SBSize((uint32_t)LUTPerBurst(config->dstConn())) 
+                | CxControl_DBSize((uint32_t)LUTPerBurst(config->dstConn())) 
+                | CxControl_SWidth((uint32_t)LUTPerWid(config->dstConn())) 
+                | CxControl_DWidth((uint32_t)LUTPerWid(config->dstConn())) 
+                | CxControl_SI() 
+                | CxControl_I();
+            break;
+            
+        // Do not support any more transfer type, return ERROR
+        default:
+            return 0;
+    }
+
+     // Re-Configure DMA Request Select for source peripheral 
+    if (config->srcConn() > 15) {
+        DMAREQSEL = LPC_SC->DMAREQSEL |= (1 << (config->srcConn() - 16));
+    } 
+    else {
+        DMAREQSEL = LPC_SC->DMAREQSEL &= ~(1 << (config->srcConn() - 8));
+    }
+
+    // Re-Configure DMA Request Select for destination peripheral
+    if (config->dstConn() > 15) {
+        DMAREQSEL = LPC_SC->DMAREQSEL |= (1 << (config->dstConn() - 16));
+    } 
+    else {
+        DMAREQSEL = LPC_SC->DMAREQSEL &= ~(1 << (config->dstConn() - 8));
+    }
+
+    // Enable DMA channels, little endian 
+    LPC_GPDMA->DMACConfig = _E;
+    while (!(LPC_GPDMA->DMACConfig & _E));
+
+    // Calculate absolute value for Connection number
+    uint32_t tmp1 = config->srcConn(); tmp1 = ((tmp1 > 15) ? (tmp1 - 8) : tmp1);
+    uint32_t tmp2 = config->dstConn(); tmp2 = ((tmp2 > 15) ? (tmp2 - 8) : tmp2);
+
+    if (config->dmacSync()) {
+        uint32_t tmp3 = config->dmacSync(); tmp3 = ((tmp3 > 15) ? (tmp3 - 8) : tmp3);
+        LPC_GPDMA->DMACSync |= DMACSync = Sync_Src( tmp3 );
+    }
+    
+    uint32_t tfer_type = (uint32_t)config->transferType();
+    if (tfer_type == g2m || tfer_type == m2g) {
+        tfer_type -= 2; // Adjust psuedo transferType to a real transferType.
+    }
+    
+    // Configure DMA Channel, enable Error Counter and Terminate counter
+    pChannel->DMACCConfig = DMACCConfig
+        = CxConfig_IE() 
+        | CxConfig_ITC() 
+        | CxConfig_TransferType(tfer_type) 
+        | CxConfig_SrcPeripheral(tmp1) 
+        | CxConfig_DestPeripheral(tmp2);
+
+    return pChannel->DMACCControl;
+}
+/*
+uint32_t MODDMA_Cache::Setup(MODDMA_Config* config)
+{
+    moddma_p = this;
+    
+    //uint32_t ret = ((MODDMA*)this)->Setup(config);
+    uint32_t ret = MODDMA::Setup(config);
+    LPC_GPDMACH_TypeDef *pChannel = (LPC_GPDMACH_TypeDef*)Channel_p(config->channelNum());
+    
+    DMACCSrcAddr = pChannel->DMACCSrcAddr;
+    DMACCDestAddr = pChannel->DMACCDestAddr;
+    DMACCLLI = pChannel->DMACCLLI;
+    //DMACCControl = pChannel->DMACCControl;
+    
+    switch (config->transferType())
+    {
+    case p2m:
+        DMACCControl
+            = CxControl_TransferSize((uint32_t)config->transferSize()) 
+            | CxControl_SBSize((uint32_t)LUTPerBurst(config->srcConn())) 
+            | CxControl_DBSize((uint32_t)LUTPerBurst(config->srcConn())) 
+            | CxControl_SWidth((uint32_t)LUTPerWid(config->srcConn())) 
+            | CxControl_DWidth((uint32_t)LUTPerWid(config->srcConn())) 
+            | CxControl_DI() 
+            | CxControl_I();
+        break;
+        
+    case m2p:
+        DMACCControl
+            = CxControl_TransferSize((uint32_t)config->transferSize()) 
+            | CxControl_SBSize((uint32_t)LUTPerBurst(config->dstConn())) 
+            | CxControl_DBSize((uint32_t)LUTPerBurst(config->dstConn())) 
+            | CxControl_SWidth((uint32_t)LUTPerWid(config->dstConn())) 
+            | CxControl_DWidth((uint32_t)LUTPerWid(config->dstConn())) 
+            | CxControl_SI() 
+            | CxControl_I();
+        break;
+        
+    default:
+        return 5000; // oh no, your mbed blew up!
+    }
+    
+    uint32_t tmp1 = config->srcConn(); tmp1 = ((tmp1 > 15) ? (tmp1 - 8) : tmp1);
+    uint32_t tmp2 = config->dstConn(); tmp2 = ((tmp2 > 15) ? (tmp2 - 8) : tmp2);
+
+    if (config->dmacSync()) {
+        uint32_t tmp3 = config->dmacSync(); tmp3 = ((tmp3 > 15) ? (tmp3 - 8) : tmp3);
+        LPC_GPDMA->DMACSync |= Sync_Src( tmp3 );
+    }
+    
+    uint32_t tfer_type = (uint32_t)config->transferType();
+    if (tfer_type == g2m || tfer_type == m2g) {
+        tfer_type -= 2; // Adjust psuedo transferType to a real transferType.
+    }
+    
+    // Configure DMA Channel, enable Error Counter and Terminate counter
+        DMACCConfig = CxConfig_IE() 
+        | CxConfig_ITC() 
+        | CxConfig_TransferType(tfer_type) 
+        | CxConfig_SrcPeripheral(tmp1) 
+        | CxConfig_DestPeripheral(tmp2);
+    
+    DMACSync = LPC_GPDMA->DMACSync;
+    
+    return ret;
+}*/
+
+// This is modified from MODDMA::Setup(). Values that don't change between DMA operations will be cached to make it faster.
+// Everything commented or deleted here wasn't necessary to restart DMA.
+/*void MODDMA_Cache::Reset(MODDMA_Config* config)
+{
+    LPC_GPDMACH_TypeDef *pChannel = (LPC_GPDMACH_TypeDef*)Channel_p(config->channelNum());
+  
+    //LPC_GPDMA->DMACIntTCClear = IntTCClear_Ch( config->channelNum() );
+    //LPC_GPDMA->DMACIntErrClr  = IntErrClr_Ch ( config->channelNum() );
+    
+    //pChannel->DMACCLLI = DMACCLLI;
+    pChannel->DMACCConfig = DMACCConfig;//
+    
+    //LPC_GPDMA->DMACSync = DMACSync;
+    
+    
+    //setups[config->channelNum() & 0x7] = config;
+    
+    // Reset the Interrupt status
+    LPC_GPDMA->DMACIntTCClear = IntTCClear_Ch( config->channelNum() );//
+    LPC_GPDMA->DMACIntErrClr  = IntErrClr_Ch ( config->channelNum() );
+    
+    // BIGASS SWITCH WENT HERE
+    pChannel->DMACCControl = DMACCControl;
+    
+    if (config->transferType() == p2m)
+    {
+        pChannel->DMACCSrcAddr = DMACCSrcAddr;
+        pChannel->DMACCDestAddr = config->dstMemAddr();//
+    }
+
+    // Enable DMA channels, little endian 
+    pChannel->DMACCConfig |= _E;
+    return;
+}
+*/
+
+/*
+void MODDMA_Cache::Reset(MODDMA_Config* config)
+{
+    LPC_GPDMACH_TypeDef *pChannel = (LPC_GPDMACH_TypeDef*)Channel_p(config->channelNum());
+  
+    LPC_GPDMA->DMACIntTCClear = IntTCClear_Ch( config->channelNum() );
+    LPC_GPDMA->DMACIntErrClr  = IntErrClr_Ch ( config->channelNum() );
+    
+    pChannel->DMACCLLI = DMACCLLI;
+    pChannel->DMACCConfig = DMACCConfig;//
+    
+    LPC_GPDMA->DMACSync = DMACSync;
+    
+    
+    setups[config->channelNum() & 0x7] = config;
+    
+    // Reset the Interrupt status
+    LPC_GPDMA->DMACIntTCClear = IntTCClear_Ch( config->channelNum() );//
+    LPC_GPDMA->DMACIntErrClr  = IntErrClr_Ch ( config->channelNum() );
+    
+    // BIGASS SWITCH WENT HERE
+    pChannel->DMACCControl = DMACCControl;
+    
+    if (config->transferType() == p2m)
+    {
+        pChannel->DMACCSrcAddr = DMACCSrcAddr;
+        pChannel->DMACCDestAddr = config->dstMemAddr();//
+    }
+
+    // Enable DMA channels, little endian 
+    //pChannel->DMACCConfig |= _E;
+    LPC_GPDMA->DMACConfig = _E;
+    return;
+}*/
+/*
+These notes based on the LPC17xx family datasheet:
+
+DMACIntTCClear
+Interrupt terminal count clear
+clear interrupt request
+from <http://dreamrunner.org/wiki/public_html/Embedded%20System/kernel/DMA.html>
+"When the value in the current count register goes from 0 to -1, a terminal count (TC) signal is generated, which signifies the completion of the DMA transfer sequence."
+"DMA controllers require reprogramming when a DMA channel reaches TC."
+
+DMACIntErrClr
+Write 1 to request that some error flags be cleared
+
+DMACCControl*
+contain tons of information: transfer size, burst size, transfer width, etc
+Updated by DMA controller when the linked list is followed. So may not change in our application?
+
+DMACCConfig
+Contains Enable, src type, dest type, transfer type, error mask (to find/mask out the error bit), terminal count irq mask, active (whether there is data), and halt (to stop transfer)
+
+DMACCLLI
+address of next linked list item. If zero, this is the last item in the list and the transfer is complete after this list item is done. 
+
+DMACCSrcAddr*
+User sets the starting address. DMA updates w/ current read address as it goes.
+
+DMACCDestAddr*
+same idea as SrcAddr, but for the data destination
+
+DMAREQSEL
+Datasheet calls it "DMAReqSel" for some reason. 
+I'm not too clear on what this is. 
+Lets you pick whether you want UART or Timer DMA for DMA inputs 8-15.
+
+*Changes very quickly while the transfer in progress, so don't bother reading at that time
+*/
+void MODDMA_Cache::Reset(MODDMA_Config *config)
+{
+    /*Setup(config);
+    return;*/
+    LPC_GPDMACH_TypeDef *pChannel = (LPC_GPDMACH_TypeDef *)Channel_p( config->channelNum() );
+    
+    setups[config->channelNum() & 0x7] = config;
+    
+    // Reset the Interrupt status
+    LPC_GPDMA->DMACIntTCClear = IntTCClear_Ch( config->channelNum() );
+    LPC_GPDMA->DMACIntErrClr  = IntErrClr_Ch ( config->channelNum() );
+
+    // Clear DMA configure
+    pChannel->DMACCControl = 0x00;
+    pChannel->DMACCConfig  = 0x00;
+
+    // Assign Linker List Item value 
+    pChannel->DMACCLLI = config->dmaLLI();
+    
+    // Set value to Channel Control Registers 
+    switch (config->transferType()) {
+    
+        // Memory to memory
+        case m2m:
+            // Assign physical source and destination address
+            pChannel->DMACCSrcAddr  = config->srcMemAddr();
+            pChannel->DMACCDestAddr = config->dstMemAddr();
+            pChannel->DMACCControl
+                = CxControl_TransferSize(config->transferSize()) 
+                | CxControl_SBSize(_32) 
+                | CxControl_DBSize(_32) 
+                | CxControl_SWidth(config->transferWidth()) 
+                | CxControl_DWidth(config->transferWidth()) 
+                | CxControl_SI() 
+                | CxControl_DI() 
+                | CxControl_I();
+            break;
+        
+        // Memory to peripheral
+        case m2p:
+            // Assign physical source
+            pChannel->DMACCSrcAddr = config->srcMemAddr();
+            // Assign peripheral destination address
+            pChannel->DMACCDestAddr = (uint32_t)LUTPerAddr(config->dstConn());
+            pChannel->DMACCControl
+                = /*DMACCControl;*/CxControl_TransferSize((uint32_t)config->transferSize()) 
+                | CxControl_SBSize((uint32_t)LUTPerBurst(config->dstConn())) 
+                | CxControl_DBSize((uint32_t)LUTPerBurst(config->dstConn())) 
+                | CxControl_SWidth((uint32_t)LUTPerWid(config->dstConn())) 
+                | CxControl_DWidth((uint32_t)LUTPerWid(config->dstConn())) 
+                | CxControl_SI() 
+                | CxControl_I();
+            break;
+            
+        // Peripheral to memory
+        case p2m:
+            // Assign peripheral source address
+            pChannel->DMACCSrcAddr = (uint32_t)LUTPerAddr(config->srcConn());
+            // Assign memory destination address
+            pChannel->DMACCDestAddr = config->dstMemAddr();
+            pChannel->DMACCControl
+                = /*DMACCControl;*/CxControl_TransferSize((uint32_t)config->transferSize()) 
+                | CxControl_SBSize((uint32_t)LUTPerBurst(config->srcConn())) 
+                | CxControl_DBSize((uint32_t)LUTPerBurst(config->srcConn())) 
+                | CxControl_SWidth((uint32_t)LUTPerWid(config->srcConn())) 
+                | CxControl_DWidth((uint32_t)LUTPerWid(config->srcConn())) 
+                | CxControl_DI() 
+                | CxControl_I();
+            break;
+            
+        // Peripheral to peripheral
+        case p2p:
+            // Assign peripheral source address
+            pChannel->DMACCSrcAddr = (uint32_t)LUTPerAddr(config->srcConn());
+            // Assign peripheral destination address
+            pChannel->DMACCDestAddr = (uint32_t)LUTPerAddr(config->dstConn());
+            pChannel->DMACCControl
+                = CxControl_TransferSize((uint32_t)config->transferSize()) 
+                | CxControl_SBSize((uint32_t)LUTPerBurst(config->srcConn())) 
+                | CxControl_DBSize((uint32_t)LUTPerBurst(config->dstConn())) 
+                | CxControl_SWidth((uint32_t)LUTPerWid(config->srcConn())) 
+                | CxControl_DWidth((uint32_t)LUTPerWid(config->dstConn())) 
+                | CxControl_I();
+            break;
+            
+        // GPIO to memory
+        case g2m:
+            // Assign GPIO source address
+            pChannel->DMACCSrcAddr = config->srcMemAddr();
+            // Assign memory destination address
+            pChannel->DMACCDestAddr = config->dstMemAddr();
+            pChannel->DMACCControl
+                = CxControl_TransferSize((uint32_t)config->transferSize()) 
+                | CxControl_SBSize((uint32_t)LUTPerBurst(config->srcConn())) 
+                | CxControl_DBSize((uint32_t)LUTPerBurst(config->srcConn())) 
+                | CxControl_SWidth((uint32_t)LUTPerWid(config->srcConn())) 
+                | CxControl_DWidth((uint32_t)LUTPerWid(config->srcConn())) 
+                | CxControl_DI() 
+                | CxControl_I();
+            break;
+            
+        // Memory to GPIO
+        case m2g:
+            // Assign physical source
+            pChannel->DMACCSrcAddr = config->srcMemAddr();
+            // Assign peripheral destination address
+            pChannel->DMACCDestAddr = config->dstMemAddr();
+            pChannel->DMACCControl
+                = CxControl_TransferSize((uint32_t)config->transferSize()) 
+                | CxControl_SBSize((uint32_t)LUTPerBurst(config->dstConn())) 
+                | CxControl_DBSize((uint32_t)LUTPerBurst(config->dstConn())) 
+                | CxControl_SWidth((uint32_t)LUTPerWid(config->dstConn())) 
+                | CxControl_DWidth((uint32_t)LUTPerWid(config->dstConn())) 
+                | CxControl_SI() 
+                | CxControl_I();
+            break;
+            
+        // Do not support any more transfer type, return ERROR
+        default:
+            return;// 0;
+    }
+
+     // Re-Configure DMA Request Select for source peripheral 
+    if (config->srcConn() > 15) {
+        LPC_SC->DMAREQSEL |= (1 << (config->srcConn() - 16));
+    } 
+    else {
+        LPC_SC->DMAREQSEL &= ~(1 << (config->srcConn() - 8));
+    }
+
+    // Re-Configure DMA Request Select for destination peripheral
+    if (config->dstConn() > 15) {
+        LPC_SC->DMAREQSEL |= (1 << (config->dstConn() - 16));
+    }
+    else {
+        LPC_SC->DMAREQSEL &= ~(1 << (config->dstConn() - 8));
+    }
+
+    // Enable DMA channels, little endian 
+    LPC_GPDMA->DMACConfig = _E;
+    pChannel->DMACCConfig |= _E; // copied from MODDMA::Enable()
+    while (!(LPC_GPDMA->DMACConfig & _E));
+
+    // Calculate absolute value for Connection number
+    uint32_t tmp1 = config->srcConn(); tmp1 = ((tmp1 > 15) ? (tmp1 - 8) : tmp1);
+    uint32_t tmp2 = config->dstConn(); tmp2 = ((tmp2 > 15) ? (tmp2 - 8) : tmp2);
+
+    if (config->dmacSync()) {
+        uint32_t tmp3 = config->dmacSync(); tmp3 = ((tmp3 > 15) ? (tmp3 - 8) : tmp3);
+        LPC_GPDMA->DMACSync |= Sync_Src( tmp3 );
+    }
+    
+    uint32_t tfer_type = (uint32_t)config->transferType();
+    if (tfer_type == g2m || tfer_type == m2g) {
+        tfer_type -= 2; // Adjust psuedo transferType to a real transferType.
+    }
+    
+    // Configure DMA Channel, enable Error Counter and Terminate counter
+    pChannel->DMACCConfig 
+        = /*DMACCConfig;*/CxConfig_IE() 
+        | CxConfig_ITC() 
+        | CxConfig_TransferType(tfer_type) 
+        | CxConfig_SrcPeripheral(tmp1) 
+        | CxConfig_DestPeripheral(tmp2);
+    
+    return;// pChannel->DMACCControl;
+}
+
+extern "C" void MODDMA_Cache_IRQHandler()
+{
+    p6_TOGGLE;
+    
+    /*if (moddma_p == (class MODDMA *)NULL) {
+        if (oldDMAHandler) {
+            ((MODDMA_FN)oldDMAHandler)();
+            return;
+        }
+        else {
+            error("Interrupt without instance");
+        }
+    }*/
+    
+    
+    // SEE UNROLLED VERSION BELOW. They are equivalent.
+    for (int channel_number = 0; channel_number < 2; channel_number++)
+    {
+        uint32_t channel_mask = (1UL << channel_number);
+        
+        // Since we only have one if statement inside anyway, I took this out
+        //if (LPC_GPDMA->DMACIntStat & channel_mask)
+        //{
+            if (LPC_GPDMA->DMACIntTCStat & channel_mask)
+            {
+                moddma_p->setups[channel_number]->isrIntTCStat->call();
+                LPC_GPDMA->DMACIntTCClear = channel_mask;
+            }
+            
+            //if (LPC_GPDMA->DMACIntErrStat & channel_mask)
+            //{
+                // removed for speed
+                //moddma_p->setups[channel_number]->isrIntErrStat->call();
+                LPC_GPDMA->DMACIntErrClr = channel_mask;
+            //}
+        //}
+    }
+    
+    
+    /*
+    // This is some nasty code
+    uint32_t channel_mask;
+    channel_mask = (1UL << 0);
+    
+    // Since we only have one if statement inside anyway, I took this out
+    //if (LPC_GPDMA->DMACIntStat & channel_mask)
+    //{
+        if (LPC_GPDMA->DMACIntTCStat & channel_mask)
+        {
+            moddma_p->setups[0]->isrIntTCStat->call();
+            LPC_GPDMA->DMACIntTCClear = channel_mask;
+        }
+        
+        //if (LPC_GPDMA->DMACIntErrStat & channel_mask)
+        //{
+            // removed for speed
+            //moddma_p->setups[channel_number]->isrIntErrStat->call();
+            LPC_GPDMA->DMACIntErrClr = channel_mask;
+        //}
+    //}
+    channel_mask = (1UL << 1);
+    // Since we only have one if statement inside anyway, I took this out
+    //if (LPC_GPDMA->DMACIntStat & channel_mask)
+    //{
+        if (LPC_GPDMA->DMACIntTCStat & channel_mask)
+        {
+            moddma_p->setups[1]->isrIntTCStat->call();
+            LPC_GPDMA->DMACIntTCClear = channel_mask;
+        }
+        
+        //if (LPC_GPDMA->DMACIntErrStat & channel_mask)
+        //{
+            // removed for speed
+            //moddma_p->setups[channel_number]->isrIntErrStat->call();
+            LPC_GPDMA->DMACIntErrClr = channel_mask;
+        //}
+    //}*/
+    
+    p6_TOGGLE;
+}
+
+void MODDMA_Cache::init()
+{
+    NVIC_SetVector(DMA_IRQn, (uint32_t)MODDMA_Cache_IRQHandler);
+    NVIC_EnableIRQ(DMA_IRQn);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MODDMA_cache.h	Fri Aug 02 02:23:36 2013 +0000
@@ -0,0 +1,27 @@
+#ifndef MODDMA_CACHE_H__
+#define MODDMA_CACHE_H__
+
+#include "mbed.h"
+
+#include "MODDMA.h"
+
+// TODO: why doesn't include surfboard work? we should not have to extern this.
+//#include "surfboard.h"
+extern Serial submarine;
+
+class MODDMA_Cache : public MODDMA
+{
+public:
+    MODDMA_Cache();
+    uint32_t Setup(MODDMA_Config* config);
+    void Reset(MODDMA_Config* config);
+protected:
+
+private:
+    void init();
+    // These variables cache DMA controller parameters
+    //LPC_GPDMACH_TypeDef cache;
+    uint32_t DMACCSrcAddr, DMACCDestAddr, DMACCLLI, DMACCControl, DMACCConfig, DMACSync, DMACIntTCClear, DMACIntErrClr, DMAREQSEL;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SimpleIOMacros.lib	Fri Aug 02 02:23:36 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/AjK/code/SimpleIOMacros/#2231ee4dc937
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dma.cpp	Fri Aug 02 02:23:36 2013 +0000
@@ -0,0 +1,222 @@
+#include "dma.h"
+
+DigitalOut test5(p5);
+DigitalOut test6(p6);
+DigitalOut test(p7);
+
+char buf[NUM_CHANNELS * 3]; // 3 bytes per sample
+char dummy_buf[sizeof(buf) / sizeof(char)];
+
+SPI ads1274(/*mosi, unconnected*/ p11, /*miso, dout*/ p12, /*sclk*/ p13);
+
+MODDMA_Cache dma;
+MODDMA_Config* dma_conf = new MODDMA_Config;
+MODDMA_Config* sclk_dummy_conf = new MODDMA_Config;
+
+//q15_t parsed[2][NUM_CHANNELS][BLOCK_SIZE];
+char parsed[2][NUM_CHANNELS][BLOCK_SIZE][3];
+
+
+char write_block = 0;
+int buf_index = 0;
+unsigned int read_blocks = 0;
+
+extern bool fresh_data;
+
+int setup_dma()
+{
+    ads1274.frequency(12 * 1000000); // Theoretical minimum 9.6MHz
+    ads1274.format(8, 3); // clock polarity cpol = 1; clock phase cpha = 1
+    
+    memset(buf, 0, sizeof(buf));
+    // stolen from moddma example 2
+    dma_conf
+     ->channelNum    ( MODDMA::Channel_1 )
+     ->srcMemAddr    ( 0 )
+     ->dstMemAddr    ( (uint32_t)buf )
+     ->transferSize  ( sizeof(buf) )
+     ->transferType  ( MODDMA::p2m )
+     ->transferWidth ( MODDMA::word )   // 4 bytes at a time
+     ->srcConn       ( MODDMA::SSP0_Rx ) // SSP0: pins 11-13. SSP1: pins 5-7.
+     ->dstConn       ( 0 )
+     ->dmaLLI        ( 0 )
+     ->attach_tc     ( &parse_data ) // called when transferSize is reached
+     ->attach_err    ( &dma_error_cb )
+    ;
+    dma.Setup(dma_conf);
+    
+    memset(dummy_buf, 0x55, sizeof(dummy_buf));
+    dummy_buf[sizeof(dummy_buf) - 1] = '\0';
+    sclk_dummy_conf
+     ->channelNum    ( MODDMA::Channel_0 ) // make this one the highest priority DMA
+     ->srcMemAddr    ( (uint32_t)dummy_buf )
+     ->dstMemAddr    ( 0 )
+     ->transferSize  ( sizeof(dummy_buf) )
+     ->transferType  ( MODDMA::m2p )
+     ->transferWidth ( MODDMA::word )   // 4 bytes at a time
+     ->srcConn       ( 0 )
+     ->dstConn       ( MODDMA::SSP0_Tx ) // SSP0: pins 11-13. SSP1: pins 5-7.
+     ->dmaLLI        ( 0 )
+     ->attach_tc     ( 0 ) // called when transferSize is reached
+     ->attach_err    ( &dma_error_cb )
+    ;
+    dma.Setup(sclk_dummy_conf);
+    
+    return 0;   // success
+}
+
+void teardown_dma()
+{
+    
+}
+
+// Stolen from Andy Kirkham http://mbed.org/forum/mbed/topic/2326/
+
+// "One last piece of advice...You have to be careful when you start doing 'neat things' directly with the peripherals to ensure you don't break other things that previously worked fine."
+// -- ANdy Kirkham, aka Daniel Naito in 20 years
+/** EINT3_IRQHandler
+ */
+extern "C" void EINT3_IRQHandler()
+{
+
+    // The "event" is connected to pin p8 which is LPC1768 P0_6 so lets trap
+    // that and ignore all other GPIO interrupts.    
+    // Test for IRQ on Port0.
+    if (LPC_GPIOINT->IntStatus & 0x1)
+    {
+        // If P0_6/p8 rises, call get_data()
+        // The "R" means we're looking for the rising edge
+        if (LPC_GPIOINT->IO0IntStatR & (1 << 6))
+        {
+            // We found what we're looking for
+            get_data();
+        }
+    }
+    
+    // Clear all possible GPIO-generated interrupts as they don't concern us.
+    // Once we process the interrupt, we wipe out this interrupt and all the others
+    //LPC_GPIOINT->IO2IntClr = (LPC_GPIOINT->IO2IntStatR | LPC_GPIOINT->IO2IntStatF);
+    //LPC_GPIOINT->IO0IntClr = (LPC_GPIOINT->IO0IntStatR | LPC_GPIOINT->IO0IntStatF);
+    
+    // Clear only this interrupt
+    LPC_GPIOINT->IO0IntClr = LPC_GPIOINT->IO0IntStatR & (1 << 6);
+}
+
+void event_irq_init()
+{
+    // Use macro to set p8 as an input.
+    p8_AS_INPUT;
+    // Enable P0_6/p8 for rising edge interrupt generation.
+    LPC_GPIOINT->IO0IntEnR |= (1UL << 6); //hope this works
+    // Enable the interrupt.
+    NVIC_SetVector(EINT3_IRQn, (uint32_t)EINT3_IRQHandler);
+    NVIC_EnableIRQ(EINT3_IRQn);
+}
+
+void dma_error_cb()
+{
+    
+}
+
+// 6.80 us
+inline void get_data()
+{
+    // Need to wait 4 mbed clock cycles here. Callback overhead (about 1.2 us) is more than enough.
+    
+    p7_TOGGLE;
+    #define USE_RESET
+    
+    #ifndef USE_RESET
+    dma.Setup(dma_conf);
+    dma.Setup(sclk_dummy_conf);
+    #else
+    dma.Reset(dma_conf);
+    dma.Reset(sclk_dummy_conf);
+    #endif
+    
+    dma.Enable(sclk_dummy_conf);
+    dma.Enable(dma_conf);
+    
+    // enable ssp0 fifo. Seems to be required for DMA to work.
+    LPC_SSP0->DMACR = 0x3;
+    //wait_ms(10);
+    //fresh_data = true;
+    
+    /*
+    for (int i = 0; i < sizeof(buf)/sizeof(char); i++)
+    {
+        buf[i] = ads1274.write(0);
+    }
+    parse_data();
+    */
+    
+    if (led2num > TARGET_SPS)
+    {
+        LED2_TOGGLE;
+        led2num = 0;
+    }
+    else
+    {
+        led2num++;
+    }
+    
+    p7_TOGGLE;
+}
+
+// 310 ns
+inline void parse_data()
+{
+    p5_TOGGLE;
+    // Assumes a little-endian CPU. Intel is always little-endian. ARM is
+    // configurable, but the LPC1768 is little-endian.
+    // We flip the byte order because the ADS1274 is big-endian. Also throw out
+    // the least significant byte for speed.
+    /*
+    parsed[write_block][0][buf_index] = buf[ 5] | (((uint16_t)buf[4]) << 8);
+    parsed[write_block][1][buf_index] = buf[ 9] | (((uint16_t)buf[8]) << 8);
+    parsed[write_block][2][buf_index] = buf[ 12] | (((uint16_t)buf[11]) << 8);
+    parsed[write_block][3][buf_index] = buf[ 15] | (((uint16_t)buf[14]) << 8);
+    */
+    for (int i = 0; i < NUM_CHANNELS; i++)
+    {
+        for (int j = 0; j < 3; j++)
+        {
+            parsed[write_block][i][buf_index][j] = buf[3*i+j];
+        }
+    }
+    
+    
+    /*
+    if (buf_index > 100)
+    {
+        for (int i = 0; i < NUM_CHANNELS*3; i++)
+        {
+            submarine.printf("%02x ", buf[i]);
+        }
+        while (true);
+    }
+    */
+    bool valid = (parsed[write_block][0][buf_index] != 0);
+    
+    buf_index++;
+    
+    if (buf_index == BLOCK_SIZE)
+    {
+        fresh_data = true;
+        // Switch to the other half of this buffer.
+        write_block ^= 0x1; //toggle the last bit
+        read_blocks++;
+        buf_index = 0;
+    }
+    if (valid){
+    if (led3num > TARGET_SPS)
+    {
+        LED3_TOGGLE;
+        led3num = 0;
+    }
+    else
+    {
+        led3num++;
+    }}
+    p5_TOGGLE;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dma.h	Fri Aug 02 02:23:36 2013 +0000
@@ -0,0 +1,32 @@
+#ifndef DMA_H__
+#define DMA_H__
+
+#include "mbed.h"
+
+#include "IOMacros.h"
+
+#include "surfboard.h"
+#include "hydro_dsp.h"
+#include "MODDMA_cache.h"
+
+// TODO: check this value
+#define TARGET_SPS (105496 / 10) // samples per second
+
+int setup_dma();
+void teardown_dma();
+
+void event_irq_init();
+
+void dma_error_cb();
+void get_data();
+void parse_data();
+
+extern MODDMA_Cache dma;
+extern MODDMA_Config* dma_conf;
+//extern q15_t parsed[2][NUM_CHANNELS][BLOCK_SIZE];
+extern char parsed[2][NUM_CHANNELS][BLOCK_SIZE][3];
+extern char write_block;
+extern int buf_index;
+extern unsigned int read_blocks;
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hydro_dsp.cpp	Fri Aug 02 02:23:36 2013 +0000
@@ -0,0 +1,48 @@
+#include "hydro_dsp.h"
+
+arm_iir_lattice_instance_q15 iir_filter[NUM_CHANNELS];
+
+// TODO: need an A and B for each possible frequency
+// our pinger: 27k
+// dave's pinger(s): TODO: ask about this
+q15_t A_27k0[FILTER_ORDER] = {};
+q15_t B_27k0[FILTER_ORDER+1] = {};
+
+// Set these variables to select which set of coefficients (taps) we use
+q15_t* A_select = A_27k0;
+q15_t* B_select = B_27k0;
+
+q15_t state_arr[NUM_CHANNELS][FILTER_ORDER + BLOCK_SIZE];
+
+q15_t filtered[BLOCK_SIZE];
+
+void setup_dsp()
+{
+    memset(state_arr, 0, 2*NUM_CHANNELS*(FILTER_ORDER + BLOCK_SIZE));
+    for (int i = 0; i < NUM_CHANNELS; i++) {
+        arm_iir_lattice_init_q15(iir_filter+i, FILTER_ORDER, A_27k0, B_27k0, state_arr[i], BLOCK_SIZE);
+    }
+}
+
+// Figure out where the wave starts in the filtered output
+// TODO: does this work?
+// Returns the index of the first "high" in the signal or -1 if there is none
+int find_timestamp_tdoa(q15_t* filtered)
+{
+    // Uncomment the num_hits stuff if our filtering sucks
+    //char num_hits = 0;
+    for (int j = 0; j < BLOCK_SIZE; j++)
+    {
+        if (filtered[j] > TDOA_THRESH || filtered[j] < -1 * TDOA_THRESH)
+        {
+            return j;
+            /*num_hits++;
+            if (num_hits > TDOA_MIN_NUM_HITS)
+            {
+                return j;            
+            }*/
+        }
+    }
+    
+    return -1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hydro_dsp.h	Fri Aug 02 02:23:36 2013 +0000
@@ -0,0 +1,18 @@
+#ifndef HYDRO_DSP_H__
+#define HYDRO_DSP_H__
+
+#include "dsp.h"
+
+#define NUM_CHANNELS 6//
+#define FILTER_ORDER 6
+#define BLOCK_SIZE 512
+#define TDOA_THRESH (32767 / 2)
+//#define TDOA_MIN_NUM_HITS 2
+
+void setup_dsp();
+int find_timestamp_tdoa(q15_t* filtered);
+
+extern arm_iir_lattice_instance_q15 iir_filter[NUM_CHANNELS];
+extern q15_t filtered[BLOCK_SIZE];
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-dsp.lib	Fri Aug 02 02:23:36 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed-dsp/#da51fb522205
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Fri Aug 02 02:23:36 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/b3110cd2dd17
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/surfboard.cpp	Fri Aug 02 02:23:36 2013 +0000
@@ -0,0 +1,278 @@
+// I think we should move this to the offline compiler so we can get loop unrolling. -- Kevin
+// TODO: code size is small (4% of available space) so we should totally do loop unrolling
+
+#include "surfboard.h"
+
+// Might have to use IO Macros library for fast digital I/O http://mbed.org/users/AjK/code/SimpleIOMacros/
+// less abstracted InterruptIn http://mbed.org/forum/mbed/topic/2326/
+//InterruptIn drdy(p8); // active low
+// interruptin too slow http://mbed.org/forum/mbed/topic/2326/
+DigitalOut sync(p14); // active low
+
+//Serial submarine(/*tx*/ p28, /*rx*/ p27);
+Serial submarine(USBTX, USBRX);
+// From sub to hydrophone board:
+// r = start running
+// s = stop running
+// From hydrophone board to sub:
+// h = hello world
+// b = turned off because battery low
+
+DigitalOut avdd_ctl(p15);
+
+AnalogIn abat_sense(p16);
+// We don't want the battery to go below 6 V. 
+// TODO: read the battery after each IIR batch?
+// Calculate what the minimum battery voltage should be after it goes through
+// the voltage divider and mbed AnalogIn ADC.
+#define ABAT_RATIO ((2.21f / 6.95f) / 3.3f)
+#define ABAT_THRESH_LO (ABAT_RATIO * 6.0f)
+#define ABAT_THRESH_HI (ABAT_RATIO * 6.5f)
+Ticker abat_tick;
+
+// On Surfboard v1.0, setting these does not actually control the power because
+// of the modifications on the board.
+DigitalOut clkvdd(p9), iovdd(p10);
+
+volatile bool fresh_data = false;
+
+// lights for debugging
+// Replacing with SimpleIOMacros.h because each toggle takes ~300 ns (!!)
+DigitalOut led1(LED1), // blinking: wait for run command. solid: ready
+           led2(LED2), // in function get_data()
+           led3(LED3), // toggles when IIR filter is run
+           led4(LED4); // toggles on fresh data
+// Counts how many times we toggled the LED
+uint32_t led1num, led2num, led3num, led4num;
+Ticker* led1ticks[2];
+
+int i_timestamps[NUM_CHANNELS];
+
+// For saving received data to disk
+LocalFileSystem local("local");
+bool log_data_enabled = true;
+
+// Call this function to reset the mbed
+extern "C" void mbed_reset();
+
+int main()
+{
+    pre_setup(); // For safety, this must be the first thing that runs
+    
+    /*for (int i = 0; i < sizeof(parsed)/sizeof(uint16_t); i++)
+    {
+        *(((int16_t*)parsed) + i) = 12345;
+    }*/
+    
+    wait(5);
+    
+    led1ticks[0] = new Ticker();
+    led1ticks[1] = new Ticker();
+    
+    led1 = 1;
+    led1ticks[0]->attach(&led1_cb, 1.0);
+    wait_ms(60);
+    led1ticks[1]->attach(&led1_cb, 1.0);
+    led1 = 0;
+    
+    submarine.baud(115200);
+    submarine.putc('h');
+    submarine.printf("%d ", sizeof(q15_t));
+    
+    // Wait until we are told to turn on
+    //while (submarine.getc() != 'r');
+    
+    submarine.attach(&submarine_cb);
+    led1ticks[0]->detach();
+    led1ticks[1]->detach();
+    
+    setup();
+    
+    while (true)
+    {
+        if (fresh_data)
+        {
+            fresh_data = false;
+            
+            LED4_TOGGLE;
+            
+            if (log_data_enabled /*&& read_blocks > 3*/)
+            {
+                // Disable DRDY interrupt to prevent the buffer from changing
+                // while we save it. Writing to disk takes a long time.
+//                drdy.rise(NULL);
+                // Only log data once
+                log_data_enabled = false;
+                
+                FILE *fp = fopen("/local/out.csv", "w");
+                
+                fprintf(fp, "Index,Ch1,Ch2,Ch3,Ch4\n");
+                for (int i = 0; i < BLOCK_SIZE; i++)
+                {
+                    fprintf(fp, "%d", i);
+                    for (int j = 0; j < NUM_CHANNELS; j++)
+                    {
+                        fprintf(fp, ",%02X%02X%02X", parsed[write_block^1][j][i][0], parsed[write_block^1][j][i][1], parsed[write_block^1][j][i][2]);
+                        //int16_t n = parsed[write_block^1][j][i];
+                        //fprintf(fp, ",%X", n);
+                    }
+                    fprintf(fp, "\n");
+                }
+                
+                fclose(fp);
+                
+              //  drdy.rise(&get_data);
+            }
+            
+            /*for (char i = 0; i < NUM_CHANNELS; i++)
+            {
+                arm_iir_lattice_q15(iir_filter+i, parsed[write_block^1][i], filtered, BLOCK_SIZE);
+                i_timestamps[i] = find_timestamp_tdoa(filtered);
+            }*/
+            // hyperbolic positioning
+            // send it to the sub
+        }
+    }
+}
+
+void pre_setup()
+{
+    
+}
+
+int setup()
+{
+    int retValue = 0;
+    retValue += setup_ads1274();
+    retValue += setup_dma();
+    
+    led1 = 1;   // Debug
+    
+    // Should be at the end of setup function, in case ADS1274 tries to send data
+    //drdy.rise(&get_data);
+    event_irq_init();
+    
+    return retValue;
+}
+
+// Return values
+// 0: setup finished normally
+// 1: setup aborted because battery voltage was too low
+int setup_ads1274()
+{
+    // Don't start up if the battery is too low
+    abat_cb();
+    abat_tick.attach(&abat_cb, 1);
+    
+    // Sequence the power-on of the ADC
+    iovdd = 1;
+    __nop();
+    avdd_ctl = 1;
+    // Give it some time, just in case.
+    wait_ms(5);
+    // Now we can start driving pins without destroying the ADS1274
+    // Turn on sampling clock (CLK)
+    clkvdd = 1;
+    
+    // ADS1274 needs 2^18 clk cycles + 129 conversions before data are valid
+    // minimum wait time = 2^18 / f_clk + 129 / f_data = 2^18/27MHz + 129/0.1MHz = 11 ms
+    wait_ms(11 + 1);
+    
+    // Datasheet suggested resetting the ads1274 after start up to synchronize outputs
+    // Need to put it low for at least 1 CLK period
+    // On mbed, this is 96 MHz/27MHz = 3.556 instruction cycles
+    sync = 1;
+    // Drive *sync to logic low for about 4 instruction cycles
+    sync = 0;
+    __nop(); __nop(); __nop(); __nop(); // wait 4 cycles
+    sync = 1;
+    // Wait 1/f_data = 1/0.1MHz = 10 us for new data to be valid
+    wait_us(10 + 1);
+    
+    return 0; // success
+}
+
+void teardown()
+{
+    // Disable interrupts. Not sure if this is necessary, but I don't want samples to be read while we shut down
+    __disable_irq();
+    
+    teardown_ads1274();
+    teardown_dma();
+}
+
+void teardown_ads1274()
+{
+    // Turn off peripherals, then turn off ADS1274
+    // Turn off CLK
+    clkvdd = 0;
+    __nop(); __nop();
+    // Turn off op amps and analog power to ADS1274
+    avdd_ctl = 0;
+    // Turn off IO power
+    iovdd = 0;
+}
+
+// Called when the submarine sends a message to us
+void submarine_cb()
+{
+    // Loop as long as there are more characters to be read
+    while (submarine.readable())
+    {
+        // If we are told to stop
+        if (submarine.getc() == 's')
+        {
+            // Shut everything down
+            teardown();
+            // Reset the mbed
+            mbed_reset();
+        }
+    }
+}
+
+// Called every few seconds to check the battery status
+void abat_cb()
+{
+    if (abat_sense < ABAT_THRESH_LO)
+    {
+        // Uh oh, stop the hydrophone board
+        teardown();
+        
+        submarine.putc('b');
+        led1 = led2 = led3 = led4 = 0;
+        
+        // Wait until someone changes the battery
+        int num_hi = 0, loop_count = 0;;
+        while (num_hi < 100)
+        {
+            if (abat_sense > ABAT_THRESH_HI)
+            {
+                num_hi++;
+            }
+            else if (num_hi > 0)
+            {
+                num_hi--;
+            }
+            
+            if (loop_count == 32000)
+            {
+                led1 = led2 = led3 = led4 = !led1;
+                loop_count = 0;
+            }
+            else
+            {
+                loop_count++;
+            }
+        }
+        
+        // On battery replacement, wait for the user to plug the battery in all the way
+        wait_ms(3000);
+        
+        mbed_reset();
+    }
+}
+
+void led1_cb()
+{
+    led1 = !led1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/surfboard.h	Fri Aug 02 02:23:36 2013 +0000
@@ -0,0 +1,25 @@
+#ifndef SURFBOARD_H__
+#define SURFBOARD_H__
+
+#include "mbed.h"
+#include "dsp.h"
+
+#include "dma.h"
+#include "hydro_dsp.h"
+
+void pre_setup();
+int setup();
+int setup_ads1274();
+
+void teardown();
+void teardown_ads1274();
+
+void submarine_cb();
+void abat_cb();
+void led1_cb();
+
+extern Serial submarine;
+extern DigitalOut led1, led2, led3, led4;
+extern uint32_t led1num, led2num, led3num, led4num;
+
+#endif