Файлы структурированы как в readme

This commit is contained in:
2025-02-20 18:39:34 +03:00
parent 58be5b4d6b
commit 1b63defd86
20 changed files with 893 additions and 911 deletions

562
Core/GFX_Lib/gfx_lib.c Normal file
View File

@@ -0,0 +1,562 @@
/*
* gfx_lib.c
*
* Библиотека для заполнения буфера дисплея
*
* Для разработки библиотеки иcпользовались материалы:
* https://www.youtube.com/watch?v=ajEqZN5s5xc
* https://narodstream.ru/stm-urok-37-displej-tft-240x320-8bit-chast-1/
* https://hubstub.ru/display/126-vyvod-simvolov-i-strok-na-tft-displey-na-primere-ili9341.html
*/
#include "gfx_lib.h"
#include "font_tahoma_8_prop.h"
#include "math.h"
/* переменные */
uint8_t chSpacing = 0; //межсимвольный интервал в px
/* функция очистки буфера кадра */
void GFX_Clean_Buffer_Frame(uint8_t *Buffer_Frame, uint32_t Buffer_Frame_Size)
{
if(Buffer_Frame == NULL)
return;
memset(Buffer_Frame, 0x00, Buffer_Frame_Size);
}
/* функция прорисовки пикселя */
void GFX_Draw_Pixel(uint8_t *Buffer_Frame, uint8_t xPos, uint8_t yPos, uint8_t pxColor)
{
if(Buffer_Frame == NULL)
return;
if ((xPos >= GFX_BufferWidth)||(xPos < 0)||(yPos >= GFX_BufferHeight)||(yPos < 0))
{
//если значения по x и y больше пределов то выходим из функции
return;
}
else
{
uint16_t arrayPos = xPos + ((yPos/8)*GFX_BufferWidth);
//заполняем буфер кадра
if (pxColor)
{
Buffer_Frame[arrayPos] |= 1 << (yPos % 8);
}
else
{
Buffer_Frame[arrayPos] &= 0xFF ^ 1 << (yPos % 8);
}
}
}
/* функция инверсии любой области в буфере кадра */
void GFX_Invertion_Area(uint8_t *Buffer_Frame, uint16_t xPos_Start, uint16_t yPos_Start, uint16_t width, uint16_t height)
{
if(Buffer_Frame == NULL)
return;
if ((xPos_Start+width > GFX_BufferWidth)||(xPos_Start < 0)||(yPos_Start+ height> GFX_BufferHeight)||(yPos_Start < 0))
{
//если значения по x и y больше пределов то выходим из функции
return;
}
for (uint16_t xPos = xPos_Start; xPos < xPos_Start + width; xPos++)
{
for(uint16_t yPos = yPos_Start; yPos < yPos_Start + height; yPos++)
{
uint16_t arrayPos = xPos + ((yPos/8)*GFX_BufferWidth);
Buffer_Frame[arrayPos] ^= (1 << (yPos % 8)); // Инвертируем бит, отвечающий за пиксель
}
}
}
/* работа со шрифтами */
/* функция прорисовки однобайтового символа шрифта */
void GFX_Draw_Char_1_Byte(uint8_t *Buffer_Frame, uint8_t xPos, uint8_t yPos, char Symbol, uint8_t Inversion)
{
/*
* выгрузка кода шрифта в "h" файл из программы matrixFont
* https://habr.com/ru/articles/575332/
* параметры выгрузки:
* сначала строки, справа на лево, сверху вниз, пропорциональный, HEX, нули, 8(uint8_t, unsigned char), С99
* ниже циклы для однобайтового символа шрифта, т.е. ширина символа не более 8 пикселей
*/
if(Buffer_Frame == NULL)
return;
/* если ASCII номер символа в диапазоне 32...255, то рисуем символы шрифта */
if ((Symbol >= 32) && (Symbol <= 255))
{
uint8_t pxChView;
uint8_t pxBkView;
//включение/выключении инверсии символа
if (!(Inversion & GFX_ChInvers))
{
pxChView = GFX_pxView_On;
pxBkView = GFX_pxView_Off;
}
else
{
pxChView = GFX_pxView_Off;
pxBkView = GFX_pxView_On;
}
//извлекаем ширину символа + 1px для минимального межсимвольного интервала
uint8_t chWidth = font_tahoma_8[(Symbol - 0x20) * (1 + FONT_TAHOMA_8_CHAR_HEIGHT)] + 1;
//запоминаем ширину данного символа
chSpacing = chWidth;
//рисуем заданный символ шрифта
for (uint8_t y = 1; y <= FONT_TAHOMA_8_CHAR_HEIGHT; y++) //высота холста шрифта в пикселях
{
for (uint8_t x = 0; x < chWidth; x++) //ширина холста символа шрифта
{
/*
* извлекаем из массива шрифта байт строки пикселей символа
* и с помощью операции побитового И определяем где 1 и где 0
* 0x20 на 32
*/
if (font_tahoma_8[((Symbol - 0x20) * (1 + FONT_TAHOMA_8_CHAR_HEIGHT)) + y] >> (7 - x) & 0x01)
{
GFX_Draw_Pixel(Buffer_Frame, xPos + x, yPos + y, pxChView);
}
else
{
GFX_Draw_Pixel(Buffer_Frame, xPos + x, yPos + y, pxBkView);
}
}
/*
* заполняем фоном справа от символа если ширина символа меньше ширины холста шрифта
* с учетом заданного минимального межсимвольного интервала в 1px
*/
if (chWidth < FONT_TAHOMA_8_CHAR_WIDTH)
{
for (uint8_t n = chWidth; n < (FONT_TAHOMA_8_CHAR_WIDTH + 1); n++)
{
if (!(Inversion & GFX_ChInvers))
{
GFX_Draw_Pixel(Buffer_Frame, xPos + n, yPos + y, pxBkView);
}
else
{
GFX_Draw_Pixel(Buffer_Frame, xPos + n, yPos + y, pxChView);
}
}
}
}
}
}
/* функция прорисовки двухбайтового символа шрифта */
void GFX_Draw_Char_2_Byte(uint8_t *Buffer_Frame, uint8_t xPos, uint8_t yPos, uint8_t Symbol, uint8_t Inversion)
{
// /*
// * выгрузка кода шрифта в "h" файл из программы matrixFont
// * https://habr.com/ru/articles/575332/
// * параметры выгрузки:
// * сначала строки, справа на лево, сверху вниз, моноширный, HEX, нули, 8(uint8_t, unsigned char), С99
// * ниже циклы для двухбайтового символа шрифта, т.е. ширина символа более 8 пикселей
// */
// /* если ASCII номер символа в диапазоне 48...57, то рисуем символы цифр шрифта */
// if ((Symbol >= 48) && (Symbol <= 57))
// {
// Symbol = (Symbol - 48) * 15;
// uint8_t pxChView;
// uint8_t pxBkView;
// //включение/выключении инверсии символа
// if (!(Inversion & GFX_ChInvers))
// {
// pxChView = GFX_pxView_On;
// pxBkView = GFX_pxView_Off;
// }
// else
// {
// pxChView = GFX_pxView_Off;
// pxBkView = GFX_pxView_On;
// }
// //рисуем заданный символ шрифта
// for (uint8_t y = 0; y < FONT_TERMINUS_10X15__CHAR_HEIGHT; y++) //высота холста шрифта в пикселях
// {
// for (uint8_t x = 0; x < 8; x++) //8 - один байт
// {
// /*
// * извлекаем из массива шрифта правый байт строки пикселей символа
// * и с помощью операции побитового И определяем где 1 а где 0
// */
// if (font_terminus_10x15_[(Symbol + y) * 2 + 1] >> (7 - x) & 0x01)
// {
// /*
// * рисуем только нужную нам часть из правого байта
// * пикселя символа, -6 смещаем картинку на заданную позицию
// */
// if (x > 5)
// {
// GFX_Draw_Pixel(Buffer_Frame, xPos + x - 6, yPos + y, pxChView);
// }
// }
// else
// {
// /*
// * рисуем только нужную нам часть из правого байта
// * пикселя фона символа, -6 смещаем картинку на заданную позицию
// */
// if (x > 5)
// {
// GFX_Draw_Pixel(Buffer_Frame, xPos + x - 6, yPos + y, pxBkView);
// }
// }
// /*
// * извлекаем из массива шрифта левый байт строки пикселей символа
// * и с помощью операции побитового И определяем где 1 а где 0
// */
// if (font_terminus_10x15_[(Symbol + y) * 2] >> (7 - x) & 0x01)
// {
// /*
// * смещаем картинку на 8 пикселей вправо
// * т.е. "склеиваем" картинку символа и рисуем пиксель символа,
// * -6 смещаем картинку на заданную позицию
// */
// GFX_Draw_Pixel(Buffer_Frame, xPos + x + 8 - 6, yPos + y, pxChView);
// }
// else
// {
// /*
// * смещаем картинку на 8 пикселей вправо
// * т.е. "склеиваем" картинку символа и рисуем пиксель фона символа,
// * -6 смещаем картинку на заданную позицию
// */
// GFX_Draw_Pixel(Buffer_Frame, xPos + x + 8 - 6, yPos + y, pxBkView);
// }
// }
// }
// }
}
/* функция вывода строки на дисплей */
void GFX_Output_String(uint8_t *Buffer_Frame, uint8_t xPos, uint8_t yPos, char *String, uint8_t setChSpacing, uint8_t Inversion)
{
/*
* вывод строки шрифтом font_tahoma_8
* параметры функции:
* xPos - позиция по X
* yPos - позиция по Y
* *String - строка для вывода
* setChSpacing - межсимвольный интервал в px
* Inversion - включение/выключени инверсии
*/
//uint8_t xPos_Start = xPos; //px начала вывода строки по X
if((Buffer_Frame == NULL) || (String == NULL))
return;
//посимвольный вывод строки
while(*String)
{
//проверяем не вылезем ли мы за пределы экрана при отрисовке следующего символа,
// если да, то переходим на следующую строчку
/*if((xPos + 8) > GFX_BufferWidth)
{
xPos = xPos_Start;
yPos = yPos + 10;
}*/
//вывод текущего символа строки
GFX_Draw_Char_1_Byte(Buffer_Frame, xPos, yPos, *String, Inversion);
//изменяем координату для отрисовки следующего символа
xPos += chSpacing - 1 + setChSpacing;
//инкремент указателя на следующий символ в строке
String++;
}
}
/* геометрические примитивы */
/* функция рисования линии */
void GFX_Draw_Line(uint8_t *Buffer_Frame, GFX_LineHandleTypeDef *hLine)
{
if((Buffer_Frame == NULL) || (hLine == NULL))
return;
uint8_t xPos_Start = hLine->xPos_Start;
uint8_t yPos_Start = hLine->yPos_Start;
uint8_t xPos_End = hLine->xPos_End;
uint8_t yPos_End = hLine->yPos_End;
uint8_t pxColor = hLine->pxColor;
int dx = (xPos_End >= xPos_Start) ? xPos_End - xPos_Start : xPos_Start - xPos_End;
int dy = (yPos_End >= yPos_Start) ? yPos_End - yPos_Start : yPos_Start - yPos_End;
int sx = (xPos_Start < xPos_End) ? 1 : -1;
int sy = (yPos_Start < yPos_End) ? 1 : -1;
int err = dx - dy;
for (;;)
{
GFX_Draw_Pixel(Buffer_Frame, xPos_Start, yPos_Start, pxColor);
if (xPos_Start == xPos_End && yPos_Start == yPos_End)
break;
int e2 = err + err;
if (e2 > -dy)
{
err -= dy;
xPos_Start += sx;
}
if (e2 < dx)
{
err += dx;
yPos_Start += sy;
}
}
}
/* функция рисования пустотелого прямоугольника */
void GFX_Draw_Rectangle(uint8_t *Buffer_Frame, GFX_RectangleHandleTypeDef *hRectangle)
{
if((Buffer_Frame == NULL) || (hRectangle == NULL))
return;
uint8_t xPos_Start = hRectangle->xPos_Start;
uint8_t yPos_Start = hRectangle->yPos_Start;
uint8_t rectangle_Width = hRectangle->rectangle_Width;
uint8_t rectangle_Height = hRectangle->rectangle_Height;
uint8_t pxColor = hRectangle->pxColor;
if(hRectangle->Filled)
__GFX_Draw_Rectangle_Filled(Buffer_Frame, xPos_Start, yPos_Start, rectangle_Width, rectangle_Height, pxColor);
else
__GFX_Draw_Rectangle(Buffer_Frame, xPos_Start, yPos_Start, rectangle_Width, rectangle_Height, pxColor);
}
/* функция рисования пустотелой окружности */
void GFX_Draw_Circle(uint8_t *Buffer_Frame, GFX_CircleHandleTypeDef *hCircle)
{
if((Buffer_Frame == NULL) || (hCircle == NULL))
return;
uint8_t xPos = hCircle->xPos;
uint8_t yPos = hCircle->yPos;
uint8_t circle_Radius = hCircle->circle_Radius;
uint8_t pxColor = hCircle->pxColor;
if(hCircle->Filled)
__GFX_Draw_Circle_Filled(Buffer_Frame, xPos, yPos, circle_Radius, pxColor);
else
__GFX_Draw_Circle(Buffer_Frame, xPos, yPos, circle_Radius, pxColor);
}
/* функция рисования треугольника */
void GFX_Draw_Triangle(uint8_t *Buffer_Frame, GFX_TriangleHandleTypeDef *hTriangle)
{
uint8_t xPos1 = hTriangle->xPos1;
uint8_t xPos2 = hTriangle->xPos2;
uint8_t xPos3 = hTriangle->xPos3;
uint8_t yPos1 = hTriangle->yPos1;
uint8_t yPos2 = hTriangle->yPos2;
uint8_t yPos3 = hTriangle->yPos3;
uint8_t pxColor = hTriangle->pxColor;
if((Buffer_Frame == NULL) || (hTriangle == NULL))
return;
__GFX_Draw_Line(Buffer_Frame, xPos1, yPos1, xPos2, yPos2, pxColor);
__GFX_Draw_Line(Buffer_Frame, xPos2, yPos2, xPos3, yPos3, pxColor);
__GFX_Draw_Line(Buffer_Frame, xPos3, yPos3, xPos1, yPos1, pxColor);
}
/* Функция рисования дуги (четверти окружности) */
void GFX_Draw_Arc(uint8_t *Buffer_Frame, GFX_ArcHandleTypeDef *hArc)
{
if((Buffer_Frame == NULL) || (hArc == NULL))
return;
uint8_t xPos = hArc->xPos;
uint8_t yPos = hArc->yPos;
uint8_t radius = hArc->radius;
uint8_t startAngle = hArc->startAngle;
uint8_t endAngle = hArc->endAngle;
uint8_t pxColor = hArc->pxColor;
int xPos_tmp = 0;
int yPos_tmp = 0;
for (int angle = startAngle; angle <= endAngle; angle++)
{
xPos_tmp = roundf(xPos + (radius * cosf(angle * 3.14159 / 180)));
yPos_tmp = roundf(yPos + (radius * sinf(angle * 3.14159 / 180)));
GFX_Draw_Pixel(Buffer_Frame, xPos_tmp, yPos_tmp, pxColor);
}
}
/* Функция инвертирования прямоугольной области */
void GFX_Invertion_Display(uint8_t *Buffer_Frame)
{
if(Buffer_Frame == NULL)
return;
GFX_Invertion_Area(Buffer_Frame, 0, 0, GFX_BufferWidth-1, GFX_BufferHeight-1);
}
/* Низкоуровневая функция рисования линии */
void __GFX_Draw_Line(uint8_t *Buffer_Frame, uint8_t xPos_Start, uint8_t yPos_Start, uint8_t xPos_End, uint8_t yPos_End, uint8_t pxColor)
{
int dx = (xPos_End >= xPos_Start) ? xPos_End - xPos_Start : xPos_Start - xPos_End;
int dy = (yPos_End >= yPos_Start) ? yPos_End - yPos_Start : yPos_Start - yPos_End;
int sx = (xPos_Start < xPos_End) ? 1 : -1;
int sy = (yPos_Start < yPos_End) ? 1 : -1;
int err = dx - dy;
for (;;)
{
GFX_Draw_Pixel(Buffer_Frame, xPos_Start, yPos_Start, pxColor);
if (xPos_Start == xPos_End && yPos_Start == yPos_End)
break;
int e2 = err + err;
if (e2 > -dy)
{
err -= dy;
xPos_Start += sx;
}
if (e2 < dx)
{
err += dx;
yPos_Start += sy;
}
}
}
/* функция рисования пустотелого прямоугольника */
void __GFX_Draw_Rectangle(uint8_t *Buffer_Frame, uint8_t xPos_Start, uint8_t yPos_Start, uint8_t rectangle_Width, uint8_t rectangle_Height, uint8_t pxColor)
{
/* рисуем стороны прямоугольника */
//левая сторона прямоугольника
__GFX_Draw_Line(Buffer_Frame, xPos_Start, yPos_Start, xPos_Start, yPos_Start + rectangle_Height, pxColor);
//верх прямоугольника
__GFX_Draw_Line(Buffer_Frame, xPos_Start, yPos_Start, xPos_Start + rectangle_Width, yPos_Start, pxColor);
//правая сторона прямоугольника
__GFX_Draw_Line(Buffer_Frame, xPos_Start + rectangle_Width, yPos_Start, xPos_Start + rectangle_Width, yPos_Start + rectangle_Height, pxColor);
//низ прямоугольника
__GFX_Draw_Line(Buffer_Frame, xPos_Start, yPos_Start + rectangle_Height, xPos_Start + rectangle_Width, yPos_Start + rectangle_Height, pxColor);
}
/* функция рисования закрашенного прямоугольника */
void __GFX_Draw_Rectangle_Filled(uint8_t *Buffer_Frame, uint8_t xPos_Start, uint8_t yPos_Start, uint8_t rectangle_Width, uint8_t rectangle_Height, uint8_t pxColor)
{
for (uint8_t i = 0; i <= rectangle_Height; i++)
{
__GFX_Draw_Line(Buffer_Frame, xPos_Start, yPos_Start + i, xPos_Start + rectangle_Width, yPos_Start + i, pxColor);
}
}
/* функция рисования пустотелой окружности */
void __GFX_Draw_Circle(uint8_t *Buffer_Frame, uint8_t xPos, uint8_t yPos, uint8_t circle_Radius, uint8_t pxColor)
{
int f = 1 - (int)circle_Radius;
int ddF_x = 1;
int ddF_y = -2 * (int)circle_Radius;
int x_0 = 0;
GFX_Draw_Pixel(Buffer_Frame, xPos, yPos + circle_Radius, pxColor);
GFX_Draw_Pixel(Buffer_Frame, xPos, yPos - circle_Radius, pxColor);
GFX_Draw_Pixel(Buffer_Frame, xPos + circle_Radius, yPos, pxColor);
GFX_Draw_Pixel(Buffer_Frame, xPos - circle_Radius, yPos, pxColor);
int y_0 = circle_Radius;
while (x_0 < y_0)
{
if (f >= 0)
{
y_0--;
ddF_y += 2;
f += ddF_y;
}
x_0++;
ddF_x += 2;
f += ddF_x;
GFX_Draw_Pixel(Buffer_Frame, xPos + x_0, yPos + y_0, pxColor);
GFX_Draw_Pixel(Buffer_Frame, xPos - x_0, yPos + y_0, pxColor);
GFX_Draw_Pixel(Buffer_Frame, xPos + x_0, yPos - y_0, pxColor);
GFX_Draw_Pixel(Buffer_Frame, xPos - x_0, yPos - y_0, pxColor);
GFX_Draw_Pixel(Buffer_Frame, xPos + y_0, yPos + x_0, pxColor);
GFX_Draw_Pixel(Buffer_Frame, xPos - y_0, yPos + x_0, pxColor);
GFX_Draw_Pixel(Buffer_Frame, xPos + y_0, yPos - x_0, pxColor);
GFX_Draw_Pixel(Buffer_Frame, xPos - y_0, yPos - x_0, pxColor);
}
}
/* функция рисования закрашенной окружности */
void __GFX_Draw_Circle_Filled(uint8_t *Buffer_Frame, int8_t xPos, int8_t yPos, int8_t circle_Radius, uint8_t pxColor)
{
int16_t f = 1 - circle_Radius;
int16_t ddF_x = 1;
int16_t ddF_y = -2 * circle_Radius;
int16_t x_0 = 0;
int16_t y_0 = circle_Radius;
GFX_Draw_Pixel(Buffer_Frame, xPos, yPos + circle_Radius, pxColor);
GFX_Draw_Pixel(Buffer_Frame, xPos, yPos - circle_Radius, pxColor);
GFX_Draw_Pixel(Buffer_Frame, xPos + circle_Radius, yPos, pxColor);
GFX_Draw_Pixel(Buffer_Frame, xPos - circle_Radius, yPos, pxColor);
__GFX_Draw_Line(Buffer_Frame, xPos - circle_Radius, yPos, xPos + circle_Radius, yPos, pxColor);
while (x_0 < y_0)
{
if (f >= 0)
{
y_0--;
ddF_y += 2;
f += ddF_y;
}
x_0++;
ddF_x += 2;
f += ddF_x;
__GFX_Draw_Line(Buffer_Frame, xPos - x_0, yPos + y_0, xPos + x_0, yPos + y_0, pxColor);
__GFX_Draw_Line(Buffer_Frame, xPos + x_0, yPos - y_0, xPos - x_0, yPos - y_0, pxColor);
__GFX_Draw_Line(Buffer_Frame, xPos + y_0, yPos + x_0, xPos - y_0, yPos + x_0, pxColor);
__GFX_Draw_Line(Buffer_Frame, xPos + y_0, yPos - x_0, xPos - y_0, yPos - x_0, pxColor);
}
}
/* функция рисования треугольника */
void __GFX_Draw_Triangle(uint8_t *Buffer_Frame,
uint8_t xPos1, uint8_t yPos1,
uint8_t xPos2, uint8_t yPos2,
uint8_t xPos3, uint8_t yPos3,
uint8_t pxColor)
{
__GFX_Draw_Line(Buffer_Frame, xPos1, yPos1, xPos2, yPos2, pxColor);
__GFX_Draw_Line(Buffer_Frame, xPos2, yPos2, xPos3, yPos3, pxColor);
__GFX_Draw_Line(Buffer_Frame, xPos3, yPos3, xPos1, yPos1, pxColor);
}
/* Функция рисования дуги (четверти окружности) */
void __GFX_Draw_Arc(uint8_t *Buffer_Frame, uint8_t xPos, uint8_t yPos, uint8_t radius, uint16_t startAngle, uint16_t endAngle, uint8_t pxColor)
{
int xPos_tmp = 0;
int yPos_tmp = 0;
for (int angle = startAngle; angle <= endAngle; angle++)
{
xPos_tmp = roundf(xPos + (radius * cosf(angle * 3.14159 / 180)));
yPos_tmp = roundf(yPos + (radius * sinf(angle * 3.14159 / 180)));
GFX_Draw_Pixel(Buffer_Frame, xPos_tmp, yPos_tmp, pxColor);
}
}