/*********************************************************************************************************************** PicoMite MMBasic SSD1963.c Geoff Graham, Peter Mather Copyright (c) 2021, All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name MMBasic be used when referring to the interpreter in any documentation and promotional material and the original copyright message be displayed on the console at startup (additional copyright messages may be added). 4. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the . 5. Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ************************************************************************************************************************/ /** * @file SSD1963.c * @author Geoff Graham, Peter Mather * @brief Source for getscanline MMBasic function */ /** * @cond * The following section will be excluded from the documentation. */ #include "MMBasic_Includes.h" #include "Hardware_Includes.h" int ScrollStart; int Has100Pins = 0; // parameters for the SSD1963 display panel (refer to the glass data sheet) int SSD1963HorizPulseWidth, SSD1963HorizBackPorch, SSD1963HorizFrontPorch; int SSD1963VertPulseWidth, SSD1963VertBackPorch, SSD1963VertFrontPorch; int SSD1963PClock1, SSD1963PClock2, SSD1963PClock3; int SSD1963Mode1, SSD1963Mode2; unsigned int RDpin, RDport; int SSD1963PixelInterface, SSD1963PixelFormat; #define DELAY 0x80 //Bit7 of the count indicates a delay is also added. #define REPEAT 0x40 //Bit6 of the count indicates same data is repeated instead of reading next byte. int SSD1963rgb,SSD1963data=0; void WriteCmdDataIPS_4_16(int cmd,int n,int data); unsigned int ReadData(void); void ReadBLITBufferSSD1963(int x1, int y1, int x2, int y2, unsigned char* p); void DrawBLITBufferSSD1963(int x1, int y1, int x2, int y2, unsigned char* p); //#define dx(...) {char s[140];sprintf(s, __VA_ARGS__); SerUSBPutS(s); SerUSBPutS("\r\n");} //////////////////////////////////////////////////////////////////////////////////////////////////////// // // Functions used by MMBasic to setup the display // //////////////////////////////////////////////////////////////////////////////////////////////////////// void MIPS16 ConfigDisplaySSD(unsigned char *p) { getargs(&p, 13, (unsigned char *)","); if((argc & 1) != 1 || argc < 3) error("Argument count"); if(checkstring(argv[0], (unsigned char *)"SSD1963_4")) { // this is the 4" glass Option.DISPLAY_TYPE = SSD1963_4; } else if(checkstring(argv[0], (unsigned char *)"SSD1963_5")) { // this is the 5" glass Option.DISPLAY_TYPE = SSD1963_5; } else if(checkstring(argv[0], (unsigned char *)"SSD1963_5A")) { // this is the 5" glass alternative version Option.DISPLAY_TYPE = SSD1963_5A; } else if(checkstring(argv[0], (unsigned char *)"SSD1963_7")) { // there appears to be two versions of the 7" glass in circulation, this is type 1 Option.DISPLAY_TYPE = SSD1963_7; } else if(checkstring(argv[0], (unsigned char *)"SSD1963_7A")) { // this is type 2 of the 7" glass (high luminosity version) Option.DISPLAY_TYPE = SSD1963_7A; } else if(checkstring(argv[0], (unsigned char *)"SSD1963_8")) { // this is the 8" glass (EastRising) Option.DISPLAY_TYPE = SSD1963_8; } else if(checkstring(argv[0], (unsigned char *)"ILI9341_8")) { // this is the 8" glass (EastRising) Option.DISPLAY_TYPE = ILI9341_8; } else if(checkstring(argv[0], (unsigned char *)"SSD1963_4_16")) { // this is the 4" glass Option.DISPLAY_TYPE = SSD1963_4_16; } else if(checkstring(argv[0], (unsigned char *)"SSD1963_5_16")) { // this is the 5" glass Option.DISPLAY_TYPE = SSD1963_5_16; } else if(checkstring(argv[0], (unsigned char *)"SSD1963_5A_16")) { // this is the 5" glass alternative version Option.DISPLAY_TYPE = SSD1963_5A_16; } else if(checkstring(argv[0], (unsigned char *)"SSD1963_5ER_16")) { // this is the 5" EastRising RGB is BGR Option.DISPLAY_TYPE = SSD1963_5ER_16; } else if(checkstring(argv[0], (unsigned char *)"SSD1963_7_16")) { // there appears to be two versions of the 7" glass in circulation, this is type 1 Option.DISPLAY_TYPE = SSD1963_7_16; } else if(checkstring(argv[0], (unsigned char *)"SSD1963_7A_16")) { // this is type 2 of the 7" glass (high luminosity version) Option.DISPLAY_TYPE = SSD1963_7A_16; } else if(checkstring(argv[0], (unsigned char *)"SSD1963_7ER_16")) { // this is the 7" EastRising RGB is BGR Option.DISPLAY_TYPE = SSD1963_7ER_16; } else if(checkstring(argv[0], (unsigned char *)"SSD1963_8_16")) { // this is the 8" and 9" glass (EastRising) Option.DISPLAY_TYPE = SSD1963_8_16; } else if(checkstring(argv[0], (unsigned char *)"ILI9341_16")) { Option.DISPLAY_TYPE = ILI9341_16; } else if(checkstring(argv[0], (unsigned char *)"ILI9486_16")) { Option.DISPLAY_TYPE = ILI9486_16; } else if(checkstring(argv[0], (unsigned char *)"IPS_4_16")) { Option.DISPLAY_TYPE = IPS_4_16; /***G.A***/ } else return; #ifdef rp2350 if(!(argc == 3 || argc == 5 || argc == 7 || argc == 9 || (argc == 11 && !rp2350a))) error("Argument count"); #else if(!(argc == 3 || argc == 5 || argc == 7 || argc == 9 )) error("Argument count"); #endif if(checkstring(argv[2], (unsigned char *)"L") || checkstring(argv[2], (unsigned char *)"LANDSCAPE")) Option.DISPLAY_ORIENTATION = LANDSCAPE; else if(checkstring(argv[2], (unsigned char *)"P") || checkstring(argv[2], (unsigned char *)"PORTRAIT")) Option.DISPLAY_ORIENTATION = PORTRAIT; else if(checkstring(argv[2], (unsigned char *)"RL") || checkstring(argv[2], (unsigned char *)"RLANDSCAPE")) Option.DISPLAY_ORIENTATION = RLANDSCAPE; else if(checkstring(argv[2], (unsigned char *)"RP") || checkstring(argv[2], (unsigned char *)"RPORTRAIT")) Option.DISPLAY_ORIENTATION = RPORTRAIT; else error("Orientation"); Option.SSD_DATA=1; if(argc==11){ //only valid on rp2350b char code; if(!(code=codecheck(argv[10])))argv[10]+=2; int pin = getinteger(argv[10]); if(!code)pin=codemap(pin); if(IsInvalidPin(pin)) error("Invalid pin"); Option.SSD_DATA=pin; } CheckPin(SSD1963_DAT1, OptionErrorCheck); CheckPin(SSD1963_DAT2, OptionErrorCheck); CheckPin(SSD1963_DAT3, OptionErrorCheck); CheckPin(SSD1963_DAT4, OptionErrorCheck); CheckPin(SSD1963_DAT5, OptionErrorCheck); CheckPin(SSD1963_DAT6, OptionErrorCheck); CheckPin(SSD1963_DAT7, OptionErrorCheck); CheckPin(SSD1963_DAT8, OptionErrorCheck); if(Option.DISPLAY_TYPE>SSD_PANEL_8){ CheckPin(SSD1963_DAT9, OptionErrorCheck); CheckPin(SSD1963_DAT10, OptionErrorCheck); CheckPin(SSD1963_DAT11, OptionErrorCheck); CheckPin(SSD1963_DAT12, OptionErrorCheck); CheckPin(SSD1963_DAT13, OptionErrorCheck); CheckPin(SSD1963_DAT14, OptionErrorCheck); CheckPin(SSD1963_DAT15, OptionErrorCheck); CheckPin(SSD1963_DAT16, OptionErrorCheck); } if(argc > 3 && *argv[4]) { char code; if(!(code=codecheck(argv[4])))argv[4]+=2; int pin = getinteger(argv[4]); if(!code)pin=codemap(pin); if(IsInvalidPin(pin)) error("Invalid pin"); if(ExtCurrentConfig[pin] != EXT_NOT_CONFIG) error("Pin %/| is in use",pin,pin); if((PinDef[pin].slice & 0x7f) == Option.AUDIO_SLICE) error("Channel in use for Audio"); Option.DISPLAY_BL = pin; } else Option.DISPLAY_BL = 0; if(argc>=7 && *argv[6]){ char code; if(!(code=codecheck(argv[6])))argv[6]+=2; int pin = getinteger(argv[6]); if(!code)pin=codemap(pin); if(IsInvalidPin(pin)) error("Invalid pin"); #ifdef rp2350 if(Option.DISPLAY_TYPE>SSD_PANEL_8 && PinDef[pin].GPno!=16 && rp2350a)error("Must be GP16 for 16-bit displays on the RP2350A"); #else if(Option.DISPLAY_TYPE>SSD_PANEL_8 && PinDef[pin].GPno!=16)error("Must be GP16 for 16-bit displays"); #endif if(ExtCurrentConfig[pin] != EXT_NOT_CONFIG) error("Pin %/| is in use",pin,pin); Option.SSD_DC = PinDef[pin].GPno; Option.SSD_WR= Option.SSD_DC+1; Option.SSD_RD= Option.SSD_DC+2; Option.SSD_RESET= Option.SSD_DC+3; } else { if(Option.DISPLAY_TYPE>SSD_PANEL_8){ Option.SSD_DC = 16; Option.SSD_WR = 17; Option.SSD_RD = 18; Option.SSD_RESET = 19; } else { Option.SSD_DC = 13; Option.SSD_WR = 14; Option.SSD_RD = 15; Option.SSD_RESET = 16; } } if(argc>=9 && *argv[8]){ if(checkstring(argv[8],(unsigned char *)"NORESET"))Option.SSD_RESET=-1; } CheckPin(SSD1963_DC_PIN, OptionErrorCheck); if(Option.SSD_RESET!=-1) CheckPin(SSD1963_RESET_PIN, OptionErrorCheck); CheckPin(SSD1963_WR_PIN, OptionErrorCheck); CheckPin(SSD1963_RD_PIN, OptionErrorCheck); // disable the SPI LCD and touch Option.LCD_CD = Option.LCD_Reset = Option.LCD_CS = 0; Option.TOUCH_XZERO = Option.TOUCH_YZERO = 0; // record the touch feature as not calibrated } void Write16bitCommand(int cmd) { PinSetBit(SSD1963_DC_PIN, LATCLR); nop;nop;nop;nop;nop; nop; gpio_put_masked64((0xFFFF<0) { /* Write 16-bit Index, then write register */ Write16bitCommand(cmd); /* Write 16-bit Reg */ WriteData16bit(data); cmd++; n--; } } // Companion code to the above tables. Reads and issues // a series of LCD commands. //command types // 0= B7-B0 as 8bit command - command is not repeated for each subsequent byte of data. LCDPanel accepts multiple data bytes with each commande // 1=single byte shifted to B15-B8 as 16bit command -command incremented and sent with each new data byte // 2= two command bytes read to fill B15-B0 as 16bit command - command incremented and sent with each new data byte void static SendCommand16Block(const uint8_t *addr) { uint8_t numCommands, numArgs,cmdType,numReps; uint16_t ms,cmd; numCommands = *(addr++); // Number of commands to follow cmdType = *(addr++); // Number of commands to follow while(numCommands--) { // For each command... if (cmdType==2){ cmd = (*(addr++)<<8); cmd |= *(addr++); }else if (cmdType==1){ cmd = (*(addr++)<<8); }else{ //cmdType==0 cmd = *(addr++); Write16bitCommand(cmd) ; // Read, issue command and increment address } numArgs = *(addr++); // Number of args to follow ms = numArgs & DELAY; // If hibit set, delay follows args numArgs &= ~DELAY; // Mask out delay bit numReps = numArgs & REPEAT; // If B6 set then repeat same byte for numArgs numArgs &= ~REPEAT; // Mask out repeat bit if (numArgs==0 && cmdType!=0){Write16bitCommand(cmd++) ;} while(numArgs--) { // For each argument... if (numReps){ if(cmdType!=0){Write16bitCommand(cmd++) ; } WriteData16bit(*(addr)); // Read, issue argument DONT step address }else{ if(cmdType!=0){Write16bitCommand(cmd++) ; } WriteData16bit(*(addr++)); // Read, issue argument step address } } if (numReps){addr++;} //move pointer to next value if(ms) { ms = *(addr++); // Read post-command delay time (ms) if(ms == 255) ms = 500; // If 255, delay for 500 ms uSec(ms*1000); //convert to uS } } } static const uint8_t ILI9341_16Init[] = { // Initialization commands for ILI9486-16 screen 17, // no of commands in list: 0, // 0= B7-B0 as 8bit command 8=single byte shifted to B15-B8 as 16bit command 2= two bytes needed 0xCB,5,0x39,0x2c,0x00,0x34,0x02, 0xCF,3,0x00,0xc1,0x30, 0xE8,3,0x85,0x00,0x78, 0xEA,2,0x00,0x00, 0xED,4,0x64,0x03,0x12,0x81, 0xF7,1,0x20, 0xC0,1,0x23, //Power control //VRH[5:0] 0xC1,1,0x10, //Power control //SAP[2:0];BT[3:0] 0xC5,2,0x3e,0x28, //VCM control //Contrast 0xC7,1,0x86, //VCM control2 0x3A,1,0x55, // 55= RGB565 66=RGB666 0xB1,2,0x00,0x18, 0xB6,3,0x08,0x82,0x27, // Display Function Control 0xF2,1,0x00, // 3Gamma Function Disable 0x26,1,0x01, //Gamma curve selected 0xe0,15,0x0f,0x31,0x2b,0x0c,0x0e,0x08,0x4e,0xf1,0x37,0x07,0x10,0x03,0x0e,0x09,0x00, //Set Gamma 0xe1,15,0x00,0x0e,0x14,0x03,0x11,0x07,0x31,0xc1,0x48,0x08,0x0f,0x0c,0x31,0x36,0x0f //Set Gamma }; // First byte is the number of commands // Second byte is command type 0= B7-B0 as 8bit command 8=single byte shifted to B15-B8 as 16bit command 2= two bytes needed static const uint8_t ILI9486_16Init[] = { // Initialization commands for ILI9486-16 screen 15, // no of commands in list: 0, // 0= B7-B0 as 8bit command 1=single byte shifted to B15-B8 as 16bit command 2= two bytes needed 0x01,DELAY,100, // uSec( 100000); // software reset 100ms pause 0x28,0, // display off 0xF1,6,0x36,0x04,0x00,0x3c,0x0f,0x8f, 0xF2,9,0x18,0xa3,0x12,0x02,0xb2,0x12,0xff,0x10,0x00, 0xf8,2,0x21,0x04, 0xf9,2,0x00,0x08, 0xb4,1,0x00, 0xc1,1,0x47, 0xC5,4,0x00,0xaf,0x80,0x00, 0xe0,15,0x0f,0x1f,0x1c,0x0c,0x0f,0x08,0x48,0x98,0x37,0x0a,0x13,0x04,0x11,0x0d,0x00, 0xe1,15,0x0f,0x32,0x2e,0x0b,0x0d,0x05,0x47,0x75,0x37,0x06,0x10,0x03,0x24,0x20,0x00, 0x34,0, //Tearing Effect Off 0x3A,1,0x66, // Pixel Interface Format // 18 bit colour for SPI 0x11,DELAY,150, //uSec( 150000); 0x29,DELAY,255 //uSec(500000); }; static const uint8_t NT35510_16Init[] = { // Initialization commands for ILI9486-16 screen 36, // no of commands in list: 1, // 0= B7-B0 as 8bit command 1=single byte shifted to B15-B8 as 16bit command 2= two bytes needed 0xF0,5,0x55,0xAA,0x52,0x08,0x01, 0xB6,REPEAT+3,0x34,//0x34,0x34, 0xB0,REPEAT+3,0x0D,//0x0D,0x0D, 0xB7,REPEAT+3,0x24,//0x24,0x24, 0xB1,REPEAT+3,0x0D,//0x0D,0x0D, 0xB8,REPEAT+3,0x24,//0x24,0x24, 0xB2,1,0x00, 0xB9,REPEAT+3,0x24,//0x24,0x24, 0xB3,REPEAT+3,0x05,//0x05,0x05, 0xBF,1,0x01, 0xBA,REPEAT+3,0x34,//0x34,0x34, 0xB5,REPEAT+3,0x0B,//0x0B,0x0B, 0xBC,3,0x00,0xA3,0x00, 0xBD,3,0x00,0xA3,0x00, 0xBE,2,0x00,0x63, 0xD1,52, 0x00,0x37,0x00,0x52,0x00,0x7B,0x00,0x99,0x00,0xB1,0x00,0xD2,0x00,0xF6,0x01,0x27, 0x01,0x4E,0x01,0x8C,0x01,0xBE,0x02,0x0B,0x02,0x48,0x02,0x4A,0x02,0x7E,0x02,0xBC, 0x02,0xE1,0x03,0x10,0x03,0x31,0x03,0x5A,0x03,0x73,0x03,0x94,0x03,0x9F,0x03,0xB3, 0x03,0xB9,0x03,0xC1, 0xD2,52, 0x00,0x37,0x00,0x52,0x00,0x7B,0x00,0x99,0x00,0xB1,0x00,0xD2,0x00,0xF6,0x01,0x27, 0x01,0x4E,0x01,0x8C,0x01,0xBE,0x02,0x0B,0x02,0x48,0x02,0x4A,0x02,0x7E,0x02,0xBC, 0x02,0xE1,0x03,0x10,0x03,0x31,0x03,0x5A,0x03,0x73,0x03,0x94,0x03,0x9F,0x03,0xB3, 0x03,0xB9,0x03,0xC1, 0xD3,52, 0x00,0x37,0x00,0x52,0x00,0x7B,0x00,0x99,0x00,0xB1,0x00,0xD2,0x00,0xF6,0x01,0x27, 0x01,0x4E,0x01,0x8C,0x01,0xBE,0x02,0x0B,0x02,0x48,0x02,0x4A,0x02,0x7E,0x02,0xBC, 0x02,0xE1,0x03,0x10,0x03,0x31,0x03,0x5A,0x03,0x73,0x03,0x94,0x03,0x9F,0x03,0xB3, 0x03,0xB9,0x03,0xC1, 0xD4,52, 0x00,0x37,0x00,0x52,0x00,0x7B,0x00,0x99,0x00,0xB1,0x00,0xD2,0x00,0xF6,0x01,0x27, 0x01,0x4E,0x01,0x8C,0x01,0xBE,0x02,0x0B,0x02,0x48,0x02,0x4A,0x02,0x7E,0x02,0xBC, 0x02,0xE1,0x03,0x10,0x03,0x31,0x03,0x5A,0x03,0x73,0x03,0x94,0x03,0x9F,0x03,0xB3, 0x03,0xB9,0x03,0xC1, 0xD5,52, 0x00,0x37,0x00,0x52,0x00,0x7B,0x00,0x99,0x00,0xB1,0x00,0xD2,0x00,0xF6,0x01,0x27, 0x01,0x4E,0x01,0x8C,0x01,0xBE,0x02,0x0B,0x02,0x48,0x02,0x4A,0x02,0x7E,0x02,0xBC, 0x02,0xE1,0x03,0x10,0x03,0x31,0x03,0x5A,0x03,0x73,0x03,0x94,0x03,0x9F,0x03,0xB3, 0x03,0xB9,0x03,0xC1, 0xD6,52, 0x00,0x37,0x00,0x52,0x00,0x7B,0x00,0x99,0x00,0xB1,0x00,0xD2,0x00,0xF6,0x01,0x27, 0x01,0x4E,0x01,0x8C,0x01,0xBE,0x02,0x0B,0x02,0x48,0x02,0x4A,0x02,0x7E,0x02,0xBC, 0x02,0xE1,0x03,0x10,0x03,0x31,0x03,0x5A,0x03,0x73,0x03,0x94,0x03,0x9F,0x03,0xB3, 0x03,0xB9,0x03,0xC1, 0xF0,5,0x55,0xAA,0x52,0x08,0x00, 0xB0,5,0x08,0x05,0x02,0x05,0x02, 0xB6,1,0x08, 0xB5,1,0x50, //480x800 0xB7,2,0x00,0x00, 0xB8,4,0x01,0x05,0x05,0x05, 0xBC,3,0x00,0x00,0x00, 0xCC,3,0x03,0x00,0x00, 0xBD,5,0x01,0x84,0x07,0x31,0x00, 0xBA,1,0x01, 0xFF,4,0xAA,0x55,0x25,0x01, 0x35,1,0x00, 0x3A,1,0x66, // 55=Colour 565 66=Colour 666 77=Colour 888 0x11,DELAY,100, // uSec( 100000);//delay(100); 0x29,DELAY,100 // uSec( 100000);//delay(50); }; static const uint8_t OTM8009A_16Init[] = { // Initialization commands for OTM8009A_16 screen 51, // no of commands in list: 2, // 0= B7-B0 as 8bit command 1=single byte shifted to B15-B8 as 16bit command 2= two bytes needed 0xff,0x00,3,0x80,0x09,0x01, //enable access command2 0xff,0x80,2,0x80,0x09, //enable access command2 0xff,0x03,1,0x01, //DON?T KNOW ??? 0xc5,0xb1,1,0xA9, //power control 0xc5,0x91,1,0x0F, //power control 0xc0,0xB4,1,0x50, 0xE1,0x00,16,0x00,0x09,0x0F,0x0E,0x07,0x10,0x0B,0x0A,0x04,0x07,0x0B,0x08,0x0F,0x10,0x0A,0x01, 0xE2,0x00,16,0x00,0x09,0x0F,0x0E,0x07,0x10,0x0B,0x0A,0x04,0x07,0x0B,0x08,0x0F,0x10,0x0A,0x01, 0xD9,0x00,1,0x4E, /* VCOM Voltage Setting */ 0xc1,0x81,1,0x66, //osc=65HZ 0xc1,0xa1,1,0x08, //RGB Video Mode Setting 0xc5,0x92,1,0x01, //power control 0xc5,0x95,1,0x34, //power control 0xd8,0x00,2,0x79,0x79, //GVDD / NGVDD setting 0xc5,0x94,1,0x33, //power control 0xc0,0xa3,1,0x1B,//panel timing setting 0xc5,0x82,1,0x83, //power control 0xc4,0x81,1,0x83, //source driver setting 0xc1,0xa1,1,0x0E, 0xb3,0xa6,2,0x20,0x01, 0xce,0x80,6,0x85,0x01,0x00,0x84,0x01,0x00, // GOA VST 0xce,0xa0,7,0x18,0x04,0x03,0x39,0x00,0x00,0x00, 0xce,0xa7,7,0x18,0x03,0x03,0x3a,0x00,0x00,0x00, 0xce,0xb0,14,0x18,0x02,0x03,0x3b,0x00,0x00,0x00,0x18,0x01,0x03,0x3c,0x00,0x00,0x00, 0xcf,0xc0,10,0x01,0x01,0x20,0x20,0x00,0x00,0x01,0x00,0x00,0x00, 0xcf,0xd0,1,0x00, 0xcb,0x80,REPEAT+10,0x00, 0xcb,0x90,REPEAT+15,0x00, 0xcb,0xa0,REPEAT+15,0x00, 0xcb,0xb0,REPEAT+10,0x00, 0xcb,0xc0,1,0x00, 0xcb,0xc1,REPEAT+5,0x04, 0xcb,0xc6,REPEAT+9,0x00, 0xcb,0xd0,REPEAT+6,0x00, 0xcb,0xd6,REPEAT+5,0x04, 0xcb,0xdb,REPEAT+4,0x00, 0xcb,0xe0,REPEAT+10,0x00, 0xcb,0xf0,REPEAT+10,0xff, 0xcc,0x80,10,0x00,0x26,0x09,0x0B,0x01,0x25,0x00,0x00,0x00,0x00, 0xcc,0x90,REPEAT+11,0x00, 0xcc,0x9b,4,0x26,0x0A,0x0C,0x02, 0xcc,0xa0,1,0x25, 0xcc,0xa1,REPEAT+14,0x00, 0xcc,0xb0,10,0x00,0x25,0x0c,0x0a,0x02,0x26,0x00,0x00,0x00,0x00, 0xcc,0xc0,REPEAT+11,0x00, 0xcc,0xcb,4,0x25,0x0B,0x09,0x01, 0xcc,0xd0,1,0x26, 0xcc,0xd1,REPEAT+14,0x00, 0x3A,0x00,1,0x55, 0x11,0x00,DELAY,100, 0x29,0x00,DELAY,100 }; void MIPS16 InitILI9341(void){ if(Option.SSD_RESET){ PinSetBit(SSD1963_RESET_PIN, LATCLR); // reset the SSD1963 uSec(10000); PinSetBit(SSD1963_RESET_PIN, LATSET); // release from reset state to sleep state uSec(100000); } if(Option.DISPLAY_TYPE == ILI9486_16) { SendCommand16Block(ILI9486_16Init); //ILI9486 initialisation } else { //ILI9341_16 Initialisation SendCommand16Block(ILI9341_16Init); } uSec(120000); int i=0; switch(Option.DISPLAY_ORIENTATION) { case LANDSCAPE: i=ILI9341_Landscape; break; case PORTRAIT: i=ILI9341_Portrait; break; case RLANDSCAPE: i=ILI9341_Landscape180; break; case RPORTRAIT: i=ILI9341_Portrait180; break; } Write16bitCommand(0x36); // Memory Access Control WriteData16bit(i); Write16bitCommand(0x11); //Exit Sleep uSec(120000); Write16bitCommand(0x29); //display on Write16bitCommand(0x2c); //display on ClearScreen(Option.DefaultBC); } void MIPS16 InitIPS_4_16(void){ if(Option.SSD_RESET){ PinSetBit(SSD1963_RESET_PIN, LATCLR); // reset the SSD1963 uSec(10000); PinSetBit(SSD1963_RESET_PIN, LATSET); // release from reset state to sleep state uSec(100000); } int t=0; //read the id to see if OTM8009A or NT35510 WriteComand(0xDA00); gpio_set_dir_in_masked64(Option.DISPLAY_TYPE>SSD_PANEL_8 ? (0xFFFF<SSD_PANEL_8 ? (0xFFFF< SSD_PANEL_8){ SSD1963PixelInterface=3; //PIXEL data interface - 16-bit RGB565 SSD1963PixelFormat=0b01010000; //PIXEL data interface RGB565 } else { SSD1963PixelInterface=0; //PIXEL data interface - 8-bit SSD1963PixelFormat=0b01110000; //PIXEL data interface 24-bit } // setup the pointers to the drawing primitives DrawRectangle= DrawRectangleSSD1963; DrawBitmap = DrawBitmapSSD1963; if(!(Option.DISPLAY_TYPE == ILI9341_8 || Option.DISPLAY_TYPE == ILI9341_16 || Option.DISPLAY_TYPE == IPS_4_16 ))ScrollLCD = ScrollSSD1963; else ScrollLCD=ScrollLCDSPI; DrawBuffer = DrawBufferSSD1963; ReadBuffer = ReadBufferSSD1963; if(SSD16TYPE || Option.DISPLAY_TYPE==IPS_4_16){ DrawBLITBuffer= DrawBLITBufferSSD1963; ReadBLITBuffer = ReadBLITBufferSSD1963; } else { DrawBLITBuffer= DrawBufferSSD1963; ReadBLITBuffer = ReadBufferSSD1963; } DrawPixel = DrawPixelNormal; if(Option.DISPLAY_TYPE==ILI9341_8)InitILI9341_8(); else if(Option.DISPLAY_TYPE==ILI9341_16 || Option.DISPLAY_TYPE == ILI9486_16 )InitILI9341(); else if(Option.DISPLAY_TYPE == IPS_4_16) InitIPS_4_16(); else InitSSD1963(); SetFont(Option.DefaultFont); PromptFont = gui_font; PromptFC = gui_fcolour = Option.DefaultFC; PromptBC = gui_bcolour = Option.DefaultBC; ResetDisplay(); } //////////////////////////////////////////////////////////////////////////////////////////////////////// // // The SSD1963 driver // //////////////////////////////////////////////////////////////////////////////////////////////////////// // Write a command byte to the SSD1963 void WriteComand(int cmd) { gpio_put_masked64((Option.DISPLAY_TYPE>SSD_PANEL_8 ? (0xFFFF<SSD_PANEL_8 ? (0xFFFF<SSD_PANEL_8){ gpio_put_masked64((0xFFFF<> 16) & 0xFF)<> 8) & 0xFF)<SSD_PANEL_8 ? (0xFFFF<SSD_PANEL_8 ? (0xFFFF<SSD_PANEL_8 ? (0xFFFF<>SSD1963data; } unsigned int ReadDataIPS(void) { gpio_put(SSD1963_RD_GPPIN,0);nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;gpio_put(SSD1963_RD_GPPIN,1); return (gpio_get_all64() & (Option.DISPLAY_TYPE>SSD_PANEL_8 ? (0xFFFF<>SSD1963data; } // Slowly read a byte from the SSD1963 unsigned int ReadDataSlow(void) { gpio_put(SSD1963_RD_GPPIN,0); uSec(2); gpio_put(SSD1963_RD_GPPIN,1); uSec(2); return (gpio_get_all64() & (Option.DISPLAY_TYPE>SSD_PANEL_8 ?(0xFFFF<>SSD1963data; } /********************************************************************* * defines start/end coordinates for memory access from host to SSD1963 * also maps the start and end points to suit the orientation ********************************************************************/ void SetAreaSSD1963(int x1, int y1, int x2, int y2) { int start_x, start_y, end_x, end_y; switch(Option.DISPLAY_ORIENTATION) { case LANDSCAPE: case RLANDSCAPE: start_x = x1; end_x = x2; start_y = y1; end_y = y2; break; case PORTRAIT: case RPORTRAIT: start_x = y1; end_x = y2; start_y = (DisplayVRes - 1) - x2; end_y = (DisplayVRes - 1) - x1; break; default: return; } WriteComand(CMD_SET_COLUMN); WriteData(start_x>>8); WriteData(start_x); WriteData(end_x>>8); WriteData(end_x); WriteComand(CMD_SET_PAGE); WriteData(start_y>>8); WriteData(start_y); WriteData(end_y>>8); WriteData(end_y); } /********************************************************************* * Set a GPIO pin to state high(1) or low(0) * * PreCondition: Set the GPIO pin an output prior using this function * * Arguments: BYTE pin - LCD_RESET * LCD_SPENA * LCD_SPCLK * LCD_SPDAT * * BOOL state - 0 for low * 1 for high *********************************************************************/ static void GPIO_WR(int pin, int state) { int _gpioStatus = 0; if(state==1) _gpioStatus = _gpioStatus|pin; else _gpioStatus = _gpioStatus&(~pin); WriteComand(CMD_SET_GPIO_VAL); // Set GPIO value WriteData(_gpioStatus); } /********************************************************************* * SetBacklight(BYTE intensity) * Some boards may use of PWM feature of ssd1963 to adjust the backlight * intensity and this function supports that. However, most boards have * a separate PWM input pin and that is also supported by using the variable * display_backlight intimer.c * * Input: intensity = 0 (off) to 100 (full on) * * Note: The base frequency of PWM set to around 300Hz with PLL set to 120MHz. * This parameter is hardware dependent ********************************************************************/ void MIPS16 SetBacklightSSD1963(int intensity) { WriteComand(CMD_SET_PWM_CONF); // Set PWM configuration for backlight control WriteData(0x0E); // PWMF[7:0] = 2, PWM base freq = PLL/(256*(1+5))/256 = 300Hz for a PLL freq = 120MHz WriteData((intensity * 255)/100); // Set duty cycle, from 0x00 (total pull-down) to 0xFF (99% pull-up , 255/256) WriteData(0x01); // PWM enabled and controlled by host (mcu) WriteData(0x00); WriteData(0x00); WriteData(0x00); display_backlight = intensity/5; // this is used in timer.c } /********************************************************************* * SetTearingCfg(BOOL state, BOOL mode) * This function enable/disable tearing effect * * Input: BOOL state - 1 to enable * 0 to disable * BOOL mode - 0: the tearing effect output line consists * of V-blanking information only * 1: the tearing effect output line consists * of both V-blanking and H-blanking info. ********************************************************************/ void SetTearingCfg(int state, int mode) { if(state == 1) { WriteComand(CMD_SET_TEAR_ON); WriteData(mode&0x01); } else { WriteComand(0x34); } } /*********************************************************************************************************************************** * Function: void InitSSD1963() * Initialise SSD1963 for PCLK, HSYNC, VSYNC etc ***********************************************************************************************************************************/ void MIPS16 InitILI9341_8(void){ if(Option.SSD_RESET){ PinSetBit(SSD1963_RESET_PIN, LATCLR); // reset the SSD1963 uSec(10000); PinSetBit(SSD1963_RESET_PIN, LATSET); // release from reset state to sleep state uSec(100000); } WriteComand(0xEF); WriteData(0x03); WriteData(0x80); WriteData(0x02); WriteComand(0xCF); WriteData(0x00); WriteData(0XC1); WriteData(0X30); WriteComand(0xED); WriteData(0x64); WriteData(0x03); WriteData(0X12); WriteData(0X81); WriteComand(0xE8); WriteData(0x85); WriteData(0x00); WriteData(0x78); WriteComand(0xCB); WriteData(0x39); WriteData(0x2C); WriteData(0x00); WriteData(0x34); WriteData(0x02); WriteComand(0xF7); WriteData(0x20); WriteComand(0xEA); WriteData(0x00); WriteData(0x00); WriteComand(ILI9341_PWCTR1); //Power control WriteData(0x23); //VRH[5:0] WriteComand(ILI9341_PWCTR2); //Power control WriteData(0x10); //SAP[2:0];BT[3:0] WriteComand(ILI9341_VMCTR1); //VCM control WriteData(0x3e); WriteData(0x28); WriteComand(ILI9341_VMCTR2); //VCM control2 WriteData(0x86); //-- WriteComand(ILI9341_PIXFMT); WriteData(0x66); WriteComand(ILI9341_FRMCTR1); WriteData(0x00); WriteData(0x13); // 0x18 79Hz, 0x1B default 70Hz, 0x13 100Hz WriteComand(ILI9341_DFUNCTR); // Display Function Control WriteData(0x08); WriteData(0x82); WriteData(0x27); WriteComand(0xF2); // 3Gamma Function Disable WriteData(0x00); WriteComand(ILI9341_GAMMASET); //Gamma curve selected WriteData(0x01); WriteComand(ILI9341_GMCTRP1); //Set Gamma WriteData(0x0F); WriteData(0x31); WriteData(0x2B); WriteData(0x0C); WriteData(0x0E); WriteData(0x08); WriteData(0x4E); WriteData(0xF1); WriteData(0x37); WriteData(0x07); WriteData(0x10); WriteData(0x03); WriteData(0x0E); WriteData(0x09); WriteData(0x00); WriteComand(ILI9341_GMCTRN1); //Set Gamma WriteData(0x00); WriteData(0x0E); WriteData(0x14); WriteData(0x03); WriteData(0x11); WriteData(0x07); WriteData(0x31); WriteData(0xC1); WriteData(0x48); WriteData(0x08); WriteData(0x0F); WriteData(0x0C); WriteData(0x31); WriteData(0x36); WriteData(0x0F); WriteComand(ILI9341_SLPOUT); //Exit Sleep WriteComand(ILI9341_MADCTL); // Memory Access Control switch(Option.DISPLAY_ORIENTATION) { case LANDSCAPE: WriteData(ILI9341_Landscape); break; case PORTRAIT: WriteData(ILI9341_Portrait); break; case RLANDSCAPE: WriteData(ILI9341_Landscape180); break; case RPORTRAIT: WriteData(ILI9341_Portrait180); break; } WriteComand(ILI9341_DISPON); //Display on uSec(100000); ClearScreen(Option.DefaultBC); } void MIPS16 InitSSD1963(void) { if(Option.SSD_RESET){ PinSetBit(SSD1963_RESET_PIN, LATCLR); // reset the SSD1963 uSec(10000); PinSetBit(SSD1963_RESET_PIN, LATSET); // release from reset state to sleep state uSec(100000); } // IMPORTANT: At this stage the SSD1963 is running at a slow speed and cannot respond to high speed commands // So we use slow speed versions of WriteComand/WriteData with a 3 uS delay between each control signal change // Set MN(multipliers) of PLL, VCO = crystal freq * (N+1) // PLL freq = VCO/M with 250MHz < VCO < 800MHz // The max PLL freq is around 120MHz. To obtain 120MHz as the PLL freq WriteComandSlow(CMD_SET_PLL_MN); // Set PLL with OSC = 10MHz (hardware), command is 0xE3 WriteDataSlow(0x23); // Multiplier N = 35, VCO (>250MHz)= OSC*(N+1), VCO = 360MHz WriteDataSlow(0x02); // Divider M = 2, PLL = 360/(M+1) = 120MHz WriteDataSlow(0x54); // Validate M and N values WriteComandSlow(CMD_PLL_START); // Start PLL command WriteDataSlow(0x01); // enable PLL uSec(1000); // wait for it to stabilise WriteComandSlow(CMD_PLL_START); // Start PLL command again WriteDataSlow(0x03); // now, use PLL output as system clock WriteComandSlow(CMD_SOFT_RESET); // Soft reset uSec(10000); #define parallel_write_data WriteData #define TFT_Write_Data WriteData // Configure for the TFT panel, varies from individual manufacturer WriteComandSlow(CMD_SET_PCLK); // set pixel clock (LSHIFT signal) frequency WriteDataSlow(SSD1963PClock1); // paramaters set by DISPLAY INIT WriteDataSlow(SSD1963PClock2); WriteDataSlow(SSD1963PClock3); // uSec(10000); // Set panel mode, varies from individual manufacturer WriteComand(CMD_SET_PANEL_MODE); WriteData(SSD1963Mode1); // parameters set by DISPLAY INIT WriteData(SSD1963Mode2); WriteData((DisplayHRes - 1) >> 8); // Set panel size WriteData(DisplayHRes - 1); WriteData((DisplayVRes - 1) >> 8); WriteData(DisplayVRes - 1); WriteData(0x00); // RGB sequence // Set horizontal period WriteComand(CMD_SET_HOR_PERIOD); #define HT (DisplayHRes + SSD1963HorizPulseWidth + SSD1963HorizBackPorch + SSD1963HorizFrontPorch) WriteData((HT - 1) >> 8); WriteData(HT - 1); #define HPS (SSD1963HorizPulseWidth + SSD1963HorizBackPorch) WriteData((HPS - 1) >> 8); WriteData(HPS - 1); WriteData(SSD1963HorizPulseWidth - 1); WriteData(0x00); WriteData(0x00); WriteData(0x00); // Set vertical period WriteComand(CMD_SET_VER_PERIOD); #define VT (SSD1963VertPulseWidth + SSD1963VertBackPorch + SSD1963VertFrontPorch + DisplayVRes) WriteData((VT - 1) >> 8); WriteData(VT - 1); #define VSP (SSD1963VertPulseWidth + SSD1963VertBackPorch) WriteData((VSP - 1) >> 8); WriteData(VSP - 1); WriteData(SSD1963VertPulseWidth - 1); WriteData(0x00); WriteData(0x00); // Set pixel data interface WriteComand(CMD_SET_DATA_INTERFACE); WriteData(SSD1963PixelInterface); // 8-bit colour format WriteComand(CMD_SET_PIXEL_FORMAT); WriteData(SSD1963PixelFormat); // 8-bit colour format // initialise the GPIOs WriteComand(CMD_SET_GPIO_CONF); // Set all GPIOs to output, controlled by host WriteData(0x0f); // Set GPIO0 as output WriteData(0x01); // GPIO[3:0] used as normal GPIOs // LL Reset to LCD!!! GPIO_WR(LCD_SPENA, 1); GPIO_WR(LCD_SPCLK, 1); GPIO_WR(LCD_SPDAT,1); GPIO_WR(LCD_RESET,1); GPIO_WR(LCD_RESET,0); uSec(1000); GPIO_WR(LCD_RESET,1); // setup the pixel write order WriteComand(CMD_SET_ADDR_MODE); switch(Option.DISPLAY_ORIENTATION) { case LANDSCAPE: WriteData(SSD1963_LANDSCAPE | SSD1963rgb); break; case PORTRAIT: WriteData(SSD1963_PORTRAIT | SSD1963rgb); break; case RLANDSCAPE: WriteData(SSD1963_RLANDSCAPE | SSD1963rgb); break; case RPORTRAIT: WriteData(SSD1963_RPORTRAIT | SSD1963rgb); break; } // Set the scrolling area WriteComand(CMD_SET_SCROLL_AREA); WriteData(0); WriteData(0); WriteData(DisplayVRes >> 8); WriteData(DisplayVRes); WriteData(0); WriteData(0); ScrollStart = 0; ClearScreen(Option.DefaultBC); SetBacklightSSD1963(Option.DefaultBrightness); WriteComand(CMD_ON_DISPLAY); // Turn on display; show the image on display } void SetAreaIPS_4_16(int xstart, int ystart, int xend, int yend, int rw) { if(HRes == 0) error("Display not configured"); WriteCmdDataIPS_4_16(0x2A00,1,xstart>>8); WriteCmdDataIPS_4_16(0x2A01,1,xstart & 0xFF); WriteCmdDataIPS_4_16(0x2A02,1,xend>>8); WriteCmdDataIPS_4_16(0x2A03,1,xend & 0xFF); WriteCmdDataIPS_4_16(0x2B00,1,ystart>>8); WriteCmdDataIPS_4_16(0x2B01,1,ystart & 0xFF); WriteCmdDataIPS_4_16(0x2B02,1,yend>>8); WriteCmdDataIPS_4_16(0x2B03,1,yend & 0xFF); if(rw){ Write16bitCommand(0x2C00); //write to memory } else { Write16bitCommand(0x2E00); //read from memory } } void SetAreaILI9341(int xstart, int ystart, int xend, int yend, int rw) { if(HRes == 0) error("Display not configured"); WriteComand(ILI9341_COLADDRSET); WriteData(xstart >> 8); WriteData(xstart); WriteData(xend >> 8); WriteData(xend); WriteComand(ILI9341_PAGEADDRSET); WriteData(ystart >> 8); WriteData(ystart); WriteData(yend >> 8); WriteData(yend); if(rw){ WriteComand(ILI9341_MEMORYWRITE); } else { WriteComand(ILI9341_RAMRD); } } /********************************************************************************************** Draw a filled rectangle on the video output with physical frame buffer coordinates x1, y1 - the start physical frame buffer coordinate x2, y2 - the end physical frame buffer coordinate c - the colour to use for both the fill This is only called by DrawRectangleSSD1963() below ***********************************************************************************************/ void PhysicalDrawRect(int x1, int y1, int x2, int y2, int c) { int i; if(Option.DISPLAY_TYPE == ILI9341_8){ SetAreaILI9341(x1, y1 , x2, y2, 1); } else if(Option.DISPLAY_TYPE == ILI9341_16 || Option.DISPLAY_TYPE == ILI9486_16) { if(Option.DISPLAY_TYPE == ILI9486_16){ Write16bitCommand(ILI9341_PIXELFORMAT); WriteData16bit(0x55); } SetAreaILI9341(x1, y1 , x2, y2, 1); } else if(Option.DISPLAY_TYPE==IPS_4_16) { if(LCDAttrib==1)WriteCmdDataIPS_4_16(0x3A00,1,0x55); SetAreaIPS_4_16(x1, y1 , x2, y2, 1); } else { SetAreaSSD1963(x1, y1 , x2, y2); // setup the area to be filled WriteComand(CMD_WR_MEMSTART); } if(Option.DISPLAY_TYPE>SSD_PANEL_8){ c=((c>>8) & 0xf800) | ((c>>5) & 0x07e0) | ((c>>3) & 0x001f); i=(x2 - x1 + 1) * (y2 - y1 + 1); while(i--){ gpio_put(SSD1963_WR_GPPIN,0); gpio_put_masked64(0xFFFF< 0; i--) WriteColor(c); } if(LCDAttrib==1)WriteCmdDataIPS_4_16(0x3A00,1,0x66); if(Option.DISPLAY_TYPE == ILI9486_16){ Write16bitCommand(ILI9341_PIXELFORMAT); WriteData16bit(0x66); } } //////////////////////////////////////////////////////////////////////////////////////////////////////// // // Drawing primitives used by the functions in GUI.c and Draw.c // //////////////////////////////////////////////////////////////////////////////////////////////////////// /********************************************************************************************** Draw a filled rectangle on the video output with logical (MMBasic) coordinates x1, y1 - the start coordinate x2, y2 - the end coordinate c - the colour to use for both the fill ***********************************************************************************************/ void DrawRectangleSSD1963(int x1, int y1, int x2, int y2, int c) { int t; // make sure the coordinates are kept within the display area if(x2 <= x1) { t = x1; x1 = x2; x2 = t; } if(y2 <= y1) { t = y1; y1 = y2; y2 = t; } if(x1 < 0) x1 = 0; if(x1 >= HRes) x1 = HRes - 1; if(x2 < 0) x2 = 0; if(x2 >= HRes) x2 = HRes - 1; if(y1 < 0) y1 = 0; if(y1 >= VRes) y1 = VRes - 1; if(y2 < 0) y2 = 0; if(y2 >= VRes) y2 = VRes - 1; t = y2 - y1; // get the distance between the top and bottom // set y1 to the physical location in the frame buffer (only really has an effect when scrolling is in action) if(Option.DISPLAY_ORIENTATION == RLANDSCAPE) y1 = (y1 + (VRes - ScrollStart)) % VRes; else y1 = (y1 + ScrollStart) % VRes; y2 = y1 + t; // and set y2 to the same if(y2 >= VRes) { // if the box splits over the frame buffer boundary PhysicalDrawRect(x1, y1, x2, VRes - 1, c); // draw the top part PhysicalDrawRect(x1, 0, x2, y2 - VRes , c); // and the bottom part } else PhysicalDrawRect(x1, y1, x2, y2, c); // the whole box is within the frame buffer - much easier } void DrawRectangle320(int x1, int y1, int x2, int y2, int c) { if(Option.DISPLAY_TYPE!=SSD1963_4_16){ y1*=2; y2=y2*2+1; HRes=720; VRes=480; x1=x1*2+80; x2=x2*2+81; } else { HRes=400; VRes=272; x1+=80; x2+=80; y1+=16; y2+=16; if(y1<16 && y2<16)return; } if(x1<80 && x2<80)return; if(x1<80)x1=80; if(x2<80)x2=80; DrawRectangleSSD1963(x1,y1,x2,y2,c); HRes=320; VRes=240; } // written by Peter Mather (matherp on the Back Shed forum) void DrawBufferSSD1963(int x1, int y1, int x2, int y2, unsigned char* p) { int i,t,toggle=0; unsigned int bl=0; union colourmap { char rgbbytes[4]; unsigned int rgb; } c; // make sure the coordinates are kept within the display area if(x2 <= x1) { t = x1; x1 = x2; x2 = t; } if(y2 <= y1) { t = y1; y1 = y2; y2 = t; } if(x1 < 0) x1 = 0; if(x1 >= HRes) x1 = HRes - 1; if(x2 < 0) x2 = 0; if(x2 >= HRes) x2 = HRes - 1; if(y1 < 0) y1 = 0; if(y1 >= VRes) y1 = VRes - 1; if(y2 < 0) y2 = 0; if(y2 >= VRes) y2 = VRes - 1; t = y2 - y1; // get the distance between the top and bottom if(Option.DISPLAY_TYPE!=ILI9341_8){ if(Option.DISPLAY_ORIENTATION == RLANDSCAPE) y1 = (y1 + (VRes - ScrollStart)) % VRes; else y1 = (y1 + ScrollStart) % VRes; y2 = y1 + t; } // and set y2 to the same if(y2 >= VRes) { SetAreaSSD1963(x1, y1, x2, VRes - 1); // if the box splits over the frame buffer boundary WriteComand(CMD_WR_MEMSTART); for(i = (x2 - x1 + 1) * ((VRes - 1) - y1 + 1); i > 0; i--){ c.rgbbytes[0] = *p++; // this order swaps the bytes to match the .BMP file c.rgbbytes[1] = *p++; c.rgbbytes[2] = *p++; if(Option.DISPLAY_TYPE>SSD_PANEL_8) WriteColor(((c.rgb>>8) & 0xf800) | ((c.rgb>>5) & 0x07e0) | ((c.rgb>>3) & 0x001f)); else WriteColor(c.rgb); } SetAreaSSD1963(x1, 0, x2, y2 - VRes ); WriteComand(CMD_WR_MEMSTART); for(i = (x2 - x1 + 1) * (y2 - VRes + 1); i > 0; i--) { c.rgbbytes[0] = *p++; // this order swaps the bytes to match the .BMP file c.rgbbytes[1] = *p++; c.rgbbytes[2] = *p++; if(Option.DISPLAY_TYPE>SSD_PANEL_8) WriteColor(((c.rgb>>8) & 0xf800) | ((c.rgb>>5) & 0x07e0) | ((c.rgb>>3) & 0x001f)); else WriteColor(c.rgb); } } else { // the whole box is within the frame buffer - much easier if(Option.DISPLAY_TYPE==ILI9341_8 || Option.DISPLAY_TYPE == ILI9341_16 || Option.DISPLAY_TYPE == ILI9486_16 ) { if(Option.DISPLAY_TYPE==ILI9486_16)LCDAttrib=2; SetAreaILI9341(x1, y1 , x2, y2, 1); } else if(Option.DISPLAY_TYPE==IPS_4_16) { SetAreaIPS_4_16(x1, y1 , x2, y2, 1); } else { SetAreaSSD1963(x1, y1 , x2, y2); // setup the area to be filled WriteComand(CMD_WR_MEMSTART); } for(int y=y1;y<=y2;y++){ for(int x=x1;x<=x2;x++){ if(x>=0 && x=0 && ySSD_PANEL_8){ if(LCDAttrib==0){ WriteColor(((c.rgb>>8) & 0xf800) | ((c.rgb>>5) & 0x07e0) | ((c.rgb>>3) & 0x001f)); } else { if(toggle==0){ gpio_put_masked64(0xFFFF<>8) & 0xf800) | ((c.rgb>>8) & 0x00fc))<>16) & 0x00f8))<>8) & 0x00f8))<= HRes) x1 = HRes - 1; if(x2 < 0) x2 = 0; if(x2 >= HRes) x2 = HRes - 1; if(y1 < 0) y1 = 0; if(y1 >= VRes) y1 = VRes - 1; if(y2 < 0) y2 = 0; if(y2 >= VRes) y2 = VRes - 1; if(Option.DISPLAY_TYPE!=SSD1963_4_16){ HRes=720; VRes=480; } else { HRes=400; VRes=256; } unsigned char *q = buff320; for(int y=y1;y<=y2;y++){ int yo=y*2; unsigned char *pp=q; if(Option.DISPLAY_TYPE!=SSD1963_4_16){ for(int x=x1;x<=x2;x++){ pp[0]=pp[3]=*p++; pp[1]=pp[4]=*p++; pp[2]=pp[5]=*p++; pp+=6; } DrawBufferSSD1963(x1*2+80,yo,x2*2+81,yo,q); DrawBufferSSD1963(x1*2+80,yo+1,x2*2+81,yo+1,q); } else { for(int x=x1;x<=x2;x++){ pp[0]=*p++; pp[1]=*p++; pp[2]=*p++; pp+=3; } DrawBufferSSD1963(x1+80,y+16,x2+80,y+16,q); } } HRes=320; VRes=240; } void DrawBLITBufferSSD1963(int x1, int y1, int x2, int y2, unsigned char* p) { int i,t; // make sure the coordinates are kept within the display area if(x2 <= x1) { t = x1; x1 = x2; x2 = t; } if(y2 <= y1) { t = y1; y1 = y2; y2 = t; } if(x1 < 0) x1 = 0; if(x1 >= HRes) x1 = HRes - 1; if(x2 < 0) x2 = 0; if(x2 >= HRes) x2 = HRes - 1; if(y1 < 0) y1 = 0; if(y1 >= VRes) y1 = VRes - 1; if(y2 < 0) y2 = 0; if(y2 >= VRes) y2 = VRes - 1; uint16_t *pp = (uint16_t *)p; t = y2 - y1; // get the distance between the top and bottom if(Option.DISPLAY_TYPE!=ILI9341_8){ if(Option.DISPLAY_ORIENTATION == RLANDSCAPE) y1 = (y1 + (VRes - ScrollStart)) % VRes; else y1 = (y1 + ScrollStart) % VRes; y2 = y1 + t; } // and set y2 to the same if(y2 >= VRes) { SetAreaSSD1963(x1, y1, x2, VRes - 1); // if the box splits over the frame buffer boundary WriteComand(CMD_WR_MEMSTART); for(i = (x2 - x1 + 1) * ((VRes - 1) - y1 + 1); i > 0; i--){ gpio_put(SSD1963_WR_GPPIN,0); gpio_put_masked64(0xFFFF< 0; i--) { gpio_put(SSD1963_WR_GPPIN,0); gpio_put_masked64(0xFFFF<=0 && x=0 && y= HRes) x1 = HRes - 1; if(x2 < 0) x2 = 0; if(x2 >= HRes) x2 = HRes - 1; if(y1 < 0) y1 = 0; if(y1 >= VRes) y1 = VRes - 1; if(y2 < 0) y2 = 0; if(y2 >= VRes) y2 = VRes - 1; if(Option.DISPLAY_TYPE!=SSD1963_4_16){ HRes=720; VRes=480; } else { HRes=400; VRes=256; } unsigned char *q = buff320; for(int x=x1;x<=x2;x++){ unsigned char *pp=q; if(Option.DISPLAY_TYPE!=SSD1963_4_16){ for(int y=y1;y<=y2;y++){ pp[0]=pp[2]=*p++; pp[1]=pp[3]=*p++; pp+=4; } DrawBLITBufferSSD1963(x*2+80,y1*2,x*2+80,y2*2,q); DrawBLITBufferSSD1963(x*2+81,y1*2,x*2+81,y2*2,q); } else { for(int y=y1;y<=y2;y++){ *pp++=*p++; *pp++=*p++; } DrawBLITBufferSSD1963(x+80,y1+16,x+80,y2+16,q); } } HRes=320; VRes=240; } // Read RGB colour over an 8 bit bus inline __attribute((always_inline)) unsigned int ReadColor(void) { if(Option.DISPLAY_TYPE>SSD_PANEL_8){ uint32_t d=ReadData(); return ((d & 0xf800)<<8) | ((d & 0x7E0)<<5) | ((d & 0x1f)<<3); } else { return(ReadData() << 16) | (ReadData() << 8) | ReadData(); } } // Read RGB colour over an 8 bit bus // but do it slowly to avoid timing issues with the first pixel unsigned int ReadColorSlow(void) { if(Option.DISPLAY_TYPE>SSD_PANEL_8){ uint32_t d=ReadDataSlow(); return ((d & 0xf800)<<8) | ((d & 0x7E0)<<5) | ((d & 0x1f)<<3); } else return(ReadDataSlow() << 16) | (ReadDataSlow() << 8) | ReadDataSlow(); } // written by Peter Mather (matherp on the Back Shed forum) void ReadBufferSSD1963(int x1, int y1, int x2, int y2, unsigned char* p) { int i, t; int toggle=0,t1=0, nr=0 ; union colourmap { char rgbbytes[4]; unsigned int rgb; } c; // make sure the coordinates are kept within the display area if(x2 <= x1) { t = x1; x1 = x2; x2 = t; } if(y2 <= y1) { t = y1; y1 = y2; y2 = t; } if(x1 < 0) x1 = 0; if(x1 >= HRes) x1 = HRes - 1; if(x2 < 0) x2 = 0; if(x2 >= HRes) x2 = HRes - 1; if(y1 < 0) y1 = 0; if(y1 >= VRes) y1 = VRes - 1; if(y2 < 0) y2 = 0; if(y2 >= VRes) y2 = VRes - 1; t = y2 - y1; // get the distance between the top and bottom if(!(Option.DISPLAY_TYPE==ILI9341_8 || Option.DISPLAY_TYPE == IPS_4_16 || Option.DISPLAY_TYPE == ILI9341_16 || Option.DISPLAY_TYPE == ILI9486_16 )){ if(Option.DISPLAY_ORIENTATION == RLANDSCAPE) y1 = (y1 + (VRes - ScrollStart)) % VRes; else y1 = (y1 + ScrollStart) % VRes; y2 = y1 + t; } // and set y2 to the same if(y2 >= VRes) { SetAreaSSD1963(x1, y1, x2, VRes - 1); // if the box splits over the frame buffer boundary WriteComand(CMD_RD_MEMSTART); gpio_set_dir_in_masked64((Option.DISPLAY_TYPE>SSD_PANEL_8 ? (0xFFFF< 1; i--) { // NB loop counter terminates 1 pixel earlier c.rgb = ReadColor(); *p++ = c.rgbbytes[0]; // this order swaps the bytes to match the .BMP file *p++ = c.rgbbytes[1]; *p++ = c.rgbbytes[2]; } gpio_set_dir_out_masked64((Option.DISPLAY_TYPE>SSD_PANEL_8 ? (0xFFFF<SSD_PANEL_8 ? (0xFFFF< 1; i--) { // NB loop counter terminates 1 pixel earlier c.rgb = ReadColor(); *p++ = c.rgbbytes[0]; // this order swaps the bytes to match the .BMP file *p++ = c.rgbbytes[1]; *p++ = c.rgbbytes[2]; } gpio_set_dir_out_masked64((Option.DISPLAY_TYPE>SSD_PANEL_8 ? (0xFFFF<SSD_PANEL_8 ?(0xFFFF< 0; i--){ if(Option.DISPLAY_TYPE == ILI9341_16 || Option.DISPLAY_TYPE == ILI9486_16 ) { if(toggle==0){ t=ReadData(); t<<=8 ; t1=ReadData(); t|=(t1>>8); t1 &=0xFF; toggle=1; } else { t=ReadData(); t|= (t1<<16); toggle=0; } *p++=(t & 0xf8); *p++=(t & 0xfc00)>>8; *p++=(t & 0xf80000)>>16; } else if(Option.DISPLAY_TYPE==IPS_4_16) { if(toggle==0){ //RGBR 8bit each t=ReadDataIPS(); t1=ReadDataIPS(); *p++=(t1 & 0xF800)>>8; //BLUE *p++=(t & 0xFC); //GREEN *p++=(t & 0xF800)>>8; //RED nr=(t1 & 0xF8); //save next red if(LCDAttrib==1){ //NT35510 does not need toggle=1 toggle=0; }else{ toggle=1; } } else { t=ReadDataIPS(); //get the second GB *p++=(t & 0xF8); //Blue *p++=(t & 0xFC00)>>8; //Green FIX HERE *p++=nr ; //add the red toggle=0; } } else { c.rgb=ReadColor(); *p++=c.rgbbytes[0]; // this order swaps the bytes to match the .BMP file *p++=c.rgbbytes[1]; *p++=c.rgbbytes[2]; } } gpio_set_dir_out_masked64((Option.DISPLAY_TYPE>SSD_PANEL_8 ? (0xFFFF<= HRes) x1 = HRes - 1; if(x2 < 0) x2 = 0; if(x2 >= HRes) x2 = HRes - 1; if(y1 < 0) y1 = 0; if(y1 >= VRes) y1 = VRes - 1; if(y2 < 0) y2 = 0; if(y2 >= VRes) y2 = VRes - 1; if(Option.DISPLAY_TYPE!=SSD1963_4_16){ HRes=720; VRes=480; } else { HRes=400; VRes=256; } unsigned char *q = buff320; for(int y=y1;y<=y2;y++){ int yo=y*2; if(Option.DISPLAY_TYPE!=SSD1963_4_16)ReadBufferSSD1963(x1*2+80,yo,x2*2+81,yo,q); else ReadBufferSSD1963(x1+80,y+16,x2+80,y+16,q); unsigned char *pp=q; for(int x=x1;x<=x2;x++){ *p++=*pp++; *p++=*pp++; *p++=*pp++; pp+=3; } } HRes=320; VRes=240; } void ReadBLITBufferSSD1963(int x1, int y1, int x2, int y2, unsigned char* p) { int i, t; int toggle=0,t1=0, nr=0 ; union colourmap { char rgbbytes[4]; unsigned int rgb; } c; // make sure the coordinates are kept within the display area if(x2 <= x1) { t = x1; x1 = x2; x2 = t; } if(y2 <= y1) { t = y1; y1 = y2; y2 = t; } if(x1 < 0) x1 = 0; if(x1 >= HRes) x1 = HRes - 1; if(x2 < 0) x2 = 0; if(x2 >= HRes) x2 = HRes - 1; if(y1 < 0) y1 = 0; if(y1 >= VRes) y1 = VRes - 1; if(y2 < 0) y2 = 0; if(y2 >= VRes) y2 = VRes - 1; uint16_t *pp=(uint16_t *)p; t = y2 - y1; // get the distance between the top and bottom if(Option.DISPLAY_ORIENTATION == RLANDSCAPE) y1 = (y1 + (VRes - ScrollStart)) % VRes; else y1 = (y1 + ScrollStart) % VRes; y2 = y1 + t; if(y2 >= VRes) { SetAreaSSD1963(x1, y1, x2, VRes - 1); // if the box splits over the frame buffer boundary WriteComand(CMD_RD_MEMSTART); gpio_set_dir_in_masked64(0xFFFF< 1; i--) { // NB loop counter terminates 1 pixel earlier gpio_put(SSD1963_RD_GPPIN,0);nop;nop;nop;nop;nop;nop;gpio_put(SSD1963_RD_GPPIN,1); *pp++ = ((gpio_get_all64() & (0xFFFF<>SSD1963data); } gpio_set_dir_out_masked64(0xFFFF< 1; i--) { // NB loop counter terminates 1 pixel earlier gpio_put(SSD1963_RD_GPPIN,0);nop;nop;nop;nop;nop;nop;gpio_put(SSD1963_RD_GPPIN,1); *pp++ = ((gpio_get_all64() & (0xFFFF<>SSD1963data); } gpio_set_dir_out_masked64(0xFFFF 0; i--){ if(Option.DISPLAY_TYPE==IPS_4_16) { if(toggle==0){ //RGBR 8bit each t=ReadDataIPS(); t1=ReadDataIPS(); c.rgbbytes[0]=(t1 & 0xF800)>>8; //BLUE c.rgbbytes[1]=(t & 0xFC); //GREEN c.rgbbytes[2]=(t & 0xF800)>>8; //RED *pp++=((c.rgb>>8) & 0xf800) | ((c.rgb>>5) & 0x07e0) | ((c.rgb>>3) & 0x001f); nr=(t1 & 0xF8); //save next red if(LCDAttrib==1){ //NT35510 does not need toggle=1 toggle=0; }else{ toggle=1; } } else { t=ReadDataIPS(); //get the second GB c.rgbbytes[0]=(t & 0xF8); //Blue c.rgbbytes[1]=(t & 0xFC00)>>8; //Green FIX HERE c.rgbbytes[2]=nr ; //add the red *pp++=((c.rgb>>8) & 0xf800) | ((c.rgb>>5) & 0x07e0) | ((c.rgb>>3) & 0x001f); toggle=0; } } else { gpio_put(SSD1963_RD_GPPIN,0);nop;nop;nop;nop;nop;nop;gpio_put(SSD1963_RD_GPPIN,1); *pp++ = (gpio_get_all64() & (0xFFFF<>SSD1963data; } } gpio_set_dir_out_masked64(0xFFFF<= HRes) x1 = HRes - 1; if(x2 < 0) x2 = 0; if(x2 >= HRes) x2 = HRes - 1; if(y1 < 0) y1 = 0; if(y1 >= VRes) y1 = VRes - 1; if(y2 < 0) y2 = 0; if(y2 >= VRes) y2 = VRes - 1; if(Option.DISPLAY_TYPE!=SSD1963_4_16){ HRes=720; VRes=480; } else { HRes=400; VRes=256; } unsigned char *q = buff320; for(int x=x1;x<=x2;x++){ if(Option.DISPLAY_TYPE!=SSD1963_4_16)ReadBLITBufferSSD1963(x*2+80,y1*2,x*2+80,y2*2,q); else ReadBLITBufferSSD1963(x1+80,y1+16,x2+80,y2+16,q); unsigned char *pp=q; for(int y=y1;y<=y2;y++){ *p++=*pp++; *p++=*pp++; if(Option.DISPLAY_TYPE!=SSD1963_4_16)pp+=2; } } HRes=320; VRes=240; } /* @endcond */ void MIPS16 fun_getscanline(void){ if(Option.DISPLAY_TYPE < SSDPANEL && !(Option.DISPLAY_TYPE==ILI9341 || Option.DISPLAY_TYPE==ST7789B || Option.DISPLAY_TYPE == ILI9488P || Option.DISPLAY_TYPE==ILI9488)) { iret=-1; targ = T_INT; } if(Option.DISPLAY_TYPE==ILI9341 || Option.DISPLAY_TYPE==ST7789B || Option.DISPLAY_TYPE==ILI9488 || Option.DISPLAY_TYPE == ILI9488P ){ iret=GetLineILI9341(); targ = T_INT; } else { WriteComand(CMD_GET_SCANLINE); gpio_set_dir_in_masked64(0xFF<SSD_PANEL_8){ fg=((fg>>8) & 0xf800) | ((fg>>5) & 0x07e0) | ((fg>>3) & 0x001f); if(bg!=-1)bg=((bg>>8) & 0xf800) | ((bg>>5) & 0x07e0) | ((bg>>3) & 0x001f); } // adjust when part of the bitmap is outside the displayable coordinates vertCoord = y1; if(y1 < 0) y1 = 0; // the y coord is above the top of the screen XStart = x1; if(XStart < 0) XStart = 0; // the x coord is to the left of the left marginn XEnd = x1 + (width * scale) - 1; if(XEnd >= HRes) XEnd = HRes - 1; // the width of the bitmap will extend beyond the right margin if(bg == -1) { buff = GetMemory(width * height * scale * scale * 3 ); ReadBuffer(XStart, y1, XEnd, (y1 + (height * scale) - 1) , (unsigned char *)buff); n = 0; } // set y and yt to the physical location in the frame buffer (only is important when scrolling is in action) if(Option.DISPLAY_ORIENTATION == RLANDSCAPE) yt = y = (y1 + (VRes - ScrollStart)) % VRes; else yt = y = (y1 + ScrollStart) % VRes; if(Option.DISPLAY_TYPE == ILI9341_8){ SetAreaILI9341(XStart, y, XEnd, (y + (height * scale) - 1) % VRes, 1); } else if(Option.DISPLAY_TYPE == ILI9341_16 || Option.DISPLAY_TYPE == ILI9486_16) { if(Option.DISPLAY_TYPE == ILI9486_16){ Write16bitCommand(ILI9341_PIXELFORMAT); WriteData16bit(0x55); } SetAreaILI9341(XStart, y, XEnd, (y + (height * scale) - 1) % VRes, 1); } else if(Option.DISPLAY_TYPE==IPS_4_16) { if(LCDAttrib==1)WriteCmdDataIPS_4_16(0x3A00,1,0x55); SetAreaIPS_4_16(XStart, y, XEnd, (y + (height * scale) - 1) % VRes, 1); } else { SetAreaSSD1963(XStart, y, XEnd, (y + (height * scale) - 1) % VRes); // setup the area to be filled WriteComand(CMD_WR_MEMSTART); } for(i = 0; i < height; i++) { // step thru the font scan line by line for(j = 0; j < scale; j++) { // repeat lines to scale the font if(vertCoord++ < 0) continue; // we are above the top of the screen if(vertCoord > VRes) { // we have extended beyond the bottom of the screen if(buff != NULL) FreeMemory((unsigned char *)buff); return; } // if we have scrolling in action we could run over the end of the frame buffer // if so, terminate this area and start a new one at the top of the frame buffer if(y++ == VRes) { if(Option.DISPLAY_TYPE == ILI9341_8){ SetAreaILI9341(XStart, 0, XEnd, ((yt + (height * scale) - 1) % VRes) - y, 1); } else if(Option.DISPLAY_TYPE == ILI9341_16 || Option.DISPLAY_TYPE == ILI9486_16) { if(Option.DISPLAY_TYPE == ILI9486_16){ Write16bitCommand(ILI9341_PIXELFORMAT); WriteData16bit(0x55); } SetAreaILI9341(XStart, 0, XEnd, ((yt + (height * scale) - 1) % VRes) - y, 1); } else if(Option.DISPLAY_TYPE==IPS_4_16) { if(LCDAttrib==1)WriteCmdDataIPS_4_16(0x3A00,1,0x55); SetAreaIPS_4_16(XStart, 0, XEnd, ((yt + (height * scale) - 1) % VRes) - y, 1); } else { SetAreaSSD1963(XStart, 0, XEnd, ((yt + (height * scale) - 1) % VRes) - y); // setup the area to be filled WriteComand(CMD_WR_MEMSTART); } } horizCoord = x1; // optimise by dedicating the code to just writing to the 100 pin chip for(k = 0; k < width; k++) { // step through each bit in a scan line for(m = 0; m < scale; m++) { // repeat pixels to scale in the x axis if(horizCoord++ < 0) continue; // we have not reached the left margin if(horizCoord > HRes) continue; // we are beyond the right margin if((bitmap[((i * width) + k)/8] >> (7 - (((i * width) + k) % 8))) & 1) // if((bitmap[((i * width) + k)/8] >> (((height * width) - ((i * width) + k) - 1) %8)) & 1) WriteColor(fg); else { if(buff != NULL){ c.rgbbytes[0] = buff[n]; c.rgbbytes[1] = buff[n+1]; c.rgbbytes[2] = buff[n+2]; if(Option.DISPLAY_TYPE>SSD_PANEL_8)bg=((c.rgb>>8) & 0xf800) | ((c.rgb>>5) & 0x07e0) | ((c.rgb>>3) & 0x001f); else bg=c.rgb; } WriteColor(bg); } n+=3; } } } } if(buff != NULL) FreeMemory((unsigned char *)buff); } void DrawBitmap320(int x1, int y1, int width, int height, int scale, int fg, int bg, unsigned char *bitmap ){ if(Option.DISPLAY_TYPE!=SSD1963_4_16){ y1*=2; scale *=2; HRes=720; VRes=480; x1=x1*2+80; } else { x1+=80; y1+=16; HRes=400; VRes=256; } ReadBuffer=ReadBufferSSD1963; if(x1<80){ unsigned char *p=GetTempMemory((80-x1)*height*scale*2); ReadBLITBufferSSD1963(x1,y1,79,y1+(height*scale-1),p); DrawBitmapSSD1963(x1, y1, width, height, scale, fg, bg, bitmap ); DrawBLITBufferSSD1963(x1,y1,79,y1+(height*scale-1),p); } else DrawBitmapSSD1963(x1, y1, width, height, scale, fg, bg, bitmap ); ReadBuffer=ReadBuffer320; HRes=320; VRes=240; } /********************************************************************************************** Scroll the image by a number of scan lines Will only work in landscape or reverse landscape lines - the number of scan lines to scroll if positive the display will scroll up if negative it will scroll down ***********************************************************************************************/ void ScrollSSD1963(int lines) { int t; t = ScrollStart; if(lines >= 0) { DrawRectangleSSD1963(0, 0, HRes - 1, lines - 1, gui_bcolour); // erase the line to be scrolled off while(lines--) { if(Option.DISPLAY_ORIENTATION == LANDSCAPE) { if(++t >= VRes) t = 0; } else if(Option.DISPLAY_ORIENTATION == RLANDSCAPE) { if(--t < 0) t = VRes - 1; } } } else { while(lines++) { if(Option.DISPLAY_ORIENTATION == LANDSCAPE) { if(--t < 0) t = VRes - 1; } else if(Option.DISPLAY_ORIENTATION == RLANDSCAPE) { if(++t >= VRes) t = 0; } } DrawRectangleSSD1963(0, 0, HRes - 1, lines - 1, gui_bcolour); // erase the line introduced at the top } WriteComand(CMD_SET_SCROLL_START); WriteData(t >> 8); WriteData(t); ScrollStart = t; } /* @endcond */