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
Revision 0:2381a319fc35, committed 2013-08-02
- 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
--- /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