kopia lustrzana https://github.com/Jean-MarcHarvengt/MCUME
1775 wiersze
54 KiB
C++
1775 wiersze
54 KiB
C++
/*
|
|
TFT/VGA driver
|
|
DMA TFT driver based on C64 ILI9341 dma driver from Frank Bösing, 2017
|
|
*/
|
|
|
|
#include "T4_DSP.h"
|
|
|
|
#include <SPI.h>
|
|
#include <DMAChannel.h>
|
|
|
|
#include "font8x8.h"
|
|
|
|
// TFT constants and variables
|
|
#define TFT_LINEARINT 1
|
|
#define LINEARINT_HACK 1
|
|
#define DMA_LINES_PER_BLOCK 64
|
|
#define DMA_NUM_SETTINGS 4
|
|
|
|
#define TFT_SWRESET 0x01
|
|
#define TFT_SLPOUT 0x11
|
|
#define TFT_INVON 0x21
|
|
#define TFT_DISPOFF 0x28
|
|
#define TFT_DISPON 0x29
|
|
#define TFT_CASET 0x2A
|
|
#define TFT_PASET 0x2B
|
|
#define TFT_RAMWR 0x2C
|
|
#define TFT_MADCTL 0x36
|
|
#define TFT_PIXFMT 0x3A
|
|
#define TFT_MADCTL_MY 0x80
|
|
#define TFT_MADCTL_MX 0x40
|
|
#define TFT_MADCTL_MV 0x20
|
|
#define TFT_MADCTL_ML 0x10
|
|
#define TFT_MADCTL_RGB 0x00
|
|
#define TFT_MADCTL_BGR 0x08
|
|
#define TFT_MADCTL_MH 0x04
|
|
|
|
#define RGBVAL16(r,g,b) ( (((r>>3)&0x1f)<<11) | (((g>>2)&0x3f)<<5) | (((b>>3)&0x1f)<<0) )
|
|
//#define RGBVAL32(r,g,b) ( (r<<16) | (g<<8) | b )
|
|
//#define RGBVAL8(r,g,b) ( (((r>>5)&0x07)<<5) | (((g>>5)&0x07)<<2) | (((b>>6)&0x3)<<0) )
|
|
//#define R16(rgb) ((rgb>>8)&0xf8)
|
|
//#define G16(rgb) ((rgb>>3)&0xfc)
|
|
//#define B16(rgb) ((rgb<<3)&0xf8)
|
|
|
|
// LPSPI4 = SPI0 in Teensy 4.0
|
|
// LPSPI3 = SPI1 in Teensy 4.0
|
|
// LPSPI1 = SPI2 in Teensy 4.0 (used for SD on T4.0 but not T4.1)
|
|
|
|
#ifdef TFTSPI1
|
|
#define SPI SPI1
|
|
#define LPSPIP_TDR LPSPI3_TDR
|
|
#define LPSPIP_CR LPSPI3_CR
|
|
#define LPSPIP_CFGR1 LPSPI3_CFGR1
|
|
#define LPSPIP_TCR LPSPI3_TCR
|
|
#define LPSPIP_DER LPSPI3_DER
|
|
#define DMAMUX_SOURCE_LPSPIP_TX DMAMUX_SOURCE_LPSPI3_TX
|
|
#else
|
|
#define LPSPIP_TDR LPSPI4_TDR
|
|
#define LPSPIP_CR LPSPI4_CR
|
|
#define LPSPIP_CFGR1 LPSPI4_CFGR1
|
|
#define LPSPIP_TCR LPSPI4_TCR
|
|
#define LPSPIP_DER LPSPI4_DER
|
|
#define DMAMUX_SOURCE_LPSPIP_TX DMAMUX_SOURCE_LPSPI4_TX
|
|
#endif
|
|
|
|
#define SPICLOCK 60000000
|
|
#define SPI_MODE SPI_MODE0
|
|
|
|
// VGA constants and macros
|
|
typedef uint8_t vga_pixel;
|
|
#define VGA_RGB(r,g,b) ( (((r>>5)&0x07)<<5) | (((g>>5)&0x07)<<2) | (((b>>6)&0x3)<<0) )
|
|
|
|
static DMASetting dmasettings[DMA_NUM_SETTINGS];
|
|
static DMAChannel dmatx;
|
|
static uint16_t * blocks[DMA_NUM_SETTINGS]; // for DMA transfer, you need to divide in blocks < 128K
|
|
static volatile uint8_t rstop = 0;
|
|
static volatile bool cancelled = false;
|
|
static volatile uint8_t curTransfer = 0;
|
|
static uint8_t nbTransfer = 0;
|
|
static uint16_t * tft_buffer;
|
|
static int tft_width;
|
|
static int tft_height;
|
|
static int tft_stride;
|
|
|
|
#define DELAY_MASK 0x80
|
|
PROGMEM static const uint8_t init_commands[] = {
|
|
1+DELAY_MASK, TFT_SWRESET, 150,
|
|
1+DELAY_MASK, TFT_SLPOUT, 255,
|
|
2+DELAY_MASK, TFT_PIXFMT, 0x55, 10,
|
|
2, TFT_MADCTL, TFT_MADCTL_MV | TFT_MADCTL_BGR,
|
|
1, TFT_INVON,
|
|
1, TFT_DISPON,
|
|
0
|
|
};
|
|
|
|
// VGA constants and variables
|
|
// Objective:
|
|
// generates VGA signal fully in hardware with as little as possible CPU help
|
|
|
|
// Principle:
|
|
// QTimer3 (timer3) used to generate H-PUSE and line interrupt (and V-PULSE)
|
|
// 2 FlexIO shift registers (1 and 2) and 2 DMA channels used to generate
|
|
// RGB out, combined to create 8bits(/12bits) output.
|
|
|
|
// Note:
|
|
// - supported resolutions: 320x240,320x480,640x240 and 640x480 pixels
|
|
// - experimental resolution: 352x240,352x480
|
|
// - experimental resolution: 512x240,512x480 (not stable)
|
|
// - video memory is allocated using malloc in T4 heap
|
|
// - as the 2 DMA transfers are not started exactly at same time, there is a bit of color smearing
|
|
// but tried to be compensated by pixel shifting
|
|
// - Default is 8bits RRRGGGBB (332)
|
|
// But 12bits GBB0RRRRGGGBB (444) feasible BUT NOT TESTED !!!!
|
|
// - Only ok at 600MHz else some disturbances visible
|
|
|
|
|
|
|
|
#define TOP_BORDER 40
|
|
#define PIN_HBLANK 15
|
|
|
|
#define PIN_R_B2 33
|
|
#define PIN_R_B1 4
|
|
#define PIN_R_B0 3
|
|
#define PIN_G_B2 2
|
|
#define PIN_G_B1 13
|
|
#define PIN_G_B0 11
|
|
#define PIN_B_B1 12
|
|
#define PIN_B_B0 10
|
|
|
|
|
|
#define DMA_HACK 0x80
|
|
|
|
#define R16(rgb) ((rgb>>8)&0xf8)
|
|
#define G16(rgb) ((rgb>>3)&0xfc)
|
|
#define B16(rgb) ((rgb<<3)&0xf8)
|
|
|
|
// VGA 640x480@60Hz
|
|
// Screen refresh rate 60 Hz
|
|
// Vertical refresh 31.46875 kHz
|
|
// Pixel freq. 25.175 MHz
|
|
//
|
|
// Visible area 640 25.422045680238 us
|
|
// Front porch 16 0.63555114200596 us
|
|
// Sync pulse 96 3.8133068520357 us
|
|
// Back porch 48 1.9066534260179 us
|
|
// Whole line 800 31.777557100298 us
|
|
|
|
#define frame_freq 60.0 // Hz
|
|
#define line_freq 31.46875 // KHz
|
|
#define pix_freq (line_freq*800) // KHz (25.175 MHz)
|
|
|
|
// pix_period = 39.7ns
|
|
// H-PULSE is 3.8133us = 3813.3ns => 96 pixels (see above for the rest)
|
|
#define frontporch_pix 20 //16
|
|
#define backporch_pix 44 //48
|
|
|
|
// Flexio Clock
|
|
// PLL3 SW CLOCK (3) => 480 MHz
|
|
// PLL5 VIDEO CLOCK (2) => See formula for clock (we take 604200 KHz as /24 it gives 25175)
|
|
#define FLEXIO_CLK_SEL_PLL3 3
|
|
#define FLEXIO_CLK_SEL_PLL5 2
|
|
|
|
/* Set video PLL */
|
|
// There are /1, /2, /4, /8, /16 post dividers for the Video PLL.
|
|
// The output frequency can be set by programming the fields in the CCM_ANALOG_PLL_VIDEO,
|
|
// and CCM_ANALOG_MISC2 register sets according to the following equation.
|
|
// PLL output frequency = Fref * (DIV_SELECT + NUM/DENOM)
|
|
|
|
// nfact:
|
|
// This field controls the PLL loop divider.
|
|
// Valid range for DIV_SELECT divider value: 27~54.
|
|
|
|
#define POST_DIV_SELECT 2
|
|
|
|
// Full buffer including back/front porch
|
|
static vga_pixel * gfxbuffer __attribute__((aligned(32))) = NULL;
|
|
static uint32_t dstbuffer __attribute__((aligned(32)));
|
|
|
|
// Visible vuffer
|
|
static vga_pixel * vga_buffer;
|
|
static int vga_width;
|
|
static int vga_height;
|
|
static int vga_stride;
|
|
|
|
static int maxpixperline;
|
|
static int left_border;
|
|
static int right_border;
|
|
static int line_double;
|
|
static int pix_shift;
|
|
static int ref_div_select;
|
|
static int ref_freq_num;
|
|
static int ref_freq_denom;
|
|
static int ref_pix_shift;
|
|
static int combine_shiftreg;
|
|
|
|
#ifdef DEBUG
|
|
static uint32_t ISRTicks_prev = 0;
|
|
volatile uint32_t ISRTicks = 0;
|
|
#endif
|
|
|
|
uint8_t T4_DSP::_rst;
|
|
uint8_t T4_DSP::_cs;
|
|
uint8_t T4_DSP::_dc;
|
|
uint8_t T4_DSP::_mosi;
|
|
uint8_t T4_DSP::_sclk;
|
|
uint8_t T4_DSP::_vsync_pin = -1;
|
|
DMAChannel T4_DSP::flexio1DMA;
|
|
DMAChannel T4_DSP::flexio2DMA;
|
|
|
|
static volatile uint32_t VSYNC = 0;
|
|
static volatile uint32_t currentLine=0;
|
|
#define NOP asm volatile("nop\n\t");
|
|
static gfx_mode_t gfxmode = MODE_UNDEFINED;
|
|
|
|
|
|
FASTRUN void T4_DSP::TFT_isr(void) {
|
|
dmatx.clearInterrupt();
|
|
curTransfer++;
|
|
if (curTransfer >= nbTransfer) {
|
|
curTransfer = 0;
|
|
if (cancelled) {
|
|
dmatx.disable();
|
|
rstop = 1;
|
|
}
|
|
}
|
|
arm_dcache_flush(blocks[curTransfer], DMA_LINES_PER_BLOCK*TFT_WIDTH*2);
|
|
}
|
|
|
|
FASTRUN void T4_DSP::QT3_isr(void) {
|
|
TMR3_SCTRL3 &= ~(TMR_SCTRL_TCF);
|
|
TMR3_CSCTRL3 &= ~(TMR_CSCTRL_TCF1|TMR_CSCTRL_TCF2);
|
|
|
|
cli();
|
|
|
|
// V-PULSE
|
|
if (currentLine > 0) {
|
|
digitalWrite(_vsync_pin, 1);
|
|
VSYNC = 0;
|
|
} else {
|
|
digitalWrite(_vsync_pin, 0);
|
|
VSYNC = 1;
|
|
}
|
|
|
|
currentLine++;
|
|
currentLine = currentLine % 525;
|
|
|
|
|
|
uint32_t y = (currentLine - TOP_BORDER) >> line_double;
|
|
// Visible area
|
|
|
|
if (y >= 0 && y < vga_height) {
|
|
// Disable DMAs
|
|
//DMA_CERQ = flexio2DMA.channel;
|
|
//DMA_CERQ = flexio1DMA.channel;
|
|
|
|
// Setup source adress
|
|
// Aligned 32 bits copy
|
|
unsigned long * p=(uint32_t *)&gfxbuffer[vga_stride*y];
|
|
flexio2DMA.TCD->SADDR = p;
|
|
if (pix_shift & DMA_HACK)
|
|
{
|
|
// Unaligned copy
|
|
uint8_t * p2=(uint8_t *)&gfxbuffer[vga_stride*y+(pix_shift&0xf)];
|
|
flexio1DMA.TCD->SADDR = p2;
|
|
}
|
|
else {
|
|
p=(uint32_t *)&gfxbuffer[vga_stride*y+(pix_shift&0xc)]; // multiple of 4
|
|
flexio1DMA.TCD->SADDR = p;
|
|
}
|
|
|
|
// Enable DMAs
|
|
//flexio2DMA.enable();
|
|
//flexio1DMA.enable();
|
|
DMA_SERQ = flexio2DMA.channel;
|
|
DMA_SERQ = flexio1DMA.channel;
|
|
arm_dcache_flush_delete((void*)((uint32_t *)&gfxbuffer[vga_stride*y]), vga_stride);
|
|
}
|
|
sei();
|
|
|
|
#ifdef DEBUG
|
|
ISRTicks++;
|
|
#endif
|
|
asm volatile("dsb");
|
|
}
|
|
|
|
static void setDmaStruct() {
|
|
uint32_t remaining = TFT_HEIGHT*TFT_WIDTH*2;
|
|
uint16_t * fb = (uint16_t*)malloc(remaining);
|
|
tft_buffer = fb;
|
|
tft_width = TFT_WIDTH;
|
|
tft_height = TFT_HEIGHT;
|
|
tft_stride = TFT_WIDTH;
|
|
uint16_t col=RGBVAL16(0x00,0x00,0x00);
|
|
int i=0;
|
|
while (remaining > 0) {
|
|
int32_t len = (remaining >= (DMA_LINES_PER_BLOCK*TFT_WIDTH*2)?DMA_LINES_PER_BLOCK*TFT_WIDTH*2:remaining);
|
|
blocks[i] = fb;
|
|
for (int j=0;j<len/2;j++) fb[j]=col;
|
|
dmasettings[i].sourceBuffer(fb, len);
|
|
dmasettings[i].destination((uint8_t &)LPSPIP_TDR);
|
|
dmasettings[i].TCD->ATTR_DST = 1;
|
|
dmasettings[i].replaceSettingsOnCompletion(dmasettings[i+1]);
|
|
dmasettings[i].interruptAtCompletion();
|
|
fb += len/2;
|
|
remaining -= len;
|
|
i++;
|
|
}
|
|
dmasettings[i-1].replaceSettingsOnCompletion(dmasettings[0]);
|
|
nbTransfer = i;
|
|
}
|
|
|
|
static void set_videoClock(int nfact, int32_t nmult, uint32_t ndiv, bool force) // sets PLL5
|
|
{
|
|
//if (!force && (CCM_ANALOG_PLL_VIDEO & CCM_ANALOG_PLL_VIDEO_ENABLE)) return;
|
|
CCM_ANALOG_PLL_VIDEO = CCM_ANALOG_PLL_VIDEO_BYPASS | CCM_ANALOG_PLL_VIDEO_ENABLE
|
|
| CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT(1) // 2: 1/1; 1: 1/2; 0: 1/4
|
|
| CCM_ANALOG_PLL_VIDEO_DIV_SELECT(nfact);
|
|
CCM_ANALOG_PLL_VIDEO_NUM = nmult /*& CCM_ANALOG_PLL_VIDEO_NUM_MASK*/;
|
|
CCM_ANALOG_PLL_VIDEO_DENOM = ndiv /*& CCM_ANALOG_PLL_VIDEO_DENOM_MASK*/;
|
|
CCM_ANALOG_PLL_VIDEO &= ~CCM_ANALOG_PLL_VIDEO_POWERDOWN;//Switch on PLL
|
|
while (!(CCM_ANALOG_PLL_VIDEO & CCM_ANALOG_PLL_VIDEO_LOCK)) {}; //Wait for pll-lock
|
|
const int div_post_pll = 1; // other values: 2,4
|
|
if(div_post_pll>3) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_MSB;
|
|
CCM_ANALOG_PLL_VIDEO &= ~CCM_ANALOG_PLL_VIDEO_BYPASS;//Disable Bypass
|
|
}
|
|
|
|
T4_DSP::T4_DSP()
|
|
{
|
|
_cs = TFT_CS;
|
|
_dc = TFT_DC;
|
|
_rst = TFT_RST;
|
|
_mosi = TFT_MOSI;
|
|
_sclk = TFT_SCLK;
|
|
pinMode(_dc, OUTPUT);
|
|
pinMode(_cs, OUTPUT);
|
|
digitalWrite(_cs, 1);
|
|
digitalWrite(_dc, 1);
|
|
_vsync_pin = 8;
|
|
}
|
|
|
|
|
|
void T4_DSP::setArea(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2) {
|
|
int dx=0;
|
|
int dy=0;
|
|
digitalWrite(_dc, 0);
|
|
SPI.transfer(TFT_CASET);
|
|
digitalWrite(_dc, 1);
|
|
SPI.transfer16(x1+dx);
|
|
digitalWrite(_dc, 1);
|
|
SPI.transfer16(x2+dx);
|
|
digitalWrite(_dc, 0);
|
|
SPI.transfer(TFT_PASET);
|
|
digitalWrite(_dc, 1);
|
|
SPI.transfer16(y1+dy);
|
|
digitalWrite(_dc, 1);
|
|
SPI.transfer16(y2+dy);
|
|
digitalWrite(_dc, 0);
|
|
SPI.transfer(TFT_RAMWR);
|
|
digitalWrite(_dc, 1);
|
|
return;
|
|
}
|
|
|
|
void T4_DSP::tft_setup(bool isST) {
|
|
SPI.setMOSI(_mosi);
|
|
SPI.setSCK(_sclk);
|
|
SPI.begin();
|
|
// RESET if reset pin defined
|
|
if (_rst != 0xff) {
|
|
pinMode(_rst, OUTPUT);
|
|
digitalWrite(_rst, HIGH);
|
|
delay(100);
|
|
digitalWrite(_rst, LOW);
|
|
delay(100);
|
|
digitalWrite(_rst, HIGH);
|
|
delay(200);
|
|
}
|
|
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE));
|
|
const uint8_t *addr = init_commands;
|
|
uint8_t count;
|
|
digitalWrite(_cs, 0);
|
|
while (count = *addr++) {
|
|
uint8_t command = *addr++;
|
|
if ( (command == TFT_INVON) && (!isST) ) {
|
|
// Skip TFT_INVON for ILI
|
|
}
|
|
else {
|
|
digitalWrite(_dc, 0); // command
|
|
SPI.transfer(command);
|
|
uint16_t ms = count & DELAY_MASK;
|
|
count &= ~DELAY_MASK;
|
|
while (--count > 0) { // data
|
|
uint8_t data = *addr++;
|
|
if ( (command == TFT_MADCTL) && (isST) ) {
|
|
data = TFT_MADCTL_MX | TFT_MADCTL_MV |TFT_MADCTL_RGB;
|
|
}
|
|
digitalWrite(_dc, 1);
|
|
SPI.transfer(data);
|
|
}
|
|
if (ms) {
|
|
ms = *addr++; // Read post-command delay time (ms)
|
|
if(ms == 255) ms = 500; // If 255, delay for 500 ms
|
|
digitalWrite(_cs, 1);
|
|
SPI.endTransaction();
|
|
delay(2);
|
|
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE));
|
|
digitalWrite(_cs, 0);
|
|
}
|
|
}
|
|
}
|
|
digitalWrite(_cs, 1);
|
|
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE));
|
|
setArea(0, 0, TFT_REALWIDTH-1, TFT_REALHEIGHT-1);
|
|
SPI.endTransaction();
|
|
/*
|
|
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE));
|
|
digitalWrite(_dc, 0);
|
|
digitalWrite(_cs, 0);
|
|
SPI.transfer(TFT_MADCTL);
|
|
digitalWrite(_dc, 1);
|
|
SPI.transfer(TFT_MADCTL_MV | TFT_MADCTL_BGR);
|
|
// SPI.transfer(TFT_MADCTL_MX | TFT_MADCTL_MV |TFT_MADCTL_RGB);
|
|
digitalWrite(_cs, 1);
|
|
SPI.endTransaction();
|
|
*/
|
|
cancelled = false;
|
|
}
|
|
|
|
// display VGA image
|
|
gfx_error_t T4_DSP::begin(gfx_mode_t mode)
|
|
{
|
|
uint32_t flexio_clock_div;
|
|
combine_shiftreg = 0;
|
|
// int div_select = 49;
|
|
// int num = 135;
|
|
// int denom = 100;
|
|
int div_select = 20;
|
|
int num = 9800;
|
|
int denom = 10000;
|
|
int flexio_clk_sel = FLEXIO_CLK_SEL_PLL5;
|
|
int flexio_freq = ( 24000*div_select + (num*24000)/denom )/POST_DIV_SELECT;
|
|
set_videoClock(div_select,num,denom,true);
|
|
|
|
#ifdef DEBUG
|
|
Serial.println(mode);
|
|
Serial.println("mode");
|
|
#endif
|
|
|
|
switch(mode)
|
|
{
|
|
case MODE_TFTILI_320x240:
|
|
#ifdef DEBUG
|
|
Serial.println("TFTILI_320x240");
|
|
#endif
|
|
tft_setup(false);
|
|
gfxmode = mode;
|
|
break;
|
|
case MODE_TFTST_320x240:
|
|
#ifdef DEBUG
|
|
Serial.println("TFTST_320x240");
|
|
#endif
|
|
tft_setup(true);
|
|
gfxmode = mode;
|
|
break;
|
|
|
|
case MODE_VGA_320x240:
|
|
#ifdef DEBUG
|
|
Serial.println("VGA_320x240");
|
|
#endif
|
|
left_border = backporch_pix/2;
|
|
right_border = frontporch_pix/2;
|
|
vga_width = 320;
|
|
vga_height = 240 ;
|
|
vga_stride = left_border+vga_width+right_border;
|
|
flexio_clock_div = flexio_freq/(pix_freq/2);
|
|
line_double = 1;
|
|
pix_shift = 2+DMA_HACK;
|
|
break;
|
|
|
|
case MODE_VGA_320x480:
|
|
#ifdef DEBUG
|
|
Serial.println("VGA_320x480");
|
|
#endif
|
|
left_border = backporch_pix/2;
|
|
right_border = frontporch_pix/2;
|
|
vga_width = 320;
|
|
vga_height = 480 ;
|
|
vga_stride = left_border+vga_width+right_border;
|
|
flexio_clock_div = flexio_freq/(pix_freq/2);
|
|
line_double = 0;
|
|
pix_shift = 2+DMA_HACK;
|
|
break;
|
|
|
|
case MODE_VGA_640x240:
|
|
#ifdef DEBUG
|
|
Serial.println("VGA_640x240");
|
|
#endif
|
|
left_border = backporch_pix;
|
|
right_border = frontporch_pix;
|
|
vga_width = 640;
|
|
vga_height = 240 ;
|
|
vga_stride = left_border+vga_width+right_border;
|
|
flexio_clock_div = flexio_freq/pix_freq;
|
|
line_double = 1;
|
|
pix_shift = 4;
|
|
combine_shiftreg = 1;
|
|
break;
|
|
|
|
case MODE_VGA_640x480:
|
|
#ifdef DEBUG
|
|
Serial.println("VGA_640x480");
|
|
#endif
|
|
left_border = backporch_pix;
|
|
right_border = frontporch_pix;
|
|
vga_width = 640;
|
|
vga_height = 480 ;
|
|
vga_stride = left_border+vga_width+right_border;
|
|
flexio_clock_div = (flexio_freq/pix_freq);
|
|
line_double = 0;
|
|
pix_shift = 4;
|
|
combine_shiftreg = 1;
|
|
break;
|
|
|
|
case MODE_VGA_512x240:
|
|
#ifdef DEBUG
|
|
Serial.println("VGA_512x240");
|
|
#endif
|
|
left_border = backporch_pix/1.3;
|
|
right_border = frontporch_pix/1.3;
|
|
vga_width = 512;
|
|
vga_height = 240 ;
|
|
vga_stride = left_border+vga_width+right_border;
|
|
flexio_clock_div = flexio_freq/(pix_freq/1.3)+2;
|
|
line_double = 1;
|
|
pix_shift = 0;
|
|
break;
|
|
|
|
case MODE_VGA_512x480:
|
|
#ifdef DEBUG
|
|
Serial.println("VGA_512x480");
|
|
#endif
|
|
left_border = backporch_pix/1.3;
|
|
right_border = frontporch_pix/1.3;
|
|
vga_width = 512;
|
|
vga_height = 480 ;
|
|
vga_stride = left_border+vga_width+right_border;
|
|
flexio_clock_div = flexio_freq/(pix_freq/1.3)+2;
|
|
line_double = 0;
|
|
pix_shift = 0;
|
|
break;
|
|
|
|
case MODE_VGA_352x240:
|
|
#ifdef DEBUG
|
|
Serial.println("VGA_352x240");
|
|
#endif
|
|
left_border = backporch_pix/1.75;
|
|
right_border = frontporch_pix/1.75;
|
|
vga_width = 352;
|
|
vga_height = 240 ;
|
|
vga_stride = left_border+vga_width+right_border;
|
|
flexio_clock_div = flexio_freq/(pix_freq/1.75)+2;
|
|
line_double = 1;
|
|
pix_shift = 2+DMA_HACK;
|
|
break;
|
|
|
|
case MODE_VGA_352x480:
|
|
#ifdef DEBUG
|
|
Serial.println("VGA_352x480");
|
|
#endif
|
|
left_border = backporch_pix/1.75;
|
|
right_border = frontporch_pix/1.75;
|
|
vga_width = 352;
|
|
vga_height = 480 ;
|
|
vga_stride = left_border+vga_width+right_border;
|
|
flexio_clock_div = flexio_freq/(pix_freq/1.75)+2;
|
|
line_double = 0;
|
|
pix_shift = 2+DMA_HACK;
|
|
break;
|
|
}
|
|
|
|
if (mode >= MODE_VGA_320x240)
|
|
{
|
|
if ( (gfxmode != MODE_UNDEFINED) && (gfxmode < MODE_VGA_320x240) ) {
|
|
fillScreenNoDma(RGBVAL16(0x0,0x00,0x00));
|
|
digitalWrite(_cs, 0);
|
|
digitalWrite(_dc, 0);
|
|
SPI.transfer(TFT_DISPOFF);
|
|
digitalWrite(_cs, 1);
|
|
delay(20);
|
|
digitalWrite(_cs, 0);
|
|
digitalWrite(_cs, 1);
|
|
}
|
|
gfxmode = mode;
|
|
|
|
maxpixperline = vga_stride;
|
|
// Save param for tweek adjustment
|
|
ref_div_select = div_select;
|
|
ref_freq_num = num;
|
|
ref_freq_denom = denom;
|
|
ref_pix_shift = pix_shift;
|
|
|
|
#ifdef DEBUG
|
|
Serial.println("frequency");
|
|
Serial.println(flexio_freq);
|
|
Serial.println("div");
|
|
Serial.println(flexio_freq/pix_freq);
|
|
#endif
|
|
|
|
pinMode(_vsync_pin, OUTPUT);
|
|
pinMode(PIN_HBLANK, OUTPUT);
|
|
|
|
/* Basic pin setup FlexIO1 */
|
|
pinMode(PIN_G_B2, OUTPUT); // FlexIO1:4 = 0x10
|
|
pinMode(PIN_R_B0, OUTPUT); // FlexIO1:5 = 0x20
|
|
pinMode(PIN_R_B1, OUTPUT); // FlexIO1:6 = 0x40
|
|
pinMode(PIN_R_B2, OUTPUT); // FlexIO1:7 = 0x80
|
|
/* Basic pin setup FlexIO2 */
|
|
pinMode(PIN_B_B0, OUTPUT); // FlexIO2:0 = 0x00001
|
|
pinMode(PIN_B_B1, OUTPUT); // FlexIO2:1 = 0x00002
|
|
pinMode(PIN_G_B0, OUTPUT); // FlexIO2:2 = 0x00004
|
|
pinMode(PIN_G_B1, OUTPUT); // FlexIO2:3 = 0x00008
|
|
|
|
/* High speed and drive strength configuration */
|
|
*(portControlRegister(PIN_G_B2)) = 0xFF;
|
|
*(portControlRegister(PIN_R_B0)) = 0xFF;
|
|
*(portControlRegister(PIN_R_B1)) = 0xFF;
|
|
*(portControlRegister(PIN_R_B2)) = 0xFF;
|
|
*(portControlRegister(PIN_B_B0)) = 0xFF;
|
|
*(portControlRegister(PIN_B_B1)) = 0xFF;
|
|
*(portControlRegister(PIN_G_B0)) = 0xFF;
|
|
*(portControlRegister(PIN_G_B1)) = 0xFF;
|
|
|
|
/* Set clock for FlexIO1 and FlexIO2 */
|
|
CCM_CCGR5 &= ~CCM_CCGR5_FLEXIO1(CCM_CCGR_ON);
|
|
CCM_CDCDR = (CCM_CDCDR & ~(CCM_CDCDR_FLEXIO1_CLK_SEL(3) | CCM_CDCDR_FLEXIO1_CLK_PRED(7) | CCM_CDCDR_FLEXIO1_CLK_PODF(7)))
|
|
| CCM_CDCDR_FLEXIO1_CLK_SEL(flexio_clk_sel) | CCM_CDCDR_FLEXIO1_CLK_PRED(0) | CCM_CDCDR_FLEXIO1_CLK_PODF(0);
|
|
CCM_CCGR3 &= ~CCM_CCGR3_FLEXIO2(CCM_CCGR_ON);
|
|
CCM_CSCMR2 = (CCM_CSCMR2 & ~(CCM_CSCMR2_FLEXIO2_CLK_SEL(3))) | CCM_CSCMR2_FLEXIO2_CLK_SEL(flexio_clk_sel);
|
|
CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_FLEXIO2_CLK_PRED(7)|CCM_CS1CDR_FLEXIO2_CLK_PODF(7)) )
|
|
| CCM_CS1CDR_FLEXIO2_CLK_PRED(0) | CCM_CS1CDR_FLEXIO2_CLK_PODF(0);
|
|
|
|
|
|
/* Set up pin mux FlexIO1 */
|
|
*(portConfigRegister(PIN_G_B2)) = 0x14;
|
|
*(portConfigRegister(PIN_R_B0)) = 0x14;
|
|
*(portConfigRegister(PIN_R_B1)) = 0x14;
|
|
*(portConfigRegister(PIN_R_B2)) = 0x14;
|
|
/* Set up pin mux FlexIO2 */
|
|
*(portConfigRegister(PIN_B_B0)) = 0x14;
|
|
*(portConfigRegister(PIN_B_B1)) = 0x14;
|
|
*(portConfigRegister(PIN_G_B0)) = 0x14;
|
|
*(portConfigRegister(PIN_G_B1)) = 0x14;
|
|
|
|
/* Enable the clock */
|
|
CCM_CCGR5 |= CCM_CCGR5_FLEXIO1(CCM_CCGR_ON);
|
|
CCM_CCGR3 |= CCM_CCGR3_FLEXIO2(CCM_CCGR_ON);
|
|
/* Enable the FlexIO with fast access */
|
|
FLEXIO1_CTRL = FLEXIO_CTRL_FLEXEN | FLEXIO_CTRL_FASTACC;
|
|
FLEXIO2_CTRL = FLEXIO_CTRL_FLEXEN | FLEXIO_CTRL_FASTACC;
|
|
|
|
uint32_t timerSelect, timerPolarity, pinConfig, pinSelect, pinPolarity, shifterMode, parallelWidth, inputSource, stopBit, startBit;
|
|
uint32_t triggerSelect, triggerPolarity, triggerSource, timerMode, timerOutput, timerDecrement, timerReset, timerDisable, timerEnable;
|
|
|
|
/* Shifter 0 registers for FlexIO2 */
|
|
parallelWidth = FLEXIO_SHIFTCFG_PWIDTH(4); // 8-bit parallel shift width
|
|
pinSelect = FLEXIO_SHIFTCTL_PINSEL(0); // Select pins FXIO_D0 through FXIO_D3
|
|
inputSource = FLEXIO_SHIFTCFG_INSRC*(1); // Input source from next shifter
|
|
stopBit = FLEXIO_SHIFTCFG_SSTOP(0); // Stop bit disabled
|
|
startBit = FLEXIO_SHIFTCFG_SSTART(0); // Start bit disabled, transmitter loads data on enable
|
|
timerSelect = FLEXIO_SHIFTCTL_TIMSEL(0); // Use timer 0
|
|
timerPolarity = FLEXIO_SHIFTCTL_TIMPOL*(1); // Shift on negedge of clock
|
|
pinConfig = FLEXIO_SHIFTCTL_PINCFG(3); // Shifter pin output
|
|
pinPolarity = FLEXIO_SHIFTCTL_PINPOL*(0); // Shifter pin active high polarity
|
|
shifterMode = FLEXIO_SHIFTCTL_SMOD(2); // Shifter transmit mode
|
|
/* Shifter 0 registers for FlexIO1 */
|
|
FLEXIO2_SHIFTCFG0 = parallelWidth | inputSource | stopBit | startBit;
|
|
FLEXIO2_SHIFTCTL0 = timerSelect | timerPolarity | pinConfig | pinSelect | pinPolarity | shifterMode;
|
|
|
|
/* Shifter 0 registers for FlexIO1 */
|
|
parallelWidth = FLEXIO_SHIFTCFG_PWIDTH(4); // 8-bit parallel shift width
|
|
pinSelect = FLEXIO_SHIFTCTL_PINSEL(4); // Select pins FXIO_D4 through FXIO_D7
|
|
FLEXIO1_SHIFTCFG0 = parallelWidth | inputSource | stopBit | startBit;
|
|
FLEXIO1_SHIFTCTL0 = timerSelect | timerPolarity | pinConfig | pinSelect | pinPolarity | shifterMode;
|
|
|
|
if (combine_shiftreg) {
|
|
pinConfig = FLEXIO_SHIFTCTL_PINCFG(0); // Shifter pin output disabled
|
|
FLEXIO2_SHIFTCFG1 = parallelWidth | inputSource | stopBit | startBit;
|
|
FLEXIO2_SHIFTCTL1 = timerSelect | timerPolarity | pinConfig | shifterMode;
|
|
FLEXIO1_SHIFTCFG1 = parallelWidth | inputSource | stopBit | startBit;
|
|
FLEXIO1_SHIFTCTL1 = timerSelect | timerPolarity | pinConfig | shifterMode;
|
|
}
|
|
/* Timer 0 registers for FlexIO2 */
|
|
timerOutput = FLEXIO_TIMCFG_TIMOUT(1); // Timer output is logic zero when enabled and is not affected by the Timer reset
|
|
timerDecrement = FLEXIO_TIMCFG_TIMDEC(0); // Timer decrements on FlexIO clock, shift clock equals timer output
|
|
timerReset = FLEXIO_TIMCFG_TIMRST(0); // Timer never reset
|
|
timerDisable = FLEXIO_TIMCFG_TIMDIS(2); // Timer disabled on Timer compare
|
|
timerEnable = FLEXIO_TIMCFG_TIMENA(2); // Timer enabled on Trigger assert
|
|
stopBit = FLEXIO_TIMCFG_TSTOP(0); // Stop bit disabled
|
|
startBit = FLEXIO_TIMCFG_TSTART*(0); // Start bit disabled
|
|
if (combine_shiftreg) {
|
|
triggerSelect = FLEXIO_TIMCTL_TRGSEL(1+4*(1)); // Trigger select Shifter 1 status flag
|
|
}
|
|
else {
|
|
triggerSelect = FLEXIO_TIMCTL_TRGSEL(1+4*(0)); // Trigger select Shifter 0 status flag
|
|
}
|
|
triggerPolarity = FLEXIO_TIMCTL_TRGPOL*(1); // Trigger active low
|
|
triggerSource = FLEXIO_TIMCTL_TRGSRC*(1); // Internal trigger selected
|
|
pinConfig = FLEXIO_TIMCTL_PINCFG(0); // Timer pin output disabled
|
|
//pinSelect = FLEXIO_TIMCTL_PINSEL(0); // Select pin FXIO_D0
|
|
//pinPolarity = FLEXIO_TIMCTL_PINPOL*(0); // Timer pin polarity active high
|
|
timerMode = FLEXIO_TIMCTL_TIMOD(1); // Dual 8-bit counters baud mode
|
|
// flexio_clock_div : Output clock frequency is N times slower than FlexIO clock (41.7 ns period) (23.980MHz?)
|
|
|
|
int shifts_per_transfer;
|
|
if (combine_shiftreg) {
|
|
shifts_per_transfer = 8; // Shift out 8 times with every transfer = 64-bit word = contents of Shifter 0+1
|
|
}
|
|
else {
|
|
shifts_per_transfer = 4; // Shift out 4 times with every transfer = 32-bit word = contents of Shifter 0
|
|
}
|
|
FLEXIO2_TIMCFG0 = timerOutput | timerDecrement | timerReset | timerDisable | timerEnable | stopBit | startBit;
|
|
FLEXIO2_TIMCTL0 = triggerSelect | triggerPolarity | triggerSource | pinConfig /*| pinSelect | pinPolarity*/ | timerMode;
|
|
FLEXIO2_TIMCMP0 = ((shifts_per_transfer*2-1)<<8) | ((flexio_clock_div/2-1)<<0);
|
|
/* Timer 0 registers for FlexIO1 */
|
|
FLEXIO1_TIMCFG0 = timerOutput | timerDecrement | timerReset | timerDisable | timerEnable | stopBit | startBit;
|
|
FLEXIO1_TIMCTL0 = triggerSelect | triggerPolarity | triggerSource | pinConfig /*| pinSelect | pinPolarity*/ | timerMode;
|
|
FLEXIO1_TIMCMP0 = ((shifts_per_transfer*2-1)<<8) | ((flexio_clock_div/2-1)<<0);
|
|
#ifdef DEBUG
|
|
Serial.println("FlexIO setup complete");
|
|
#endif
|
|
|
|
/* Enable DMA trigger on Shifter0, DMA request is generated when data is transferred from buffer0 to shifter0 */
|
|
if (combine_shiftreg) {
|
|
FLEXIO2_SHIFTSDEN |= (1<<1);
|
|
FLEXIO1_SHIFTSDEN |= (1<<1);
|
|
}
|
|
else {
|
|
FLEXIO2_SHIFTSDEN |= (1<<0);
|
|
FLEXIO1_SHIFTSDEN |= (1<<0);
|
|
}
|
|
/* Disable DMA channel so it doesn't start transferring yet */
|
|
flexio1DMA.disable();
|
|
flexio2DMA.disable();
|
|
/* Set up DMA channel to use Shifter 0 trigger */
|
|
flexio1DMA.triggerAtHardwareEvent(DMAMUX_SOURCE_FLEXIO1_REQUEST0);
|
|
flexio2DMA.triggerAtHardwareEvent(DMAMUX_SOURCE_FLEXIO2_REQUEST0);
|
|
|
|
|
|
if (combine_shiftreg) {
|
|
flexio2DMA.TCD->NBYTES = 8;
|
|
flexio2DMA.TCD->SOFF = 4;
|
|
flexio2DMA.TCD->SLAST = -maxpixperline;
|
|
flexio2DMA.TCD->BITER = maxpixperline / 8;
|
|
flexio2DMA.TCD->CITER = maxpixperline / 8;
|
|
flexio2DMA.TCD->DADDR = &FLEXIO2_SHIFTBUF0;
|
|
flexio2DMA.TCD->DOFF = 0;
|
|
flexio2DMA.TCD->DLASTSGA = 0;
|
|
flexio2DMA.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(3); // 32bits => 64bits
|
|
flexio2DMA.TCD->CSR |= DMA_TCD_CSR_DREQ;
|
|
|
|
flexio1DMA.TCD->NBYTES = 8;
|
|
flexio1DMA.TCD->SOFF = 4;
|
|
flexio1DMA.TCD->SLAST = -maxpixperline;
|
|
flexio1DMA.TCD->BITER = maxpixperline / 8;
|
|
flexio1DMA.TCD->CITER = maxpixperline / 8;
|
|
flexio1DMA.TCD->DADDR = &FLEXIO1_SHIFTBUFNBS0;
|
|
flexio1DMA.TCD->DOFF = 0;
|
|
flexio1DMA.TCD->DLASTSGA = 0;
|
|
flexio1DMA.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(3); // 32bits => 64bits
|
|
flexio1DMA.TCD->CSR |= DMA_TCD_CSR_DREQ;
|
|
}
|
|
else {
|
|
// Setup DMA2 Flexio2 copy
|
|
flexio2DMA.TCD->NBYTES = 4;
|
|
flexio2DMA.TCD->SOFF = 4;
|
|
flexio2DMA.TCD->SLAST = -maxpixperline;
|
|
flexio2DMA.TCD->BITER = maxpixperline / 4;
|
|
flexio2DMA.TCD->CITER = maxpixperline / 4;
|
|
flexio2DMA.TCD->DADDR = &FLEXIO2_SHIFTBUF0;
|
|
flexio2DMA.TCD->DOFF = 0;
|
|
flexio2DMA.TCD->DLASTSGA = 0;
|
|
flexio2DMA.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2); // 32bits
|
|
flexio2DMA.TCD->CSR |= DMA_TCD_CSR_DREQ;
|
|
|
|
// Setup DMA1 Flexio1 copy
|
|
// Use pixel shift to avoid color smearing?
|
|
if (pix_shift & DMA_HACK)
|
|
{
|
|
if (pix_shift & 0x3 == 0) {
|
|
// Aligned 32 bits copy (32bits to 32bits)
|
|
flexio1DMA.TCD->NBYTES = 4;
|
|
flexio1DMA.TCD->SOFF = 4;
|
|
flexio1DMA.TCD->SLAST = -maxpixperline;
|
|
flexio1DMA.TCD->BITER = maxpixperline / 4;
|
|
flexio1DMA.TCD->CITER = maxpixperline / 4;
|
|
flexio1DMA.TCD->DADDR = &FLEXIO1_SHIFTBUFNBS0;
|
|
flexio1DMA.TCD->DOFF = 0;
|
|
flexio1DMA.TCD->DLASTSGA = 0;
|
|
flexio1DMA.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2); // 32bits to 32bits
|
|
flexio1DMA.TCD->CSR |= DMA_TCD_CSR_DREQ;
|
|
}
|
|
else {
|
|
// Unaligned (source) 32 bits copy (8bits to 32bits)
|
|
flexio1DMA.TCD->NBYTES = 4;
|
|
flexio1DMA.TCD->SOFF = 1;
|
|
flexio1DMA.TCD->SLAST = -maxpixperline;
|
|
flexio1DMA.TCD->BITER = maxpixperline / 4;
|
|
flexio1DMA.TCD->CITER = maxpixperline / 4;
|
|
flexio1DMA.TCD->DADDR = &FLEXIO1_SHIFTBUFNBS0;
|
|
flexio1DMA.TCD->DOFF = 0;
|
|
flexio1DMA.TCD->DLASTSGA = 0;
|
|
flexio1DMA.TCD->ATTR = DMA_TCD_ATTR_SSIZE(0) | DMA_TCD_ATTR_DSIZE(2); // 8bits to 32bits
|
|
flexio1DMA.TCD->CSR |= DMA_TCD_CSR_DREQ; // disable on completion
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Aligned 32 bits copy
|
|
flexio1DMA.TCD->NBYTES = 4;
|
|
flexio1DMA.TCD->SOFF = 4;
|
|
flexio1DMA.TCD->SLAST = -maxpixperline;
|
|
flexio1DMA.TCD->BITER = maxpixperline / 4;
|
|
flexio1DMA.TCD->CITER = maxpixperline / 4;
|
|
flexio1DMA.TCD->DADDR = &FLEXIO1_SHIFTBUFNBS0;
|
|
flexio1DMA.TCD->DOFF = 0;
|
|
flexio1DMA.TCD->DLASTSGA = 0;
|
|
flexio1DMA.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2); // 32bits
|
|
flexio1DMA.TCD->CSR |= DMA_TCD_CSR_DREQ;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
Serial.println("DMA setup complete");
|
|
#endif
|
|
|
|
// enable clocks for QTIMER3: generates the 15KHz for hsync
|
|
// Pulse:
|
|
// low : 3.8133 us => 569x6.7ns
|
|
// total: 31.777 us => 4743x6.7ns (high = 4174x6.7ns)
|
|
// (OLD TEST)
|
|
// (4us low, 28us high => 32us)
|
|
// (597x6.7ns for 4us)
|
|
// (4179x6.7ns for 28us)
|
|
CCM_CCGR6 |= 0xC0000000; //enable clocks to CG15 of CGR6 for QT3
|
|
//configure QTIMER3 Timer3 for test of alternating Compare1 and Compare2
|
|
|
|
#define MARGIN_N 1005 // 1206 at 720MHz //1005 at 600MHz
|
|
#define MARGIN_D 1000
|
|
|
|
TMR3_CTRL3 = 0b0000000000100000; //stop all functions of timer
|
|
// Invert output pin as we want the interupt on rising edge
|
|
TMR3_SCTRL3 = 0b0000000000000011; //0(TimerCompareFlag),0(TimerCompareIntEnable),00(TimerOverflow)0000(NoCapture),0000(Capture Disabled),00, 1(INV output),1(OFLAG to Ext Pin)
|
|
TMR3_CNTR3 = 0;
|
|
TMR3_LOAD3 = 0;
|
|
|
|
/* Inverted timings */
|
|
unsigned long long cpu_freq = F_CPU;
|
|
unsigned long long rate = (1005ULL * cpu_freq) / 600000000ULL;
|
|
int rate2 = rate;
|
|
|
|
int substract = 1;
|
|
if (cpu_freq > 950000000ULL && cpu_freq < 990000000ULL) {
|
|
substract=5;
|
|
rate2=1652;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
Serial.print("MARGIN_N is: ");
|
|
Serial.println(rate2, DEC);
|
|
Serial.print("SUBSTRACT is: ");
|
|
Serial.println(substract, DEC);
|
|
#endif
|
|
TMR3_COMP13 = ((4174*rate2)/MARGIN_D)-substract;
|
|
TMR3_CMPLD13 = ((4174*rate2)/MARGIN_D)-substract;
|
|
TMR3_COMP23 = ((569*rate2)/MARGIN_D)-substract;
|
|
TMR3_CMPLD23 = ((569*rate2)/MARGIN_D)-substract;
|
|
/*
|
|
TMR3_COMP13 = ((4174*MARGIN_N)/MARGIN_D)-1;
|
|
TMR3_CMPLD13 = ((4174*MARGIN_N)/MARGIN_D)-1;
|
|
TMR3_COMP23 = ((569*MARGIN_N)/MARGIN_D)-1;
|
|
TMR3_CMPLD23 = ((569*MARGIN_N)/MARGIN_D)-1;
|
|
*/
|
|
TMR3_CSCTRL3 = 0b0000000010000101; //Compare1 only enabled - Compare Load1 control and Compare Load2 control both on
|
|
TMR3_CTRL3 = 0b0011000000100100; // 001(Count rising edges Primary Source),1000(IP Bus Clock),00 (Secondary Source),
|
|
// 0(Count Once),1(Count up to Compare),0(Count Up),0(Co Channel Init),100(Toggle OFLAG on alternating Compare1/Compare2)
|
|
//configure Teensy pin Compare output
|
|
IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_03 = 1; // QT3 Timer3 is now on pin 15
|
|
attachInterruptVector(IRQ_QTIMER3, QT3_isr); //declare which routine performs the ISR function
|
|
NVIC_ENABLE_IRQ(IRQ_QTIMER3);
|
|
#ifdef DEBUG
|
|
Serial.println("QTIMER3 setup complete");
|
|
Serial.print("V-PIN is ");
|
|
Serial.println(_vsync_pin);
|
|
#endif
|
|
|
|
/* initialize gfx buffer */
|
|
if (gfxbuffer == NULL) gfxbuffer = (vga_pixel*)malloc(vga_stride*vga_height*sizeof(vga_pixel)+4); // 4bytes for pixel shift
|
|
if (gfxbuffer == NULL) return(GFX_ERROR);
|
|
#ifdef DEBUG
|
|
Serial.println("Memory allocated");
|
|
#endif
|
|
|
|
memset((void*)&gfxbuffer[0],0, vga_stride*vga_height*sizeof(vga_pixel)+4);
|
|
vga_buffer = (vga_pixel*)&gfxbuffer[left_border];
|
|
#ifdef DEBUG
|
|
Serial.println(vga_stride);
|
|
Serial.println(vga_height);
|
|
Serial.println("Screen cleared");
|
|
#endif
|
|
}
|
|
|
|
return(GFX_OK);
|
|
}
|
|
|
|
gfx_mode_t T4_DSP::getMode(void)
|
|
{
|
|
return gfxmode;
|
|
}
|
|
|
|
void T4_DSP::startRefresh(void) {
|
|
if (gfxmode < MODE_VGA_320x240) {
|
|
curTransfer = 0;
|
|
rstop = 0;
|
|
//dmatx.begin(true);
|
|
dmatx.attachInterrupt(TFT_isr);
|
|
setDmaStruct();
|
|
setArea((TFT_REALWIDTH-TFT_WIDTH)/2, (TFT_REALHEIGHT-TFT_HEIGHT)/2, (TFT_REALWIDTH-TFT_WIDTH)/2 + TFT_WIDTH-1, (TFT_REALHEIGHT-TFT_HEIGHT)/2+TFT_HEIGHT-1);
|
|
fillScreen(RGBVAL16(0x00,0x00,0x00));
|
|
|
|
digitalWrite(_cs, HIGH);
|
|
SPI.begin();
|
|
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
|
|
|
|
LPSPIP_CR &= ~LPSPI_CR_MEN;//disable LPSPI:
|
|
LPSPIP_CFGR1 |= LPSPI_CFGR1_NOSTALL; //prevent stall from RX
|
|
LPSPIP_TCR = 15; // Framesize 16 Bits
|
|
//LPSPIP_FCR = 0; // Fifo Watermark
|
|
LPSPIP_DER = LPSPI_DER_TDDE; //TX DMA Request Enable
|
|
LPSPIP_CR |= LPSPI_CR_MEN; //enable LPSPI:
|
|
dmatx.triggerAtHardwareEvent( DMAMUX_SOURCE_LPSPIP_TX );
|
|
|
|
dmatx = dmasettings[0];
|
|
digitalWrite(_cs, 0);
|
|
setArea((TFT_REALWIDTH-TFT_WIDTH)/2, (TFT_REALHEIGHT-TFT_HEIGHT)/2, (TFT_REALWIDTH-TFT_WIDTH)/2+TFT_WIDTH-1, (TFT_REALHEIGHT-TFT_HEIGHT)/2+TFT_HEIGHT-1);
|
|
digitalWrite(_dc, 0);
|
|
SPI.transfer(TFT_RAMWR);
|
|
digitalWrite(_dc, 1);
|
|
dmatx.enable();
|
|
}
|
|
}
|
|
|
|
|
|
void T4_DSP::stopRefresh(void) {
|
|
if (gfxmode < MODE_VGA_320x240) {
|
|
rstop = 1;
|
|
unsigned long m = millis();
|
|
cancelled = true;
|
|
while (!rstop) {
|
|
if ((millis() - m) > 100) break;
|
|
delay(10);
|
|
asm volatile("wfi");
|
|
};
|
|
rstop = 0;
|
|
delay(50);
|
|
cancelled = false;
|
|
dmatx.detachInterrupt();
|
|
fillScreen(RGBVAL16(0x00,0x00,0x00));
|
|
SPI.end();
|
|
#ifdef ST7789
|
|
// begin(gfxmode);
|
|
#endif
|
|
setArea(0, 0, TFT_REALWIDTH-1, TFT_REALHEIGHT-1);
|
|
}
|
|
}
|
|
|
|
int T4_DSP::get_frame_buffer_size(int *width, int *height) {
|
|
if (gfxmode < MODE_VGA_320x240) {
|
|
if (width != nullptr) *width = tft_width;
|
|
if (height != nullptr) *height = tft_height;
|
|
return tft_stride;
|
|
}
|
|
else {
|
|
if (width != nullptr) *width = vga_width;
|
|
if (height != nullptr) *height = vga_height;
|
|
return vga_stride;
|
|
}
|
|
}
|
|
|
|
void T4_DSP::waitSync()
|
|
{
|
|
if (gfxmode >= MODE_VGA_320x240) {
|
|
while (VSYNC == 0) {};
|
|
}
|
|
}
|
|
|
|
void T4_DSP::waitLine(int line)
|
|
{
|
|
if (gfxmode >= MODE_VGA_320x240) {
|
|
while (currentLine != line) {};
|
|
}
|
|
}
|
|
|
|
/***********************************************************************************************
|
|
No DMA functions
|
|
***********************************************************************************************/
|
|
void T4_DSP::fillScreenNoDma(uint16_t color) {
|
|
if (gfxmode < MODE_VGA_320x240) {
|
|
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE));
|
|
digitalWrite(_cs, 0);
|
|
setArea(0, 0, TFT_REALWIDTH-1, TFT_REALHEIGHT-1);
|
|
int i,j;
|
|
for (j=0; j<TFT_REALHEIGHT; j++)
|
|
{
|
|
for (i=0; i<TFT_REALWIDTH; i++) {
|
|
//digitalWrite(_dc, 1);
|
|
SPI.transfer16(color);
|
|
}
|
|
}
|
|
digitalWrite(_cs, 1);
|
|
setArea(0, 0, (TFT_REALWIDTH-1), (TFT_REALHEIGHT-1));
|
|
SPI.endTransaction();
|
|
}
|
|
else {
|
|
fillScreen(color);
|
|
}
|
|
|
|
}
|
|
|
|
void T4_DSP::drawRectNoDma(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
|
|
if (gfxmode < MODE_VGA_320x240) {
|
|
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE));
|
|
digitalWrite(_cs, 0);
|
|
setArea(x,y,x+w-1,y+h-1);
|
|
int i;
|
|
for (i=0; i<(w*h); i++)
|
|
{
|
|
SPI.transfer16(color);
|
|
}
|
|
digitalWrite(_cs, 1);
|
|
setArea(0, 0, (TFT_REALWIDTH-1), (TFT_REALHEIGHT-1));
|
|
SPI.endTransaction();
|
|
}
|
|
else {
|
|
drawRect(x, y, w, h, color);
|
|
}
|
|
}
|
|
|
|
|
|
void T4_DSP::drawSpriteNoDma(int16_t x, int16_t y, const uint16_t *bitmap) {
|
|
drawSpriteNoDma(x,y,bitmap, 0,0,0,0);
|
|
}
|
|
|
|
void T4_DSP::drawSpriteNoDma(int16_t x, int16_t y, const uint16_t *bitmap, uint16_t arx, uint16_t ary, uint16_t arw, uint16_t arh)
|
|
{
|
|
if (gfxmode < MODE_VGA_320x240) {
|
|
int bmp_offx = 0;
|
|
int bmp_offy = 0;
|
|
uint16_t *bmp_ptr;
|
|
int w =*bitmap++;
|
|
int h = *bitmap++;
|
|
if ( (arw == 0) || (arh == 0) ) {
|
|
// no crop window
|
|
arx = x;
|
|
ary = y;
|
|
arw = w;
|
|
arh = h;
|
|
}
|
|
else {
|
|
if ( (x>(arx+arw)) || ((x+w)<arx) || (y>(ary+arh)) || ((y+h)<ary) ) {
|
|
return;
|
|
}
|
|
// crop area
|
|
if ( (x > arx) && (x<(arx+arw)) ) {
|
|
arw = arw - (x-arx);
|
|
arx = arx + (x-arx);
|
|
} else {
|
|
bmp_offx = arx;
|
|
}
|
|
if ( ((x+w) > arx) && ((x+w)<(arx+arw)) ) {
|
|
arw -= (arx+arw-x-w);
|
|
}
|
|
if ( (y > ary) && (y<(ary+arh)) ) {
|
|
arh = arh - (y-ary);
|
|
ary = ary + (y-ary);
|
|
} else {
|
|
bmp_offy = ary;
|
|
}
|
|
if ( ((y+h) > ary) && ((y+h)<(ary+arh)) ) {
|
|
arh -= (ary+arh-y-h);
|
|
}
|
|
}
|
|
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE));
|
|
digitalWrite(_cs, 0);
|
|
setArea(arx, ary, arx+arw-1, ary+arh-1);
|
|
bitmap = bitmap + bmp_offy*w + bmp_offx;
|
|
for (int row=0;row<arh; row++)
|
|
{
|
|
bmp_ptr = (uint16_t*)bitmap;
|
|
for (int col=0;col<arw; col++)
|
|
{
|
|
uint16_t color = *bmp_ptr++;
|
|
SPI.transfer16(color);
|
|
}
|
|
bitmap += w;
|
|
}
|
|
setArea(0, 0, TFT_REALWIDTH-1, TFT_REALHEIGHT-1);
|
|
digitalWrite(_cs, 1);
|
|
SPI.endTransaction();
|
|
}
|
|
else {
|
|
drawSprite(x, y, bitmap, arx, ary, arw, arh);
|
|
}
|
|
}
|
|
|
|
void T4_DSP::drawTextNoDma(int16_t x, int16_t y, const char * text, uint16_t fgcolor, uint16_t bgcolor, bool doublesize) {
|
|
if (gfxmode < MODE_VGA_320x240) {
|
|
uint16_t c;
|
|
while ((c = *text++)) {
|
|
const unsigned char * charpt=&font8x8[c][0];
|
|
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE));
|
|
digitalWrite(_cs, 0);
|
|
setArea(x,y,x+7,y+(doublesize?15:7));
|
|
for (int i=0;i<8;i++)
|
|
{
|
|
unsigned char bits;
|
|
if (doublesize) {
|
|
bits = *charpt;
|
|
//digitalWrite(_dc, 1);
|
|
if (bits&0x01) SPI.transfer16(fgcolor);
|
|
else SPI.transfer16(bgcolor);
|
|
bits = bits >> 1;
|
|
if (bits&0x01) SPI.transfer16(fgcolor);
|
|
else SPI.transfer16(bgcolor);
|
|
bits = bits >> 1;
|
|
if (bits&0x01) SPI.transfer16(fgcolor);
|
|
else SPI.transfer16(bgcolor);
|
|
bits = bits >> 1;
|
|
if (bits&0x01) SPI.transfer16(fgcolor);
|
|
else SPI.transfer16(bgcolor);
|
|
bits = bits >> 1;
|
|
if (bits&0x01) SPI.transfer16(fgcolor);
|
|
else SPI.transfer16(bgcolor);
|
|
bits = bits >> 1;
|
|
if (bits&0x01) SPI.transfer16(fgcolor);
|
|
else SPI.transfer16(bgcolor);
|
|
bits = bits >> 1;
|
|
if (bits&0x01) SPI.transfer16(fgcolor);
|
|
else SPI.transfer16(bgcolor);
|
|
bits = bits >> 1;
|
|
if (bits&0x01) SPI.transfer16(fgcolor);
|
|
else SPI.transfer16(bgcolor);
|
|
}
|
|
bits = *charpt++;
|
|
//digitalWrite(_dc, 1);
|
|
if (bits&0x01) SPI.transfer16(fgcolor);
|
|
else SPI.transfer16(bgcolor);
|
|
bits = bits >> 1;
|
|
if (bits&0x01) SPI.transfer16(fgcolor);
|
|
else SPI.transfer16(bgcolor);
|
|
bits = bits >> 1;
|
|
if (bits&0x01) SPI.transfer16(fgcolor);
|
|
else SPI.transfer16(bgcolor);
|
|
bits = bits >> 1;
|
|
if (bits&0x01) SPI.transfer16(fgcolor);
|
|
else SPI.transfer16(bgcolor);
|
|
bits = bits >> 1;
|
|
if (bits&0x01) SPI.transfer16(fgcolor);
|
|
else SPI.transfer16(bgcolor);
|
|
bits = bits >> 1;
|
|
if (bits&0x01) SPI.transfer16(fgcolor);
|
|
else SPI.transfer16(bgcolor);
|
|
bits = bits >> 1;
|
|
if (bits&0x01) SPI.transfer16(fgcolor);
|
|
else SPI.transfer16(bgcolor);
|
|
bits = bits >> 1;
|
|
if (bits&0x01) SPI.transfer16(fgcolor);
|
|
else SPI.transfer16(bgcolor);
|
|
}
|
|
x +=8;
|
|
digitalWrite(_cs, 1);
|
|
SPI.endTransaction();
|
|
}
|
|
SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE));
|
|
digitalWrite(_cs, 0);
|
|
setArea(0, 0, (TFT_REALWIDTH-1), (TFT_REALHEIGHT-1));
|
|
digitalWrite(_cs, 1);
|
|
SPI.endTransaction();
|
|
}
|
|
else {
|
|
drawText(x, y, text, fgcolor, bgcolor, doublesize);
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
DMA functions
|
|
***********************************************************************************************/
|
|
void T4_DSP::fillScreen(uint16_t color) {
|
|
int i,j;
|
|
if (gfxmode < MODE_VGA_320x240) {
|
|
for (j=0; j<tft_height; j++)
|
|
{
|
|
uint16_t * dst=&tft_buffer[j*tft_stride];
|
|
for (i=0; i<tft_width; i++)
|
|
{
|
|
*dst++ = color;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
vga_pixel color8 = VGA_RGB(R16(color),G16(color),B16(color));
|
|
for (j=0; j<vga_height; j++)
|
|
{
|
|
vga_pixel * dst=&vga_buffer[j*vga_stride];
|
|
for (i=0; i<vga_width; i++)
|
|
{
|
|
*dst++ = color8;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void T4_DSP::drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
|
|
int i,j,l=y;
|
|
if (gfxmode < MODE_VGA_320x240) {
|
|
for (j=0; j<h; j++)
|
|
{
|
|
uint16_t * dst=&tft_buffer[l*tft_stride+x];
|
|
for (i=0; i<w; i++)
|
|
{
|
|
*dst++ = color;
|
|
}
|
|
l++;
|
|
}
|
|
}
|
|
else {
|
|
vga_pixel color8 = VGA_RGB(R16(color),G16(color),B16(color));
|
|
for (j=0; j<h; j++)
|
|
{
|
|
vga_pixel * dst=&vga_buffer[l*vga_stride+x];
|
|
for (i=0; i<w; i++)
|
|
{
|
|
*dst++ = color8;
|
|
}
|
|
l++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void T4_DSP::drawText(int16_t x, int16_t y, const char * text, uint16_t fgcolor, uint16_t bgcolor, bool doublesize) {
|
|
uint16_t c;
|
|
if (gfxmode < MODE_VGA_320x240) {
|
|
uint16_t * dst;
|
|
while ((c = *text++)) {
|
|
const unsigned char * charpt=&font8x8[c][0];
|
|
int l=y;
|
|
for (int i=0;i<8;i++)
|
|
{
|
|
unsigned char bits;
|
|
if (doublesize) {
|
|
dst=&tft_buffer[l*tft_stride+x];
|
|
bits = *charpt;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
l++;
|
|
}
|
|
dst=&tft_buffer[l*tft_stride+x];
|
|
bits = *charpt++;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
l++;
|
|
}
|
|
x +=8;
|
|
}
|
|
}
|
|
else {
|
|
vga_pixel fgcolor8 = VGA_RGB(R16(fgcolor),G16(fgcolor),B16(fgcolor));
|
|
vga_pixel bgcolor8 = VGA_RGB(R16(bgcolor),G16(bgcolor),B16(bgcolor));
|
|
vga_pixel * dst;
|
|
while ((c = *text++)) {
|
|
const unsigned char * charpt=&font8x8[c][0];
|
|
int l=y;
|
|
for (int i=0;i<8;i++)
|
|
{
|
|
unsigned char bits;
|
|
if (doublesize) {
|
|
dst=&vga_buffer[l*vga_stride+x];
|
|
bits = *charpt;
|
|
if (bits&0x01) *dst++=fgcolor8;
|
|
else *dst++=bgcolor8;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor8;
|
|
else *dst++=bgcolor8;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor8;
|
|
else *dst++=bgcolor8;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor8;
|
|
else *dst++=bgcolor8;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor8;
|
|
else *dst++=bgcolor8;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor8;
|
|
else *dst++=bgcolor8;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor8;
|
|
else *dst++=bgcolor8;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor8;
|
|
else *dst++=bgcolor8;
|
|
l++;
|
|
}
|
|
dst=&vga_buffer[l*vga_stride+x];
|
|
bits = *charpt++;
|
|
if (bits&0x01) *dst++=fgcolor8;
|
|
else *dst++=bgcolor8;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor8;
|
|
else *dst++=bgcolor8;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor8;
|
|
else *dst++=bgcolor8;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor8;
|
|
else *dst++=bgcolor8;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor8;
|
|
else *dst++=bgcolor8;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor8;
|
|
else *dst++=bgcolor8;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor8;
|
|
else *dst++=bgcolor8;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor8;
|
|
else *dst++=bgcolor8;
|
|
l++;
|
|
}
|
|
x +=8;
|
|
}
|
|
}
|
|
}
|
|
|
|
void T4_DSP::drawSprite(int16_t x, int16_t y, const uint16_t *bitmap) {
|
|
drawSprite(x,y,bitmap, 0,0,0,0);
|
|
}
|
|
|
|
void T4_DSP::drawSprite(int16_t x, int16_t y, const uint16_t *bitmap, uint16_t arx, uint16_t ary, uint16_t arw, uint16_t arh)
|
|
{
|
|
int bmp_offx = 0;
|
|
int bmp_offy = 0;
|
|
uint16_t *bmp_ptr;
|
|
int w =*bitmap++;
|
|
int h = *bitmap++;
|
|
if ( (arw == 0) || (arh == 0) ) {
|
|
// no crop window
|
|
arx = x;
|
|
ary = y;
|
|
arw = w;
|
|
arh = h;
|
|
}
|
|
else {
|
|
if ( (x>(arx+arw)) || ((x+w)<arx) || (y>(ary+arh)) || ((y+h)<ary) ) {
|
|
return;
|
|
}
|
|
|
|
// crop area
|
|
if ( (x > arx) && (x<(arx+arw)) ) {
|
|
arw = arw - (x-arx);
|
|
arx = arx + (x-arx);
|
|
} else {
|
|
bmp_offx = arx;
|
|
}
|
|
if ( ((x+w) > arx) && ((x+w)<(arx+arw)) ) {
|
|
arw -= (arx+arw-x-w);
|
|
}
|
|
if ( (y > ary) && (y<(ary+arh)) ) {
|
|
arh = arh - (y-ary);
|
|
ary = ary + (y-ary);
|
|
} else {
|
|
bmp_offy = ary;
|
|
}
|
|
if ( ((y+h) > ary) && ((y+h)<(ary+arh)) ) {
|
|
arh -= (ary+arh-y-h);
|
|
}
|
|
}
|
|
int l=ary;
|
|
bitmap = bitmap + bmp_offy*w + bmp_offx;
|
|
|
|
if (gfxmode < MODE_VGA_320x240) {
|
|
for (int row=0;row<arh; row++)
|
|
{
|
|
uint16_t * dst=&tft_buffer[l*tft_stride+arx];
|
|
bmp_ptr = (uint16_t*)bitmap;
|
|
for (int col=0;col<arw; col++)
|
|
{
|
|
*dst++ = *bmp_ptr++;
|
|
}
|
|
bitmap += w;
|
|
l++;
|
|
}
|
|
}
|
|
else {
|
|
for (int row=0;row<arh; row++)
|
|
{
|
|
vga_pixel * dst=&vga_buffer[l*vga_stride+arx];
|
|
bmp_ptr = (uint16_t*)bitmap;
|
|
for (int col=0;col<arw; col++)
|
|
{
|
|
uint16_t pix= *bmp_ptr++;
|
|
*dst++ = VGA_RGB(R16(pix),G16(pix),B16(pix));
|
|
}
|
|
bitmap += w;
|
|
l++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void T4_DSP::writeLine(int width, int height, int y, uint16_t *buf) {
|
|
if (gfxmode < MODE_VGA_320x240) {
|
|
uint16_t * dst=&tft_buffer[y*tft_stride];
|
|
if (width > tft_width) {
|
|
#ifdef TFT_LINEARINT
|
|
int delta = (width/(width-tft_width))-1;
|
|
int pos = delta;
|
|
for (int i=0; i<tft_width; i++)
|
|
{
|
|
uint16_t val = *buf++;
|
|
pos--;
|
|
if (pos == 0) {
|
|
#ifdef LINEARINT_HACK
|
|
val = ((uint32_t)*buf++ + val)/2;
|
|
#else
|
|
uint16_t val2 = *buf++;
|
|
val = RGBVAL16((R16(val)+R16(val2))/2,(G16(val)+G16(val2))/2,(B16(val)+B16(val2))/2);
|
|
#endif
|
|
pos = delta;
|
|
}
|
|
*dst++=val;
|
|
}
|
|
#else
|
|
int step = ((width << 8)/tft_width);
|
|
int pos = 0;
|
|
for (int i=0; i<tft_width; i++)
|
|
{
|
|
*dst++=buf[pos >> 8];
|
|
pos +=step;
|
|
}
|
|
#endif
|
|
}
|
|
else if ((width*2) == tft_width)
|
|
{
|
|
for (int i=0; i<width; i++)
|
|
{
|
|
*dst++=*buf;
|
|
*dst++=*buf++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (width <= tft_width) {
|
|
dst += (tft_width-width)/2;
|
|
}
|
|
for (int i=0; i<width; i++)
|
|
{
|
|
*dst++=*buf++;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if ( (height<vga_height) && (height > 2) ) y += (vga_height-height)/2;
|
|
vga_pixel * dst=&vga_buffer[y*vga_stride];
|
|
if (width > vga_width) {
|
|
int step = ((width << 8)/vga_width);
|
|
int pos = 0;
|
|
for (int i=0; i<vga_width; i++)
|
|
{
|
|
uint16_t pix = buf[pos >> 8];
|
|
*dst++ = VGA_RGB(R16(pix),G16(pix),B16(pix));
|
|
pos +=step;
|
|
}
|
|
}
|
|
else if ((width*2) == vga_width) {
|
|
for (int i=0; i<width; i++)
|
|
{
|
|
uint16_t pix = *buf++;
|
|
vga_pixel col = VGA_RGB(R16(pix),G16(pix),B16(pix));
|
|
*dst++= col;
|
|
*dst++= col;
|
|
}
|
|
}
|
|
else {
|
|
if (width <= vga_width) {
|
|
dst += (vga_width-width)/2;
|
|
}
|
|
for (int i=0; i<width; i++)
|
|
{
|
|
uint16_t pix = *buf++;
|
|
*dst++= VGA_RGB(R16(pix),G16(pix),B16(pix));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void T4_DSP::writeLinePal(int width, int height, int y, uint8_t *buf, uint16_t *palette) {
|
|
if (gfxmode < MODE_VGA_320x240) {
|
|
if ( (height<tft_height) && (height > 2) ) y += (tft_height-height)/2;
|
|
uint16_t * dst=&tft_buffer[y*tft_stride];
|
|
if (width > tft_width) {
|
|
#ifdef TFT_LINEARINT
|
|
int delta = (width/(width-tft_width))-1;
|
|
int pos = delta;
|
|
for (int i=0; i<tft_width; i++)
|
|
{
|
|
uint16_t val = palette[*buf++];
|
|
pos--;
|
|
if (pos == 0) {
|
|
#ifdef LINEARINT_HACK
|
|
val = ((uint32_t)palette[*buf++] + val)/2;
|
|
#else
|
|
uint16_t val2 = *buf++;
|
|
val = RGBVAL16((R16(val)+R16(val2))/2,(G16(val)+G16(val2))/2,(B16(val)+B16(val2))/2);
|
|
#endif
|
|
pos = delta;
|
|
}
|
|
*dst++=val;
|
|
}
|
|
#else
|
|
int step = ((width << 8)/tft_width);
|
|
int pos = 0;
|
|
for (int i=0; i<tft_width; i++)
|
|
{
|
|
*dst++=palette[buf[pos >> 8]];
|
|
pos +=step;
|
|
}
|
|
#endif
|
|
}
|
|
else if ((width*2) == tft_width) {
|
|
for (int i=0; i<width; i++)
|
|
{
|
|
*dst++=palette[*buf];
|
|
*dst++=palette[*buf++];
|
|
}
|
|
}
|
|
else {
|
|
if (width <= tft_width) {
|
|
dst += (tft_width-width)/2;
|
|
}
|
|
for (int i=0; i<width; i++)
|
|
{
|
|
*dst++=palette[*buf++];
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if ( (height<vga_height) && (height > 2) ) y += (vga_height-height)/2;
|
|
vga_pixel * dst=&vga_buffer[y*vga_stride];
|
|
if (width > vga_width) {
|
|
int step = ((width << 8)/vga_width);
|
|
int pos = 0;
|
|
for (int i=0; i<vga_width; i++)
|
|
{
|
|
uint16_t pix = palette[buf[pos >> 8]];
|
|
*dst++= VGA_RGB(R16(pix),G16(pix),B16(pix));
|
|
pos +=step;
|
|
}
|
|
}
|
|
else if ((width*2) == vga_width) {
|
|
for (int i=0; i<width; i++)
|
|
{
|
|
uint16_t pix = palette[*buf++];
|
|
*dst++= VGA_RGB(R16(pix),G16(pix),B16(pix));
|
|
*dst++= VGA_RGB(R16(pix),G16(pix),B16(pix));
|
|
}
|
|
}
|
|
else {
|
|
if (width <= vga_width) {
|
|
dst += (vga_width-width)/2;
|
|
}
|
|
for (int i=0; i<width; i++)
|
|
{
|
|
uint16_t pix = palette[*buf++];
|
|
*dst++= VGA_RGB(R16(pix),G16(pix),B16(pix));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void T4_DSP::writeScreenPal(int width, int height, int stride, uint8_t *buf, uint16_t *palette16) {
|
|
uint8_t *src;
|
|
int i,j,y=0;
|
|
int sy = 0;
|
|
int systep=(1<<8);
|
|
int h = height;
|
|
|
|
if (gfxmode < MODE_VGA_320x240) {
|
|
if (height <= ( (2*tft_height)/3)) {
|
|
systep=(systep*height)/tft_height;
|
|
h = tft_height;
|
|
}
|
|
if (width*2 <= tft_width) {
|
|
for (j=0; j<h; j++)
|
|
{
|
|
uint16_t * dst=&tft_buffer[y*tft_stride];
|
|
src=&buf[(sy>>8)*stride];
|
|
for (i=0; i<width; i++)
|
|
{
|
|
uint16_t val = palette16[*src++];
|
|
*dst++ = val;
|
|
*dst++ = val;
|
|
}
|
|
y++;
|
|
sy+=systep;
|
|
}
|
|
}
|
|
else if (width <= tft_width) {
|
|
for (j=0; j<h; j++)
|
|
{
|
|
uint16_t * dst=&tft_buffer[y*tft_stride+(tft_width-width)/2];
|
|
src=&buf[(sy>>8)*stride];
|
|
for (i=0; i<width; i++)
|
|
{
|
|
uint16_t val = palette16[*src++];
|
|
*dst++ = val;
|
|
}
|
|
y++;
|
|
sy+=systep;
|
|
}
|
|
}
|
|
}
|
|
else { // VGA
|
|
if (height <= ( (2*vga_height)/3)) {
|
|
systep=(systep*height)/vga_height;
|
|
h = vga_height;
|
|
}
|
|
if (width*2 <= vga_width) {
|
|
for (j=0; j<h; j++)
|
|
{
|
|
vga_pixel * dst=&vga_buffer[y*vga_stride];
|
|
src=&buf[(sy>>8)*stride];
|
|
for (i=0; i<width; i++)
|
|
{
|
|
uint16_t pix = palette16[*src++];
|
|
*dst++ = VGA_RGB(R16(pix),G16(pix),B16(pix));
|
|
*dst++ = VGA_RGB(R16(pix),G16(pix),B16(pix));
|
|
}
|
|
y++;
|
|
sy+=systep;
|
|
}
|
|
}
|
|
else if (width <= vga_width) {
|
|
for (j=0; j<h; j++)
|
|
{
|
|
vga_pixel * dst=&vga_buffer[y*vga_stride+(vga_width-width)/2];
|
|
src=&buf[(sy>>8)*stride];
|
|
for (i=0; i<width; i++)
|
|
{
|
|
uint16_t pix = palette16[*src++];
|
|
*dst++ = VGA_RGB(R16(pix),G16(pix),B16(pix));
|
|
}
|
|
y++;
|
|
sy+=systep;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void T4_DSP::writeLine8(int width, int height, int y, uint8_t *buf, uint16_t *palette) {
|
|
if (gfxmode < MODE_VGA_320x240) {
|
|
uint16_t * dst=&tft_buffer[y*tft_stride];
|
|
if (width > tft_width) {
|
|
int step = ((width << 8)/tft_width);
|
|
int pos = 0;
|
|
for (int i=0; i<tft_width; i++)
|
|
{
|
|
*dst++=palette[buf[pos >> 8]];
|
|
pos +=step;
|
|
}
|
|
}
|
|
else if ((width*2) == tft_width)
|
|
{
|
|
for (int i=0; i<width; i++)
|
|
{
|
|
uint16_t val = palette[*buf++];
|
|
*dst++=val;
|
|
*dst++=val;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (width <= tft_width) {
|
|
dst += (tft_width-width)/2;
|
|
}
|
|
for (int i=0; i<width; i++)
|
|
{
|
|
*dst++=palette[*buf++];
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if ( (height<vga_height) && (height > 2) ) y += (vga_height-height)/2;
|
|
vga_pixel * dst=&vga_buffer[y*vga_stride];
|
|
if (width > vga_width) {
|
|
int step = ((width << 8)/vga_width);
|
|
int pos = 0;
|
|
for (int i=0; i<vga_width; i++)
|
|
{
|
|
uint8_t pix = buf[pos >> 8];
|
|
*dst++ = pix;
|
|
pos +=step;
|
|
}
|
|
}
|
|
else if ((width*2) == vga_width) {
|
|
for (int i=0; i<width; i++)
|
|
{
|
|
vga_pixel col = *buf++;
|
|
*dst++= col;
|
|
*dst++= col;
|
|
}
|
|
}
|
|
else {
|
|
if (width <= vga_width) {
|
|
dst += (vga_width-width)/2;
|
|
}
|
|
for (int i=0; i<width; i++)
|
|
{
|
|
*dst++= *buf++;
|
|
}
|
|
}
|
|
}
|
|
}
|