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