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