Files
DS_ST7789V/Src/DS_ST7789V.c

376 lines
13 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "../inc/DS_ST7789V.h"
/* Вспомогательные статические макросы для локального управления линиями GPIO */
#define CS_ACTIVE(lcd) \
HAL_GPIO_WritePin(lcd->CS.Port, lcd->CS.Pin, GPIO_PIN_RESET)
#define CS_IDLE(lcd) HAL_GPIO_WritePin(lcd->CS.Port, lcd->CS.Pin, GPIO_PIN_SET)
#define DC_COMMAND(lcd) \
HAL_GPIO_WritePin(lcd->DC.Port, lcd->DC.Pin, GPIO_PIN_RESET)
#define DC_DATA(lcd) HAL_GPIO_WritePin(lcd->DC.Port, lcd->DC.Pin, GPIO_PIN_SET)
#define RES_ACTIVE(lcd) \
HAL_GPIO_WritePin(lcd->RES.Port, lcd->RES.Pin, GPIO_PIN_RESET)
#define RES_IDLE(lcd) \
HAL_GPIO_WritePin(lcd->RES.Port, lcd->RES.Pin, GPIO_PIN_SET)
void DS_ST7789V_WriteCommand(DS_ST7789V *lcd, uint8_t cmd) {
DC_COMMAND(lcd);
CS_ACTIVE(lcd);
HAL_SPI_Transmit(lcd->hspi, &cmd, 1, HAL_MAX_DELAY);
CS_IDLE(lcd);
}
void DS_ST7789V_WriteData(DS_ST7789V *lcd, uint8_t *data, uint16_t size) {
DC_DATA(lcd);
CS_ACTIVE(lcd);
HAL_SPI_Transmit(lcd->hspi, data, size, HAL_MAX_DELAY);
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];
/* Настройка столбцов (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); // Передаем пачку строго из 4 байт
/* Готовность к записи в 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,
GPIO_TypeDef *res_port, uint16_t res_pin)
{
lcd->hspi = hspi;
lcd->CS.Port = cs_port;
lcd->CS.Pin = cs_pin;
lcd->DC.Port = dc_port;
lcd->DC.Pin = dc_pin;
lcd->RES.Port = res_port;
lcd->RES.Pin = res_pin;
lcd->Width = DS_ST7789V_WIDTH;
lcd->Height = DS_ST7789V_HEIGHT;
/* Аппаратный сброс матрицы */
RES_ACTIVE(lcd);
HAL_Delay(25);
RES_IDLE(lcd);
HAL_Delay(120);
/* Программный сброс контроллера */
DS_ST7789V_WriteCommand(lcd, ST7789V_CMD_SWRESET);
HAL_Delay(150);
/* Выход из спящего режима */
DS_ST7789V_WriteCommand(lcd, ST7789V_CMD_SLPOUT);
HAL_Delay(120);
/* Установка цветового режима: 16-бит/пиксель (RGB565) */
DS_ST7789V_WriteCommand(lcd, ST7789V_CMD_COLMOD);
uint8_t color_mode = 0x55;
DS_ST7789V_WriteData(lcd, &color_mode, 1);
/* Установка дефолтной ориентации */
DS_ST7789V_SetOrientation(lcd, DS_ST7789V_ORIENTATION_PORTRAIT);
/* Инверсия цвета дисплея (требуется для большинства IPS матриц этой серии) */
DS_ST7789V_WriteCommand(lcd, ST7789V_CMD_INVON);
/* Включение дисплея */
DS_ST7789V_WriteCommand(lcd, ST7789V_CMD_DISPON);
HAL_Delay(50);
/* Первоначальная очистка экрана черным цветом */
DS_ST7789V_FillRect(lcd, 0, 0, lcd->Width, lcd->Height, 0x0000);
}
void DS_ST7789V_SetOrientation(DS_ST7789V *lcd,
DS_ST7789V_Orientation orientation) {
lcd->Orientation = orientation;
uint8_t madctl_val = 0;
switch (orientation) {
case DS_ST7789V_ORIENTATION_PORTRAIT:
madctl_val = 0x00; // Развертка сверху-вниз, слева-направо
lcd->Width = DS_ST7789V_WIDTH;
lcd->Height = DS_ST7789V_HEIGHT;
break;
case DS_ST7789V_ORIENTATION_LANDSCAPE:
madctl_val = 0x60; // Обмен строк и столбцов (поворот на 90°)
lcd->Width = DS_ST7789V_HEIGHT;
lcd->Height = DS_ST7789V_WIDTH;
break;
case DS_ST7789V_ORIENTATION_PORTRAIT_REV:
madctl_val = 0xC0; // Отражение по осям X и Y
lcd->Width = DS_ST7789V_WIDTH;
lcd->Height = DS_ST7789V_HEIGHT;
break;
case DS_ST7789V_ORIENTATION_LANDSCAPE_REV:
madctl_val = 0xA0; // Альтернативный альбомный режим
lcd->Width = DS_ST7789V_HEIGHT;
lcd->Height = DS_ST7789V_WIDTH;
break;
}
DS_ST7789V_WriteCommand(lcd, ST7789V_CMD_MADCTL);
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);
// Выделяем буфер под увеличенный блок (например, 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;
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];
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);
}
// Быстрая горизонтальная линия — это прямоугольник высотой в 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];
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];
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;
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++;
}
}