From 21169eca91b0b2fb67349c038ca7bbbd392a9a0b Mon Sep 17 00:00:00 2001
From: Federico Terraneo <fede.tft@miosix.org>
Date: Wed, 8 Dec 2021 23:41:33 +0100
Subject: [PATCH] Added er_oledm015 driver

---
 .../display_er_oledm015.cpp                   | 394 ++++++++++++++++++
 .../display_er_oledm015/display_er_oledm015.h | 303 ++++++++++++++
 _examples/display_er_oledm015/main.cpp        |  53 +++
 drivers/display_st7735.h                      |   6 +-
 line.h                                        |   5 -
 5 files changed, 752 insertions(+), 9 deletions(-)
 create mode 100644 _examples/display_er_oledm015/display_er_oledm015.cpp
 create mode 100644 _examples/display_er_oledm015/display_er_oledm015.h
 create mode 100644 _examples/display_er_oledm015/main.cpp

diff --git a/_examples/display_er_oledm015/display_er_oledm015.cpp b/_examples/display_er_oledm015/display_er_oledm015.cpp
new file mode 100644
index 0000000..edbc4cc
--- /dev/null
+++ b/_examples/display_er_oledm015/display_er_oledm015.cpp
@@ -0,0 +1,394 @@
+/***************************************************************************
+ *   Copyright (C) 2021 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_oledm015.h"
+#include <miosix.h>
+#include <algorithm>
+#include <line.h>
+
+using namespace std;
+using namespace miosix;
+using namespace mxgui;
+
+#ifndef _BOARD_STM32F411CE_BLACKPILL
+#warning "The SPI driver has only been tested on an STM32F411CE_BLACKPILL"
+#endif
+
+//Display connection
+
+using cs   = Gpio<GPIOB_BASE,7>;
+using sck  = Gpio<GPIOB_BASE,3>; //Used as HW SPI
+using mosi = Gpio<GPIOB_BASE,5>; //Used as HW SPI
+using dc   = Gpio<GPIOB_BASE,6>;
+using res  = Gpio<GPIOB_BASE,4>;
+
+/**
+ * Send and receive a byte, thus returning only after transmission is complete
+ * \param x byte to send
+ * \return the received byte
+ */
+static unsigned char spi1sendRecv(unsigned char x=0)
+{
+    SPI1->DR=x;
+    while((SPI1->SR & SPI_SR_RXNE)==0) ;
+    return SPI1->DR;
+}
+
+/**
+ * Send a byte only.
+ * NOTE: this function requires special care to use as
+ * - it returns before the byte has been transmitted, and if this is the last
+ *   byte, you have to wait with spi1waitCompletion() before deasserting cs
+ * - as the received byte is ignored, the overrun flag gets set and it must be
+ *   cleared (spi1waitCompletion() does that as well)
+ */
+static void spi1sendOnly(unsigned char x)
+{
+    //NOTE: data is sent after the function returns, watch out!
+    while((SPI1->SR & SPI_SR_TXE)==0) ;
+    SPI1->DR=x;
+}
+
+/**
+ * Must be called after using spi1sendOnly(), complete the last byte transmission
+ */
+static void spi1waitCompletion()
+{
+    while(SPI1->SR & SPI_SR_BSY) ;
+    //Reading DR and then SR clears overrun flag
+    [[gnu::unused]] volatile int unused;
+    unused=SPI1->DR;
+    unused=SPI1->SR;
+}
+
+/**
+ * Send a command to the display
+ * \param c command
+ */
+static void cmd(unsigned char c)
+{
+    dc::low();
+    cs::low();
+    spi1sendRecv(c);
+    cs::high();
+    delayUs(1);
+}
+
+/**
+ * Send data to the display
+ * \param d data
+ */
+static void dat(unsigned char d)
+{
+    dc::high();
+    cs::low();
+    spi1sendRecv(d);
+    cs::high();
+    delayUs(1);
+}
+
+static const int width=128, height=128; ///< Display size
+
+/**
+ * Set cursor to desired location
+ * \param point where to set cursor (0<=x<128, 0<=y<128)
+ */
+static void setCursor(Point p)
+{
+    #ifdef MXGUI_ORIENTATION_VERTICAL
+    cmd(0x15); dat(p.x()); dat(0x7f); // Set column address
+    cmd(0x75); dat(p.y()); dat(0x7f); // Set row address
+    #else //MXGUI_ORIENTATION_HORIZONTAL
+    cmd(0x15); dat(p.y()); dat(0x7f); // Set column address
+    cmd(0x75); dat(p.x()); dat(0x7f); // Set row address
+    #endif //Hardware doesn't seem to support mirroring
+}
+
+/**
+ * Set a hardware window on the screen, optimized for writing text.
+ * The GRAM increment will be set to up-to-down first, then left-to-right which
+ * is the correct increment to draw fonts
+ * \param p1 upper left corner of the window
+ * \param p2 lower right corner of the window
+ */
+static void textWindow(Point p1, Point p2)
+{
+    #ifdef MXGUI_ORIENTATION_VERTICAL
+    cmd(0x15); dat(p1.x()); dat(p2.x()); // Set column address
+    cmd(0x75); dat(p1.y()); dat(p2.y()); // Set row address
+    cmd(0xa0); dat(0x67);
+    #else //MXGUI_ORIENTATION_HORIZONTAL
+    cmd(0x15); dat(p1.y()); dat(p2.y()); // Set column address
+    cmd(0x75); dat(p1.x()); dat(p2.x()); // Set row address
+    cmd(0xa0); dat(0x64);
+    #endif //Hardware doesn't seem to support mirroring
+}
+
+/**
+ * Set a hardware window on the screen, optimized for drawing images.
+ * The GRAM increment will be set to left-to-right first, then up-to-down which
+ * is the correct increment to draw images
+ * \param p1 upper left corner of the window
+ * \param p2 lower right corner of the window
+ */
+static inline void imageWindow(Point p1, Point p2)
+{
+    #ifdef MXGUI_ORIENTATION_VERTICAL
+    cmd(0x15); dat(p1.x()); dat(p2.x()); // Set column address
+    cmd(0x75); dat(p1.y()); dat(p2.y()); // Set row address
+    cmd(0xa0); dat(0x66);
+    #else //MXGUI_ORIENTATION_HORIZONTAL
+    cmd(0x15); dat(p1.y()); dat(p2.y()); // Set column address
+    cmd(0x75); dat(p1.x()); dat(p2.x()); // Set row address
+    cmd(0xa0); dat(0x65);
+    #endif //Hardware doesn't seem to support mirroring
+}
+
+//
+// class DisplayErOledm015
+//
+
+namespace mxgui {
+
+DisplayErOledm015::DisplayErOledm015() : buffer(nullptr)
+{
+    {
+        FastInterruptDisableLock dLock;
+        cs::mode(Mode::OUTPUT);      cs::high();
+        sck::mode(Mode::ALTERNATE);  sck::alternateFunction(5);
+        mosi::mode(Mode::ALTERNATE); mosi::alternateFunction(5);
+        dc::mode(Mode::OUTPUT);
+        res::mode(Mode::OUTPUT);
+
+        RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
+        RCC_SYNC();
+    }
+
+    SPI1->CR1=SPI_CR1_SSM  //No HW cs
+            | SPI_CR1_SSI
+            | SPI_CR1_SPE  //SPI enabled
+            | SPI_CR1_BR_0 //SPI clock 50/4=12.5 MHz (Fmax=20MHz)
+            | SPI_CR1_MSTR;//Master mode
+
+    res::high();
+    Thread::sleep(1);
+    res::low();
+    delayUs(100);
+    res::high();
+    delayUs(100);
+    
+    static const unsigned char grayTable[]=
+    {
+          0,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
+         17, 18, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 42, 45, 48,
+         51, 54, 57, 60, 63, 66, 69, 72, 76, 80, 84, 88, 92, 96,100,104,
+        108,112,116,120,125,130,135,140,145,150,155,160,165,170,175,180
+    };
+    
+    cmd(0xfd); dat(0x12);                       // Disable command lock
+    cmd(0xfd); dat(0xb1);                       // Enable all commands
+    cmd(0xae);                                  // Display OFF
+    cmd(0xa1); dat(0x00);                       // Set display start line
+    cmd(0xa2); dat(0x00);                       // Set display offset
+    cmd(0xa6);                                  // Normal display mode
+    cmd(0xab); dat(0x01);                       // 8bit iface, 2.5V regulator ON
+    cmd(0xb1); dat(0x32);                       // Precharge phase2=3 phase1=5
+    cmd(0xb3); dat(0xf1);                       // Oscillator 0xf divide by 2
+    cmd(0xb4); dat(0xa0); dat(0xb5); dat(0x55); // External VSL
+    cmd(0xb6); dat(0x01);                       // Second precharge 1 DCLKS
+    cmd(0xb8);                                  // Set gray table
+    for(unsigned int i=0;i<sizeof(grayTable);i++) dat(grayTable[i]);
+    cmd(0xbb); dat(0x17);                       // Precharge voltage ~0.5VCC
+    cmd(0xbe); dat(0x05);                       // VCOMH
+    cmd(0xc1); dat(0x88); dat(0x70); dat(0x88); // A B C brightness
+    cmd(0xc7); dat(0x0f);                       // Master brightness
+    cmd(0xca); dat(0x7f);                       // Duty 1:128
+    clear(0);
+    cmd(0xaf);                                  // Display ON
+
+    setTextColor(make_pair(Color(0xffff),Color(0x0)));
+}
+
+void DisplayErOledm015::doTurnOn()
+{
+    cmd(0xaf);
+}
+
+void DisplayErOledm015::doTurnOff()
+{
+    cmd(0xae);
+}
+
+void DisplayErOledm015::doSetBrightness(int brt)
+{
+    cmd(0xc7); dat(max(0,min(15,brt/6)));
+}
+
+pair<short int, short int> DisplayErOledm015::doGetSize() const
+{
+    return make_pair(height,width);
+}
+
+void DisplayErOledm015::write(Point p, const char *text)
+{
+    font.draw(*this,textColor,p,text);
+}
+
+void DisplayErOledm015::clippedWrite(Point p, Point a, Point b, const char *text)
+{
+    font.clippedDraw(*this,textColor,p,a,b,text);
+}
+
+void DisplayErOledm015::clear(Color color)
+{
+    clear(Point(0,0),Point(width-1,height-1),color);
+}
+
+void DisplayErOledm015::clear(Point p1, Point p2, Color color)
+{
+    imageWindow(p1,p2);
+    doBeginPixelWrite();
+    int numPixels=(p2.x()-p1.x()+1)*(p2.y()-p1.y()+1);
+    for(int i=0;i<numPixels;i++) doWritePixel(color);
+    doEndPixelWrite();
+}
+
+void DisplayErOledm015::beginPixel() {}
+
+void DisplayErOledm015::setPixel(Point p, Color color)
+{
+    //Can't move boilerplate to beginPixel, as can't do setCursor in between
+    setCursor(p);
+    doBeginPixelWrite();
+    doWritePixel(color);
+    doEndPixelWrite();
+}
+
+void DisplayErOledm015::line(Point a, Point b, Color color)
+{
+    //Horizontal line speed optimization
+    if(a.y()==b.y())
+    {
+        imageWindow(Point(min(a.x(),b.x()),a.y()),
+                    Point(max(a.x(),b.x()),a.y()));
+        doBeginPixelWrite();
+        int numPixels=abs(a.x()-b.x());
+        for(int i=0;i<=numPixels;i++) doWritePixel(color);
+        doEndPixelWrite();
+        return;
+    }
+    //Vertical line speed optimization
+    if(a.x()==b.x())
+    {
+        textWindow(Point(a.x(),min(a.y(),b.y())),
+                    Point(a.x(),max(a.y(),b.y())));
+        doBeginPixelWrite();
+        int numPixels=abs(a.y()-b.y());
+        for(int i=0;i<=numPixels;i++) doWritePixel(color);
+        doEndPixelWrite();
+        return;
+    }
+    //General case
+    Line::draw(*this,a,b,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();
+}
+
+Color *DisplayErOledm015::getScanLineBuffer()
+{
+    if(buffer==nullptr) buffer=new Color[getWidth()];
+    return buffer;
+}
+
+void DisplayErOledm015::scanLineBuffer(Point p, unsigned short length)
+{
+    scanLine(p,buffer,length);
+}
+
+void DisplayErOledm015::drawImage(Point p, const ImageBase& img)
+{
+    img.draw(*this,p);
+}
+
+void DisplayErOledm015::clippedDrawImage(Point p, Point a, Point b, const ImageBase& img)
+{
+    img.clippedDraw(*this,p,a,b);
+}
+
+void DisplayErOledm015::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);
+}
+
+DisplayErOledm015::pixel_iterator DisplayErOledm015::begin(Point p1, Point p2,
+        IteratorDirection d)
+{
+    if(p1.x()<0 || p1.y()<0 || p2.x()<0 || p2.y()<0) return pixel_iterator();
+    if(p1.x()>=width || p1.y()>=height || p2.x()>=width || p2.y()>=height)
+        return pixel_iterator();
+    if(p2.x()<p1.x() || p2.y()<p1.y()) return pixel_iterator();
+ 
+    if(d==DR) textWindow(p1,p2);
+    else imageWindow(p1,p2);
+    doBeginPixelWrite();
+
+    unsigned int numPixels=(p2.x()-p1.x()+1)*(p2.y()-p1.y()+1);
+    return pixel_iterator(numPixels);
+}
+
+DisplayErOledm015::~DisplayErOledm015() {}
+
+void DisplayErOledm015::doBeginPixelWrite()
+{
+    cmd(0x5c);
+    dc::high();
+    cs::low();
+}
+    
+void DisplayErOledm015::doWritePixel(Color c)
+{
+    spi1sendOnly(c>>8);
+    spi1sendOnly(c);
+}
+    
+void DisplayErOledm015::doEndPixelWrite()
+{
+    spi1waitCompletion();
+    cs::high();
+    delayUs(1);
+}
+
+} //namespace mxgui
diff --git a/_examples/display_er_oledm015/display_er_oledm015.h b/_examples/display_er_oledm015/display_er_oledm015.h
new file mode 100644
index 0000000..3380825
--- /dev/null
+++ b/_examples/display_er_oledm015/display_er_oledm015.h
@@ -0,0 +1,303 @@
+/***************************************************************************
+ *   Copyright (C) 2021 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 {
+
+//This display is 16 bit per pixel, check that the color depth is properly
+//configured
+#ifndef MXGUI_COLOR_DEPTH_16_BIT
+#error The SSD1351 driver requires a color depth of 16bit per pixel
+#endif
+
+class DisplayErOledm015 : public Display
+{
+public:
+    /**
+     * Constructor.
+     */
+    DisplayErOledm015();
+    
+    /**
+     * 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;
+    
+    /**
+     * \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(): pixelLeft(0) {}
+
+        /**
+         * 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)
+        {
+            pixelLeft--;
+            DisplayErOledm015::doWritePixel(color);
+            if(pixelLeft==0) DisplayErOledm015::doEndPixelWrite();
+            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->pixelLeft==itr.pixelLeft;
+        }
+
+        /**
+         * Compare two pixel_iterators for inequality.
+         * They different if they point to different locations.
+         */
+        bool operator!= (const pixel_iterator& itr)
+        {
+            return this->pixelLeft!=itr.pixelLeft;
+        }
+
+        /**
+         * \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()
+        {
+            DisplayErOledm015::doEndPixelWrite();
+        }
+
+    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(unsigned int pixelLeft): pixelLeft(pixelLeft) {}
+
+        unsigned int pixelLeft; ///< How many pixels are left to draw
+
+        friend class DisplayErOledm015; //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
+    {
+        //Default ctor: pixelLeft is zero.
+        return pixel_iterator();
+    }
+    
+    /**
+     * Destructor
+     */
+    ~DisplayErOledm015();
+
+private:
+    
+    static void doBeginPixelWrite();
+    
+    static void doWritePixel(Color c);
+    
+    static void doEndPixelWrite();
+    
+    Color *buffer;             ///< For scanLineBuffer
+};
+
+} //namespace mxgui
diff --git a/_examples/display_er_oledm015/main.cpp b/_examples/display_er_oledm015/main.cpp
new file mode 100644
index 0000000..33806c9
--- /dev/null
+++ b/_examples/display_er_oledm015/main.cpp
@@ -0,0 +1,53 @@
+
+#include <cstdio>
+#include "miosix.h"
+#include "mxgui/display.h"
+#include "mxgui/misc_inst.h"
+#include "display_er_oledm015.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_oledm015.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 DisplayErOledm015);
+}
+} //namespace mxgui
+
+int main()
+{
+    auto& display=DisplayManager::instance().getDisplay();
+    {
+        DrawingContext dc(display);
+        dc.setFont(tahoma);
+        dc.write(Point(0,0),"Miosix OS");
+        dc.write(Point(0,tahoma.getHeight()),"MXGUI graphics library");
+    }
+   for(int i=0;;i++)
+   {
+       {
+           DrawingContext dc(display);
+           char s[16];
+           sniprintf(s,15,"%02d:%02d",i/60,i%60);
+           dc.write(Point(0,2*tahoma.getHeight()),s);
+       }
+       Thread::sleep(1000);
+   }
+}
diff --git a/drivers/display_st7735.h b/drivers/display_st7735.h
index d248460..02a8f37 100644
--- a/drivers/display_st7735.h
+++ b/drivers/display_st7735.h
@@ -329,14 +329,12 @@ protected:
     
 private:
 
-    #if defined MXGUI_ORIENTATION_VERTICAL
+    #ifdef MXGUI_ORIENTATION_VERTICAL
     static const short int width    = 128;
     static const short int height   = 160;
-    #elif defined MXGUI_ORIENTATION_HORIZONTAL
+    #else //MXGUI_ORIENTATION_HORIZONTAL
     static const short int width    = 160;
     static const short int height   = 128;
-    #else
-    #error Orientation not defined
     #endif
 
     /**
diff --git a/line.h b/line.h
index 035fa42..ca9a225 100644
--- a/line.h
+++ b/line.h
@@ -25,11 +25,6 @@
  *   along with this program; if not, see <http://www.gnu.org/licenses/>   *
  ***************************************************************************/
 
-#ifndef MXGUI_LIBRARY
-#error "This is header is private, it can be used only within mxgui."
-#error "If your code depends on a private header, it IS broken."
-#endif //MXGUI_LIBRARY
-
 #include "point.h"
 #include "color.h"
 
-- 
GitLab