/* * 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((yyPos+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((yyPos+hPlot->plotHeight) && (y>=hPlot->yPos)) GFX_Draw_Pixel(Buffer_Frame, hPlot->dataX, y, 1); } }// Плато else { if((hPlot->dataYyPos+hPlot->plotHeight) && (hPlot->dataY>=hPlot->yPos)) GFX_Draw_Pixel(Buffer_Frame, hPlot->dataX, hPlot->dataY, 1); } }