Add SCREEN_DIM_15 (Off / 100 / 50 / 30 / 15). The value now needs 3 bits, so repurpose the spare BIT_UNUSED_2 as BIT_SCREEN_DIM_2; existing codeplugs (bit 2 clear) keep their level, no reset. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
480 lines
13 KiB
C
480 lines
13 KiB
C
/*
|
|
* Copyright (C) 2019 Kai Ludwig, DG4KLU
|
|
* Copyright (C) 2019-2025 Roger Clark, VK3KYY / G4KYF
|
|
* Daniel Caujolle-Bert, F1RMB
|
|
*
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
|
|
* in the documentation and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* 4. Use of this source code or binary releases for commercial purposes is strictly forbidden. This includes, without limitation,
|
|
* incorporation in a commercial product or incorporation into a product or project which allows commercial use.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
|
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
|
|
#include <FreeRTOS.h>
|
|
#include "hardware/HX8353E.h"
|
|
#include "io/display.h"
|
|
#include "functions/settings.h"
|
|
#include "interfaces/gpio.h"
|
|
#include "main.h"
|
|
#include "user_interface/uiGlobals.h"
|
|
|
|
uint8_t displayLCD_Type = 1;
|
|
static bool displayIsInverseVideo = false;
|
|
#if defined(PLATFORM_VARIANT_DM1701)
|
|
static void displayClearScreenBlankLines(bool isInverted);
|
|
#endif
|
|
|
|
void displayWriteCmd(uint8_t cmd)
|
|
{
|
|
*((volatile uint8_t*) LCD_FSMC_ADDR_COMMAND) = cmd;
|
|
}
|
|
void displayWriteData(uint8_t val)
|
|
{
|
|
*((volatile uint8_t*) LCD_FSMC_ADDR_DATA) = val;
|
|
}
|
|
|
|
void displayWriteCmds(uint8_t cmd, size_t len, uint8_t opts[])
|
|
{
|
|
*((volatile uint8_t*) LCD_FSMC_ADDR_COMMAND) = cmd;
|
|
|
|
for(volatile int x = 0; x < 1; x++);//Display seems to need a tiny delay to respond to the data
|
|
|
|
if (len)
|
|
{
|
|
for (size_t i = 0; i < len; i++)
|
|
{
|
|
*((volatile uint8_t*) LCD_FSMC_ADDR_DATA) = opts[i];
|
|
for(volatile int x = 0; x < 1; x++);//Display seems to need a tiny delay to respond to the data
|
|
}
|
|
}
|
|
}
|
|
|
|
void displaySetInvertedState(bool isInverted)
|
|
{
|
|
if (displayIsInverseVideo != isInverted)
|
|
{
|
|
GPIO_InitTypeDef GPIO_InitStruct = {0};
|
|
|
|
displayIsInverseVideo = isInverted;
|
|
|
|
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
|
|
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
|
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
|
|
GPIO_InitStruct.Alternate = GPIO_AF12_FSMC;
|
|
|
|
GPIO_InitStruct.Pin = LCD_D0_Pin | LCD_D1_Pin | LCD_D2_Pin | LCD_D3_Pin;
|
|
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
|
|
|
|
GPIO_InitStruct.Pin = LCD_D4_Pin | LCD_D5_Pin | LCD_D6_Pin | LCD_D7_Pin;
|
|
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
|
|
|
|
HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET);
|
|
osDelay(10);
|
|
displayWriteCmd(displayIsInverseVideo ? HX8583_CMD_INVON : HX8583_CMD_INVOFF);
|
|
osDelay(10);
|
|
HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET);
|
|
|
|
*((volatile uint8_t*) LCD_FSMC_ADDR_DATA) = 0;// write 0 to the display pins , to pull them all low, so keyboard reads don't need to
|
|
#if defined(PLATFORM_VARIANT_DM1701)
|
|
displayClearScreenBlankLines(displayIsInverseVideo);
|
|
#endif
|
|
displaySetInverseVideo(displayIsInverseVideo);
|
|
}
|
|
}
|
|
|
|
void displayInit(bool isInverted, bool SPIFlashAvailable)
|
|
{
|
|
GPIO_InitTypeDef GPIO_InitStruct = {0};
|
|
|
|
// Enable the FMC interface clock
|
|
__HAL_RCC_FSMC_CLK_ENABLE();
|
|
|
|
// was 0x10D9;
|
|
FSMC_Bank1->BTCR[0] =
|
|
FSMC_BCR1_MBKEN | // bit 0
|
|
FSMC_BCR1_MTYP_1 | // bit 3
|
|
FSMC_BCR1_MWID_0 | // bit 4
|
|
FSMC_BCR1_FACCEN | // bit 6
|
|
//((0x2UL << FSMC_BCR1_FACCEN_Pos)) | // bit 7: UNDOCUMENTED
|
|
FSMC_BCR1_WREN; // bit 12
|
|
|
|
// was 0x00100517;
|
|
FSMC_Bank1->BTCR[1] =
|
|
FSMC_BCR1_MBKEN | // bit 0
|
|
FSMC_BCR1_MUXEN | // bit 1
|
|
FSMC_BCR1_MTYP_0 | // bit 2
|
|
FSMC_BCR1_MWID_0 | // bit 4
|
|
FSMC_BCR1_BURSTEN | // bit 8
|
|
FSMC_BCR1_WRAPMOD | // bit 10
|
|
FSMC_BCR1_CBURSTRW; // bit 19, as bit 20 isn't documented
|
|
|
|
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
|
|
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
|
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
|
|
GPIO_InitStruct.Alternate = GPIO_AF12_FSMC;
|
|
|
|
GPIO_InitStruct.Pin = LCD_D0_Pin | LCD_D1_Pin | LCD_D2_Pin | LCD_D3_Pin | LCD_RS_Pin | LCD_WR_Pin | LCD_RD_Pin;
|
|
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
|
|
|
|
GPIO_InitStruct.Pin = LCD_D4_Pin | LCD_D5_Pin | LCD_D6_Pin | LCD_D7_Pin;
|
|
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
|
|
|
|
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
|
|
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
|
GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
|
|
GPIO_InitStruct.Alternate = 0x00;
|
|
GPIO_InitStruct.Pin = LCD_CS_Pin | LCD_RST_Pin;
|
|
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
|
|
|
|
// Reset the screen
|
|
HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET);
|
|
HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_RESET);
|
|
osDelay(20);
|
|
HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_SET);
|
|
HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET);
|
|
|
|
// Init
|
|
if((DISPLAYLCD_GET_TYPE(displayLCD_Type) == 2) || (DISPLAYLCD_GET_TYPE(displayLCD_Type) == 3))
|
|
{
|
|
displayWriteCmd(0xfe);
|
|
displayWriteCmd(0xef);
|
|
|
|
{
|
|
uint8_t opts[] = { 0x00 };
|
|
displayWriteCmds(0xb4, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x16 };
|
|
displayWriteCmds(0xff, 1, opts);
|
|
}
|
|
|
|
|
|
{
|
|
uint8_t opts[] = { ((DISPLAYLCD_GET_TYPE(displayLCD_Type) == 3) ? 0x40 : 0x4f) };
|
|
displayWriteCmds(0xfd, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x70 };
|
|
displayWriteCmds(0xa4, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x94, 0x88 };
|
|
displayWriteCmds(0xe7, 2, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x3a };
|
|
displayWriteCmds(0xea, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x11 };
|
|
displayWriteCmds(0xed, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0xc5 };
|
|
displayWriteCmds(0xe4, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x80 };
|
|
displayWriteCmds(0xe2, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x12 };
|
|
displayWriteCmds(0xa3, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x07 };
|
|
displayWriteCmds(0xe3, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x10 };
|
|
displayWriteCmds(0xe5, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x00 };
|
|
displayWriteCmds(0xf0, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x55 };
|
|
displayWriteCmds(0xf1, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x05 };
|
|
displayWriteCmds(0xf2, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x53 };
|
|
displayWriteCmds(0xf3, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x00 };
|
|
displayWriteCmds(0xf4, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x00 };
|
|
displayWriteCmds(0xf5, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x27 };
|
|
displayWriteCmds(0xf7, 1 , opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x22 };
|
|
displayWriteCmds(0xf8, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x77 };
|
|
displayWriteCmds(0xf9, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x35 };
|
|
displayWriteCmds(0xfa, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x00 };
|
|
displayWriteCmds(0xfb, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x00 };
|
|
displayWriteCmds(0xfc, 1, opts);
|
|
}
|
|
|
|
displayWriteCmd(0xfe);
|
|
|
|
displayWriteCmd(0xef);
|
|
|
|
{
|
|
uint8_t opts[] = { 0x00 };
|
|
displayWriteCmds(0xe9, 1, opts);
|
|
}
|
|
|
|
osDelay(20);
|
|
}
|
|
else
|
|
{
|
|
displayWriteCmd(0x11);
|
|
osDelay(120);
|
|
|
|
{
|
|
uint8_t opts[] = {0x05, 0x3c, 0x3c };
|
|
displayWriteCmds(0xb1, sizeof(opts), opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x05, 0x3c, 0x3c };
|
|
displayWriteCmds(0xb2, sizeof(opts), opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x05, 0x3c, 0x3c, 0x05, 0x3c, 0x3c };
|
|
displayWriteCmds(0xb3, sizeof(opts), opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x03 };
|
|
displayWriteCmds(0xb4, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x28, 0x08, 0x04 };
|
|
displayWriteCmds(0xc0, sizeof(opts), opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0xc0 };
|
|
displayWriteCmds(0xc1, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0xd, 0x00};
|
|
displayWriteCmds(0xc2, sizeof(opts), opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x8d, 0x2a };
|
|
displayWriteCmds(0xc3, sizeof(opts), opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x8d, 0xee };
|
|
displayWriteCmds(0xc4, sizeof(opts), opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x1a };
|
|
displayWriteCmds(0xc5, 1 , opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x08 };
|
|
displayWriteCmds(0x36, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x04, 0x0c, 0x07, 0x0a, 0x2e, 0x30, 0x25, 0x2a, 0x28, 0x26, 0x2e, 0x3a, 0x00, 0x01, 0x03, 0x13 };
|
|
displayWriteCmds(0xe0, sizeof(opts), opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x04, 0x16, 0x06, 0x0d, 0x2d, 0x26, 0x23, 0x27, 0x27, 0x25, 0x2d, 0x3b, 0x00, 0x01, 0x04, 0x13 };
|
|
displayWriteCmds(0xE1, sizeof(opts), opts);
|
|
}
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = {
|
|
(DISPLAYLCD_GET_TYPE(displayLCD_Type) == 1) ? 0x60 :
|
|
(DISPLAYLCD_GET_TYPE(displayLCD_Type) == 2) ? 0xE0 :
|
|
0xA0
|
|
};
|
|
displayWriteCmds(HX8583_CMD_MADCTL, 1, opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x00, 0x00, 0x00, DISPLAY_SIZE_X };
|
|
displayWriteCmds(HX8583_CMD_CASET, sizeof(opts), opts);
|
|
}
|
|
|
|
{
|
|
#if defined(PLATFORM_VARIANT_DM1701)
|
|
uint8_t opts[] = { 0x00, 0x00, 0x00, DISPLAY_SIZE_Y + DISPLAY_Y_OFFSET};
|
|
#else
|
|
uint8_t opts[] = { 0x00, 0x00, 0x00, DISPLAY_SIZE_Y};
|
|
#endif
|
|
displayWriteCmds(HX8583_CMD_RASET, sizeof(opts), opts);
|
|
}
|
|
|
|
{
|
|
uint8_t opts[] = { 0x05 }; // RGB565 16 bits per pixel
|
|
displayWriteCmds(HX8583_CMD_COLMOD, 1, opts);
|
|
}
|
|
|
|
//osDelay(10);// does not seem to be needed
|
|
displayWriteCmd(HX8583_CMD_SLPOUT); // Activate the display
|
|
//osDelay(120);// does not seem to be needed
|
|
|
|
displayWriteCmd(HX8583_CMD_DISPON);
|
|
|
|
HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET);
|
|
|
|
displayBegin(isInverted, SPIFlashAvailable);
|
|
|
|
displayClearBuf();
|
|
|
|
#if defined(PLATFORM_VARIANT_DM1701)
|
|
displayClearScreenBlankLines(isInverted);
|
|
#endif
|
|
displayRender();
|
|
}
|
|
#if defined(PLATFORM_VARIANT_DM1701)
|
|
static void displayClearScreenBlankLines(bool isInverted)
|
|
{
|
|
GPIO_InitTypeDef GPIO_InitStruct = {0};
|
|
|
|
memset(&GPIO_InitStruct, 0x00, sizeof(GPIO_InitTypeDef));
|
|
|
|
// Display shares its pins with the keypad, so the pins need to be put into alternate mode to work with the FSMC
|
|
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
|
|
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
|
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
|
|
GPIO_InitStruct.Alternate = GPIO_AF12_FSMC;
|
|
|
|
GPIO_InitStruct.Pin = LCD_D0_Pin | LCD_D1_Pin | LCD_D2_Pin | LCD_D3_Pin;
|
|
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
|
|
|
|
GPIO_InitStruct.Pin = LCD_D4_Pin | LCD_D5_Pin | LCD_D6_Pin | LCD_D7_Pin;
|
|
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
|
|
|
|
HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET);
|
|
|
|
uint8_t opts[] = { 0x00, 0, 0x00, DISPLAY_Y_OFFSET };
|
|
displayWriteCmds(HX8583_CMD_RASET, sizeof(opts), opts);
|
|
|
|
displayWriteCmd(HX8583_CMD_RAMWR);
|
|
|
|
uint8_t fillData = isInverted ? 0xFF: 0x00;
|
|
|
|
for(int y = 0; y < DISPLAY_Y_OFFSET * DISPLAY_SIZE_X * sizeof(uint16_t); y++)
|
|
{
|
|
*((volatile uint8_t*) LCD_FSMC_ADDR_DATA) = fillData;
|
|
}
|
|
|
|
HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET);
|
|
|
|
*((volatile uint8_t*) LCD_FSMC_ADDR_DATA) = 0;// write 0 to the display pins , to pull them all low, so keyboard reads don't need to
|
|
|
|
}
|
|
#endif
|
|
|
|
void displayEnableBacklight(bool enable, int displayBacklightPercentageOff)
|
|
{
|
|
if (enable)
|
|
{
|
|
gpioSetDisplayBacklightIntensityPercentage(nonVolatileSettings.displayBacklightPercentage[DAYTIME_CURRENT]);
|
|
}
|
|
else
|
|
{
|
|
gpioSetDisplayBacklightIntensityPercentage(((nonVolatileSettings.backlightMode == BACKLIGHT_MODE_NONE) ? 0 : displayBacklightPercentageOff));
|
|
}
|
|
}
|
|
|
|
bool displayIsBacklightLit(void)
|
|
{
|
|
return (gpioGetDisplayBacklightIntensityPercentage() != nonVolatileSettings.displayBacklightPercentageOff);
|
|
}
|
|
|
|
// Backlight level to use when idle. If screen dimming is enabled (Auto mode only),
|
|
// dim to a fraction of the configured brightness; otherwise use the normal off level.
|
|
int displayGetIdleBacklightPercentage(void)
|
|
{
|
|
if (nonVolatileSettings.backlightMode == BACKLIGHT_MODE_AUTO)
|
|
{
|
|
static const uint8_t dimPercent[SCREEN_DIM_NUM] = { 0, 100, 50, 30, 15 };
|
|
uint8_t level = settingsGetScreenDimLevel();
|
|
|
|
if (level != SCREEN_DIM_OFF)
|
|
{
|
|
return ((nonVolatileSettings.displayBacklightPercentage[DAYTIME_CURRENT] * dimPercent[level]) / 100);
|
|
}
|
|
}
|
|
|
|
return nonVolatileSettings.displayBacklightPercentageOff;
|
|
}
|