1037 lines
35 KiB
C
1037 lines
35 KiB
C
/*
|
||
* 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);
|
||
}
|
||
|
||
}
|