From 31ff82858aa6329dd70af144f3047b2edd9e8b9c Mon Sep 17 00:00:00 2001
From: Federico Terraneo <fede.tft@miosix.org>
Date: Wed, 22 Aug 2018 18:04:35 +0200
Subject: [PATCH] Added two generic display drivers and two drivers for
 monochrome OLED displays

---
 Makefile                                      |   4 +-
 .../display_er_oledm028.cpp                   | 176 ++++++++++
 .../display_er_oledm028/display_er_oledm028.h |  67 ++++
 _examples/display_er_oledm028/main.cpp        |  45 +++
 _examples/display_ly091wg14/main.cpp          |  47 +++
 drivers/display_generic_1bpp.cpp              | 196 +++++++++++
 drivers/display_generic_1bpp.h                | 328 +++++++++++++++++
 drivers/display_generic_4bpp.cpp              | 209 +++++++++++
 drivers/display_generic_4bpp.h                | 330 ++++++++++++++++++
 drivers/display_ly091wg14.h                   | 149 ++++++++
 10 files changed, 1550 insertions(+), 1 deletion(-)
 create mode 100644 _examples/display_er_oledm028/display_er_oledm028.cpp
 create mode 100644 _examples/display_er_oledm028/display_er_oledm028.h
 create mode 100644 _examples/display_er_oledm028/main.cpp
 create mode 100644 _examples/display_ly091wg14/main.cpp
 create mode 100644 drivers/display_generic_1bpp.cpp
 create mode 100644 drivers/display_generic_1bpp.h
 create mode 100644 drivers/display_generic_4bpp.cpp
 create mode 100644 drivers/display_generic_4bpp.h
 create mode 100644 drivers/display_ly091wg14.h

diff --git a/Makefile b/Makefile
index 778c16b..268b3c4 100644
--- a/Makefile
+++ b/Makefile
@@ -34,7 +34,9 @@ drivers/display_bitsboard.cpp          \
 drivers/display_sony-newman.cpp        \
 drivers/event_sony-newman.cpp          \
 drivers/display_stm32f4discovery.cpp   \
-drivers/event_stm32f4discovery.cpp
+drivers/event_stm32f4discovery.cpp     \
+drivers/display_generic_1bpp.cpp       \
+drivers/display_generic_4bpp.cpp
 
 ifeq ("$(VERBOSE)","1")
 Q := 
diff --git a/_examples/display_er_oledm028/display_er_oledm028.cpp b/_examples/display_er_oledm028/display_er_oledm028.cpp
new file mode 100644
index 0000000..fb0ac0f
--- /dev/null
+++ b/_examples/display_er_oledm028/display_er_oledm028.cpp
@@ -0,0 +1,176 @@
+/***************************************************************************
+ *   Copyright (C) 2018 by Terraneo Federico                               *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   As a special exception, if other files instantiate templates or use   *
+ *   macros or inline functions from this file, or you compile this file   *
+ *   and link it with other works to produce a work based on this file,    *
+ *   this file does not by itself cause the resulting work to be covered   *
+ *   by the GNU General Public License. However the source code for this   *
+ *   file must still be made available in accordance with the GNU General  *
+ *   Public License. This exception does not invalidate any other reasons  *
+ *   why a work based on this file might be covered by the GNU General     *
+ *   Public License.                                                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, see <http://www.gnu.org/licenses/>   *
+ ***************************************************************************/
+
+#include "display_er_oledm028.h"
+#include "miosix.h"
+#include <algorithm>
+
+using namespace std;
+using namespace miosix;
+
+#ifndef _BOARD_STM32F4DISCOVERY
+#warning "The SPI driver has only been tested on an STM32F4DISCOVERY"
+#endif
+
+//Display connection
+
+typedef Gpio<GPIOB_BASE,3> sck;
+typedef Gpio<GPIOB_BASE,4> miso; //Not used
+typedef Gpio<GPIOB_BASE,5> mosi;
+typedef Gpio<GPIOB_BASE,7> cs;
+
+typedef Gpio<GPIOB_BASE,8> dc;
+typedef Gpio<GPIOB_BASE,15> reset;
+
+static void spiInit()
+{
+    sck::mode(Mode::ALTERNATE);
+    sck::alternateFunction(6);
+    mosi::mode(Mode::ALTERNATE);
+    mosi::alternateFunction(6);
+    cs::mode(Mode::OUTPUT);
+    cs::high();
+
+    {
+        FastInterruptDisableLock dLock;
+        RCC->APB1ENR |= RCC_APB1ENR_SPI3EN;
+        RCC_SYNC();
+    }
+    //Master mode no hardware CS pin
+    //Note: SPI3 is attached on the 42MHz APB2 bus, so the clock is set
+    //to APB2/2/2=10.5MHz. This overclocking the SSD1332 by 500KHz
+    SPI3->CR1=SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_MSTR | SPI_CR1_BR_0;
+    SPI3->CR2=0;
+    SPI3->CR1 |= SPI_CR1_SPE; //Set enable bit
+}
+
+static unsigned char spiSendRecv(unsigned char data)
+{
+    SPI3->DR=data;
+    while((SPI3->SR & SPI_SR_RXNE)==0) ;
+    return SPI3->DR;
+}
+
+static void cmd(unsigned char c)
+{
+    dc::low();
+
+    cs::low();
+    delayUs(1);
+    spiSendRecv(c);
+    cs::high();
+    delayUs(1);
+}
+
+static void data(unsigned char d)
+{
+    dc::high();
+
+    cs::low();
+    delayUs(1);
+    spiSendRecv(d);
+    cs::high();
+    delayUs(1);
+}
+
+//
+// class DisplayErOledm028
+//
+
+namespace mxgui {
+
+DisplayErOledm028::DisplayErOledm028() : DisplayGeneric4BPP(256,64)
+{
+    spiInit();
+    dc::mode(Mode::OUTPUT);
+    reset::mode(Mode::OUTPUT);
+
+    reset::high();
+    Thread::sleep(1);
+    reset::low();
+    delayUs(100);
+    reset::high();
+    delayUs(100);
+
+    cmd(0xfd); data(0x12);             // Disable command lock
+    cmd(0xae);                         // Display off
+    cmd(0xb3); data(0x91);             // Oscillator settings 0x9, divide by 2
+    cmd(0xca); data(0x3f);             // Mux ratio 64
+    cmd(0xa2); data(0x00);             // Display offset 0
+    cmd(0xa1); data(0x00);             // Display start line 0
+    cmd(0xa0); data(0x14); data(0x11); // Remap: dual com enabled, nibble remap
+    cmd(0xab); data(0x01);             // Select internal VDD
+    cmd(0xb4); data(0xa0); data(0xfd); // ?
+    cmd(0xc1); data(0x80);             // Contrast current 0x80
+    cmd(0xc7); data(0x0f);             // Maximum brightness
+    cmd(0xb1); data(0xe2);             // Phase length
+    cmd(0xd1); data(0x82); data(0x20); // ?
+    cmd(0xbb); data(0x1f);             // Precharge voltage
+    cmd(0xb6); data(0x08);             // Second precharge period
+    cmd(0xbe); data(0x07);             // VCOMH
+    cmd(0xa6);                         // Normal display mode
+    clear(0);
+    update();
+    cmd(0xaf);                         // Display on
+}
+
+void DisplayErOledm028::doTurnOn()
+{
+    cmd(0xaf);
+}
+
+void DisplayErOledm028::doTurnOff()
+{
+    cmd(0xae);
+}
+
+void DisplayErOledm028::doSetBrightness(int brt)
+{
+    int brightness=max(0,min(15,brt/6));
+    cmd(0xc7); data(brightness);
+}
+
+void DisplayErOledm028::update()
+{
+    static const unsigned char xStart=28;
+    static const unsigned char xEnd=91;
+    static const unsigned char yStart=0;
+    static const unsigned char yEnd=63;
+    
+    cmd(0x15); data(xStart); data(xEnd);
+    cmd(0x75); data(yStart); data(yEnd);
+    cmd(0x5c);
+
+    dc::high();
+    cs::low();
+    delayUs(1);
+    for(int i=0;i<fbSize;i++) spiSendRecv(backbuffer[i]);
+    cs::high();
+    delayUs(1);
+}
+
+} //namespace mxgui
diff --git a/_examples/display_er_oledm028/display_er_oledm028.h b/_examples/display_er_oledm028/display_er_oledm028.h
new file mode 100644
index 0000000..a9a3536
--- /dev/null
+++ b/_examples/display_er_oledm028/display_er_oledm028.h
@@ -0,0 +1,67 @@
+/***************************************************************************
+ *   Copyright (C) 2018 by Terraneo Federico                               *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   As a special exception, if other files instantiate templates or use   *
+ *   macros or inline functions from this file, or you compile this file   *
+ *   and link it with other works to produce a work based on this file,    *
+ *   this file does not by itself cause the resulting work to be covered   *
+ *   by the GNU General Public License. However the source code for this   *
+ *   file must still be made available in accordance with the GNU General  *
+ *   Public License. This exception does not invalidate any other reasons  *
+ *   why a work based on this file might be covered by the GNU General     *
+ *   Public License.                                                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, see <http://www.gnu.org/licenses/>   *
+ ***************************************************************************/
+
+#pragma once
+
+#include "mxgui/drivers/display_generic_4bpp.h"
+
+namespace mxgui {
+
+class DisplayErOledm028 : public DisplayGeneric4BPP
+{
+public:
+    /*
+     * Constructor
+     */
+    DisplayErOledm028();
+        
+    /**
+     * Turn the display On after it has been turned Off.
+     * Display initial state is On.
+     */
+    void doTurnOn() override;
+
+    /**
+     * Turn the display Off. It can be later turned back On.
+     */
+    void doTurnOff() override;
+    
+    /**
+     * Set display brightness. Depending on the underlying driver,
+     * may do nothing.
+     * \param brt from 0 to 100
+     */
+    void doSetBrightness(int brt) override;
+    
+    /**
+     * Make all changes done to the display since the last call to update()
+     * visible. Backends that require it may override this.
+     */
+    void update() override;
+};
+
+} //namespace mxgui
diff --git a/_examples/display_er_oledm028/main.cpp b/_examples/display_er_oledm028/main.cpp
new file mode 100644
index 0000000..3d56c86
--- /dev/null
+++ b/_examples/display_er_oledm028/main.cpp
@@ -0,0 +1,45 @@
+
+#include <cstdio>
+#include "miosix.h"
+#include "mxgui/display.h"
+#include "mxgui/misc_inst.h"
+#include "display_er_oledm028.h"
+
+using namespace std;
+using namespace miosix;
+using namespace mxgui;
+
+//NOTE: to try this example, you have to connect the display to the board as
+//written in display_er_oledm028.cpp
+
+/*
+ * On boards which do not have a built-in display, MXGUI requires you to
+ * implement the registerDisplayHook callback to tell MXGUI which display to
+ * use. If you want to adapt this example for a board that already has a
+ * display, you can register a secondary display in the main with the following
+ * line
+ * \code
+ * DisplayManager::instance().registerDisplay(new DisplayLy091wg14<sda,scl,reset>);
+ * \endcode
+ * And then get the display with DisplayManager::instance().getDisplay(1).
+ * Note that 0 is the default display, 1 would be the secondary one.
+ */
+namespace mxgui {
+void registerDisplayHook(DisplayManager& dm)
+{
+    dm.registerDisplay(new DisplayErOledm028);
+}
+} //namespace mxgui
+
+int main()
+{
+    auto& display=DisplayManager::instance().getDisplay();
+    {
+        DrawingContext dc(display);
+        dc.setFont(droid21);
+        dc.write(Point(0,0),"Miosix OS");
+        dc.setFont(tahoma);
+        dc.write(Point(0,droid21.getHeight()),"MXGUI graphics library");
+    }
+   for(;;) Thread::sleep(100);
+}
diff --git a/_examples/display_ly091wg14/main.cpp b/_examples/display_ly091wg14/main.cpp
new file mode 100644
index 0000000..c6ecc5a
--- /dev/null
+++ b/_examples/display_ly091wg14/main.cpp
@@ -0,0 +1,47 @@
+
+#include <cstdio>
+#include "miosix.h"
+#include "mxgui/display.h"
+#include "mxgui/misc_inst.h"
+#include "mxgui/drivers/display_ly091wg14.h"
+
+using namespace std;
+using namespace miosix;
+using namespace mxgui;
+
+// Configure these GPIOs to match how you connected the display
+typedef Gpio<GPIOA_BASE,1> reset;
+typedef Gpio<GPIOA_BASE,2> scl;
+typedef Gpio<GPIOA_BASE,3> sda;
+
+/*
+ * On boards which do not have a built-in display, MXGUI requires you to
+ * implement the registerDisplayHook callback to tell MXGUI which display to
+ * use. If you want to adapt this example for a board that already has a
+ * display, you can register a secondary display in the main with the following
+ * line
+ * \code
+ * DisplayManager::instance().registerDisplay(new DisplayLy091wg14<sda,scl,reset>);
+ * \endcode
+ * And then get the display with DisplayManager::instance().getDisplay(1).
+ * Note that 0 is the default display, 1 would be the secondary one.
+ */
+namespace mxgui {
+void registerDisplayHook(DisplayManager& dm)
+{
+    dm.registerDisplay(new DisplayLy091wg14<sda,scl,reset>);
+}
+} //namespace mxgui
+
+int main()
+{
+    auto& display=DisplayManager::instance().getDisplay();
+    {
+        DrawingContext dc(display);
+        dc.setFont(droid21);
+        dc.write(Point(0,0),"Miosix OS");
+        dc.setFont(tahoma);
+        dc.write(Point(0,32-tahoma.getHeight()),"MXGUI graphics library");
+    }
+    for(;;) Thread::sleep(100);
+}
diff --git a/drivers/display_generic_1bpp.cpp b/drivers/display_generic_1bpp.cpp
new file mode 100644
index 0000000..39bf2bd
--- /dev/null
+++ b/drivers/display_generic_1bpp.cpp
@@ -0,0 +1,196 @@
+/***************************************************************************
+ *   Copyright (C) 2018 by Terraneo Federico                               *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   As a special exception, if other files instantiate templates or use   *
+ *   macros or inline functions from this file, or you compile this file   *
+ *   and link it with other works to produce a work based on this file,    *
+ *   this file does not by itself cause the resulting work to be covered   *
+ *   by the GNU General Public License. However the source code for this   *
+ *   file must still be made available in accordance with the GNU General  *
+ *   Public License. This exception does not invalidate any other reasons  *
+ *   why a work based on this file might be covered by the GNU General     *
+ *   Public License.                                                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, see <http://www.gnu.org/licenses/>   *
+ ***************************************************************************/
+
+#include "display_generic_1bpp.h"
+#include "font.h"
+#include "image.h"
+#include "misc_inst.h"
+#include "line.h"
+#include <algorithm>
+
+using namespace std;
+
+namespace mxgui {
+
+//
+// Class DisplayGeneric1BPP
+//
+
+DisplayGeneric1BPP::DisplayGeneric1BPP(short width, short height)
+    : width(width), height(height), fbSize(width*height/8),
+      backbuffer(new unsigned char[fbSize]), buffer(new Color[width]) {}
+
+pair<short int, short int> DisplayGeneric1BPP::doGetSize() const
+{
+    return make_pair(height,width);
+}
+
+void DisplayGeneric1BPP::write(Point p, const char *text)
+{
+    font.draw(*this,textColor,p,text);
+}
+
+void DisplayGeneric1BPP::clippedWrite(Point p, Point a, Point b, const char *text)
+{
+    font.clippedDraw(*this,textColor,p,a,b,text);
+}
+
+void DisplayGeneric1BPP::clear(Color color)
+{
+    memset(backbuffer,conv2(color),fbSize);
+}
+
+void DisplayGeneric1BPP::clear(Point p1, Point p2, Color color)
+{
+    if(p1.x()<0 || p2.x()<p1.x() || p2.x()>=width
+     ||p1.y()<0 || p2.y()<p1.y() || p2.y()>=height) return;
+    
+    //Vertical line is the most optimized
+    for(short x=p1.x();x<=p2.x();x++) line(Point(x,p1.y()),Point(x,p2.y()),color);
+}
+
+void DisplayGeneric1BPP::beginPixel() {}
+
+void DisplayGeneric1BPP::setPixel(Point p, Color color)
+{
+    int offset=p.x()+(p.y()/8)*width;
+    if(offset<0 || offset>=fbSize) return;
+    //TODO: optimize with bit banding
+    if(color) backbuffer[offset] |=  (1<<(p.y() & 0x7));
+    else      backbuffer[offset] &= ~(1<<(p.y() & 0x7));
+}
+
+void DisplayGeneric1BPP::line(Point a, Point b, Color color)
+{
+    //Horizontal line speed optimization
+    if(a.y()==b.y())
+    {
+        short minx=min(a.x(),b.x());
+        short maxx=max(a.x(),b.x());
+        if(minx<0 || maxx>=width || a.y()<0 || a.y()>=height) return;
+        for(short x=minx;x<=maxx;x++) doSetPixel(x,a.y(),color);
+        return;
+    }
+    //Vertical line speed optimization
+    if(a.x()==b.x())
+    {
+        short miny=min(a.y(),b.y());
+        short maxy=max(a.y(),b.y())+1;
+        if(a.x()<0 || a.x()>=width || miny<0 || maxy>height) return;
+        if(maxy-miny<8) for(short y=miny;y<maxy;y++) doSetPixel(a.x(),y,color);
+        else {
+            short minyaligned=(miny+7) & ~0x7;
+            short maxyaligned=maxy & ~0x7;
+            for(short y=miny;y<minyaligned;y++) doSetPixel(a.x(),y,color);
+            unsigned char *ptr=backbuffer+a.x()+(minyaligned/8)*width;
+            for(short y=minyaligned;y<maxyaligned;y+=8)
+            {
+                *ptr=conv2(color);
+                ptr+=width;
+            }
+            for(short y=maxyaligned;y<maxy;y++) doSetPixel(a.x(),y,color);
+        }
+        return;
+    }
+    //General case
+    Line::draw(*this,a,b,color);
+}
+
+void DisplayGeneric1BPP::scanLine(Point p, const Color *colors, unsigned short length)
+{
+    if(p.x()<0 || static_cast<int>(p.x())+static_cast<int>(length)>width
+        ||p.y()<0 || p.y()>=height) return;
+    for(short x=0;x<length;x++) doSetPixel(p.x()+x,p.y(),colors[x]);
+}
+
+Color *DisplayGeneric1BPP::getScanLineBuffer()
+{
+    return buffer;
+}
+
+void DisplayGeneric1BPP::scanLineBuffer(Point p, unsigned short length)
+{
+    scanLine(p,buffer,length);
+}
+
+void DisplayGeneric1BPP::drawImage(Point p, const ImageBase& img)
+{
+    short int xEnd=p.x()+img.getWidth()-1;
+    short int yEnd=p.y()+img.getHeight()-1;
+    if(p.x()<0 || p.y()<0 || xEnd<p.x() || yEnd<p.y()
+        ||xEnd >= width || yEnd >= height) return;
+
+//    const unsigned short *imgData=img.getData();
+//    if(imgData!=0)
+//    {
+//        //TODO Optimized version for in-memory images
+//    } else
+    img.draw(*this,p);
+}
+
+void DisplayGeneric1BPP::clippedDrawImage(Point p, Point a, Point b, const ImageBase& img)
+{
+//    const unsigned short *imgData=img.getData();
+//    if(imgData!=0)
+//    {
+//        //TODO Optimized version for in-memory images
+//    } else
+    img.clippedDraw(*this,p,a,b);
+}
+
+void DisplayGeneric1BPP::drawRectangle(Point a, Point b, Color c)
+{
+    line(a,Point(b.x(),a.y()),c);
+    line(Point(b.x(),a.y()),b,c);
+    line(b,Point(a.x(),b.y()),c);
+    line(Point(a.x(),b.y()),a,c);
+}
+
+DisplayGeneric1BPP::pixel_iterator DisplayGeneric1BPP::begin(Point p1, Point p2,
+        IteratorDirection d)
+{
+    bool fail=false;
+    if(p1.x()<0 || p1.y()<0 || p2.x()<0 || p2.y()<0) fail=true;
+    if(p1.x()>=width || p1.y()>=height || p2.x()>=width || p2.y()>=height) fail=true;
+    if(p2.x()<p1.x() || p2.y()<p1.y()) fail=true;
+    if(fail)
+    {
+        //Return invalid (dummy) iterators
+        this->last=pixel_iterator();
+        return this->last;
+    }
+
+    //Set the last iterator to a suitable one-past-the last value
+    if(d==DR) this->last=pixel_iterator(Point(p2.x()+1,p1.y()),p2,d,this);
+    else this->last=pixel_iterator(Point(p1.x(),p2.y()+1),p2,d,this);
+
+    return pixel_iterator(p1,p2,d,this);
+}
+
+DisplayGeneric1BPP::~DisplayGeneric1BPP() {}
+
+} //namespace mxgui
diff --git a/drivers/display_generic_1bpp.h b/drivers/display_generic_1bpp.h
new file mode 100644
index 0000000..531561e
--- /dev/null
+++ b/drivers/display_generic_1bpp.h
@@ -0,0 +1,328 @@
+/***************************************************************************
+ *   Copyright (C) 2018 by Terraneo Federico                               *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   As a special exception, if other files instantiate templates or use   *
+ *   macros or inline functions from this file, or you compile this file   *
+ *   and link it with other works to produce a work based on this file,    *
+ *   this file does not by itself cause the resulting work to be covered   *
+ *   by the GNU General Public License. However the source code for this   *
+ *   file must still be made available in accordance with the GNU General  *
+ *   Public License. This exception does not invalidate any other reasons  *
+ *   why a work based on this file might be covered by the GNU General     *
+ *   Public License.                                                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, see <http://www.gnu.org/licenses/>   *
+ ***************************************************************************/
+
+#pragma once
+
+#include <config/mxgui_settings.h>
+#include "display.h"
+#include "point.h"
+#include "color.h"
+#include "iterator_direction.h"
+#include <cstdio>
+#include <cstring>
+#include <algorithm>
+
+namespace mxgui {
+
+/**
+ * Generic driver for a 1 bit per pixel display, like the OLED displays based
+ * on the SSD1306 controller.
+ * To use this class for a display driver, you have to derive from this class
+ * and provide:
+ * - a constructor that shall initialize and turn on the display, following
+ *   whatever initialization sequence is recomended by the manufacturer
+ * - the implementation for the doTurnOn(), doTurnOff() and doSetBrightness()
+ *   member functions
+ * - the update() member function which shall copy the backbuffer which is in
+ *   the microcontroller memory to the display memory
+ */
+class DisplayGeneric1BPP : public Display
+{
+public:
+    /**
+     * Constructor.
+     * \param width display width
+     * \param height display height
+     */
+    DisplayGeneric1BPP(short width, short height);
+    
+    /**
+     * \return a pair with the display height and width
+     */
+    std::pair<short int, short int> doGetSize() const override;
+
+    /**
+     * Write text to the display. If text is too long it will be truncated
+     * \param p point where the upper left corner of the text will be printed
+     * \param text, text to print.
+     */
+    void write(Point p, const char *text) override;
+
+    /**
+     * Write part of text to the display
+     * \param p point of the upper left corner where the text will be drawn.
+     * Negative coordinates are allowed, as long as the clipped view has
+     * positive or zero coordinates
+     * \param a Upper left corner of clipping rectangle
+     * \param b Lower right corner of clipping rectangle
+     * \param text text to write
+     */
+    void clippedWrite(Point p, Point a, Point b, const char *text) override;
+
+    /**
+     * Clear the Display. The screen will be filled with the desired color
+     * \param color fill color
+     */
+    void clear(Color color) override;
+
+    /**
+     * Clear an area of the screen
+     * \param p1 upper left corner of area to clear
+     * \param p2 lower right corner of area to clear
+     * \param color fill color
+     */
+    void clear(Point p1, Point p2, Color color) override;
+
+    /**
+     * This backend does not require it, so it is a blank.
+     */
+    void beginPixel() override;
+
+    /**
+     * Draw a pixel with desired color. You have to call beginPixel() once
+     * before calling setPixel()
+     * \param p point where to draw pixel
+     * \param color pixel color
+     */
+    void setPixel(Point p, Color color) override;
+
+    /**
+     * Draw a line between point a and point b, with color c
+     * \param a first point
+     * \param b second point
+     * \param c line color
+     */
+    void line(Point a, Point b, Color color) override;
+
+    /**
+     * Draw an horizontal line on screen.
+     * Instead of line(), this member function takes an array of colors to be
+     * able to individually set pixel colors of a line.
+     * \param p starting point of the line
+     * \param colors an array of pixel colors whoase size must be b.x()-a.x()+1
+     * \param length length of colors array.
+     * p.x()+length must be <= display.width()
+     */
+    void scanLine(Point p, const Color *colors, unsigned short length) override;
+    
+    /**
+     * \return a buffer of length equal to this->getWidth() that can be used to
+     * render a scanline.
+     */
+    Color *getScanLineBuffer() override;
+    
+    /**
+     * Draw the content of the last getScanLineBuffer() on an horizontal line
+     * on the screen.
+     * \param p starting point of the line
+     * \param length length of colors array.
+     * p.x()+length must be <= display.width()
+     */
+    void scanLineBuffer(Point p, unsigned short length) override;
+
+    /**
+     * Draw an image on the screen
+     * \param p point of the upper left corner where the image will be drawn
+     * \param i image to draw
+     */
+    void drawImage(Point p, const ImageBase& img) override;
+
+    /**
+     * Draw part of an image on the screen
+     * \param p point of the upper left corner where the image will be drawn.
+     * Negative coordinates are allowed, as long as the clipped view has
+     * positive or zero coordinates
+     * \param a Upper left corner of clipping rectangle
+     * \param b Lower right corner of clipping rectangle
+     * \param i Image to draw
+     */
+    void clippedDrawImage(Point p, Point a, Point b, const ImageBase& img) override;
+
+    /**
+     * Draw a rectangle (not filled) with the desired color
+     * \param a upper left corner of the rectangle
+     * \param b lower right corner of the rectangle
+     * \param c color of the line
+     */
+    void drawRectangle(Point a, Point b, Color c) override;
+    
+    /**
+     * Pixel iterator. A pixel iterator is an output iterator that allows to
+     * define a window on the display and write to its pixels.
+     */
+    class pixel_iterator
+    {
+    public:
+        /**
+         * Default constructor, results in an invalid iterator.
+         */
+        pixel_iterator() : x(0), y(0), disp(nullptr) {}
+
+        /**
+         * Set a pixel and move the pointer to the next one
+         * \param color color to set the current pixel
+         * \return a reference to this
+         */
+        pixel_iterator& operator= (Color color)
+        {
+            if(disp==nullptr) return *this;
+            disp->doSetPixel(x,y,color);
+            
+            if(direction==RD)
+            {
+                if(++x>xe)
+                {
+                    x=xs;
+                    y++;
+                }
+            } else {
+                if(++y>ye)
+                {
+                    y=ys;
+                    x++;
+                }
+            }
+            return *this;
+        }
+
+        /**
+         * Compare two pixel_iterators for equality.
+         * They are equal if they point to the same location.
+         */
+        bool operator== (const pixel_iterator& itr)
+        {
+            return this->x==itr.x && this->y==itr.y;
+        }
+
+        /**
+         * Compare two pixel_iterators for inequality.
+         * They different if they point to different locations.
+         */
+        bool operator!= (const pixel_iterator& itr)
+        {
+            return this->x!=itr.x || this->y!=itr.y;
+        }
+
+        /**
+         * \return a reference to this.
+         */
+        pixel_iterator& operator* () { return *this; }
+
+        /**
+         * \return a reference to this. Does not increment pixel pointer.
+         */
+        pixel_iterator& operator++ ()  { return *this; }
+
+        /**
+         * \return a reference to this. Does not increment pixel pointer.
+         */
+        pixel_iterator& operator++ (int)  { return *this; }
+        
+        /**
+         * Must be called if not all pixels of the required window are going
+         * to be written.
+         */
+        void invalidate() {}
+
+    private:
+        /**
+         * Constructor
+         * \param start Upper left corner of window
+         * \param end Lower right corner of window
+         * \param direction Iterator direction
+         * \param disp Display we're associated
+         */
+        pixel_iterator(Point start, Point end, IteratorDirection direction,
+                       DisplayGeneric1BPP *disp) : direction(direction), disp(disp)
+        {
+            x=xs=start.x();
+            y=ys=start.y();
+            xe=end.x();
+            ye=end.y();
+        }
+        
+        short x,xs,xe;
+        short y,ys,ye;
+        IteratorDirection direction;
+        DisplayGeneric1BPP *disp;
+
+        friend class DisplayGeneric1BPP; //Needs access to ctor
+    };
+
+    /**
+     * Specify a window on screen and return an object that allows to write
+     * its pixels.
+     * Note: a call to begin() will invalidate any previous iterator.
+     * \param p1 upper left corner of window
+     * \param p2 lower right corner (included)
+     * \param d increment direction
+     * \return a pixel iterator
+     */
+    pixel_iterator begin(Point p1, Point p2, IteratorDirection d);
+
+    /**
+     * \return an iterator which is one past the last pixel in the pixel
+     * specified by begin. Behaviour is undefined if called before calling
+     * begin()
+     */
+    pixel_iterator end() const { return last; }
+    
+    /**
+     * Destructor
+     */
+    ~DisplayGeneric1BPP();
+    
+protected:
+    
+    //FIXME: ignoring MXGUI_ORIENTATION
+    const short int width;
+    const short int height;
+    
+    const int fbSize; //Framebuffer is 1 bit per pixel
+
+    unsigned char *backbuffer; ///< Display backbuffer (frontbuffer is in the display chip)
+
+private:
+
+    static unsigned char conv2(Color c) { return c ? 0xff : 0; }
+    
+    /**
+     * Non bound checked non virtual setPixel.
+     */
+    void doSetPixel(short x, short y, Color c)
+    {
+        int offset=x+(y/8)*width;
+        //TODO: optimize with bit banding
+        if(c) backbuffer[offset] |=  (1<<(y & 0x7));
+        else  backbuffer[offset] &= ~(1<<(y & 0x7));
+    }
+    
+    Color *buffer;             ///< For scanLineBuffer
+    pixel_iterator last;       ///< Last iterator for end of iteration check
+};
+
+} //namespace mxgui
diff --git a/drivers/display_generic_4bpp.cpp b/drivers/display_generic_4bpp.cpp
new file mode 100644
index 0000000..a5a824e
--- /dev/null
+++ b/drivers/display_generic_4bpp.cpp
@@ -0,0 +1,209 @@
+/***************************************************************************
+ *   Copyright (C) 2018 by Terraneo Federico                               *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   As a special exception, if other files instantiate templates or use   *
+ *   macros or inline functions from this file, or you compile this file   *
+ *   and link it with other works to produce a work based on this file,    *
+ *   this file does not by itself cause the resulting work to be covered   *
+ *   by the GNU General Public License. However the source code for this   *
+ *   file must still be made available in accordance with the GNU General  *
+ *   Public License. This exception does not invalidate any other reasons  *
+ *   why a work based on this file might be covered by the GNU General     *
+ *   Public License.                                                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, see <http://www.gnu.org/licenses/>   *
+ ***************************************************************************/
+
+#include "display_generic_4bpp.h"
+#include "font.h"
+#include "image.h"
+#include "misc_inst.h"
+#include "line.h"
+#include <algorithm>
+
+using namespace std;
+
+namespace mxgui {
+
+//
+// Class DisplayGeneric4BPP
+//
+
+DisplayGeneric4BPP::DisplayGeneric4BPP(short width, short height)
+    : width(width), height(height), fbSize(width*height/2),
+      backbuffer(new unsigned char[fbSize]), buffer(new Color[width])
+{
+    setTextColor(make_pair(Color(0xf),Color(0x0)));
+}
+
+pair<short int, short int> DisplayGeneric4BPP::doGetSize() const
+{
+    return make_pair(height,width);
+}
+
+void DisplayGeneric4BPP::write(Point p, const char *text)
+{
+    font.draw(*this,textColor,p,text);
+}
+
+void DisplayGeneric4BPP::clippedWrite(Point p, Point a, Point b, const char *text)
+{
+    font.clippedDraw(*this,textColor,p,a,b,text);
+}
+
+void DisplayGeneric4BPP::clear(Color color)
+{
+    memset(backbuffer,conv2(color),fbSize);
+}
+
+void DisplayGeneric4BPP::clear(Point p1, Point p2, Color color)
+{
+    if(p1.x()<0 || p2.x()<p1.x() || p2.x()>=width
+     ||p1.y()<0 || p2.y()<p1.y() || p2.y()>=height) return;
+    
+    //Horizontal line is memeset-optimized
+    for(short y=p1.y();y<=p2.y();y++) line(Point(p1.x(),y),Point(p2.x(),y),color);
+}
+
+void DisplayGeneric4BPP::beginPixel() {}
+
+void DisplayGeneric4BPP::setPixel(Point p, Color color)
+{
+    int offset=(p.x()+p.y()*width)/2;
+    if(offset<0 || offset>=fbSize) return;
+    if(p.x() & 1)
+        backbuffer[offset]=(backbuffer[offset] & 0b11110000) | conv1(color);
+    else
+        backbuffer[offset]=(backbuffer[offset] & 0b00001111) | (conv1(color)<<4);
+}
+
+void DisplayGeneric4BPP::line(Point a, Point b, Color color)
+{
+    //Horizontal line speed optimization
+    if(a.y()==b.y())
+    {
+        short minx=min(a.x(),b.x());
+        short maxx=max(a.x(),b.x())+1;
+        if(minx<0 || maxx>width || a.y()<0 || a.y()>=height) return;
+        unsigned char c1=conv1(color), c2=conv2(color);
+        if(minx & 1) doSetPixel(minx,a.y(),c1);
+        short minxaligned=minx & 1 ? minx+1 : minx;
+        short maxxaligned=maxx & 1 ? maxx-1 : maxx;
+        memset(backbuffer+(minxaligned+width*a.y())/2,c2,(maxxaligned-minxaligned)/2);
+        if(maxx & 1) doSetPixel(maxx-1,a.y(),c1);
+        return;
+    }
+    //Vertical line speed optimization
+    if(a.x()==b.x())
+    {
+        short miny=min(a.y(),b.y());
+        short maxy=max(a.y(),b.y());
+        if(a.x()<0 || a.x()>=width || miny<0 || maxy>=height) return;
+        unsigned char *ptr=backbuffer+(a.x()+width*miny)/2;
+        if(a.x() & 1)
+        {
+            unsigned char c=conv1(color);
+            for(short i=miny;i<=maxy;i++)
+            {
+                *ptr=(*ptr & 0b11110000) | c;
+                ptr+=width/2;
+            }
+        } else {
+            unsigned char c=conv1(color)<<4;
+            for(short i=miny;i<=maxy;i++)
+            {
+                *ptr=(*ptr & 0b00001111) | c;
+                ptr+=width/2;
+            }
+        }
+        return;
+    }
+    //General case
+    Line::draw(*this,a,b,color);
+}
+
+void DisplayGeneric4BPP::scanLine(Point p, const Color *colors, unsigned short length)
+{
+    if(p.x()<0 || static_cast<int>(p.x())+static_cast<int>(length)>width
+        ||p.y()<0 || p.y()>=height) return;
+    for(short x=0;x<length;x++) doSetPixel(p.x()+x,p.y(),conv1(colors[x]));
+}
+
+Color *DisplayGeneric4BPP::getScanLineBuffer()
+{
+    return buffer;
+}
+
+void DisplayGeneric4BPP::scanLineBuffer(Point p, unsigned short length)
+{
+    scanLine(p,buffer,length);
+}
+
+void DisplayGeneric4BPP::drawImage(Point p, const ImageBase& img)
+{
+    short int xEnd=p.x()+img.getWidth()-1;
+    short int yEnd=p.y()+img.getHeight()-1;
+    if(p.x()<0 || p.y()<0 || xEnd<p.x() || yEnd<p.y()
+        ||xEnd >= width || yEnd >= height) return;
+
+//    const unsigned short *imgData=img.getData();
+//    if(imgData!=0)
+//    {
+//        //TODO Optimized version for in-memory images
+//    } else
+    img.draw(*this,p);
+}
+
+void DisplayGeneric4BPP::clippedDrawImage(Point p, Point a, Point b, const ImageBase& img)
+{
+//    const unsigned short *imgData=img.getData();
+//    if(imgData!=0)
+//    {
+//        //TODO Optimized version for in-memory images
+//    } else
+    img.clippedDraw(*this,p,a,b);
+}
+
+void DisplayGeneric4BPP::drawRectangle(Point a, Point b, Color c)
+{
+    line(a,Point(b.x(),a.y()),c);
+    line(Point(b.x(),a.y()),b,c);
+    line(b,Point(a.x(),b.y()),c);
+    line(Point(a.x(),b.y()),a,c);
+}
+
+DisplayGeneric4BPP::pixel_iterator DisplayGeneric4BPP::begin(Point p1, Point p2,
+        IteratorDirection d)
+{
+    bool fail=false;
+    if(p1.x()<0 || p1.y()<0 || p2.x()<0 || p2.y()<0) fail=true;
+    if(p1.x()>=width || p1.y()>=height || p2.x()>=width || p2.y()>=height) fail=true;
+    if(p2.x()<p1.x() || p2.y()<p1.y()) fail=true;
+    if(fail)
+    {
+        //Return invalid (dummy) iterators
+        this->last=pixel_iterator();
+        return this->last;
+    }
+
+    //Set the last iterator to a suitable one-past-the last value
+    if(d==DR) this->last=pixel_iterator(Point(p2.x()+1,p1.y()),p2,d,this);
+    else this->last=pixel_iterator(Point(p1.x(),p2.y()+1),p2,d,this);
+
+    return pixel_iterator(p1,p2,d,this);
+}
+
+DisplayGeneric4BPP::~DisplayGeneric4BPP() {}
+
+} //namespace mxgui
diff --git a/drivers/display_generic_4bpp.h b/drivers/display_generic_4bpp.h
new file mode 100644
index 0000000..f5ce404
--- /dev/null
+++ b/drivers/display_generic_4bpp.h
@@ -0,0 +1,330 @@
+/***************************************************************************
+ *   Copyright (C) 2018 by Terraneo Federico                               *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   As a special exception, if other files instantiate templates or use   *
+ *   macros or inline functions from this file, or you compile this file   *
+ *   and link it with other works to produce a work based on this file,    *
+ *   this file does not by itself cause the resulting work to be covered   *
+ *   by the GNU General Public License. However the source code for this   *
+ *   file must still be made available in accordance with the GNU General  *
+ *   Public License. This exception does not invalidate any other reasons  *
+ *   why a work based on this file might be covered by the GNU General     *
+ *   Public License.                                                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, see <http://www.gnu.org/licenses/>   *
+ ***************************************************************************/
+
+#pragma once
+
+#include <config/mxgui_settings.h>
+#include "display.h"
+#include "point.h"
+#include "color.h"
+#include "iterator_direction.h"
+#include <cstdio>
+#include <cstring>
+#include <algorithm>
+
+namespace mxgui {
+
+/**
+ * Generic driver for a 4 bit per pixel display, like the OLED displays based
+ * on the SSD1322 controller.
+ * To use this class for a display driver, you have to derive from this class
+ * and provide:
+ * - a constructor that shall initialize and turn on the display, following
+ *   whatever initialization sequence is recomended by the manufacturer
+ * - the implementation for the doTurnOn(), doTurnOff() and doSetBrightness()
+ *   member functions
+ * - the update() member function which shall copy the backbuffer which is in
+ *   the microcontroller memory to the display memory
+ */
+class DisplayGeneric4BPP : public Display
+{
+public:
+    /**
+     * Constructor.
+     * \param width display width
+     * \param height display height
+     */
+    DisplayGeneric4BPP(short width, short height);
+    
+    /**
+     * \return a pair with the display height and width
+     */
+    std::pair<short int, short int> doGetSize() const override;
+
+    /**
+     * Write text to the display. If text is too long it will be truncated
+     * \param p point where the upper left corner of the text will be printed
+     * \param text, text to print.
+     */
+    void write(Point p, const char *text) override;
+
+    /**
+     * Write part of text to the display
+     * \param p point of the upper left corner where the text will be drawn.
+     * Negative coordinates are allowed, as long as the clipped view has
+     * positive or zero coordinates
+     * \param a Upper left corner of clipping rectangle
+     * \param b Lower right corner of clipping rectangle
+     * \param text text to write
+     */
+    void clippedWrite(Point p, Point a, Point b, const char *text) override;
+
+    /**
+     * Clear the Display. The screen will be filled with the desired color
+     * \param color fill color
+     */
+    void clear(Color color) override;
+
+    /**
+     * Clear an area of the screen
+     * \param p1 upper left corner of area to clear
+     * \param p2 lower right corner of area to clear
+     * \param color fill color
+     */
+    void clear(Point p1, Point p2, Color color) override;
+
+    /**
+     * This backend does not require it, so it is a blank.
+     */
+    void beginPixel() override;
+
+    /**
+     * Draw a pixel with desired color. You have to call beginPixel() once
+     * before calling setPixel()
+     * \param p point where to draw pixel
+     * \param color pixel color
+     */
+    void setPixel(Point p, Color color) override;
+
+    /**
+     * Draw a line between point a and point b, with color c
+     * \param a first point
+     * \param b second point
+     * \param c line color
+     */
+    void line(Point a, Point b, Color color) override;
+
+    /**
+     * Draw an horizontal line on screen.
+     * Instead of line(), this member function takes an array of colors to be
+     * able to individually set pixel colors of a line.
+     * \param p starting point of the line
+     * \param colors an array of pixel colors whoase size must be b.x()-a.x()+1
+     * \param length length of colors array.
+     * p.x()+length must be <= display.width()
+     */
+    void scanLine(Point p, const Color *colors, unsigned short length) override;
+    
+    /**
+     * \return a buffer of length equal to this->getWidth() that can be used to
+     * render a scanline.
+     */
+    Color *getScanLineBuffer() override;
+    
+    /**
+     * Draw the content of the last getScanLineBuffer() on an horizontal line
+     * on the screen.
+     * \param p starting point of the line
+     * \param length length of colors array.
+     * p.x()+length must be <= display.width()
+     */
+    void scanLineBuffer(Point p, unsigned short length) override;
+
+    /**
+     * Draw an image on the screen
+     * \param p point of the upper left corner where the image will be drawn
+     * \param i image to draw
+     */
+    void drawImage(Point p, const ImageBase& img) override;
+
+    /**
+     * Draw part of an image on the screen
+     * \param p point of the upper left corner where the image will be drawn.
+     * Negative coordinates are allowed, as long as the clipped view has
+     * positive or zero coordinates
+     * \param a Upper left corner of clipping rectangle
+     * \param b Lower right corner of clipping rectangle
+     * \param i Image to draw
+     */
+    void clippedDrawImage(Point p, Point a, Point b, const ImageBase& img) override;
+
+    /**
+     * Draw a rectangle (not filled) with the desired color
+     * \param a upper left corner of the rectangle
+     * \param b lower right corner of the rectangle
+     * \param c color of the line
+     */
+    void drawRectangle(Point a, Point b, Color c) override;
+    
+    /**
+     * Pixel iterator. A pixel iterator is an output iterator that allows to
+     * define a window on the display and write to its pixels.
+     */
+    class pixel_iterator
+    {
+    public:
+        /**
+         * Default constructor, results in an invalid iterator.
+         */
+        pixel_iterator() : x(0), y(0), disp(nullptr) {}
+
+        /**
+         * Set a pixel and move the pointer to the next one
+         * \param color color to set the current pixel
+         * \return a reference to this
+         */
+        pixel_iterator& operator= (Color color)
+        {
+            if(disp==nullptr) return *this;
+            disp->doSetPixel(x,y,conv1(color));
+            
+            if(direction==RD)
+            {
+                if(++x>xe)
+                {
+                    x=xs;
+                    y++;
+                }
+            } else {
+                if(++y>ye)
+                {
+                    y=ys;
+                    x++;
+                }
+            }
+            return *this;
+        }
+
+        /**
+         * Compare two pixel_iterators for equality.
+         * They are equal if they point to the same location.
+         */
+        bool operator== (const pixel_iterator& itr)
+        {
+            return this->x==itr.x && this->y==itr.y;
+        }
+
+        /**
+         * Compare two pixel_iterators for inequality.
+         * They different if they point to different locations.
+         */
+        bool operator!= (const pixel_iterator& itr)
+        {
+            return this->x!=itr.x || this->y!=itr.y;
+        }
+
+        /**
+         * \return a reference to this.
+         */
+        pixel_iterator& operator* () { return *this; }
+
+        /**
+         * \return a reference to this. Does not increment pixel pointer.
+         */
+        pixel_iterator& operator++ ()  { return *this; }
+
+        /**
+         * \return a reference to this. Does not increment pixel pointer.
+         */
+        pixel_iterator& operator++ (int)  { return *this; }
+        
+        /**
+         * Must be called if not all pixels of the required window are going
+         * to be written.
+         */
+        void invalidate() {}
+
+    private:
+        /**
+         * Constructor
+         * \param start Upper left corner of window
+         * \param end Lower right corner of window
+         * \param direction Iterator direction
+         * \param disp Display we're associated
+         */
+        pixel_iterator(Point start, Point end, IteratorDirection direction,
+                       DisplayGeneric4BPP *disp) : direction(direction), disp(disp)
+        {
+            x=xs=start.x();
+            y=ys=start.y();
+            xe=end.x();
+            ye=end.y();
+        }
+        
+        short x,xs,xe;
+        short y,ys,ye;
+        IteratorDirection direction;
+        DisplayGeneric4BPP *disp;
+
+        friend class DisplayGeneric4BPP; //Needs access to ctor
+    };
+
+    /**
+     * Specify a window on screen and return an object that allows to write
+     * its pixels.
+     * Note: a call to begin() will invalidate any previous iterator.
+     * \param p1 upper left corner of window
+     * \param p2 lower right corner (included)
+     * \param d increment direction
+     * \return a pixel iterator
+     */
+    pixel_iterator begin(Point p1, Point p2, IteratorDirection d);
+
+    /**
+     * \return an iterator which is one past the last pixel in the pixel
+     * specified by begin. Behaviour is undefined if called before calling
+     * begin()
+     */
+    pixel_iterator end() const { return last; }
+    
+    /**
+     * Destructor
+     */
+    ~DisplayGeneric4BPP();
+    
+protected:
+    
+    //FIXME: ignoring MXGUI_ORIENTATION
+    const short int width;
+    const short int height;
+    
+    const int fbSize; //Framebuffer is 4 bit per pixel
+
+    unsigned char *backbuffer; ///< Display backbuffer (frontbuffer is in the display chip)
+
+private:
+
+    static unsigned char conv1(Color c) { return c & 0xf; }
+    static unsigned char conv2(Color c) { unsigned char x=c & 0xf; return x | x<<4; }
+    
+    /**
+     * Non bound checked no color conversion non virtual setPixel.
+     */
+    void doSetPixel(short x, short y, unsigned char cc)
+    {
+        int offset=(x+y*width)/2;
+        if(x & 1)
+            backbuffer[offset]=(backbuffer[offset] & 0b11110000) | cc;
+        else
+            backbuffer[offset]=(backbuffer[offset] & 0b00001111) | (cc<<4);
+    }
+    
+    Color *buffer;             ///< For scanLineBuffer
+    pixel_iterator last;       ///< Last iterator for end of iteration check
+};
+
+} //namespace mxgui
diff --git a/drivers/display_ly091wg14.h b/drivers/display_ly091wg14.h
new file mode 100644
index 0000000..fe8ed57
--- /dev/null
+++ b/drivers/display_ly091wg14.h
@@ -0,0 +1,149 @@
+/***************************************************************************
+ *   Copyright (C) 2018 by Terraneo Federico                               *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   As a special exception, if other files instantiate templates or use   *
+ *   macros or inline functions from this file, or you compile this file   *
+ *   and link it with other works to produce a work based on this file,    *
+ *   this file does not by itself cause the resulting work to be covered   *
+ *   by the GNU General Public License. However the source code for this   *
+ *   file must still be made available in accordance with the GNU General  *
+ *   Public License. This exception does not invalidate any other reasons  *
+ *   why a work based on this file might be covered by the GNU General     *
+ *   Public License.                                                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, see <http://www.gnu.org/licenses/>   *
+ ***************************************************************************/
+
+#pragma once
+
+#include "display_generic_1bpp.h"
+#include <util/software_i2c.h>
+#include <algorithm>
+
+namespace mxgui {
+
+/**
+ * A small (0.91"), 128x32 pixel monochrome inexpensive and not particularly
+ * fast (I2C interface) OLED display that can be easily found on ebay.
+ * 
+ * This driver uses software I2C
+ * 
+ * \tparam SDA I2C SDA Gpio connected to the display (requires 4k7 pullup)
+ * \tparam SCL I2C SCL Gpio connected to the display (requires 4k7 pullup)
+ * \tparam RESET RESET Gpio connected to the display
+ */
+template<typename SDA, typename SCL, typename RESET>
+class DisplayLy091wg14 : public DisplayGeneric1BPP
+{
+public:
+    /*
+     * Constructor
+     */
+    DisplayLy091wg14();
+        
+    /**
+     * Turn the display On after it has been turned Off.
+     * Display initial state is On.
+     */
+    void doTurnOn() override { cmd(0xaf); }
+
+    /**
+     * Turn the display Off. It can be later turned back On.
+     */
+    void doTurnOff() override { cmd(0xae); }
+    
+    /**
+     * Set display brightness. Depending on the underlying driver,
+     * may do nothing.
+     * \param brt from 0 to 100
+     */
+    void doSetBrightness(int brt) override
+    {
+        int brightness=std::max(0,std::min(255,5+brt*2+brt/2)); //5+brt*2.5
+        cmd(0x81); cmd(brightness);
+    }
+    
+    /**
+     * Make all changes done to the display since the last call to update()
+     * visible. Backends that require it may override this.
+     */
+    void update() override
+    {
+        //Takes ~21ms
+        cmd(0x21); cmd(0); cmd(127);
+        cmd(0x22); cmd(0); cmd(3);
+        
+        i2c::sendStart();
+        if(i2c::send(ADDR)==0) { i2c::sendStop(); return; }
+        i2c::send(GDATA);
+        for(int i=0;i<fbSize;i++) i2c::send(backbuffer[i]);
+        i2c::sendStop();
+    }
+    
+private:
+    using i2c=miosix::SoftwareI2C<SDA,SCL,0,true>;
+
+    static const unsigned char ADDR    = 0b01111000;
+    static const unsigned char COMMAND = 0b10000000;
+    static const unsigned char GDATA   = 0b01000000;
+
+    static void cmd(unsigned char c);
+};
+
+template<typename SDA, typename SCL, typename RESET>
+DisplayLy091wg14<SDA,SCL,RESET>::DisplayLy091wg14() : DisplayGeneric1BPP(128,32)
+{
+    using namespace miosix;
+
+    RESET::mode(Mode::OUTPUT);
+    i2c::init();
+
+    RESET::high();
+    Thread::sleep(1);
+    RESET::low();
+    delayUs(100);
+    RESET::high();
+    delayUs(100);
+    
+    cmd(0xae);                         // Display off
+    cmd(0xd5); cmd(0x80);              // Oscillator settings 0x8, divide by 1
+    cmd(0xa8); cmd(height-1);          // Mux ratio
+    cmd(0xd3); cmd(0x00);              // Display offset 0
+    cmd(0x40);                         // Display start line 0
+    cmd(0x8d); cmd(0x14);              // ?
+    cmd(0x20); cmd(0x00);              // Memory addrressing horizontal
+    cmd(0xa1);                         // Remap: col127=seg0
+    cmd(0xc8);                         // COM scan direction decreasing
+    cmd(0xda); cmd(0x02);              // COM sequential, not remapped
+    cmd(0x81); cmd(0x80);              // Default brightness
+    cmd(0xd9); cmd(0xf1);              // Precharge periods
+    cmd(0xdb); cmd(0x40);              // VCOMH
+    cmd(0xa6);                         // Normal display mode
+    clear(0);
+    update();
+    cmd(0xaf);                         // Display on
+    setTextColor(std::make_pair(Color(0xf),Color(0x0)));
+}
+
+template<typename SDA, typename SCL, typename RESET>
+void DisplayLy091wg14<SDA,SCL,RESET>::cmd(unsigned char c)
+{
+    i2c::sendStart();
+    if(i2c::send(ADDR)==0) { i2c::sendStop(); return; }
+    i2c::send(COMMAND);
+    i2c::send(c);
+    i2c::sendStop();
+}
+
+} //namespace mxgui
-- 
GitLab