From 20524e41c1ee5f1ae620eb8ad8190951ae32bd26 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Sun, 21 Jun 2026 22:53:30 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4=20=D1=82=D0=B5=D0=BA?= =?UTF-8?q?=D1=81=D1=82=D0=B0=20=D0=B8=20=D1=87=D0=B8=D1=81=D0=B5=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Inc/DS_ST7789V.h | 81 ++++++++++++- Src/DS_ST7789V.c | 301 ++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 338 insertions(+), 44 deletions(-) diff --git a/Inc/DS_ST7789V.h b/Inc/DS_ST7789V.h index 3eb746f..1c16153 100644 --- a/Inc/DS_ST7789V.h +++ b/Inc/DS_ST7789V.h @@ -34,12 +34,12 @@ #define ST7789V_CMD_COLMOD 0x3A /* Варианты ориентации экрана */ -typedef enum { +typedef enum DS_ST7789V_Orientation{ DS_ST7789V_ORIENTATION_PORTRAIT = 0, DS_ST7789V_ORIENTATION_LANDSCAPE, DS_ST7789V_ORIENTATION_PORTRAIT_REV, DS_ST7789V_ORIENTATION_LANDSCAPE_REV -} DS_ST7789V_Orientation_t; +} DS_ST7789V_Orientation; /* Структура конфигурации пинов (аналогично DS_Button) */ typedef struct { @@ -55,7 +55,7 @@ typedef struct DS_ST7789V { DS_ST7789V_Pin_t RES; // Пин аппаратного сброса uint16_t Width; // Текущая ширина с учетом поворота uint16_t Height; // Текущая высота с учетом поворота - DS_ST7789V_Orientation_t Orientation; // Текущая ориентация + DS_ST7789V_Orientation Orientation; // Текущая ориентация } DS_ST7789V; /**@brief Инициализация дескриптора дисплея и передача начальных команд @@ -68,7 +68,7 @@ void DS_ST7789V_Init(DS_ST7789V *lcd, SPI_HandleTypeDef *hspi, /**@brief Установка ориентации экрана*/ void DS_ST7789V_SetOrientation(DS_ST7789V *lcd, - DS_ST7789V_Orientation_t orientation); + DS_ST7789V_Orientation orientation); /**@brief Заливка экрана или выделенной области цветом (RGB565)*/ void DS_ST7789V_FillRect(DS_ST7789V *lcd, uint16_t x, uint16_t y, uint16_t w, @@ -80,4 +80,77 @@ void DS_ST7789V_WriteCommand(DS_ST7789V *lcd, uint8_t cmd); void DS_ST7789V_WriteData(DS_ST7789V *lcd, uint8_t *data, uint16_t size); +/** + * @brief Отрисовка одиночного пикселя заданным цветом (RGB565) + * @param lcd Указатель на структуру дисплея + * @param x Координата по горизонтали + * @param y Координата по вертикали + * @param color Цвет пикселя в формате 16-бит RGB565 + */ +void DS_ST7789V_DrawPixel(DS_ST7789V *lcd, uint16_t x, uint16_t y, uint16_t color); +void DS_ST7789V_DrawHLine(DS_ST7789V *lcd, uint16_t x, uint16_t y, uint16_t length, uint16_t color); +void DS_ST7789V_DrawVLine(DS_ST7789V *lcd, uint16_t x, uint16_t y, uint16_t length, uint16_t color); + + +/* Компактный растровый шрифт 5x7 (95 символов ASCII, начиная с пробела 0x20) */ +static const uint8_t DS_Font5x7[] = { + 0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x5F,0x00,0x00, 0x00,0x07,0x00,0x07,0x00, 0x14,0x7F,0x14,0x7F,0x14, + 0x24,0x2A,0x7F,0x2A,0x12, 0x23,0x13,0x08,0x64,0x62, 0x36,0x49,0x55,0x22,0x50, 0x00,0x05,0x03,0x00,0x00, + 0x00,0x1C,0x22,0x41,0x00, 0x00,0x41,0x22,0x1C,0x00, 0x08,0x2A,0x1C,0x2A,0x08, 0x08,0x08,0x3E,0x08,0x08, + 0x00,0x50,0x30,0x00,0x00, 0x08,0x08,0x08,0x08,0x08, 0x00,0x60,0x60,0x00,0x00, 0x20,0x10,0x08,0x04,0x02, + 0x3E,0x51,0x49,0x45,0x3E, 0x00,0x42,0x7F,0x40,0x00, 0x42,0x61,0x51,0x49,0x46, 0x21,0x41,0x45,0x4B,0x31, + 0x18,0x14,0x12,0x7F,0x10, 0x27,0x45,0x45,0x45,0x39, 0x3C,0x4A,0x49,0x49,0x30, 0x01,0x71,0x09,0x05,0x03, + 0x36,0x49,0x49,0x49,0x36, 0x06,0x49,0x49,0x29,0x1E, 0x00,0x36,0x36,0x00,0x00, 0x00,0x56,0x36,0x00,0x00, + 0x00,0x08,0x14,0x22,0x41, 0x14,0x14,0x14,0x14,0x14, 0x41,0x22,0x14,0x08,0x00, 0x02,0x01,0x51,0x09,0x06, + 0x32,0x49,0x79,0x41,0x3E, 0x7E,0x11,0x11,0x11,0x7E, 0x7F,0x49,0x49,0x49,0x36, 0x3E,0x41,0x41,0x41,0x22, + 0x7F,0x41,0x41,0x22,0x1C, 0x7F,0x49,0x49,0x49,0x41, 0x7F,0x09,0x09,0x09,0x01, 0x3E,0x41,0x41,0x51,0x72, + 0x7F,0x08,0x08,0x08,0x7F, 0x00,0x41,0x7F,0x41,0x00, 0x20,0x40,0x41,0x3F,0x01, 0x7F,0x08,0x14,0x22,0x41, + 0x7F,0x40,0x40,0x40,0x40, 0x7F,0x02,0x0C,0x02,0x7F, 0x7F,0x04,0x08,0x10,0x7F, 0x3E,0x41,0x41,0x41,0x3E, + 0x7F,0x09,0x09,0x09,0x06, 0x3E,0x41,0x51,0x21,0x5E, 0x7F,0x09,0x19,0x29,0x46, 0x46,0x49,0x49,0x49,0x31, + 0x01,0x01,0x7F,0x01,0x01, 0x3F,0x40,0x40,0x40,0x3F, 0x1F,0x20,0x40,0x20,0x1F, 0x3F,0x40,0x38,0x40,0x3F, + 0x63,0x14,0x08,0x14,0x63, 0x03,0x04,0x78,0x04,0x03, 0x61,0x51,0x49,0x45,0x43, 0x00,0x7F,0x41,0x41,0x00, + 0x02,0x04,0x08,0x10,0x20, 0x00,0x41,0x41,0x7F,0x00, 0x04,0x02,0x01,0x02,0x04, 0x40,0x40,0x40,0x40,0x40, + 0x00,0x01,0x02,0x04,0x00, 0x20,0x54,0x54,0x54,0x78, 0x7F,0x48,0x44,0x44,0x38, 0x38,0x44,0x44,0x44,0x20, + 0x38,0x44,0x44,0x48,0x7F, 0x38,0x54,0x54,0x54,0x18, 0x08,0x7E,0x09,0x01,0x02, 0x98,0xA4,0xA4,0xA4,0x7C, + 0x7F,0x08,0x04,0x04,0x38, 0x00,0x44,0x7D,0x40,0x00, 0x20,0x40,0x44,0x3D,0x00, 0x7F,0x10,0x28,0x44,0x00, + 0x00,0x41,0x7F,0x40,0x00, 0x7C,0x04,0x18,0x04,0x78, 0x7C,0x08,0x04,0x04,0x78, 0x38,0x44,0x44,0x44,0x38, + 0xFC,0x24,0x24,0x24,0x18, 0x18,0x24,0x24,0x24,0xFC, 0x7C,0x08,0x04,0x04,0x08, 0x48,0x54,0x54,0x54,0x20, + 0x04,0x3F,0x44,0x40,0x20, 0x3C,0x40,0x40,0x20,0x7C, 0x1C,0x20,0x40,0x20,0x1C, 0x3C,0x40,0x30,0x40,0x3C, + 0x44,0x28,0x10,0x28,0x44, 0x4C,0x50,0x50,0x50,0x3C, 0x44,0x64,0x54,0x4C,0x44, 0x00,0x08,0x36,0x41,0x00, + 0x00,0x00,0x77,0x00,0x00, 0x00,0x41,0x36,0x08,0x00, 0x08,0x08,0x2A,0x1C,0x08 +}; + +/** + * @brief Отрисовка одного символа текста с масштабированием + */ +void DS_ST7789V_DrawChar(DS_ST7789V *lcd, uint16_t x, uint16_t y, char ch, uint16_t color, uint16_t bg_color, uint8_t scale); + +/** + * @brief Отрисовка строки текста с масштабированием + */ +void DS_ST7789V_DrawString(DS_ST7789V *lcd, uint16_t x, uint16_t y, const char *str, uint16_t color, uint16_t bg_color, uint8_t scale); + +/** + * @brief Вывод целого числа на дисплей + */ +void DS_ST7789V_DrawInt(DS_ST7789V *lcd, uint16_t x, uint16_t y, int32_t num, uint16_t color, uint16_t bg_color, uint8_t scale); + +/** + * @brief Вывод числа с плавающей точкой (float) на дисплей + * @param decimals Количество знаков после запятой (обычно 1 или 2) + */ +void DS_ST7789V_DrawFloat(DS_ST7789V *lcd, uint16_t x, uint16_t y, float num, uint8_t decimals, uint16_t color, uint16_t bg_color, uint8_t scale); + + +/** + * @brief Прозрачная отрисовка одного символа текста с масштабированием + */ +void DS_ST7789V_DrawCharTransparent(DS_ST7789V *lcd, uint16_t x, uint16_t y, char ch, uint16_t color, uint8_t scale); + +/** + * @brief Прозрачная отрисовка строки текста с масштабированием + */ +void DS_ST7789V_DrawStringTransparent(DS_ST7789V *lcd, uint16_t x, uint16_t y, const char *str, uint16_t color, uint8_t scale); + + #endif // DS_ST7789V_H \ No newline at end of file diff --git a/Src/DS_ST7789V.c b/Src/DS_ST7789V.c index 295efb2..7d4b26f 100644 --- a/Src/DS_ST7789V.c +++ b/Src/DS_ST7789V.c @@ -26,30 +26,30 @@ void DS_ST7789V_WriteData(DS_ST7789V *lcd, uint8_t *data, uint16_t size) { CS_IDLE(lcd); } -static void DS_ST7789V_SetAddressWindow(DS_ST7789V *lcd, uint16_t x0, - uint16_t y0, uint16_t x1, uint16_t y1) { - uint8_t data[4]; +static void DS_ST7789V_SetAddressWindow(DS_ST7789V *lcd, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { + uint8_t data[4]; - /* Настройка столбцов (X) */ - DS_ST7789V_WriteCommand(lcd, ST7789V_CMD_CASET); - data[0] = (x0 >> 8) & 0xFF; - data[1] = x0 & 0xFF; - data[2] = (x1 >> 8) & 0xFF; - data[3] = x1 & 0xFF; - DS_ST7789V_WriteData(lcd, data, 4); + /* Настройка столбцов (X) */ + DS_ST7789V_WriteCommand(lcd, ST7789V_CMD_CASET); + data[0] = (x0 >> 8) & 0xFF; + data[1] = x0 & 0xFF; + data[2] = (x1 >> 8) & 0xFF; + data[3] = x1 & 0xFF; + DS_ST7789V_WriteData(lcd, data, 4); // Передаем пачку строго из 4 байт - /* Настройка строк (Y) */ - DS_ST7789V_WriteCommand(lcd, ST7789V_CMD_RASET); - data[0] = (y0 >> 8) & 0xFF; - data[1] = y0 & 0xFF; - data[2] = (y1 >> 8) & 0xFF; - data[3] = y1 & 0xFF; - DS_ST7789V_WriteData(lcd, data, 4); + /* Настройка строк (Y) */ + DS_ST7789V_WriteCommand(lcd, ST7789V_CMD_RASET); + data[0] = (y0 >> 8) & 0xFF; + data[1] = y0 & 0xFF; + data[2] = (y1 >> 8) & 0xFF; + data[3] = y1 & 0xFF; + DS_ST7789V_WriteData(lcd, data, 4); // Передаем пачку строго из 4 байт - /* Готовность к записи в RAM */ - DS_ST7789V_WriteCommand(lcd, ST7789V_CMD_RAMWR); + /* Готовность к записи в RAM дисплея */ + DS_ST7789V_WriteCommand(lcd, ST7789V_CMD_RAMWR); } + void DS_ST7789V_Init(DS_ST7789V *lcd, SPI_HandleTypeDef *hspi, GPIO_TypeDef *cs_port, uint16_t cs_pin, GPIO_TypeDef *dc_port, uint16_t dc_pin, @@ -102,7 +102,7 @@ void DS_ST7789V_Init(DS_ST7789V *lcd, SPI_HandleTypeDef *hspi, } void DS_ST7789V_SetOrientation(DS_ST7789V *lcd, - DS_ST7789V_Orientation_t orientation) { + DS_ST7789V_Orientation orientation) { lcd->Orientation = orientation; uint8_t madctl_val = 0; switch (orientation) { @@ -131,26 +131,247 @@ void DS_ST7789V_SetOrientation(DS_ST7789V *lcd, DS_ST7789V_WriteData(lcd, &madctl_val, 1); } -void DS_ST7789V_FillRect(DS_ST7789V *lcd, uint16_t x, uint16_t y, uint16_t w, - uint16_t h, uint16_t color) { - if ((x >= lcd->Width) || (y >= lcd->Height)) - return; - if ((x + w) > lcd->Width) - w = lcd->Width - x; - if ((y + h) > lcd->Height) - h = lcd->Height - y; - DS_ST7789V_SetAddressWindow(lcd, x, y, x + w - 1, y + h - 1); +void DS_ST7789V_FillRect(DS_ST7789V *lcd, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) { + if ((x >= lcd->Width) || (y >= lcd->Height)) return; + if ((x + w) > lcd->Width) w = lcd->Width - x; + if ((y + h) > lcd->Height) h = lcd->Height - y; - uint8_t color_bytes[2]; - color_bytes[0] = (color >> 8) & 0xFF; // Старший байт цвета (MSB) - color_bytes[1] = color & 0xFF; // Младший байт цвета (LSB) - uint32_t total_pixels = w * h; - DC_DATA(lcd); - CS_ACTIVE(lcd); + DS_ST7789V_SetAddressWindow(lcd, x, y, x + w - 1, y + h - 1); - /* Передача массива пикселей через HAL блоками для экономии ОЗУ */ - for (uint32_t i = 0; i < total_pixels; i++) { + // Выделяем буфер под увеличенный блок (например, 20 строк экрана за раз) + // 240 пикселей * 20 строк * 2 байта = 9600 байт (у STM32G0B1 целых 144 КБ ОЗУ, это неощутимо) + #define CHUNK_ROWS 20 + static uint8_t chunk_buffer[DS_ST7789V_HEIGHT * CHUNK_ROWS * 2]; + + uint8_t high_byte = (color >> 8) & 0xFF; + uint8_t low_byte = color & 0xFF; + + // Заполняем этот большой буфер один раз + uint32_t pixels_in_chunk = w * CHUNK_ROWS; + uint32_t idx = 0; + for (uint32_t i = 0; i < pixels_in_chunk; i++) { + chunk_buffer[idx++] = high_byte; + chunk_buffer[idx++] = low_byte; + } + + DC_DATA(lcd); + CS_ACTIVE(lcd); + + uint32_t total_pixels = w * h; + uint32_t pixels_left = total_pixels; + + // Отправляем данные огромными блоками + while (pixels_left > 0) { + uint32_t current_chunk_pixels = (pixels_left > pixels_in_chunk) ? pixels_in_chunk : pixels_left; + uint32_t bytes_to_send = current_chunk_pixels * 2; + + // Отправка всего блока на максимальной скорости аппаратного SPI + HAL_SPI_Transmit(lcd->hspi, chunk_buffer, bytes_to_send, HAL_MAX_DELAY); + + pixels_left -= current_chunk_pixels; + } + + CS_IDLE(lcd); +} + + +void DS_ST7789V_DrawPixel(DS_ST7789V *lcd, uint16_t x, uint16_t y, uint16_t color) { + if ((x >= lcd->Width) || (y >= lcd->Height)) return; + + DS_ST7789V_SetAddressWindow(lcd, x, y, x, y); + + uint8_t color_bytes[2]; // Явно объявляем массив из 2 элементов + color_bytes[0] = (color >> 8) & 0xFF; // Старший байт (MSB) + color_bytes[1] = color & 0xFF; // Младший байт (LSB) + + DC_DATA(lcd); + CS_ACTIVE(lcd); + + // Передаем строго массив color_bytes размером 2 байта HAL_SPI_Transmit(lcd->hspi, color_bytes, 2, HAL_MAX_DELAY); - } - CS_IDLE(lcd); -} \ No newline at end of file + + CS_IDLE(lcd); +} + +// Быстрая горизонтальная линия — это прямоугольник высотой в 1 пиксель +void DS_ST7789V_DrawHLine(DS_ST7789V *lcd, uint16_t x, uint16_t y, uint16_t length, uint16_t color) { + DS_ST7789V_FillRect(lcd, x, y, length, 1, color); +} + +// Быстрая вертикальная линия — это прямоугольник шириной в 1 пиксель +void DS_ST7789V_DrawVLine(DS_ST7789V *lcd, uint16_t x, uint16_t y, uint16_t length, uint16_t color) { + DS_ST7789V_FillRect(lcd, x, y, 1, length, color); +} + +void DS_ST7789V_DrawChar(DS_ST7789V *lcd, uint16_t x, uint16_t y, char ch, uint16_t color, uint16_t bg_color, uint8_t scale) { + if (ch < 0x20 || ch > 0x7E) return; + if (scale == 0) scale = 1; // Защита от нулевого масштаба + + uint16_t font_index = (ch - 0x20) * 5; + + for (uint8_t col = 0; col < 5; col++) { + uint8_t line = DS_Font5x7[font_index + col]; + for (uint8_t row = 0; row < 7; row++) { + // Вычисляем координаты увеличенного "пикселя" + uint16_t px = x + (col * scale); + uint16_t py = y + (row * scale); + + if (line & (1 << row)) { + // Вместо одной точки рисуем закрашенный квадрат размером scale x scale + DS_ST7789V_FillRect(lcd, px, py, scale, scale, color); + } else { + DS_ST7789V_FillRect(lcd, px, py, scale, scale, bg_color); + } + } + } +} + +void DS_ST7789V_DrawString(DS_ST7789V *lcd, uint16_t x, uint16_t y, const char *str, uint16_t color, uint16_t bg_color, uint8_t scale) { + if (scale == 0) scale = 1; + + // Шаг смещения: ширина символа (5) * масштаб + 1 пиксель межсимвольного интервала + uint8_t char_width = 5 * scale; + uint8_t char_height = 7 * scale; + uint8_t step_x = char_width + scale; + + while (*str) { + // Перенос строки при достижении правой границы экрана + if (x + char_width >= lcd->Width) { + x = 0; + y += char_height + scale; // Сдвиг вниз на высоту символа + межстрочный интервал + } + + if (y + char_height >= lcd->Height) break; + + DS_ST7789V_DrawChar(lcd, x, y, *str, color, bg_color, scale); + + x += step_x; + str++; + } +} + +void DS_ST7789V_DrawInt(DS_ST7789V *lcd, uint16_t x, uint16_t y, int32_t num, uint16_t color, uint16_t bg_color, uint8_t scale) { + char buf[16]; // ИСПРАВЛЕНО: жестко выделили массив на 16 символов + int i = 14; + buf[15] = '\0'; // ИСПРАВЛЕНО: корректный знак конца строки + + uint8_t is_negative = 0; + if (num < 0) { + is_negative = 1; + num = -num; + } + + if (num == 0) { + buf[i--] = '0'; + } else { + while (num > 0 && i > 0) { + buf[i--] = (num % 10) + '0'; + num /= 10; + } + } + + if (is_negative) { + buf[i--] = '-'; + } + + DS_ST7789V_DrawString(lcd, x, y, &buf[i + 1], color, bg_color, scale); +} + + +void DS_ST7789V_DrawFloat(DS_ST7789V *lcd, uint16_t x, uint16_t y, float num, uint8_t decimals, uint16_t color, uint16_t bg_color, uint8_t scale) { + char buf[32]; // ИСПРАВЛЕНО: жестко выделили массив на 32 символа + int i = 30; + buf[31] = '\0'; // ИСПРАВЛЕНО: корректный знак конца строки + + uint8_t is_negative = 0; + if (num < 0) { + is_negative = 1; + num = -num; + } + + float rounding = 0.5f; + for (uint8_t d = 0; d < decimals; d++) rounding /= 10.0f; + num += rounding; + + int32_t int_part = (int32_t)num; + float frac_part = num - (float)int_part; + + if (decimals > 0) { + for (uint8_t d = 0; d < decimals; d++) { + frac_part *= 10.0f; + int32_t digit = (int32_t)frac_part; + buf[i--] = digit + '0'; + frac_part -= digit; + } + + int left = i + 1; + int right = 30; // ИСПРАВЛЕНО: индекс конца массива изменен под размер 32 + while (left < right) { + char temp = buf[left]; + buf[left] = buf[right]; + buf[right] = temp; + left++; right--; + } + i = 30 - decimals; // ИСПРАВЛЕНО + buf[i--] = '.'; + } + + if (int_part == 0) { + buf[i--] = '0'; + } else { + while (int_part > 0 && i > 0) { + buf[i--] = (int_part % 10) + '0'; + int_part /= 10; + } + } + + if (is_negative) { + buf[i--] = '-'; + } + + DS_ST7789V_DrawString(lcd, x, y, &buf[i + 1], color, bg_color, scale); +} + +void DS_ST7789V_DrawCharTransparent(DS_ST7789V *lcd, uint16_t x, uint16_t y, char ch, uint16_t color, uint8_t scale) { + if (ch < 0x20 || ch > 0x7E) return; + if (scale == 0) scale = 1; + + uint16_t font_index = (ch - 0x20) * 5; + + for (uint8_t col = 0; col < 5; col++) { + uint8_t line = DS_Font5x7[font_index + col]; + for (uint8_t row = 0; row < 7; row++) { + // Если бит установлен — отрисовываем масштабированный пиксель цвета текста + if (line & (1 << row)) { + uint16_t px = x + (col * scale); + uint16_t py = y + (row * scale); + DS_ST7789V_FillRect(lcd, px, py, scale, scale, color); + } + // Если бит равен 0 — просто ничего не делаем, пропуская пиксель фона + } + } +} + +void DS_ST7789V_DrawStringTransparent(DS_ST7789V *lcd, uint16_t x, uint16_t y, const char *str, uint16_t color, uint8_t scale) { + if (scale == 0) scale = 1; + + uint8_t char_width = 5 * scale; + uint8_t char_height = 7 * scale; + uint8_t step_x = char_width + scale; + + while (*str) { + if (x + char_width >= lcd->Width) { + x = 0; + y += char_height + scale; + } + + if (y + char_height >= lcd->Height) break; + + DS_ST7789V_DrawCharTransparent(lcd, x, y, *str, color, scale); + + x += step_x; + str++; + } +} + +