diff --git a/drivers/display_oledboard2.cpp b/drivers/display_oledboard2.cpp index 01bff4783d08fbf707bf33d2b3e4402ef64e0c86..7acceab5b288fdf329af2c2c401618c9065ffefd 100644 --- a/drivers/display_oledboard2.cpp +++ b/drivers/display_oledboard2.cpp @@ -83,10 +83,251 @@ static void sendCommand16(unsigned char reg, unsigned short val) delayUs(1); } +void registerDisplayHook(DisplayManager& dm) +{ + dm.registerDisplay(&DisplayImpl::instance()); +} + // // Class DisplayImpl // +DisplayImpl& DisplayImpl::instance() +{ + static DisplayImpl instance; + return instance; +} + +void DisplayImpl::doTurnOn() +{ + LTDC->GCR |= LTDC_GCR_LTDCEN; + sendCommand8(0x1d,0xa0); + Thread::sleep(200); + //If display supply is provided too early an intense white flash appears + //Total delay should be 250ms, display power is added after the first 200ms + display::vregEn::high(); + Thread::sleep(50); + sendCommand8(0x14,0x03); +} + +void DisplayImpl::doTurnOff() +{ + sendCommand8(0x14,0x00); + Thread::sleep(35); + display::vregEn::low(); + Thread::sleep(15); + sendCommand8(0x1d,0xa1); + LTDC->GCR &=~ LTDC_GCR_LTDCEN; +} + +void DisplayImpl::doSetBrightness(int brt) +{ + int brightness=max(0,min(4,brt/24)); + sendCommand8(0x39,brightness<<4); +} + +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) +{ + if(p1.x()<0 || p2.x()<p1.x() || p2.x()>=width + ||p1.y()<0 || p2.y()<p1.y() || p2.y()>=height) return; + if((color & 0xff)==(color>>8)) + { + //Can use memset + if(p1.x()==0 && p2.x()==width-1) + { + //Can merge lines + memset(framebuffer1+p1.y()*width,color,(p2.y()-p1.y()+1)*width*bpp); + } else { + //Can't merge lines + Color *ptr=framebuffer1+p1.x()+width*p1.y(); + short len=p2.x()-p1.x()+1; + for(short i=p1.y();i<=p2.y();i++) + { + memset(ptr,color,len*bpp); + ptr+=width; + } + } + } else { + //Can't use memset + if(p1.x()==0 && p2.x()==width-1) + { + //Can merge lines + Color *ptr=framebuffer1+p1.y()*width; + int numPixels=(p2.y()-p1.y()+1)*width; + //This loop is worth unrolling + for(int i=0;i<numPixels/4;i++) + { + *ptr++=color; + *ptr++=color; + *ptr++=color; + *ptr++=color; + } + for(int i=0;i<(numPixels & 3);i++) *ptr++=color; + } else { + //Can't merge lines + Color *ptr=framebuffer1+p1.x()+width*p1.y(); + short len=p2.x()-p1.x()+1; + for(short i=p1.y();i<=p2.y();i++) + { + for(short j=0;j<len;j++) *ptr++=color; + ptr+=width-len; + } + } + } +} + +void DisplayImpl::beginPixel() {} + +void DisplayImpl::setPixel(Point p, Color color) +{ + int offset=p.x()+p.y()*width; + if(offset<0 || offset>=numPixels) return; + *(framebuffer1+offset)=color; +} + +void DisplayImpl::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; + Color *ptr=framebuffer1+minx+width*a.y(); + for(short i=minx;i<=maxx;i++) *ptr++=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()); + if(a.x()<0 || a.x()>=width || miny<0 || maxy>=height) return; + Color *ptr=framebuffer1+a.x()+width*miny; + for(short i=miny;i<=maxy;i++) + { + *ptr=color; + ptr+=width; + } + return; + } + //General case + Line::draw(*this,a,b,color); +} + +void DisplayImpl::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; + Color *ptr=framebuffer1+p.x()+p.y()*width; + memcpy(ptr,colors,length*bpp); +} + +Color *DisplayImpl::getScanLineBuffer() +{ + return buffer; +} + +void DisplayImpl::scanLineBuffer(Point p, unsigned short length) +{ + int offset=p.x()+p.y()*width; + if(offset<0 || offset>=numPixels) return; + memcpy(framebuffer1+offset,buffer,length*bpp); +} + +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(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 DisplayImpl::clippedDrawImage(Point p, Point a, Point b, const ImageBase& img) +{ +// if(img.getData()==0) +// { + img.clippedDraw(*this,p,a,b); + return; +// } //else optimized version for memory-loaded images +// //TODO: optimize +// } +} + +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::setTextColor(pair<Color,Color> colors) +{ + Font::generatePalette(textColor,colors.first,colors.second); +} + +pair<Color,Color> DisplayImpl::getTextColor() const +{ + return make_pair(textColor[3],textColor[0]); +} + +void DisplayImpl::setFont(const Font& font) { this->font=font; } + +Font DisplayImpl::getFont() const { return font; } + +void DisplayImpl::update() {} + +DisplayImpl::pixel_iterator DisplayImpl::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); +} + +DisplayImpl::~DisplayImpl() {} + DisplayImpl::DisplayImpl() : framebuffer1(reinterpret_cast<unsigned short*>(0xd0600000)), buffer(framebuffer1+numPixels), font(droid21) @@ -101,7 +342,7 @@ DisplayImpl::DisplayImpl() * current consumption measured as the STOD13AS power supply (the chip used * to generate ELVDD and ELVSS) when powered at 4.3V and with a fully white * display and the gamma settings that produce the maximum brightness. - * As I don't have a way to measure the display brightness un cd/m^2, the + * As I don't have a way to measure the display brightness in cd/m^2, the * brightness data is measured connecting a SFH216 photodiode to an * oscilloscope and measuring the voltage produced in mV when facing the * display. @@ -258,116 +499,9 @@ DisplayImpl::DisplayImpl() sendCommand8(0x23,0x00); sendCommand8(0x26,0xa0); - setTextColor(Color(0xffff),Color(0x0000)); + setTextColor(make_pair(Color(0xffff),Color(0x0000))); clear(black); - turnOn(); -} - -void DisplayImpl::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; - if((color & 0xff)==(color>>8)) - { - //Can use memset - if(p1.x()==0 && p2.x()==width-1) - { - //Can merge lines - memset(framebuffer1+p1.y()*width,color,(p2.y()-p1.y()+1)*width*bpp); - } else { - //Can't merge lines - Color *ptr=framebuffer1+p1.x()+width*p1.y(); - short len=p2.x()-p1.x()+1; - for(short i=p1.y();i<=p2.y();i++) - { - memset(ptr,color,len*bpp); - ptr+=width; - } - } - } else { - //Can't use memset - if(p1.x()==0 && p2.x()==width-1) - { - //Can merge lines - Color *ptr=framebuffer1+p1.y()*width; - int numPixels=(p2.y()-p1.y()+1)*width; - //This loop is worth unrolling - for(int i=0;i<numPixels/4;i++) - { - *ptr++=color; - *ptr++=color; - *ptr++=color; - *ptr++=color; - } - for(int i=0;i<(numPixels & 3);i++) *ptr++=color; - } else { - //Can't merge lines - Color *ptr=framebuffer1+p1.x()+width*p1.y(); - short len=p2.x()-p1.x()+1; - for(short i=p1.y();i<=p2.y();i++) - { - for(short j=0;j<len;j++) *ptr++=color; - ptr+=width-len; - } - } - } -} - -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::turnOn() -{ - LTDC->GCR |= LTDC_GCR_LTDCEN; - sendCommand8(0x1d,0xa0); - Thread::sleep(200); - //If display supply is provided too early an intense white flash appears - //Total delay should be 250ms, display power is added after the first 200ms - display::vregEn::high(); - Thread::sleep(50); - sendCommand8(0x14,0x03); -} - -void DisplayImpl::turnOff() -{ - sendCommand8(0x14,0x00); - Thread::sleep(35); - display::vregEn::low(); - Thread::sleep(15); - sendCommand8(0x1d,0xa1); - LTDC->GCR &=~ LTDC_GCR_LTDCEN; -} - -void DisplayImpl::setBrightness(int brt) -{ - int brightness=max(0,min(4,brt/24)); - sendCommand8(0x39,brightness<<4); -} - -DisplayImpl::pixel_iterator DisplayImpl::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); + doTurnOn(); } Color DisplayImpl::pixel_iterator::dummy; diff --git a/drivers/display_oledboard2.h b/drivers/display_oledboard2.h index 7ab530b7c12ba1041b9ae7a00a1768a8e5e8482d..941a73c938a9255560d16d73288851a6189b2075 100644 --- a/drivers/display_oledboard2.h +++ b/drivers/display_oledboard2.h @@ -36,6 +36,7 @@ #ifdef _BOARD_STM32F429ZI_OLEDBOARD2 #include <config/mxgui_settings.h> +#include "display.h" #include "point.h" #include "color.h" #include "font.h" @@ -52,28 +53,46 @@ namespace mxgui { //This display is 16 bit per pixel, check that the color depth is properly //configured #ifndef MXGUI_COLOR_DEPTH_16_BIT -#error The AMS369FG03-0 driver requires a color depth of 16bit per pixel +#error The AMS369FG03-0 driver requires a color depth of 16bit per pixel #endif -class DisplayImpl +class DisplayImpl : public Display { public: /** - * Constructor. - * Do not instantiate objects of this type directly from application code, - * use Display::instance() instead. + * \return an instance to this class (singleton) */ - DisplayImpl(); + static DisplayImpl& instance(); + + /** + * Turn the display On after it has been turned Off. + * Display initial state is On. + */ + void doTurnOn() override; + + /** + * Turn the display Off. It can be later turned back On. + */ + void doTurnOff() override; + + /** + * Set display brightness. Depending on the underlying driver, + * may do nothing. + * \param brt from 0 to 100 + */ + void doSetBrightness(int brt) override; + + /** + * \return a pair with the display height and width + */ + std::pair<short int, short int> doGetSize() const override; /** * Write text to the display. If text is too long it will be truncated * \param p point where the upper left corner of the text will be printed * \param text, text to print. */ - void write(Point p, const char *text) - { - font.draw(*this,textColor,p,text); - } + void write(Point p, const char *text) override; /** * Write part of text to the display @@ -84,19 +103,13 @@ public: * \param b Lower right corner of clipping rectangle * \param text text to write */ - void clippedWrite(Point p, Point a, Point b, const char *text) - { - font.clippedDraw(*this,textColor,p,a,b,text); - } + 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) - { - clear(Point(0,0),Point(width-1,height-1),color); - } + void clear(Color color) override; /** * Clear an area of the screen @@ -104,12 +117,12 @@ public: * \param p2 lower right corner of area to clear * \param color fill color */ - void clear(Point p1, Point p2, Color color); + void clear(Point p1, Point p2, Color color) override; /** * This backend does not require it, so it is a blank. */ - void beginPixel() {} + void beginPixel() override; /** * Draw a pixel with desired color. You have to call beginPixel() once @@ -117,12 +130,7 @@ public: * \param p point where to draw pixel * \param color pixel color */ - void setPixel(Point p, Color color) - { - int offset=p.x()+p.y()*width; - if(offset<0 || offset>=numPixels) return; - *(framebuffer1+offset)=color; - } + void setPixel(Point p, Color color) override; /** * Draw a line between point a and point b, with color c @@ -130,36 +138,7 @@ public: * \param b second point * \param c line color */ - void line(Point a, Point b, Color color) - { - using namespace std; - //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; - Color *ptr=framebuffer1+minx+width*a.y(); - for(short i=minx;i<=maxx;i++) *ptr++=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()); - if(a.x()<0 || a.x()>=width || miny<0 || maxy>=height) return; - Color *ptr=framebuffer1+a.x()+width*miny; - for(short i=miny;i<=maxy;i++) - { - *ptr=color; - ptr+=width; - } - return; - } - //General case - Line::draw(*this,a,b,color); - } + void line(Point a, Point b, Color color) override; /** * Draw an horizontal line on screen. @@ -170,22 +149,13 @@ public: * \param length length of colors array. * p.x()+length must be <= display.width() */ - void 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; - Color *ptr=framebuffer1+p.x()+p.y()*width; - memcpy(ptr,colors,length*bpp); - } + 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() - { - return buffer; - } + Color *getScanLineBuffer() override; /** * Draw the content of the last getScanLineBuffer() on an horizontal line @@ -194,32 +164,14 @@ public: * \param length length of colors array. * p.x()+length must be <= display.width() */ - void scanLineBuffer(Point p, unsigned short length) - { - int offset=p.x()+p.y()*width; - if(offset<0 || offset>=numPixels) return; - memcpy(framebuffer1+offset,buffer,length*bpp); - } + 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) - { - 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 drawImage(Point p, const ImageBase& img) override; /** * Draw part of an image on the screen @@ -230,16 +182,7 @@ public: * \param b Lower right corner of clipping rectangle * \param i Image to draw */ - void clippedDrawImage(Point p, Point a, Point b, const ImageBase& img) - { -// if(img.getData()==0) -// { - img.clippedDraw(*this,p,a,b); - return; -// } //else optimized version for memory-loaded images -// //TODO: optimize -// } - } + void clippedDrawImage(Point p, Point a, Point b, const ImageBase& img) override; /** * Draw a rectangle (not filled) with the desired color @@ -247,74 +190,37 @@ public: * \param b lower right corner of the rectangle * \param c color of the line */ - void drawRectangle(Point a, Point b, Color c); - - /** - * \return the display's height - */ - short int getHeight() const { return height; } - - /** - * \return the display's width - */ - short int getWidth() const { return width; } - - /** - * Turn the display On after it has been turned Off. - * Display initial state is On. - */ - void turnOn(); - - /** - * Turn the display Off. It can be later turned back On. - */ - void turnOff(); - - /** - * Set display brightness. Depending on the underlying driver, - * may do nothing. - * \param brt from 0 to 100 - */ - void setBrightness(int brt); + void drawRectangle(Point a, Point b, Color c) override; /** * Set colors used for writing text * \param fgcolor text color * \param bgcolor background color */ - void setTextColor(Color fgcolor, Color bgcolor) - { - Font::generatePalette(textColor,fgcolor,bgcolor); - } - - /** - * \return the current foreground color. - * The foreground color is used to draw text on screen - */ - Color getForeground() const { return textColor[3]; } + void setTextColor(std::pair<Color,Color> colors) override; /** - * \return the current background color. - * The foreground color is used to draw text on screen + * \return a pair with the foreground and background colors. + * Those colors are used to draw text on screen */ - Color getBackground() const { return textColor[0]; } + std::pair<Color,Color> getTextColor() const override; /** * Set the font used for writing text * \param font new font */ - void setFont(const Font& font) { this->font=font; } + void setFont(const Font& font) override; /** * \return the current font used to draw text */ - Font getFont() const { return font; } + Font getFont() const override; /** * Make all changes done to the display since the last call to update() * visible. This backends does not require it, so it is empty. */ - void update() {} + void update() override; /** * Pixel iterator. A pixel iterator is an output iterator that allows to @@ -451,9 +357,15 @@ public: /** * Destructor */ - ~DisplayImpl() {} + ~DisplayImpl() override; private: + /** + * Constructor. + * Do not instantiate objects of this type directly from application code. + */ + DisplayImpl(); + #if defined MXGUI_ORIENTATION_VERTICAL static const short int width=480; static const short int height=800;