From 3a3c55f4474c7538fab2e1046da09e81d3305d3a Mon Sep 17 00:00:00 2001
From: Jay <jdev.coffee@gmail.com>
Date: Mon, 21 Aug 2023 20:02:10 +0200
Subject: [PATCH] Add st25dvdiscovery driver (#1)

* Add sendCmd(), doTurnOn(), doTurnOff()

* Override all virtual DisplayImpl methods

* Include ST25DVDISCOVERY Display driver to Makefile

* Successful GRAM Access

* Write() functionality

---------

Co-authored-by: Jay <jdev.coffee@gmail.com>
Signed-off-by: Federico Terraneo <fede.tft@miosix.org>
---
 Makefile                            |   5 +-
 drivers/display_st25dvdiscovery.cpp | 325 ++++++++++++++++++++++
 drivers/display_st25dvdiscovery.h   | 411 ++++++++++++++++++++++++++++
 3 files changed, 739 insertions(+), 2 deletions(-)
 create mode 100644 drivers/display_st25dvdiscovery.cpp
 create mode 100644 drivers/display_st25dvdiscovery.h

diff --git a/Makefile b/Makefile
index e1f8674..26801ab 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
 ## Makefile for mxgui
 ## This makefile builds libmxgui.a
 ##
-MAKEFILE_VERSION := 1.09
+MAKEFILE_VERSION := 1.10
 GCCMAJOR := $(shell arm-miosix-eabi-gcc --version | \
                     perl -e '$$_=<>;/\(GCC\) (\d+)/;print "$$1"')
 ## KPATH and CONFPATH are forwarded by the parent Makefile
@@ -40,7 +40,8 @@ drivers/display_stm32f4discovery.cpp   \
 drivers/event_stm32f4discovery.cpp     \
 drivers/display_generic_1bpp.cpp       \
 drivers/display_generic_4bpp.cpp       \
-drivers/display_st7735.cpp
+drivers/display_st7735.cpp			   \
+drivers/display_st25dvdiscovery.cpp
 
 ifeq ("$(VERBOSE)","1")
 Q := 
diff --git a/drivers/display_st25dvdiscovery.cpp b/drivers/display_st25dvdiscovery.cpp
new file mode 100644
index 0000000..c96cfda
--- /dev/null
+++ b/drivers/display_st25dvdiscovery.cpp
@@ -0,0 +1,325 @@
+/***************************************************************************
+ *   Copyright (C) 2014 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/>   *
+ ***************************************************************************/
+
+#ifdef _BOARD_STM32F415VG_ST25DVDISCOVERY
+#include "display_st25dvdiscovery.h"
+#include "miosix.h"
+#include "misc_inst.h"
+#include "line.h"
+#include <cstdarg>
+
+using namespace std;
+using namespace miosix;
+
+namespace mxgui {
+
+//Control interface
+typedef Gpio<GPIOB_BASE,13> scl; //SPI SCK
+typedef Gpio<GPIOC_BASE, 3> sda; //SPI MOSI
+typedef Gpio<GPIOC_BASE, 0> dcx; //Data/command
+typedef Gpio<GPIOB_BASE,12> csx; //SPI CS
+
+/**
+ * Send and receive a byte through SPI2
+ * \param c byte to send
+ * \return byte received
+ */
+static unsigned char spi2sendRev(unsigned char c=0)
+{
+    SPI2->DR=c;
+    while((SPI2->SR & SPI_SR_RXNE)==0) ;
+    return SPI2->DR;
+}
+
+void sendCmd(unsigned char cmd, int len, ...)
+{
+
+    // Send Command
+    dcx::low();
+    csx::low();
+    spi2sendRev(cmd);
+    delayUs(1);
+    dcx::high();
+
+    // Send Arguments
+    va_list arg;
+    va_start(arg,len);
+    for(int i=0;i<len;i++)
+    {   
+        spi2sendRev(va_arg(arg,int));
+        delayUs(1);
+    }
+    va_end(arg);
+    csx::high();
+}
+
+Transaction::Transaction(unsigned char cmd)
+{
+    dcx::low();
+    csx::low();
+    spi2sendRev(cmd);
+    delayUs(1);
+    dcx::high();
+}
+
+void Transaction::write(unsigned char c)
+{
+    spi2sendRev(c);
+    delayUs(1);
+}
+
+Transaction::~Transaction()
+{
+    csx::high();
+}
+
+
+void registerDisplayHook(DisplayManager& dm)
+{
+    dm.registerDisplay(&DisplayImpl::instance());
+}
+
+DisplayImpl& DisplayImpl::instance()
+{
+    static DisplayImpl instance;
+    return instance;
+}
+
+void DisplayImpl::doTurnOn()
+{
+    sendCmd(0x29,0); //LCD_DISPLAY_ON
+};
+
+void DisplayImpl::doTurnOff()
+{
+    sendCmd(0x28,0); //LCD_DISPLAY_ON
+};
+
+void DisplayImpl::doSetBrightness(int brt) 
+{
+    //TODO - Can be set by 0x51h
+}
+
+pair<short int, short int> DisplayImpl::doGetSize() const
+{
+    return make_pair(height,width);
+}
+
+void DisplayImpl::write(Point p, const char *text) {
+    font.draw(*this, textColor, p, text);
+}
+
+void DisplayImpl::clippedWrite(Point p, Point a,  Point b, const char *text) {
+    font.clippedDraw(*this, textColor, p, a, b, text);
+}
+
+void DisplayImpl::clear(Color color) {
+    clear(Point(0,0), Point(width-1,height-1), color);
+}
+
+void DisplayImpl::clear(Point p1, Point p2, Color color) {
+    unsigned char lsb = color & 0xFF;
+    unsigned char msb = (color >> 8) & 0xFF;
+
+    imageWindow(p1, p2);
+    int numPixels = (p2.x() - p1.x() + 1) * (p2.y() - p1.y() + 1);
+
+    Transaction t(0x2c);
+    //Send data to write on GRAM
+    for(int i=0; i < numPixels; i++) {
+        t.write(msb);
+        t.write(lsb);
+    }
+}
+
+void DisplayImpl::beginPixel() {}
+
+void DisplayImpl::setPixel(Point p, Color color)
+{
+    unsigned char lsb = color & 0xFF;
+    unsigned char msb = (color >> 8) & 0xFF;
+    sendCmd(0x2c,2,msb,lsb); // RAMWR
+}
+
+void DisplayImpl::line(Point a, Point b, Color color)
+{
+    // TODO - Horizontal line optimization
+    Line::draw(*this, a, b, color);
+}
+
+void DisplayImpl::scanLine(Point p, const Color *colors, unsigned short length)
+{
+    imageWindow(p,Point(width-1,p.y()));
+
+    unsigned char lsb = 0x00;
+    unsigned char msb = 0x00;
+
+    Transaction t(0x2c); // RAMWR
+    for (int i = 0; i < length; i++)
+    {
+        lsb = colors[i] & 0xFF;
+        msb = (colors[i] >> 8) & 0xFF;
+        t.write(msb);
+        t.write(lsb);
+    }
+}
+
+Color* DisplayImpl::getScanLineBuffer() {
+    if(buffer == 0) buffer = new Color[getWidth()];
+    return buffer;
+}
+
+void DisplayImpl::scanLineBuffer(Point p, unsigned short length)
+{
+    scanLine(p, buffer, length);
+}
+
+void DisplayImpl::drawImage(Point p, const ImageBase& img)
+{
+    short int xEnd = p.x() + img.getWidth() - 1;
+    short int yEnd = p.y() + img.getHeight() - 1;
+    if(xEnd >= width || yEnd >= height) { return; }
+
+    const unsigned short *imgData = img.getData();
+    if(imgData != 0)
+    {
+        unsigned char lsb = 0x00;
+        unsigned char msb = 0x00;
+
+        //Optimized version for memory-loaded images
+        imageWindow(p, Point(xEnd, yEnd));
+        int numPixels = img.getHeight() * img.getWidth();
+
+        Transaction t(0x2c); // RAMWR
+        for(int i=0; i <= numPixels; i++)
+        {
+            lsb = imgData[i] & 0xFF;
+            msb = (imgData[i] >> 8) & 0xFF;
+            t.write(msb);
+            t.write(lsb);
+        }
+    }
+    else { img.draw(*this,p); }
+}
+
+void DisplayImpl::clippedDrawImage(Point p, Point a, Point b, const ImageBase& img) {
+    img.clippedDraw(*this,p,a,b);
+}
+
+void DisplayImpl::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);
+}
+
+DisplayImpl::pixel_iterator DisplayImpl::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);
+
+    unsigned int numPixels=(p2.x()-p1.x()+1)*(p2.y()-p1.y()+1);
+    return pixel_iterator(numPixels);
+}
+
+DisplayImpl::DisplayImpl() : buffer(0)
+{
+    // TODO - RCC Sequence needed for PLLSAI? There is no PLLSAI for this MCU
+
+    // SPI2 Configuration
+    {
+        FastInterruptDisableLock dLock;
+        
+        scl::mode(Mode::ALTERNATE);    scl::alternateFunction(5); //SPI5
+        sda::mode(Mode::ALTERNATE);    sda::alternateFunction(5);
+        csx::mode(Mode::OUTPUT);       csx::high();
+        dcx::mode(Mode::OUTPUT);
+        
+        RCC->APB1ENR |= RCC_APB1ENR_SPI2EN;      
+        RCC_SYNC();
+    }
+
+    SPI2->CR1=SPI_CR1_SSM   //Sowtware CS
+            | SPI_CR1_SSI   //Software CS high
+            | SPI_CR1_SPE   //SPI enabled
+            | (3<<3)        //Divide input clock by 16: 84/16=5.25MHz
+            | SPI_CR1_MSTR; //Master mode
+    Thread::sleep(1);
+
+    // ILI9341 Configuration
+    sendCmd(0xca,3,0xc3,0x08,0x50);           //undocumented command
+    sendCmd(0xcf,3,0x00,0xc1,0x30);           //LCD_POWERB
+    sendCmd(0xed,4,0x64,0x03,0x12,0x81);      //LCD_POWER_SEQ
+    sendCmd(0xe8,3,0x85,0x00,0x78);           //LCD_DTCA
+    sendCmd(0xcb,5,0x39,0x2c,0x00,0x34,0x02); //LCD_POWERA
+    sendCmd(0xf7,1,0x20);                     //LCD_PRC
+    sendCmd(0xea,2,0x00,0x00);                //LCD_DTCB
+    sendCmd(0xb1,2,0x00,0x1b);                //LCD_FRMCTR1
+    sendCmd(0xb6,2,0x0a,0xa2);                //LCD_DFC
+    sendCmd(0xc0,1,0x10);                     //LCD_POWER1
+    sendCmd(0xc1,1,0x10);                     //LCD_POWER2
+    sendCmd(0xc5,2,0x45,0x15);                //LCD_VCOM1
+    sendCmd(0xc7,1,0x90);                     //LCD_VCOM2
+    sendCmd(0x36,1,0x08);                     //LCD_MAC
+    sendCmd(0xf2,1,0x00);                     //LCD_3GAMMA_EN
+    sendCmd(0xb6,4,0x0a,0xa7,0x27,0x04);      //LCD_DFC
+    sendCmd(0x2a,4,0x00,0x00,0x00,0xef);      //LCD_COLUMN_ADDR
+    sendCmd(0x2b,4,0x00,0x00,0x01,0x3f);      //LCD_PAGE_ADDR
+    sendCmd(0xf6,3,0x01,0x00,0x00);           //LCD_INTERFACE
+    sendCmd(0x3a,1,0x05);
+    sendCmd(0x2c,0);                          //LCD_GRAM
+    Thread::sleep(200);
+    sendCmd(0x26,1,0x01);                     //LCD_GAMMA
+    sendCmd(0xe0,15,0x0f,0x29,0x24,0x0c,0x0e,0x09,0x4e,0x78,0x3c,0x09,0x13,
+            0x05,0x17,0x11,0x00);             //LCD_PGAMMA
+    sendCmd(0xe1,15,0x00,0x16,0x1b,0x04,0x11,0x07,0x31,0x33,0x42,0x05,0x0c,
+            0x0a,0x28,0x2f,0x0f);             //LCD_NGAMMA
+    sendCmd(0x11,0);                          //LCD_SLEEP_OUT
+    Thread::sleep(500);
+    sendCmd(0x29,0);                          //LCD_DISPLAY_ON
+    sendCmd(0x2c,0);                          //LCD_GRAM
+
+    imageWindow(Point(0,0), Point(width-1,height-1));
+};
+
+DisplayImpl::~DisplayImpl() 
+{
+    if (buffer) delete[] buffer;
+};
+
+}
+
+#endif //_BOARD_STM32F415VG_ST25DVDISCOVERY
\ No newline at end of file
diff --git a/drivers/display_st25dvdiscovery.h b/drivers/display_st25dvdiscovery.h
new file mode 100644
index 0000000..5ee40f0
--- /dev/null
+++ b/drivers/display_st25dvdiscovery.h
@@ -0,0 +1,411 @@
+/***************************************************************************
+ *   Copyright (C) 2014 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/>   *
+ ***************************************************************************/
+
+#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
+
+#ifndef DISPLAY_ST25DVDISCOVERY_H
+#define DISPLAY_ST25DVDISCOVERY_H
+
+#ifdef _BOARD_STM32F415VG_ST25DVDISCOVERY
+#include "display.h"
+#include "color.h"
+#include "point.h"
+
+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 ILI9341 driver requires a color depth of 16bit per pixel
+#endif
+
+/**
+ * Send a command to the ILI9341 display controller
+ * \param cmd command
+ * \param len length of the (optional) argument, or 0 for commands without
+ * arguments.
+ */
+void sendCmd(unsigned char cmd, int len, ...);
+
+/**
+ * Simply another flavour of sending data to Display
+ * Useful for sending dynamically sized data
+*/
+class Transaction
+{
+public:
+    Transaction(unsigned char cmd);
+    void write(unsigned char c);
+    ~Transaction();
+};
+
+class DisplayImpl : public Display
+{
+
+public:
+    /**
+     * \return an instance to this class (singleton)
+     */
+    static DisplayImpl& instance();
+
+    /**
+     * 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 member function is used on some target displays to reset the
+     * drawing window to its default value. You have to call beginPixel() once
+     * before calling setPixel(). You can then make any number of calls to
+     * setPixel() without calling beginPixel() again, as long as you don't
+     * call any other member function in this class. If you call another
+     * member function, for example line(), you have to call beginPixel() again
+     * before calling setPixel().
+     */
+    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;
+
+    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--;
+            
+            unsigned char lsb = color & 0xFF;
+            unsigned char msb = (color >> 8) & 0xFF;
+
+            wr->write(msb);
+            wr->write(lsb);
+
+            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() {}
+
+    private:
+
+        /**
+         * Constructor
+         * \param pixelLeft number of remaining pixels
+         */
+        pixel_iterator(unsigned int pixelLeft): pixelLeft(pixelLeft) 
+        {
+            wr = new Transaction(0x2c);
+        }
+
+        unsigned int pixelLeft; ///< How many pixels are left to draw
+
+        Transaction* wr;
+
+        friend class DisplayImpl;
+    };
+
+    /**
+     * 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
+     */
+    ~DisplayImpl() override;
+
+private:
+
+    /**
+     * Constructor.
+     * Do not instantiate objects of this type directly from application code.
+     */
+    DisplayImpl();
+    
+    #if defined MXGUI_ORIENTATION_VERTICAL
+    static const short int width=240;
+    static const short int height=320;
+    #elif defined MXGUI_ORIENTATION_HORIZONTAL || \
+          defined MXGUI_ORIENTATION_VERTICAL_MIRRORED || \
+          defined MXGUI_ORIENTATION_HORIZONTAL_MIRRORED
+    #error unsupported orientation
+    #else
+    #error No orientation defined
+    #endif
+
+    Color* buffer; ///< For scanLineBuffer
+
+    /**
+     * This member function is used to set the cursor stored in memory, as well as
+     * the SC,SP,EC,EP counters of display driver.
+     * \param p1 top-left point of the rectangle
+     * \param p2 bottom-right point of the rectangle
+     */
+    static inline void window(Point p1, Point p2)
+    {
+        int SC[] = {p1.x() & 0xff, (p1.x() >> 8) & 0xff};
+        int EC[] = {p2.x() & 0xff, (p2.x() >> 8) & 0xff};
+        int SP[] = {p1.y() & 0xff, (p1.y() >> 8) & 0xff};
+        int EP[] = {p2.y() & 0xff, (p2.y() >> 8) & 0xff};
+
+        sendCmd(0x2a,4,SC[1],SC[0],EC[1],EC[0]); //LCD_COLUMN_ADDR
+        sendCmd(0x2b,4,SP[1],SP[0],EP[1],EP[0]); //LCD_PAGE_ADDR
+        sendCmd(0x2c,0); //LCD_RAMWR
+    }
+
+    /**
+     * 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 inline void textWindow(Point p1, Point p2)
+    {
+        #ifdef MXGUI_ORIENTATION_VERTICAL
+        // p3 is p2 transposed relative to p1. So that the column and page addresses exchanges
+        Point p3 = Point(p1.x()+p2.y()-p1.y(),p1.y()+p2.x()-p1.x()); 
+        window(p1,p3);
+        sendCmd(0x36,1,0x28); //LCD_MAC
+        #elif defined MXGUI_ORIENTATION_HORIZONTAL
+            #error Not implemented
+        #elif defined MXGUI_ORIENTATION_VERTICAL_MIRRORED
+            #error Not implemented
+        #else //MXGUI_ORIENTATION_HORIZONTAL_MIRRORED
+            #error Not implemented
+        #endif
+    }
+
+    /**
+     * 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
+        window(p1,p2);
+        sendCmd(0x36,1,0x08); //LCD_MAC
+        #elif defined MXGUI_ORIENTATION_HORIZONTAL
+            #error Not implemented
+        #elif defined MXGUI_ORIENTATION_VERTICAL_MIRRORED
+            #error Not implemented
+        #else //MXGUI_ORIENTATION_HORIZONTAL_MIRRORED
+            #error Not implemented
+        #endif
+    }
+};
+
+
+} //namespace mxgui
+#endif //_BOARD_STM32F415VG_ST25DVDISCOVERY
+
+#endif //DISPLAY_ST25DVDISCOVERY_H
\ No newline at end of file
-- 
GitLab