346 lines
12 KiB
C
346 lines
12 KiB
C
// gc9a01.c (version nettoyée + optimisée)
|
|
#include "gc9a01.h"
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
static SPI_HandleTypeDef *hspi_gc9a01 = NULL;
|
|
|
|
// police 8x8 tronquée pour la demo (ton tableau complet ici)
|
|
static const uint8_t font8x8_basic[128][8] = {
|
|
[32] = {0,0,0,0,0,0,0,0},
|
|
/* ... place ici le tableau complet depuis ton fichier original ... */
|
|
[65] = { 0x0C,0x1E,0x33,0x33,0x3F,0x33,0x33,0x00 },
|
|
/* etc. */
|
|
};
|
|
|
|
// helpers pour contrôle des lignes CS/DC
|
|
static inline void GC9A01_Select(void) {
|
|
HAL_GPIO_WritePin(GC9A01_CS_GPIO_Port, GC9A01_CS_Pin, GPIO_PIN_RESET);
|
|
}
|
|
static inline void GC9A01_Unselect(void) {
|
|
HAL_GPIO_WritePin(GC9A01_CS_GPIO_Port, GC9A01_CS_Pin, GPIO_PIN_SET);
|
|
}
|
|
static inline void GC9A01_DC_Command(void) {
|
|
HAL_GPIO_WritePin(GC9A01_DC_GPIO_Port, GC9A01_DC_Pin, GPIO_PIN_RESET);
|
|
}
|
|
static inline void GC9A01_DC_Data(void) {
|
|
HAL_GPIO_WritePin(GC9A01_DC_GPIO_Port, GC9A01_DC_Pin, GPIO_PIN_SET);
|
|
}
|
|
|
|
static void GC9A01_WriteCommand(uint8_t cmd) {
|
|
GC9A01_DC_Command();
|
|
GC9A01_Select();
|
|
HAL_SPI_Transmit(hspi_gc9a01, &cmd, 1, HAL_MAX_DELAY);
|
|
GC9A01_Unselect();
|
|
}
|
|
|
|
static void GC9A01_WriteData(uint8_t data) {
|
|
GC9A01_DC_Data();
|
|
GC9A01_Select();
|
|
HAL_SPI_Transmit(hspi_gc9a01, &data, 1, HAL_MAX_DELAY);
|
|
GC9A01_Unselect();
|
|
}
|
|
|
|
static void GC9A01_WriteDataBuffer(uint8_t *buffer, uint32_t len) {
|
|
if (len == 0) return;
|
|
GC9A01_DC_Data();
|
|
GC9A01_Select();
|
|
HAL_SPI_Transmit(hspi_gc9a01, buffer, len, HAL_MAX_DELAY);
|
|
GC9A01_Unselect();
|
|
}
|
|
|
|
static void GC9A01_SetAddressWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
|
|
GC9A01_WriteCommand(GC9A01_CASET);
|
|
uint8_t data_col[4] = { (uint8_t)(x0>>8), (uint8_t)(x0&0xFF), (uint8_t)(x1>>8), (uint8_t)(x1&0xFF) };
|
|
GC9A01_WriteDataBuffer(data_col, 4);
|
|
|
|
GC9A01_WriteCommand(GC9A01_RASET);
|
|
uint8_t data_row[4] = { (uint8_t)(y0>>8), (uint8_t)(y0&0xFF), (uint8_t)(y1>>8), (uint8_t)(y1&0xFF) };
|
|
GC9A01_WriteDataBuffer(data_row, 4);
|
|
|
|
GC9A01_WriteCommand(GC9A01_RAMWR);
|
|
}
|
|
|
|
void GC9A01_Init(SPI_HandleTypeDef *hspi) {
|
|
hspi_gc9a01 = hspi;
|
|
GC9A01_Reset();
|
|
HAL_Delay(120);
|
|
|
|
GC9A01_WriteCommand(GC9A01_SWRESET);
|
|
HAL_Delay(150);
|
|
|
|
GC9A01_WriteCommand(GC9A01_SLPOUT); // sortie du mode veille
|
|
HAL_Delay(120);
|
|
|
|
// Format pixel RGB565 16-bit
|
|
GC9A01_WriteCommand(GC9A01_COLMOD);
|
|
GC9A01_WriteData(0x55); // 16 bits/pixel
|
|
HAL_Delay(10);
|
|
|
|
// Memory Access Control : rotation / orientation
|
|
GC9A01_WriteCommand(GC9A01_MADCTL);
|
|
GC9A01_WriteData(0x00); // rotation normale, ajuste si besoin
|
|
|
|
// Activation de l'écran
|
|
GC9A01_WriteCommand(GC9A01_INVON); // inversion couleurs (souvent requis)
|
|
HAL_Delay(10);
|
|
|
|
GC9A01_WriteCommand(GC9A01_NORON); // mode normal on
|
|
HAL_Delay(10);
|
|
|
|
GC9A01_WriteCommand(GC9A01_DISPON); // allume l'écran
|
|
HAL_Delay(100);
|
|
|
|
}
|
|
|
|
|
|
void GC9A01_Reset(void) {
|
|
HAL_GPIO_WritePin(GC9A01_RST_GPIO_Port, GC9A01_RST_Pin, GPIO_PIN_RESET);
|
|
HAL_Delay(10);
|
|
HAL_GPIO_WritePin(GC9A01_RST_GPIO_Port, GC9A01_RST_Pin, GPIO_PIN_SET);
|
|
HAL_Delay(10);
|
|
}
|
|
|
|
void GC9A01_DisplayOn(void) { GC9A01_WriteCommand(GC9A01_DISPON); }
|
|
void GC9A01_DisplayOff(void) { GC9A01_WriteCommand(GC9A01_DISPOFF); }
|
|
|
|
void GC9A01_SetRotation(uint8_t rotation) {
|
|
GC9A01_WriteCommand(GC9A01_MADCTL);
|
|
switch(rotation) {
|
|
case 0: GC9A01_WriteData(0x08); break;
|
|
case 1: GC9A01_WriteData(0x68); break;
|
|
case 2: GC9A01_WriteData(0xC8); break;
|
|
case 3: GC9A01_WriteData(0xA8); break;
|
|
}
|
|
}
|
|
|
|
// Optimisé : envoie des blocs de pixels pour remplir un rectangle
|
|
void GC9A01_FillRect(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t color) {
|
|
if (x >= GC9A01_WIDTH || y >= GC9A01_HEIGHT) return;
|
|
if (x + width > GC9A01_WIDTH) width = GC9A01_WIDTH - x;
|
|
if (y + height > GC9A01_HEIGHT) height = GC9A01_HEIGHT - y;
|
|
|
|
GC9A01_SetAddressWindow(x, y, x + width - 1, y + height - 1);
|
|
|
|
static uint8_t data[PIX_BUF * 2];
|
|
uint8_t high = color >> 8;
|
|
uint8_t low = color & 0xFF;
|
|
for (uint32_t i = 0; i < PIX_BUF; ++i) {
|
|
data[2*i] = high;
|
|
data[2*i + 1] = low;
|
|
}
|
|
|
|
uint32_t total = (uint32_t)width * height;
|
|
GC9A01_DC_Data();
|
|
GC9A01_Select();
|
|
while (total) {
|
|
uint32_t chunk = (total > PIX_BUF) ? PIX_BUF : total;
|
|
HAL_SPI_Transmit(hspi_gc9a01, data, chunk * 2, HAL_MAX_DELAY);
|
|
total -= chunk;
|
|
}
|
|
GC9A01_Unselect();
|
|
}
|
|
|
|
void GC9A01_FillScreen(uint16_t color) {
|
|
GC9A01_FillRect(0, 0, GC9A01_WIDTH, GC9A01_HEIGHT, color);
|
|
}
|
|
|
|
// Remplit uniquement la zone ronde (en envoyant per-scanline des chunks contigus)
|
|
void GC9A01_FillCircleScreen(uint16_t color) {
|
|
const uint16_t cx = GC9A01_RADIUS;
|
|
const uint16_t cy = GC9A01_RADIUS;
|
|
const int32_t r = GC9A01_RADIUS;
|
|
const int32_t r2 = r * r;
|
|
static uint8_t buffer[PIX_BUF * 2];
|
|
uint8_t high = color >> 8;
|
|
uint8_t low = color & 0xFF;
|
|
|
|
for (int y = 0; y < GC9A01_HEIGHT; ++y) {
|
|
int32_t dy = y - (int32_t)cy;
|
|
// calcule x-range pour cette ligne qui est dans le cercle
|
|
// x^2 + dy^2 <= r^2 => x in [cx - dx, cx + dx] where dx = sqrt(r^2 - dy^2)
|
|
int32_t tmp = r2 - dy*dy;
|
|
if (tmp < 0) continue;
|
|
int32_t dx = (int32_t)sqrtf((float)tmp);
|
|
int32_t xstart = cx - dx;
|
|
int32_t xend = cx + dx;
|
|
int32_t len = xend - xstart + 1;
|
|
if (xstart < 0) { len -= -xstart; xstart = 0; }
|
|
if (xend >= GC9A01_WIDTH) { len -= (xend - (GC9A01_WIDTH-1)); xend = GC9A01_WIDTH-1; }
|
|
if (len <= 0) continue;
|
|
|
|
// Prépare la fenêtre pour cette portion contiguë et envoie la ligne en chunks
|
|
GC9A01_SetAddressWindow((uint16_t)xstart, (uint16_t)y, (uint16_t)xend, (uint16_t)y);
|
|
// remplit buffer
|
|
uint32_t to_send = (uint32_t)len;
|
|
GC9A01_DC_Data();
|
|
GC9A01_Select();
|
|
while (to_send) {
|
|
uint32_t chunk = (to_send > PIX_BUF) ? PIX_BUF : to_send;
|
|
for (uint32_t i = 0; i < chunk; ++i) {
|
|
buffer[2*i] = high;
|
|
buffer[2*i + 1] = low;
|
|
}
|
|
HAL_SPI_Transmit(hspi_gc9a01, buffer, chunk * 2, HAL_MAX_DELAY);
|
|
to_send -= chunk;
|
|
}
|
|
GC9A01_Unselect();
|
|
}
|
|
}
|
|
|
|
void GC9A01_SetPixel(uint16_t x, uint16_t y, uint16_t color) {
|
|
if (x >= GC9A01_WIDTH || y >= GC9A01_HEIGHT) return;
|
|
GC9A01_SetAddressWindow(x, y, x, y);
|
|
uint8_t data[2] = { (uint8_t)(color >> 8), (uint8_t)(color & 0xFF) };
|
|
GC9A01_WriteDataBuffer(data, 2);
|
|
}
|
|
|
|
// (les fonctions DrawLine, DrawCircle, FillCircle, DrawChar, DrawString...)
|
|
// tu peux réutiliser les implémentations que tu avais — les voici en version compacte:
|
|
|
|
void GC9A01_DrawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color) {
|
|
int16_t dx = abs(x1 - x0);
|
|
int16_t dy = abs(y1 - y0);
|
|
int16_t sx = (x0 < x1) ? 1 : -1;
|
|
int16_t sy = (y0 < y1) ? 1 : -1;
|
|
int16_t err = dx - dy;
|
|
while (1) {
|
|
GC9A01_SetPixel(x0, y0, color);
|
|
if (x0 == x1 && y0 == y1) break;
|
|
int16_t e2 = 2 * err;
|
|
if (e2 > -dy) { err -= dy; x0 += sx; }
|
|
if (e2 < dx) { err += dx; y0 += sy; }
|
|
}
|
|
}
|
|
|
|
void GC9A01_DrawRect(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t color) {
|
|
GC9A01_DrawLine(x, y, x + width - 1, y, color);
|
|
GC9A01_DrawLine(x + width - 1, y, x + width - 1, y + height - 1, color);
|
|
GC9A01_DrawLine(x + width - 1, y + height - 1, x, y + height - 1, color);
|
|
GC9A01_DrawLine(x, y + height - 1, x, y, color);
|
|
}
|
|
|
|
void GC9A01_DrawCircle(uint16_t x, uint16_t y, uint8_t radius, uint16_t color) {
|
|
int16_t f = 1 - radius;
|
|
int16_t dx = 1;
|
|
int16_t dy = -2 * radius;
|
|
int16_t xi = 0;
|
|
int16_t yi = radius;
|
|
GC9A01_SetPixel(x, y + radius, color);
|
|
GC9A01_SetPixel(x, y - radius, color);
|
|
GC9A01_SetPixel(x + radius, y, color);
|
|
GC9A01_SetPixel(x - radius, y, color);
|
|
while (xi < yi) {
|
|
if (f >= 0) {
|
|
yi--;
|
|
dy += 2;
|
|
f += dy;
|
|
}
|
|
xi++;
|
|
dx += 2;
|
|
f += dx;
|
|
GC9A01_SetPixel(x + xi, y + yi, color);
|
|
GC9A01_SetPixel(x - xi, y + yi, color);
|
|
GC9A01_SetPixel(x + xi, y - yi, color);
|
|
GC9A01_SetPixel(x - xi, y - yi, color);
|
|
GC9A01_SetPixel(x + yi, y + xi, color);
|
|
GC9A01_SetPixel(x - yi, y + xi, color);
|
|
GC9A01_SetPixel(x + yi, y - xi, color);
|
|
GC9A01_SetPixel(x - yi, y - xi, color);
|
|
}
|
|
}
|
|
|
|
void GC9A01_FillCircle(uint16_t x, uint16_t y, uint8_t radius, uint16_t color) {
|
|
for (int16_t dy = -radius; dy <= radius; dy++) {
|
|
for (int16_t dx = -radius; dx <= radius; dx++) {
|
|
if (dx*dx + dy*dy <= radius*radius) {
|
|
GC9A01_SetPixel(x + dx, y + dy, color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GC9A01_DrawChar(uint16_t x, uint16_t y, char c, uint16_t color, uint16_t bg_color, uint8_t size) {
|
|
if ((uint8_t)c < 32 || (uint8_t)c > 127) c = ' ';
|
|
for (int i = 0; i < 8; ++i) {
|
|
uint8_t line = font8x8_basic[(uint8_t)c][i];
|
|
for (int j = 0; j < 8; ++j) {
|
|
if (line & (1 << j)) {
|
|
if (size == 1) GC9A01_SetPixel(x + j, y + i, color);
|
|
else GC9A01_FillRect(x + j*size, y + i*size, size, size, color);
|
|
} else if (bg_color != color) {
|
|
if (size == 1) GC9A01_SetPixel(x + j, y + i, bg_color);
|
|
else GC9A01_FillRect(x + j*size, y + i*size, size, size, bg_color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GC9A01_DrawString(uint16_t x, uint16_t y, const char *str, uint16_t color, uint16_t bg_color, uint8_t size) {
|
|
while (*str) {
|
|
GC9A01_DrawChar(x, y, *str, color, bg_color, size);
|
|
x += 8 * size;
|
|
++str;
|
|
}
|
|
}
|
|
|
|
uint16_t GC9A01_RGB565(uint8_t r, uint8_t g, uint8_t b) {
|
|
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
|
|
}
|
|
|
|
bool GC9A01_IsInCircle(uint16_t x, uint16_t y) {
|
|
int32_t dx = (int32_t)x - GC9A01_RADIUS;
|
|
int32_t dy = (int32_t)y - GC9A01_RADIUS;
|
|
return (dx*dx + dy*dy) <= (GC9A01_RADIUS * GC9A01_RADIUS);
|
|
}
|
|
|
|
// Fonctions moto (réutilise celles que tu as déjà)
|
|
void GC9A01_DrawGauge(uint16_t center_x, uint16_t center_y, uint8_t radius,
|
|
float value, float min_val, float max_val,
|
|
uint16_t color, const char* label)
|
|
{
|
|
GC9A01_DrawCircle(center_x, center_y, radius, GC9A01_WHITE);
|
|
float normalized = (value - min_val) / (max_val - min_val);
|
|
if (normalized < 0) normalized = 0;
|
|
if (normalized > 1) normalized = 1;
|
|
float angle = -90.0f + normalized * 180.0f;
|
|
float rad = angle * (float)M_PI / 180.0f;
|
|
int16_t nx = center_x + (radius - 5) * cosf(rad);
|
|
int16_t ny = center_y + (radius - 5) * sinf(rad);
|
|
GC9A01_DrawLine(center_x, center_y, nx, ny, color);
|
|
GC9A01_FillCircle(center_x, center_y, 3, color);
|
|
char buf[16];
|
|
snprintf(buf, sizeof(buf), "%.1f", value);
|
|
GC9A01_DrawString(center_x - 20, center_y + radius + 8, buf, color, GC9A01_BLACK, 1);
|
|
if (label) GC9A01_DrawString(center_x - (int)strlen(label)*4, center_y - radius - 18, label, GC9A01_WHITE, GC9A01_BLACK, 1);
|
|
}
|
|
|
|
void GC9A01_DrawAngleIndicator(float roll, float pitch) {
|
|
uint16_t cx = GC9A01_WIDTH / 2;
|
|
uint16_t cy = GC9A01_HEIGHT / 2;
|
|
uint16_t radius = 50;
|
|
|
|
GC9A01_FillCircle(cx, cy, radius, GC9A01_BLACK);
|
|
GC9A01_DrawCircle(cx, cy, radius, GC9A01_WHITE);
|
|
|
|
int16_t hor = (int16_t)(pitch * 2.0f); // décalage vertical (pitch)
|
|
GC9A01_DrawLine(cx - 40, cy + hor, cx + 40, cy + hor, GC9A01_CYAN);
|
|
|
|
float rad = roll * ((float)M_PI / 180.0f); // conversion roll en radians
|
|
int16_t nx = cx + (int16_t)(30 * cosf(rad));
|
|
int16_t ny = cy + (int16_t)(30 * sinf(rad));
|
|
|
|
GC9A01_DrawLine(cx, cy, nx, ny, GC9A01_YELLOW);
|
|
GC9A01_FillCircle(nx, ny, 3, GC9A01_YELLOW);
|
|
}
|
|
|
|
|
|
void GC9A01_DrawStateIndicator(const char* state, uint16_t color) {
|
|
GC9A01_FillRect(0, GC9A01_HEIGHT - 30, GC9A01_WIDTH, 30, GC9A01_BLACK);
|
|
uint16_t tw = strlen(state) * 8;
|
|
uint16_t sx = (GC9A01_WIDTH > tw) ? (GC9A01_WIDTH - tw) / 2 : 0;
|
|
GC9A01_DrawString(sx, GC9A01_HEIGHT - 20, state, color, GC9A01_BLACK, 1);
|
|
}
|