// gc9a01.c (version nettoyée + optimisée) #include "gc9a01.h" #include #include #include #include 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); }