diff --git a/Makefile b/Makefile index 25aed4fba44dc04cc6135957bdf4ef990126420b..c50eb5c4c3a8843bfdb5182f068e1b052feb1eb7 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,7 @@ drivers/display_mp3v2.cpp \ drivers/event_mp3v2.cpp \ drivers/resfs_mp3v2.cpp \ drivers/display_strive.cpp \ +drivers/display_st7735.cpp \ drivers/display_oledboard2.cpp \ drivers/event_strive.cpp \ drivers/display_redbull_v2.cpp \ diff --git a/drivers/display_st7735.cpp b/drivers/display_st7735.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8beb6bdc439489358d4f0b51f57b713c29899b7f --- /dev/null +++ b/drivers/display_st7735.cpp @@ -0,0 +1,411 @@ +/*************************************************************************** + * Copyright (C) 2013 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 * + * 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_st7735.h" +#include "miosix.h" +#include <cstdarg> + +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 + */ +const unsigned char initST7735b[] = { + 0x3A, 0X01, 0x05, // ST7735_COLMOD, color mode: 16-bit/pixel + 0xB1, 0x03, 0x00, 0x06, 0x03, // ST7735_FRMCTR1, normal mode frame rate + 0xB6, 0x02, 0x15, 0x02, // ST7735_DISSET5, display settings + 0xB4, 0x01, 0x00, // ST7735_INVCTR, line inversion active + 0xC0, 0x02, 0x02, 0x70, // ST7735_PWCTR1, default (4.7V, 1.0 uA) + 0xC1, 0x01, 0x05, // ST7735_PWCTR2, default (VGH=14.7V, VGL=-7.35V) + 0xC3, 0x02, 0x02, 0x07, // ST7735_PWCTR4, bclk/2, opamp current small and medium low + 0xC5, 0x02, 0x3C, 0x38, // ST7735_VMCTR1, VCOMH=4V VCOML=-1.1 + 0xFC, 0x02, 0x11, 0x15, // ST7735_PWCTR6, power control (partial mode+idle) + 0xE0, 0x10, // ST7735_GMCTRP1, Gamma adjustments (pos. polarity) + 0x09, 0x16, 0x09, 0x20, // (Not entirely necessary, but provides + 0x21, 0x1B, 0x13, 0x19, // accurate colors) + 0x17, 0x15, 0x1E, 0x2B, + 0x04, 0x05, 0x02, 0x0E, + 0xE1, 0x10, // ST7735_GMCTRN1, Gamma adjustments (neg. polarity) + 0x0B, 0x14, 0x08, 0x1E, // (Not entirely necessary, but provides + 0x22, 0x1D, 0x18, 0x1E, // accurate colors) + 0x1B, 0x1A, 0x24, 0x2B, + 0x06, 0x06, 0x02, 0x0F, + 0x13, // ST7735_NORON, normal display mode on + 0x00 +}; + +/** + * Class DisplayImpl + */ +DisplayImpl& DisplayImpl::instance() { + static DisplayImpl instance; + return instance; +} + +void DisplayImpl::doTurnOn() { + writeReg(0x29); //ST7735_DISPON + delayMs(150); +} + +void DisplayImpl::doTurnOff() { + writeReg(0x28); //ST7735_DISPOFF + delayMs(150); +} + +void DisplayImpl::doSetBrightness(int brt) { + //No function to set brightness for this display +} + +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); + + SPITransaction t; + writeRamBegin(); + + //Send data to write on GRAM + for(int i=0; i < numPixels; i++) { + writeRam(msb); + writeRam(lsb); + } +} + +void DisplayImpl::beginPixel() { + imageWindow(Point(0,0), Point(width-1, height-1)); +} + +void DisplayImpl::setPixel(Point p, Color color) { + unsigned char lsb = color & 0xFF; + unsigned char msb = (color >> 8) & 0xFF; + + setCursor(p); + SPITransaction t; + writeRamBegin(); + writeRam(msb); + writeRam(lsb); +} + +void DisplayImpl::line(Point a, Point b, Color color) { + unsigned char lsb = color & 0xFF; + unsigned char msb = (color >> 8) & 0xFF; + + //Horizontal line speed optimization + if(a.y() == b.y()) + { + imageWindow(Point(min(a.x(), b.x()), a.y()), Point(max(a.x(), b.x()), a.y())); + int numPixels = abs(a.x() - b.x()); + + SPITransaction t; + writeRamBegin(); + + //Send data to write on GRAM + for(int i=0; i <= numPixels; i++) { + writeRam(msb); + writeRam(lsb); + } + return; + } + //Vertical line speed optimization + if(a.x() == b.x()) + { + textWindow(Point(a.x(), min(a.y(), b.y())), Point(a.x(), max(a.y(), b.y()))); + int numPixels = abs(a.y() - b.y()); + + SPITransaction t; + writeRamBegin(); + + //Send data to write on GRAM + for(int i=0; i <= numPixels; i++) { + writeRam(msb); + writeRam(lsb); + } + return; + } + //General case, always works but it is much slower due to the display + //not having fast random access to pixels + Line::draw(*this, a, b, color); +} + +void DisplayImpl::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; + writeRamBegin(); + + //Send data to write on GRAM + for(int i=0; i < length; i++) { + lsb = colors[i] & 0xFF; + msb = (colors[i] >> 8) & 0xFF; + + writeRam(msb); + writeRam(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(); + + SPITransaction t; + writeRamBegin(); + + for(int i=0; i <= numPixels; i++) + { + lsb = imgData[i] & 0xFF; + msb = (imgData[i] >> 8) & 0xFF; + writeRam(msb); + writeRam(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); +} + +void DisplayImpl::window(Point p1, Point p2, bool swap) { + #ifdef MXGUI_ORIENTATION_VERTICAL + char caset_offset = 2; + char raset_offset = 1; + #else //MXGUI_ORIENTATION_HORIZONTAL + char caset_offset = 1; + char raset_offset = 2; + #endif + + //Setting column bounds, ST7735_CASET (adding offset) + unsigned char buff_caset[4]; + buff_caset[0] = (p1.x()+caset_offset)>>8 & 255; + buff_caset[1] = (p1.x()+caset_offset) & 255; + buff_caset[2] = (p2.x()+caset_offset)>>8 & 255; + buff_caset[3] = (p2.x()+caset_offset) & 255; + + //Setting row bounds, ST7735_RASET (adding offset) + unsigned char buff_raset[4]; + buff_raset[0] = (p1.y()+raset_offset)>>8 & 255; + buff_raset[1] = (p1.y()+raset_offset) & 255; + buff_raset[2] = (p2.y()+raset_offset)>>8 & 255; + buff_raset[3] = (p2.y()+raset_offset) & 255; + + // For drawing texts, swap the caset and raset buffers + if (swap){ + writeReg(0x2A, buff_raset, sizeof(buff_raset)); + writeReg(0x2B, buff_caset, sizeof(buff_caset)); + } + else { + writeReg(0x2A, buff_caset, sizeof(buff_caset)); + writeReg(0x2B, buff_raset, sizeof(buff_raset)); + } +} + +void DisplayImpl::update() { + // Useless for ST7735 display +} + +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); } + + SPITransaction t; + writeRamBegin(); + + unsigned int numPixels = (p2.x() - p1.x() + 1) * (p2.y() - p1.y() + 1); + return pixel_iterator(numPixels); +} + +//Destructor +DisplayImpl::~DisplayImpl() { + 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(); + + // POWER ON SEQUENCE: HW RESET -> SW RESET -> SLPOUT + resx::high(); + delayMs(150); + resx::low(); + delayMs(150); + resx::high(); + delayMs(150); + + writeReg(0x01); // ST7735_SWRESET + delayMs(150); + writeReg(0x11); // ST7735_SLPOUT + delayMs(150); + + sendCmds(initST7735b); + + doTurnOn(); + setFont(droid11); + 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) { + while(*cmds) + { + unsigned char cmd = *cmds++; + unsigned char numArgs = *cmds++; + writeReg(cmd, cmds, numArgs); + cmds += numArgs; + delayUs(1); + } +} + +} //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 new file mode 100644 index 0000000000000000000000000000000000000000..f5fa19e7d4862bd967973066adeb4a8dd29c1c1d --- /dev/null +++ b/drivers/display_st7735.h @@ -0,0 +1,468 @@ +/*************************************************************************** + * Copyright (C) 2013 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 * + * 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_ST7735H +#define DISPLAY_ST7735H + +#ifdef _BOARD_STM32F4DISCOVERY + +#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 +{ +public: + CommandTransaction() { dcx::low(); } + ~CommandTransaction() { dcx::high(); } +}; + +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 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. + * \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 img 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; + + /** + * Make all changes done to the display since the last call to update() + * visible. + */ + void update() override; + + /** + * Pixel iterator. A pixel iterator is an output iterator that allows to + * define a window on the display and write to its pixels. + */ + class pixel_iterator + { + public: + /** + * Default constructor, results in an invalid iterator. + */ + pixel_iterator(): pixelLeft(0) {} + + /** + * Set a pixel and move the pointer to the next one + * \param color color to set the current pixel + * \return a reference to this + */ + pixel_iterator& operator= (Color color) + { + pixelLeft--; + + unsigned char lsb = color & 0xFF; + unsigned char msb = (color >> 8) & 0xFF; + + SPITransaction t; + writeRam(msb); + writeRam(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) {} + + unsigned int pixelLeft; ///< How many pixels are left to draw + + friend class DisplayImpl; //Needs access to ctor + }; + + /** + * Specify a window on screen and return an object that allows to write + * its pixels. + * Note: a call to begin() will invalidate any previous iterator. + * \param p1 upper left corner of window + * \param p2 lower right corner (included) + * \param d increment direction + * \return a pixel iterator + */ + pixel_iterator begin(Point p1, Point p2, IteratorDirection d); + + /** + * \return an iterator which is one past the last pixel in the pixel + * specified by begin. Behaviour is undefined if called before calling + * begin() + */ + pixel_iterator end() const + { + // Default ctor: pixelLeft is zero + return pixel_iterator(); + } + + /** + * Destructor + */ + ~DisplayImpl() override; + +private: + + #if defined MXGUI_ORIENTATION_VERTICAL + 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; + #else + #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) + { + window(p, p, false); + } + + /** + * Register 0x36: MADCTL + * bit 7------0 + * 4: |||||+-- MH horizontal referesh (0 L-to-R, 1 R-to-L) + * 8: ||||+--- RGB BRG order (0 for RGB) + * 16: |||+---- ML vertical refesh (0 T-to-B, 1 B-to-T) + * 32: ||+----- MV row column exchange (1 for X-Y exchange) + * 64: |+------ MX column address order (1 for mirror X axis) + * 128: +------- MY row address order (1 for mirror Y axis) + */ + + /** + * 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 + writeReg (0x36, 0xE0); // MADCTL: MX + MY + MV + window(p1, p2, true); + #else //MXGUI_ORIENTATION_HORIZONTAL + writeReg (0x36, 0x80); // MADCTL: MY + window(p1, p2, true); + #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 + writeReg (0x36, 0xC0); // MADCTL: MX + MY + window(p1, p2, false); + #else //MXGUI_ORIENTATION_HORIZONTAL + writeReg (0x36, 0xA0); // MADCTL: MY + MV + window(p1, p2, false); + #endif + } + + /** + * Common part of all window commands + */ + static void window(Point p1, Point p2, bool swap); + + /** + * Sends command 0x2C to signal the start of data sending + */ + static void writeRamBegin() + { + CommandTransaction c; + writeRam(0x2C); //ST7735_RAMWR, to write the GRAM + } + + /** + * Used to send pixel data to the display's RAM, and also to send commands. + * 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. + } + + /** + * Write data to a display register + * \param reg which register? + * \param data data to write + */ + static void writeReg(unsigned char reg, unsigned char data); + + /** + * Write data to a display register + * \param reg which register? + * \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); + + /** + * 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); + + Color *buffer; //< For scanLineBuffer +}; + +} //namespace mxgui + +#endif //_BOARD_STM32F4DISCOVERY + +#endif //DISPLAY_ST7735H