From db56509e4fe647d78d64a2994c6d6b442f3d6562 Mon Sep 17 00:00:00 2001 From: Federico Terraneo <fede.tft@miosix.org> Date: Mon, 13 May 2019 01:20:58 +0200 Subject: [PATCH] Adding code by Alessandro Fiorillo to support the display in the stm32f469 discovery board --- drivers/display_stm32f4discovery.cpp | 845 +++++++++++++++++++++++++++ drivers/display_stm32f4discovery.h | 336 +++++++++++ 2 files changed, 1181 insertions(+) diff --git a/drivers/display_stm32f4discovery.cpp b/drivers/display_stm32f4discovery.cpp index fe75d08..c86b278 100644 --- a/drivers/display_stm32f4discovery.cpp +++ b/drivers/display_stm32f4discovery.cpp @@ -477,3 +477,848 @@ Color DisplayImpl::pixel_iterator::dummy; } //namespace mxgui #endif //_BOARD_STM32F429ZI_STM32F4DISCOVERY + +#ifdef _BOARD_STM32F469NI_STM32F469I_DISCO + +namespace mxgui { + +//Control interface + +//Pixel sync interface +typedef Gpio<GPIOF_BASE,10> en; +typedef Gpio<GPIOG_BASE, 7> dotclk; +typedef Gpio<GPIOA_BASE, 4> vsync; +typedef Gpio<GPIOC_BASE, 6> hsync; +//Pixel data bus +typedef Gpio<GPIOC_BASE,10> r0; //r2 +typedef Gpio<GPIOJ_BASE, 2> r1; //r3 +typedef Gpio<GPIOA_BASE,11> r2; //r4 +typedef Gpio<GPIOA_BASE,12> r3; //r5 +typedef Gpio<GPIOJ_BASE, 5> r4; //r6 +typedef Gpio<GPIOA_BASE, 6> g0; //g2 +typedef Gpio<GPIOG_BASE,10> g1; //g3 AF9 +typedef Gpio<GPIOJ_BASE,13> g2; //g4 AF9 +typedef Gpio<GPIOH_BASE, 4> g3; //g5 AF9 +typedef Gpio<GPIOC_BASE, 7> g4; //g6 +typedef Gpio<GPIOD_BASE, 3> g5; //g7 +typedef Gpio<GPIOD_BASE, 6> b0; //b2 +typedef Gpio<GPIOG_BASE,11> b1; //b3 +typedef Gpio<GPIOG_BASE,12> b2; //b4 AF9 +typedef Gpio<GPIOA_BASE, 3> b3; //b5 +typedef Gpio<GPIOB_BASE, 8> b4; //b6 + +void shortWrite(uint8_t param0, uint8_t param1) { + // Command FIFO Empty + while ((DSI->GPSR & DSI_GPSR_CMDFE) == 0); + + DSI->GHCR = (0x15 | // DSI_DCS_SHORT_PKT_WRITE_P1 + (0 << 6) | // Virtual Channel ID + (param0 << 8) | \ + (param1 << 16)); +} + +void longWrite(uint32_t numParams, uint32_t param0, uint8_t *pParams) { + // Command FIFO Empty + while ((DSI->GPSR & DSI_GPSR_CMDFE) == 0); + + uint32_t uicounter = 0; + while (uicounter < numParams) { + if (uicounter == 0x00) { + DSI->GPDR = (param0 | \ + ((*(pParams+uicounter)) << 8) | \ + ((*(pParams+uicounter+1)) << 16) | \ + ((*(pParams+uicounter+2)) << 24)); + uicounter += 3; + } else { + DSI->GPDR = ((*(pParams+uicounter)) | \ + ((*(pParams+uicounter+1)) << 8) | \ + ((*(pParams+uicounter+2)) << 16) | \ + ((*(pParams+uicounter+3)) << 24)); + uicounter += 4; + } + } + + DSI->GHCR = (0x39 | // DSI_DCS_LONG_PKT_WRITE + (0 << 6) | // Virtual Channel Id + (((numParams+1)&0x00FF) << 8) | \ + ((((numParams+1)&0xFF00) >> 8) << 16)); +} + +void sendCmd(uint32_t numParams, uint8_t *pParams) { + if (numParams <= 1) { + shortWrite(pParams[0], pParams[1]); + } else { + longWrite(numParams, pParams[numParams], pParams); + } +} + +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; + Thread::sleep(40); + uint8_t set_display_on[] = {0x29, 0x00}; + sendCmd(0, set_display_on); +} + +void DisplayImpl::doTurnOff() +{ + uint8_t set_display_off[] = {0x28, 0x00}; + sendCmd(0, set_display_off); + LTDC->GCR &= ~LTDC_GCR_LTDCEN; +} + +void DisplayImpl::doSetBrightness(int brt) {} + +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::update() +{ + DSI->WCR |= DSI_WCR_LTDCEN; +} + +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 *>(0xc0000000)), + buffer(framebuffer1 + numPixels) +{ + /* This driver uses DSI interface in command mode, but it was firstly programmed in video mode. + * For this reason some instructions are still there but they don't actually affect the driver. + * For example these timing parameters are important in video mode, but in command mode they can + * take any value and the display still works. + */ + const unsigned int hsync = 12; // hsync timing + const unsigned int vsync = 120; // vsync timing + const unsigned int hbp = 12; // horizontal back porch + const unsigned int vbp = 120; // vertical back porch + const unsigned int hfp = 12; // horizontal front porch + const unsigned int vfp = 120; // vertical front porch + enum { + ARGB8888 = 0, + RGB888 = 1, + RGB565 = 2, + ARGB1555 = 3, + ARGB4444 = 4, + L8 = 5, + AL44 = 6, + AL88 = 7 + }; + + // Parameters for DSI PLL + // These values assume HSE oscillator is 8 MHz and system clock is 168 MHz + #if HSE_VALUE != 8000000 + #error The display driver requires an HSE oscillator running at 8 MHz + #endif + const unsigned int IDF = 4; // must be in the range 1..7 + const unsigned int ODF = 1; // must be in the set {1, 2, 4, 8} + const unsigned int NDIV = 125; // must be in the range 10..125 + const unsigned int F_VCO = (HSE_VALUE/IDF)*2*NDIV; // 500 MHz - must be between 500 and 1000 MHz + const unsigned int F_PHY_MHz = (F_VCO/(2*ODF))/1000000; // 250 MHz - HS clock for D-PHY must be between 80 and 500 MHz + const unsigned int lane_byte_clk = F_VCO/(2*ODF*8); // 31,25 MHz - must be no more than 62,5 MHz + const unsigned int TXECLKDIV = 2; // must be at least 2 and ensure lane_byte_clk/TXECLKDIV <= 20 MHz + const unsigned int pixel_clock = F_VCO/bpp; // 31,25 MHz + const unsigned int clock_ratio = lane_byte_clk/pixel_clock; + + memset(framebuffer1, 0, height*width*bpp); + + // Reset of screen by active low on GPIO PH7 + typedef Gpio<GPIOH_BASE, 7> reset; + reset::mode(Mode::OUTPUT); + reset::speed(Speed::_100MHz); + reset::low(); + Thread::sleep(20); + reset::high(); + Thread::sleep(10); + + // Enable clock for DSI and LTDC then force their reset + { + FastInterruptDisableLock dLock; + + en::mode(Mode::ALTERNATE); en::alternateFunction(14); en::speed(Speed::_100MHz); + dotclk::mode(Mode::ALTERNATE); dotclk::alternateFunction(14); dotclk::speed(Speed::_100MHz); + vsync::mode(Mode::ALTERNATE); vsync::alternateFunction(14); vsync::speed(Speed::_100MHz); + hsync::mode(Mode::ALTERNATE); hsync::alternateFunction(14); hsync::speed(Speed::_100MHz); + r0::mode(Mode::ALTERNATE); r0::alternateFunction(14); r0::speed(Speed::_100MHz); + r1::mode(Mode::ALTERNATE); r1::alternateFunction(14); r1::speed(Speed::_100MHz); + r2::mode(Mode::ALTERNATE); r2::alternateFunction(14); r2::speed(Speed::_100MHz); + r3::mode(Mode::ALTERNATE); r3::alternateFunction(14); r3::speed(Speed::_100MHz); + r4::mode(Mode::ALTERNATE); r4::alternateFunction(14); r4::speed(Speed::_100MHz); + g0::mode(Mode::ALTERNATE); g0::alternateFunction(14); g0::speed(Speed::_100MHz); + g1::mode(Mode::ALTERNATE); g1::alternateFunction(9); g1::speed(Speed::_100MHz); + g2::mode(Mode::ALTERNATE); g2::alternateFunction(9); g2::speed(Speed::_100MHz); + g3::mode(Mode::ALTERNATE); g3::alternateFunction(9); g3::speed(Speed::_100MHz); + g4::mode(Mode::ALTERNATE); g4::alternateFunction(14); g4::speed(Speed::_100MHz); + g5::mode(Mode::ALTERNATE); g5::alternateFunction(14); g5::speed(Speed::_100MHz); + b0::mode(Mode::ALTERNATE); b0::alternateFunction(14); b0::speed(Speed::_100MHz); + b1::mode(Mode::ALTERNATE); b1::alternateFunction(14); b1::speed(Speed::_100MHz); + b2::mode(Mode::ALTERNATE); b2::alternateFunction(9); b2::speed(Speed::_100MHz); + b3::mode(Mode::ALTERNATE); b3::alternateFunction(14); b3::speed(Speed::_100MHz); + b4::mode(Mode::ALTERNATE); b4::alternateFunction(14); b4::speed(Speed::_100MHz); + + RCC->APB2ENR |= RCC_APB2ENR_LTDCEN; + RCC_SYNC(); + RCC->APB2ENR |= RCC_APB2ENR_DSIEN; + RCC_SYNC(); + + RCC->APB2RSTR |= RCC_APB2RSTR_LTDCRST; + RCC->APB2RSTR &= ~RCC_APB2RSTR_LTDCRST; + + RCC->APB2RSTR |= RCC_APB2RSTR_DSIRST; + RCC->APB2RSTR &= ~RCC_APB2RSTR_DSIRST; + } + + // Configure PLLSAI for LTDC, turn it ON and wait for its lock + { + FastInterruptDisableLock dLock; + + // LTDC clock depends on PLL_M which is fixed at boot with value 8 + // It also depends on PLLSAI which can be freely configured + const unsigned int PLLSAI_N = 384; + const unsigned int PLLSAI_R = 7; + //const unsigned int PLLSAI_DIVR = 0; + + // Input VCO Frequency = HSE_VALUE/PPL_M must be between 1 and 2 MHz, so 8/8 = 1 MHz + // N must be in the range 50..432 and ensure a frequency between 100 and 432 MHz + // if N = 384 then 1 MHz * 384 = 384 MHz + // R must be in the range 2..7, we choose R = 7 so 384/7 = 54,857 MHz + // and then we divide it by 2 (setting DIVR to 0) to obtain 27,428 Mhz + + // Read PLLSAI_P and PLLSAI_Q values from PLLSAICFGR register + uint32_t PLLSAI_P = ((RCC->PLLSAICFGR & RCC_PLLSAICFGR_PLLSAIP) >> 16); + uint32_t PLLSAI_Q = ((RCC->PLLSAICFGR & RCC_PLLSAICFGR_PLLSAIQ) >> 24); + // PLLSAI_VCO_Input = PLL_SOURCE/PLL_M + // PLLSAI_VCO_Output = PLLSAI_VCO_Input * PLLSAI_N + // LTDC_CLK(first level) = PLLSAI_VCO_Output/PLLSAI_R + RCC->PLLSAICFGR = (PLLSAI_N << 6) | \ + (PLLSAI_P << 16) | \ + (PLLSAI_Q << 24) | \ + (PLLSAI_R << 28); + // LTDC_CLK = LTDC_CLK(first level)/PLLSAI_DIVR + RCC->DCKCFGR = RCC->DCKCFGR & ~RCC_DCKCFGR_PLLSAIDIVR; + + RCC->CR |= RCC_CR_PLLSAION; + } + while ((RCC->CR & RCC_CR_PLLSAIRDY) == 0); + + /* Start LTDC configuration */ + // Configure timings + LTDC->SSCR = (hsync-1) << 16 | (vsync-1); + LTDC->BPCR = (hsync+hbp-1) << 16 | (vsync+vbp-1); + LTDC->AWCR = (hsync+hbp+width-1) << 16 | (vsync+vbp+height-1); + LTDC->TWCR = (hsync+hbp+width+hfp-1) << 16 | (vsync+vbp+height+vfp-1); + + // Configure polarities (everything active high except data enabled) + LTDC->GCR |= (LTDC_GCR_HSPOL | LTDC_GCR_VSPOL | LTDC_GCR_DEPOL | LTDC_GCR_PCPOL); + LTDC->GCR &= ~LTDC_GCR_DEPOL; + + // Configure background color + LTDC->BCCR = 0; + + // Configure the layer + // Horizontal start and stop position + LTDC_Layer1->WHPCR &= ~(LTDC_LxWHPCR_WHSTPOS | LTDC_LxWHPCR_WHSPPOS); + LTDC_Layer1->WHPCR = ((hsync+hbp+width-1) << 16) | (hsync+hbp); + // Vertical start and stop position + LTDC_Layer1->WVPCR &= ~(LTDC_LxWVPCR_WVSTPOS | LTDC_LxWVPCR_WVSPPOS); + LTDC_Layer1->WVPCR = ((vsync+vbp+height-1) << 16) | (vsync+vbp); + // Pixel format + LTDC_Layer1->PFCR &= ~(LTDC_LxPFCR_PF); + LTDC_Layer1->PFCR |= RGB565; + // Color frame buffer start address + LTDC_Layer1->CFBAR &= ~(LTDC_LxCFBAR_CFBADD); + LTDC_Layer1->CFBAR = reinterpret_cast<unsigned int>(framebuffer1); + // Color frame buffer pitch in byte (multiply by 2 for RGB565) + LTDC_Layer1->CFBLR &= ~(LTDC_LxCFBLR_CFBP | LTDC_LxCFBLR_CFBLL); + LTDC_Layer1->CFBLR = (width*2) << 16 | (width*2+3); + // Frame buffer line number + LTDC_Layer1->CFBLNR &= ~(LTDC_LxCFBLNR_CFBLNBR); + LTDC_Layer1->CFBLNR = height; + // Default color values (black with no alpha) + LTDC_Layer1->DCCR &= ~(LTDC_LxDCCR_DCBLUE | LTDC_LxDCCR_DCGREEN | LTDC_LxDCCR_DCRED | LTDC_LxDCCR_DCALPHA); + LTDC_Layer1->DCCR = 0; + // Specifies the constant alpha value + LTDC_Layer1->CACR &= ~(LTDC_LxCACR_CONSTA); + LTDC_Layer1->CACR = 255; + // Blending factors (constant alpha) + LTDC_Layer1->BFCR &= ~(LTDC_LxBFCR_BF2 | LTDC_LxBFCR_BF1); + LTDC_Layer1->BFCR = (0x400 | 0x5); + // Enable layer + LTDC_Layer1->CR |= LTDC_LxCR_LEN; + + // If needed enable dithering and color keying + LTDC_Layer1->CKCR = 0; + + // Reload shadow registers + LTDC->SRCR |= LTDC_SRCR_IMR; + + // Finally enable the display + LTDC->GCR |= LTDC_GCR_LTDCEN; + /* End LTDC configuration */ + + /* Start DSI configuration */ + // Turn on the DSI regulator and wait for the regulator ready + DSI->WRPCR |= DSI_WRPCR_REGEN; + while ((DSI->WISR & DSI_WISR_RRS) == 0); + + // Configure the DSI PLL, turn it ON and wait for its lock + // F_VCO = (HSE_VALUE / IDF) * 2 * NDIV + // Lane_Byte_CLK = F_VCO / (2 * ODF * 8) + // F_VCO must be in the range from 500 MHz to 1 GHz + // To obtain 500 Mbit/s rate, Lane_Byte_CLK must be 31,25 MHz + // Since HSE_VALUE = 8 MHz this is possible with NDIV = 125, IDF = 4, ODF = 1 + DSI->WRPCR &= ~(DSI_WRPCR_PLL_NDIV | DSI_WRPCR_PLL_IDF | DSI_WRPCR_PLL_ODF); + DSI->WRPCR |= ((NDIV << 2) | (IDF << 11) | (ODF << 16)); + DSI->WRPCR |= DSI_WRPCR_PLLEN; + while ((DSI->WISR & DSI_WISR_PLLLS) == 0); + + // Configure the D-PHY parameters + // Calculate the bit period in high-speed mode in unit of 0.25 ns (UIX4) + // The equation is: UIX4 = IntegerPart( (1000/F_PHY_MHz) * 4 ) + // Where: F_PHY_MHz = (NDIV * HSE_MHz) / (IDF * ODF) + DSI->WPCR[0] &= ~DSI_WPCR0_UIX4; + DSI->WPCR[0] |= (1000/F_PHY_MHz)*4; + // Disable all error interrupts and reset the Error Mask + DSI->IER[0] = 0; + DSI->IER[1] = 0; + // Configure the number of active data lanes (just one out of two for 16 bpp) + DSI->PCONFR &= ~DSI_PCONFR_NL; + DSI->PCONFR |= 1; + // Set automatic clock lane control + DSI->CLCR |= (DSI_CLCR_DPCC | DSI_CLCR_ACR); + // Time for LP/HS and HS/LP transitions for both clock lane and data lanes + DSI->CLTCR |= (40 << 16) // HS to LP + | (40 << 0); // LP to HS + DSI->DLTCR |= (20 << 24) // HS to LP + | (20 << 16); // HS to LP + // Stop wait time (don't know how much should it be, random high number in 8 bit) + DSI->PCONFR &= ~DSI_PCONFR_SW_TIME; + DSI->PCONFR |= (100 << 8); + + // Configure the DSI Host timing + //DSI->CCR |= (... << 8); // timeout clock configuration non dice nulla... + // Configure clock speed for low-power mode + DSI->CCR &= ~DSI_CCR_TXECKDIV; + DSI->CCR |= TXECLKDIV; + + // Configure the DSI Host flow control and DBI interface + DSI->PCR &= ~(DSI_PCR_CRCRXE | DSI_PCR_ECCRXE | DSI_PCR_BTAE | DSI_PCR_ETRXE | DSI_PCR_ETTXE); + DSI->GVCIDR &= ~DSI_GVCIDR_VCID; // set Virtual Channel ID = 0 for the display + + // Configure the DSI Host LTDC interface + DSI->LVCIDR &= ~3; // Virtual channel ID for LTDC interface traffic + DSI->LCOLCR &= ~DSI_LCOLCR_COLC; + DSI->LCOLCR |= RGB565; // Color coding for the host + DSI->WCFGR &= ~DSI_WCFGR_COLMUX; + DSI->WCFGR |= RGB565 << 1; // Color coding for the wrapper + DSI->LPCR &= ~(DSI_LPCR_DEP | DSI_LPCR_VSP | DSI_LPCR_HSP); + DSI->LPCR |= (DSI_LPCR_DEP | 0 | 0); // Polarity of control signals: same of LTDC except for DE + DSI->WCFGR |= DSI_WCFGR_VSPOL; // LTDC halts at VSYNC rising edge + + // Configure the DSI Host for command mode + // Select command mode by setting CMDM and DSIM bits + DSI->MCR |= DSI_MCR_CMDM; + DSI->WCFGR |= DSI_WCFGR_DSIM; + // Configure the maximum allowed size for write memory command + DSI->LCCR &= ~DSI_LCCR_CMDSIZE; + DSI->LCCR |= width; + + DSI->VMCR |= 0x3f << 8; // LP allowed in all video periods + DSI->VMCR &= ~DSI_VMCR_FBTAAE; // Do not request acknowledge at end of frame (at this time) + DSI->VMCR |= DSI_VMCR_LPCE; // Allow commands in LP + DSI->VPCR |= width; // Video packet size + DSI->VCCR = 0; // Chunks number to be transmitted through the DSI link + DSI->VNPCR |= 0xFFF; // Size of the null packet + // Timings in lane byte clock cycles + DSI->VLCR |= (hsync + hbp + width + hfp)*clock_ratio; + DSI->VHSACR |= hsync*clock_ratio; + DSI->VHBPCR |= hbp*clock_ratio; + DSI->VVSACR |= vsync; + DSI->VVBPCR |= vbp; + DSI->VVFPCR |= vfp; + DSI->VVACR |= height; + DSI->LPMCR |= (64 << 16); // Low power largest packet size + DSI->LPMCR |= 64; // Low power VACT largest packet size + // Command trasmission only in low power mode + DSI->CMCR |= (DSI_CMCR_GSW0TX | \ + DSI_CMCR_GSW1TX | \ + DSI_CMCR_GSW2TX | \ + DSI_CMCR_GSR0TX | \ + DSI_CMCR_GSR1TX | \ + DSI_CMCR_GSR2TX | \ + DSI_CMCR_GLWTX | \ + DSI_CMCR_DSW0TX | \ + DSI_CMCR_DSW1TX | \ + DSI_CMCR_DSR0TX | \ + DSI_CMCR_DLWTX | \ + DSI_CMCR_MRDPS); + + // Configure the acknowledge request after each packet transmission + DSI->CMCR |= DSI_CMCR_ARE; + + // Enable the D-PHY data lane + DSI->PCTLR |= DSI_PCTLR_DEN; + + // Enable the D-PHY clock lane + DSI->PCTLR |= DSI_PCTLR_CKE; + + // Enable the DSI Host + DSI->CR |= DSI_CR_EN; + + // Enable the DSI wrapper + DSI->WCR |= DSI_WCR_DSIEN; + /* End DSI configuration */ + + // Send DCS commands through the APB generic interface to configure the display + /* OTM8009A power up sequence */ + const uint8_t lcdRegData1[] = {0x80,0x09,0x01,0xFF}; + const uint8_t lcdRegData2[] = {0x80,0x09,0xFF}; + const uint8_t lcdRegData3[] = {0x00,0x09,0x0F,0x0E,0x07,0x10,0x0B,0x0A,0x04,0x07,0x0B,0x08,0x0F,0x10,0x0A,0x01,0xE1}; + const uint8_t lcdRegData4[] = {0x00,0x09,0x0F,0x0E,0x07,0x10,0x0B,0x0A,0x04,0x07,0x0B,0x08,0x0F,0x10,0x0A,0x01,0xE2}; + const uint8_t lcdRegData5[] = {0x79,0x79,0xD8}; + const uint8_t lcdRegData6[] = {0x00,0x01,0xB3}; + const uint8_t lcdRegData7[] = {0x85,0x01,0x00,0x84,0x01,0x00,0xCE}; + const uint8_t lcdRegData8[] = {0x18,0x04,0x03,0x39,0x00,0x00,0x00,0x18,0x03,0x03,0x3A,0x00,0x00,0x00,0xCE}; + const uint8_t lcdRegData9[] = {0x18,0x02,0x03,0x3B,0x00,0x00,0x00,0x18,0x01,0x03,0x3C,0x00,0x00,0x00,0xCE}; + const uint8_t lcdRegData10[] = {0x01,0x01,0x20,0x20,0x00,0x00,0x01,0x02,0x00,0x00,0xCF}; + const uint8_t lcdRegData11[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xCB}; + const uint8_t lcdRegData12[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xCB}; + const uint8_t lcdRegData13[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xCB}; + const uint8_t lcdRegData14[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xCB}; + const uint8_t lcdRegData15[] = {0x00,0x04,0x04,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xCB}; + const uint8_t lcdRegData16[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x04,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0xCB}; + const uint8_t lcdRegData17[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xCB}; + const uint8_t lcdRegData18[] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xCB}; + const uint8_t lcdRegData19[] = {0x00,0x26,0x09,0x0B,0x01,0x25,0x00,0x00,0x00,0x00,0xCC}; + const uint8_t lcdRegData20[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x26,0x0A,0x0C,0x02,0xCC}; + const uint8_t lcdRegData21[] = {0x25,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xCC}; + const uint8_t lcdRegData22[] = {0x00,0x25,0x0C,0x0A,0x02,0x26,0x00,0x00,0x00,0x00,0xCC}; + const uint8_t lcdRegData23[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x25,0x0B,0x09,0x01,0xCC}; + const uint8_t lcdRegData24[] = {0x26,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xCC}; + const uint8_t lcdRegData25[] = {0xFF,0xFF,0xFF,0xFF}; + + /* + * CASET value (Column Address Set): X direction LCD GRAM boundaries depending on LCD orientation mode + * PASET value (Page Address Set): Y direction LCD GRAM boundaries depending on LCD orientation mode + * + * XS[15:0] = 0x000 = 0, XE[15:0] = 0x31F = 799 for landscape mode: apply to CASET + * YS[15:0] = 0x000 = 0, YE[15:0] = 0x31F = 799 for portrait mode: apply to PASET + * + * XS[15:0] = 0x000 = 0, XE[15:0] = 0x1DF = 479 for portrait mode: apply to CASET + * YS[15:0] = 0x000 = 0, YE[15:0] = 0x1DF = 479 for landscape mode: apply to PASET + */ + +// #if defined MXGUI_ORIENTATION_VERTICAL +// const uint8_t lcdRegData27[] = {0x00, 0x00, 0x03, 0x1F, 0x2B}; +// const uint8_t lcdRegData28[] = {0x00, 0x00, 0x01, 0xDF, 0x2A}; +// #elif defined MXGUI_ORIENTATION_HORIZONTAL +// const uint8_t lcdRegData27[] = {0x00, 0x00, 0x03, 0x1F, 0x2A}; +// const uint8_t lcdRegData28[] = {0x00, 0x00, 0x01, 0xDF, 0x2B}; +// #endif + + const uint8_t ShortRegData1[] = {0x00, 0x00}; + const uint8_t ShortRegData2[] = {0x00, 0x80}; + const uint8_t ShortRegData3[] = {0xC4, 0x30}; + const uint8_t ShortRegData4[] = {0x00, 0x8A}; + const uint8_t ShortRegData5[] = {0xC4, 0x40}; + const uint8_t ShortRegData6[] = {0x00, 0xB1}; + const uint8_t ShortRegData7[] = {0xC5, 0xA9}; + const uint8_t ShortRegData8[] = {0x00, 0x91}; + const uint8_t ShortRegData9[] = {0xC5, 0x34}; + const uint8_t ShortRegData10[] = {0x00, 0xB4}; + const uint8_t ShortRegData11[] = {0xC0, 0x50}; + const uint8_t ShortRegData12[] = {0xD9, 0x4E}; + const uint8_t ShortRegData13[] = {0x00, 0x81}; + const uint8_t ShortRegData14[] = {0xC1, 0x66}; + const uint8_t ShortRegData15[] = {0x00, 0xA1}; + const uint8_t ShortRegData16[] = {0xC1, 0x08}; + const uint8_t ShortRegData17[] = {0x00, 0x92}; + const uint8_t ShortRegData18[] = {0xC5, 0x01}; + const uint8_t ShortRegData19[] = {0x00, 0x95}; + const uint8_t ShortRegData20[] = {0x00, 0x94}; + const uint8_t ShortRegData21[] = {0xC5, 0x33}; + const uint8_t ShortRegData22[] = {0x00, 0xA3}; + const uint8_t ShortRegData23[] = {0xC0, 0x1B}; + const uint8_t ShortRegData24[] = {0x00, 0x82}; + const uint8_t ShortRegData25[] = {0xC5, 0x83}; + const uint8_t ShortRegData26[] = {0xC4, 0x83}; + const uint8_t ShortRegData27[] = {0xC1, 0x0E}; + const uint8_t ShortRegData28[] = {0x00, 0xA6}; + const uint8_t ShortRegData29[] = {0x00, 0xA0}; + const uint8_t ShortRegData30[] = {0x00, 0xB0}; + const uint8_t ShortRegData31[] = {0x00, 0xC0}; + const uint8_t ShortRegData32[] = {0x00, 0xD0}; + const uint8_t ShortRegData33[] = {0x00, 0x90}; + const uint8_t ShortRegData34[] = {0x00, 0xE0}; + const uint8_t ShortRegData35[] = {0x00, 0xF0}; + const uint8_t ShortRegData36[] = {0x11, 0x00}; + const uint8_t ShortRegData37[] = {0x3A, 0x55}; +// const uint8_t ShortRegData38[] = {0x3A, 0x77}; + +// #if defined MXGUI_ORIENTATION_VERTICAL +// const uint8_t ShortRegData39[] = {0x36, 0x00}; +// #elif defined MXGUI_ORIENTATION_HORIZONTAL +// const uint8_t ShortRegData39[] = {0x36, 0x60}; +// #endif + + const uint8_t ShortRegData40[] = {0x51, 0xFF}; /* Draupner: Brightness changed from 0x7F */ + const uint8_t ShortRegData41[] = {0x53, 0x2C}; + const uint8_t ShortRegData42[] = {0x55, 0x02}; + const uint8_t ShortRegData43[] = {0x5E, 0xFF}; + const uint8_t ShortRegData44[] = {0x29, 0x00}; + const uint8_t ShortRegData45[] = {0x2C, 0x00}; + const uint8_t ShortRegData46[] = {0xCF, 0x00}; + const uint8_t ShortRegData47[] = {0xC5, 0x66}; + const uint8_t ShortRegData48[] = {0x00, 0xB6}; + const uint8_t ShortRegData49[] = {0xF5, 0x06}; + + sendCmd(0, (uint8_t *)ShortRegData1); + sendCmd(3, (uint8_t *)lcdRegData1); + sendCmd(0, (uint8_t *)ShortRegData2); + sendCmd(2, (uint8_t *)lcdRegData2); + sendCmd(0, (uint8_t *)ShortRegData2); + sendCmd(0, (uint8_t *)ShortRegData3); + Thread::sleep(10); + sendCmd(0, (uint8_t *)ShortRegData4); + sendCmd(0, (uint8_t *)ShortRegData5); + Thread::sleep(10); + sendCmd(0, (uint8_t *)ShortRegData6); + sendCmd(0, (uint8_t *)ShortRegData7); + sendCmd(0, (uint8_t *)ShortRegData8); + sendCmd(0, (uint8_t *)ShortRegData9); + sendCmd(0, (uint8_t *)ShortRegData10); + sendCmd(0, (uint8_t *)ShortRegData11); + sendCmd(0, (uint8_t *)ShortRegData1); + sendCmd(0, (uint8_t *)ShortRegData12); + sendCmd(0, (uint8_t *)ShortRegData13); + sendCmd(0, (uint8_t *)ShortRegData14); + sendCmd(0, (uint8_t *)ShortRegData15); + sendCmd(0, (uint8_t *)ShortRegData16); + sendCmd(0, (uint8_t *)ShortRegData17); + sendCmd(0, (uint8_t *)ShortRegData18); + sendCmd(0, (uint8_t *)ShortRegData19); + sendCmd(0, (uint8_t *)ShortRegData9); + sendCmd(0, (uint8_t *)ShortRegData1); + sendCmd(2, (uint8_t *)lcdRegData5); + sendCmd(0, (uint8_t *)ShortRegData20); + sendCmd(0, (uint8_t *)ShortRegData21); + sendCmd(0, (uint8_t *)ShortRegData22); + sendCmd(0, (uint8_t *)ShortRegData23); + sendCmd(0, (uint8_t *)ShortRegData24); + sendCmd(0, (uint8_t *)ShortRegData25); + sendCmd(0, (uint8_t *)ShortRegData13); + sendCmd(0, (uint8_t *)ShortRegData26); + sendCmd(0, (uint8_t *)ShortRegData15); + sendCmd(0, (uint8_t *)ShortRegData27); + sendCmd(0, (uint8_t *)ShortRegData28); + sendCmd(2, (uint8_t *)lcdRegData6); + sendCmd(0, (uint8_t *)ShortRegData2); + sendCmd(6, (uint8_t *)lcdRegData7); + sendCmd(0, (uint8_t *)ShortRegData29); + sendCmd(14, (uint8_t *)lcdRegData8); + sendCmd(0, (uint8_t *)ShortRegData30); + sendCmd(14, (uint8_t *)lcdRegData9); + sendCmd(0, (uint8_t *)ShortRegData31); + sendCmd(10, (uint8_t *)lcdRegData10); + sendCmd(0, (uint8_t *)ShortRegData32); + sendCmd(0, (uint8_t *)ShortRegData46); + sendCmd(0, (uint8_t *)ShortRegData2); + sendCmd(10, (uint8_t *)lcdRegData11); + sendCmd(0, (uint8_t *)ShortRegData33); + sendCmd(15, (uint8_t *)lcdRegData12); + sendCmd(0, (uint8_t *)ShortRegData29); + sendCmd(15, (uint8_t *)lcdRegData13); + sendCmd(0, (uint8_t *)ShortRegData30); + sendCmd(10, (uint8_t *)lcdRegData14); + sendCmd(0, (uint8_t *)ShortRegData31); + sendCmd(15, (uint8_t *)lcdRegData15); + sendCmd(0, (uint8_t *)ShortRegData32); + sendCmd(15, (uint8_t *)lcdRegData16); + sendCmd(0, (uint8_t *)ShortRegData34); + sendCmd(10, (uint8_t *)lcdRegData17); + sendCmd(0, (uint8_t *)ShortRegData35); + sendCmd(10, (uint8_t *)lcdRegData18); + sendCmd(0, (uint8_t *)ShortRegData2); + sendCmd(10, (uint8_t *)lcdRegData19); + sendCmd(0, (uint8_t *)ShortRegData33); + sendCmd(15, (uint8_t *)lcdRegData20); + sendCmd(0, (uint8_t *)ShortRegData29); + sendCmd(15, (uint8_t *)lcdRegData21); + sendCmd(0, (uint8_t *)ShortRegData30); + sendCmd(10, (uint8_t *)lcdRegData22); + sendCmd(0, (uint8_t *)ShortRegData31); + sendCmd(15, (uint8_t *)lcdRegData23); + sendCmd(0, (uint8_t *)ShortRegData32); + sendCmd(15, (uint8_t *)lcdRegData24); + sendCmd(0, (uint8_t *)ShortRegData13); + sendCmd(0, (uint8_t *)ShortRegData47); + sendCmd(0, (uint8_t *)ShortRegData48); + sendCmd(0, (uint8_t *)ShortRegData49); + sendCmd(0, (uint8_t *)ShortRegData1); + sendCmd(3, (uint8_t *)lcdRegData25); + sendCmd(0, (uint8_t *)ShortRegData1); + sendCmd(0, (uint8_t *)ShortRegData1); + sendCmd(16, (uint8_t *)lcdRegData3); + sendCmd(0, (uint8_t *)ShortRegData1); + sendCmd(16, (uint8_t *)lcdRegData4); + sendCmd(0, (uint8_t *)ShortRegData36); + Thread::sleep(120); + + /* Set Pixel color format to RGB565 */ + sendCmd(0, (uint8_t *)ShortRegData37); + /* Set Pixel color format to RGB888 */ + //sendCmd(0, (uint8_t *)ShortRegData38); + + #if defined MXGUI_ORIENTATION_HORIZONTAL + /* Send command to configure display in landscape orientation mode. + By default the orientation mode is portrait. */ + sendCmd(0, (uint8_t *)ShortRegData39); + sendCmd(4, (uint8_t *)lcdRegData27); + sendCmd(4, (uint8_t *)lcdRegData28); + #endif + + sendCmd(0, (uint8_t *)ShortRegData40); + sendCmd(0, (uint8_t *)ShortRegData41); + sendCmd(0, (uint8_t *)ShortRegData42); + sendCmd(0, (uint8_t *)ShortRegData43); + sendCmd(0, (uint8_t *)ShortRegData44); + sendCmd(0, (uint8_t *)ShortRegData1); + sendCmd(0, (uint8_t *)ShortRegData45); + /* End OTM8009A power up sequence */ + + // Disable command trasmission only in low power mode + DSI->CMCR &= ~(DSI_CMCR_GSW0TX | \ + DSI_CMCR_GSW1TX | \ + DSI_CMCR_GSW2TX | \ + DSI_CMCR_GSR0TX | \ + DSI_CMCR_GSR1TX | \ + DSI_CMCR_GSR2TX | \ + DSI_CMCR_GLWTX | \ + DSI_CMCR_DSW0TX | \ + DSI_CMCR_DSW1TX | \ + DSI_CMCR_DSR0TX | \ + DSI_CMCR_DLWTX | \ + DSI_CMCR_MRDPS); + + DSI->PCR &= ~(DSI_PCR_CRCRXE | DSI_PCR_ECCRXE | \ + DSI_PCR_BTAE | DSI_PCR_ETRXE | \ + DSI_PCR_ETTXE); + DSI->PCR |= DSI_PCR_BTAE; + + // Enable the LTDC + LTDC->GCR |= LTDC_GCR_LTDCEN; + + // Start the LTDC flow through the DSI wrapper (CR.LTDCEN = 1). + // In video mode, the data streaming starts as soon as the LTDC is enabled. + // In adapted command mode, the frame buffer update is launched as soon as the CR.LTDCEN bit is set. + DSI->CR |= DSI_CR_EN; + + // Update the display + DSI->WCR |= DSI_WCR_LTDCEN; + + setFont(droid11); + setTextColor(make_pair(Color(0xffff), Color(0x0000))); + clear(black); +} + +Color DisplayImpl::pixel_iterator::dummy; + +} //namespace mxgui + +#endif //_BOARD_STM32F469NI_STM32F469I_DISCO diff --git a/drivers/display_stm32f4discovery.h b/drivers/display_stm32f4discovery.h index bee1d5b..2182bbd 100644 --- a/drivers/display_stm32f4discovery.h +++ b/drivers/display_stm32f4discovery.h @@ -361,4 +361,340 @@ private: #endif //_BOARD_STM32F429ZI_STM32F4DISCOVERY +#ifdef _BOARD_STM32F469NI_STM32F469I_DISCO + +#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 <cstdio> +#include <cstring> +#include <algorithm> + +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 OTM8009A driver requires a color depth of 16bit per pixel +#endif + +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 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) 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; + + /** + * Make all changes done to the display since the last call to update() + * visible. This backend requires it. + */ + 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. + * Note that since aIncr and sIncr are both zero all the writes will + * happens to the same memory location, but we need a safe + * /dev/null-like location where to write, which is dummy + */ + pixel_iterator() : ctr(0), endCtr(0), aIncr(0), sIncr(0), + dataPtr(&dummy) {} + + /** + * 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) + { + *dataPtr=color; + + //This is to move to the adjacent pixel + dataPtr+=aIncr; + + //This is the step move to the next horizontal/vertical line + if(++ctr>=endCtr) + { + ctr=0; + dataPtr+=sIncr; + } + 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->dataPtr==itr.dataPtr; + } + + /** + * Compare two pixel_iterators for inequality. + * They different if they point to different locations. + */ + bool operator!= (const pixel_iterator& itr) + { + return this->dataPtr!=itr.dataPtr; + } + + /** + * \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, + DisplayImpl *disp) : ctr(0), dataPtr(disp->framebuffer1) + { + //Compute the increment in the adjacent direction (aIncr) and in the + //step direction (sIncr) depending on the direction + dataPtr+=start.y()*disp->getWidth()+start.x(); + if(direction==RD) + { + endCtr=end.x()+1-start.x(); + aIncr=1; + sIncr=disp->getWidth()-endCtr; + } else { + endCtr=end.y()+1-start.y(); + aIncr=disp->getWidth(); + sIncr=-aIncr*endCtr+1; + } + } + + unsigned short ctr; ///< Counter to decide when to step + unsigned short endCtr; ///< When ctr==endCtr apply a step + + short aIncr; ///< Adjacent increment + int sIncr; ///< Step increment + Color *dataPtr; ///< Pointer to framebuffer + + static Color dummy; ///< Invalid iterators write here + + 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 { return last; } + + /** + * Destructor + */ + ~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; + #elif defined MXGUI_ORIENTATION_HORIZONTAL + static const short int width=800; + static const short int height=480; + #elif defined MXGUI_ORIENTATION_VERTICAL_MIRRORED || \ + defined MXGUI_ORIENTATION_HORIZONTAL_MIRRORED + #error unsupported orientation + #else + #error No orientation defined + #endif + + /** + * Pointer to the memory mapped display. + */ + Color * const framebuffer1; + Color *buffer; ///< For scanLineBuffer + pixel_iterator last; ///< Last iterator for end of iteration check + static const unsigned int bpp=sizeof(Color); ///< Bytes per pixel + static const int numPixels=width*height; ///< Number of pixels of the display +}; + +} //namespace mxgui + +#endif //_BOARD_STM32F469NI_STM32F469I_DISCO + #endif //DISPLAY_STM32F4DISCOVERY_H -- GitLab