/* * lcd.c * * Copyright (c) 2023 Thomas Buck (thomas@xythobuz.de) * * 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. * * See . */ #include #include "pico/stdlib.h" #include "hardware/spi.h" #include "hardware/pwm.h" #include "driver_st7789.h" #include "config.h" #include "log.h" #include "lcd.h" #define LCD_PIN_DC 8 #define LCD_PIN_CS 9 #define LCD_PIN_CLK 10 #define LCD_PIN_DIN 11 #define LCD_PIN_RST 12 #define LCD_PIN_BL 13 #define ST7789_PICO_COLUMN LCD_HEIGHT #define ST7789_PICO_ROW LCD_WIDTH #define ST7789_PICO_ACCESS (ST7789_ORDER_PAGE_TOP_TO_BOTTOM | \ ST7789_ORDER_COLUMN_LEFT_TO_RIGHT | \ ST7789_ORDER_PAGE_COLUMN_NORMAL | \ ST7789_ORDER_LINE_TOP_TO_BOTTOM | \ ST7789_ORDER_COLOR_RGB | \ ST7789_ORDER_REFRESH_LEFT_TO_RIGHT) #define ST7789_PICO_RGB_INTERFACE_COLOR_FORMAT 0 #define ST7789_PICO_CONTROL_INTERFACE_COLOR_FORMAT ST7789_CONTROL_INTERFACE_COLOR_FORMAT_16_BIT #define ST7789_PICO_PORCH_NORMAL_BACK 0x0C #define ST7789_PICO_PORCH_NORMAL_FRONT 0x0C #define ST7789_PICO_PORCH_ENABLE ST7789_BOOL_FALSE #define ST7789_PICO_PORCH_IDEL_BACK 0x03 #define ST7789_PICO_PORCH_IDEL_FRONT 0x03 #define ST7789_PICO_PORCH_PART_BACK 0x03 #define ST7789_PICO_PORCH_PART_FRONT 0x03 #define ST7789_PICO_VGHS ST7789_VGHS_13P26_V #define ST7789_PICO_VGLS_NEGATIVE ST7789_VGLS_NEGATIVE_10P43 #define ST7789_PICO_VCOMS 0.725f #define ST7789_PICO_XMY ST7789_BOOL_FALSE #define ST7789_PICO_XBGR ST7789_BOOL_TRUE #define ST7789_PICO_XINV ST7789_BOOL_FALSE #define ST7789_PICO_XMX ST7789_BOOL_TRUE #define ST7789_PICO_XMH ST7789_BOOL_TRUE #define ST7789_PICO_XMV ST7789_BOOL_FALSE #define ST7789_PICO_XGS ST7789_BOOL_FALSE #define ST7789_PICO_VDV_VRH_FROM ST7789_VDV_VRH_FROM_CMD #define ST7789_PICO_VRHS 4.45f #define ST7789_PICO_VDV 0.0f #define ST7789_PICO_INVERSION_SELECTION ST7789_INVERSION_SELECTION_DOT #define ST7789_PICO_FRAME_RATE ST7789_FRAME_RATE_60_HZ #define ST7789_PICO_AVDD ST7789_AVDD_6P8_V #define ST7789_PICO_AVCL_NEGTIVE ST7789_AVCL_NEGTIVE_4P8_V #define ST7789_PICO_VDS ST7789_VDS_2P3_V #define ST7789_PICO_POSITIVE_VOLTAGE_GAMMA {0xD0, 0x04, 0x0D, 0x11, 0x13, 0x2B, 0x3F, \ 0x54, 0x4C, 0x18, 0x0D, 0x0B, 0x1F, 0x23} #define ST7789_PICO_NEGATIVA_VOLTAGE_GAMMA {0xD0, 0x04, 0x0C, 0x11, 0x13, 0x2C, 0x3F, \ 0x44, 0x51, 0x2F, 0x1F, 0x1F, 0x20, 0x23} static st7789_handle_t gs_handle; static uint16_t bl_value = 0; static uint8_t st7789_interface_spi_init(void) { // Use SPI1 at 100MHz spi_init(spi1, 100 * 1000 * 1000); gpio_set_function(LCD_PIN_CLK, GPIO_FUNC_SPI); gpio_set_function(LCD_PIN_DIN, GPIO_FUNC_SPI); // Chip select is active-low, so we'll initialise it to a driven-high state gpio_init(LCD_PIN_CS); gpio_set_dir(LCD_PIN_CS, GPIO_OUT); gpio_put(LCD_PIN_CS, 1); spi_set_format(spi1, 8, // Number of bits per transfer 0, // Polarity (CPOL) 0, // Phase (CPHA) SPI_MSB_FIRST); return 0; } static uint8_t st7789_interface_spi_deinit(void) { spi_deinit(spi1); gpio_deinit(LCD_PIN_CS); return 0; } static uint8_t st7789_interface_spi_write_cmd(uint8_t *buf, uint16_t len) { gpio_put(LCD_PIN_CS, 0); spi_write_blocking(spi1, buf, len); gpio_put(LCD_PIN_CS, 1); return 0; } static void st7789_interface_delay_ms(uint32_t ms) { sleep_ms(ms); } static void st7789_interface_debug_print(const char *const fmt, ...) { va_list args; va_start(args, fmt); debug_log_va(true, fmt, args); va_end(args); } static uint8_t st7789_interface_cmd_data_gpio_init(void) { gpio_init(LCD_PIN_DC); gpio_set_dir(LCD_PIN_DC, GPIO_OUT); return 0; } static uint8_t st7789_interface_cmd_data_gpio_deinit(void) { gpio_deinit(LCD_PIN_DC); return 0; } static uint8_t st7789_interface_cmd_data_gpio_write(uint8_t value) { gpio_put(LCD_PIN_DC, value); return 0; } static uint8_t st7789_interface_reset_gpio_init(void) { gpio_init(LCD_PIN_RST); gpio_set_dir(LCD_PIN_RST, GPIO_OUT); return 0; } static uint8_t st7789_interface_reset_gpio_deinit(void) { gpio_deinit(LCD_PIN_RST); return 0; } static uint8_t st7789_interface_reset_gpio_write(uint8_t value) { gpio_put(LCD_PIN_RST, value); return 0; } void lcd_init(void) { uint8_t reg; DRIVER_ST7789_LINK_INIT(&gs_handle, st7789_handle_t); DRIVER_ST7789_LINK_SPI_INIT(&gs_handle, st7789_interface_spi_init); DRIVER_ST7789_LINK_SPI_DEINIT(&gs_handle, st7789_interface_spi_deinit); DRIVER_ST7789_LINK_SPI_WRITE_COMMAND(&gs_handle, st7789_interface_spi_write_cmd); DRIVER_ST7789_LINK_COMMAND_DATA_GPIO_INIT(&gs_handle, st7789_interface_cmd_data_gpio_init); DRIVER_ST7789_LINK_COMMAND_DATA_GPIO_DEINIT(&gs_handle, st7789_interface_cmd_data_gpio_deinit); DRIVER_ST7789_LINK_COMMAND_DATA_GPIO_WRITE(&gs_handle, st7789_interface_cmd_data_gpio_write); DRIVER_ST7789_LINK_RESET_GPIO_INIT(&gs_handle, st7789_interface_reset_gpio_init); DRIVER_ST7789_LINK_RESET_GPIO_DEINIT(&gs_handle, st7789_interface_reset_gpio_deinit); DRIVER_ST7789_LINK_RESET_GPIO_WRITE(&gs_handle, st7789_interface_reset_gpio_write); DRIVER_ST7789_LINK_DELAY_MS(&gs_handle, st7789_interface_delay_ms); DRIVER_ST7789_LINK_DEBUG_PRINT(&gs_handle, st7789_interface_debug_print); st7789_init(&gs_handle); st7789_set_column(&gs_handle, ST7789_PICO_COLUMN); st7789_set_row(&gs_handle, ST7789_PICO_ROW); st7789_set_memory_data_access_control(&gs_handle, ST7789_PICO_ACCESS); st7789_set_interface_pixel_format(&gs_handle, ST7789_PICO_RGB_INTERFACE_COLOR_FORMAT, ST7789_PICO_CONTROL_INTERFACE_COLOR_FORMAT); st7789_set_porch(&gs_handle, ST7789_PICO_PORCH_NORMAL_BACK, ST7789_PICO_PORCH_NORMAL_FRONT, ST7789_PICO_PORCH_ENABLE, ST7789_PICO_PORCH_IDEL_BACK, ST7789_PICO_PORCH_IDEL_FRONT, ST7789_PICO_PORCH_PART_BACK, ST7789_PICO_PORCH_PART_FRONT); st7789_set_gate_control(&gs_handle, ST7789_PICO_VGHS, ST7789_PICO_VGLS_NEGATIVE); st7789_vcom_convert_to_register(&gs_handle, ST7789_PICO_VCOMS, ®); st7789_set_vcoms(&gs_handle, reg); st7789_set_lcm_control(&gs_handle, ST7789_PICO_XMY, ST7789_PICO_XBGR, ST7789_PICO_XINV, ST7789_PICO_XMX, ST7789_PICO_XMH, ST7789_PICO_XMV, ST7789_PICO_XGS); st7789_set_vdv_vrh_from(&gs_handle, ST7789_PICO_VDV_VRH_FROM); st7789_vrhs_convert_to_register(&gs_handle, ST7789_PICO_VRHS, ®); st7789_set_vrhs(&gs_handle, reg); st7789_vdv_convert_to_register(&gs_handle, ST7789_PICO_VDV, ®); st7789_set_vdv(&gs_handle, reg); st7789_set_frame_rate(&gs_handle, ST7789_PICO_INVERSION_SELECTION, ST7789_PICO_FRAME_RATE); st7789_set_power_control_1(&gs_handle, ST7789_PICO_AVDD, ST7789_PICO_AVCL_NEGTIVE, ST7789_PICO_VDS); uint8_t param_positive[14] = ST7789_PICO_POSITIVE_VOLTAGE_GAMMA; st7789_set_positive_voltage_gamma_control(&gs_handle, param_positive); uint8_t param_negative[14] = ST7789_PICO_NEGATIVA_VOLTAGE_GAMMA; st7789_set_negative_voltage_gamma_control(&gs_handle, param_negative); st7789_display_inversion_on(&gs_handle); st7789_sleep_out(&gs_handle); st7789_display_on(&gs_handle); st7789_clear(&gs_handle); // backlight uint bl_slice = pwm_gpio_to_slice_num(LCD_PIN_BL); uint bl_channel = pwm_gpio_to_channel(LCD_PIN_BL); gpio_set_function(LCD_PIN_BL, GPIO_FUNC_PWM); pwm_set_wrap(bl_slice, 0xFFFF); pwm_set_clkdiv(bl_slice, 1.0f); pwm_set_chan_level(bl_slice, bl_channel, bl_value); pwm_set_enabled(bl_slice, true); } uint16_t lcd_get_backlight(void) { return bl_value; } void lcd_set_backlight(uint16_t value) { uint bl_slice = pwm_gpio_to_slice_num(LCD_PIN_BL); uint bl_channel = pwm_gpio_to_channel(LCD_PIN_BL); bl_value = value; pwm_set_chan_level(bl_slice, bl_channel, bl_value); } void lcd_clear(void) { st7789_clear(&gs_handle); } void lcd_write_point(uint16_t x, uint16_t y, uint32_t color) { st7789_draw_point(&gs_handle, ST7789_PICO_COLUMN - y - 1, x, color); } void lcd_write_rect(uint16_t left, uint16_t top, uint16_t right, uint16_t bottom, uint32_t color) { if (right >= ST7789_PICO_ROW) { right = ST7789_PICO_ROW - 1; } if (bottom >= ST7789_PICO_COLUMN) { bottom = ST7789_PICO_COLUMN - 1; } if (top >= bottom) { return; } if (left >= right) { return; } // handle, left, top, right, bottom, color); st7789_fill_rect(&gs_handle, 240 - bottom - 1, left, 240 - top - 1, right, color); } uint32_t from_hsv(float h, float s, float v) { float i = floorf(h * 6.0f); float f = h * 6.0f - i; v *= 255.0f; float p = v * (1.0f - s); float q = v * (1.0f - f * s); float t = v * (1.0f - (1.0f - f) * s); switch (((uint32_t)i) % 6) { case 0: return RGB_565((uint32_t)v, (uint32_t)t, (uint32_t)p); case 1: return RGB_565((uint32_t)q, (uint32_t)v, (uint32_t)p); case 2: return RGB_565((uint32_t)p, (uint32_t)v, (uint32_t)t); case 3: return RGB_565((uint32_t)p, (uint32_t)q, (uint32_t)v); case 4: return RGB_565((uint32_t)t, (uint32_t)p, (uint32_t)v); case 5: return RGB_565((uint32_t)v, (uint32_t)p, (uint32_t)q); default: return RGB_565(0, 0, 0); } }