3196 lines
94 KiB
C
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();
|
||
|
}
|