diff --git a/_examples/display_er_oledm015/display_er_oledm015.cpp b/_examples/display_er_oledm015/display_er_oledm015.cpp
index edbc4cc09dfbdbef95c5b9550cb7b0bb131dfd44..69554bea0a8a34bfdbba205f1a51a65221cf2ceb 100644
--- a/_examples/display_er_oledm015/display_er_oledm015.cpp
+++ b/_examples/display_er_oledm015/display_er_oledm015.cpp
@@ -27,6 +27,8 @@
 
 #include "display_er_oledm015.h"
 #include <miosix.h>
+#include <kernel/scheduler/scheduler.h>
+#include <interfaces/endianness.h>
 #include <algorithm>
 #include <line.h>
 
@@ -85,6 +87,83 @@ static void spi1waitCompletion()
     unused=SPI1->SR;
 }
 
+/**
+ * DMA TX end of transfer
+ * NOTE: conflicts with SDIO driver but this board does not have an SD card
+ */
+void __attribute__((naked)) DMA2_Stream3_IRQHandler()
+{
+    saveContext();
+    asm volatile("bl _Z20SPI1txDmaHandlerImplv");
+    restoreContext();
+}
+
+static Thread *waiting=nullptr;
+static bool error;
+
+void __attribute__((used)) SPI1txDmaHandlerImpl()
+{
+    if(DMA2->LISR & (DMA_LISR_TEIF3 | DMA_LISR_DMEIF3 | DMA_LIFCR_CFEIF3))
+        error=true;
+    DMA2->LIFCR=DMA_LIFCR_CTCIF3
+              | DMA_LIFCR_CTEIF3
+              | DMA_LIFCR_CDMEIF3
+              | DMA_LIFCR_CFEIF3;
+    waiting->IRQwakeup();
+    if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
+        Scheduler::IRQfindNextThread();
+    waiting=nullptr;
+}
+
+static void spi1SendDMA(const Color *data, int size)
+{
+    error=false;
+    unsigned short tempCr1=SPI1->CR1;
+    SPI1->CR1=0;
+    SPI1->CR2=SPI_CR2_TXDMAEN;
+    SPI1->CR1=tempCr1;
+    
+    waiting=Thread::getCurrentThread();
+    NVIC_ClearPendingIRQ(DMA2_Stream3_IRQn);
+    NVIC_SetPriority(DMA2_Stream3_IRQn,10);//Low priority for DMA
+    NVIC_EnableIRQ(DMA2_Stream3_IRQn);
+
+    DMA2_Stream3->CR=0;
+    DMA2_Stream3->PAR=reinterpret_cast<unsigned int>(&SPI1->DR);
+    DMA2_Stream3->M0AR=reinterpret_cast<unsigned int>(data);
+    DMA2_Stream3->NDTR=2*size; //Size is at the peripheral side (8bit)
+    DMA2_Stream3->FCR=DMA_SxFCR_FEIE
+                    | DMA_SxFCR_DMDIS;
+    DMA2_Stream3->CR=DMA_SxCR_CHSEL_0 //Channel 3 SPI1
+                   | DMA_SxCR_CHSEL_1
+                   //| DMA_SxCR_MSIZE_0 //Memory size 16 bit
+                   | DMA_SxCR_MINC    //Increment memory pointer
+                   | DMA_SxCR_DIR_0   //Memory to peripheral
+                   | DMA_SxCR_TCIE    //Interrupt on transfer complete
+                   | DMA_SxCR_TEIE    //Interrupt on transfer error
+                   | DMA_SxCR_DMEIE   //Interrupt on direct mode error
+                   | DMA_SxCR_EN;     //Start DMA
+    
+    {
+        FastInterruptDisableLock dLock;
+        while(waiting!=nullptr)
+        {
+            waiting->IRQwait();
+            {
+                FastInterruptEnableLock eLock(dLock);
+                Thread::yield();
+            }
+        }
+    }
+                
+    NVIC_DisableIRQ(DMA2_Stream3_IRQn);
+    spi1waitCompletion();
+    SPI1->CR1=0;
+    SPI1->CR2=0;
+    SPI1->CR1=tempCr1;
+    //if(error) puts("SPI1 DMA tx failed"); //TODO: look into why this fails
+}
+
 /**
  * Send a command to the display
  * \param c command
@@ -174,7 +253,7 @@ static inline void imageWindow(Point p1, Point p2)
 
 namespace mxgui {
 
-DisplayErOledm015::DisplayErOledm015() : buffer(nullptr)
+DisplayErOledm015::DisplayErOledm015() : buffer(nullptr), buffer2(nullptr)
 {
     {
         FastInterruptDisableLock dLock;
@@ -318,14 +397,21 @@ void DisplayErOledm015::line(Point a, Point b, Color color)
 
 void DisplayErOledm015::scanLine(Point p, const Color *colors, unsigned short length)
 {
-    imageWindow(p,Point(width-1,p.y()));
-    doBeginPixelWrite();
-    for(int i=0;i<length;i++) doWritePixel(colors[i]);
-    doEndPixelWrite();
+    if(buffer2==nullptr) buffer2=new Color[buffer2Size];
+    length=min<unsigned short>(length,width-p.x());
+    imageWindow(p,Point(length-1,p.y()));
+    cmd(0x5c);
+    dc::high();
+    cs::low();
+    for(int i=0;i<length;i++) buffer2[i]=toBigEndian16(colors[i]);
+    spi1SendDMA(buffer2,length);
+    cs::high();
+    delayUs(1);
 }
 
 Color *DisplayErOledm015::getScanLineBuffer()
 {
+    //getWidth() would be enough as size, but we reuse the buffer for DMA
     if(buffer==nullptr) buffer=new Color[getWidth()];
     return buffer;
 }
@@ -337,7 +423,32 @@ void DisplayErOledm015::scanLineBuffer(Point p, unsigned short length)
 
 void DisplayErOledm015::drawImage(Point p, const ImageBase& img)
 {
-    img.draw(*this,p);
+    const Color *imgData=img.getData();
+    if(imgData!=0)
+    {
+        if(buffer2==nullptr) buffer2=new Color[buffer2Size];
+        short int xEnd=p.x()+img.getWidth()-1;
+        short int yEnd=p.y()+img.getHeight()-1;
+        imageWindow(p,Point(xEnd,yEnd));
+        cmd(0x5c);
+        dc::high();
+        cs::low();
+        //Unfortunately the DMA requires the endianness to be swapped, the
+        //pointer we get is read-only (can be in flash), and we may not have
+        //enough memory to allocate a large enough buffer to hold the entire
+        //image, so we'll have to split it in chunks
+        int imgSize=img.getHeight()*img.getWidth();
+        while(imgSize>0)
+        {
+            int chunkSize=min(imgSize,buffer2Size);
+            for(int i=0;i<chunkSize;i++) buffer2[i]=toBigEndian16(imgData[i]);
+            spi1SendDMA(buffer2,chunkSize);
+            imgSize-=chunkSize;
+            imgData+=chunkSize;
+        }
+        cs::high();
+        delayUs(1);
+    } else img.draw(*this,p);
 }
 
 void DisplayErOledm015::clippedDrawImage(Point p, Point a, Point b, const ImageBase& img)
diff --git a/_examples/display_er_oledm015/display_er_oledm015.h b/_examples/display_er_oledm015/display_er_oledm015.h
index 33808258a36fa697ff41ae850ff927d9966e08b3..b44f47d63a3dde2af12fbba43362f0054ea7a628 100644
--- a/_examples/display_er_oledm015/display_er_oledm015.h
+++ b/_examples/display_er_oledm015/display_er_oledm015.h
@@ -297,7 +297,9 @@ private:
     
     static void doEndPixelWrite();
     
-    Color *buffer;             ///< For scanLineBuffer
+    Color *buffer;                    ///< For scanLineBuffer
+    Color *buffer2;                   ///< For DMA transfers
+    static const int buffer2Size=512; ///< DMA buffer size
 };
 
 } //namespace mxgui