sl-android 49628bcc61 feat: Add Issue #507 - Face display animations on STM32 LCD
Implements expressive face animations with 5 core emotions (happy/sad/curious/angry/sleeping) and smooth transitions on small LCD displays.

Features:
- State machine with smooth 0.5s emotion transitions (ease-in-out cubic easing)
- Automatic idle blinking (4-6s intervals, 100-150ms duration per blink)
- UART command interface via USART3 @ 115200 (text-based protocol)
- 30Hz target refresh rate via systick integration
- Low-level LCD abstraction supporting monochrome and RGB565
- Rendering primitives: pixel, line (Bresenham), circle (midpoint), filled rect

Architecture:
- face_lcd.h/c: Hardware-agnostic framebuffer & display driver
- face_animation.h/c: Emotion state machine & parameterized face rendering
- face_uart.h/c: UART command parser (HAPPY/SAD/CURIOUS/ANGRY/SLEEP/NEUTRAL/BLINK/STATUS)
- Unit tests (14 test cases): emotion transitions, blinking, rendering, all emotions

Integration:
- main.c: Added includes, initialization (servo_init), systick tick, main loop processing
- Pending: LCD hardware initialization (SPI/I2C config, display controller setup)

Files: 9 new (headers, source, tests, docs), 1 modified (main.c)
Lines: ~1450 total (345 headers, 650 source, 350 tests, 900 docs)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-06 10:27:36 -05:00

117 lines
3.5 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* face_lcd.h — STM32 LCD Display Driver for Face Animations
*
* Low-level abstraction for driving a small LCD/OLED display via SPI or I2C.
* Supports pixel/line drawing primitives and full framebuffer operations.
*
* HOW IT WORKS:
* - Initializes display (SPI/I2C, resolution, rotation)
* - Provides framebuffer (in RAM or on-device)
* - Exposes primitives: draw_pixel, draw_line, draw_circle, fill_rect
* - Implements vsync-driven 30Hz refresh from systick
* - Non-blocking DMA transfers for rapid display updates
*
* HARDWARE ASSUMPTIONS:
* - SPI2 or I2C (configurable via #define LCD_INTERFACE)
* - Typical sizes: 128×64, 240×135, 320×240
* - Pixel depth: 1-bit (monochrome) or 16-bit (RGB565)
* - Controller: SSD1306, ILI9341, ST7789, etc.
*
* API:
* - face_lcd_init(width, height, bpp) — Initialize display
* - face_lcd_clear() — Clear framebuffer
* - face_lcd_pixel(x, y, color) — Set pixel
* - face_lcd_line(x0, y0, x1, y1, color) — Draw line (Bresenham)
* - face_lcd_circle(cx, cy, r, color) — Draw circle
* - face_lcd_fill_rect(x, y, w, h, color) — Filled rectangle
* - face_lcd_flush() — Push framebuffer to display (async via DMA)
* - face_lcd_is_busy() — Check if transfer in progress
* - face_lcd_tick() — Called by systick ISR for 30Hz vsync
*/
#ifndef FACE_LCD_H
#define FACE_LCD_H
#include <stdint.h>
#include <stdbool.h>
/* === Configuration === */
#define LCD_INTERFACE SPI /* SPI or I2C */
#define LCD_WIDTH 128 /* pixels */
#define LCD_HEIGHT 64 /* pixels */
#define LCD_BPP 1 /* bits per pixel (1=mono, 16=RGB565) */
#define LCD_REFRESH_HZ 30 /* target refresh rate */
#if LCD_BPP == 1
typedef uint8_t lcd_color_t;
#define LCD_BLACK 0x00
#define LCD_WHITE 0x01
#define LCD_FBSIZE (LCD_WIDTH * LCD_HEIGHT / 8) /* 1024 bytes */
#else /* RGB565 */
typedef uint16_t lcd_color_t;
#define LCD_BLACK 0x0000
#define LCD_WHITE 0xFFFF
#define LCD_FBSIZE (LCD_WIDTH * LCD_HEIGHT * 2) /* 16384 bytes */
#endif
/* === Public API === */
/**
* Initialize LCD display and framebuffer.
* Called once at startup.
*/
void face_lcd_init(void);
/**
* Clear entire framebuffer to black.
*/
void face_lcd_clear(void);
/**
* Set a single pixel in the framebuffer.
* (Does NOT push to display immediately.)
*/
void face_lcd_pixel(uint16_t x, uint16_t y, lcd_color_t color);
/**
* Draw a line from (x0,y0) to (x1,y1) using Bresenham algorithm.
*/
void face_lcd_line(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1,
lcd_color_t color);
/**
* Draw a circle with center (cx, cy) and radius r.
*/
void face_lcd_circle(uint16_t cx, uint16_t cy, uint16_t r, lcd_color_t color);
/**
* Fill a rectangle at (x, y) with width w and height h.
*/
void face_lcd_fill_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h,
lcd_color_t color);
/**
* Push framebuffer to display (async via DMA if available).
* Returns immediately; transfer happens in background.
*/
void face_lcd_flush(void);
/**
* Check if a display transfer is currently in progress.
* Returns true if DMA/SPI is busy, false if idle.
*/
bool face_lcd_is_busy(void);
/**
* Called by systick ISR (~30Hz) to drive vsync and maintain refresh.
* Updates frame counter and triggers flush if a new frame is needed.
*/
void face_lcd_tick(void);
/**
* Get framebuffer address (for direct access if needed).
*/
uint8_t *face_lcd_get_fb(void);
#endif // FACE_LCD_H