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