sl-firmware fa75c442a7 feat: remove all STM32/Mamba/BlackPill references — ESP32-S3 only
Archive STM32 firmware to legacy/stm32/:
- src/, include/, lib/USB_CDC/, platformio.ini, test stubs, flash_firmware.py
- test/test_battery_adc.c, test_hw_button.c, test_pid_schedule.c, test_vesc_can.c, test_can_watchdog.c
- USB_CDC_BUG.md

Rename: stm32_protocol → esp32_protocol, mamba_protocol → balance_protocol,
  stm32_cmd_node → esp32_cmd_node, stm32_cmd_params → esp32_cmd_params,
  stm32_cmd.launch.py → esp32_cmd.launch.py,
  test_stm32_protocol → test_esp32_protocol, test_stm32_cmd_node → test_esp32_cmd_node

Content cleanup across all files:
- Mamba F722S → ESP32-S3 BALANCE
- BlackPill → ESP32-S3 IO
- STM32F722/F7xx → ESP32-S3
- stm32Mode/Version/Port → esp32Mode/Version/Port
- STM32 State/Mode labels → ESP32 State/Mode
- Jetson Nano → Jetson Orin Nano Super
- /dev/stm32 → /dev/esp32
- stm32_bridge → esp32_bridge
- STM32 HAL → ESP-IDF

docs/SALTYLAB.md:
- Update "Drone FC Details" to describe ESP32-S3 BALANCE board (Waveshare ESP32-S3 Touch LCD 1.28)
- Replace verbose "Self-Balancing Control" STM32 section with brief note pointing to SAUL-TEE-SYSTEM-REFERENCE.md

TEAM.md: Update Embedded Firmware Engineer role to ESP32-S3 / ESP-IDF

No new functionality — cleanup only.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 09:00:38 -04:00

192 lines
5.3 KiB
C

/*
* face_lcd.c — STM32 LCD Display Driver for Face Animations
*
* Implements low-level LCD framebuffer management and display control.
* Supports 1-bit monochrome displays (SSD1306, etc.) via SPI.
*
* HARDWARE:
* - SPI2 (PB13=SCK, PB14=MISO, PB15=MOSI) — already configured for OSD
* - CS (GPIO) for LCD chip select
* - DC (GPIO) for data/command mode select
* - RES (GPIO) optional reset
*
* NOTE: SPI2 is currently used by OSD (MAX7456). For face LCD, we would
* typically use a separate SPI or I2C. This implementation assumes
* a dedicated I2C or separate SPI interface. Configure LCD_INTERFACE
* in face_lcd.h to match your hardware.
*/
#include "face_lcd.h"
#include <string.h>
/* === State Variables === */
static uint8_t lcd_framebuffer[LCD_FBSIZE];
static volatile uint32_t frame_counter = 0;
static volatile bool flush_requested = false;
static volatile bool transfer_busy = false;
/* === Private Functions === */
/**
* Initialize hardware (SPI/I2C) and LCD controller.
* Sends initialization sequence to put display in active mode.
*/
static void lcd_hardware_init(void) {
/* TODO: Implement hardware-specific initialization
* - Configure SPI/I2C pins and clock
* - Send controller init sequence (power on, set contrast, etc.)
* - Clear display
*
* For SSD1306 (common monochrome):
* - Send 0xAE (display off)
* - Set contrast 0x81, 0x7F
* - Set clock div ratio, precharge, comdesat
* - Set address mode, column/page range
* - Send 0xAF (display on)
*/
}
/**
* Push framebuffer to display via SPI/I2C DMA transfer.
* Handles paging for monochrome displays (8 pixels per byte, horizontal pages).
*/
static void lcd_transfer_fb(void) {
transfer_busy = true;
/* TODO: Implement DMA/blocking transfer
* For SSD1306 (8-pixel pages):
* For each page (8 rows):
* - Send page address command
* - Send column address command
* - DMA transfer 128 bytes (1 row of page data)
*
* Can use SPI DMA for async transfer or blocking transfer.
* Set transfer_busy=false when complete (in ISR or blocking).
*/
transfer_busy = false;
}
/* === Public API Implementation === */
void face_lcd_init(void) {
memset(lcd_framebuffer, 0, LCD_FBSIZE);
frame_counter = 0;
flush_requested = false;
transfer_busy = false;
lcd_hardware_init();
}
void face_lcd_clear(void) {
memset(lcd_framebuffer, 0, LCD_FBSIZE);
}
void face_lcd_pixel(uint16_t x, uint16_t y, lcd_color_t color) {
/* Bounds check */
if (x >= LCD_WIDTH || y >= LCD_HEIGHT)
return;
#if LCD_BPP == 1
/* Monochrome: pack 8 pixels per byte, LSB = leftmost pixel */
uint16_t byte_idx = (y / 8) * LCD_WIDTH + x;
uint8_t bit_pos = y % 8;
if (color)
lcd_framebuffer[byte_idx] |= (1 << bit_pos);
else
lcd_framebuffer[byte_idx] &= ~(1 << bit_pos);
#else
/* RGB565: 2 bytes per pixel */
uint16_t pixel_idx = y * LCD_WIDTH + x;
((uint16_t *)lcd_framebuffer)[pixel_idx] = color;
#endif
}
void face_lcd_line(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1,
lcd_color_t color) {
/* Bresenham line algorithm */
int16_t dx = (x1 > x0) ? (x1 - x0) : (x0 - x1);
int16_t dy = (y1 > y0) ? (y1 - y0) : (y0 - y1);
int16_t sx = (x0 < x1) ? 1 : -1;
int16_t sy = (y0 < y1) ? 1 : -1;
int16_t err = (dx > dy) ? (dx / 2) : -(dy / 2);
int16_t x = x0, y = y0;
while (1) {
face_lcd_pixel(x, y, color);
if (x == x1 && y == y1)
break;
int16_t e2 = err;
if (e2 > -dx) {
err -= dy;
x += sx;
}
if (e2 < dy) {
err += dx;
y += sy;
}
}
}
void face_lcd_circle(uint16_t cx, uint16_t cy, uint16_t r, lcd_color_t color) {
/* Midpoint circle algorithm */
int16_t x = r, y = 0;
int16_t err = 0;
while (x >= y) {
face_lcd_pixel(cx + x, cy + y, color);
face_lcd_pixel(cx + y, cy + x, color);
face_lcd_pixel(cx - y, cy + x, color);
face_lcd_pixel(cx - x, cy + y, color);
face_lcd_pixel(cx - x, cy - y, color);
face_lcd_pixel(cx - y, cy - x, color);
face_lcd_pixel(cx + y, cy - x, color);
face_lcd_pixel(cx + x, cy - y, color);
if (err <= 0) {
y++;
err += 2 * y + 1;
} else {
x--;
err -= 2 * x + 1;
}
}
}
void face_lcd_fill_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h,
lcd_color_t color) {
for (uint16_t row = y; row < y + h && row < LCD_HEIGHT; row++) {
for (uint16_t col = x; col < x + w && col < LCD_WIDTH; col++) {
face_lcd_pixel(col, row, color);
}
}
}
void face_lcd_flush(void) {
flush_requested = true;
}
bool face_lcd_is_busy(void) {
return transfer_busy;
}
void face_lcd_tick(void) {
frame_counter++;
/* Request flush every N frames to achieve LCD_REFRESH_HZ */
uint32_t frames_per_flush = (1000 / LCD_REFRESH_HZ) / 33; /* ~30ms per frame */
if (frame_counter % frames_per_flush == 0) {
flush_requested = true;
}
/* Perform transfer if requested and not busy */
if (flush_requested && !transfer_busy) {
flush_requested = false;
lcd_transfer_fb();
}
}
uint8_t *face_lcd_get_fb(void) {
return lcd_framebuffer;
}