2025-05-19 14:50:19 +08:00

2054 lines
86 KiB
C

/***********************************************************************************************************************
PicoMite MMBasic
SSD1963.c
<COPYRIGHT HOLDERS> Geoff Graham, Peter Mather
Copyright (c) 2021, <COPYRIGHT HOLDERS> 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 <copyright holder>.
5. Neither the name of the <copyright holder> 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 <COPYRIGHT HOLDERS> 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 <COPYRIGHT HOLDERS> 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<<SSD1963data),(cmd<<SSD1963data)); nop;nop;nop;nop;nop;nop;
gpio_put(SSD1963_WR_GPPIN,0);nop;nop;nop;nop;nop; nop; ;gpio_put(SSD1963_WR_GPPIN,1);nop;nop;nop;nop;nop;nop;
PinSetBit(SSD1963_DC_PIN, LATSET);
nop;
}
// Write an 8 bit data word to the SSD1963
void WriteData16bit(int data) {
gpio_put_masked64((0xFFFF<<SSD1963data),(data<<SSD1963data)); nop;nop;nop;nop;nop; nop;
gpio_put(SSD1963_WR_GPPIN,0);nop;nop;nop;nop;nop; nop; gpio_put(SSD1963_WR_GPPIN,1);nop;nop;nop;nop;nop;nop;
}
// Write sequential 16 bit command with the same 16 bit data word n times to the IPS_4_16
void WriteCmdDataIPS_4_16(int cmd,int n,int data) {
while (n>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<<SSD1963data):(0xFF<<SSD1963data));
t=ReadData() ; // dummy read
t=ReadData() ; // dummy read
gpio_set_dir_out_masked64(Option.DISPLAY_TYPE>SSD_PANEL_8 ? (0xFFFF<<SSD1963data):(0xFF<<SSD1963data));
// MMPrintString("ID1=");PIntH(t);PRet();
if ((t & 0x7F) == 0x55){ //was ((t & 0x7F) == 0x55) //((t & 0x71) == 0x51)
// NT35510 IPS Display detected. Identified in code by (LCDAttrib==1)
LCDAttrib = 1;
SaveOptions();
SendCommand16Block(NT35510_16Init);
}else{
//============ OTM8009A+HSD3.97 20140613 ===============================================//
LCDAttrib = 0;
SendCommand16Block(OTM8009A_16Init);
}
//It is a natural PORTrait LCD, but we will fudge it to make it
//work for 1=landscape,2=portrait 3=RLandscape,4=RPortrait
#define OTM8009A_MADCTL_MY 0x80
#define OTM8009A_MADCTL_MX 0x40
#define OTM8009A_MADCTL_MV 0x20
int i=0;
switch(Option.DISPLAY_ORIENTATION) {
case LANDSCAPE: i=OTM8009A_MADCTL_MX | OTM8009A_MADCTL_MV; break;
case PORTRAIT: i=0x0 ; break;
case RLANDSCAPE: i=OTM8009A_MADCTL_MY | OTM8009A_MADCTL_MV; break;
case RPORTRAIT: i=OTM8009A_MADCTL_MX | OTM8009A_MADCTL_MY; break;
}
WriteCmdDataIPS_4_16(0x3600,1,i); //set Memory Access Control
ClearScreen(Option.DefaultBC);
}
// initialise the display controller
// this is used in the initial boot sequence of the Micromite
void MIPS16 InitDisplaySSD(void) {
SSD1963rgb=0b0;
LCDAttrib=0;
SSD1963data=PinDef[Option.SSD_DATA].GPno;
if(Option.DISPLAY_TYPE<SSDPANEL || Option.DISPLAY_ORIENTATION>=VIRTUAL)return;
// the parameters for the display panel are set here (refer to the data sheet for the glass)
switch(Option.DISPLAY_TYPE) {
case SSD1963_4_16:
case SSD1963_4: DisplayHRes = 480; // this is the 4.3" glass
DisplayVRes = 272;
SSD1963HorizPulseWidth = 41;
SSD1963HorizBackPorch = 2;
SSD1963HorizFrontPorch = 2;
SSD1963VertPulseWidth = 10;
SSD1963VertBackPorch = 2;
SSD1963VertFrontPorch = 2;
//Set LSHIFT freq, i.e. the DCLK with PLL freq 120MHz set previously
//Typical DCLK is 9MHz. 9MHz = 120MHz*(LCDC_FPR+1)/2^20. LCDC_FPR = 78642 (0x13332)
SSD1963PClock1 = 0x01;
SSD1963PClock2 = 0x33;
SSD1963PClock3 = 0x32;
SSD1963Mode1 = 0x20; // 24-bit for 4.3" panel, data latch in rising edge for LSHIFT
SSD1963Mode2 = 0; // Hsync+Vsync mode
break;
case SSD1963_5ER_16:
SSD1963rgb=0b1000;
case SSD1963_5_16:
case SSD1963_5: DisplayHRes = 800; // this is the 5" glass
DisplayVRes = 480;
SSD1963HorizPulseWidth = 128;
SSD1963HorizBackPorch = 88;
SSD1963HorizFrontPorch = 40;
SSD1963VertPulseWidth = 2;
SSD1963VertBackPorch = 25;
SSD1963VertFrontPorch = 18;
//Set LSHIFT freq, i.e. the DCLK with PLL freq 120MHz set previously
//Typical DCLK is 33MHz. 30MHz = 120MHz*(LCDC_FPR+1)/2^20. LCDC_FPR = 262143 (0x3FFFF)
SSD1963PClock1 = 0x03;
SSD1963PClock2 = 0xff;
SSD1963PClock3 = 0xff;
SSD1963Mode1 = 0x24; // 24-bit for 5" panel, data latch in falling edge for LSHIFT
SSD1963Mode2 = 0; // Hsync+Vsync mode
break;
case SSD1963_5A_16:
case SSD1963_5A: DisplayHRes = 800; // this is a 5" glass alternative version
DisplayVRes = 480;
SSD1963HorizPulseWidth = 128;
SSD1963HorizBackPorch = 88;
SSD1963HorizFrontPorch = 40;
SSD1963VertPulseWidth = 2;
SSD1963VertBackPorch = 25;
SSD1963VertFrontPorch = 18;
//Set LSHIFT freq, i.e. the DCLK with PLL freq 120MHz set previously
//Typical DCLK is 33MHz. 30MHz = 120MHz*(LCDC_FPR+1)/2^20. LCDC_FPR = 262143 (0x3FFFF)
SSD1963PClock1 = 0x04;
SSD1963PClock2 = 0x93;
SSD1963PClock3 = 0xe0;
SSD1963Mode1 = 0x24; // 24-bit for 5" panel, data latch in falling edge for LSHIFT
SSD1963Mode2 = 0; // Hsync+Vsync mode
break;
case SSD1963_7ER_16:
SSD1963rgb=0b1000;
case SSD1963_7_16:
case SSD1963_7: DisplayHRes = 800; // this is the 7" glass
DisplayVRes = 480;
SSD1963HorizPulseWidth = 1;
SSD1963HorizBackPorch = 210;
SSD1963HorizFrontPorch = 45;
SSD1963VertPulseWidth = 1;
SSD1963VertBackPorch = 34;
SSD1963VertFrontPorch = 10;
//Set LSHIFT freq, i.e. the DCLK with PLL freq 120MHz set previously
//Typical DCLK is 33.3MHz(datasheet), experiment shows 30MHz gives a stable result
//30MHz = 120MHz*(LCDC_FPR+1)/2^20. LCDC_FPR = 262143 (0x3FFFF)
//Time per line = (DISP_HOR_RESOLUTION+DISP_HOR_PULSE_WIDTH+DISP_HOR_BACK_PORCH+DISP_HOR_FRONT_PORCH)/30 us = 1056/30 = 35.2us
SSD1963PClock1 = 0x03;
SSD1963PClock2 = 0xff;
SSD1963PClock3 = 0xff;
SSD1963Mode1 = 0x10; // 18-bit for 7" panel
SSD1963Mode2 = 0x80; // TTL mode
break;
case SSD1963_7A_16:
case SSD1963_7A: DisplayHRes = 800; // this is a 7" glass alternative version (high brightness)
DisplayVRes = 480;
SSD1963HorizPulseWidth = 3;
SSD1963HorizBackPorch = 88;
SSD1963HorizFrontPorch = 37;
SSD1963VertPulseWidth = 3;
SSD1963VertBackPorch = 32;
SSD1963VertFrontPorch = 10;
SSD1963PClock1 = 0x03;
SSD1963PClock2 = 0xff;
SSD1963PClock3 = 0xff;
SSD1963Mode1 = 0x10; // 18-bit for 7" panel
SSD1963Mode2 = 0x80; // TTL mode
break;
case SSD1963_8_16:
case SSD1963_8: DisplayHRes = 800; // this is the 8" glass (not documented because the 40 pin connector is non standard)
DisplayVRes = 480;
SSD1963HorizPulseWidth = 1;
SSD1963HorizBackPorch = 210;
SSD1963HorizFrontPorch = 45;
SSD1963VertPulseWidth = 1;
SSD1963VertBackPorch = 34;
SSD1963VertFrontPorch = 10;
//Set LSHIFT freq, i.e. the DCLK with PLL freq 120MHz set previously
//Typical DCLK is 33.3MHz(datasheet), experiment shows 30MHz gives a stable result
//30MHz = 120MHz*(LCDC_FPR+1)/2^20. LCDC_FPR = 262143 (0x3FFFF)
//Time per line = (DISP_HOR_RESOLUTION+DISP_HOR_PULSE_WIDTH+DISP_HOR_BACK_PORCH+DISP_HOR_FRONT_PORCH)/30 us = 1056/30 = 35.2us
SSD1963PClock1 = 0x03;
SSD1963PClock2 = 0xff;
SSD1963PClock3 = 0xff;
SSD1963Mode1 = 0x20;
SSD1963Mode2 = 0x00;
break;
case ILI9341_8:
case ILI9341_16:
DisplayHRes=320;
DisplayVRes=240;
break;
case ILI9486_16:
DisplayHRes=480;
DisplayVRes=320;
break;
case IPS_4_16: /***G.A***/
DisplayHRes=800;
DisplayVRes=480;
}
if(DISPLAY_LANDSCAPE) {
VRes=DisplayVRes;
HRes=DisplayHRes;
} else {
VRes=DisplayHRes;
HRes=DisplayVRes;
}
if(Option.DISPLAY_TYPE > 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<<SSD1963data) : (0xFF<<SSD1963data)),(cmd<<SSD1963data));
gpio_put(SSD1963_DC_GPPIN,0);
gpio_put(SSD1963_WR_GPPIN,0);nop;gpio_put(SSD1963_WR_GPPIN,1);
gpio_put(SSD1963_DC_GPPIN,1);
}
// Write an 8 bit data word to the SSD1963
void WriteData(int data) {
gpio_put_masked64((Option.DISPLAY_TYPE>SSD_PANEL_8 ? (0xFFFF<<SSD1963data) : (0xFF<<SSD1963data)),(data<<SSD1963data));
gpio_put(SSD1963_WR_GPPIN,0);nop;gpio_put(SSD1963_WR_GPPIN,1);
}
// For the 100 pin chip write RGB colour over an 8 bit bus
void WriteColor(unsigned int c) {
if(Option.DISPLAY_TYPE>SSD_PANEL_8){
gpio_put_masked64((0xFFFF<<SSD1963data),(c<<SSD1963data));
gpio_put(SSD1963_WR_GPPIN,0);nop;gpio_put(SSD1963_WR_GPPIN,1);
} else {
gpio_put_masked64((0xFF<<SSD1963data),(((c >> 16) & 0xFF)<<SSD1963data));
nop;gpio_put(SSD1963_WR_GPPIN,0);nop;nop;gpio_put(SSD1963_WR_GPPIN,1);
gpio_put_masked64((0xFF<<SSD1963data),(((c >> 8) & 0xFF)<<SSD1963data));
nop;gpio_put(SSD1963_WR_GPPIN,0);nop;nop;gpio_put(SSD1963_WR_GPPIN,1);
nop;gpio_put_masked64((0xFF<<SSD1963data),((c & 0xFF)<<SSD1963data));
gpio_put(SSD1963_WR_GPPIN,0);nop;nop;gpio_put(SSD1963_WR_GPPIN,1);
}
}
// The next two functions are used in the initial setup where the SSD1963 cannot respond to fast signals
// Slowly write a command byte to the SSD1963
static void WriteComandSlow(int cmd) {
gpio_put_masked64((Option.DISPLAY_TYPE>SSD_PANEL_8 ? (0xFFFF<<SSD1963data) : (0xFF<<SSD1963data)),(cmd<<SSD1963data));
gpio_put(SSD1963_DC_GPPIN,0);
gpio_put(SSD1963_WR_GPPIN,0);uSec(5);gpio_put(SSD1963_WR_GPPIN,1);
gpio_put(SSD1963_DC_GPPIN,1);
}
// Slowly write an 8 bit data word to the SSD1963
void WriteDataSlow(int data) {
gpio_put_masked64((Option.DISPLAY_TYPE>SSD_PANEL_8 ? (0xFFFF<<SSD1963data) : (0xFF<<SSD1963data)),(data<<SSD1963data));
gpio_put(SSD1963_WR_GPPIN,0);uSec(5);gpio_put(SSD1963_WR_GPPIN,1);
}
// Read a byte from the SSD1963
inline __attribute((always_inline)) unsigned int ReadData(void) {
gpio_put(SSD1963_RD_GPPIN,0);nop;nop;nop;nop;nop;nop;gpio_put(SSD1963_RD_GPPIN,1);
return (gpio_get_all64() & (Option.DISPLAY_TYPE>SSD_PANEL_8 ? (0xFFFF<<SSD1963data) : (0xFF<<SSD1963data)))>>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) : (0xFF<<SSD1963data)))>>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) : (0xFF<<SSD1963data)))>>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<<SSD1963data,c<<SSD1963data);
//nop;gpio_put(SSD1963_WR_GPPIN,0);
nop;gpio_put(SSD1963_WR_GPPIN,1);
}
} else {
for(i = (x2 - x1 + 1) * (y2 - y1 + 1); i > 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<HRes && y>=0 && y<VRes){
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){
if(LCDAttrib==0){
WriteColor(((c.rgb>>8) & 0xf800) | ((c.rgb>>5) & 0x07e0) | ((c.rgb>>3) & 0x001f));
} else {
if(toggle==0){
gpio_put_masked64(0xFFFF<<SSD1963data,(((c.rgb>>8) & 0xf800) | ((c.rgb>>8) & 0x00fc))<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,0);nop;gpio_put(SSD1963_WR_GPPIN,1);
bl=(c.rgb & 0x00f8); //save blue
toggle=1;
} else {
gpio_put_masked64(0xFFFF<<SSD1963data,((bl<<8) | ((c.rgb>>16) & 0x00f8))<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,0);nop;gpio_put(SSD1963_WR_GPPIN,1);
gpio_put_masked64(0xFFFF<<SSD1963data,(((c.rgb) & 0xfc00) | ((c.rgb) & 0x00f8))<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,0);nop;gpio_put(SSD1963_WR_GPPIN,1);
toggle=0;
}
}
} else WriteColor(c.rgb);
} else p+=3;
}
}
if(LCDAttrib==2)LCDAttrib=0;
if((LCDAttrib==1) && (toggle==1)){
// extra packet needed
gpio_put_masked64(0xFFFF<<SSD1963data,((bl<<8) | ((c.rgb>>8) & 0x00f8))<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,0);nop;gpio_put(SSD1963_WR_GPPIN,1);
}
}
}
void DrawBuffer320(int x1, int y1, int x2, int y2, unsigned char* p){
int t;
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;
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<<SSD1963data,(*pp++)<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,1);
}
SetAreaSSD1963(x1, 0, x2, y2 - VRes );
WriteComand(CMD_WR_MEMSTART);
for(i = (x2 - x1 + 1) * (y2 - VRes + 1); i > 0; i--) {
gpio_put(SSD1963_WR_GPPIN,0);
gpio_put_masked64(0xFFFF<<SSD1963data,(*pp++)<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,1);
}
} else {
// the whole box is within the frame buffer - much easier
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<HRes && y>=0 && y<VRes){
gpio_put(SSD1963_WR_GPPIN,0);
gpio_put_masked64(0xFFFF<<SSD1963data,(*pp++)<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,1);
} else pp++;
}
}
}
}
void DrawBLITBuffer320(int x1, int y1, int x2, int y2, unsigned char* p){
int t;
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;
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<<SSD1963data) : (0xFF<<SSD1963data)));
i=(x2 - x1 + 1) * ((VRes - 1) - y1 + 1);
uSec(10);
for( ; i > 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<<SSD1963data) : (0xFF<<SSD1963data)));
uSec(10);
SetAreaSSD1963(x1, 0, x2, y2 - VRes );
WriteComand(CMD_RD_MEMSTART);
gpio_set_dir_in_masked64((Option.DISPLAY_TYPE>SSD_PANEL_8 ? (0xFFFF<<SSD1963data) : (0xFF<<SSD1963data)));
uSec(10);
for(i = (x2 - x1 + 1) * (y2 - VRes + 1); i > 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<<SSD1963data) : (0xFF<<SSD1963data)));
uSec(10);
} else {
if(Option.DISPLAY_TYPE == ILI9341_8){
Write16bitCommand(ILI9341_PIXELFORMAT) ;
WriteData16bit(0x66);
SetAreaILI9341(x1, y1 , x2, y2, 0);
} else if(Option.DISPLAY_TYPE == ILI9341_16 || Option.DISPLAY_TYPE == ILI9486_16) {
if(Option.DISPLAY_TYPE == ILI9486_16){
Write16bitCommand(ILI9341_PIXELFORMAT);
WriteData16bit(0x66);
}
SetAreaILI9341(x1, y1 , x2, y2, 0);
} else if(Option.DISPLAY_TYPE==IPS_4_16) {
if(LCDAttrib==1)WriteCmdDataIPS_4_16(0x3A00,1,0x66);
SetAreaIPS_4_16(x1, y1 , x2, y2, 0);
} else {
SetAreaSSD1963(x1, y1 , x2, y2); // setup the area to be filled
WriteComand(CMD_RD_MEMSTART);
}
gpio_set_dir_in_masked64((Option.DISPLAY_TYPE>SSD_PANEL_8 ?(0xFFFF<<SSD1963data) : (0xFF<<SSD1963data)));
uSec(2);
if(Option.DISPLAY_TYPE==ILI9341_8 || Option.DISPLAY_TYPE==ILI9341_16 || Option.DISPLAY_TYPE == IPS_4_16 )ReadDataSlow();
for(i = (x2 - x1 + 1) * (y2 - y1 + 1); i > 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<<SSD1963data) : (0xFF<<SSD1963data)));
if(Option.DISPLAY_TYPE==ILI9341_16){
Write16bitCommand(ILI9341_PIXELFORMAT) ;
WriteData16bit(0x55);
}
uSec(10);
}
}
void ReadBuffer320(int x1, int y1, int x2, int y2, unsigned char* p) {
int t;
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;
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<<SSD1963data);
i=(x2 - x1 + 1) * ((VRes - 1) - y1 + 1);
uSec(2);
for( ; i > 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))>>SSD1963data);
}
gpio_set_dir_out_masked64(0xFFFF<<SSD1963data);
uSec(2);
SetAreaSSD1963(x1, 0, x2, y2 - VRes );
WriteComand(CMD_RD_MEMSTART);
gpio_set_dir_in_masked64(0xFFFF);
uSec(2);
for(i = (x2 - x1 + 1) * (y2 - VRes + 1); i > 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))>>SSD1963data);
}
gpio_set_dir_out_masked64(0xFFFF<SSD1963data);
uSec(2);
} else {
if(Option.DISPLAY_TYPE==IPS_4_16) {
if(LCDAttrib==1)WriteCmdDataIPS_4_16(0x3A00,1,0x66);
SetAreaIPS_4_16(x1, y1 , x2, y2, 0);
ReadDataSlow();
} else {
SetAreaSSD1963(x1, y1 , x2, y2); // setup the area to be filled
WriteComand(CMD_RD_MEMSTART);
}
gpio_set_dir_in_masked64(0xFFFF<<SSD1963data);
uSec(2);
for(i = (x2 - x1 + 1) * (y2 - y1 + 1); i > 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))>>SSD1963data;
}
}
gpio_set_dir_out_masked64(0xFFFF<<SSD1963data);
}
}
void ReadBLITBuffer320(int x1, int y1, int x2, int y2, unsigned char* p) {
int t;
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;
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<<SSD1963data);
iret = (ReadData() << 8) | ReadData(); // get the scan line
gpio_set_dir_out_masked64(0xFF<<SSD1963data);
targ = T_INT;
}
}
/*
* @cond
* The following section will be excluded from the documentation.
*/
/***********************************************************************************************
Print the bitmap of a char on the video output
Modifications by Peter Mather (matherp on the Back Shed forum) to support transparent text
x1, y1 - the top left of the char
width, height - size of the char's bitmap
scale - how much to scale the bitmap
fg, bg - foreground and background colour
bitmap - pointer to the butmap
***********************************************************************************************/
void DrawBitmapSSD1963(int x1, int y1, int width, int height, int scale, int fg, int bg, unsigned char *bitmap ){
int i, j, k, m, y, yt, n ;
int vertCoord, horizCoord, XStart, XEnd;
char *buff=NULL;
union car {
char rgbbytes[4];
unsigned int rgb;
} c;
if(Option.DISPLAY_TYPE>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 */