diff --git a/meson.build b/meson.build index 9df1ed87..11298e9e 100644 --- a/meson.build +++ b/meson.build @@ -461,7 +461,8 @@ cs7000_src = ['platform/drivers/stubs/nvmem_stub.c', 'platform/drivers/stubs/cps_io_stub.c', 'platform/drivers/stubs/radio_stub.c', 'platform/drivers/stubs/keyboard_stub.c', - 'platform/drivers/stubs/display_stub.c', + 'platform/drivers/display/ST7735R_CS7000.c', + 'platform/drivers/backlight/backlight_CS7000.c', 'platform/drivers/stubs/audio_stub.c', 'platform/drivers/GPIO/gpio_shiftReg.c', 'platform/drivers/SPI/spi_custom.c', diff --git a/platform/drivers/backlight/backlight_CS7000.c b/platform/drivers/backlight/backlight_CS7000.c new file mode 100644 index 00000000..0a8f5bd3 --- /dev/null +++ b/platform/drivers/backlight/backlight_CS7000.c @@ -0,0 +1,72 @@ +/*************************************************************************** + * Copyright (C) 2024 by Silvano Seva IU2KWO * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see * + ***************************************************************************/ + +#include +#include +#include +#include "backlight.h" + +void backlight_init() +{ + gpio_setMode(LCD_BKLIGHT, ALTERNATE | ALTERNATE_FUNC(3)); + + /* + * Configure TIM8 for backlight PWM: Fpwm = 1kHz with 8 bit of resolution. + * APB2 freq. is 84MHz, but timer runs at twice this frequency. + * Then: PSC = 655 to have Ftick = 256.097kHz + * With ARR = 256, Fpwm is 1kHz; + * Backlight pin is connected to TIM8 CR1. + */ + RCC->APB2ENR |= RCC_APB2ENR_TIM8EN; + __DSB(); + + TIM8->ARR = 255; + TIM8->PSC = 654; + TIM8->CNT = 0; + TIM8->CR1 |= TIM_CR1_ARPE; /* LCD backlight is on PC6, TIM8-CH1 */ + TIM8->CCMR2 |= TIM_CCMR1_OC2M_2 + | TIM_CCMR1_OC2M_1 + | TIM_CCMR1_OC2PE; + TIM8->CCER |= TIM_CCER_CC4E; + TIM8->BDTR |= TIM_BDTR_MOE; + TIM8->CCR1 = 0; + TIM8->EGR = TIM_EGR_UG; /* Update registers */ + TIM8->CR1 |= TIM_CR1_CEN; /* Start timer */ +} + +void backlight_terminate() +{ + /* Shut down backlight */ + gpio_setMode(LCD_BKLIGHT, OUTPUT); + gpio_clearPin(LCD_BKLIGHT); + + /* Shut down timer */ + RCC->APB2ENR &= ~RCC_APB2ENR_TIM8EN; + __DSB(); +} + +/* + * This function is defined in display.h + */ +void display_setBacklightLevel(uint8_t level) +{ + if(level > 100) + level = 100; + + uint8_t pwmLevel = (2 * level) + (level * 55)/100; // Convert value to 0 - 255 + TIM8->CCR4 = pwmLevel; +} diff --git a/platform/drivers/display/ST7735R_CS7000.c b/platform/drivers/display/ST7735R_CS7000.c new file mode 100644 index 00000000..19e8bc0b --- /dev/null +++ b/platform/drivers/display/ST7735R_CS7000.c @@ -0,0 +1,292 @@ +/*************************************************************************** + * Copyright (C) 2024 by Silvano Seva IU2KWO * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +enum ST775RCmd +{ + ST775R_CMD_NOP = 0x00, + ST775R_CMD_SWRESET = 0x01, + ST775R_CMD_RDID = 0x04, + ST775R_CMD_RDDST = 0x09, + ST775R_CMD_RDDPM = 0x0A, + ST775R_CMD_RDDMADCTL = 0x0B, + ST775R_CMD_RDDCOLMOD = 0x0C, + ST775R_CMD_RDDIM = 0x0D, + ST775R_CMD_RDDSM = 0x0E, + ST775R_CMD_SLPIN = 0x10, + ST775R_CMD_SLPOUT = 0x11, + ST775R_CMD_PTLON = 0x12, + ST775R_CMD_NORON = 0x13, + ST775R_CMD_INVOFF = 0x20, + ST775R_CMD_INVON = 0x21, + ST775R_CMD_GAMSET = 0x26, + ST775R_CMD_DISPOFF = 0x28, + ST775R_CMD_DISPON = 0x29, + ST775R_CMD_CASET = 0x2A, + ST775R_CMD_RASET = 0x2B, + ST775R_CMD_RAMWR = 0x2C, + ST775R_CMD_RGBSET = 0x2D, + ST775R_CMD_RAMRD = 0x2E, + ST775R_CMD_PTLAR = 0x30, + ST775R_CMD_TEOFF = 0x34, + ST775R_CMD_TEON = 0x35, + ST775R_CMD_MADCTL = 0x36, + ST775R_CMD_IDMOFF = 0x38, + ST775R_CMD_IDMON = 0x39, + ST775R_CMD_COLMOD = 0x3A, + ST775R_CMD_RDID1 = 0xDA, + ST775R_CMD_RDID2 = 0xDB, + ST775R_CMD_RDID3 = 0xDC +}; + +static inline void sendCmd(uint8_t cmd) +{ + // Set D/C low (command mode), clear WR and data lines + GPIOD->BSRR = 0x30FF0000; + asm volatile(" mov r1, #21 \n" + "___loop_d: cmp r1, #0 \n" + " itt ne \n" + " subne r1, r1, #1 \n" + " bne ___loop_d \n":::"r1"); + GPIOD->BSRR = cmd | (1 << 13); +} + +static inline void sendData(uint8_t val) +{ + // Set D/C high (data mode), clear WR and data lines + GPIOD->BSRR = 0x20FF1000; + asm volatile(" mov r1, #21 \n" + "___loop_e: cmp r1, #0 \n" + " itt ne \n" + " subne r1, r1, #1 \n" + " bne ___loop_e \n":::"r1"); + GPIOD->BSRR = val | (1 << 13); +} + +void display_init() +{ + backlight_init(); + + /* Set up gpios */ + gpio_setMode(LCD_D0, OUTPUT); + gpio_setMode(LCD_D1, OUTPUT); + gpio_setMode(LCD_D2, OUTPUT); + gpio_setMode(LCD_D3, OUTPUT); + gpio_setMode(LCD_D4, OUTPUT); + gpio_setMode(LCD_D5, OUTPUT); + gpio_setMode(LCD_D6, OUTPUT); + gpio_setMode(LCD_D7, OUTPUT); + gpio_setMode(LCD_WR, OUTPUT); + gpio_setMode(LCD_RD, OUTPUT); + gpio_setMode(LCD_DC, OUTPUT); + gpio_setMode(LCD_RST, OUTPUT); + gpio_setMode(LCD_CS, OUTPUT); + + gpio_setOutputSpeed(LCD_D0, HIGH); + gpio_setOutputSpeed(LCD_D1, HIGH); + gpio_setOutputSpeed(LCD_D2, HIGH); + gpio_setOutputSpeed(LCD_D3, HIGH); + gpio_setOutputSpeed(LCD_D4, HIGH); + gpio_setOutputSpeed(LCD_D5, HIGH); + gpio_setOutputSpeed(LCD_D6, HIGH); + gpio_setOutputSpeed(LCD_D7, HIGH); + gpio_setOutputSpeed(LCD_WR, HIGH); + gpio_setOutputSpeed(LCD_RD, HIGH); + gpio_setOutputSpeed(LCD_DC, HIGH); + gpio_setOutputSpeed(LCD_RST, HIGH); + gpio_setOutputSpeed(LCD_CS, HIGH); + + gpio_clearPin(LCD_RST); /* Put LCD in reset mode */ + gpio_setPin(LCD_CS); /* CS idle state is high level */ + gpio_setPin(LCD_DC); /* Idle state for DC line */ + gpio_setPin(LCD_WR); /* Idle state for WR line */ + gpio_setPin(LCD_RD); /* Idle state for RD line */ + gpio_setPin(LCD_RST); /* Exit from reset */ + + delayMs(10); + + gpio_clearPin(LCD_CS); + + sendCmd(ST775R_CMD_SWRESET); + sendCmd(0xb1); /* Undocumented command */ + sendData(5); + sendData(8); + sendData(5); + sendCmd(0xb2); /* Undocumented command */ + sendData(5); + sendData(8); + sendData(5); + sendCmd(0xb3); /* Undocumented command */ + sendData(5); + sendData(8); + sendData(5); + sendData(5); + sendData(8); + sendData(5); + sendCmd(0xb4); /* Undocumented command */ + sendData(0); + sendCmd(0xb6); /* Undocumented command */ + sendData(0xb4); + sendData(0xf0); + sendCmd(0xc0); /* Undocumented command */ + sendData(0xa2); + sendData(2); + /* sendData(0x85); TODO: see original fw */ + sendData(0x84); + sendCmd(0xc1); /* Undocumented command */ + sendData(5); + sendCmd(0xc2); /* Undocumented command */ + sendData(10); + sendData(0); + sendCmd(0xc3); /* Undocumented command */ + sendData(0x8a); + sendData(0x2a); + sendCmd(0xc4); /* Undocumented command */ + sendData(0x8a); + sendData(0xee); + sendCmd(0xc5); /* Undocumented command */ + sendData(0xe); + sendCmd(ST775R_CMD_MADCTL); + sendData(0xB0); + sendCmd(0xe0); /* Undocumented command */ + sendData(5); + /* sendData(0x28); TODO: see original fw */ + /* sendData(0x28); TODO: see original fw */ + sendData(0x16); + sendData(0xf); + sendData(0x18); + sendData(0x2f); + sendData(0x28); + sendData(0x20); + sendData(0x22); + sendData(0x1f); + sendData(0x1b); + sendData(0x23); + sendData(0x37); + sendData(0); + sendData(7); + sendData(2); + sendData(0x10); + sendCmd(0xe1); /* Undocumented command */ + sendData(7); + /* sendData(0x28); TODO: see original fw */ + /* sendData(0x28); TODO: see original fw */ + sendData(0x1b); + sendData(0xf); + sendData(0x17); + sendData(0x33); + sendData(0x2c); + sendData(0x29); + sendData(0x2e); + sendData(0x30); + sendData(0x30); + sendData(0x39); + sendData(0x3f); + sendData(0); + sendData(7); + sendData(3); + sendData(0x10); + sendCmd(0xf0); /* Undocumented command */ + sendData(1); + sendCmd(0xf6); /* Undocumented command */ + sendData(0); + sendCmd(ST775R_CMD_COLMOD); + sendData(0x05); /* 16bpp - RGB 565 */ + sendCmd(ST775R_CMD_RASET); + sendData(0); + sendData(0); + sendData(0); + sendData(0x7f); /* 128 rows */ + sendCmd(ST775R_CMD_CASET); + sendData(0); + sendData(0); + sendData(0); + sendData(0x9f); /* 160 columns */ + + /* Exit from sleep */ + sendCmd(ST775R_CMD_SLPOUT); + delayMs(120); + + /* Enable display */ + sendCmd(ST775R_CMD_DISPON); + + gpio_setPin(LCD_CS); +} + +void display_terminate() +{ +} + +void display_renderRows(uint8_t startRow, uint8_t endRow, void *fb) +{ + /* + * Put screen data lines back to output mode, since they are in common with + * keyboard buttons and the keyboard driver sets them as inputs. + */ + gpio_setMode(LCD_D0, OUTPUT); + gpio_setMode(LCD_D1, OUTPUT); + gpio_setMode(LCD_D2, OUTPUT); + gpio_setMode(LCD_D3, OUTPUT); + gpio_setMode(LCD_D4, OUTPUT); + gpio_setMode(LCD_D5, OUTPUT); + gpio_setMode(LCD_D6, OUTPUT); + gpio_setMode(LCD_D7, OUTPUT); + + /* + * Select the display, configure start and end rows in display driver and + * write to display memory. + */ + gpio_clearPin(LCD_CS); + + sendCmd(ST775R_CMD_RASET); + sendData(0x00); + sendData(startRow); + sendData(0x00); + sendData(endRow); + sendCmd(ST775R_CMD_RAMWR); + + for(uint8_t y = startRow; y < endRow; y++) + { + for(uint8_t x = 0; x < CONFIG_SCREEN_WIDTH; x++) + { + size_t pos = x + y * CONFIG_SCREEN_WIDTH; + uint16_t pixel = ((uint16_t *) fb)[pos]; + sendData((pixel >> 8) & 0xff); + sendData(pixel & 0xff); + } + } + + gpio_setPin(LCD_CS); +} + +void display_render(void *fb) +{ + display_renderRows(0, CONFIG_SCREEN_HEIGHT, fb); +} + +void display_setContrast(uint8_t contrast) +{ + /* This controller does not support contrast regulation */ + (void) contrast; +}