A simple library to access the DMA functionality.

Fork of SimpleDMA by Erik -

Files at this revision

API Documentation at this revision

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

SimpleDMA.h Show annotated file Show diff for this revision Revisions of this file
SimpleDMA_KL25.cpp Show annotated file Show diff for this revision Revisions of this file
SimpleDMA_KL25.h Show annotated file Show diff for this revision Revisions of this file
SimpleDMA_LPC1768.cpp Show annotated file Show diff for this revision Revisions of this file
SimpleDMA_LPC1768.h Show annotated file Show diff for this revision Revisions of this file
SimpleDMA_common.cpp Show annotated file Show diff for this revision Revisions of this file
--- 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