Compare commits

..

3 Commits

Author SHA1 Message Date
Razvalyaev
142b9faf5a Оптимизированы функции полттера добавлены функции для рисования стрелок
Плоттер
- функция для отрисовки интерфейса плоттера
- функция для отрисовки самого графика (кривой)
- функции для отрисовки float, int, uint8_t, uint16_t
- добавлены сдвиги осей

Стрелки
- функция для рисования ортогональных стрелок (вправо, влево, вверх, вниз)
- функция для рисования стрелок в любом направлении
2025-02-21 17:22:04 +03:00
Razvalyaev
9a4e3c3dad Добавлен плот не по массиву и экзампл для синуса 2025-02-21 12:30:34 +03:00
Razvalyaev
d58273fe41 Функция рисования графика перенесена в gfx_lib и автоматизирована 2025-02-21 12:19:14 +03:00
9 changed files with 733 additions and 249 deletions

View File

@@ -2,16 +2,17 @@
#include "math.h"
int menu_white_theme = 0;
//#define SINE_EXAMPLE
//#define ROTATING_ARROW_EXAMPLE
#define SINE_EXAMPLE
//#define ECG_EXAMPLE
#define PLAYER_EXAMPLE
//#define PLAYER_EXAMPLE
#define ECG_SIZE 550
// Примерный массив, симулирующий ЭКГ-сигнал (нормализованный от 0 до 1)
const float ecg_data[ECG_SIZE] = {1.05893, 0.999357, 0.933132, 0.744792, 0.664672, 0.328846, 0.136133, -0.00837916, -0.131494,
float ecg_data[ECG_SIZE] = {1.05893, 0.999357, 0.933132, 0.744792, 0.664672, 0.328846, 0.136133, -0.00837916, -0.131494,
-0.1437, -0.182276, -0.215992, -0.128979, -0.131278, -0.0675223, 0.0027252, 0.0467067, -0.078859, -0.0658536, 0.0626352,
-0.0462435, 0.0476623, 0.0535328, 0.122938, 0.0501136, 0.034033, 0.0919253, 0.108374, 0.0667457, 0.0351678, -0.00115748,
0.0896459, 0.0288452, -0.0102636, 0.136738, 0.0397245, 0.0856079, 0.141222, 0.0826385, 0.165799, 0.105852, 0.172638,
@@ -63,94 +64,15 @@ const float ecg_data[ECG_SIZE] = {1.05893, 0.999357, 0.933132, 0.744792, 0.66467
void GFX_Draw_LoopIcon(uint8_t *Buffer_Frame, uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t pxColor);
GFXIconsTypeDef icons;
void Example_GFX_IconInit(void)
{
// play
icons.StopPlay.stop.xPos1 = play_icon_x_left-1;
icons.StopPlay.stop.yPos1 = play_icon_y_up;
icons.StopPlay.stop.xPos2 = play_icon_x_left-1;
icons.StopPlay.stop.yPos2 = play_icon_y_down;
icons.StopPlay.stop.xPos3 = play_icon_x_rigth+1;
icons.StopPlay.stop.yPos3 = play_icon_y_mid;
icons.StopPlay.stop.pxColor = 1;
// stop
icons.StopPlay.play_lines[0].xPos_Start = play_icon_x_left+1;
icons.StopPlay.play_lines[0].yPos_Start = play_icon_y_up;
icons.StopPlay.play_lines[0].xPos_End = play_icon_x_left+1;
icons.StopPlay.play_lines[0].yPos_End = play_icon_y_down;
icons.StopPlay.play_lines[0].pxColor = 1;
icons.StopPlay.play_lines[1].xPos_Start = play_icon_x_rigth;
icons.StopPlay.play_lines[1].yPos_Start = play_icon_y_up;
icons.StopPlay.play_lines[1].xPos_End = play_icon_x_rigth;
icons.StopPlay.play_lines[1].yPos_End = play_icon_y_down;
icons.StopPlay.play_lines[1].pxColor = 1;
// stop/play pressed
icons.StopPlay.PressedArea.selected_Width = selected_width;
icons.StopPlay.PressedArea.xPos_Start = play_icon_x_left-1-selected_width;
icons.StopPlay.PressedArea.yPos_Start = play_icon_y_up-selected_width;
icons.StopPlay.PressedArea.area_Width = play_icon_x_rigth+2-(play_icon_x_left-1)+2*selected_width+1;
icons.StopPlay.PressedArea.area_Height = play_icon_y_down-play_icon_y_up+2*selected_width+1;
// forward
icons.Forward.line.xPos_Start = forward_icon_x_rigth;
icons.Forward.line.yPos_Start = forward_icon_y_up;
icons.Forward.line.xPos_End = forward_icon_x_rigth;
icons.Forward.line.yPos_End = forward_icon_y_down;
icons.Forward.line.pxColor = 1;
icons.Forward.trig.xPos1 = forward_icon_x_left;
icons.Forward.trig.yPos1 = forward_icon_y_up;
icons.Forward.trig.xPos2 = forward_icon_x_left;
icons.Forward.trig.yPos2 = forward_icon_y_down;
icons.Forward.trig.xPos3 = forward_icon_x_rigth-1;
icons.Forward.trig.yPos3 = forward_icon_y_mid;
icons.Forward.trig.pxColor = 1;
// forward pressed
icons.Forward.PressedArea.selected_Width = selected_width;
icons.Forward.PressedArea.xPos_Start = forward_icon_x_left-selected_width;
icons.Forward.PressedArea.yPos_Start = forward_icon_y_up-selected_width;
icons.Forward.PressedArea.area_Width = forward_icon_x_rigth-(forward_icon_x_left)+2*selected_width+1;
icons.Forward.PressedArea.area_Height = forward_icon_y_down-forward_icon_y_up+2*selected_width+1;
// backward
icons.Backward.line.xPos_Start = backward_icon_x_left;
icons.Backward.line.yPos_Start = backward_icon_y_up;
icons.Backward.line.xPos_End = backward_icon_x_left;
icons.Backward.line.yPos_End = backward_icon_y_down;
icons.Backward.line.pxColor = 1;
icons.Backward.trig.xPos1 = backward_icon_x_rigth;
icons.Backward.trig.yPos1 = backward_icon_y_up;
icons.Backward.trig.xPos2 = backward_icon_x_rigth;
icons.Backward.trig.yPos2 = backward_icon_y_down;
icons.Backward.trig.xPos3 = backward_icon_x_left+1;
icons.Backward.trig.yPos3 = backward_icon_y_mid;
icons.Backward.trig.pxColor = 1;
// backward pressed
icons.Backward.PressedArea.xPos_Start = backward_icon_x_left-selected_width;
icons.Backward.PressedArea.yPos_Start = backward_icon_y_up-selected_width;
icons.Backward.PressedArea.area_Width = backward_icon_x_rigth-(backward_icon_x_left)+2*selected_width+1;
icons.Backward.PressedArea.area_Height = backward_icon_y_down-backward_icon_y_up+2*selected_width+1;
// loop
icons.Loop.xPos_Start = loop_icon_x_start;
icons.Loop.yPos_Start = loop_icon_y_start;
icons.Loop.icon_Width = loop_icon_width;
icons.Loop.icon_Height = loop_icon_height;
// loop pressed
icons.Loop.PressedArea.xPos_Start = loop_icon_x_start+3;
icons.Loop.PressedArea.yPos_Start = loop_icon_y_start+3;
icons.Loop.PressedArea.area_Width = loop_icon_width-5;
icons.Loop.PressedArea.area_Height = loop_icon_height-5;
// иконка зацикливания
GFX_Draw_LoopIcon(oled_buf, loop_icon_x_start, loop_icon_y_start, loop_icon_width, loop_icon_height, 1);
}
GFX_PlotterHandleTypeDef plotter;
void Example_GFX_Init(void)
{
Example_GFX_PlotterInit();
Example_GFX_IconInit();
oled_init();
}
void Example_OLED_GFX_Update(PlayerTypeDef *player)
{
@@ -163,61 +85,29 @@ void Example_OLED_GFX_Update(PlayerTypeDef *player)
oled_refresh();
}
}
float sine_cnt_step = 0.01;
int shift = 1;
float ecg_cnt = 0;
float ecg_cnt_step = 1;
float ecg_scale = 16;
uint8_t sine2plot;
float sine_cnt_step = 0.5;
float sine_cnt = 1;
float sine_scale = 255;
float ecg_step = 2.5;
float ecg_scale = 1.1;
void Example_GFX_CreateFrame(PlayerTypeDef *player)
{
#if defined(SINE_EXAMPLE) || defined(ECG_EXAMPLE)
static float sine_cnt;
static int display_cnt;
static int pix_y_prev = 0;
#if defined(SINE_EXAMPLE)
int pix_y = (int)((float)(sinf(sine_cnt)+shift)*((float)32/(1+shift)));
#elif defined(ECG_EXAMPLE)
int pix_y = (ecg_data[(int)ecg_cnt]*ecg_scale)+16;
ecg_cnt += ecg_cnt_step;
if(ecg_cnt > ECG_SIZE)
ecg_cnt = 0;
#endif
if(pix_y - pix_y_prev > 0)
{
for(int y = pix_y_prev+1; y <= pix_y; y++)
{
if(y<=32)
GFX_Draw_Pixel(oled_buf, display_cnt, 32 - y, 1);
}
}
else if (pix_y - pix_y_prev < 0)
{
for(int y = pix_y_prev-1; y >= pix_y; y--)
{
if(y<=32)
GFX_Draw_Pixel(oled_buf, display_cnt, 32 - y, 1);
}
}
else
GFX_Draw_Pixel(oled_buf, display_cnt, 32 - pix_y, 1);
display_cnt++;
#if defined(ROTATING_ARROW_EXAMPLE)
GFX_Clean_Buffer_Frame(oled_buf);
__GFX_Draw_Arrow(oled_buf, 64, 16, 5, sine_cnt, 1);
sine_cnt+=ecg_step;
#elif defined(SINE_EXAMPLE)
sine2plot = (sinf(sine_cnt)+1)*128;
GFX_Plotter_uint8_t(oled_buf, &plotter, &sine2plot, 0, 0, sine_scale);
sine_cnt += sine_cnt_step;
pix_y_prev = pix_y;
if(display_cnt>GFX_BufferWidth)
{
display_cnt = 0;
GFX_Clean_Buffer_Frame(oled_buf, sizeof(oled_buf));
}
#elif defined(ECG_EXAMPLE)
GFX_Plotter_float(oled_buf, &plotter, ecg_data, ECG_SIZE, ecg_step, ecg_scale);
#elif defined(PLAYER_EXAMPLE)
GFX_Clean_Buffer_Frame(oled_buf, sizeof(oled_buf));
GFX_Clean_Buffer_Frame(oled_buf);
// название песни
GFX_Output_String(oled_buf, 0, 0, "Harry Potter Theme", 0, 0);
@@ -312,3 +202,112 @@ void GFX_Draw_LoopIcon(uint8_t *Buffer_Frame, uint8_t x, uint8_t y, uint8_t widt
__GFX_Draw_Line(Buffer_Frame, x + r+spot_width, y + height, x + r + arr_size+spot_width, y + height - arr_size, pxColor); // Нижняя стрелка
__GFX_Draw_Line(Buffer_Frame, x + r+spot_width, y + height, x + r + arr_size+spot_width, y + height + arr_size, pxColor);
}
void Example_GFX_PlotterInit(void)
{
#if defined(SINE_EXAMPLE)
plotter.xPos = 0;
plotter.yPos = 0;
plotter.plotWidth = 128;
plotter.plotHeight = 30;
plotter.plotYShift = 64;
plotter.f.plotXAxis = 1;
plotter.f.plotYAxis = 1;
#elif defined(ECG_EXAMPLE)
plotter.xPos = 2;
plotter.yPos = 0;
plotter.plotWidth = 128-plotter.xPos;
plotter.plotHeight = 32;
plotter.f.plotXAxis = 1;
plotter.f.plotYAxis = 1;
plotter.f.plotFrame = 1;
#endif
}
void Example_GFX_IconInit(void)
{
// play
icons.StopPlay.stop.xPos1 = play_icon_x_left-1;
icons.StopPlay.stop.yPos1 = play_icon_y_up;
icons.StopPlay.stop.xPos2 = play_icon_x_left-1;
icons.StopPlay.stop.yPos2 = play_icon_y_down;
icons.StopPlay.stop.xPos3 = play_icon_x_rigth+1;
icons.StopPlay.stop.yPos3 = play_icon_y_mid;
icons.StopPlay.stop.pxColor = 1;
// stop
icons.StopPlay.play_lines[0].xPos_Start = play_icon_x_left+1;
icons.StopPlay.play_lines[0].yPos_Start = play_icon_y_up;
icons.StopPlay.play_lines[0].xPos_End = play_icon_x_left+1;
icons.StopPlay.play_lines[0].yPos_End = play_icon_y_down;
icons.StopPlay.play_lines[0].pxColor = 1;
icons.StopPlay.play_lines[1].xPos_Start = play_icon_x_rigth;
icons.StopPlay.play_lines[1].yPos_Start = play_icon_y_up;
icons.StopPlay.play_lines[1].xPos_End = play_icon_x_rigth;
icons.StopPlay.play_lines[1].yPos_End = play_icon_y_down;
icons.StopPlay.play_lines[1].pxColor = 1;
// stop/play pressed
icons.StopPlay.PressedArea.selected_Width = selected_width;
icons.StopPlay.PressedArea.xPos_Start = play_icon_x_left-1-selected_width;
icons.StopPlay.PressedArea.yPos_Start = play_icon_y_up-selected_width;
icons.StopPlay.PressedArea.area_Width = play_icon_x_rigth+2-(play_icon_x_left-1)+2*selected_width+1;
icons.StopPlay.PressedArea.area_Height = play_icon_y_down-play_icon_y_up+2*selected_width+1;
// forward
icons.Forward.line.xPos_Start = forward_icon_x_rigth;
icons.Forward.line.yPos_Start = forward_icon_y_up;
icons.Forward.line.xPos_End = forward_icon_x_rigth;
icons.Forward.line.yPos_End = forward_icon_y_down;
icons.Forward.line.pxColor = 1;
icons.Forward.trig.xPos1 = forward_icon_x_left;
icons.Forward.trig.yPos1 = forward_icon_y_up;
icons.Forward.trig.xPos2 = forward_icon_x_left;
icons.Forward.trig.yPos2 = forward_icon_y_down;
icons.Forward.trig.xPos3 = forward_icon_x_rigth-1;
icons.Forward.trig.yPos3 = forward_icon_y_mid;
icons.Forward.trig.pxColor = 1;
// forward pressed
icons.Forward.PressedArea.selected_Width = selected_width;
icons.Forward.PressedArea.xPos_Start = forward_icon_x_left-selected_width;
icons.Forward.PressedArea.yPos_Start = forward_icon_y_up-selected_width;
icons.Forward.PressedArea.area_Width = forward_icon_x_rigth-(forward_icon_x_left)+2*selected_width+1;
icons.Forward.PressedArea.area_Height = forward_icon_y_down-forward_icon_y_up+2*selected_width+1;
// backward
icons.Backward.line.xPos_Start = backward_icon_x_left;
icons.Backward.line.yPos_Start = backward_icon_y_up;
icons.Backward.line.xPos_End = backward_icon_x_left;
icons.Backward.line.yPos_End = backward_icon_y_down;
icons.Backward.line.pxColor = 1;
icons.Backward.trig.xPos1 = backward_icon_x_rigth;
icons.Backward.trig.yPos1 = backward_icon_y_up;
icons.Backward.trig.xPos2 = backward_icon_x_rigth;
icons.Backward.trig.yPos2 = backward_icon_y_down;
icons.Backward.trig.xPos3 = backward_icon_x_left+1;
icons.Backward.trig.yPos3 = backward_icon_y_mid;
icons.Backward.trig.pxColor = 1;
// backward pressed
icons.Backward.PressedArea.xPos_Start = backward_icon_x_left-selected_width;
icons.Backward.PressedArea.yPos_Start = backward_icon_y_up-selected_width;
icons.Backward.PressedArea.area_Width = backward_icon_x_rigth-(backward_icon_x_left)+2*selected_width+1;
icons.Backward.PressedArea.area_Height = backward_icon_y_down-backward_icon_y_up+2*selected_width+1;
// loop
icons.Loop.xPos_Start = loop_icon_x_start;
icons.Loop.yPos_Start = loop_icon_y_start;
icons.Loop.icon_Width = loop_icon_width;
icons.Loop.icon_Height = loop_icon_height;
// loop pressed
icons.Loop.PressedArea.xPos_Start = loop_icon_x_start+3;
icons.Loop.PressedArea.yPos_Start = loop_icon_y_start+3;
icons.Loop.PressedArea.area_Width = loop_icon_width-5;
icons.Loop.PressedArea.area_Height = loop_icon_height-5;
// иконка зацикливания
GFX_Draw_LoopIcon(oled_buf, loop_icon_x_start, loop_icon_y_start, loop_icon_width, loop_icon_height, 1);
}

View File

@@ -105,6 +105,8 @@ typedef struct
}GFXIconsTypeDef;
void Example_GFX_Init(void);
void Example_GFX_PlotterInit(void);
void Example_GFX_IconInit(void);
void Example_GFX_CreateFrame(PlayerTypeDef *player);
void Example_OLED_GFX_Update(PlayerTypeDef *player);

View File

@@ -10,20 +10,42 @@
*/
#include "gfx_lib.h"
#include "font_tahoma_8_prop.h"
#include "math.h"
#define PI 3.1415926535
/* переменные */
uint8_t chSpacing = 0; //межсимвольный интервал в px
/* функция очистки буфера кадра */
void GFX_Clean_Buffer_Frame(uint8_t *Buffer_Frame, uint32_t Buffer_Frame_Size)
void GFX_Clean_Buffer_Frame(uint8_t *Buffer_Frame)
{
if(Buffer_Frame == NULL)
return;
memset(Buffer_Frame, 0x00, Buffer_Frame_Size);
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)
{
@@ -377,6 +399,21 @@ void GFX_Draw_Triangle(uint8_t *Buffer_Frame, GFX_TriangleHandleTypeDef *hTriang
__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)
@@ -401,6 +438,171 @@ void GFX_Draw_Arc(uint8_t *Buffer_Frame, GFX_ArcHandleTypeDef *hArc)
}
}
/* Функция для отрисовки графика 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)
{
@@ -536,17 +738,108 @@ void __GFX_Draw_Circle_Filled(uint8_t *Buffer_Frame, int8_t xPos, int8_t yPos, i
}
/* функция рисования треугольника */
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)
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)
{
@@ -560,3 +853,101 @@ void __GFX_Draw_Arc(uint8_t *Buffer_Frame, uint8_t xPos, uint8_t yPos, uint8_t r
}
}
/* Функция для отрисовки осей и рамки графика */
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);
}
}

View File

@@ -10,7 +10,6 @@
#include "main.h"
#include "string.h"
#include "stdio.h"
#include "oled.h"
#define GFX_BufferWidth 128 //ширина дисплея в пикселях
#define GFX_BufferHeight 32 //высота дисплея в пикселях
@@ -31,8 +30,6 @@ typedef struct
uint8_t yPos_End;
uint8_t pxColor;
}GFX_LineHandleTypeDef;
typedef struct
{
uint8_t xPos_Start;
@@ -51,7 +48,6 @@ typedef struct
uint8_t pxColor;
uint8_t Filled;
}GFX_CircleHandleTypeDef;
typedef struct
{
uint8_t xPos1;
@@ -63,6 +59,17 @@ typedef struct
uint8_t pxColor;
uint8_t Filled;
}GFX_TriangleHandleTypeDef;
typedef struct
{
uint8_t xPos;
uint8_t yPos;
uint8_t size;
uint16_t angle;
uint8_t pxColor;
uint8_t Filled;
}GFX_ArrowHandleTypeDef;
typedef struct
{
uint8_t xPos;
@@ -74,10 +81,36 @@ typedef struct
uint8_t Filled;
}GFX_ArcHandleTypeDef;
typedef struct
{
uint8_t xPos;
uint8_t yPos;
uint8_t plotHeight;
uint8_t plotWidth;
int16_t dataX;
int32_t dataY;
float dataInd;
int32_t dataPrevY;
int16_t plotXShift;
int16_t plotYShift;
struct
{
unsigned dataSigned:1;
unsigned plotFrame:1;
unsigned plotXAxis:1;
unsigned plotYAxis:1;
unsigned initialized:1;
}f;
}GFX_PlotterHandleTypeDef;
/* прототипы функций */
void GFX_Clean_Buffer_Frame(uint8_t *Buffer_Frame, uint32_t Buffer_Frame_Size);
void GFX_Clean_Buffer_Frame(uint8_t *Buffer_Frame);
void GFX_Clean_Area(uint8_t *Buffer_Frame, uint16_t xPos_Start, uint16_t yPos_Start, uint16_t width, uint16_t height);
void GFX_Draw_Pixel(uint8_t *Buffer_Frame, uint8_t xPos, uint8_t yPos, uint8_t pxColor);
void GFX_Invertion_Area(uint8_t *Buffer_Frame, uint16_t xPos_Start, uint16_t yPos_Start, uint16_t width, uint16_t height);
void GFX_Invertion_Display(uint8_t *Buffer_Frame);
@@ -90,7 +123,12 @@ void GFX_Draw_Line(uint8_t *Buffer_Frame, GFX_LineHandleTypeDef *hLine);
void GFX_Draw_Rectangle(uint8_t *Buffer_Frame, GFX_RectangleHandleTypeDef *hRectangle);
void GFX_Draw_Circle(uint8_t *Buffer_Frame, GFX_CircleHandleTypeDef *hCircle);
void GFX_Draw_Triangle(uint8_t *Buffer_Frame, GFX_TriangleHandleTypeDef *hTriangle);
void GFX_Draw_Arrow(uint8_t *Buffer_Frame, GFX_ArrowHandleTypeDef *hArrow);
void GFX_Draw_Arc(uint8_t *Buffer_Frame, GFX_ArcHandleTypeDef *hArc);
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);
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);
void GFX_Plotter_int(uint8_t *Buffer_Frame, GFX_PlotterHandleTypeDef *hPlot, int *data, uint32_t data_size, float data_step, int data_max);
void GFX_Plotter_float(uint8_t *Buffer_Frame, GFX_PlotterHandleTypeDef *hPlot, float *data, uint32_t data_size, float data_step, float data_max);
@@ -104,5 +142,9 @@ void __GFX_Draw_Triangle(uint8_t *Buffer_Frame,
uint8_t xPos2, uint8_t yPos2,
uint8_t xPos3, uint8_t yPos3,
uint8_t pxColor);
void __GFX_Draw_Arrow(uint8_t *Buffer_Frame, uint8_t xPos, uint8_t yPos, uint8_t size, uint16_t angle, uint8_t 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);
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);
HAL_StatusTypeDef __GFX_StartPlot(uint8_t *Buffer_Frame, GFX_PlotterHandleTypeDef *hPlot);
void __GFX_Draw_Plotter_Value(uint8_t *Buffer_Frame, GFX_PlotterHandleTypeDef *hPlot);
#endif /* INC_PIXEL_GRAPHICS_H_ */

View File

@@ -90,7 +90,7 @@ int main(void)
MX_I2C1_Init();
/* USER CODE BEGIN 2 */
Menu_Control_Init(&player);
Example_GFX_IconInit();
Example_GFX_Init();
/* USER CODE END 2 */
/* Infinite loop */

File diff suppressed because one or more lines are too long

View File

@@ -163,7 +163,7 @@
<Ww>
<count>2</count>
<WinNumber>1</WinNumber>
<ItemText>sine_cnt_step</ItemText>
<ItemText>plotter,0x0A</ItemText>
</Ww>
<Ww>
<count>3</count>
@@ -205,6 +205,56 @@
<WinNumber>1</WinNumber>
<ItemText>ecg_scale</ItemText>
</Ww>
<Ww>
<count>11</count>
<WinNumber>1</WinNumber>
<ItemText>(pix_y_float/hPlot-&gt;dataMax)*(hPlot-&gt;plotHeight),0x0A</ItemText>
</Ww>
<Ww>
<count>12</count>
<WinNumber>1</WinNumber>
<ItemText>-4,0x0A</ItemText>
</Ww>
<Ww>
<count>13</count>
<WinNumber>1</WinNumber>
<ItemText>y,0x0A</ItemText>
</Ww>
<Ww>
<count>14</count>
<WinNumber>1</WinNumber>
<ItemText>sine_cnt_step</ItemText>
</Ww>
<Ww>
<count>15</count>
<WinNumber>1</WinNumber>
<ItemText>sine2plot,0x0A</ItemText>
</Ww>
<Ww>
<count>16</count>
<WinNumber>1</WinNumber>
<ItemText>sine_scale</ItemText>
</Ww>
<Ww>
<count>17</count>
<WinNumber>1</WinNumber>
<ItemText>pix_y_float</ItemText>
</Ww>
<Ww>
<count>18</count>
<WinNumber>1</WinNumber>
<ItemText>ecg_scale</ItemText>
</Ww>
<Ww>
<count>19</count>
<WinNumber>1</WinNumber>
<ItemText>sine_cnt</ItemText>
</Ww>
<Ww>
<count>20</count>
<WinNumber>1</WinNumber>
<ItemText>sine_cnt_step</ItemText>
</Ww>
</WatchWindow1>
<Tracepoint>
<THDelay>0</THDelay>

View File

@@ -129,7 +129,7 @@ Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
1 LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0

View File

@@ -5,5 +5,5 @@
Включает в себя:
- библиотеку для рисования (Core\GFX_Lib)
- библиотеку для I2C OLED дисплея (Core\OLED_Driver)
- библиотеку для SPI OLED дисплея (Core\LCD_Driver) (не сделано пока)
- библиотеку для SPI LCD дисплея (Core\LCD_Driver) (не сделано пока)
- примеры использования (Core\Example) (не сделано пока)