/* -*- 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 #include #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; extern sFONT GL_Font8x12_bold; extern sFONT GL_Font12x12; extern sFONT GL_Font16x24; #ifdef USE_8bit_FONT extern sFONT GL_Font16x24_8b_Square; #endif static sFONT *fontList[] = { &GL_Font8x12_bold, &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 // 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_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 //Add ILI9341 //#ifdef USE_GFX_ILI9341 static const RegisterValue_t ili9341[] = { { 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 ili9341_regs = { ili9341, sizeof(ili9341)/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) }; #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() { 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; //Add ILI9341 //#ifdef USE_GFX_ILI9341 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_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 // 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 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() { 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 static void UiLcdHy28_SpiLcdCsDisable() { GPIO_SetBits(mchf_display.lcd_cs_pio, mchf_display.lcd_cs); } inline static void UiLcdHy28_SpiLcdCsEnable() { GPIO_ResetBits(mchf_display.lcd_cs_pio, mchf_display.lcd_cs); } #ifdef USE_DISPLAY_PAR static void UiLcdHy28_ParallelInit() { MEM_Init(); } static void UiLcdHy28_ParallelDeInit() { HAL_SRAM_DeInit(&hsram1); } #endif static void UiLcdHy28_Reset() { // 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() { #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() { 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() { 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() { 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() { 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() { #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() { 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>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(); } void UiLcdHy28_DrawChar(ushort x, ushort y, char symb,ushort Color, ushort bkColor,const sFONT *cf) { #ifdef USE_8bit_FONT switch(cf->BitCount) { case 1: //1 bit font (basic type) #endif UiLcdHy28_DrawChar_1bit(x, y, symb, Color, bkColor, cf); #ifdef USE_8bit_FONT break; case 8: //8 bit grayscaled font UiLcdHy28_DrawChar_8bit(x, y, symb, Color, bkColor, cf); break; } #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) { 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); if(XposCurrent < (MAX_X - Xshift)) { XposCurrent += Xshift; } else if (YposCurrent < (MAX_Y - cf->Height)) { XposCurrent = XposStart; YposCurrent += cf->Height; } else { XposCurrent = XposStart; YposCurrent = XposStart; } } } } /** * @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) { 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) { 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) { // 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 (txtWHeight; 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() { 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() { 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_ILI9486 static uint16_t UiLcdHy28_ReadDisplayId_ILI9486() { 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() { 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() { 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 //Add ILI9341 //#ifdef USE_GFX_ILI9341 static uint16_t UiLcdHy28_ReadDisplayId_ILI9341() { uint16_t retval = UiLcdHy28_ReadReg(0x00); switch (retval) { default: mchf_display.reg_info = &ili9341_regs; break; } return retval; } static inline void UiLcdHy28_WriteDataSpiStart_Prepare_ILI9341() { UiLcdHy28_SpiSendByte(SPI_START | SPI_WR | SPI_DATA); /* Write : RS = 1, RW = 0 */ } void UiLcdHy28_WriteIndexSpi_Prepare_ILI9341() { UiLcdHy28_SpiSendByte(SPI_START | SPI_WR | SPI_INDEX); /* Write : RS = 0, RW = 0 */ } static void UiLcdHy28_SetCursorA_ILI9341( unsigned short Xpos, unsigned short Ypos ) { UiLcdHy28_WriteReg(0x20, Ypos ); UiLcdHy28_WriteReg(0x21, Xpos ); } static void UiLcdHy28_WriteRAM_Prepare_ILI9341() { UiLcdHy28_WriteRAM_Prepare_Index(0x22); } static void UiLcdHy28_SetActiveWindow_ILI9341(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_ILI932x static uint16_t UiLcdHy28_ReadDisplayId_ILI932x() { 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() { 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() { 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 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() { 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_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 #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 //Add ILI9341 same function 9486 //#ifdef USE_GFX_ILI9341 { DISPLAY_HY28B_SPI, "HY28B SPI", .ReadDisplayId = UiLcdHy28_ReadDisplayId_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: 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: 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() { // 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() { 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(); }