You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/core/embed/trezorhal/stm32f4/displays/ug-2828tswig01.c

373 lines
11 KiB

/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include TREZOR_BOARD
#include "display.h"
#include "memzero.h"
#include STM32_HAL_H
// FSMC/FMC Bank 1 - NOR/PSRAM 1
#define DISPLAY_MEMORY_BASE 0x60000000
#define DISPLAY_MEMORY_PIN 16
#define CMD(X) (*((__IO uint8_t *)((uint32_t)(DISPLAY_MEMORY_BASE))) = (X))
#define ADDR \
(*((__IO uint8_t *)((uint32_t)(DISPLAY_MEMORY_BASE | \
(1 << DISPLAY_MEMORY_PIN)))))
#define DATA(X) (ADDR) = (X)
static int DISPLAY_BACKLIGHT = -1;
static int DISPLAY_ORIENTATION = -1;
struct {
uint8_t RAM[DISPLAY_RESY / 8][DISPLAY_RESX];
uint32_t row;
uint32_t col;
uint32_t window_x0;
uint32_t window_x1;
uint32_t window_y0;
uint32_t window_y1;
} DISPLAY_STATE;
static void display_set_page_and_col(uint8_t page, uint8_t col) {
if (page < (DISPLAY_RESY / 8)) {
CMD(0xB0 | (page & 0xF));
if (col < DISPLAY_RESX) {
CMD(0x10 | ((col & 0x70) >> 4));
CMD(0x00 | (col & 0x0F));
} else {
// Reset column to start
CMD(0x10);
CMD(0x00);
}
}
}
void display_pixeldata(uint16_t c) {
uint8_t data = DISPLAY_STATE.RAM[DISPLAY_STATE.row / 8][DISPLAY_STATE.col];
uint8_t bit = 1 << (DISPLAY_STATE.row % 8);
// set to white if highest bits of all R, G, B values are set to 1
// bin(10000 100000 10000) = hex(0x8410)
// otherwise set to black
if (c & 0x8410) {
data |= bit;
} else {
data &= ~bit;
}
DISPLAY_STATE.RAM[DISPLAY_STATE.row / 8][DISPLAY_STATE.col] = data;
DISPLAY_STATE.col++;
if (DISPLAY_STATE.col > DISPLAY_STATE.window_x1) {
// next line
DISPLAY_STATE.col = DISPLAY_STATE.window_x0;
DISPLAY_STATE.row++;
if (DISPLAY_STATE.row > DISPLAY_STATE.window_y1) {
// reached end of the window, go to start
DISPLAY_STATE.row = DISPLAY_STATE.window_y1;
}
// set display to start of next line, sets also page, even if it stays on
// the same one
display_set_page_and_col(DISPLAY_STATE.row / 8, DISPLAY_STATE.col);
}
}
void display_pixeldata_dirty(void) {}
void display_reset_state(void) {
memzero(DISPLAY_STATE.RAM, sizeof(DISPLAY_STATE.RAM));
DISPLAY_STATE.row = 0;
DISPLAY_STATE.col = 0;
DISPLAY_STATE.window_x0 = 0;
DISPLAY_STATE.window_x1 = DISPLAY_RESX - 1;
DISPLAY_STATE.window_y0 = 0;
DISPLAY_STATE.window_y1 = DISPLAY_RESY - 1;
}
static void __attribute__((unused)) display_sleep(void) {
CMD(0xAE); // DISPOFF: Display Off
HAL_Delay(5);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_8, GPIO_PIN_RESET); // Vpp disable
}
static void display_unsleep(void) {
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_8, GPIO_PIN_SET); // Vpp enable
HAL_Delay(100); // 100 ms mandatory wait
CMD(0xAF); // Display ON
}
void display_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
if (x1 >= DISPLAY_RESX) {
x1 = DISPLAY_RESX - 1;
}
if (y1 >= DISPLAY_RESY) {
y1 = DISPLAY_RESY - 1;
}
if (x0 < DISPLAY_RESX && x1 < DISPLAY_RESX && x0 <= x1 && y0 < DISPLAY_RESY &&
y1 < DISPLAY_RESY && y0 <= y1) {
DISPLAY_STATE.window_x0 = x0;
DISPLAY_STATE.window_x1 = x1;
DISPLAY_STATE.window_y0 = y0;
DISPLAY_STATE.window_y1 = y1;
DISPLAY_STATE.row = y0;
DISPLAY_STATE.col = x0;
display_set_page_and_col(DISPLAY_STATE.row / 8, DISPLAY_STATE.col);
}
}
int display_orientation(int degrees) {
if (degrees != DISPLAY_ORIENTATION) {
if (degrees == 0 || degrees == 180) {
DISPLAY_ORIENTATION = degrees;
if (degrees == 0) {
// Set Segment Re-map: (A0H - A1H)
CMD(0xA1);
// Set COM Output Scan Direction
CMD(0xC8);
}
if (degrees == 180) {
// Set Segment Re-map: (A0H - A1H)
CMD(0xA0);
// Set COM Output Scan Direction
CMD(0xC0);
}
}
}
return DISPLAY_ORIENTATION;
}
int display_get_orientation(void) { return DISPLAY_ORIENTATION; }
int display_backlight(int val) {
if (DISPLAY_BACKLIGHT != val && val >= 0 && val <= 255) {
DISPLAY_BACKLIGHT = val;
// Set Contrast Control Register: (Double Bytes Command)
CMD(0x81);
CMD(val & 0xFF);
}
return DISPLAY_BACKLIGHT;
}
static void send_init_seq_SH1107(void) {
// Display OFF
CMD(0xAE);
// Set Display Clock Divide Ratio/Oscillator Frequency: (Double Bytes Command)
CMD(0xD5);
// Divide ratio 0, Oscillator Frequency +0%
CMD(0x50);
// Set Memory Addressing Mode - page addressing mode
CMD(0x20);
// Set Contrast Control Register: (Double Bytes Command)
CMD(0x81);
CMD(0x8F);
// Set DC-DC Setting: (Double Bytes Command)
CMD(0xAD);
CMD(0x8A);
// Set Segment Re-map: (A0H - A1H)
// CMD(0xA0);
CMD(0xA1);
// Set COM Output Scan Direction
CMD(0xC8);
// CMD(0xC0);
// Set Display Start Line:Double Bytes Command
CMD(0xDC);
CMD(0x00);
// Set Display Offset:Double Bytes Command
CMD(0xD3);
CMD(0x00);
// Set Discharge / Pre-Charge Period (Double Bytes Command)
CMD(0xD9);
CMD(0x22);
// Set VCOM Deselect Level
CMD(0xDB);
CMD(0x35);
// Set Multiplex Ratio
CMD(0xA8);
CMD(0x7F);
// Set Page
CMD(0xB0);
// Set Column
CMD(0x00);
CMD(0x10);
// Set Entire Display Off
// to be clear, this command turns of the function
// which turns entire display on, but it does not clear
// the data in display RAM
CMD(0xA4);
// Set Normal Display
CMD(0xA6);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_8, GPIO_PIN_SET); // Vpp enable
// Vpp stabilization period
HAL_Delay(100);
display_set_window(0, 0, MAX_DISPLAY_RESX - 1, MAX_DISPLAY_RESY - 1);
for (int i = 0; i < DISPLAY_RESY; i++) {
for (int j = 0; j < DISPLAY_RESX; j++) {
display_pixeldata(0);
}
}
// Display ON
CMD(0xAF);
}
void display_init_seq(void) {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_RESET); // LCD_RST/PC14
// wait 10 milliseconds. only needs to be low for 10 microseconds.
// my dev display module ties display reset and touch panel reset together.
// keeping this low for max(display_reset_time, ctpm_reset_time) aids
// development and does not hurt.
HAL_Delay(10);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_SET); // LCD_RST/PC14
// max wait time for hardware reset is 120 milliseconds
// (experienced display flakiness using only 5ms wait before sending commands)
HAL_Delay(120);
send_init_seq_SH1107();
display_unsleep();
}
void display_init(void) {
// init peripherals
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_FMC_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStructure;
// LCD_RST/PC14
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStructure.Alternate = 0;
GPIO_InitStructure.Pin = GPIO_PIN_14;
// default to keeping display in reset
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_RESET);
HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
// VPP Enable
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pull = GPIO_PULLDOWN;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStructure.Alternate = 0;
GPIO_InitStructure.Pin = GPIO_PIN_8;
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_8, GPIO_PIN_RESET);
HAL_GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStructure.Alternate = GPIO_AF12_FMC;
// LCD_CS/PD7 LCD_RS/PD11 LCD_RD/PD4 LCD_WR/PD5
GPIO_InitStructure.Pin = GPIO_PIN_7 | GPIO_PIN_11 | GPIO_PIN_4 | GPIO_PIN_5;
HAL_GPIO_Init(GPIOD, &GPIO_InitStructure);
// LCD_D0/PD14 LCD_D1/PD15 LCD_D2/PD0 LCD_D3/PD1
GPIO_InitStructure.Pin = GPIO_PIN_14 | GPIO_PIN_15 | GPIO_PIN_0 | GPIO_PIN_1;
HAL_GPIO_Init(GPIOD, &GPIO_InitStructure);
// LCD_D4/PE7 LCD_D5/PE8 LCD_D6/PE9 LCD_D7/PE10
GPIO_InitStructure.Pin = GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10;
HAL_GPIO_Init(GPIOE, &GPIO_InitStructure);
// Reference UM1725 "Description of STM32F4 HAL and LL drivers",
// section 64.2.1 "How to use this driver"
SRAM_HandleTypeDef external_display_data_sram = {0};
external_display_data_sram.Instance = FMC_NORSRAM_DEVICE;
external_display_data_sram.Extended = FMC_NORSRAM_EXTENDED_DEVICE;
external_display_data_sram.Init.NSBank = FMC_NORSRAM_BANK1;
external_display_data_sram.Init.DataAddressMux = FMC_DATA_ADDRESS_MUX_DISABLE;
external_display_data_sram.Init.MemoryType = FMC_MEMORY_TYPE_SRAM;
external_display_data_sram.Init.MemoryDataWidth = FMC_NORSRAM_MEM_BUS_WIDTH_8;
external_display_data_sram.Init.BurstAccessMode =
FMC_BURST_ACCESS_MODE_DISABLE;
external_display_data_sram.Init.WaitSignalPolarity =
FMC_WAIT_SIGNAL_POLARITY_LOW;
external_display_data_sram.Init.WrapMode = FMC_WRAP_MODE_DISABLE;
external_display_data_sram.Init.WaitSignalActive = FMC_WAIT_TIMING_BEFORE_WS;
external_display_data_sram.Init.WriteOperation = FMC_WRITE_OPERATION_ENABLE;
external_display_data_sram.Init.WaitSignal = FMC_WAIT_SIGNAL_DISABLE;
external_display_data_sram.Init.ExtendedMode = FMC_EXTENDED_MODE_DISABLE;
external_display_data_sram.Init.AsynchronousWait =
FMC_ASYNCHRONOUS_WAIT_DISABLE;
external_display_data_sram.Init.WriteBurst = FMC_WRITE_BURST_DISABLE;
external_display_data_sram.Init.ContinuousClock =
FMC_CONTINUOUS_CLOCK_SYNC_ONLY;
external_display_data_sram.Init.PageSize = FMC_PAGE_SIZE_NONE;
// reference RM0090 section 37.5 Table 259, 37.5.4, Mode 1 SRAM, and 37.5.6
FMC_NORSRAM_TimingTypeDef normal_mode_timing = {0};
normal_mode_timing.AddressSetupTime = 10;
normal_mode_timing.AddressHoldTime = 10;
normal_mode_timing.DataSetupTime = 10;
normal_mode_timing.BusTurnAroundDuration = 0;
normal_mode_timing.CLKDivision = 2;
normal_mode_timing.DataLatency = 2;
normal_mode_timing.AccessMode = FMC_ACCESS_MODE_A;
HAL_SRAM_Init(&external_display_data_sram, &normal_mode_timing, NULL);
display_init_seq();
}
void display_sync(void) {}
void display_refresh(void) {
for (int y = 0; y < (DISPLAY_RESY / 8); y++) {
display_set_page_and_col(y, 0);
for (int x = 0; x < DISPLAY_RESX; x++) {
DATA(DISPLAY_STATE.RAM[y][x]);
}
}
}
void display_reinit(void) {}
const char *display_save(const char *prefix) { return NULL; }
void display_clear_save(void) {}
void display_finish_actions(void) {}