733 lines
22 KiB
C
733 lines
22 KiB
C
// Driver afficheur ADA 1983/chip ILI9341 pour Kit STM32L476 IUT1 de Grenoble Dpt GEII
|
|
//
|
|
// Adaptations L476, et fusion libs : V. GRENNERAT, IUT1 de Grenoble, Dpt GEII
|
|
// Correction bug coordonnees X et Y pour Draw_Char et Draw_Text : V. GRENNERAT
|
|
// Ajout fonction LCD_Draw_Image_XY : V. GRENNERAT
|
|
// Intégration des fonctions de configuration SPI3 et GPIO : V. GRENNERAT
|
|
// Version 2 (1/12/2019) :
|
|
// Suppression de l'utilisation de la HAL STM32 : V. GRENNERAT
|
|
// Diverses optimisations, dans les burst images.
|
|
// --------------------------------
|
|
// MIT License
|
|
//
|
|
// Copyright (c) 2017 Matej Artnak
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in all
|
|
// copies or substantial portions of the Software.
|
|
//
|
|
|
|
#include "LCD_driver.h"
|
|
|
|
void LCD_Write_Command(uint8_t Command) {
|
|
while ((SPI1->SR & SPI_SR_BSY) != 0);// Wait that everything is sent before changing the RS pin
|
|
CMD
|
|
;
|
|
while ((SPI1->SR & SPI_SR_TXE) == 0);
|
|
SPI1->DR = Command;
|
|
}
|
|
/* Send Data (char) to LCD via SPI bus */
|
|
void LCD_Write_Data(uint8_t Data) {
|
|
while ((SPI1->SR & SPI_SR_BSY) != 0);// Wait that everything is sent before changing the RS pin
|
|
DATA
|
|
;
|
|
while ((SPI1->SR & SPI_SR_TXE) == 0);
|
|
SPI1->DR = Data;
|
|
}
|
|
void LCD_Write_Data16(uint16_t data) {
|
|
while ((SPI1->SR & SPI_SR_BSY) != 0);// Wait that everything is sent before changing the RS pin
|
|
DATA
|
|
;
|
|
while ((SPI1->SR & SPI_SR_TXE) == 0);
|
|
SPI1->DR = (data >> 8) & 0xFF;// Send MSB
|
|
while ((SPI1->SR & SPI_SR_TXE) == 0);
|
|
SPI1->DR = data & 0xFF;// Send LSB
|
|
}
|
|
|
|
/* Set the frame to draw into and sends a write into frame command */
|
|
void LCD_Set_Address(uint16_t X1, uint16_t Y1, uint16_t X2, uint16_t Y2) {
|
|
LCD_Write_Command(0x2A);
|
|
LCD_Write_Data16(X1);
|
|
LCD_Write_Data16(X2);
|
|
|
|
LCD_Write_Command(0x2B);
|
|
LCD_Write_Data16(Y1 + 19);
|
|
LCD_Write_Data16(Y2 + 19);
|
|
|
|
LCD_Write_Command(0x2C);
|
|
}
|
|
|
|
void LCD_HardwareReset() {
|
|
HAL_GPIO_WritePin(DRESET_GPIO_Port, DRESET_Pin, GPIO_PIN_SET);
|
|
HAL_Delay(1);
|
|
HAL_GPIO_WritePin(DRESET_GPIO_Port, DRESET_Pin, GPIO_PIN_RESET);
|
|
HAL_Delay(20);
|
|
HAL_GPIO_WritePin(DRESET_GPIO_Port, DRESET_Pin, GPIO_PIN_SET);
|
|
HAL_Delay(150);
|
|
}
|
|
|
|
void LCD_Init(void) {
|
|
|
|
LCD_HardwareReset();
|
|
|
|
// enable spi1
|
|
SPI1->CR1 |= SPI_CR1_SPE;// NSS (CS) pin is automatically pulled low
|
|
HAL_Delay(300);
|
|
|
|
// Software reset
|
|
LCD_Write_Command(0x01);
|
|
HAL_Delay(150);
|
|
|
|
// Color mode: 16bit/pixels
|
|
LCD_Write_Command(0x3A);
|
|
LCD_Write_Data(0x55);
|
|
HAL_Delay(150);
|
|
|
|
// Enable color inversion (INVON)
|
|
LCD_Write_Command(0x21);
|
|
|
|
// Configure orientation stuff
|
|
LCD_Write_Command(0x36);
|
|
LCD_Write_Data(0b00111100);
|
|
|
|
// Exit sleep
|
|
LCD_Write_Command(0x11);
|
|
HAL_Delay(150);
|
|
|
|
// Turn on display
|
|
LCD_Write_Command(0x29);
|
|
HAL_Delay(400);
|
|
|
|
// Fill white
|
|
// LCD_Fill_Screen(WHITE);
|
|
//
|
|
//// Draw colors columns
|
|
// LCD_Set_Address(30, 30, LCD_WIDTH - 30, LCD_HEIGHT - 30);
|
|
// uint32_t size = (LCD_WIDTH - 59) * 20;
|
|
// LCD_Draw_Colour_Burst(BLACK, size);
|
|
// LCD_Draw_Colour_Burst(WHITE, size);
|
|
// LCD_Draw_Colour_Burst(BLUE, size);
|
|
// LCD_Draw_Colour_Burst(GREEN, size);
|
|
// LCD_Draw_Colour_Burst(RED, size);
|
|
// LCD_Draw_Colour_Burst(BLACK, 4 * size);
|
|
//
|
|
//// Draw rectangles in the angles
|
|
// LCD_Draw_Rectangle(1, 1, 20, 20, RED);
|
|
// LCD_Draw_Rectangle(LCD_WIDTH - 21, 1, 20, 20, GREEN);
|
|
// LCD_Draw_Rectangle(LCD_WIDTH - 21, LCD_HEIGHT - 21, 20, 20,
|
|
// BLACK);
|
|
// LCD_Draw_Rectangle(1, LCD_HEIGHT - 21, 20, 20, BLUE);
|
|
|
|
}
|
|
|
|
//INTERNAL FUNCTIONS OF THE LIBRARY
|
|
|
|
void LCD_Draw_Colour_Burst(uint16_t color, uint32_t size) {
|
|
while ((SPI1->SR & SPI_SR_BSY) != 0);// Wait that everything is sent before changing the RS pin
|
|
DATA
|
|
;
|
|
for (uint32_t j = 0; j < size; j++) {
|
|
while ((SPI1->SR & SPI_SR_TXE) == 0);
|
|
SPI1->DR = (color >> 8) & 0xFF;
|
|
while ((SPI1->SR & SPI_SR_TXE) == 0);
|
|
SPI1->DR = color & 0xFF;
|
|
}
|
|
}
|
|
|
|
void LCD_Fill_Screen(uint16_t color, uint8_t full_screen) {
|
|
uint16_t height = full_screen ? LCD_FULL_HEIGHT : LCD_HEIGHT;
|
|
|
|
LCD_Set_Address(0, 0, LCD_WIDTH - 1, height - 1);
|
|
LCD_Draw_Colour_Burst(color, LCD_WIDTH * height);
|
|
}
|
|
|
|
void LCD_Draw_Pixel(uint16_t x, uint16_t y, uint16_t color) {
|
|
if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)) return;
|
|
|
|
LCD_Set_Address(x, y, x, y);
|
|
LCD_Draw_Colour_Burst(color, 1);
|
|
}
|
|
|
|
void LCD_Draw_Rectangle(
|
|
uint16_t X,
|
|
uint16_t Y,
|
|
uint16_t Width,
|
|
uint16_t Height,
|
|
uint16_t Colour) {
|
|
if ((X >= LCD_WIDTH) || (Y >= LCD_HEIGHT)) return;
|
|
if ((X + Width - 1) >= LCD_WIDTH) {
|
|
Width = LCD_WIDTH - X;
|
|
}
|
|
if ((Y + Height - 1) >= LCD_HEIGHT) {
|
|
Height = LCD_HEIGHT - Y;
|
|
}
|
|
LCD_Set_Address(X, Y, X + Width - 1, Y + Height - 1);
|
|
LCD_Draw_Colour_Burst(Colour, Height * Width);
|
|
}
|
|
|
|
void LCD_Draw_Horizontal_Line(
|
|
uint16_t X,
|
|
uint16_t Y,
|
|
uint16_t Width,
|
|
uint16_t Colour) {
|
|
if ((X >= LCD_WIDTH) || (Y >= LCD_HEIGHT)) return;
|
|
if ((X + Width - 1) >= LCD_WIDTH) {
|
|
Width = LCD_WIDTH - X;
|
|
}
|
|
LCD_Set_Address(X, Y, X + Width - 1, Y);
|
|
LCD_Draw_Colour_Burst(Colour, Width);
|
|
}
|
|
|
|
void LCD_Draw_Vertical_Line(
|
|
uint16_t X,
|
|
uint16_t Y,
|
|
uint16_t Height,
|
|
uint16_t Colour) {
|
|
if ((X >= LCD_WIDTH) || (Y >= LCD_HEIGHT)) return;
|
|
if ((Y + Height - 1) >= LCD_HEIGHT) {
|
|
Height = LCD_HEIGHT - Y;
|
|
}
|
|
LCD_Set_Address(X, Y, X, Y + Height - 1);
|
|
LCD_Draw_Colour_Burst(Colour, Height);
|
|
}
|
|
|
|
void LCD_Draw_Hollow_Circle(
|
|
uint16_t X,
|
|
uint16_t Y,
|
|
uint16_t Radius,
|
|
uint16_t Colour) {
|
|
int x = Radius - 1;
|
|
int y = 0;
|
|
int dx = 1;
|
|
int dy = 1;
|
|
int err = dx - (Radius << 1);
|
|
|
|
while (x >= y) {
|
|
LCD_Draw_Pixel(X + x, Y + y, Colour);
|
|
LCD_Draw_Pixel(X + y, Y + x, Colour);
|
|
LCD_Draw_Pixel(X - y, Y + x, Colour);
|
|
LCD_Draw_Pixel(X - x, Y + y, Colour);
|
|
LCD_Draw_Pixel(X - x, Y - y, Colour);
|
|
LCD_Draw_Pixel(X - y, Y - x, Colour);
|
|
LCD_Draw_Pixel(X + y, Y - x, Colour);
|
|
LCD_Draw_Pixel(X + x, Y - y, Colour);
|
|
|
|
if (err <= 0) {
|
|
y++;
|
|
err += dy;
|
|
dy += 2;
|
|
}
|
|
if (err > 0) {
|
|
x--;
|
|
dx += 2;
|
|
err += (-Radius << 1) + dx;
|
|
}
|
|
}
|
|
}
|
|
|
|
void LCD_Draw_Filled_Circle(
|
|
uint16_t X,
|
|
uint16_t Y,
|
|
uint16_t Radius,
|
|
uint16_t Colour) {
|
|
|
|
int x = Radius;
|
|
int y = 0;
|
|
int xChange = 1 - (Radius << 1);
|
|
int yChange = 0;
|
|
int radiusError = 0;
|
|
|
|
while (x >= y) {
|
|
for (int i = X - x; i <= X + x; i++) {
|
|
LCD_Draw_Pixel(i, Y + y, Colour);
|
|
LCD_Draw_Pixel(i, Y - y, Colour);
|
|
}
|
|
for (int i = X - y; i <= X + y; i++) {
|
|
LCD_Draw_Pixel(i, Y + x, Colour);
|
|
LCD_Draw_Pixel(i, Y - x, Colour);
|
|
}
|
|
|
|
y++;
|
|
radiusError += yChange;
|
|
yChange += 2;
|
|
if (((radiusError << 1) + xChange) > 0) {
|
|
x--;
|
|
radiusError += xChange;
|
|
xChange += 2;
|
|
}
|
|
}
|
|
//Really slow implementation, will require future overhaul
|
|
//TODO: https://stackoverflow.com/questions/1201200/fast-algorithm-for-drawing-filled-circles
|
|
}
|
|
|
|
void LCD_Draw_Filled_Rectangle_Coord(
|
|
uint16_t X0,
|
|
uint16_t Y0,
|
|
uint16_t X1,
|
|
uint16_t Y1,
|
|
uint16_t Colour) {
|
|
uint16_t X_length = 0;
|
|
uint16_t Y_length = 0;
|
|
uint8_t Negative_X = 0;
|
|
uint8_t Negative_Y = 0;
|
|
int32_t Calc_Negative = 0;
|
|
|
|
uint16_t X0_true = 0;
|
|
uint16_t Y0_true = 0;
|
|
|
|
Calc_Negative = X1 - X0;
|
|
if (Calc_Negative < 0) Negative_X = 1;
|
|
Calc_Negative = 0;
|
|
|
|
Calc_Negative = Y1 - Y0;
|
|
if (Calc_Negative < 0) Negative_Y = 1;
|
|
|
|
//DRAW HORIZONTAL!
|
|
if (!Negative_X) {
|
|
X_length = X1 - X0 + 1;
|
|
X0_true = X0;
|
|
} else {
|
|
X_length = X0 - X1;
|
|
X0_true = X1;
|
|
}
|
|
|
|
//DRAW VERTICAL!
|
|
if (!Negative_Y) {
|
|
Y_length = Y1 - Y0;
|
|
Y0_true = Y0;
|
|
} else {
|
|
Y_length = Y0 - Y1;
|
|
Y0_true = Y1;
|
|
}
|
|
|
|
LCD_Draw_Rectangle(X0_true, Y0_true, X_length, Y_length, Colour);
|
|
}
|
|
|
|
/*Dessine une image dans une zone de l'ecran, aux coordonnées X et Y*/
|
|
//CONVERTISSEUR: http://www.digole.com/tools/PicturetoC_Hex_converter.php
|
|
//65K colour (2Bytes / Pixel)
|
|
//void LCD_Draw_Image_XY(
|
|
// const char *Image_Array,
|
|
// uint16_t X,
|
|
// uint16_t Y,
|
|
// uint16_t Width,
|
|
// uint16_t Height) {
|
|
// LCD_Set_Address(X, Y, X + Width - 1, Y + Height - 1);
|
|
//
|
|
// DATA
|
|
// ;
|
|
// CS_ON;
|
|
//
|
|
// for (uint32_t i = 0; i < Width * Height * 2; i += 2) {
|
|
// while ((SPI3->SR & SPI_SR_TXE) == 0);//Si FIFO full (TX buffer Empty=0), on attend
|
|
//// on utilise l'ecriture 16 bits dans DR, pour des envois 8 bits
|
|
//// Le LSB doit etre place ds le MSB :
|
|
// SPI3->DR = ((short) Image_Array[i + 1]) << 8 | Image_Array[i];
|
|
// }
|
|
//
|
|
// while ((SPI3->SR & SPI_SR_BSY) != 0);//Attendre fin envoi trame
|
|
// CS_OFF;
|
|
//}
|
|
/*Draws a full screen picture from flash. Image converted from RGB .jpeg/other to C array using online converter*/
|
|
//USING CONVERTER: http://www.digole.com/tools/PicturetoC_Hex_converter.php
|
|
//65K colour (2Bytes / Pixel)
|
|
//void LCD_Draw_Image_Full(const char *Image_Array, uint8_t Orientation) {
|
|
// switch (Orientation) {
|
|
// case SCREEN_HORIZONTAL_1:
|
|
// LCD_Set_Rotation(SCREEN_HORIZONTAL_1);
|
|
// LCD_Set_Address(0, 0, LCD_SCREEN_WIDTH, LCD_SCREEN_HEIGHT);
|
|
// break;
|
|
//
|
|
// case SCREEN_HORIZONTAL_2:
|
|
// LCD_Set_Rotation(SCREEN_HORIZONTAL_2);
|
|
// LCD_Set_Address(0, 0, LCD_SCREEN_WIDTH, LCD_SCREEN_HEIGHT);
|
|
// break;
|
|
//
|
|
// case SCREEN_VERTICAL_1:
|
|
// LCD_Set_Rotation(SCREEN_VERTICAL_1);
|
|
// LCD_Set_Address(0, 0, LCD_SCREEN_HEIGHT, LCD_SCREEN_WIDTH);
|
|
// break;
|
|
//
|
|
// case SCREEN_VERTICAL_2:
|
|
// LCD_Set_Rotation(SCREEN_VERTICAL_2);
|
|
// LCD_Set_Address(0, 0, LCD_SCREEN_HEIGHT, LCD_SCREEN_WIDTH);
|
|
// break;
|
|
// }
|
|
//
|
|
// DATA
|
|
// ;
|
|
// CS_ON;
|
|
//
|
|
// for (uint32_t i = 0; i < LCD_SCREEN_WIDTH * LCD_SCREEN_HEIGHT * 2; i += 2) {
|
|
// while ((SPI3->SR & SPI_SR_TXE) == 0);//Si FIFO full (TX buffer Empty=0), on attend
|
|
//// on utilise l'ecriture 16 bits dans DR, pour des envois 8 bits
|
|
//// Le LSB doit etre place ds le MSB :
|
|
// SPI3->DR = ((short) Image_Array[i + 1]) << 8 | Image_Array[i];
|
|
// }
|
|
//
|
|
// while ((SPI3->SR & SPI_SR_BSY) != 0);//Attendre fin envoi trame
|
|
// CS_OFF;
|
|
//}
|
|
void LCD_DrawHollowRoundRect(
|
|
uint16_t x,
|
|
uint16_t y,
|
|
uint16_t w,
|
|
uint16_t h,
|
|
uint8_t radius_top,
|
|
uint8_t radius_bottom,
|
|
uint8_t border_width,
|
|
uint16_t color,
|
|
uint16_t fill_color,
|
|
uint8_t do_fill) {
|
|
|
|
uint8_t radius_top_lines = radius_top ? radius_top - 1 : 0;
|
|
uint8_t radius_bottom_lines = radius_bottom ? radius_bottom - 1 : 0;
|
|
|
|
// Fill interior if requested
|
|
if (do_fill) {
|
|
uint16_t x_inner, y_inner, w_inner, h_inner;
|
|
uint8_t radius_top_fill, radius_bottom_fill;
|
|
|
|
if (border_width == 0) {
|
|
// Full area fill (no border)
|
|
x_inner = x;
|
|
y_inner = y;
|
|
w_inner = w;
|
|
h_inner = h;
|
|
radius_top_fill = radius_top;
|
|
radius_bottom_fill = radius_bottom;
|
|
} else {
|
|
// Interior area (inside border)
|
|
x_inner = x + border_width;
|
|
y_inner = y + border_width;
|
|
w_inner = (w > 2 * border_width) ? w - 2 * border_width : 0;
|
|
h_inner = (h > 2 * border_width) ? h - 2 * border_width : 0;
|
|
|
|
radius_top_fill =
|
|
(radius_top > border_width) ? radius_top - border_width : 0;
|
|
radius_bottom_fill =
|
|
(radius_bottom > border_width) ? radius_bottom - border_width : 0;
|
|
}
|
|
|
|
// Draw filled interior (only if dimensions are valid)
|
|
if (w_inner > 0 && h_inner > 0) {
|
|
uint16_t x_central = x_inner + radius_top_fill;
|
|
uint16_t y_central = y;
|
|
if (w_inner > 2 * radius_top_fill) {
|
|
uint16_t w_central = w_inner - 2 * radius_top_fill;
|
|
uint16_t h_central = h;
|
|
LCD_Draw_Rectangle(x_central, y_central, w_central, h_central,
|
|
fill_color);
|
|
}
|
|
|
|
uint16_t x_left = x;
|
|
uint16_t y_left = y_inner + radius_top_fill;
|
|
if (x_inner + radius_top_fill + 1 > x) {
|
|
uint16_t w_left = x_inner + radius_top_fill + 1 - x;
|
|
if (h_inner > radius_bottom_fill + radius_top_fill) {
|
|
uint16_t h_left = h_inner - radius_bottom_fill - radius_top_fill;
|
|
LCD_Draw_Rectangle(x_left, y_left, w_left, h_left, fill_color);
|
|
if (x + w > w_left) {
|
|
uint16_t x_right = x + w - w_left;
|
|
LCD_Draw_Rectangle(x_right, y_left, w_left, h_left, fill_color);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// Top-left rounded corner
|
|
if (radius_top_fill > 0) {
|
|
uint32_t r_sq = (uint32_t) radius_top_fill * radius_top_fill;
|
|
int16_t center_x = x_inner + radius_top_fill - 1;
|
|
int16_t center_y = y_inner + radius_top_fill - 1;
|
|
|
|
for (int16_t i = x_inner; i < x_inner + radius_top_fill; i++) {
|
|
for (int16_t j = y_inner; j < y_inner + radius_top_fill; j++) {
|
|
int16_t dx = center_x - i;
|
|
int16_t dy = center_y - j;
|
|
uint32_t dist_sq = (uint32_t) dx * dx + (uint32_t) dy * dy;
|
|
if (dist_sq <= r_sq) {
|
|
LCD_Draw_Pixel(i, j, fill_color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Top-right rounded corner
|
|
if (radius_top_fill > 0) {
|
|
uint32_t r_sq = (uint32_t) radius_top_fill * radius_top_fill;
|
|
int16_t center_x = x_inner + w_inner - radius_top_fill;
|
|
int16_t center_y = y_inner + radius_top_fill - 1;
|
|
|
|
for (int16_t i = x_inner + w_inner - radius_top_fill;
|
|
i < x_inner + w_inner; i++) {
|
|
for (int16_t j = y_inner; j < y_inner + radius_top_fill; j++) {
|
|
int16_t dx = i - center_x;
|
|
int16_t dy = center_y - j;
|
|
uint32_t dist_sq = (uint32_t) dx * dx + (uint32_t) dy * dy;
|
|
if (dist_sq <= r_sq) {
|
|
LCD_Draw_Pixel(i, j, fill_color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Bottom-left rounded corner
|
|
if (radius_bottom_fill > 0) {
|
|
uint32_t r_sq = (uint32_t) radius_bottom_fill * radius_bottom_fill;
|
|
int16_t center_x = x_inner + radius_bottom_fill - 1;
|
|
int16_t center_y = y_inner + h_inner - radius_bottom_fill;
|
|
|
|
for (int16_t i = x_inner; i < x_inner + radius_bottom_fill; i++) {
|
|
for (int16_t j = y_inner + h_inner - radius_bottom_fill;
|
|
j < y_inner + h_inner; j++) {
|
|
int16_t dx = center_x - i;
|
|
int16_t dy = j - center_y;
|
|
uint32_t dist_sq = (uint32_t) dx * dx + (uint32_t) dy * dy;
|
|
if (dist_sq <= r_sq) {
|
|
LCD_Draw_Pixel(i, j, fill_color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Bottom-right rounded corner
|
|
if (radius_bottom_fill > 0) {
|
|
uint32_t r_sq = (uint32_t) radius_bottom_fill * radius_bottom_fill;
|
|
int16_t center_x = x_inner + w_inner - radius_bottom_fill;
|
|
int16_t center_y = y_inner + h_inner - radius_bottom_fill;
|
|
|
|
for (int16_t i = x_inner + w_inner - radius_bottom_fill;
|
|
i < x_inner + w_inner; i++) {
|
|
for (int16_t j = y_inner + h_inner - radius_bottom_fill;
|
|
j < y_inner + h_inner; j++) {
|
|
int16_t dx = i - center_x;
|
|
int16_t dy = j - center_y;
|
|
uint32_t dist_sq = (uint32_t) dx * dx + (uint32_t) dy * dy;
|
|
if (dist_sq <= r_sq) {
|
|
LCD_Draw_Pixel(i, j, fill_color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Draw border only if requested (border_width > 0)
|
|
if (border_width > 0) {
|
|
// Top horizontal border segment
|
|
if (w > 2 * radius_top_lines) {
|
|
uint16_t x_top = x + radius_top_lines;
|
|
uint16_t y_top = y;
|
|
uint16_t w_top = w - 2 * radius_top_lines;
|
|
uint16_t h_top = border_width;
|
|
LCD_Draw_Rectangle(x_top, y_top, w_top, h_top, color);
|
|
}
|
|
|
|
// Bottom horizontal border segment
|
|
if (w > 2 * radius_bottom_lines) {
|
|
uint16_t x_bottom = x + radius_bottom_lines;
|
|
uint16_t y_bottom = y + h - border_width;
|
|
uint16_t w_bottom = w - 2 * radius_bottom_lines;
|
|
uint16_t h_bottom = border_width;
|
|
LCD_Draw_Rectangle(x_bottom, y_bottom, w_bottom, h_bottom, color);
|
|
}
|
|
|
|
// Left vertical border segment
|
|
if (h > radius_top_lines + radius_bottom_lines) {
|
|
uint16_t x_left = x;
|
|
uint16_t y_left = y + radius_top_lines;
|
|
uint16_t w_left = border_width;
|
|
uint16_t h_left = h - radius_top_lines - radius_bottom_lines;
|
|
LCD_Draw_Rectangle(x_left, y_left, w_left, h_left, color);
|
|
}
|
|
|
|
// Right vertical border segment
|
|
if (h > radius_top_lines + radius_bottom_lines) {
|
|
uint16_t x_right = x + w - border_width;
|
|
uint16_t y_right = y + radius_top_lines;
|
|
uint16_t w_right = border_width;
|
|
uint16_t h_right = h - radius_top_lines - radius_bottom_lines;
|
|
LCD_Draw_Rectangle(x_right, y_right, w_right, h_right, color);
|
|
}
|
|
|
|
// Top-left rounded corner
|
|
if (radius_top > 0) {
|
|
int16_t inner_radius = radius_top - border_width;
|
|
if (inner_radius < 0) inner_radius = 0;
|
|
uint32_t inner_sq = (uint32_t) inner_radius * inner_radius;
|
|
uint32_t outer_sq = (uint32_t) radius_top * radius_top;
|
|
int16_t center_x = x + radius_top - 1;
|
|
int16_t center_y = y + radius_top - 1;
|
|
|
|
for (int16_t i = x; i < x + radius_top; i++) {
|
|
for (int16_t j = y; j < y + radius_top; j++) {
|
|
int16_t dx = center_x - i;
|
|
int16_t dy = center_y - j;
|
|
uint32_t dist_sq = (uint32_t) dx * dx + (uint32_t) dy * dy;
|
|
if (dist_sq <= outer_sq && dist_sq > inner_sq) {
|
|
LCD_Draw_Pixel(i, j, color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Top-right rounded corner
|
|
if (radius_top > 0) {
|
|
int16_t inner_radius = radius_top - border_width;
|
|
if (inner_radius < 0) inner_radius = 0;
|
|
uint32_t inner_sq = (uint32_t) inner_radius * inner_radius;
|
|
uint32_t outer_sq = (uint32_t) radius_top * radius_top;
|
|
int16_t center_x = x + w - radius_top;
|
|
int16_t center_y = y + radius_top - 1;
|
|
|
|
for (int16_t i = x + w - radius_top; i < x + w; i++) {
|
|
for (int16_t j = y; j < y + radius_top; j++) {
|
|
int16_t dx = i - center_x;
|
|
int16_t dy = center_y - j;
|
|
uint32_t dist_sq = (uint32_t) dx * dx + (uint32_t) dy * dy;
|
|
if (dist_sq <= outer_sq && dist_sq > inner_sq) {
|
|
LCD_Draw_Pixel(i, j, color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Bottom-left rounded corner
|
|
if (radius_bottom > 0) {
|
|
int16_t inner_radius = radius_bottom - border_width;
|
|
if (inner_radius < 0) inner_radius = 0;
|
|
uint32_t inner_sq = (uint32_t) inner_radius * inner_radius;
|
|
uint32_t outer_sq = (uint32_t) radius_bottom * radius_bottom;
|
|
int16_t center_x = x + radius_bottom - 1;
|
|
int16_t center_y = y + h - radius_bottom;
|
|
|
|
for (int16_t i = x; i < x + radius_bottom; i++) {
|
|
for (int16_t j = y + h - radius_bottom; j < y + h; j++) {
|
|
int16_t dx = center_x - i;
|
|
int16_t dy = j - center_y;
|
|
uint32_t dist_sq = (uint32_t) dx * dx + (uint32_t) dy * dy;
|
|
if (dist_sq <= outer_sq && dist_sq > inner_sq) {
|
|
LCD_Draw_Pixel(i, j, color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Bottom-right rounded corner
|
|
if (radius_bottom > 0) {
|
|
int16_t inner_radius = radius_bottom - border_width;
|
|
if (inner_radius < 0) inner_radius = 0;
|
|
uint32_t inner_sq = (uint32_t) inner_radius * inner_radius;
|
|
uint32_t outer_sq = (uint32_t) radius_bottom * radius_bottom;
|
|
int16_t center_x = x + w - radius_bottom;
|
|
int16_t center_y = y + h - radius_bottom;
|
|
|
|
for (int16_t i = x + w - radius_bottom; i < x + w; i++) {
|
|
for (int16_t j = y + h - radius_bottom; j < y + h; j++) {
|
|
int16_t dx = i - center_x;
|
|
int16_t dy = j - center_y;
|
|
uint32_t dist_sq = (uint32_t) dx * dx + (uint32_t) dy * dy;
|
|
if (dist_sq <= outer_sq && dist_sq > inner_sq) {
|
|
LCD_Draw_Pixel(i, j, color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Build icons with https://notisrac.github.io/FileToCArray/
|
|
void LCD_Draw_Icon(
|
|
const uint8_t *icon,
|
|
uint16_t x,
|
|
uint16_t y,
|
|
uint16_t w,
|
|
uint16_t h,
|
|
uint16_t fg_color,
|
|
uint16_t bg_color) {
|
|
|
|
LCD_Set_Address(x, y, x + w - 1, y + h - 1);
|
|
|
|
uint16_t pendingPixelCount = 0;
|
|
uint8_t pendingPixelBit = 0;
|
|
uint16_t i = 0;
|
|
for (int line = 0; line < h; line++) {
|
|
for (int col = 0; col < w; col++) {
|
|
|
|
uint16_t byteIndex = i / 8;
|
|
uint8_t bitIndex = i % 8;
|
|
uint8_t pixelBit = (icon[byteIndex] & (0x80 >> bitIndex)) ? 1 : 0;
|
|
|
|
if (pixelBit == pendingPixelBit) {
|
|
pendingPixelCount++;
|
|
} else {
|
|
if (pendingPixelCount != 0) {
|
|
LCD_Draw_Colour_Burst(pendingPixelBit ? fg_color : bg_color,
|
|
pendingPixelCount);
|
|
pendingPixelCount = 1;
|
|
}
|
|
pendingPixelBit = pixelBit;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
if (pendingPixelCount != 0) {
|
|
LCD_Draw_Colour_Burst(pendingPixelBit ? fg_color : bg_color,
|
|
pendingPixelCount);
|
|
}
|
|
|
|
}
|
|
|
|
// Three colors icon, 8bits/pixel RRRGGGBB, RRR >= 0b100 => FG, GGG >= 0b100 => ALT, BB >= 0b10 => BG
|
|
// Build icons with https://notisrac.github.io/FileToCArray/
|
|
void LCD_Draw_Icon_3(
|
|
const uint8_t *icon,
|
|
uint16_t x,
|
|
uint16_t y,
|
|
uint16_t w,
|
|
uint16_t h,
|
|
uint16_t fg_color,
|
|
uint16_t alt_color,
|
|
uint16_t bg_color) {
|
|
LCD_Set_Address(x, y, x + w - 1, y + h - 1);
|
|
|
|
uint16_t pendingPixelCount = 0;
|
|
uint16_t pendingPixelColor = 0;
|
|
uint16_t i = 0;
|
|
for (int line = 0; line < h; line++) {
|
|
for (int col = 0; col < w; col++) {
|
|
|
|
uint8_t pixelByte = icon[i];
|
|
uint8_t rrr = (pixelByte & 0b11100000) >> 5;
|
|
uint8_t ggg = (pixelByte & 0b00011100) >> 2;
|
|
//uint8_t bb = (pixelByte & 0b00000011);
|
|
|
|
uint16_t color =
|
|
rrr >= 0b100 ? fg_color : (ggg >= 0b100 ? alt_color : bg_color);
|
|
|
|
LCD_Draw_Colour_Burst(color, 1);
|
|
// if (color == pendingPixelColor) {
|
|
// pendingPixelCount++;
|
|
// } else {
|
|
// if (pendingPixelCount != 0) {
|
|
// LCD_Draw_Colour_Burst(color, pendingPixelCount);
|
|
// pendingPixelCount = 1;
|
|
// }
|
|
// pendingPixelColor = color;
|
|
// }
|
|
i++;
|
|
}
|
|
}
|
|
if (pendingPixelCount != 0) {
|
|
LCD_Draw_Colour_Burst(pendingPixelColor, pendingPixelCount);
|
|
}
|
|
}
|
|
|