mirror of
https://github.com/clockworkpi/PicoCalc.git
synced 2025-12-12 18:28:53 +01:00
2054 lines
86 KiB
C
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 */
|
|
|