UHSDR/UHSDR-active-devel/mchf-eclipse/drivers/ui/lcd/ui_lcd_hy28.c
2022-11-08 16:13:55 +01:00

3196 lines
94 KiB
C

/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
// Common
#include <stdio.h>
#include <stdlib.h>
#include "uhsdr_board.h"
#include "uhsdr_board_config.h"
#include "ui_lcd_hy28_fonts.h"
#include "ui_lcd_hy28.h"
#define hspiDisplay hspi2
#define SPI_DISPLAY SPI2
#ifndef STM32H7
// FIXME: H7 Port, re-enable DMA once SPI display is working
#define USE_SPI_DMA
#endif
#if defined(STM32F7) || defined(STM32H7)
#define USE_SPI_HAL
#endif
#define USE_SPI_DISPLAY
#define USE_DISPLAY_PAR
#if !defined(USE_DISPLAY_PAR) && !defined(USE_SPI_DISPLAY)
#warning Both USE_DISPLAY_PAR and USE_SPI_DISPLAY are disabled, no display driver will be available!
#endif
#include "spi.h"
#ifdef USE_DISPLAY_PAR
#ifdef UI_BRD_OVI40
#include "fmc.h"
#define MEM_Init() MX_FMC_Init()
#else
#include "fsmc.h"
#define MEM_Init() MX_FSMC_Init()
#endif
#define LCD_REG_RA8875 (*((volatile unsigned short *) 0x60004000))
#define LCD_RAM_RA8875 (*((volatile unsigned short *) 0x60000000))
#define LCD_REG (*((volatile unsigned short *) 0x60000000))
#if defined(UI_BRD_MCHF)
#define LCD_RAM (*((volatile unsigned short *) 0x60020000))
#elif defined(UI_BRD_OVI40)
#define LCD_RAM (*((volatile unsigned short *) 0x60004000))
#endif
#endif
#ifdef TimeDebug
#define MARKER LCD_D0
#define MARKER_PIO LCD_D0_PIO
#define Marker_ON MARKER_PIO->BSRR=MARKER;
#define Marker_OFF MARKER_PIO->BSRR=MARKER<<16;
#endif
#define SPI_START (0x70) /* Start byte for SPI transfer */
#define SPI_RD (0x01) /* WR bit 1 within start */
#define SPI_WR (0x00) /* WR bit 0 within start */
#define SPI_DATA (0x02) /* RS bit 1 within start byte */
#define SPI_INDEX (0x00) /* RS bit 0 within start byte */
#define SPI_TIMEOUT 100
mchf_display_t mchf_display;
extern const uhsdr_display_info_t display_infos[];
const uhsdr_display_info_t* UiLcdHy28_DisplayInfoGet(mchf_display_types_t display_type)
{
const uhsdr_display_info_t* retval = NULL;
for (int i=DISPLAY_NUM-1; i && retval == NULL; i--)
{
if (display_type == display_infos[i].display_type)
{
retval = &display_infos[i];
}
}
return retval;
}
#ifndef BOOTLOADER_BUILD
// Saved fonts
extern sFONT GL_Font8x8;
extern sFONT GL_Font8x12;
#ifndef SDR_AMBER
extern sFONT GL_Font8x12_bold;
#endif
extern sFONT GL_Font12x12;
extern sFONT GL_Font16x24;
#ifdef USE_8bit_FONT
extern sFONT GL_Font16x24_8b_Square;
#endif
extern sFONT GL_Font8x12_bold_noserif;
static sFONT *fontList[] =
{
#ifdef SDR_AMBER
&GL_Font8x12_bold_noserif,
#else
&GL_Font8x12_bold,
#endif
&GL_Font16x24,
&GL_Font12x12,
&GL_Font8x12,
&GL_Font8x8,
#ifdef USE_8bit_FONT
&GL_Font16x24_8b_Square,
#endif
};
#else
extern sFONT GL_Font8x12_bold_short;
static sFONT *fontList[] =
{
&GL_Font8x12_bold_short,
};
#endif
static inline void UiLcdHy28_TouchscreenStartSpiTransfer(void);
// we can do this here since fontList is an array variable not just a pointer!
static const uint8_t fontCount = sizeof(fontList)/sizeof(fontList[0]);
#ifdef USE_GFX_ILI932x
static const RegisterValue_t ili9320[] =
{
{ 0xE5, 0x8000}, // Set the internal vcore voltage
{ REGVAL_DELAY, 0x0001}, // Start internal OSC.
// Direction related
{ 0x01, 0x0100}, // set SS and SM bit
{ 0x02, 0x0700}, // set 1 line inversionc
{ 0x03, 0x1038}, // set GRAM write direction and BGR=1 and ORG = 1.
{ 0x04, 0x0000}, // Resize register
{ 0x08, 0x0202}, // set the back porch and front porch
{ 0x09, 0x0000}, // set non-display area refresh cycle ISC[3:0]
{ 0x0A, 0x0000}, // FMARK function
{ 0x0C, 0x0000}, // RGB interface setting
{ 0x0D, 0x0000}, // Frame marker Position
{ 0x0F, 0x0000}, // RGB interface polarity
// Power On sequence
{ 0x10, 0x0000}, // SAP, BT[3:0], AP, DSTB, SLP, STB
{ 0x11, 0x0000}, // DC1[2:0], DC0[2:0], VC[2:0]
{ 0x12, 0x0000}, // VREG1OUT voltage
{ 0x13, 0x0000}, // VDV[4:0] for VCOM amplitude
{ REGVAL_DELAY, 300}, // Dis-charge capacitor power voltage (300ms)
{ 0x10, 0x17B0}, // SAP, BT[3:0], AP, DSTB, SLP, STB
{ 0x11, 0x0137}, // DC1[2:0], DC0[2:0], VC[2:0]
{ REGVAL_DELAY, 100}, // Delay 100 ms
{ 0x12, 0x0139}, // VREG1OUT voltage
{ REGVAL_DELAY, 100}, // Delay 100 ms
{ 0x13, 0x1d00}, // VDV[4:0] for VCOM amplitude
{ 0x29, 0x0013}, // VCM[4:0] for VCOMH
{ REGVAL_DELAY, 100}, // Delay 100 ms
{ 0x20, 0x0000}, // GRAM horizontal Address
{ 0x21, 0x0000}, // GRAM Vertical Address
// Adjust the Gamma Curve
{ 0x30, 0x0007},
{ 0x31, 0x0007},
{ 0x32, 0x0007},
{ 0x35, 0x0007},
{ 0x36, 0x0007},
{ 0x37, 0x0700},
{ 0x38, 0x0700},
{ 0x39, 0x0700},
{ 0x3C, 0x0700},
{ 0x3D, 0x1F00},
// Set GRAM area
{ 0x50, 0x0000}, // Horizontal GRAM Start Address
{ 0x51, 0x00EF}, // Horizontal GRAM End Address
{ 0x52, 0x0000}, // Vertical GRAM Start Address
{ 0x53, 0x013F}, // Vertical GRAM End Address
// Direction related
{ 0x60, 0xA700}, // Gate Scan Line
{ 0x61, 0x0001}, // NDL,VLE, REV
{ 0x6A, 0x0000}, // set scrolling line
// Partial Display Control
{ 0x80, 0x0000},
{ 0x81, 0x0000},
{ 0x82, 0x0000},
{ 0x83, 0x0000},
{ 0x84, 0x0000},
{ 0x85, 0x0000},
// Panel Control
{ 0x90, 0x0010},
{ 0x92, 0x0000},
{ 0x93, 0x0003},
{ 0x95, 0x0110},
{ 0x97, 0x0000},
{ 0x98, 0x0000},
// Set GRAM write direction
{ 0x03, 0x1038},
// 262K color and display ON
{ 0x07, 0x0173},
// delay 50 ms
{ REGVAL_DELAY, 50},
};
static const RegisterValueSetInfo_t ili9320_regs =
{
ili9320, sizeof(ili9320)/sizeof(RegisterValue_t)
};
static const RegisterValue_t spdfd5408b[] =
{
{ 0x01, 0x0000}, // (SS bit 8) - 0x0100 will flip 180 degree
{ 0x02, 0x0700}, // LCD Driving Waveform Contral
{ 0x03, 0x1038}, // Entry Mode (AM bit 3)
{ 0x04, 0x0000}, // Scaling Control register
{ 0x08, 0x0207}, // Display Control 2
{ 0x09, 0x0000}, // Display Control 3
{ 0x0A, 0x0000}, // Frame Cycle Control
{ 0x0C, 0x0000}, // External Display Interface Control 1
{ 0x0D, 0x0000}, // Frame Maker Position
{ 0x0F, 0x0000}, // External Display Interface Control 2
{ REGVAL_DELAY, 50},
{ 0x07, 0x0101}, // Display Control
{ REGVAL_DELAY, 50},
{ 0x10, 0x16B0}, // Power Control 1
{ 0x11, 0x0001}, // Power Control 2
{ 0x17, 0x0001}, // Power Control 3
{ 0x12, 0x0138}, // Power Control 4
{ 0x13, 0x0800}, // Power Control 5
{ 0x29, 0x0009}, // NVM read data 2
{ 0x2a, 0x0009}, // NVM read data 3
{ 0xa4, 0x0000},
{ 0x50, 0x0000},
{ 0x51, 0x00EF},
{ 0x52, 0x0000},
{ 0x53, 0x013F},
{ 0x60, 0x2700}, // Driver Output Control (GS bit 15)
{ 0x61, 0x0003}, // Driver Output Control
{ 0x6A, 0x0000}, // Vertical Scroll Control
{ 0x80, 0x0000}, // Display Position ?C Partial Display 1
{ 0x81, 0x0000}, // RAM Address Start ?C Partial Display 1
{ 0x82, 0x0000}, // RAM address End - Partial Display 1
{ 0x83, 0x0000}, // Display Position ?C Partial Display 2
{ 0x84, 0x0000}, // RAM Address Start ?C Partial Display 2
{ 0x85, 0x0000}, // RAM address End ?C Partail Display2
{ 0x90, 0x0013}, // Frame Cycle Control
{ 0x92, 0x0000}, // Panel Interface Control 2
{ 0x93, 0x0003}, // Panel Interface control 3
{ 0x95, 0x0110}, // Frame Cycle Control
{ 0x07, 0x0173},
};
static const RegisterValueSetInfo_t spdfd5408b_regs =
{
spdfd5408b, sizeof(spdfd5408b)/sizeof(RegisterValue_t)
};
#ifdef USE_GFX_SSD1289
static const RegisterValue_t ssd1289[] =
{
{0x00,0x0001},
{0x03,0xA8A4},
{0x0C,0x0000},
{0x0D,0x080C},
{0x0E,0x2B00},
{0x1E,0x00B7},
{0x01,0x2B3F},
{0x02,0x0600},
{0x10,0x0000},
{0x11,0x6070},
{0x05,0x0000},
{0x06,0x0000},
{0x16,0xEF1C},
{0x17,0x0003},
{0x07,0x0233},
{0x0B,0x0000},
{0x0F,0x0000},
{0x41,0x0000},
{0x42,0x0000},
{0x48,0x0000},
{0x49,0x013F},
{0x4A,0x0000},
{0x4B,0x0000},
{0x44,0xEF00},
{0x45,0x0000},
{0x46,0x013F},
{0x30,0x0707},
{0x31,0x0204},
{0x32,0x0204},
{0x33,0x0502},
{0x34,0x0507},
{0x35,0x0204},
{0x36,0x0204},
{0x37,0x0502},
{0x3A,0x0302},
{0x3B,0x0302},
{0x23,0x0000},
{0x24,0x0000},
{0x25,0x8000},
{0x4f,0x0000},
{0x4e,0x0000},
};
static const RegisterValueSetInfo_t ssd1289_regs =
{
ssd1289, sizeof(ssd1289)/sizeof(RegisterValue_t)
};
#endif
static const RegisterValue_t ili932x[] =
{
// NPI: { 0xE5, 0x78F0}, // set SRAM internal timing I guess this is the relevant line for getting LCDs to work which are "out-of-specs"...
{ 0x01, 0x0000}, // set SS and SM bit
{ 0x02, 0x0700}, // set 1 line inversion
{ 0x03, 0x1038}, // set GRAM write direction and BGR=1 and ORG = 1
{ 0x04, 0x0000}, // resize register
{ 0x08, 0x0207}, // set the back porch and front porch
{ 0x09, 0x0000}, // set non-display area refresh cycle
{ 0x0a, 0x0000}, // FMARK function
{ 0x0c, 0x0001}, // RGB interface setting
// NPI: { 0x0c, 0x0000}, // RGB interface setting
{ 0x0d, 0x0000}, // frame marker position
{ 0x0f, 0x0000}, // RGB interface polarity
// Power On sequence
{ 0x10, 0x0000}, // SAP, BT[3:0], AP, DSTB, SLP, STB
{ 0x11, 0x0007}, // DC1[2:0], DC0[2:0], VC[2:0]
{ 0x12, 0x0000}, // VREG1OUT voltage
{ 0x13, 0x0000}, // VDV[4:0] for VCOM amplitude
// NPI: { 0x0c, 0x0001}, // RGB interface setting
{ REGVAL_DELAY, 200}, // delay 200 ms
{ 0x10, 0x1590}, // SAP, BT[3:0], AP, DSTB, SLP, STB
// NPI: { 0x10, 0x1090}, // SAP, BT[3:0], AP, DSTB, SLP, STB
{ 0x11, 0x0227}, // set DC1[2:0], DC0[2:0], VC[2:0]
{ REGVAL_DELAY, 50}, // delay 50 ms
{ 0x12, 0x009c}, // internal reference voltage init
// NPI: { 0x12, 0x001F},
{ REGVAL_DELAY, 50}, // delay 50 ms
{ 0x13, 0x1900}, // set VDV[4:0] for VCOM amplitude
// NPI: { 0x13, 0x1500},
{ 0x29, 0x0023}, // VCM[5:0] for VCOMH
// NPI: { 0x29, 0x0027}, // VCM[5:0] for VCOMH
{ 0x2b, 0x000d}, // set frame rate: changed from 0e to 0d on 03/28/2016
{ REGVAL_DELAY, 50}, // delay 50 ms
{ 0x20, 0x0000}, // GRAM horizontal address
{ 0x21, 0x0000}, // GRAM vertical address
// /* NPI:
// ----------- Adjust the Gamma Curve ----------
{ 0x30, 0x0000},
{ 0x31, 0x0707},
{ 0x32, 0x0307},
{ 0x35, 0x0200},
{ 0x36, 0x0008},
{ 0x37, 0x0004},
{ 0x38, 0x0000},
{ 0x39, 0x0707},
{ 0x3C, 0x0002},
{ 0x3D, 0x1D04},
// */
{ 0x50, 0x0000}, // horizontal GRAM start address
{ 0x51, 0x00ef}, // horizontal GRAM end address
{ 0x52, 0x0000}, // vertical GRAM start address
{ 0x53, 0x013f}, // vertical GRAM end address
{ 0x60, 0xa700}, // gate scan line
{ 0x61, 0x0001}, // NDL, VLE, REV
{ 0x6a, 0x0000}, // set scrolling line
// partial display control
{ 0x80, 0x0000},
{ 0x81, 0x0000},
{ 0x82, 0x0000},
{ 0x83, 0x0000},
{ 0x84, 0x0000},
{ 0x85, 0x0000},
// panel control
{ 0x90, 0x0010},
{ 0x92, 0x0000},
// NPI: { 0x92, 0x0600},
// activate display using 262k colours
{ 0x07, 0x0133},
};
static const RegisterValueSetInfo_t ili932x_regs =
{
ili932x, sizeof(ili932x)/sizeof(RegisterValue_t)
};
#endif
#ifdef USE_GFX_ILI9341
static const RegisterValue_t ili9341[] =
{
{ 0x01,0x00}, //SOFTWARE RESET
{ REGVAL_DELAY, 500},
{ 0xCB,0x39}, // POWER CONTROL A
{ REGVAL_DATA, 0x2C},
{ REGVAL_DATA, 0x00},
{ REGVAL_DATA, 0x34},
{ REGVAL_DATA, 0x02},
{ 0xCF,0x00}, // POWER CONTROL B
{ REGVAL_DATA, 0xC1},
{ REGVAL_DATA, 0x30},
{ 0xE8,0x85}, // DRIVER TIMING CONTROL A
{ REGVAL_DATA, 0x00},
{ REGVAL_DATA, 0x78},
{ 0xEA,0x00}, // DRIVER TIMING CONTROL B
{ REGVAL_DATA, 0x00},
{ 0xED,0x64}, // POWER ON SEQUENCE CONTROL
{ REGVAL_DATA, 0x03},
{ REGVAL_DATA, 0x12},
{ REGVAL_DATA, 0x81},
{ 0xF7, 0x20}, // PUMP RATIO CONTROL
{ 0xC0, 0x23}, // POWER CONTROL,VRH[5:0]
{ 0xC1, 0x10}, // POWER CONTROL,SAP[2:0];BT[3:0]
{ 0xC5, 0x3e}, // VCM CONTROL
{ REGVAL_DATA, 0x28},
{ 0xC7, 0x86}, // VCM CONTROL 2
{ 0x36, 0x48}, // MEMORY ACCESS CONTROL
{ 0x3A, 0x55}, // PIXEL FORMAT
{ 0xB1, 0x00}, // FRAME RATIO CONTROL, STANDARD RGB COLOR
{ REGVAL_DATA, 0x18},
{ 0xB6, 0x08}, // DISPLAY FUNCTION CONTROL
{ REGVAL_DATA, 0x82},
{ REGVAL_DATA, 0x27},
{ 0xF2, 0x00}, // 3GAMMA FUNCTION DISABLE
{ 0x26, 0x01}, // GAMMA CURVE SELECTED
{ 0xE0, 0x0F}, // POSITIVE GAMMA CORRECTION
{ REGVAL_DATA, 0x31},
{ REGVAL_DATA, 0x2B},
{ REGVAL_DATA, 0x0C},
{ REGVAL_DATA, 0x0E},
{ REGVAL_DATA, 0x08},
{ REGVAL_DATA, 0x4E},
{ REGVAL_DATA, 0xF1},
{ REGVAL_DATA, 0x37},
{ REGVAL_DATA, 0x07},
{ REGVAL_DATA, 0x10},
{ REGVAL_DATA, 0x03},
{ REGVAL_DATA, 0x0E},
{ REGVAL_DATA, 0x09},
{ REGVAL_DATA, 0x00},
{ 0xE1, 0x00}, // NEGATIVE GAMMA CORRECTION
{ REGVAL_DATA, 0x0E},
{ REGVAL_DATA, 0x14},
{ REGVAL_DATA, 0x03},
{ REGVAL_DATA, 0x11},
{ REGVAL_DATA, 0x07},
{ REGVAL_DATA, 0x31},
{ REGVAL_DATA, 0xC1},
{ REGVAL_DATA, 0x48},
{ REGVAL_DATA, 0x08},
{ REGVAL_DATA, 0x0F},
{ REGVAL_DATA, 0x0C},
{ REGVAL_DATA, 0x31},
{ REGVAL_DATA, 0x36},
{ REGVAL_DATA, 0x0F},
{ 0x11, 0x00}, // EXIT SLEEP
{ REGVAL_DELAY, 120},
{ 0x29, 0x00}, // TURN ON DISPLAY
{ 0x36, 0xE8}, // MADCTL ORIENTATION 0x80+0x40+0x20+0x08 320x240
};
static const RegisterValueSetInfo_t ili9341_regs =
{
ili9341, sizeof(ili9341)/sizeof(RegisterValue_t)
};
#endif
#ifdef USE_GFX_RA8875
static const RegisterValue_t ra8875[] =
{
{ 0x01, 0x01}, // Software reset the LCD
{ REGVAL_DELAY, 100}, // delay 100 ms
{ 0x01, 0x00},
{ REGVAL_DELAY, 100}, // delay 100 ms
{ 0x88, 0x0a},
{ REGVAL_DELAY, 100}, // delay 100 ms
{ 0x89, 0x02},
{ REGVAL_DELAY, 100}, // delay 100 ms
{ 0x10, 0x0F}, // 65K 16 bit 8080 mpu interface
{ 0x04, 0x80}, // 00b: PCLK period = System Clock period.
{ REGVAL_DELAY, 100}, // delay 100 ms
//Horizontal set
{ 0x14, 0x63}, //Horizontal display width(pixels) = (HDWR + 1)*8
{ 0x15, 0x00}, //Horizontal Non-Display Period Fine Tuning(HNDFT) [3:0]
{ 0x16, 0x03}, //Horizontal Non-Display Period (pixels) = (HNDR + 1)*8
{ 0x17, 0x03}, //HSYNC Start Position(PCLK) = (HSTR + 1)*8
{ 0x18, 0x0B}, //HSYNC Width [4:0] HSYNC Pulse width(PCLK) = (HPWR + 1)*8
//Vertical set
{ 0x19, 0xdf}, //Vertical pixels = VDHR + 1
{ 0x1a, 0x01}, //Vertical pixels = VDHR + 1
{ 0x1b, 0x20}, //Vertical Non-Display area = (VNDR + 1)
{ 0x1c, 0x00}, //Vertical Non-Display area = (VNDR + 1)
{ 0x1d, 0x16}, //VSYNC Start Position(PCLK) = (VSTR + 1)
{ 0x1e, 0x00}, //VSYNC Start Position(PCLK) = (VSTR + 1)
{ 0x1f, 0x01}, //VSYNC Pulse Width(PCLK) = (VPWR + 1)
// setting active window 0,799,0,479
{ 0x30, 0x00},
{ 0x31, 0x00},
{ 0x34, 0x1f},
{ 0x35, 0x03},
{ 0x32, 0x00},
{ 0x33, 0x00},
{ 0x36, 0xDF},
{ 0x37, 0x01},
{ 0x8a, 0x80},
{ 0x8a, 0x81}, //open PWM
{ 0x8b, 0x1f}, //Brightness parameter 0xff-0x00
{ 0x01, 0x80}, //display on
//UiLcdHy28_WriteReg(LCD_DPCR,0b00001111}, // rotacion 180º
{ 0x60, 0x00}, /* ra8875_red */
{ 0x61, 0x00}, /* ra8875_green */
{ 0x62, 0x00}, /* ra8875_blue */
{ 0x8E, 0x80},
};
static const RegisterValueSetInfo_t ra8875_regs =
{
ra8875, sizeof(ra8875)/sizeof(RegisterValue_t)
};
#endif
#ifdef USE_GFX_ILI9486
static const RegisterValue_t ili9486[] =
{
{ 0xB0,0},
{ 0x11,0},
{ REGVAL_DELAY, 250},
{ 0x3A, 0x55}, // COLMOD_PIXEL_FORMAT_SET 16 bit pixel
{ 0xC0, 0x0f},
{ REGVAL_DATA, 0x0f},
{ 0xC1, 0x42},
{ 0xC2, 0x22},
{ 0xC5, 0x01},
{ REGVAL_DATA, 0x29}, //4D
{ REGVAL_DATA, 0x80},
{ 0xB6, 0x00},
{ REGVAL_DATA, 0x02}, //42
{ REGVAL_DATA, 0x3b},
{ 0xB1, 0xB0},
{ REGVAL_DATA, 0x11},
{ 0xB4, 0x02},
{ 0xE0, 0x0f},
{ REGVAL_DATA, 0x1F},
{ REGVAL_DATA, 0x1C},
{ REGVAL_DATA, 0x0C},
{ REGVAL_DATA, 0x0F},
{ REGVAL_DATA, 0x08},
{ REGVAL_DATA, 0x48},
{ REGVAL_DATA, 0x98},
{ REGVAL_DATA, 0x37},
{ REGVAL_DATA, 0x0a},
{ REGVAL_DATA, 0x13},
{ REGVAL_DATA, 0x04},
{ REGVAL_DATA, 0x11},
{ REGVAL_DATA, 0x0d},
{ REGVAL_DATA, 0x00},
{ 0xE1, 0x0f},
{ REGVAL_DATA, 0x32},
{ REGVAL_DATA, 0x2e},
{ REGVAL_DATA, 0x0b},
{ REGVAL_DATA, 0x0d},
{ REGVAL_DATA, 0x05},
{ REGVAL_DATA, 0x47},
{ REGVAL_DATA, 0x75},
{ REGVAL_DATA, 0x37},
{ REGVAL_DATA, 0x06},
{ REGVAL_DATA, 0x10},
{ REGVAL_DATA, 0x03},
{ REGVAL_DATA, 0x24},
{ REGVAL_DATA, 0x20},
{ REGVAL_DATA, 0x00},
{ 0xE2, 0x0f},
{ REGVAL_DATA, 0x32},
{ REGVAL_DATA, 0x2e},
{ REGVAL_DATA, 0x0b},
{ REGVAL_DATA, 0x0d},
{ REGVAL_DATA, 0x05},
{ REGVAL_DATA, 0x47},
{ REGVAL_DATA, 0x75},
{ REGVAL_DATA, 0x37},
{ REGVAL_DATA, 0x06},
{ REGVAL_DATA, 0x10},
{ REGVAL_DATA, 0x03},
{ REGVAL_DATA, 0x24},
{ REGVAL_DATA, 0x20}, { REGVAL_DATA, 0x00},
{ 0x13, 0x00}, //normal display mode ON
{ 0x20, 0x00}, //display inversion off
{ 0x36, 0x028},
{ 0x11, 0x00},
{ REGVAL_DELAY, 250},
{ 0x29, 0x00},
{ REGVAL_DELAY, 250},
};
static const RegisterValueSetInfo_t ili9486_regs =
{
ili9486, sizeof(ili9486)/sizeof(RegisterValue_t)
};
static const RegisterValueSetInfo_t ST7796_regs =
{
ili9486, sizeof(ili9486)/sizeof(RegisterValue_t)
};
#endif
typedef struct
{
uint16_t x;
uint16_t width;
uint16_t y;
uint16_t height;
} lcd_bulk_transfer_header_t;
#ifdef USE_GFX_RA8875
void UiLcdHy28_RA8875_WaitReady();
void UiLcdRA8875_SetForegroundColor(uint16_t Color);
void UiLcdRa8875_WriteReg_8bit(uint16_t LCD_Reg, uint8_t LCD_RegValue);
void UiLcdRa8875_WriteReg_16bit(uint16_t LCD_Reg, uint16_t LCD_RegValue);
#endif
static void UiLcdHy28_BulkWriteColor(uint16_t Color, uint32_t len);
static inline bool UiLcdHy28_SpiDisplayUsed(void)
{
bool retval = false;
#ifdef USE_SPI_DISPLAY
retval = mchf_display.use_spi;
#endif
return retval;
}
void UiLcdHy28_BacklightInit()
{
GPIO_InitTypeDef GPIO_InitStructure;
// Set as output
GPIO_InitStructure.Pin = LCD_BACKLIGHT;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LCD_BACKLIGHT_PIO, &GPIO_InitStructure);
// Backlight off
GPIO_ResetBits(LCD_BACKLIGHT_PIO, LCD_BACKLIGHT);
}
void UiLcdHy28_BacklightEnable(bool on)
{
if (on)
{
GPIO_SetBits(LCD_BACKLIGHT_PIO, LCD_BACKLIGHT);
}
else
{
GPIO_ResetBits(LCD_BACKLIGHT_PIO, LCD_BACKLIGHT);
}
}
#ifdef STM32F4
#define SPI_PRESCALE_LCD_DEFAULT (SPI_BAUDRATEPRESCALER_4)
#define SPI_PRESCALE_LCD_HIGH (SPI_BAUDRATEPRESCALER_2)
#define SPI_PRESCALE_TS_DEFAULT (SPI_BAUDRATEPRESCALER_64)
#endif
#ifdef STM32F7
#define SPI_PRESCALE_LCD_DEFAULT (SPI_BAUDRATEPRESCALER_8)
#define SPI_PRESCALE_LCD_HIGH (SPI_BAUDRATEPRESCALER_4)
#define SPI_PRESCALE_TS_DEFAULT (SPI_BAUDRATEPRESCALER_128)
#endif
#ifdef STM32H7
#define SPI_PRESCALE_LCD_DEFAULT (SPI_BAUDRATEPRESCALER_8)
#define SPI_PRESCALE_LCD_HIGH (SPI_BAUDRATEPRESCALER_4)
#define SPI_PRESCALE_TS_DEFAULT (SPI_BAUDRATEPRESCALER_32)
// 16 may be a little bit high for some displays but works with the 480x320 display
#endif
static uint32_t lcd_spi_prescaler = SPI_PRESCALE_LCD_DEFAULT;
// static SPI_HandleTypeDef SPI_Handle;
void UiLcdHy28_SpiInit(bool hispeed, mchf_display_types_t display_type)
{
lcd_spi_prescaler = hispeed?SPI_PRESCALE_LCD_HIGH:SPI_PRESCALE_LCD_DEFAULT;
#ifdef USE_GFX_ILI9486
if (display_type == DISPLAY_RPI_SPI)
{
hspiDisplay.Init.CLKPolarity = SPI_POLARITY_LOW;
hspiDisplay.Init.CLKPhase = SPI_PHASE_1EDGE;
hspiDisplay.Init.NSS = SPI_NSS_SOFT;
hspiDisplay.Init.BaudRatePrescaler = lcd_spi_prescaler;
if (HAL_SPI_Init(&hspiDisplay) != HAL_OK)
{
Error_Handler();
}
}
#endif
#ifdef USE_GFX_ILI9341
hspiDisplay.Init.CLKPolarity = SPI_POLARITY_LOW;
hspiDisplay.Init.CLKPhase = SPI_PHASE_1EDGE;
hspiDisplay.Init.NSS = SPI_NSS_SOFT;
hspiDisplay.Init.BaudRatePrescaler = lcd_spi_prescaler;
if (HAL_SPI_Init(&hspiDisplay) != HAL_OK)
{
Error_Handler();
}
#endif
// Enable the SPI periph
// the main init is already done earlier, we need this if we want to use our own code to access SPI
__HAL_SPI_ENABLE(&hspiDisplay);
}
void UiLcdHy28_GpioInit(mchf_display_types_t display_type)
{
GPIO_InitTypeDef GPIO_InitStructure;
// Common misc pins settings
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStructure.Pull = GPIO_NOPULL;
if (mchf_display.lcd_cs_pio != NULL)
{
// Configure GPIO PIN for Chip select
GPIO_InitStructure.Pin = mchf_display.lcd_cs;
HAL_GPIO_Init(mchf_display.lcd_cs_pio, &GPIO_InitStructure);
// Deselect : Chip Select high
GPIO_SetBits(mchf_display.lcd_cs_pio, mchf_display.lcd_cs);
}
// Configure GPIO PIN for Reset
GPIO_InitStructure.Pin = LCD_RESET;
HAL_GPIO_Init(LCD_RESET_PIO, &GPIO_InitStructure);
// TODO: Function Gets Display Type (!) not controller as parameter
#ifdef USE_GFX_ILI9486
if (display_type == DISPLAY_RPI_SPI)
{
// Configure GPIO PIN for RS
GPIO_InitStructure.Pin = LCD_RS;
HAL_GPIO_Init(LCD_RS_PIO, &GPIO_InitStructure);
}
#endif
#ifdef USE_GFX_ILI9341
// Configure GPIO PIN for RS
GPIO_InitStructure.Pin = LCD_RS;
HAL_GPIO_Init(LCD_RS_PIO, &GPIO_InitStructure);
#endif
#ifdef TimeDebug
//Configure GPIO pin for routine time optimization (for scope probe)
GPIO_InitStructure.Pin = MARKER;
HAL_GPIO_Init(MARKER_PIO, &GPIO_InitStructure);
#endif
}
DMA_HandleTypeDef DMA_Handle;
static inline void UiLcdHy28_SpiDmaStop(void)
{
while (DMA1_Stream4->CR & DMA_SxCR_EN) { asm(""); }
}
void UiLcdHy28_SpiDmaStart(uint8_t* buffer, uint32_t size)
{
// do busy waiting here. This is just for testing if everything goes according to plan
// if this works okay, we can let SPI DMA running while doing something else
// and just check before next transfer if DMA is being done.
// and finally we can move that into an interrupt, of course.
if (size > 0) {
UiLcdHy28_SpiDmaStop();
HAL_SPI_Transmit_DMA(&hspiDisplay,buffer,size);
}
}
void UiLcdHy28_SpiDeInit()
{
// __HAL_SPI_DISABLE(&hspiDisplay);
// HAL_SPI_DeInit(&hspiDisplay);
GPIO_InitTypeDef GPIO_InitStructure;
// Set as inputs
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStructure.Pull = GPIO_NOPULL;
if (mchf_display.lcd_cs_pio != NULL)
{
// Deconfigure GPIO PIN for Chip select
GPIO_InitStructure.Pin = mchf_display.lcd_cs;
HAL_GPIO_Init(mchf_display.lcd_cs_pio, &GPIO_InitStructure);
}
}
inline void UiLcdHy28_SpiLcdCsDisable()
{
GPIO_SetBits(mchf_display.lcd_cs_pio, mchf_display.lcd_cs);
}
static inline void UiLcdHy28_SpiLcdCsEnable(void)
{
GPIO_ResetBits(mchf_display.lcd_cs_pio, mchf_display.lcd_cs);
}
#ifdef USE_DISPLAY_PAR
static void UiLcdHy28_ParallelInit(void)
{
MEM_Init();
}
static void UiLcdHy28_ParallelDeInit(void)
{
HAL_SRAM_DeInit(&hsram1);
}
#endif
static void UiLcdHy28_Reset(void)
{
// Reset
GPIO_SetBits(LCD_RESET_PIO, LCD_RESET);
HAL_Delay(1);
GPIO_ResetBits(LCD_RESET_PIO, LCD_RESET);
HAL_Delay(1);
GPIO_SetBits(LCD_RESET_PIO, LCD_RESET);
HAL_Delay(300);
}
static inline void UiLcdHy28_SpiSendByte(uint8_t byte)
{
#ifdef USE_SPI_HAL
uint8_t dummy;
HAL_SPI_TransmitReceive(&hspi2, &byte, &dummy,1,SPI_TIMEOUT);
#else
while ((SPI_DISPLAY->SR & (SPI_FLAG_TXE)) == (uint16_t)RESET) {}
SPI_DISPLAY->DR = byte;
while ((SPI_DISPLAY->SR & (SPI_FLAG_RXNE)) == (uint16_t)RESET) {}
byte = SPI_DISPLAY->DR;
#endif
}
/*
static inline void UiLcdHy28_SpiSendByteFast(uint8_t byte)
{
while ((SPI_DISPLAY->SR & (SPI_FLAG_TXE)) == (uint16_t)RESET) {}
SPI_DISPLAY->DR = byte;
while ((SPI_DISPLAY->SR & (SPI_FLAG_RXNE)) == (uint16_t)RESET) {}
byte = SPI_DISPLAY->DR;
}*/
uint8_t spi_dr_dummy; // used to make sure that DR is being read
static inline void UiLcdHy28_SpiFinishTransfer(void)
{
#ifdef STM32H7
#ifndef USE_SPI_HAL
// we cannot use this with HAL, the "normal" HAL Transmit does check the flags AND resets them (ARGH)
while (__HAL_SPI_GET_FLAG(&hspiDisplay, SPI_SR_EOT) == 0 || __HAL_SPI_GET_FLAG(&hspiDisplay, SPI_SR_EOT) == 0 ) { asm("nop"); }
while (__HAL_SPI_GET_FLAG(&hspiDisplay, SPI_FLAG_RXWNE) != 0 || __HAL_SPI_GET_FLAG(&hspiDisplay, SPI_SR_RXPLVL) != 0 )
{
spi_dr_dummy = SPI_DISPLAY->RXDR;
}
#endif
#else
while ((SPI_DISPLAY->SR & (SPI_FLAG_TXE)) == (uint16_t)RESET) {}
while (SPI_DISPLAY->SR & SPI_FLAG_BSY) {}
if (SPI_DISPLAY->SR & SPI_FLAG_RXNE)
{
spi_dr_dummy = SPI_DISPLAY->DR;
}
#endif
}
static void UiLcdHy28_LcdSpiFinishTransfer(void)
{
UiLcdHy28_SpiFinishTransfer();
GPIO_SetBits(mchf_display.lcd_cs_pio, mchf_display.lcd_cs);
}
uint8_t UiLcdHy28_SpiReadByte()
{
uint8_t dummy = 0;
uint8_t retval = 0;
/* Send a Transmit a dummy byte and Receive Byte through the SPI peripheral */
HAL_SPI_TransmitReceive(&hspi2, &dummy,&retval,1,SPI_TIMEOUT);
return retval;
}
uint8_t UiLcdHy28_SpiReadByteFast()
{
uint8_t retval = 0;
#ifdef USE_SPI_HAL
uint8_t dummy = 0;
HAL_SPI_TransmitReceive(&hspi2, &dummy, &retval,1,SPI_TIMEOUT);
#else
/* Send a Transmit a dummy byte and Receive Byte through the SPI peripheral */
while ((SPI_DISPLAY->SR & (SPI_FLAG_TXE)) == (uint16_t)RESET) {}
SPI_DISPLAY->DR = 0;
while ((SPI_DISPLAY->SR & (SPI_FLAG_RXNE)) == (uint16_t)RESET) {}
retval = SPI_DISPLAY->DR;
#endif
return retval;
}
// TODO: Function Per Controller Group
void UiLcdHy28_WriteIndexSpi(unsigned char index)
{
UiLcdHy28_SpiLcdCsEnable();
mchf_display.WriteIndexSpi_Prepare();
UiLcdHy28_SpiSendByte(0);
UiLcdHy28_SpiSendByte(index);
UiLcdHy28_LcdSpiFinishTransfer();
}
// TODO: Function Per Controller Group
static inline void UiLcdHy28_WriteDataSpiStart(void)
{
UiLcdHy28_SpiLcdCsEnable();
mchf_display.WriteDataSpiStart_Prepare();
}
void UiLcdHy28_WriteDataSpi( unsigned short data)
{
UiLcdHy28_WriteDataSpiStart();
UiLcdHy28_SpiSendByte((data >> 8)); /* Write D8..D15 */
UiLcdHy28_SpiSendByte((data & 0xFF)); /* Write D0..D7 */
UiLcdHy28_LcdSpiFinishTransfer();
}
static inline void UiLcdHy28_WriteDataOnly( unsigned short data)
{
if(UiLcdHy28_SpiDisplayUsed())
{
UiLcdHy28_SpiSendByte((data >> 8)); /* Write D8..D15 */
UiLcdHy28_SpiSendByte((data & 0xFF)); /* Write D0..D7 */
}
else
{
#ifdef USE_DISPLAY_PAR
LCD_RAM = data;
__DMB();
#endif
}
}
#ifdef USE_GFX_RA8875
static inline void UiLcdHy28_WriteDataOnlyRA8875( unsigned short data)
{
LCD_RAM_RA8875 = data;
__DMB();
}
#endif
static inline void UiLcdHy28_WriteData( unsigned short data)
{
if(mchf_display.DeviceCode==0x8875)
{
LCD_RAM_RA8875 = data;
__DMB();
}
else if(UiLcdHy28_SpiDisplayUsed())
{
UiLcdHy28_WriteDataSpiStart();
UiLcdHy28_SpiSendByte((data >> 8)); /* Write D8..D15 */
UiLcdHy28_SpiSendByte((data & 0xFF)); /* Write D0..D7 */
UiLcdHy28_LcdSpiFinishTransfer();
}
else
{
#ifdef USE_DISPLAY_PAR
LCD_RAM = data;
__DMB();
#endif
}
}
unsigned short UiLcdHy28_LcdReadDataSpi()
{
unsigned short value = 0;
uchar y,z;
UiLcdHy28_SpiLcdCsEnable();
UiLcdHy28_SpiSendByte(SPI_START | SPI_RD | SPI_DATA); /* Read: RS = 1, RW = 1 */
UiLcdHy28_SpiReadByte(); /* Dummy read 1 */
y = UiLcdHy28_SpiReadByte(); /* Read D8..D15 */
value = y;
value <<= 8;
z = UiLcdHy28_SpiReadByte(); /* Read D0..D7 */
value |= z;
UiLcdHy28_LcdSpiFinishTransfer();
return value;
}
/**
* @brief writes a controller register in its native width 16 bit or 8bit
* width is controller dependent (RA8875 uses 8bit, all other 16bit)
*/
void UiLcdHy28_WriteReg(unsigned short LCD_Reg, unsigned short LCD_RegValue)
{
mchf_display.WriteReg(LCD_Reg,LCD_RegValue);
}
void UiLcdHy28_WriteReg_ILI(unsigned short LCD_Reg, unsigned short LCD_RegValue)
{
if(UiLcdHy28_SpiDisplayUsed())
{
UiLcdHy28_WriteIndexSpi(LCD_Reg);
UiLcdHy28_WriteDataSpi(LCD_RegValue);
}
else
{
#ifdef USE_DISPLAY_PAR
LCD_REG = LCD_Reg;
__DMB();
LCD_RAM = LCD_RegValue;
__DMB();
#endif
}
}
uint16_t UiLcdHy28_ReadReg(uint16_t LCD_Reg)
{
return mchf_display.ReadReg(LCD_Reg);
}
#ifdef USE_GFX_RA8875
void UiLcdHy28_WriteRegRA8875(unsigned short LCD_Reg, unsigned short LCD_RegValue)
{
UiLcdHy28_RA8875_WaitReady();
LCD_REG_RA8875 = LCD_Reg;
__DMB();
LCD_RAM_RA8875 = LCD_RegValue;
__DMB();
}
uint16_t UiLcdHy28_ReadRegRA8875(uint16_t LCD_Reg)
{
uint16_t retval;
// Write 16-bit Index (then Read Reg)
LCD_REG_RA8875 = LCD_Reg;
// Read 16-bit Reg
__DMB();
retval = LCD_RAM_RA8875;
return retval;
}
#endif
unsigned short UiLcdHy28_ReadRegILI(uint16_t LCD_Reg)
{
uint16_t retval;
if(UiLcdHy28_SpiDisplayUsed())
{
// Write 16-bit Index (then Read Reg)
UiLcdHy28_WriteIndexSpi(LCD_Reg);
// Read 16-bit Reg
retval = UiLcdHy28_LcdReadDataSpi();
}
else
{
#ifdef USE_DISPLAY_PAR
// Write 16-bit Index (then Read Reg)
LCD_REG = LCD_Reg;
// Read 16-bit Reg
__DMB();
retval = LCD_RAM;
#else
retval = 0;
#endif
}
return retval;
}
static void UiLcdHy28_SetCursorA( unsigned short Xpos, unsigned short Ypos )
{
mchf_display.SetCursorA(Xpos, Ypos);
}
static void UiLcdHy28_WriteRAM_Prepare_Index(uint16_t wr_prep_reg)
{
if(mchf_display.DeviceCode==0x8875)
{
LCD_REG_RA8875 = wr_prep_reg;
__DMB();
}
else if(UiLcdHy28_SpiDisplayUsed())
{
UiLcdHy28_WriteIndexSpi(wr_prep_reg);
UiLcdHy28_WriteDataSpiStart();
}
else
{
#ifdef USE_DISPLAY_PAR
LCD_REG = wr_prep_reg;
__DMB();
#endif
}
}
static void UiLcdHy28_WriteRAM_Prepare(void)
{
mchf_display.WriteRAM_Prepare();
}
static void UiLcdHy28_SetActiveWindow(uint16_t XLeft, uint16_t XRight, uint16_t YTop,
uint16_t YBottom)
{
mchf_display.SetActiveWindow(XLeft, XRight, YTop, YBottom);
}
static void UiLcdHy28_BulkWrite(uint16_t* pixel, uint32_t len)
{
// if we are not using SPI DMA, we send the data as it comes
// if we are using SPI DMA, we do this only if we are NOT using SPI
#ifdef USE_SPI_DMA
if(UiLcdHy28_SpiDisplayUsed() == false)
#endif
{
#ifdef USE_GFX_RA8875
if(mchf_display.DeviceCode==0x8875)
{
for (uint32_t i = len; i; i--)
{
UiLcdHy28_WriteDataOnlyRA8875(*(pixel++));
}
}
else
#endif
{
for (uint32_t i = len; i; i--)
{
UiLcdHy28_WriteDataOnly(*(pixel++));
}
}
}
#ifdef USE_SPI_DMA
else
{
for (uint32_t i = 0; i < len; i++)
{
pixel[i] = __REV16(pixel[i]); // reverse byte order;
}
UiLcdHy28_SpiDmaStart((uint8_t*)pixel,len*2);
}
#endif
}
static void UiLcdHy28_FinishWaitBulkWrite(void)
{
if(UiLcdHy28_SpiDisplayUsed()) // SPI enabled?
{
#ifdef USE_SPI_DMA
UiLcdHy28_SpiDmaStop();
#endif
UiLcdHy28_LcdSpiFinishTransfer();
}
}
static void UiLcdHy28_OpenBulkWrite(ushort x, ushort width, ushort y, ushort height)
{
UiLcdHy28_FinishWaitBulkWrite();
UiLcdHy28_SetActiveWindow(x, x + width - 1, y, y + height - 1);
UiLcdHy28_SetCursorA(x, y);
UiLcdHy28_WriteRAM_Prepare();
}
static void UiLcdHy28_CloseBulkWrite(void)
{
#ifdef USE_GFX_RA8875
if(mchf_display.DeviceCode==0x8875)
{
uint16_t MAX_X=mchf_display.MAX_X; uint16_t MAX_Y=mchf_display.MAX_Y;
UiLcdHy28_SetActiveWindow(0, MAX_X - 1, 0, MAX_Y - 1);
UiLcdHy28_WriteReg(0x40, 0);
}
#endif
}
#define PIXELBUFFERSIZE 512
#define PIXELBUFFERCOUNT 2
static __UHSDR_DMAMEM uint16_t pixelbuffer[PIXELBUFFERCOUNT][PIXELBUFFERSIZE];
static uint16_t pixelcount = 0;
static uint16_t pixelbufidx = 0;
static inline void UiLcdHy28_BulkPixel_BufferInit(void)
{
pixelbufidx= (pixelbufidx+1)%PIXELBUFFERCOUNT;
pixelcount = 0;
}
inline void UiLcdHy28_BulkPixel_BufferFlush()
{
UiLcdHy28_BulkWrite(pixelbuffer[pixelbufidx],pixelcount);
UiLcdHy28_BulkPixel_BufferInit();
}
inline void UiLcdHy28_BulkPixel_Put(uint16_t pixel)
{
pixelbuffer[pixelbufidx][pixelcount++] = pixel;
if (pixelcount == PIXELBUFFERSIZE)
{
UiLcdHy28_BulkPixel_BufferFlush();
}
}
// TODO: Not most efficient way, we could use remaining buffer size to judge
// if it will fit without flush and fill accordingly.
inline void UiLcdHy28_BulkPixel_PutBuffer(uint16_t* pixel_buffer, uint32_t len)
{
// We bypass the buffering if in parallel mode
// since as for now, it will not benefit from it.
// this can be changed if someone write DMA code for the parallel
// interface (memory to memory DMA)
if(UiLcdHy28_SpiDisplayUsed()) // SPI enabled?
{
for (uint32_t idx = 0; idx < len; idx++)
{
UiLcdHy28_BulkPixel_Put(pixel_buffer[idx]);
}
}
else
{
UiLcdHy28_BulkWrite(pixel_buffer, len);
}
}
inline void UiLcdHy28_BulkPixel_OpenWrite(ushort x, ushort width, ushort y, ushort height)
{
UiLcdHy28_OpenBulkWrite(x, width,y,height);
UiLcdHy28_BulkPixel_BufferInit();
}
inline void UiLcdHy28_BulkPixel_CloseWrite()
{
UiLcdHy28_BulkPixel_BufferFlush();
UiLcdHy28_CloseBulkWrite();
}
void UiLcdHy28_LcdClear(ushort Color)
{
uint32_t MAX_X=mchf_display.MAX_X; uint32_t MAX_Y=mchf_display.MAX_Y;
UiLcdHy28_OpenBulkWrite(0,MAX_X,0,MAX_Y);
#ifdef USE_SPI_DMA
if(UiLcdHy28_SpiDisplayUsed())
{
int idx;
UiLcdHy28_BulkPixel_BufferInit();
for (idx = 0; idx < MAX_X * MAX_Y; idx++)
{
UiLcdHy28_BulkPixel_Put(Color);
}
UiLcdHy28_BulkPixel_BufferFlush();
}
else
#endif
{
UiLcdHy28_BulkWriteColor(Color,MAX_X * MAX_Y);
}
UiLcdHy28_CloseBulkWrite();
}
#if 1
void UiLcdHy28_DrawColorPoint(uint16_t Xpos,uint16_t Ypos,uint16_t point)
{
mchf_display.DrawColorPoint(Xpos,Ypos,point);
}
void UiLcdHy28_DrawColorPoint_ILI(uint16_t Xpos,uint16_t Ypos,uint16_t point)
{
uint16_t MAX_X=mchf_display.MAX_X; uint16_t MAX_Y=mchf_display.MAX_Y;
if( Xpos < MAX_X && Ypos < MAX_Y )
{
UiLcdHy28_OpenBulkWrite(Xpos,1,Ypos,1);
UiLcdHy28_WriteDataOnly(point);
UiLcdHy28_CloseBulkWrite();
}
}
#endif
void UiLcdHy28_DrawFullRect(uint16_t Xpos, uint16_t Ypos, uint16_t Height, uint16_t Width ,uint16_t color)
{
mchf_display.DrawFullRect(Xpos,Ypos,Height,Width,color);
}
void UiLcdHy28_DrawFullRect_ILI(uint16_t Xpos, uint16_t Ypos, uint16_t Height, uint16_t Width ,uint16_t color)
{
UiLcdHy28_OpenBulkWrite(Xpos, Width, Ypos, Height);
UiLcdHy28_BulkWriteColor(color,(uint32_t)Height * (uint32_t)Width);
UiLcdHy28_CloseBulkWrite();
}
#ifdef USE_GFX_RA8875
void UiLcdHy28_DrawFullRect_RA8875(uint16_t Xpos, uint16_t Ypos, uint16_t Height, uint16_t Width ,uint16_t color)
{
UiLcdRA8875_SetForegroundColor(color);
UiLcdRa8875_WriteReg_16bit(0x91, Xpos); //Horizontal start
UiLcdRa8875_WriteReg_16bit(0x95, Xpos + Width-1); //Horizontal end
UiLcdRa8875_WriteReg_16bit(0x93, Ypos); //Vertical start
UiLcdRa8875_WriteReg_16bit(0x97, Ypos + Height-1); //Vertical end
UiLcdHy28_WriteRegRA8875(0x90, 0xB0); // Fill rectangle
}
void UiLcdHy28_DrawColorPoint_RA8875(uint16_t Xpos,uint16_t Ypos,uint16_t point)
{
uint16_t MAX_X=mchf_display.MAX_X; uint16_t MAX_Y=mchf_display.MAX_Y;
if( Xpos < MAX_X && Ypos < MAX_Y )
{
UiLcdHy28_SetCursorA(Xpos, Ypos);
UiLcdHy28_WriteRegRA8875(0x02, point);
}
}
void UiLcdHy28_RA8875_WaitReady()
{
uint16_t temp;
do {
temp = LCD_REG_RA8875;
} while ((temp & 0x80) == 0x80);
}
void UiLcdRa8875_WriteReg_8bit(uint16_t LCD_Reg, uint8_t LCD_RegValue)
{
UiLcdHy28_WriteRegRA8875(LCD_Reg, LCD_RegValue);
}
void UiLcdRa8875_WriteReg_16bit(uint16_t LCD_Reg, uint16_t LCD_RegValue)
{
UiLcdHy28_WriteRegRA8875(LCD_Reg,LCD_RegValue & 0xff);
UiLcdHy28_WriteRegRA8875(LCD_Reg+1,(LCD_RegValue >> 8) & 0xff);
}
/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ SCROLL STUFF +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/**************************************************************************/
/*!
Sets the scroll mode. This is controlled by bits 6 and 7 of
REG[52h] Layer Transparency Register0 (LTPR0)
Author: The Experimentalist
*/
/**************************************************************************/
void UiLcdRA8875_setScrollMode(uint8_t mode)
{
#define RA8875_LTPR0 0x52//Layer Transparency Register 0
uint8_t temp = UiLcdHy28_ReadReg(RA8875_LTPR0);
temp &= 0x3F; // Clear bits 6 and 7 to zero
switch(mode){ // bit 7,6 of LTPR0
case 0: // 00b : Layer 1/2 scroll simultaneously.
// Do nothing
break;
case 1: // 01b : Only Layer 1 scroll.
temp |= 0x40;
break;
case 2: // 10b : Only Layer 2 scroll.
temp |= 0x80;
break;
case 3: // 11b: Buffer scroll (using Layer 2 as scroll buffer)
temp |= 0xC0;
break;
default:
return; //do nothing
}
LCD_RAM=temp;
__DMB();
}
/**************************************************************************/
/*!
Define a window for perform scroll
Parameters:
XL: x window start left
XR: x window end right
YT: y window start top
YB: y window end bottom
*/
/**************************************************************************/
void UiLcdRA8875_setScrollWindow(int16_t XL,int16_t XR ,int16_t YT ,int16_t YB)
{
#define RA8875_HSSW0 0x38//Horizontal Start Point 0 of Scroll Window
//#define RA8875_HSSW1 0x39//Horizontal Start Point 1 of Scroll Window
#define RA8875_VSSW0 0x3A//Vertical Start Point 0 of Scroll Window
//#define RA8875_VSSW1 0x3B//Vertical Start Point 1 of Scroll Window
#define RA8875_HESW0 0x3C//Horizontal End Point 0 of Scroll Window
//#define RA8875_HESW1 0x3D//Horizontal End Point 1 of Scroll Window
#define RA8875_VESW0 0x3E//Vertical End Point 0 of Scroll Window
//#define RA8875_VESW1 0x3F//Vertical End Point 1 of Scroll Window
UiLcdRa8875_WriteReg_16bit(RA8875_HSSW0,XL);
UiLcdRa8875_WriteReg_16bit(RA8875_HESW0,XR);
UiLcdRa8875_WriteReg_16bit(RA8875_VSSW0,YT);
UiLcdRa8875_WriteReg_16bit(RA8875_VESW0,YB);
}
/**************************************************************************/
/*!
Perform the scroll
*/
/**************************************************************************/
void UiLcdRA8875_scroll(int16_t x,int16_t y)
{
#define RA8875_HOFS0 0x24//Horizontal Scroll Offset Register 0
#define RA8875_HOFS1 0x25//Horizontal Scroll Offset Register 1
#define RA8875_VOFS0 0x26//Vertical Scroll Offset Register 0
#define RA8875_VOFS1 0x27//Vertical Scroll Offset Register 1
UiLcdRa8875_WriteReg_16bit(RA8875_HOFS0,x);
UiLcdRa8875_WriteReg_16bit(RA8875_VOFS0,y);
}
void UiLcdHy28_DrawStraightLine_RA8875(uint16_t x, uint16_t y, uint16_t Length, uint8_t Direction,uint16_t color)
{
/* Drawing Control Registers */
#define LCD_DCR (0x90) /* Draw Line/Circle/Square Control Register */
#define LCD_DLHSR0 (0x91) /* Draw Line/Square Horizontal Start Address Register0 */
#define LCD_DLHSR1 (0x92) /* Draw Line/Square Horizontal Start Address Register1 */
#define LCD_DLVSR0 (0x93) /* Draw Line/Square Vertical Start Address Register0 */
#define LCD_DLVSR1 (0x94) /* Draw Line/Square Vertical Start Address Register1 */
#define LCD_DLHER0 (0x95) /* Draw Line/Square Horizontal End Address Register0 */
#define LCD_DLHER1 (0x96) /* Draw Line/Square Horizontal End Address Register1 */
#define LCD_DLVER0 (0x97) /* Draw Line/Square Vertical End Address Register0 */
#define LCD_DLVER1 (0x98) /* Draw Line/Square Vertical End Address Register1 */
if(Length>0)
{
UiLcdRA8875_SetForegroundColor(color);
uint16_t x_end, y_end;
if (Direction == LCD_DIR_VERTICAL)
{
x_end = x;
y_end = y + Length-1;
}
else
{
x_end = x + Length-1;
y_end = y;
}
if(x_end==x && y_end==y)
UiLcdHy28_DrawColorPoint_RA8875(x,y,color);
else
{
/* Horizontal + vertical start */
UiLcdRa8875_WriteReg_16bit(LCD_DLHSR0, x);
UiLcdRa8875_WriteReg_16bit(LCD_DLVSR0, y);
UiLcdRa8875_WriteReg_16bit(LCD_DLHER0, x_end);
UiLcdRa8875_WriteReg_16bit(LCD_DLVER0, y_end);
UiLcdHy28_WriteRegRA8875(LCD_DCR, 0x80);
}
}
}
#endif
void UiLcdHy28_DrawStraightLineWidth(ushort x, ushort y, ushort Length, uint16_t Width, uchar Direction,ushort color)
{
if(Direction == LCD_DIR_VERTICAL)
{
UiLcdHy28_DrawFullRect(x,y,Length,Width,color);
}
else
{
UiLcdHy28_DrawFullRect(x,y,Width,Length,color);
}
}
void UiLcdHy28_DrawStraightLine(uint16_t x, uint16_t y, uint16_t Length, uint8_t Direction,uint16_t color)
{
mchf_display.DrawStraightLine(x,y,Length,Direction,color);
}
void UiLcdHy28_DrawStraightLine_ILI(uint16_t x, uint16_t y, uint16_t Length, uint8_t Direction,uint16_t color)
{
UiLcdHy28_DrawStraightLineWidth(x, y, Length, 1, Direction, color);
}
void UiLcdHy28_DrawStraightLineDouble(ushort x, ushort y, ushort Length, uchar Direction,ushort color)
{
UiLcdHy28_DrawStraightLineWidth(x, y, Length, 2, Direction, color);
}
void UiLcdHy28_DrawStraightLineTriple(ushort x, ushort y, ushort Length, uchar Direction,ushort color)
{
UiLcdHy28_DrawStraightLineWidth(x, y, Length, 3, Direction, color);
}
void UiLcdHy28_DrawHorizLineWithGrad(ushort x, ushort y, ushort Length,ushort gradient_start)
{
uint32_t i = 0,j = 0;
ushort k = gradient_start;
UiLcdHy28_OpenBulkWrite(x,Length,y,1);
UiLcdHy28_BulkPixel_BufferInit();
for(i = 0; i < Length; i++)
{
UiLcdHy28_BulkPixel_Put(RGB(k,k,k));
j++;
if(j == GRADIENT_STEP)
{
if(i < (Length/2))
k += (GRADIENT_STEP/2);
else
k -= (GRADIENT_STEP/2);
j = 0;
}
}
UiLcdHy28_BulkPixel_BufferFlush();
UiLcdHy28_CloseBulkWrite();
}
void UiLcdHy28_DrawEmptyRect(ushort Xpos, ushort Ypos, ushort Height, ushort Width,ushort color)
{
UiLcdHy28_DrawStraightLine(Xpos, (Ypos), Width, LCD_DIR_HORIZONTAL,color);
UiLcdHy28_DrawStraightLine(Xpos, Ypos, Height, LCD_DIR_VERTICAL,color);
UiLcdHy28_DrawStraightLine((Xpos + Width), Ypos, (Height + 1), LCD_DIR_VERTICAL,color);
UiLcdHy28_DrawStraightLine(Xpos, (Ypos + Height), Width, LCD_DIR_HORIZONTAL,color);
}
void UiLcdHy28_DrawBottomButton(ushort Xpos, ushort Ypos, ushort Height, ushort Width,ushort color)
{
UiLcdHy28_DrawStraightLine(Xpos, (Ypos), Width, LCD_DIR_HORIZONTAL,color);
UiLcdHy28_DrawStraightLine(Xpos, Ypos, Height,LCD_DIR_VERTICAL, color);
UiLcdHy28_DrawStraightLine((Xpos + Width), Ypos,Height,LCD_DIR_VERTICAL, color);
}
static void UiLcdHy28_BulkWriteColor(uint16_t Color, uint32_t len)
{
#ifdef USE_SPI_DMA
if(UiLcdHy28_SpiDisplayUsed())
{
int idx;
UiLcdHy28_BulkPixel_BufferInit();
for (idx = 0; idx < len; idx++)
{
UiLcdHy28_BulkPixel_Put(Color);
}
UiLcdHy28_BulkPixel_BufferFlush();
}
else
#endif
{
uint32_t i = len;
#ifdef USE_GFX_RA8875
if(mchf_display.DeviceCode==0x8875)
{
for (uint32_t i = len; i; i--)
{
UiLcdHy28_WriteDataOnlyRA8875(Color);
}
}
else
#endif
{
for (; i; i--)
{
UiLcdHy28_WriteDataOnly(Color);
}
}
}
}
#ifdef USE_8bit_FONT
static void UiLcdHy28_DrawChar_8bit(ushort x, ushort y, char symb,ushort Color, ushort bkColor,const sFONT *cf)
{
const uint16_t charIdx = (symb >= 0x20 && symb < cf->maxCode)? cf->offsetTable[symb - cf->firstCode] : 0xFFFF;
const uint8_t Font_H = cf->Height;
symbolData_t* sym_ptr = charIdx == 0xFFFF? NULL:((symbolData_t*)cf->table)+charIdx;
const uint8_t Font_W = sym_ptr == NULL? cf->Width:sym_ptr->width;
const uint16_t charSpacing = cf->Spacing;
UiLcdHy28_BulkPixel_OpenWrite(x, Font_W+charSpacing, y, Font_H);
if(sym_ptr == NULL) // NON EXISTING SYMBOL
{
for(int cntrY=0;cntrY < Font_H; cntrY++)
{
for(int cntrX=0; cntrX < Font_W; cntrX++)
{
UiLcdHy28_BulkPixel_Put(Color);
}
for(int cntrX=0; cntrX < charSpacing; cntrX++)
{
UiLcdHy28_BulkPixel_Put(bkColor);
}
}
}
else
{
//gray shaded font type
const int32_t ColBG_R=(bkColor>>11)&0x1f;
const int32_t ColBG_G=(bkColor>>5)&0x3f;
const int32_t ColBG_B=bkColor&0x1f;
const int32_t ColFG_R=((Color>>11)&0x1f) - ColBG_R; //decomposition of 16 bit color data into channels
const int32_t ColFG_G=((Color>>5)&0x3f) - ColBG_G;
const int32_t ColFG_B=(Color&0x1f) - ColBG_B;
uint8_t *FontData=(uint8_t*)sym_ptr->data;
for(uint8_t cntrY=0;cntrY<Font_H;cntrY++)
{
for(uint8_t cntrX=0;cntrX<Font_W;cntrX++)
{
uint32_t pixel;
uint8_t FontD;
if(cntrY<Font_H)
{
FontD=*FontData++; //get one point from bitmap
}
else
{
FontD=0;
}
if(FontD==0)
{
pixel=bkColor;
}
else
{
//shading the foreground colour
int32_t ColFG_Ro=(ColFG_R*FontD)>>8;
int32_t ColFG_Go=(ColFG_G*FontD)>>8;
int32_t ColFG_Bo=(ColFG_B*FontD)>>8;
ColFG_Ro+=ColBG_R;
ColFG_Go+=ColBG_G;
ColFG_Bo+=ColBG_B;
pixel=(ColFG_Ro<<11)|(ColFG_Go<<5)|ColFG_Bo; //assembly of destination colour
}
UiLcdHy28_BulkPixel_Put(pixel);
}
// add spacing behind the character data
for(int n=Font_W; n < Font_W + charSpacing ; n++)
{
UiLcdHy28_BulkPixel_Put(bkColor);
}
}
}
UiLcdHy28_BulkPixel_BufferFlush();
// flush all not yet transferred pixel to display.
UiLcdHy28_CloseBulkWrite();
}
#endif
static void UiLcdHy28_DrawChar_1bit(ushort x, ushort y, char symb,ushort Color, ushort bkColor,const sFONT *cf)
{
uint8_t *ch = (uint8_t *)cf->table;
// we get the address of the begin of the character table
// we support one or two byte long character definitions
// anything wider than 8 pixels uses two bytes
ch+=(symb - 32) * cf->Height* ((cf->Width>8) ? 2 : 1 );
UiLcdHy28_OpenBulkWrite(x,cf->Width,y,cf->Height);
UiLcdHy28_BulkPixel_BufferInit();
// we now get the pixel information line by line
for(uint32_t i = 0; i < cf->Height; i++)
{
uint32_t line_data; // stores pixel data for a character line, left most pixel is MSB
// we read the current pixel line data (1 or 2 bytes)
if(cf->Width>8)
{
if (cf->Width <= 12)
{
// small fonts <= 12 pixel width have left most pixel as MSB
// we have to reverse that
line_data = ch[i*2+1]<<24;
line_data |= ch[i*2] << 16;
}
else
{
uint32_t interim;
interim = ch[i*2+1]<<8;
interim |= ch[i*2];
line_data = __RBIT(interim); // rbit reverses a 32bit value bitwise
}
}
else
{
// small fonts have left most pixel as MSB
// we have to reverse that
line_data = ch[i] << 24; // rbit reverses a 32bit value bitwise
}
// now go through the data pixel by pixel
// and find out if it is background or foreground
// then place pixel color in buffer
uint32_t mask = 0x80000000U; // left most pixel aka MSB 32 bit mask
for(uint32_t j = 0; j < cf->Width; mask>>=1, j++)
{
UiLcdHy28_BulkPixel_Put((line_data & mask) != 0 ? Color : bkColor);
// we shift the mask in the for loop to the right one by one
}
}
UiLcdHy28_BulkPixel_BufferFlush();
// flush all not yet transferred pixel to display.
UiLcdHy28_CloseBulkWrite();
}
static void UiLcdHy28_DrawHorizChar_1bit(ushort x, ushort y, char symb,ushort Color, ushort bkColor,const sFONT *cf)
{
uint8_t *ch = (uint8_t *)cf->table;
// we get the address of the begin of the character table
// we support one or two byte long character definitions
// anything wider than 8 pixels uses two bytes
ch+=(symb - 32) * cf->Height* ((cf->Width>8) ? 2 : 1 );
UiLcdHy28_OpenBulkWrite(x,cf->Width,y,cf->Height);
UiLcdHy28_BulkPixel_BufferInit();
uint8_t matrixA[8][8];
uint8_t matrixB[8][8];
// we now get the pixel information line by line
for(uint32_t i = 0; i < cf->Height; i++)
{
uint32_t line_data; // stores pixel data for a character line, left most pixel is MSB
// we read the current pixel line data (1 or 2 bytes)
if(cf->Width>8)
{
if (cf->Width <= 12)
{
// small fonts <= 12 pixel width have left most pixel as MSB
// we have to reverse that
line_data = ch[i*2+1]<<24;
line_data |= ch[i*2] << 16;
}
else
{
uint32_t interim;
interim = ch[i*2+1]<<8;
interim |= ch[i*2];
line_data = __RBIT(interim); // rbit reverses a 32bit value bitwise
}
}
else
{
// small fonts have left most pixel as MSB
// we have to reverse that
line_data = ch[i] << 24; // rbit reverses a 32bit value bitwise
}
// now go through the data pixel by pixel
// and find out if it is background or foreground
// then place pixel color in buffer
uint32_t mask = 0x80000000U; // left most pixel aka MSB 32 bit mask
// for(uint32_t j = 0; j < cf->Width; mask>>=1, j++)
// {
// UiLcdHy28_BulkPixel_Put((line_data & mask) != 0 ? Color : bkColor);
// // we shift the mask in the for loop to the right one by one
// }
for(uint32_t j = 0; j < cf->Width; mask>>=1, j++)
{
matrixA [i][j] = !(line_data & mask) ? 0 : 1;
}
}
// turn around the simbol
for(uint8_t i = 0; i < cf->Height; i++)
{
for(uint32_t j = 0; j < cf->Width; j++)
{
matrixB[cf->Width-1-j][i] = matrixA[i][j];
}
}
// output
for(uint8_t i = 0; i < cf->Height; i++)
{
for(uint32_t j = 0; j < cf->Width; j++)
{
UiLcdHy28_BulkPixel_Put((matrixB[i][j]) != 0 ? Color : bkColor);
}
}
UiLcdHy28_BulkPixel_BufferFlush();
// flush all not yet transferred pixel to display.
UiLcdHy28_CloseBulkWrite();
}
//void UiLcdHy28_DrawChar(ushort x, ushort y, char symb,ushort Color, ushort bkColor,const sFONT *cf)
void UiLcdHy28_DrawChar(ushort x, ushort y, char symb,ushort Color, ushort bkColor,const sFONT *cf, bool HorizDraw)
{
#ifdef USE_8bit_FONT
switch(cf->BitCount)
{
case 1: //1 bit font (basic type)
#endif
// UiLcdHy28_DrawChar_1bit(x, y, symb, Color, bkColor, cf);
if(!HorizDraw)
{
UiLcdHy28_DrawChar_1bit(x, y, symb, Color, bkColor, cf);
}
else
{
UiLcdHy28_DrawHorizChar_1bit(x, y, symb, Color, bkColor, cf);
}
// break;
#ifdef USE_8bit_FONT
break;
case 8: //8 bit grayscaled font
UiLcdHy28_DrawChar_8bit(x, y, symb, Color, bkColor, cf);
}
#endif
}
const sFONT *UiLcdHy28_Font(uint8_t font)
{
// if we have an illegal font number, we return the first font
return fontList[font < fontCount ? font : 0];
}
//static void UiLcdHy28_PrintTextLen(uint16_t XposStart, uint16_t YposStart, const char *str, const uint16_t len, const uint32_t clr_fg, const uint32_t clr_bg,uchar font)
static void UiLcdHy28_PrintTextLen(uint16_t XposStart, uint16_t YposStart, const char *str, const uint16_t len, uint32_t clr_fg, uint32_t clr_bg,uchar font)
{
uint32_t MAX_X=mchf_display.MAX_X; uint32_t MAX_Y=mchf_display.MAX_Y;
const sFONT *cf = UiLcdHy28_Font(font);
int8_t Xshift = cf->Width - ((cf->Width == 8 && cf->Height == 8)?1:0);
// Mod the 8x8 font - the shift is too big
uint16_t XposCurrent = XposStart;
uint16_t YposCurrent = YposStart;
if (str != NULL)
{
for (uint16_t idx = 0; idx < len; idx++)
{
uint8_t TempChar = *str++;
// UiLcdHy28_DrawChar(XposCurrent, YposCurrent, TempChar,clr_fg,clr_bg,cf);
UiLcdHy28_DrawChar(XposCurrent, YposCurrent, TempChar,clr_fg,clr_bg,cf,0);
if(XposCurrent < (MAX_X - Xshift))
{
XposCurrent += Xshift;
}
else if (YposCurrent < (MAX_Y - cf->Height))
{
XposCurrent = XposStart;
YposCurrent += cf->Height;
}
else
{
XposCurrent = XposStart;
YposCurrent = XposStart;
}
}
}
}
//void UiLcdHy28_PrintTextVertLen(uint16_t XposStart, uint16_t YposStart, const char *str, const uint16_t len, const uint32_t clr_fg, const uint32_t clr_bg)
void UiLcdHy28_PrintTextVertLen(uint16_t XposStart, uint16_t YposStart, const char *str, const uint16_t len, uint32_t clr_fg, uint32_t clr_bg)
{
const sFONT *cf = UiLcdHy28_Font(4);
int8_t Yshift = cf->Width - 1;
uint16_t XposCurrent = XposStart;
uint16_t YposCurrent = YposStart;
if (str != NULL)
{
for (uint16_t idx = 0; idx < len; idx++)
{
uint8_t TempChar = *str++;
UiLcdHy28_DrawChar(XposCurrent, YposCurrent, TempChar,clr_fg,clr_bg,cf,1);
YposCurrent -= Yshift;
}
}
}
/**
* @returns pointer to next end of line or next end of string character
*/
static const char * UiLcdHy28_StringGetLine(const char* str)
{
const char* retval;
for (retval = str; *retval != '\0' && *retval != '\n'; retval++ );
return retval;
}
/**
* @brief Print multi-line text. New lines start right at XposStart
* @returns next unused Y line (i.e. the Y coordinate just below the last printed text line).
*/
//uint16_t UiLcdHy28_PrintText(uint16_t XposStart, uint16_t YposStart, const char *str,const uint32_t clr_fg, const uint32_t clr_bg,uchar font)
uint16_t UiLcdHy28_PrintText(uint16_t XposStart, uint16_t YposStart, const char *str, uint32_t clr_fg, uint32_t clr_bg,uchar font)
{
const sFONT *cf = UiLcdHy28_Font(font);
int8_t Yshift = cf->Height;
uint16_t YposCurrent = YposStart;
if (str != NULL)
{
const char* str_start = str;
for (const char* str_end = UiLcdHy28_StringGetLine(str_start); str_start != str_end; str_end = UiLcdHy28_StringGetLine(str_start))
{
UiLcdHy28_PrintTextLen(XposStart, YposCurrent, str_start, str_end - str_start, clr_fg, clr_bg, font);
YposCurrent += Yshift;
if (*str_end == '\n')
{
// next character after line break
str_start = str_end + 1;
}
else
{
// last character in string
//str_start = str_end;
break; //this line was added to prevent a kind of race condition causing random newline print of characters (for example in CW decoder). It needs testing.
//Feb 2018 SP9BSL
}
}
}
return YposCurrent;
}
uint16_t UiLcdHy28_TextHeight(uint8_t font)
{
const sFONT *cf = UiLcdHy28_Font(font);
return cf->Height;
}
#if 0
/**
* @returns pixel width of a given char (only used pixel!)
*/
uint16_t UiLcdHy28_CharWidth(const char c, uint8_t font)
{
const sFONT *cf = UiLcdHy28_Font(font);
uint16_t retval;
#ifdef USE_8bit_FONT
switch(cf->BitCount)
{
case 1: //1 bit font (basic type)
#endif
retval = UiLcdHy28_CharWidth_1bit(c, cf);
#ifdef USE_8bit_FONT
break;
case 8: //8 bit grayscaled font
retval = UiLcdHy28_CharWidth_8bit(c, cf);
break;
}
#endif
return retval;
}
#endif
/**
* @returns pixelwidth of a text of given length
*/
static uint16_t UiLcdHy28_TextWidthLen(const char *str_start, uint16_t len, uint8_t font)
{
const sFONT *cf = UiLcdHy28_Font(font);
int8_t char_width = (cf->Width + cf->Spacing) - ((cf->Width == 8 && cf->Height == 8)?1:0);
return (str_start != NULL) ? (len * char_width) : 0;
}
/**
* @returns pixelwidth of a text of given length or 0 for NULLPTR
*/
uint16_t UiLcdHy28_TextWidth(const char *str_start, uchar font)
{
return (str_start != NULL) ?
UiLcdHy28_TextWidthLen(str_start,strlen(str_start), font)
:
0;
}
//static void UiLcdHy28_PrintTextRightLen(uint16_t Xpos, uint16_t Ypos, const char *str, uint16_t len, const uint32_t clr_fg, const uint32_t clr_bg,uint8_t font)
static void UiLcdHy28_PrintTextRightLen(uint16_t Xpos, uint16_t Ypos, const char *str, uint16_t len, uint32_t clr_fg, uint32_t clr_bg,uint8_t font)
{
uint16_t Xwidth = UiLcdHy28_TextWidthLen(str, len, font);
if (Xpos < Xwidth )
{
Xpos = 0; // TODO: Overflow is not handled too well, just start at beginning of line and draw over the end.
}
else
{
Xpos -= Xwidth;
}
UiLcdHy28_PrintTextLen(Xpos, Ypos, str, len, clr_fg, clr_bg, font);
}
/**
* @brief Print multi-line text right aligned. New lines start right at XposStart
* @returns next unused Y line (i.e. the Y coordinate just below the last printed text line).
*/
//uint16_t UiLcdHy28_PrintTextRight(uint16_t XposStart, uint16_t YposStart, const char *str,const uint32_t clr_fg, const uint32_t clr_bg,uint8_t font)
uint16_t UiLcdHy28_PrintTextRight(uint16_t XposStart, uint16_t YposStart, const char *str, uint32_t clr_fg, uint32_t clr_bg,uint8_t font)
{
// this code is a full clone of the PrintText function, with exception of the function call to PrintTextRightLen
const sFONT *cf = UiLcdHy28_Font(font);
int8_t Yshift = cf->Height;
uint16_t YposCurrent = YposStart;
if (str != NULL)
{
const char* str_start = str;
for (const char* str_end = UiLcdHy28_StringGetLine(str_start); str_start != str_end; str_end = UiLcdHy28_StringGetLine(str_start))
{
UiLcdHy28_PrintTextRightLen(XposStart, YposCurrent, str_start, str_end - str_start, clr_fg, clr_bg, font);
YposCurrent += Yshift;
if (*str_end == '\n')
{
// next character after line break
str_start = str_end + 1;
}
else
{
// last character in string
str_start = str_end;
}
}
}
return YposCurrent;
}
static void UiLcdHy28_PrintTextCenteredLen(const uint16_t XposStart,const uint16_t YposStart,const uint16_t bbW,const char* str, uint16_t len ,uint32_t clr_fg,uint32_t clr_bg,uint8_t font)
{
const uint16_t bbH = UiLcdHy28_TextHeight(font);
const uint16_t txtW = UiLcdHy28_TextWidthLen(str, len, font);
const uint16_t bbOffset = txtW>bbW?0:((bbW - txtW)+1)/2;
// we draw the part of the box not used by text.
if (bbOffset)
{
UiLcdHy28_DrawFullRect(XposStart,YposStart,bbH,bbOffset,clr_bg);
}
UiLcdHy28_PrintTextLen((XposStart + bbOffset),YposStart,str, len, clr_fg,clr_bg,font);
// if the text is smaller than the box, we need to draw the end part of the
// box
if (txtW<bbW)
{
UiLcdHy28_DrawFullRect(XposStart+txtW+bbOffset,YposStart,bbH,bbW-(bbOffset+txtW),clr_bg);
}
}
/*
* Print text centered inside the bounding box. Using '\n' to print multline text
*/
uint16_t UiLcdHy28_PrintTextCentered(const uint16_t XposStart,const uint16_t YposStart,const uint16_t bbW,const char* str,uint32_t clr_fg,uint32_t clr_bg,uint8_t font)
{
// this code is a full clone of the PrintText function, with exception of the function call to PrintTextCenteredLen
const sFONT *cf = UiLcdHy28_Font(font);
int8_t Yshift = cf->Height;
uint16_t YposCurrent = YposStart;
if (str != NULL)
{
const char* str_start = str;
for (const char* str_end = UiLcdHy28_StringGetLine(str_start); str_start != str_end; str_end = UiLcdHy28_StringGetLine(str_start))
{
UiLcdHy28_PrintTextCenteredLen(XposStart, YposCurrent, bbW, str_start, str_end - str_start, clr_fg, clr_bg, font);
YposCurrent += Yshift;
if (*str_end == '\n')
{
// next character after line break
str_start = str_end + 1;
}
else
{
// last character in string
str_start = str_end;
}
}
}
return YposCurrent;
}
/*********************************************************************
*
* Controller Specific Functions Go Here (
* These functions are used via mchf_display.func(...)
* Each controller gets one single section here, guarded with USE_GFX_...
*
* *******************************************************************/
#ifdef USE_GFX_RA8875
static void UiLcdHy28_SetCursorA_RA8875( unsigned short Xpos, unsigned short Ypos )
{
UiLcdRa8875_WriteReg_16bit(0x46, Xpos);
UiLcdRa8875_WriteReg_16bit(0x48, Ypos);
}
static void UiLcdHy28_WriteRAM_Prepare_RA8875(void)
{
UiLcdHy28_WriteRAM_Prepare_Index(0x02);
}
void UiLcdRA8875_SetForegroundColor(uint16_t Color)
{
UiLcdRa8875_WriteReg_8bit(0x63, (uint16_t) (Color >> 11)); /* ra8875_red */
UiLcdRa8875_WriteReg_8bit(0x64, (uint16_t) (Color >> 5)); /* ra8875_green */
UiLcdRa8875_WriteReg_8bit(0x65, (uint16_t) (Color)); /* ra8875_blue */
}
static void UiLcdHy28_SetActiveWindow_RA8875(uint16_t XLeft, uint16_t XRight, uint16_t YTop,
uint16_t YBottom)
{
/* setting active window X */
UiLcdRa8875_WriteReg_16bit(0x30, XLeft);
UiLcdRa8875_WriteReg_16bit(0x34, XRight);
/* setting active window Y */
UiLcdRa8875_WriteReg_16bit(0x32, YTop);
UiLcdRa8875_WriteReg_16bit(0x36, YBottom);
}
static uint16_t UiLcdHy28_ReadDisplayId_RA8875(void)
{
uint16_t retval =0;
uint16_t reg_63_val=UiLcdHy28_ReadReg(0x63);
uint16_t reg_64_val=UiLcdHy28_ReadReg(0x64);
uint16_t reg_65_val=UiLcdHy28_ReadReg(0x65);
uint16_t reg_88_val=UiLcdHy28_ReadReg(0x88);
uint16_t reg_89_val=UiLcdHy28_ReadReg(0x89);
if((reg_63_val==0x1f)&&
(reg_64_val==0x3f)&&
(reg_65_val==0x1f)&&
(reg_88_val==0x07)&&
(reg_89_val==0x03))
{
retval=0x8875;
mchf_display.reg_info = &ra8875_regs;
}
return retval;
}
#endif
#ifdef USE_GFX_ST7796
static uint16_t UiLcdHy28_ReadDisplayId_ST7796()
{
uint16_t retval = 0x7796;
#ifdef USE_DISPLAY_PAR
// we can't read the id from SPI if it is the dumb RPi SPI
if (mchf_display.use_spi == false)
{
retval = UiLcdHy28_ReadReg(0xd3);
retval = LCD_RAM; //first dummy read
retval = (LCD_RAM&0xff)<<8;
retval |=LCD_RAM&0xff;
}
#endif
switch (retval)
{
case 0x7796: //ST7796
mchf_display.reg_info = &ST7796_regs;
break;
default:
retval = 0;
}
return retval;
}
#endif
#ifdef USE_GFX_ILI9486
static uint16_t UiLcdHy28_ReadDisplayId_ILI9486(void)
{
uint16_t retval = 0x9486;
#ifdef USE_DISPLAY_PAR
// we can't read the id from SPI if it is the dumb RPi SPI
if (mchf_display.use_spi == false)
{
retval = UiLcdHy28_ReadReg(0xd3);
retval = LCD_RAM; //first dummy read
retval = (LCD_RAM&0xff)<<8;
retval |=LCD_RAM&0xff;
}
#endif
switch (retval)
{
case 0x9486:
case 0x9488: // ILI9486 - Parallel & Serial interface
mchf_display.reg_info = &ili9486_regs;
break;
default:
retval = 0;
}
return retval;
}
static inline void UiLcdHy28_WriteDataSpiStart_Prepare_ILI9486(void)
{
GPIO_SetBits(LCD_RS_PIO, LCD_RS);
}
void UiLcdHy28_WriteIndexSpi_Prepare_ILI9486()
{
GPIO_ResetBits(LCD_RS_PIO, LCD_RS);
}
static void UiLcdHy28_SetCursorA_ILI9486( unsigned short Xpos, unsigned short Ypos )
{
}
static void UiLcdHy28_WriteRAM_Prepare_ILI9486(void)
{
UiLcdHy28_WriteRAM_Prepare_Index(0x2c);
}
static void UiLcdHy28_SetActiveWindow_ILI9486(uint16_t XLeft, uint16_t XRight, uint16_t YTop,
uint16_t YBottom)
{
UiLcdHy28_WriteReg(0x2a,XLeft>>8);
UiLcdHy28_WriteData(XLeft&0xff);
UiLcdHy28_WriteData((XRight)>>8);
UiLcdHy28_WriteData((XRight)&0xff);
UiLcdHy28_WriteReg(0x2b,YTop>>8);
UiLcdHy28_WriteData(YTop&0xff);
UiLcdHy28_WriteData((YBottom)>>8);
UiLcdHy28_WriteData((YBottom)&0xff);
}
#endif
#ifdef USE_GFX_ILI932x
static uint16_t UiLcdHy28_ReadDisplayId_ILI932x(void)
{
uint16_t retval = UiLcdHy28_ReadReg(0x00);
switch (retval)
{
case 0x9320: // HY28A - SPI interface only (ILI9320 controller)
mchf_display.reg_info = &ili9320_regs;
break;
case 0x5408: // HY28A - Parallel interface only (SPFD5408B controller)
mchf_display.reg_info = &spdfd5408b_regs;
break;
case 0x9325:
case 0x9328: // HY28B - Parallel & Serial interface - latest model (ILI9325 & ILI9328 controller)
mchf_display.reg_info = &ili932x_regs;
break;
default:
retval = 0;
}
return retval;
}
static inline void UiLcdHy28_WriteDataSpiStart_Prepare_ILI932x(void)
{
UiLcdHy28_SpiSendByte(SPI_START | SPI_WR | SPI_DATA); /* Write : RS = 1, RW = 0 */
}
void UiLcdHy28_WriteIndexSpi_Prepare_ILI932x()
{
UiLcdHy28_SpiSendByte(SPI_START | SPI_WR | SPI_INDEX); /* Write : RS = 0, RW = 0 */
}
static void UiLcdHy28_SetCursorA_ILI932x( unsigned short Xpos, unsigned short Ypos )
{
UiLcdHy28_WriteReg(0x20, Ypos );
UiLcdHy28_WriteReg(0x21, Xpos );
}
static void UiLcdHy28_WriteRAM_Prepare_ILI932x(void)
{
UiLcdHy28_WriteRAM_Prepare_Index(0x22);
}
static void UiLcdHy28_SetActiveWindow_ILI932x(uint16_t XLeft, uint16_t XRight, uint16_t YTop,
uint16_t YBottom)
{
UiLcdHy28_WriteReg(0x52, XLeft); // Horizontal GRAM Start Address
UiLcdHy28_WriteReg(0x53, XRight); // Horizontal GRAM End Address -1
UiLcdHy28_WriteReg(0x50, YTop); // Vertical GRAM Start Address
UiLcdHy28_WriteReg(0x51, YBottom); // Vertical GRAM End Address -1
}
#endif
#ifdef USE_GFX_ILI9341
static uint8_t UiLcdHy28_ReadIndexedReg8_ILI9341(uint8_t cmd, uint8_t index)
{
uint8_t regval = 0;
// SPI interface
UiLcdHy28_TouchscreenStartSpiTransfer();
index = 0x10 + (index & 0x0F);
UiLcdHy28_WriteReg_ILI(0xD9, index);
regval = UiLcdHy28_ReadRegILI(cmd) & 0xFF;
UiLcdHy28_LcdSpiFinishTransfer();
return regval;
}
static uint16_t UiLcdHy28_ReadDisplayId_ILI9341()
{
uint16_t retval = 0;//0x9341;
// SPI interface
uint8_t regval_2 = 0;
uint8_t regval_3 = 0;
regval_2 = UiLcdHy28_ReadIndexedReg8_ILI9341(0xD3, 2);
regval_3 = UiLcdHy28_ReadIndexedReg8_ILI9341(0xD3, 3);
if ((regval_2 == 0x93) && (regval_3 == 0x41))
{
retval = 0x9341;
mchf_display.reg_info = &ili9341_regs;
}
return retval;
}
static inline void UiLcdHy28_WriteDataSpiStart_Prepare_ILI9341()
{
GPIO_SetBits(LCD_RS_PIO, LCD_RS);
}
void UiLcdHy28_WriteIndexSpi_Prepare_ILI9341()
{
GPIO_ResetBits(LCD_RS_PIO, LCD_RS);
}
static void UiLcdHy28_SetCursorA_ILI9341( unsigned short Xpos, unsigned short Ypos )
{
// Nothing to do
}
static void UiLcdHy28_WriteRAM_Prepare_ILI9341()
{
UiLcdHy28_WriteRAM_Prepare_Index(0x2c);
}
static void UiLcdHy28_SetActiveWindow_ILI9341(uint16_t XLeft, uint16_t XRight, uint16_t YTop,
uint16_t YBottom)
{
UiLcdHy28_WriteReg(0x2a,XLeft>>8);
UiLcdHy28_WriteData(XLeft&0xff);
UiLcdHy28_WriteData((XRight)>>8);
UiLcdHy28_WriteData((XRight)&0xff);
UiLcdHy28_WriteReg(0x2b,YTop>>8);
UiLcdHy28_WriteData(YTop&0xff);
UiLcdHy28_WriteData((YBottom)>>8);
UiLcdHy28_WriteData((YBottom)&0xff);
}
#endif
static void UiLcdHy28_SendRegisters(const RegisterValueSetInfo_t* reg_info)
{
for (uint16_t idx = 0; idx < reg_info->size; idx++)
{
switch(reg_info->addr[idx].reg)
{
case REGVAL_DELAY:
HAL_Delay(reg_info->addr[idx].val);
break;
case REGVAL_DATA:
UiLcdHy28_WriteData(reg_info->addr[idx].val);
break;
default:
// TODO: Decide how we handle 16 vs. 8 bit writes here
// I would propose either per register setting or per register set setting
UiLcdHy28_WriteReg(reg_info->addr[idx].reg, reg_info->addr[idx].val);
}
}
}
#ifdef USE_GFX_SSD1289
static uint16_t UiLcdHy28_ReadDisplayId_SSD1289(void)
{
uint16_t retval = UiLcdHy28_ReadReg(0x00);
switch (retval)
{
case 0x8989: // HY28A - SPI interface only (ILI9320 controller)
mchf_display.reg_info = &ssd1289_regs;
break;
default:
retval = 0;
}
return retval;
}
static void UiLcdHy28_SetActiveWindow_SSD1289(uint16_t XLeft, uint16_t XRight, uint16_t YTop,
uint16_t YBottom)
{
UiLcdHy28_WriteReg(0x44, XRight << 8 | XLeft); // Horizontal GRAM Start Address
UiLcdHy28_WriteReg(0x45, YTop); // Horizontal GRAM End Address -1
UiLcdHy28_WriteReg(0x45, YTop); // Vertical GRAM Start Address
UiLcdHy28_WriteReg(0x46, YBottom); // Vertical GRAM End Address -1
}
static void UiLcdHy28_SetCursorA_SSD1289( unsigned short Xpos, unsigned short Ypos )
{
UiLcdHy28_WriteReg(0x4e, Ypos );
UiLcdHy28_WriteReg(0x4f, Xpos );
}
#endif
// ATTENTION: THIS LIST NEEDS TO HAVE A SPECIFIC ORDER:
// FIRST ALL DETECTABLE DISPLAYS THEN AT MOST ONE SINGLE UNDETECTABLE DISPLAY
//
// IF A DISPLAY DOES NOT HAVE A REAL DETECTION ROUTINE
// ALL DISPLAYS BEHIND THIS ONE IN THIS LIST WILL NEVER BE TESTED!
//
// Please note that the CubeMX generated code for FSMC/FMC init
// no multiple on/off cycles permits. Small change in fmc.c/fsmc.c fixes that
// so if during a new STM controller port the second or third enabled parallel display
// in the list below is not working, check if the code in these files
// and see if Initialized and DeInitialized variables are BOTH (!) set accordingly.
// When one is set, the other has to be cleared.
//
const uhsdr_display_info_t display_infos[] = {
{
DISPLAY_NONE, "No Display", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0
},
#ifdef USE_DISPLAY_PAR
#ifdef USE_GFX_ILI9486
{
DISPLAY_ILI9486_PARALLEL, "ILI9486 Para.",
.ReadDisplayId = UiLcdHy28_ReadDisplayId_ILI9486,
.SetActiveWindow = UiLcdHy28_SetActiveWindow_ILI9486,
.SetCursorA = UiLcdHy28_SetCursorA_ILI9486,
.WriteRAM_Prepare = UiLcdHy28_WriteRAM_Prepare_ILI9486,
.WriteReg = UiLcdHy28_WriteReg_ILI,
.ReadReg = UiLcdHy28_ReadRegILI,
.DrawStraightLine = UiLcdHy28_DrawStraightLine_ILI,
.DrawFullRect = UiLcdHy28_DrawFullRect_ILI,
.DrawColorPoint = UiLcdHy28_DrawColorPoint_ILI,
},
#endif
#ifdef USE_GFX_ST7796
{ //this display has the same registers and functions as ILI9486, the only difference is controller ID
DISPLAY_ST7796_PARALLEL, "ST7796 Para.",
.ReadDisplayId = UiLcdHy28_ReadDisplayId_ST7796,
.SetActiveWindow = UiLcdHy28_SetActiveWindow_ILI9486,
.SetCursorA = UiLcdHy28_SetCursorA_ILI9486,
.WriteRAM_Prepare = UiLcdHy28_WriteRAM_Prepare_ILI9486,
.WriteReg = UiLcdHy28_WriteReg_ILI,
.ReadReg = UiLcdHy28_ReadRegILI,
.DrawStraightLine = UiLcdHy28_DrawStraightLine_ILI,
.DrawFullRect = UiLcdHy28_DrawFullRect_ILI,
.DrawColorPoint = UiLcdHy28_DrawColorPoint_ILI,
},
#endif
#ifdef USE_GFX_ILI932x
{
DISPLAY_HY28B_PARALLEL, "HY28A/B Para.",
.ReadDisplayId = UiLcdHy28_ReadDisplayId_ILI932x,
.SetActiveWindow = UiLcdHy28_SetActiveWindow_ILI932x,
.SetCursorA = UiLcdHy28_SetCursorA_ILI932x,
.WriteRAM_Prepare = UiLcdHy28_WriteRAM_Prepare_ILI932x,
.WriteReg = UiLcdHy28_WriteReg_ILI,
.ReadReg = UiLcdHy28_ReadRegILI,
.DrawStraightLine = UiLcdHy28_DrawStraightLine_ILI,
.DrawFullRect = UiLcdHy28_DrawFullRect_ILI,
.DrawColorPoint = UiLcdHy28_DrawColorPoint_ILI,
},
#ifdef USE_GFX_SSD1289
{
DISPLAY_HY32D_PARALLEL_SSD1289, "HY32D Para. SSD1289",
.ReadDisplayId = UiLcdHy28_ReadDisplayId_SSD1289,
.SetActiveWindow = UiLcdHy28_SetActiveWindow_SSD1289,
.SetCursorA = UiLcdHy28_SetCursorA_SSD1289,
.WriteRAM_Prepare = UiLcdHy28_WriteRAM_Prepare_ILI932x,
.WriteReg = UiLcdHy28_WriteReg_ILI,
.ReadReg = UiLcdHy28_ReadRegILI,
.DrawStraightLine = UiLcdHy28_DrawStraightLine_ILI,
.DrawFullRect = UiLcdHy28_DrawFullRect_ILI,
.DrawColorPoint = UiLcdHy28_DrawColorPoint_ILI,
},
#endif
#endif
#ifdef USE_GFX_RA8875
{
DISPLAY_RA8875_PARALLEL, "RA8875 Para.",
.ReadDisplayId = UiLcdHy28_ReadDisplayId_RA8875,
.SetActiveWindow = UiLcdHy28_SetActiveWindow_RA8875,
.SetCursorA = UiLcdHy28_SetCursorA_RA8875,
.WriteRAM_Prepare = UiLcdHy28_WriteRAM_Prepare_RA8875,
.WriteReg = UiLcdHy28_WriteRegRA8875,
.ReadReg = UiLcdHy28_ReadRegRA8875,
.DrawStraightLine = UiLcdHy28_DrawStraightLine_RA8875,
.DrawFullRect = UiLcdHy28_DrawFullRect_RA8875,
.DrawColorPoint = UiLcdHy28_DrawColorPoint_RA8875,
},
#endif
#ifdef USE_GFX_ILI9341
{
DISPLAY_HY28B_PARALLEL, "HY28B Para.",
.ReadDisplayId = UiLcdHy28_ReadDisplayId_ILI9341,
.SetActiveWindow = UiLcdHy28_SetActiveWindow_ILI9341,
.SetCursorA = UiLcdHy28_SetCursorA_ILI9341,
.WriteRAM_Prepare = UiLcdHy28_WriteRAM_Prepare_ILI9341,
.WriteReg = UiLcdHy28_WriteReg_ILI,
.ReadReg = UiLcdHy28_ReadRegILI,
.DrawStraightLine = UiLcdHy28_DrawStraightLine_ILI,
.DrawFullRect = UiLcdHy28_DrawFullRect_ILI,
.DrawColorPoint = UiLcdHy28_DrawColorPoint_ILI,
},
#endif
#endif
#if defined(USE_SPI_DISPLAY)
// we support HY28A SPI only on the UI_BRD_MCHF
#if defined(USE_GFX_ILI932x) && defined(UI_BRD_MCHF)
{
DISPLAY_HY28A_SPI, "HY28A SPI",
.ReadDisplayId = UiLcdHy28_ReadDisplayId_ILI932x,
.SetActiveWindow = UiLcdHy28_SetActiveWindow_ILI932x,
.SetCursorA = UiLcdHy28_SetCursorA_ILI932x,
.WriteRAM_Prepare = UiLcdHy28_WriteRAM_Prepare_ILI932x,
.WriteDataSpiStart_Prepare = UiLcdHy28_WriteDataSpiStart_Prepare_ILI932x,
.WriteIndexSpi_Prepare = UiLcdHy28_WriteIndexSpi_Prepare_ILI932x,
.WriteReg = UiLcdHy28_WriteReg_ILI,
.ReadReg = UiLcdHy28_ReadRegILI,
.DrawStraightLine = UiLcdHy28_DrawStraightLine_ILI,
.DrawFullRect = UiLcdHy28_DrawFullRect_ILI,
.DrawColorPoint = UiLcdHy28_DrawColorPoint_ILI,
LCD_D11_PIO, LCD_D11,
.is_spi = true,
.spi_speed=false
},
#endif
#if defined(USE_GFX_ILI932x)
{
DISPLAY_HY28B_SPI, "HY28B SPI",
.ReadDisplayId = UiLcdHy28_ReadDisplayId_ILI932x,
.SetActiveWindow = UiLcdHy28_SetActiveWindow_ILI932x,
.SetCursorA = UiLcdHy28_SetCursorA_ILI932x,
.WriteRAM_Prepare = UiLcdHy28_WriteRAM_Prepare_ILI932x,
.WriteDataSpiStart_Prepare = UiLcdHy28_WriteDataSpiStart_Prepare_ILI932x,
.WriteIndexSpi_Prepare = UiLcdHy28_WriteIndexSpi_Prepare_ILI932x,
.spi_cs_port = LCD_CSA_PIO,
.spi_cs_pin = LCD_CSA,
.WriteReg = UiLcdHy28_WriteReg_ILI,
.ReadReg = UiLcdHy28_ReadRegILI,
.DrawStraightLine = UiLcdHy28_DrawStraightLine_ILI,
.DrawFullRect = UiLcdHy28_DrawFullRect_ILI,
.DrawColorPoint = UiLcdHy28_DrawColorPoint_ILI,
.is_spi = true,
.spi_speed=false
},
#endif
#ifdef USE_GFX_RA8875
//currently RA8875 parallel interface supported only
/* {
DISPLAY_RA8875_SPI, "RA8875 SPI",
.SetActiveWindow = UiLcdHy28_SetActiveWindow_RA8875,
.SetCursorA = UiLcdHy28_SetCursorA_RA8875,
.WriteRAM_Prepare = UiLcdHy28_WriteRAM_Prepare_RA8875,
.WriteDataSpiStart_Prepare = UiLcdHy28_WriteDataSpiStart_Prepare_RA8875,
.WriteIndexSpi_Prepare = UiLcdHy28_WriteIndexSpi_Prepare_RA8875,
.WriteReg = UiLcdHy28_WriteRegRA8875,
.ReadReg = UiLcdHy28_ReadRegRA8875,
.DrawStraightLine = UiLcdHy28_DrawStraightLine_RA8875,
.DrawFullRect = UiLcdHy28_DrawFullRect_RA8875,
.DrawColorPoint = UiLcdHy28_DrawColorPoint_RA8875,
.spi_cs_port = LCD_CSA_PIO,
.spi_cs_pin = LCD_CSA,
true, false },*/
#endif
#if defined(USE_GFX_ILI9341)
{ DISPLAY_HY28B_SPI, "HY28B SPI",
.ReadDisplayId = UiLcdHy28_ReadDisplayId_ILI9341,
.SetActiveWindow = UiLcdHy28_SetActiveWindow_ILI9341,
.SetCursorA = UiLcdHy28_SetCursorA_ILI9341,
.WriteRAM_Prepare = UiLcdHy28_WriteRAM_Prepare_ILI9341,
.WriteDataSpiStart_Prepare = UiLcdHy28_WriteDataSpiStart_Prepare_ILI9341,
.WriteIndexSpi_Prepare = UiLcdHy28_WriteIndexSpi_Prepare_ILI9341,
.WriteReg = UiLcdHy28_WriteReg_ILI,
.ReadReg = UiLcdHy28_ReadRegILI,
.DrawStraightLine = UiLcdHy28_DrawStraightLine_ILI,
.DrawFullRect = UiLcdHy28_DrawFullRect_ILI,
.DrawColorPoint = UiLcdHy28_DrawColorPoint_ILI,
.spi_cs_port = LCD_CSA_PIO,
.spi_cs_pin = LCD_CSA,
.is_spi = true,
.spi_speed=true
},
#endif
#if defined(USE_GFX_ILI9486)
{ DISPLAY_RPI_SPI, "RPi 3.5 SPI",
.ReadDisplayId = UiLcdHy28_ReadDisplayId_ILI9486,
.SetActiveWindow = UiLcdHy28_SetActiveWindow_ILI9486,
.SetCursorA = UiLcdHy28_SetCursorA_ILI9486,
.WriteRAM_Prepare = UiLcdHy28_WriteRAM_Prepare_ILI9486,
.WriteDataSpiStart_Prepare = UiLcdHy28_WriteDataSpiStart_Prepare_ILI9486,
.WriteIndexSpi_Prepare = UiLcdHy28_WriteIndexSpi_Prepare_ILI9486,
.WriteReg = UiLcdHy28_WriteReg_ILI,
.ReadReg = UiLcdHy28_ReadRegILI,
.DrawStraightLine = UiLcdHy28_DrawStraightLine_ILI,
.DrawFullRect = UiLcdHy28_DrawFullRect_ILI,
.DrawColorPoint = UiLcdHy28_DrawColorPoint_ILI,
.spi_cs_port = LCD_CSA_PIO,
.spi_cs_pin = LCD_CSA,
.is_spi = true,
.spi_speed=true
},
// RPI_SPI NEEDS TO BE LAST IN LIST!!!
#endif
#endif
{
DISPLAY_NUM, "Unknown", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0
}
};
/* Controller Specific Functions End *********************************/
static uint16_t UiLcdHy28_DetectController(const uhsdr_display_info_t* disp_info_ptr)
{
uint16_t retval = 0;
if (disp_info_ptr != NULL)
{
mchf_display.use_spi = disp_info_ptr->is_spi;
mchf_display.lcd_cs = disp_info_ptr->spi_cs_pin;
mchf_display.lcd_cs_pio = disp_info_ptr->spi_cs_port;
mchf_display.SetActiveWindow = disp_info_ptr->SetActiveWindow;
mchf_display.SetCursorA = disp_info_ptr->SetCursorA;
mchf_display.WriteRAM_Prepare = disp_info_ptr->WriteRAM_Prepare;
mchf_display.WriteDataSpiStart_Prepare = disp_info_ptr->WriteDataSpiStart_Prepare;
mchf_display.WriteIndexSpi_Prepare = disp_info_ptr->WriteIndexSpi_Prepare;
mchf_display.WriteReg = disp_info_ptr->WriteReg;
mchf_display.ReadReg = disp_info_ptr->ReadReg;
mchf_display.DrawStraightLine = disp_info_ptr->DrawStraightLine;
mchf_display.DrawFullRect = disp_info_ptr->DrawFullRect;
mchf_display.DrawColorPoint = disp_info_ptr->DrawColorPoint;
mchf_display.reg_info = NULL;
UiLcdHy28_GpioInit(disp_info_ptr->display_type);
if (mchf_display.use_spi == true)
{
#ifdef USE_SPI_DISPLAY
UiLcdHy28_SpiInit(disp_info_ptr->spi_speed, disp_info_ptr->display_type);
#endif
}
else
{
#ifdef USE_DISPLAY_PAR
UiLcdHy28_ParallelInit();
#endif
}
UiLcdHy28_Reset();
// if we have an identifier function, call it
// WITHOUT function the display will never be used!
if (disp_info_ptr->ReadDisplayId)
{
retval = disp_info_ptr->ReadDisplayId();
}
// if the identification set a register data set for initialization,
// we send it to the display controller
if (mchf_display.reg_info != NULL)
{
UiLcdHy28_SendRegisters(mchf_display.reg_info);
}
// okay, this display was not detected,
// cleanup data structures and prepare
// for next try
if (retval == 0)
{
mchf_display.SetActiveWindow = NULL;
mchf_display.SetCursorA = NULL;
mchf_display.WriteRAM_Prepare = NULL;
mchf_display.WriteDataSpiStart_Prepare = NULL;
mchf_display.WriteIndexSpi_Prepare = NULL;
mchf_display.lcd_cs = 0;
mchf_display.lcd_cs_pio = NULL;
if (mchf_display.use_spi == true)
{
#ifdef USE_SPI_DISPLAY
UiLcdHy28_SpiDeInit();
#endif
}
else
{
#ifdef USE_DISPLAY_PAR
UiLcdHy28_ParallelDeInit();
#endif
}
}
}
return retval;
}
/*
* brief Identifies and initializes to HY28x display family
*
* @param devicecode_ptr pointer to a variable to store the device code of the controller in
* @returns 0 if no display detected, DISPLAY_HY28x_xxx otherwise, see header
*/
uint8_t UiLcdHy28_Init()
{
uint8_t retval = DISPLAY_NONE;
mchf_display.DeviceCode = 0x0000;
UiLcdHy28_BacklightInit();
for (uint16_t disp_idx = 1; retval == DISPLAY_NONE && display_infos[disp_idx].display_type != DISPLAY_NUM; disp_idx++)
{
mchf_display.DeviceCode = UiLcdHy28_DetectController(&display_infos[disp_idx]);
if(mchf_display.DeviceCode != 0x0000)
{
retval = display_infos[disp_idx].display_type;
}
}
mchf_display.display_type = retval;
#ifndef BOOTLOADER_BUILD
switch(mchf_display.DeviceCode)
{
case 0x8875:
ts.Layout=&LcdLayouts[LcdLayout_800x480];
disp_resolution=RESOLUTION_800_480;
break;
case 0x9486:
case 0x9488:
case 0x7796:
ts.Layout=&LcdLayouts[LcdLayout_480x320];
disp_resolution=RESOLUTION_480_320;
break;
default:
ts.Layout=&LcdLayouts[LcdLayout_320x240];
disp_resolution=RESOLUTION_320_240;
break;
}
mchf_display.MAX_X=ts.Layout->Size.x;
mchf_display.MAX_Y=ts.Layout->Size.y;
#else
switch(mchf_display.DeviceCode)
{
case 0x8875:
mchf_display.MAX_X=800;
mchf_display.MAX_Y=480;
break;
case 0x9486:
case 0x9488:
case 0x7796:
mchf_display.MAX_X=480;
mchf_display.MAX_Y=320;
break;
default:
mchf_display.MAX_X=320;
mchf_display.MAX_Y=240;
break;
}
#endif
return retval;
}
static inline void UiLcdHy28_SetSpiPrescaler(uint32_t baudrate_prescaler)
{
/*---------------------------- SPIx CR1 Configuration ------------------------*/
// the baud rate register differs across the different processors
#if defined(STM32H7)
#define SPI_BR_REG CFG1
#elif defined(STM32F4) || defined(STM32F7)
#define SPI_BR_REG CR1
#endif
/* Get the SPIx SPI_BR_REG value */
uint32_t tmpreg = SPI_DISPLAY->SPI_BR_REG;
tmpreg &= ~SPI_BAUDRATEPRESCALER_256;
tmpreg |= baudrate_prescaler;
/* Write to SPIx CR1 */
SPI_DISPLAY->SPI_BR_REG = tmpreg;
}
mchf_touchscreen_t mchf_touchscreen;
/*
* @brief Called to run the touch detection state machine, results are stored in ts structure
*/
void UiLcdHy28_TouchscreenDetectPress()
{
if (mchf_touchscreen.present)
{
if(!HAL_GPIO_ReadPin(TP_IRQ_PIO,TP_IRQ) && mchf_touchscreen.state != TP_DATASETS_PROCESSED) // fetch touchscreen data if not already processed
UiLcdHy28_TouchscreenReadCoordinates();
if(HAL_GPIO_ReadPin(TP_IRQ_PIO,TP_IRQ) && mchf_touchscreen.state == TP_DATASETS_PROCESSED) // clear statemachine when data is processed
{
mchf_touchscreen.state = TP_DATASETS_NONE;
mchf_touchscreen.hr_x = mchf_touchscreen.hr_y = 0x7fff;
}
}
}
/*
* @brief tells you that touchscreen coordinates are ready for processing and marks them as processed
* @returns true if coordinates for processing are available and have been marked as processed, false otherwise
*/
bool UiLcdHy28_TouchscreenHasProcessableCoordinates()
{
bool retval = false;
UiLcdHy28_TouchscreenReadCoordinates();
if(mchf_touchscreen.state >= TP_DATASETS_VALID && mchf_touchscreen.state != TP_DATASETS_PROCESSED)
//if(mchf_touchscreen.state >= TP_DATASETS_WAIT && mchf_touchscreen.state != TP_DATASETS_PROCESSED)
{
mchf_touchscreen.state = TP_DATASETS_NONE; // tp data processed
retval = true;
}
return retval;
}
static inline void UiLcdHy28_TouchscreenStartSpiTransfer(void)
{
// we only have to care about other transfers if the SPI is
// use by the display as well
if (UiLcdHy28_SpiDisplayUsed())
{
UiLcdHy28_FinishWaitBulkWrite();
}
UiLcdHy28_SetSpiPrescaler(SPI_PRESCALE_TS_DEFAULT);
GPIO_ResetBits(TP_CS_PIO, TP_CS);
}
static inline void UiLcdHy28_TouchscreenFinishSpiTransfer(void)
{
UiLcdHy28_SpiFinishTransfer();
GPIO_SetBits(TP_CS_PIO, TP_CS);
// we only have to care about other transfers if the SPI is
// use by the display as well
if (UiLcdHy28_SpiDisplayUsed())
{
UiLcdHy28_SetSpiPrescaler(lcd_spi_prescaler);
}
}
/*
* @brief Extracts touchscreen touch coordinates, counts how often same position is being read consecutively
* @param do_translate false -> raw coordinates, true -> mapped coordinates according to calibration data
*/
#define XPT2046_PD_FULL 0x00
#define XPT2046_PD_REF 0x01
#define XPT2046_PD_ADC 0x02
#define XPT2046_PD_NONE 0x03
#define XPT2046_MODE_12BIT 0x00
#define XPT2046_MODE_8BIT 0x08
#define XPT2046_CH_DFR_Y 0x50
#define XPT2046_CH_DFR_X 0x10
#define XPT2046_CONV_START 0x80
#define XPT2046_COMMAND_LEN 7
static void UiLcdHy28_TouchscreenReadData(uint16_t* x_p,uint16_t* y_p)
{
static const uint8_t xpt2046_command[XPT2046_COMMAND_LEN] =
{
XPT2046_CONV_START|XPT2046_CH_DFR_X|XPT2046_MODE_12BIT|XPT2046_PD_REF,
0, XPT2046_CONV_START|XPT2046_CH_DFR_Y|XPT2046_MODE_12BIT|XPT2046_PD_REF, // the measurement for first command is here, we discard this
0, XPT2046_CONV_START|XPT2046_CH_DFR_X|XPT2046_MODE_12BIT|XPT2046_PD_FULL, // y measurement from previous command, next command turns off power
0, 0 // x measurement from previous command
};
uint8_t xpt_response[XPT2046_COMMAND_LEN];
UiLcdHy28_TouchscreenStartSpiTransfer();
HAL_SPI_TransmitReceive(&hspi2, (uint8_t*)xpt2046_command, xpt_response,XPT2046_COMMAND_LEN,SPI_TIMEOUT);
UiLcdHy28_TouchscreenFinishSpiTransfer();
*x_p = (xpt_response[5] << 8 | xpt_response[6]) >> 3;
*y_p = (xpt_response[3] << 8 | xpt_response[4]) >> 3;
}
#define HIRES_TOUCH_MaxDelta 2
#define HIRES_TOUCH_MaxFocus 4
void UiLcdHy28_TouchscreenReadCoordinates()
{
/*
statemachine stati:
TP_DATASETS_NONE = no touchscreen action detected
TP_DATASETS_WAIT 1 = first touchscreen press
>1 = x times valid data available
TP_DATASETS_PROCESSED 0xff = data was already processed by calling function
*/
if(mchf_touchscreen.state < TP_DATASETS_VALID) // no valid data ready or data ready to process
{
if(mchf_touchscreen.state > TP_DATASETS_NONE && mchf_touchscreen.state < TP_DATASETS_VALID) // first pass finished, get data
{
UiLcdHy28_TouchscreenReadData(&mchf_touchscreen.xraw,&mchf_touchscreen.yraw);
//delta/focus algorithm for filtering the noise from touch panel data
//based on LM8300/LM8500 datasheet
//first calculating the delta algorithm
int16_t TS_dx,TS_dy, TS_predicted_x, TS_predicted_y, NewDeltaX, NewDeltaY;
TS_dx=mchf_touchscreen.xraw_m1-mchf_touchscreen.xraw_m2;
TS_dy=mchf_touchscreen.yraw_m1-mchf_touchscreen.yraw_m2;
TS_predicted_x=mchf_touchscreen.yraw_m1+TS_dx;
TS_predicted_y=mchf_touchscreen.yraw_m1+TS_dy;
NewDeltaX=TS_predicted_x-mchf_touchscreen.xraw;
NewDeltaY=TS_predicted_y-mchf_touchscreen.yraw;
if(NewDeltaX<0)
NewDeltaX=-NewDeltaX;
if(NewDeltaY<0)
NewDeltaX=-NewDeltaY;
if((NewDeltaX<=HIRES_TOUCH_MaxDelta) && (NewDeltaY<=HIRES_TOUCH_MaxDelta))
{
//ok, the delta algorithm filtered out spikes and the bigger noise
//now we perform focus algorithm
NewDeltaX=mchf_touchscreen.focus_xprev-mchf_touchscreen.xraw;
NewDeltaY=mchf_touchscreen.focus_yprev-mchf_touchscreen.yraw;
if(NewDeltaX<0)
NewDeltaX=-NewDeltaX;
if(NewDeltaY<0)
NewDeltaX=-NewDeltaY;
if((NewDeltaX<=HIRES_TOUCH_MaxFocus) && (NewDeltaY<=HIRES_TOUCH_MaxFocus))
{
mchf_touchscreen.xraw=mchf_touchscreen.focus_xprev;
mchf_touchscreen.yraw=mchf_touchscreen.focus_yprev;
}
else
{
mchf_touchscreen.focus_xprev=mchf_touchscreen.xraw;
mchf_touchscreen.focus_yprev=mchf_touchscreen.yraw;
}
mchf_touchscreen.state=TP_DATASETS_VALID;
int32_t x,y;
x=mchf_touchscreen.xraw;
y=mchf_touchscreen.yraw;
int32_t xn,yn;
//transforming the coordinates by calibration coefficients calculated in touchscreen calibration
//see the UiDriver_TouchscreenCalibration
//xn=Ax+By+C
//yn=Dx+Ey+F
//all coefficients are in format 16.16
xn=mchf_touchscreen.cal[0]*x+mchf_touchscreen.cal[1]*y+mchf_touchscreen.cal[2];
yn=mchf_touchscreen.cal[3]*x+mchf_touchscreen.cal[4]*y+mchf_touchscreen.cal[5];
xn>>=16;
yn>>=16;
mchf_touchscreen.hr_x=(int16_t)xn;
mchf_touchscreen.hr_y=(int16_t)yn;
}
else
{
mchf_touchscreen.xraw_m2=mchf_touchscreen.xraw_m1;
mchf_touchscreen.yraw_m2=mchf_touchscreen.yraw_m1;
mchf_touchscreen.xraw_m1=mchf_touchscreen.xraw;
mchf_touchscreen.yraw_m1=mchf_touchscreen.yraw;
mchf_touchscreen.state = TP_DATASETS_WAIT;
}
}
else
{
mchf_touchscreen.state = TP_DATASETS_WAIT; // restart machine
}
}
}
bool UiLcdHy28_TouchscreenPresenceDetection()
{
bool retval = false;
uint16_t x = 0xffff, y = 0xffff;
UiLcdHy28_TouchscreenReadData(&x,&y);
UiLcdHy28_TouchscreenReadData(&x,&y);
mchf_touchscreen.state = TP_DATASETS_PROCESSED;
if(x != 0xffff && y != 0xffff && x != 0 && y != 0)
{// touchscreen data valid?
retval = true; // yes - touchscreen present!
}
return retval;
}
void UiLcdHy28_TouchscreenInit(uint8_t mirror)
{
mchf_touchscreen.xraw = 0;
mchf_touchscreen.yraw = 0;
mchf_touchscreen.hr_x = 0x7FFF; // invalid position
mchf_touchscreen.hr_y = 0x7FFF; // invalid position
mchf_touchscreen.present = UiLcdHy28_TouchscreenPresenceDetection();
}