From 51e2310acad6c9b2d12cd21ed5d46de64a3b159d Mon Sep 17 00:00:00 2001
From: Federico Terraneo <fede.tft@miosix.org>
Date: Sun, 21 Mar 2021 19:24:10 +0100
Subject: [PATCH] Made the ST7735 generic and retargetable to different boards

---
 Makefile                                      |   3 +-
 _examples/display_st7735/main.cpp             |  21 +++
 .../display_st7735/retargeted-display.cpp     | 101 ++++++++++++
 drivers/display_st7735.cpp                    | 153 ++++++------------
 drivers/display_st7735.h                      | 131 ++++++---------
 5 files changed, 222 insertions(+), 187 deletions(-)
 create mode 100644 _examples/display_st7735/main.cpp
 create mode 100644 _examples/display_st7735/retargeted-display.cpp

diff --git a/Makefile b/Makefile
index c50eb5c..e1f8674 100644
--- a/Makefile
+++ b/Makefile
@@ -39,7 +39,8 @@ drivers/event_sony-newman.cpp          \
 drivers/display_stm32f4discovery.cpp   \
 drivers/event_stm32f4discovery.cpp     \
 drivers/display_generic_1bpp.cpp       \
-drivers/display_generic_4bpp.cpp
+drivers/display_generic_4bpp.cpp       \
+drivers/display_st7735.cpp
 
 ifeq ("$(VERBOSE)","1")
 Q := 
diff --git a/_examples/display_st7735/main.cpp b/_examples/display_st7735/main.cpp
new file mode 100644
index 0000000..f8a561a
--- /dev/null
+++ b/_examples/display_st7735/main.cpp
@@ -0,0 +1,21 @@
+#include <cstdio>
+#include "miosix.h"
+#include "mxgui/display.h"
+#include "mxgui/misc_inst.h"
+
+using namespace std;
+using namespace miosix;
+using 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_st7735/retargeted-display.cpp b/_examples/display_st7735/retargeted-display.cpp
new file mode 100644
index 0000000..b245cba
--- /dev/null
+++ b/_examples/display_st7735/retargeted-display.cpp
@@ -0,0 +1,101 @@
+
+#include "miosix.h"
+#include "mxgui/drivers/display_st7735.h"
+
+#ifndef _BOARD_STM32F4DISCOVERY
+#warning "This SPI driver has only been tested on an STM32F4DISCOVERY"
+#endif //_BOARD_STM32F4DISCOVERY
+
+using namespace miosix;
+using namespace mxgui;
+
+//Hardware mapping
+using scl  = Gpio<GPIOB_BASE, 13>; //PB13,  SPI1_SCK (af5)
+using sda  = Gpio<GPIOB_BASE, 15>; //PB15,  SPI1_MOSI (af5)
+using csx  = Gpio<GPIOB_BASE, 4>;  //PB4,   free I/O pin
+using dcx  = Gpio<GPIOA_BASE, 8>;  //PA8,   free I/O pin, used only in 4-line SPI
+using resx = Gpio<GPIOC_BASE, 6>;  //PC6,   free I/O pin
+
+/**
+ * Non-abstract class retargeting DisplayGenericST7735 to the correct
+ * GPIOs and SPI peripheral
+ */
+class MyDisplay : public DisplayGenericST7735
+{
+public:
+    static MyDisplay& instance()
+    {
+        static MyDisplay singleton;
+        return singleton;
+    }
+    
+private:
+    MyDisplay() : DisplayGenericST7735(csx::getPin(),dcx::getPin(),resx::getPin())
+    {
+        {
+            FastInterruptDisableLock dLock;
+
+            RCC->APB1ENR |= RCC_APB1ENR_SPI2EN;
+            SPI2->CR1 = 0;
+            SPI2->CR1 = SPI_CR1_SSM   //Software cs
+                    | SPI_CR1_SSI     //Hardware cs internally tied high
+                    | SPI_CR1_BR_0    //clock divider: 4  ->  10,5 MHz -> 95 ns
+                    | SPI_CR1_MSTR    //Master mode
+                    | SPI_CR1_SPE;    //SPI enabled
+
+            scl::mode(Mode::ALTERNATE);     scl::alternateFunction(5);
+            sda::mode(Mode::ALTERNATE);     sda::alternateFunction(5);
+            // GPIO software controlled
+            csx.mode(Mode::OUTPUT);
+            dcx.mode(Mode::OUTPUT);
+            resx.mode(Mode::OUTPUT);
+        }
+        initialize();
+    }
+
+    unsigned char writeRam(unsigned char data) override
+    {
+        SPI2->DR = data;
+        while((SPI2->SR & SPI_SR_RXNE) == 0) ;
+        return SPI2->DR; //Note: reading back SPI2->DR is necessary.
+    }
+
+    void writeReg(unsigned char reg, unsigned char data) override
+    {
+        Transaction t(csx);
+        {
+            Transaction c(dcx);
+            writeRam(reg);
+        }
+        writeRam(data);
+    }
+
+    void writeReg(unsigned char reg, const unsigned char *data=0, int len=1) override
+    {
+        Transaction t(csx);
+        {
+            Transaction c(dcx);
+            writeRam(reg);
+        }
+        if(data) for(int i = 0; i < len; i++) writeRam(*data++);
+    }
+};
+
+/*
+ * 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(&MyDisplay::instance());
+}
+} //namespace mxgui
diff --git a/drivers/display_st7735.cpp b/drivers/display_st7735.cpp
index 8beb6bd..3b51b9d 100644
--- a/drivers/display_st7735.cpp
+++ b/drivers/display_st7735.cpp
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2013 by Salaorni Davide, Velati Matteo                  *
+ *   Copyright (C) 2021 by Salaorni Davide, Velati Matteo                  *
  *                                                                         *
  *   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  *
@@ -26,22 +26,15 @@
  ***************************************************************************/
 
 #include "display_st7735.h"
-#include "miosix.h"
-#include <cstdarg>
+#include "font.h"
+#include "image.h"
+#include "misc_inst.h"
+#include "line.h"
 
 using namespace std;
 using namespace miosix;
 
-#ifdef _BOARD_STM32F4DISCOVERY
-
 namespace mxgui {
-    
-/**
- * Function to attach the display we are using.
- */
-void registerDisplayHook(DisplayManager& dm) {
-    dm.registerDisplay(&DisplayImpl::instance());
-}
 
 /**
  * Init sequence for the correct functioning of the ST7735 display
@@ -71,51 +64,46 @@ const unsigned char initST7735b[] = {
 };
 
 /**
- * Class DisplayImpl
+ * Class DisplayGenericST7735
  */
-DisplayImpl& DisplayImpl::instance() {
-    static DisplayImpl instance;
-    return instance;
-}
-
-void DisplayImpl::doTurnOn() {
+void DisplayGenericST7735::doTurnOn() {
     writeReg(0x29);     //ST7735_DISPON 
     delayMs(150);
 }
 
-void DisplayImpl::doTurnOff() {
+void DisplayGenericST7735::doTurnOff() {
     writeReg(0x28);     //ST7735_DISPOFF 
     delayMs(150);
 }
 
-void DisplayImpl::doSetBrightness(int brt) {
+void DisplayGenericST7735::doSetBrightness(int brt) {
     //No function to set brightness for this display
 }
 
-pair<short int, short int> DisplayImpl::doGetSize() const {
+pair<short int, short int> DisplayGenericST7735::doGetSize() const {
     return make_pair(height, width);
 }
 
-void DisplayImpl::write(Point p, const char *text) {
+void DisplayGenericST7735::write(Point p, const char *text) {
     font.draw(*this, textColor, p, text);
 }
 
-void DisplayImpl::clippedWrite(Point p, Point a,  Point b, const char *text) {
+void DisplayGenericST7735::clippedWrite(Point p, Point a,  Point b, const char *text) {
     font.clippedDraw(*this, textColor, p, a, b, text);
 }
 
-void DisplayImpl::clear(Color color) {
+void DisplayGenericST7735::clear(Color color) {
     clear(Point(0,0), Point(width-1, height-1), color);
 }
 
-void DisplayImpl::clear(Point p1, Point p2, Color color) {
+void DisplayGenericST7735::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);
 
-    SPITransaction t;
+    Transaction t(csx);
     writeRamBegin();
 
     //Send data to write on GRAM
@@ -125,22 +113,22 @@ void DisplayImpl::clear(Point p1, Point p2, Color color) {
     }
 }
 
-void DisplayImpl::beginPixel() {
+void DisplayGenericST7735::beginPixel() {
     imageWindow(Point(0,0), Point(width-1, height-1));
 }
 
-void DisplayImpl::setPixel(Point p, Color color) {
+void DisplayGenericST7735::setPixel(Point p, Color color) {
     unsigned char lsb = color & 0xFF;
     unsigned char msb = (color >> 8) & 0xFF;
 
     setCursor(p);
-    SPITransaction t;
+    Transaction t(csx);
     writeRamBegin();
     writeRam(msb);
     writeRam(lsb);
 }
 
-void DisplayImpl::line(Point a, Point b, Color color) {
+void DisplayGenericST7735::line(Point a, Point b, Color color) {
     unsigned char lsb = color & 0xFF;
     unsigned char msb = (color >> 8) & 0xFF;
 
@@ -150,7 +138,7 @@ void DisplayImpl::line(Point a, Point b, Color color) {
         imageWindow(Point(min(a.x(), b.x()), a.y()), Point(max(a.x(), b.x()), a.y()));
         int numPixels = abs(a.x() - b.x());
 
-        SPITransaction t;
+        Transaction t(csx);
         writeRamBegin();
 
         //Send data to write on GRAM
@@ -166,7 +154,7 @@ void DisplayImpl::line(Point a, Point b, Color color) {
         textWindow(Point(a.x(), min(a.y(), b.y())), Point(a.x(), max(a.y(), b.y())));
         int numPixels = abs(a.y() - b.y());
 
-        SPITransaction t;
+        Transaction t(csx);
         writeRamBegin();
 
         //Send data to write on GRAM
@@ -181,14 +169,14 @@ void DisplayImpl::line(Point a, Point b, Color color) {
     Line::draw(*this, a, b, color);
 }
 
-void DisplayImpl::scanLine(Point p, const Color *colors, unsigned short length) {
+void DisplayGenericST7735::scanLine(Point p, const Color *colors, unsigned short length) {
     unsigned char lsb = 0x00;
     unsigned char msb = 0x00;
 
     if(p.x() + length > width) { return; }
     imageWindow(p, Point(width - 1, p.y()));
 
-    SPITransaction t;
+    Transaction t(csx);
     writeRamBegin();
 
     //Send data to write on GRAM
@@ -201,16 +189,16 @@ void DisplayImpl::scanLine(Point p, const Color *colors, unsigned short length)
     }
 }
 
-Color *DisplayImpl::getScanLineBuffer() {
+Color *DisplayGenericST7735::getScanLineBuffer() {
     if(buffer == 0) buffer = new Color[getWidth()];
     return buffer;
 }
 
-void DisplayImpl::scanLineBuffer(Point p, unsigned short length) {
+void DisplayGenericST7735::scanLineBuffer(Point p, unsigned short length) {
     scanLine(p, buffer, length);
 }
 
-void DisplayImpl::drawImage(Point p, const ImageBase& img) {
+void DisplayGenericST7735::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; }
@@ -225,7 +213,7 @@ void DisplayImpl::drawImage(Point p, const ImageBase& img) {
         imageWindow(p, Point(xEnd, yEnd));
         int numPixels = img.getHeight() * img.getWidth();
 
-        SPITransaction t;
+        Transaction t(csx);
         writeRamBegin();
 
         for(int i=0; i <= numPixels; i++)
@@ -239,18 +227,18 @@ void DisplayImpl::drawImage(Point p, const ImageBase& img) {
     else { img.draw(*this,p); }
 }
 
-void DisplayImpl::clippedDrawImage(Point p, Point a, Point b, const ImageBase& img) {
+void DisplayGenericST7735::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) {
+void DisplayGenericST7735::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);
 }
 
-void DisplayImpl::window(Point p1, Point p2, bool swap) {
+void DisplayGenericST7735::window(Point p1, Point p2, bool swap) {
     #ifdef MXGUI_ORIENTATION_VERTICAL
         char caset_offset = 2;
         char raset_offset = 1;
@@ -284,12 +272,12 @@ void DisplayImpl::window(Point p1, Point p2, bool swap) {
     }
 }
 
-void DisplayImpl::update() {
+void DisplayGenericST7735::update() {
     // Useless for ST7735 display
 }
 
-DisplayImpl::pixel_iterator
-DisplayImpl::begin(Point p1, Point p2, IteratorDirection d) {
+DisplayGenericST7735::pixel_iterator
+DisplayGenericST7735::begin(Point p1, Point p2, IteratorDirection d) {
         if(p1.x()<0 || p1.y()<0 || p2.x()<0 || p2.y()<0) {
             return pixel_iterator();
         }
@@ -303,7 +291,7 @@ DisplayImpl::begin(Point p1, Point p2, IteratorDirection d) {
         if(d == DR) { textWindow(p1, p2); }
         else { imageWindow(p1, p2); }
 
-        SPITransaction t;
+        Transaction t(csx);
         writeRamBegin();
 
         unsigned int numPixels = (p2.x() - p1.x() + 1) * (p2.y() - p1.y() + 1);
@@ -311,40 +299,24 @@ DisplayImpl::begin(Point p1, Point p2, IteratorDirection d) {
 }
 
 //Destructor
-DisplayImpl::~DisplayImpl() {
+DisplayGenericST7735::~DisplayGenericST7735() {
     if(buffer) delete[] buffer;
 }
 
 //Constructor
-DisplayImpl::DisplayImpl(): buffer(0) {
-    {
-        FastInterruptDisableLock dLock;
-
-        RCC->APB1ENR |= RCC_APB1ENR_SPI2EN;
-        SPI2->CR1 = 0;
-        SPI2->CR1 = SPI_CR1_SSM     //Software cs
-                  | SPI_CR1_SSI     //Hardware cs internally tied high
-                  | SPI_CR1_BR_0    //clock divider: 4  ->  10,5 MHz -> 95 ns
-                  | SPI_CR1_MSTR    //Master mode
-                  | SPI_CR1_SPE;    //SPI enabled
-
-        scl::mode(Mode::ALTERNATE);     scl::alternateFunction(5);
-        sda::mode(Mode::ALTERNATE);     sda::alternateFunction(5);
-        // GPIO software controlled
-        csx::mode(Mode::OUTPUT);
-        dcx::mode(Mode::OUTPUT);
-        resx::mode(Mode::OUTPUT);
-    }
-    
-    csx::high();
-    dcx::high();
+DisplayGenericST7735::DisplayGenericST7735(GpioPin csx, GpioPin dcx, GpioPin resx)
+    : csx(csx), dcx(dcx), resx(resx), buffer(0) {}
+
+void DisplayGenericST7735::initialize() {
+    csx.high();
+    dcx.high();
 
     // POWER ON SEQUENCE: HW RESET -> SW RESET -> SLPOUT
-    resx::high();
+    resx.high();
     delayMs(150);
-    resx::low();
+    resx.low();
     delayMs(150);
-    resx::high();
+    resx.high();
     delayMs(150);
 
     writeReg(0x01);    // ST7735_SWRESET
@@ -359,41 +331,10 @@ DisplayImpl::DisplayImpl(): buffer(0) {
     setTextColor(make_pair(white, black));
 }
 
-/**
- * Write only commands with one parameter.
- */
-void DisplayImpl::writeReg(unsigned char reg, unsigned char data)
-{
-    SPITransaction t;
-    {
-        CommandTransaction c;
-        writeRam(reg);
-    }
-    writeRam(data);
-}
-
-/**
- * Write commands with more parameters.
- */
-void DisplayImpl::writeReg(unsigned char reg, const unsigned char *data, int len)
-{
-     SPITransaction t;
-    {
-        CommandTransaction c;
-        writeRam(reg);
-    }
-    if(data)
-    {
-        for(int i = 0; i < len; i++) {
-            writeRam(*data++);
-        }
-    }
-}
-
 /**
  * Send commands of 8 bits to the MCU of the display.
  */
-void DisplayImpl::sendCmds(const unsigned char *cmds) {
+void DisplayGenericST7735::sendCmds(const unsigned char *cmds) {
     while(*cmds)
     {
         unsigned char cmd = *cmds++;
@@ -405,7 +346,3 @@ void DisplayImpl::sendCmds(const unsigned char *cmds) {
 }
 
 } //mxgui
-
-#else
-#warning "This SPI driver has only been tested on an STM32F4DISCOVERY"
-#endif //_BOARD_STM32F4DISCOVERY
diff --git a/drivers/display_st7735.h b/drivers/display_st7735.h
index f5fa19e..d248460 100644
--- a/drivers/display_st7735.h
+++ b/drivers/display_st7735.h
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2013 by Salaorni Davide, Velati Matteo                  *
+ *   Copyright (C) 2021 by Salaorni Davide, Velati Matteo                  *
  *                                                                         *
  *   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  *
@@ -25,70 +25,41 @@
  *   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_ST7735H
-#define DISPLAY_ST7735H
-
-#ifdef _BOARD_STM32F4DISCOVERY
+#pragma once
 
 #include <config/mxgui_settings.h>
 #include "display.h"
 #include "point.h"
 #include "color.h"
-#include "font.h"
-#include "image.h"
 #include "iterator_direction.h"
-#include "misc_inst.h"
-#include "line.h"
 #include "miosix.h"
 #include <cstdio>
 #include <cstring>
 #include <algorithm>
 
-using namespace std;
-using namespace miosix;
-
 namespace mxgui {
 
 #ifndef MXGUI_COLOR_DEPTH_16_BIT
 #error The ST7735 driver requires a color depth 16 per pixel
 #endif
 
-//Hardware mapping
-typedef Gpio<GPIOB_BASE, 13> scl;   //PB13,  SPI1_SCK (af5)
-typedef Gpio<GPIOB_BASE, 15> sda;   //PB15,  SPI1_MOSI (af5)
-typedef Gpio<GPIOB_BASE, 4> csx;    //PB4,   free I/O pin
-typedef Gpio<GPIOC_BASE, 6> resx;   //PC6,   free I/O pin
-typedef Gpio<GPIOA_BASE, 8> dcx;    //PA8,   free I/O pin, used only in 4-line SPI
-
-//A falling edge of CSX enables the SPI transaction
-class SPITransaction
-{
-public:
-    SPITransaction()  { csx::low();  }
-    ~SPITransaction() { csx::high(); }
-};
-
-//A falling edge on DCX means that the transmitted byte is a command
-class CommandTransaction
+//Used to transiently pull low either the csx or dcx pin
+class Transaction
 {
 public:
-    CommandTransaction()  { dcx::low();  }
-    ~CommandTransaction() { dcx::high(); }
+    Transaction(miosix::GpioPin pin) : pin(pin)  { pin.low();  }
+    ~Transaction() { pin.high(); }
+private:
+    miosix::GpioPin pin;
 };
 
-class DisplayImpl : public Display
+/**
+ * Generic driver for a ST7735 display. The SPI interface and mapping of the
+ * csx, dcx and resx pins is retargetable.
+ */
+class DisplayGenericST7735 : 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 On.
@@ -252,9 +223,9 @@ public:
             unsigned char lsb = color & 0xFF;
             unsigned char msb = (color >> 8) & 0xFF;
 
-            SPITransaction t;
-            writeRam(msb);
-            writeRam(lsb);
+            Transaction t(display->csx);
+            display->writeRam(msb);
+            display->writeRam(lsb);
 
             return *this;
         }
@@ -306,8 +277,9 @@ public:
         pixel_iterator(unsigned int pixelLeft): pixelLeft(pixelLeft) {}
 
         unsigned int pixelLeft; ///< How many pixels are left to draw
+        DisplayGenericST7735 *display;
 
-        friend class DisplayImpl; //Needs access to ctor
+        friend class DisplayGenericST7735; //Needs access to ctor
     };
 
     /**
@@ -335,31 +307,43 @@ public:
     /**
      * Destructor
      */
-    ~DisplayImpl() override;
+    ~DisplayGenericST7735() override;
 
+protected:
+
+    /**
+     * Constructor.
+     * \param csx chip select pin
+     * \param dcx data/command pin
+     * \param resx reset pin
+     */
+    DisplayGenericST7735(miosix::GpioPin csx,
+                         miosix::GpioPin dcx,
+                         miosix::GpioPin resx);
+
+    void initialize();
+    
+    miosix::GpioPin csx;    ///< Chip select
+    miosix::GpioPin dcx;    ///< Data/Command
+    miosix::GpioPin resx;   ///< Reset
+    
 private:
 
     #if defined MXGUI_ORIENTATION_VERTICAL
-        static const short int width    = 128;
-        static const short int height   = 160;
+    static const short int width    = 128;
+    static const short int height   = 160;
     #elif defined MXGUI_ORIENTATION_HORIZONTAL
-        static const short int width    = 160;
-        static const short int height   = 128;
+    static const short int width    = 160;
+    static const short int height   = 128;
     #else
-        #error Orientation not defined
+    #error Orientation not defined
     #endif
 
-    /**
-     * Constructor.
-     * Do not instantiate objects of this type directly from application code.
-     */
-    DisplayImpl();
-
     /**
      * Set cursor to desired location
      * \param point where to set cursor (0<=x<=127, 0<=y<=159)
      */
-    static inline void setCursor(Point p)
+    inline void setCursor(Point p)
     {
         window(p, p, false);
     }
@@ -382,7 +366,7 @@ private:
      * \param p1 upper left corner of the window
      * \param p2 lower right corner of the window
      */
-    static inline void textWindow(Point p1, Point p2)
+    inline void textWindow(Point p1, Point p2)
     {
         #ifdef MXGUI_ORIENTATION_VERTICAL
             writeReg (0x36, 0xE0);      // MADCTL:  MX + MY + MV
@@ -400,7 +384,7 @@ private:
      * \param p1 upper left corner of the window
      * \param p2 lower right corner of the window
      */
-    static inline void imageWindow(Point p1, Point p2)
+    inline void imageWindow(Point p1, Point p2)
     {
         #ifdef MXGUI_ORIENTATION_VERTICAL
             writeReg (0x36, 0xC0);      // MADCTL:  MX + MY
@@ -414,14 +398,14 @@ private:
     /**
      * Common part of all window commands
      */
-    static void window(Point p1, Point p2, bool swap);
+    void window(Point p1, Point p2, bool swap);
 
     /**
      * Sends command 0x2C to signal the start of data sending
      */
-    static void writeRamBegin()
+    void writeRamBegin()
     {
-        CommandTransaction c;
+        Transaction c(dcx);
         writeRam(0x2C);     //ST7735_RAMWR, to write the GRAM
     }
 
@@ -430,19 +414,14 @@ private:
      * The SPI chip select must be low before calling this member function
      * \param data data to write
      */
-    static unsigned char writeRam(unsigned char data)
-    {
-        SPI2->DR = data;
-        while((SPI2->SR & SPI_SR_RXNE) == 0) ;
-        return SPI2->DR; //Note: reading back SPI2->DR is necessary.
-    }
+    virtual unsigned char writeRam(unsigned char data) = 0;
 
     /**
      * Write data to a display register
      * \param reg which register?
      * \param data data to write
      */
-    static void writeReg(unsigned char reg, unsigned char data);
+    virtual void writeReg(unsigned char reg, unsigned char data) = 0;
 
     /**
      * Write data to a display register
@@ -450,19 +429,15 @@ private:
      * \param data data to write, if null only reg will be written (zero arg cmd)
      * \param len length of data, number of argument bytes
      */
-    static void writeReg(unsigned char reg, const unsigned char *data=0, int len=1);
+    virtual void writeReg(unsigned char reg, const unsigned char *data=0, int len=1) = 0;
 
     /**
      * Send multiple commands to the display MCU (we use to send init sequence)
      * \param cmds static array containing the commands
      */
-    static void sendCmds(const unsigned char *cmds);
+    void sendCmds(const unsigned char *cmds);
 
-    Color *buffer;          //< For scanLineBuffer
+    Color *buffer;          ///< For scanLineBuffer
 };
 
 } //namespace mxgui
-
-#endif //_BOARD_STM32F4DISCOVERY
-
-#endif //DISPLAY_ST7735H
-- 
GitLab