UKSI_TEST/Core/OLED_Driver/gfx_lib.c
2025-12-30 09:35:49 +03:00

1037 lines
35 KiB
C
Raw 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.

/*
* 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 "font_tahoma_15_prop.h"
#include "font_terminus_15_digi.h"
#include "math.h"
#ifndef PI
#define PI 3.1415926535
#endif
/* переменные */
uint8_t chSpacing = 0; //межсимвольный интервал в px
/* функция очистки буфера кадра */
void GFX_Clean_Buffer_Frame(uint8_t *Buffer_Frame)
{
if(Buffer_Frame == NULL)
return;
memset(Buffer_Frame, 0x00, GFX_BufferHeight*GFX_BufferWidth/8);
}
/* Функция очистки прямоугольной области */
void GFX_Clean_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_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_Tahoma8_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);
}
}
}
}
}
}
/* функция прорисовки символа шрифта Tahoma 15 */
/* функция прорисовки символа шрифта Tahoma 15 */
void GFX_Draw_Char_Tahoma15_Byte(uint8_t *Buffer_Frame, uint8_t xPos, uint8_t yPos, char Symbol, uint8_t Inversion)
{
if (!Buffer_Frame)
return;
if (Symbol < FONT_TAHOMA_15_START_CHAR ||
Symbol >= FONT_TAHOMA_15_START_CHAR + FONT_TAHOMA_15_LENGTH)
return;
uint8_t pxChView, pxBkView;
if (!(Inversion & GFX_ChInvers))
{
pxChView = GFX_pxView_On;
pxBkView = GFX_pxView_Off;
}
else
{
pxChView = GFX_pxView_Off;
pxBkView = GFX_pxView_On;
}
uint16_t index =
(Symbol - FONT_TAHOMA_15_START_CHAR) *
(1 + FONT_TAHOMA_15_CHAR_WIDTH * 2);
uint8_t chWidth = font_tahoma_15[index] + 1;
chSpacing = chWidth;
index++; // переходим к данным столбцов
/* идем по столбцам */
for (uint8_t x = 0; x < chWidth; x++)
{
uint16_t column =
font_tahoma_15[index] |
(font_tahoma_15[index + 1] << 8);
index += 2;
/* идем по высоте */
for (uint8_t y = 0; y < FONT_TAHOMA_15_CHAR_HEIGHT; y++)
{
if (column & (1 << y))
{
GFX_Draw_Pixel(Buffer_Frame,
xPos + x,
yPos + y,
pxChView);
}
else
{
GFX_Draw_Pixel(Buffer_Frame,
xPos + x,
yPos + y,
pxBkView);
}
}
}
/* межсимвольный интервал */
for (uint8_t y = 0; y < FONT_TAHOMA_15_CHAR_HEIGHT; y++)
{
GFX_Draw_Pixel(Buffer_Frame,
xPos + chWidth,
yPos + y,
pxBkView);
}
}
/* функция прорисовки двухбайтового символа шрифта */
void GFX_Draw_Char_Terminus15Dig_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, GFX_Font_t font, 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;
}*/
//вывод текущего символа строки
switch(font)
{
case GFX_FONT_TAHOMA_8: GFX_Draw_Char_Tahoma8_Byte(Buffer_Frame, xPos, yPos, *String, Inversion); break;
case GFX_FONT_TAHOMA_15: GFX_Draw_Char_Tahoma15_Byte(Buffer_Frame, xPos, yPos, *String, Inversion); break;
case GFX_FONT_DIG_TERMINUS_15: GFX_Draw_Char_Terminus15Dig_Byte(Buffer_Frame, xPos, yPos, *String, Inversion); break;
}
//изменяем координату для отрисовки следующего символа
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_Arrow(uint8_t *Buffer_Frame, GFX_ArrowHandleTypeDef *hArrow)
{
uint8_t xPos = hArrow->xPos;
uint8_t yPos = hArrow->yPos;
uint8_t size = hArrow->size;
uint16_t angle = hArrow->angle % 360;
uint8_t pxColor = hArrow->pxColor;
if((angle == 0) || (angle == 90) || (angle == 180) || (angle == 270))
__GFX_Draw_Arrow_Ortho(Buffer_Frame, xPos, yPos, size, angle, pxColor);
else
__GFX_Draw_Arrow(Buffer_Frame, xPos, yPos, size, angle, 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);
}
}
/* Функция для отрисовки графика uint8_t массива */
void GFX_Plotter_uint8_t(uint8_t *Buffer_Frame, GFX_PlotterHandleTypeDef *hPlot, uint8_t *data, uint32_t data_size, float data_step, uint8_t data_max)
{
if((Buffer_Frame == NULL) || (hPlot == NULL))
return;
if((hPlot->plotHeight == NULL) || (hPlot->plotWidth == NULL))
return;
if((hPlot->yPos + hPlot->plotHeight == NULL) || (hPlot->xPos + hPlot->plotWidth == NULL))
return;
if(data == NULL)
return;
/* Расчет позиции пикселя */
uint8_t pix_y_uint8t;
if(data_size == 0)
pix_y_uint8t = *data;
else
pix_y_uint8t = data[(int)hPlot->dataInd];
// масштабирование под размеры графика
hPlot->dataY = (pix_y_uint8t*hPlot->plotHeight)/data_max;
hPlot->f.dataSigned = 0;
/* Вывод пикселя */
__GFX_Draw_Plotter_Value(Buffer_Frame, hPlot);
/* Смещение графика далее */
hPlot->dataX++;
hPlot->dataPrevY = hPlot->dataY;
// Если используется массив плота
if(data == NULL)
{
hPlot->dataInd += data_step;
if(hPlot->dataInd >= data_size)
hPlot->dataInd -= data_size;
if(hPlot->dataInd < 0)
hPlot->dataInd += data_size;
}
}
/* Функция для отрисовки графика uint16_t массива */
void GFX_Plotter_uint16_t(uint8_t *Buffer_Frame, GFX_PlotterHandleTypeDef *hPlot, uint16_t *data, uint32_t data_size, float data_step, uint16_t data_max)
{
if((Buffer_Frame == NULL) || (hPlot == NULL))
return;
if((hPlot->plotHeight == NULL) || (hPlot->plotWidth == NULL))
return;
if((hPlot->yPos + hPlot->plotHeight == NULL) || (hPlot->xPos + hPlot->plotWidth == NULL))
return;
if(data == NULL)
return;
/* Расчет позиции пикселя */
uint16_t pix_y_uint16t;
if(data_size == 0)
pix_y_uint16t = *data;
else
pix_y_uint16t = data[(int)hPlot->dataInd];
// масштабирование под размеры графика
hPlot->dataY = (pix_y_uint16t*hPlot->plotHeight)/data_max;
hPlot->f.dataSigned = 0;
/* Вывод пикселя */
__GFX_Draw_Plotter_Value(Buffer_Frame, hPlot);
/* Смещение графика далее */
hPlot->dataX++;
hPlot->dataPrevY = hPlot->dataY;
// Если используется массив плота
if(data == NULL)
{
hPlot->dataInd += data_step;
if(hPlot->dataInd >= data_size)
hPlot->dataInd -= data_size;
if(hPlot->dataInd < 0)
hPlot->dataInd += data_size;
}
}
/* Функция для отрисовки графика int массива */
void GFX_Plotter_int(uint8_t *Buffer_Frame, GFX_PlotterHandleTypeDef *hPlot, int *data, uint32_t data_size, float data_step, int data_max)
{
if((Buffer_Frame == NULL) || (hPlot == NULL))
return;
if((hPlot->plotHeight == NULL) || (hPlot->plotWidth == NULL))
return;
if((hPlot->yPos + hPlot->plotHeight == NULL) || (hPlot->xPos + hPlot->plotWidth == NULL))
return;
if(data == NULL)
return;
/* Расчет позиции пикселя */
uint8_t pix_y_int;
if(data_size == 0)
pix_y_int = *data;
else
pix_y_int = data[(int)hPlot->dataInd];
// масштабирование под размеры графика
hPlot->dataY = (pix_y_int*hPlot->plotHeight)/data_max;
hPlot->f.dataSigned = 1;
/* Вывод пикселя */
__GFX_Draw_Plotter_Value(Buffer_Frame, hPlot);
/* Смещение графика далее */
hPlot->dataX++;
hPlot->dataPrevY = hPlot->dataY;
// Если используется массив плота
if(data == NULL)
{
hPlot->dataInd += data_step;
if(hPlot->dataInd >= data_size)
hPlot->dataInd -= data_size;
if(hPlot->dataInd < 0)
hPlot->dataInd += data_size;
}
}
/* Функция для отрисовки графика float массива */
void GFX_Plotter_float(uint8_t *Buffer_Frame, GFX_PlotterHandleTypeDef *hPlot, float *data, uint32_t data_size, float data_step, float data_max)
{
if((Buffer_Frame == NULL) || (hPlot == NULL))
return;
if((hPlot->plotHeight == NULL) || (hPlot->plotWidth == NULL))
return;
if((hPlot->yPos + hPlot->plotHeight == NULL) || (hPlot->xPos + hPlot->plotWidth == NULL))
return;
if((data == NULL) || (data_size == 0))
return;
/* Расчет позиции пикселя */
float pix_y_float;
if(data_size == 0)
pix_y_float = *data;
else
pix_y_float = data[(int)hPlot->dataInd];
hPlot->f.dataSigned = 1;
// масштабирование под размеры графика
hPlot->dataY = (pix_y_float/data_max)*(hPlot->plotHeight);
/* Вывод пикселя */
__GFX_Draw_Plotter_Value(Buffer_Frame, hPlot);
/* Смещение графика далее */
hPlot->dataX++;
hPlot->dataPrevY = hPlot->dataY;
// Если используется массив плота
if(data_size != 0)
{
hPlot->dataInd += data_step;
if(hPlot->dataInd >= data_size)
hPlot->dataInd -= data_size;
if(hPlot->dataInd < 0)
hPlot->dataInd += data_size;
}
}
/* Функция инвертирования прямоугольной области */
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);
}
float roundUp(float num) {
if (num > 0) {
return ceilf(num); // Для положительных чисел используем ceil
} else {
return floorf(num); // Для отрицательных чисел используем floor
}
}
void __GFX_Draw_Arrow(uint8_t *Buffer_Frame, uint8_t xPos, uint8_t yPos, uint8_t size, uint16_t angle, uint8_t pxColor)
{
float rad = (angle-90) * PI / 180.0; // Переводим угол в радианы
// Кончик стрелки (самая острая точка)
int16_t x1 = xPos;
int16_t y1 = yPos;
// Два боковых конца (формируют треугольник стрелки)
int16_t x2 = xPos + roundUp(size * sinf(rad + PI / 4));
int16_t y2 = yPos + roundUp(size * cosf(rad + PI / 4));
int16_t x3 = xPos + roundUp(size * sinf(rad - PI / 4));
int16_t y3 = yPos + roundUp(size * cosf(rad - PI / 4));
// Ограничение от выхода за границы (если нужно)
if (x2 < 0) x2 = 0;
if (x3 < 0) x3 = 0;
if (y2 < 0) y2 = 0;
if (y3 < 0) y3 = 0;
// Рисуем стрелку как две линии
__GFX_Draw_Line(Buffer_Frame, x1, y1, x2, y2, pxColor);
__GFX_Draw_Line(Buffer_Frame, x1, y1, x3, y3, pxColor);
}
/* функция рисования стрелки под прямым углом */
void __GFX_Draw_Arrow_Ortho(uint8_t *Buffer_Frame, uint8_t xPos, uint8_t yPos, uint8_t size, uint16_t angle, uint8_t pxColor)
{
int16_t x1, y1, x2, y2, x3, y3;
if(angle == 0) // >
{
x1 = xPos;
y1 = yPos;
x2 = xPos - size;
y2 = yPos - size;
x3 = xPos - size;
y3 = yPos + size;
}
else if(angle == 180) // <
{
x1 = xPos;
y1 = yPos;
x2 = xPos + size;
y2 = yPos - size;
x3 = xPos + size;
y3 = yPos + size;
}
else if(angle == 270) // ^
{
x1 = xPos;
y1 = yPos;
x2 = xPos - size;
y2 = yPos - size;
x3 = xPos + size;
y3 = yPos - size;
}
else if(angle == 90) // v
{
x1 = xPos;
y1 = yPos;
x2 = xPos - size;
y2 = yPos + size;
x3 = xPos + size;
y3 = yPos + size;
}
else
return;
if(x1 < 0)
x1 = 0;
if(x2 < 0)
x2 = 0;
if(x3 < 0)
x3 = 0;
if(y1 < 0)
y1 = 0;
if(y2 < 0)
y2 = 0;
if(y3 < 0)
y3 = 0;
__GFX_Draw_Line(Buffer_Frame, x1, y1, x2, y2, pxColor);
__GFX_Draw_Line(Buffer_Frame, x1, y1, x3, y3, 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);
}
}
/* Функция для отрисовки осей и рамки графика */
HAL_StatusTypeDef __GFX_StartPlot(uint8_t *Buffer_Frame, GFX_PlotterHandleTypeDef *hPlot)
{
if((hPlot->dataX < hPlot->xPos) || (hPlot->dataX >= hPlot->plotWidth))
{
hPlot->dataX = hPlot->xPos;
GFX_Clean_Area(Buffer_Frame, hPlot->xPos, hPlot->yPos, hPlot->plotWidth, hPlot->plotHeight);
if(hPlot->f.plotXAxis)
{
uint8_t xaxis_x_start = hPlot->xPos;
uint8_t xaxis_x_end = hPlot->xPos+hPlot->plotWidth-1;
uint8_t xaxis_y = hPlot->yPos-hPlot->plotXShift;
if(hPlot->f.dataSigned)
xaxis_y += hPlot->plotHeight/2;
else
xaxis_y += hPlot->plotHeight-1;
__GFX_Draw_Line(Buffer_Frame, xaxis_x_start, xaxis_y, xaxis_x_end, xaxis_y, 1);
__GFX_Draw_Arrow_Ortho(Buffer_Frame, xaxis_x_end, xaxis_y, 2, 0, 1);
}
if(hPlot->f.plotYAxis)
{
uint8_t yaxis_y_start = hPlot->yPos;
uint8_t yaxis_y_end = hPlot->yPos+hPlot->plotHeight-1;
uint8_t yaxis_x = hPlot->xPos+hPlot->plotYShift;
__GFX_Draw_Line(Buffer_Frame, yaxis_x, yaxis_y_start, yaxis_x, yaxis_y_end, 1);
__GFX_Draw_Arrow_Ortho(Buffer_Frame, yaxis_x, yaxis_y_start, 2, 90, 1);
}
if(hPlot->f.plotFrame)
{
__GFX_Draw_Rectangle(Buffer_Frame, hPlot->xPos, hPlot->yPos, hPlot->plotWidth-1, hPlot->plotHeight-1, 1);
}
return HAL_OK;
}
else
{
return HAL_ERROR;
}
}
/* Функция для отрисовки выбранной точки графика*/
void __GFX_Draw_Plotter_Value(uint8_t *Buffer_Frame, GFX_PlotterHandleTypeDef *hPlot)
{
/* Первичная инициализация плоттера */
if(hPlot->f.initialized == 0)
{
hPlot->f.initialized = 1;
GFX_Clean_Buffer_Frame(Buffer_Frame);
hPlot->dataX = -1;
if(__GFX_StartPlot(Buffer_Frame, hPlot) != HAL_OK)
return;
}
/* Очищение графика после полного заполнения */
if((hPlot->dataX < hPlot->xPos) || (hPlot->dataX >= hPlot->plotWidth))
{
__GFX_StartPlot(Buffer_Frame, hPlot);
}
/* Расчет позиции на графике */
// если график должен быть знаковым, то уменьшаем машстаб еще в два раза
if(hPlot->f.dataSigned)
hPlot->dataY = (hPlot->dataY + hPlot->plotHeight)/2;
// инвертирование y потому что он считается сверху вниз
uint8_t plot_y_down = hPlot->yPos + hPlot->plotHeight-1;
hPlot->dataY = plot_y_down - hPlot->dataY;
// Сдвиг графика
hPlot->dataY += hPlot->plotXShift;
/* Вывод пикселя позиции пикселя */
// Передний фронт
if(hPlot->dataY - hPlot->dataPrevY > 0)
{
/* Цикл для заполнения пикселей по вертикали, когда фронт очень резкий */
for(int y = hPlot->dataPrevY+1; y <= hPlot->dataY; y++)
{
if((y<hPlot->yPos+hPlot->plotHeight) && (y>=hPlot->yPos))
GFX_Draw_Pixel(Buffer_Frame, hPlot->dataX, y, 1);
}
}// Задний фронт
else if (hPlot->dataY - hPlot->dataPrevY < 0)
{
/* Цикл для заполнения пикселей по вертикали, когда фронт очень резкий */
for(int y = hPlot->dataPrevY-1; y >= hPlot->dataY; y--)
{
if((y<hPlot->yPos+hPlot->plotHeight) && (y>=hPlot->yPos))
GFX_Draw_Pixel(Buffer_Frame, hPlot->dataX, y, 1);
}
}// Плато
else
{
if((hPlot->dataY<hPlot->yPos+hPlot->plotHeight) && (hPlot->dataY>=hPlot->yPos))
GFX_Draw_Pixel(Buffer_Frame, hPlot->dataX, hPlot->dataY, 1);
}
}