A simple library to access the DMA functionality.
Fork of SimpleDMA by
Revision 5:d9f46ef80e20, committed 2014-01-04
- Comitter:
- Sissors
- Date:
- Sat Jan 04 14:42:33 2014 +0000
- Parent:
- 4:c3a84c6c432c
- Child:
- 6:e9ab0bb912c8
- Commit message:
- Refactored code, added LPC1768 support
Changed in this revision
--- a/SimpleDMA.h Thu Dec 26 16:31:54 2013 +0000 +++ b/SimpleDMA.h Sat Jan 04 14:42:33 2014 +0000 @@ -7,6 +7,7 @@ #include "mbed.h" #include "SimpleDMA_KL25.h" +#include "SimpleDMA_LPC1768.h" /** @@ -42,8 +43,10 @@ * @return - 0 on success */ template<typename Type> -int source(Type* pointer, bool autoinc, int size = sizeof(Type) * 8) { - return setAddress((uint32_t)pointer, size, true, autoinc); +void source(Type* pointer, bool autoinc, int size = sizeof(Type) * 8) { + _source = (uint32_t)pointer; + source_inc = autoinc; + source_size = size; } /** @@ -63,8 +66,10 @@ * @return - 0 on success */ template<typename Type> -int destination(Type* pointer, bool autoinc, int size = sizeof(Type) * 8) { - return setAddress((uint32_t)pointer, size, false, autoinc); +void destination(Type* pointer, bool autoinc, int size = sizeof(Type) * 8) { + _destination = (uint32_t)pointer; + destination_inc = autoinc; + destination_size = size; } /** @@ -78,7 +83,9 @@ * @param trig - trigger to use * @param return - 0 on success */ -int trigger(SimpleDMA_Trigger trig); +void trigger(SimpleDMA_Trigger trig) { + _trigger = trig; +} /** * Set the DMA channel @@ -141,36 +148,33 @@ #endif protected: -int setAddress(uint32_t address, int wordsize, bool source, bool autoinc); +int _channel; +SimpleDMA_Trigger _trigger; +uint32_t _source; +uint32_t _destination; +bool source_inc; +bool destination_inc; +uint8_t source_size; +uint8_t destination_size; -int _channel; bool auto_channel; -uint32_t SAR, DAR, DSR, DCR; -uint8_t CHCFG; //IRQ handlers FunctionPointer _callback; void irq_handler(void); -static SimpleDMA *irq_owner[4]; +static SimpleDMA *irq_owner[DMA_CHANNELS]; + +static void irq_handler0( void ); -static void irq_handler0( void ) { - if (irq_owner[0]!=NULL) - irq_owner[0]->irq_handler(); -} -static void irq_handler1( void ) { - if (irq_owner[1]!=NULL) - irq_owner[1]->irq_handler(); -} -static void irq_handler2( void ) { - if (irq_owner[2]!=NULL) - irq_owner[2]->irq_handler(); -} -static void irq_handler3( void ) { - if (irq_owner[3]!=NULL) - irq_owner[3]->irq_handler(); -} +#if DMA_IRQS > 1 +static void irq_handler1( void ); +static void irq_handler2( void ); +static void irq_handler3( void ); +#endif +//Keep searching until we find a non-busy channel, start with lowest channel number +int getFreeChannel(void); #ifdef RTOS_H osThreadId id; @@ -178,17 +182,5 @@ osSignalSet(id, 0x1); } #endif - -//Keep searching until we find a non-busy channel, start with lowest channel number -int getFreeChannel(void) { - int retval = 0; - while(1) { - if (!isBusy(retval)) - return retval; - retval++; - if (retval >= DMA_CHANNELS) - retval = 0; - } -} }; #endif \ No newline at end of file
--- a/SimpleDMA_KL25.cpp Thu Dec 26 16:31:54 2013 +0000 +++ b/SimpleDMA_KL25.cpp Sat Jan 04 14:42:33 2014 +0000 @@ -1,3 +1,4 @@ +#ifdef TARGET_KL25Z #include "SimpleDMA.h" @@ -12,8 +13,6 @@ SIM->SCGC7 |= 1<<8; //Enable clock to DMA trigger(Trigger_ALWAYS); - - DCR = (1<<29) + (1<<30); //Set to always use DMAMUX (If no trigger is needed we route via alwayson) NVIC_SetVector(DMA0_IRQn, (uint32_t)&irq_handler0); NVIC_SetVector(DMA1_IRQn, (uint32_t)&irq_handler1); @@ -25,56 +24,6 @@ NVIC_EnableIRQ(DMA3_IRQn); } -int SimpleDMA::setAddress(uint32_t address, int wordsize, bool source, bool autoinc) { - //Check if it is an allowed address - switch ((uint32_t) address >> 20) { - case 0x000: - case 0x1FF: - case 0x200: - case 0x400: - break; - default: - return -1; - } - - char _size; - - switch (wordsize) { - case 8: - _size = 1; - break; - case 16: - _size = 2; - break; - case 32: - _size = 0; - break; - default: - _size = 1; - } - - //Check if source or destination - if (source) { - SAR = address; - DCR &= ~(7<<20); - DCR |= autoinc << 22; - DCR |= _size << 20; - } else { - DAR = address; - DCR &= ~(7<<17); - DCR |= autoinc << 19; - DCR |= _size << 17; - } - - return 0; -}; - -int SimpleDMA::trigger(SimpleDMA_Trigger trig){ - - CHCFG = trig; - return 0; -} - int SimpleDMA::start(int length) { if (auto_channel) @@ -82,42 +31,42 @@ else while(isBusy()); - if (length > 0xFFFFF) + if (length > DMA_DSR_BCR_BCR_MASK) return -1; - DCR |= (1UL<<31); - irq_owner[_channel] = this; + irq_owner[_channel] = this; + + DMA0->DMA[_channel].SAR = _source; + DMA0->DMA[_channel].DAR = _destination; + DMA0->DMA[_channel].DSR_BCR = length; + DMAMUX0->CHCFG[_channel] = _trigger; - //Set registers: - DMA0->DMA[_channel].SAR = SAR; - DMA0->DMA[_channel].DAR = DAR; - DMA0->DMA[_channel].DCR = DCR; + uint32_t config = DMA_DCR_EINT_MASK | DMA_DCR_ERQ_MASK | DMA_DCR_CS_MASK | (source_inc << DMA_DCR_SINC_SHIFT) | (destination_inc << DMA_DCR_DINC_SHIFT); + switch (source_size) { + case 8: + config |= 1 << DMA_DCR_SSIZE_SHIFT; + break; + case 16: + config |= 2 << DMA_DCR_SSIZE_SHIFT; + break; + } + switch (destination_size) { + case 8: + config |= 1 << DMA_DCR_DSIZE_SHIFT; + break; + case 16: + config |= 2 << DMA_DCR_DSIZE_SHIFT; + break; + } - //Set trigger - DMAMUX0->CHCFG[_channel] = CHCFG; - - //Set length - DMA0->DMA[_channel].DSR_BCR = length; - + DMA0->DMA[_channel].DCR = config; + //Start DMAMUX0->CHCFG[_channel] |= 1<<7; return 0; } -void SimpleDMA::channel(int chan) { - if (chan == -1) { - auto_channel = true; - _channel = 0; - } else { - auto_channel = false; - if (chan >= 0 && chan < DMA_CHANNELS) - _channel = chan; - else - _channel = 3; - } -} - bool SimpleDMA::isBusy( int channel ) { //Busy bit doesn't work as I expect it to do, so just check if counter is at zero //return (DMA0->DMA[_channel].DSR_BCR & (1<<25) == 1<<25); @@ -127,8 +76,31 @@ return (DMA0->DMA[channel].DSR_BCR & 0xFFFFFF); } + +/*****************************************************************/ void SimpleDMA::irq_handler(void) { DMAMUX0->CHCFG[_channel] = 0; DMA0->DMA[_channel].DSR_BCR |= DMA_DSR_BCR_DONE_MASK ; _callback.call(); } + +void SimpleDMA::irq_handler0( void ) { + if (irq_owner[0]!=NULL) + irq_owner[0]->irq_handler(); +} + +void SimpleDMA::irq_handler1( void ) { + if (irq_owner[1]!=NULL) + irq_owner[1]->irq_handler(); +} + +void SimpleDMA::irq_handler2( void ) { + if (irq_owner[2]!=NULL) + irq_owner[2]->irq_handler(); +} + +void SimpleDMA::irq_handler3( void ) { + if (irq_owner[3]!=NULL) + irq_owner[3]->irq_handler(); +} +#endif \ No newline at end of file
--- a/SimpleDMA_KL25.h Thu Dec 26 16:31:54 2013 +0000 +++ b/SimpleDMA_KL25.h Sat Jan 04 14:42:33 2014 +0000 @@ -1,6 +1,7 @@ #ifdef TARGET_KL25Z #define DMA_CHANNELS 4 +#define DMA_IRQS 4 enum SimpleDMA_Trigger { Trigger_ALWAYS = 60,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SimpleDMA_LPC1768.cpp Sat Jan 04 14:42:33 2014 +0000 @@ -0,0 +1,105 @@ +#ifdef TARGET_LPC1768 + +#include "SimpleDMA.h" + +SimpleDMA *SimpleDMA::irq_owner[8] = {NULL}; +LPC_GPDMACH_TypeDef *LPC_GPDMACH[8] = {LPC_GPDMACH0, LPC_GPDMACH1, LPC_GPDMACH2, LPC_GPDMACH3, LPC_GPDMACH4, LPC_GPDMACH5, LPC_GPDMACH6, LPC_GPDMACH7}; +uint32_t getTransferType(SimpleDMA_Trigger trig, uint32_t source, uint32_t destination); + +SimpleDMA::SimpleDMA(int channel) { + this->channel(channel); + + //Power up + LPC_SC->PCONP |= 1<<29; + LPC_GPDMA->DMACConfig = 1; + trigger(Trigger_ALWAYS); + + NVIC_SetVector(DMA_IRQn, (uint32_t)&irq_handler0); + NVIC_EnableIRQ(DMA_IRQn); +} + +int SimpleDMA::start(int length) { + if (auto_channel) + _channel = getFreeChannel(); + else + while(isBusy()); + + uint32_t control = (source_inc << 26) | (destination_inc << 27) | (1UL << 31); + switch (source_size) { + case 16: + control |= (1<<18) | (length >> 1); + break; + case 32: + control |= (2<<18) | (length >> 2); + break; + default: + control |= length; + } + switch (destination_size) { + case 16: + control |= (1<<21); + break; + case 32: + control |= (2<<21); + break; + } + + LPC_GPDMACH[_channel]->DMACCSrcAddr = _source; + LPC_GPDMACH[_channel]->DMACCDestAddr = _destination; + LPC_GPDMACH[_channel]->DMACCLLI = 0; + LPC_GPDMACH[_channel]->DMACCControl = control; //Enable interrupt also + + irq_owner[_channel] = this; + + if (_trigger != Trigger_ALWAYS) { + if (_trigger & 16) + LPC_SC->DMAREQSEL |= 1 << (_trigger - 24); + else + LPC_SC->DMAREQSEL &= ~(1 << (_trigger - 24)); + + LPC_GPDMACH[_channel]->DMACCConfig = ((_trigger & 15) << 1) + ((_trigger & 15) << 6) + (getTransferType(_trigger, _source, _destination) << 11) + (1<<15) +1; //Both parts of the transfer get triggered at same time + } else + LPC_GPDMACH[_channel]->DMACCConfig = (getTransferType(_trigger, _source, _destination) << 11) + 1 + (1<<15); //Enable channel + + return 0; +} + +bool SimpleDMA::isBusy( int channel ) { + if (channel == -1) + channel = _channel; + return (LPC_GPDMA->DMACEnbldChns & (1<<channel)); +} + +void SimpleDMA::irq_handler0(void) { + while(LPC_GPDMA->DMACIntTCStat != 0) { + + uint32_t intloc = 31 - __CLZ(LPC_GPDMA->DMACIntTCStat & 0xFF); + if (irq_owner[intloc]!=NULL) + irq_owner[intloc]->irq_handler(); + } +} + +void SimpleDMA::irq_handler(void) { + LPC_GPDMA->DMACIntTCClear = 1<<_channel; + _callback.call(); +} + +uint32_t getTransferType(SimpleDMA_Trigger trig, uint32_t source, uint32_t destination) { + //If it is always, simply put it on memory-to-memory + if (trig == Trigger_ALWAYS) + return 0; + else if ((source >> 28) == 0 || (source >> 28) == 1) { //if source is RAM/Flash + if ((destination >> 28) == 0 || (destination >> 28) == 1) //if destination is RAM/flash + return 3; //Return p2p for m2m with a trigger (since I have no idea wtf you are trying to do) + else + return 1; //Source is memory, destination is peripheral, so m2p + } + else { + if ((destination >> 28) == 0 || (destination >> 28)) + return 2; //Source is peripheral, destination is memory + else + return 3; //Both source and destination are peripherals + } + +} +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SimpleDMA_LPC1768.h Sat Jan 04 14:42:33 2014 +0000 @@ -0,0 +1,34 @@ +#ifdef TARGET_LPC1768 + +#define DMA_CHANNELS 8 +#define DMA_IRQS 1 + +enum SimpleDMA_Trigger { + Trigger_ALWAYS = -1, + Trigger_SSP0_TX, + Trigger_SSP0_RX, + Trigger_SSP1_TX, + Trigger_SSP1_RX, + Trigger_ADC, + Trigger_I2S0, + Trigger_I2S1, + Trigger_DAC, + Trigger_UART0_TX, + Trigger_UART0_RX, + Trigger_UART1_TX, + Trigger_UART1_RX, + Trigger_UART2_TX, + Trigger_UART2_RX, + Trigger_UART3_TX, + Trigger_UART3_RX, + Trigger_MATCH0_0 = 24, + Trigger_MATCH0_1, + Trigger_MATCH1_0, + Trigger_MATCH1_1, + Trigger_MATCH2_0, + Trigger_MATCH2_1, + Trigger_MATCH3_0, + Trigger_MATCH3_1 +}; + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SimpleDMA_common.cpp Sat Jan 04 14:42:33 2014 +0000 @@ -0,0 +1,25 @@ +#include "SimpleDMA.h" + +void SimpleDMA::channel(int chan) { + if (chan == -1) { + auto_channel = true; + _channel = 0; + } else { + auto_channel = false; + if (chan >= 0 && chan < DMA_CHANNELS) + _channel = chan; + else + _channel = DMA_CHANNELS-1; + } +} + +int SimpleDMA::getFreeChannel(void) { + int retval = 0; + while(1) { + if (!isBusy(retval)) + return retval; + retval++; + if (retval >= DMA_CHANNELS) + retval = 0; + } +} \ No newline at end of file