DMA library for the KL25Z

Dependents:   SimpleDMA_HelloWorld RTOS_SPI spiDMAtest Pinscape_Controller_v1 ... more

Introduction

SimpleDMA is a standard library for different DMA peripherals. Currently the LPC1768, KL46Z and KL25Z are supported. It provided one set of functions for different peripherals. It does not allow for usage of all the advanced functions, partially because the goal was to provide a simple interface, and partially because they are different for different microcontrollers.

Examples

Helloworld: http://mbed.org/users/Sissors/code/SimpleDMA_HelloWorld/

Example in a library (SPI): http://mbed.org/users/Sissors/code/RTOS_SPI/

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