stm32-moto/Core/Src/gc9a01.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);
}