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 contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* 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) {}