Initial commit - sync AliceRC to Gitea

This commit is contained in:
2026-08-03 15:54:25 +08:00
commit 493b7d81d7
1510 changed files with 185319 additions and 0 deletions
+287
View File
@@ -0,0 +1,287 @@
/**
* @file oled.c
* @brief SSD1315 OLED 128x64 I2C driver implementation
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2c.h"
#include "esp_log.h"
#include "oled.h"
static const char *TAG = "oled";
/* I2C configuration */
#define I2C_PORT I2C_NUM_0
#define OLED_ADDR 0x3C
#define I2C_CLOCK_HZ 400000
/* Display buffer (128x64 / 8 = 1024 bytes) */
static uint8_t display_buffer[OLED_WIDTH * OLED_PAGES];
/* Simple 8x16 font (ASCII 0x20-0x7F) */
/* Using a basic 5x7 font doubled to 8x16 by row replication */
static const uint8_t font_8x8[][8] = {
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, /* space */
{0x00,0x00,0x5F,0x00,0x00,0x00,0x00,0x00}, /* ! */
{0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x00}, /* " */
{0x14,0x7F,0x14,0x7F,0x14,0x00,0x00,0x00}, /* # */
{0x24,0x2A,0x7F,0x2A,0x12,0x00,0x00,0x00}, /* $ */
{0x23,0x13,0x08,0x64,0x62,0x00,0x00,0x00}, /* % */
{0x36,0x49,0x55,0x22,0x50,0x00,0x00,0x00}, /* & */
{0x00,0x05,0x03,0x00,0x00,0x00,0x00,0x00}, /* ' */
{0x00,0x1C,0x22,0x41,0x00,0x00,0x00,0x00}, /* ( */
{0x00,0x41,0x22,0x1C,0x00,0x00,0x00,0x00}, /* ) */
{0x08,0x2A,0x1C,0x2A,0x08,0x00,0x00,0x00}, /* * */
{0x08,0x08,0x3E,0x08,0x08,0x00,0x00,0x00}, /* + */
{0x00,0x50,0x30,0x00,0x00,0x00,0x00,0x00}, /* , */
{0x08,0x08,0x08,0x08,0x08,0x00,0x00,0x00}, /* - */
{0x00,0x60,0x60,0x00,0x00,0x00,0x00,0x00}, /* . */
{0x20,0x10,0x08,0x04,0x02,0x00,0x00,0x00}, /* / */
{0x3E,0x51,0x49,0x45,0x3E,0x00,0x00,0x00}, /* 0 */
{0x00,0x42,0x7F,0x40,0x00,0x00,0x00,0x00}, /* 1 */
{0x42,0x61,0x51,0x49,0x46,0x00,0x00,0x00}, /* 2 */
{0x21,0x41,0x45,0x4B,0x31,0x00,0x00,0x00}, /* 3 */
{0x18,0x14,0x12,0x7F,0x10,0x00,0x00,0x00}, /* 4 */
{0x27,0x45,0x45,0x45,0x39,0x00,0x00,0x00}, /* 5 */
{0x3C,0x4A,0x49,0x49,0x30,0x00,0x00,0x00}, /* 6 */
{0x01,0x71,0x09,0x05,0x03,0x00,0x00,0x00}, /* 7 */
{0x36,0x49,0x49,0x49,0x36,0x00,0x00,0x00}, /* 8 */
{0x06,0x49,0x49,0x29,0x1E,0x00,0x00,0x00}, /* 9 */
{0x00,0x36,0x36,0x00,0x00,0x00,0x00,0x00}, /* : */
{0x00,0x56,0x36,0x00,0x00,0x00,0x00,0x00}, /* ; */
{0x00,0x08,0x14,0x22,0x41,0x00,0x00,0x00}, /* < */
{0x14,0x14,0x14,0x14,0x14,0x00,0x00,0x00}, /* = */
{0x41,0x22,0x14,0x08,0x00,0x00,0x00,0x00}, /* > */
{0x02,0x01,0x51,0x09,0x06,0x00,0x00,0x00}, /* ? */
{0x32,0x49,0x79,0x41,0x3E,0x00,0x00,0x00}, /* @ */
{0x7E,0x11,0x11,0x11,0x7E,0x00,0x00,0x00}, /* A */
{0x7F,0x49,0x49,0x49,0x36,0x00,0x00,0x00}, /* B */
{0x3E,0x41,0x41,0x41,0x22,0x00,0x00,0x00}, /* C */
{0x7F,0x41,0x41,0x22,0x1C,0x00,0x00,0x00}, /* D */
{0x7F,0x49,0x49,0x49,0x41,0x00,0x00,0x00}, /* E */
{0x7F,0x09,0x09,0x01,0x01,0x00,0x00,0x00}, /* F */
{0x3E,0x41,0x41,0x51,0x32,0x00,0x00,0x00}, /* G */
{0x7F,0x08,0x08,0x08,0x7F,0x00,0x00,0x00}, /* H */
{0x00,0x41,0x7F,0x41,0x00,0x00,0x00,0x00}, /* I */
{0x20,0x40,0x41,0x3F,0x01,0x00,0x00,0x00}, /* J */
{0x7F,0x08,0x14,0x22,0x41,0x00,0x00,0x00}, /* K */
{0x7F,0x40,0x40,0x40,0x40,0x00,0x00,0x00}, /* L */
{0x7F,0x02,0x04,0x02,0x7F,0x00,0x00,0x00}, /* M */
{0x7F,0x04,0x08,0x10,0x7F,0x00,0x00,0x00}, /* N */
{0x3E,0x41,0x41,0x41,0x3E,0x00,0x00,0x00}, /* O */
{0x7F,0x09,0x09,0x09,0x06,0x00,0x00,0x00}, /* P */
{0x3E,0x41,0x51,0x21,0x5E,0x00,0x00,0x00}, /* Q */
{0x7F,0x09,0x19,0x29,0x46,0x00,0x00,0x00}, /* R */
{0x46,0x49,0x49,0x49,0x31,0x00,0x00,0x00}, /* S */
{0x01,0x01,0x7F,0x01,0x01,0x00,0x00,0x00}, /* T */
{0x3F,0x40,0x40,0x40,0x3F,0x00,0x00,0x00}, /* U */
{0x1F,0x20,0x40,0x20,0x1F,0x00,0x00,0x00}, /* V */
{0x7F,0x20,0x18,0x20,0x7F,0x00,0x00,0x00}, /* W */
{0x63,0x14,0x08,0x14,0x63,0x00,0x00,0x00}, /* X */
{0x03,0x04,0x78,0x04,0x03,0x00,0x00,0x00}, /* Y */
{0x61,0x51,0x49,0x45,0x43,0x00,0x00,0x00}, /* Z */
};
/* I2C write command */
static esp_err_t oled_write_cmd(uint8_t cmd)
{
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, (OLED_ADDR << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(i2c_cmd, 0x00, true); /* Co=0, D/C#=0 (command) */
i2c_master_write_byte(i2c_cmd, cmd, true);
i2c_master_stop(i2c_cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_PORT, i2c_cmd, pdMS_TO_TICKS(100));
i2c_cmd_link_delete(i2c_cmd);
return ret;
}
/* I2C write data */
static esp_err_t oled_write_data(const uint8_t *data, size_t len)
{
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, (OLED_ADDR << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(i2c_cmd, 0x40, true); /* Co=0, D/C#=1 (data) */
i2c_master_write(i2c_cmd, data, len, true);
i2c_master_stop(i2c_cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_PORT, i2c_cmd, pdMS_TO_TICKS(100));
i2c_cmd_link_delete(i2c_cmd);
return ret;
}
esp_err_t oled_init(int sda, int scl)
{
/* Initialize I2C */
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = sda,
.scl_io_num = scl,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_CLOCK_HZ,
};
ESP_ERROR_CHECK(i2c_param_config(I2C_PORT, &conf));
ESP_ERROR_CHECK(i2c_driver_install(I2C_PORT, conf.mode, 0, 0, 0));
vTaskDelay(pdMS_TO_TICKS(100));
/* SSD1315 initialization sequence */
oled_write_cmd(0xAE); /* Display off */
oled_write_cmd(0xD5); /* Set display clock divide ratio */
oled_write_cmd(0x80);
oled_write_cmd(0xA8); /* Set multiplex ratio */
oled_write_cmd(0x3F); /* 64 lines */
oled_write_cmd(0xD3); /* Set display offset */
oled_write_cmd(0x00);
oled_write_cmd(0x40); /* Set display start line */
oled_write_cmd(0x8D); /* Charge pump setting */
oled_write_cmd(0x14); /* Enable */
oled_write_cmd(0x20); /* Memory addressing mode */
oled_write_cmd(0x00); /* Horizontal */
oled_write_cmd(0xA1); /* Segment remap (column 127 mapped to SEG0) */
oled_write_cmd(0xC8); /* COM output scan direction */
oled_write_cmd(0xDA); /* COM pins hardware configuration */
oled_write_cmd(0x12); /* Alternative pin configuration */
oled_write_cmd(0x81); /* Set contrast */
oled_write_cmd(0x7F);
oled_write_cmd(0xD9); /* Set pre-charge period */
oled_write_cmd(0xF1);
oled_write_cmd(0xDB); /* Set VCOMH deselect level */
oled_write_cmd(0x40);
oled_write_cmd(0xA4); /* Display on (resume) */
oled_write_cmd(0xA6); /* Normal display (not inverted) */
oled_write_cmd(0xAF); /* Display on */
/* Clear display buffer */
memset(display_buffer, 0, sizeof(display_buffer));
oled_update();
ESP_LOGI(TAG, "OLED initialized");
return ESP_OK;
}
void oled_clear(void)
{
memset(display_buffer, 0, sizeof(display_buffer));
}
void oled_clear_area(uint8_t x, uint8_t y, uint8_t w, uint8_t h)
{
for (uint8_t i = y; i < y + h; i++) {
for (uint8_t j = x; j < x + w; j++) {
if (j < OLED_WIDTH && i < OLED_HEIGHT) {
uint16_t idx = (i / 8) * OLED_WIDTH + j;
if (idx < sizeof(display_buffer)) {
display_buffer[idx] &= ~(1 << (i % 8));
}
}
}
}
}
void oled_draw_pixel(uint8_t x, uint8_t y, uint8_t color)
{
if (x >= OLED_WIDTH || y >= OLED_HEIGHT) return;
uint16_t idx = (y / 8) * OLED_WIDTH + x;
if (idx >= sizeof(display_buffer)) return;
if (color) {
display_buffer[idx] |= (1 << (y % 8));
} else {
display_buffer[idx] &= ~(1 << (y % 8));
}
}
void oled_draw_char(uint8_t x, uint8_t y, char c, uint8_t color)
{
if (c < 0x20 || c > 0x5A) c = 0x20; /* Space for non-printable */
c -= 0x20;
for (uint8_t i = 0; i < 8; i++) {
uint8_t line = font_8x8[(uint8_t)c][i];
for (uint8_t j = 0; j < 8; j++) {
if (line & (0x80 >> j)) {
oled_draw_pixel(x + j, y + i, color);
} else {
oled_draw_pixel(x + j, y + i, !color);
}
}
}
}
void oled_draw_string(uint8_t x, uint8_t y, const char *str, uint8_t color)
{
while (*str) {
oled_draw_char(x, y, *str, color);
x += 8;
if (x + 8 > OLED_WIDTH) {
x = 0;
y += 8;
}
str++;
}
}
void oled_draw_top_string(const char *str, uint8_t color)
{
oled_draw_string(0, 0, str, color);
}
void oled_draw_hline(uint8_t x, uint8_t y, uint8_t w, uint8_t color)
{
for (uint8_t i = 0; i < w; i++) {
oled_draw_pixel(x + i, y, color);
}
}
void oled_draw_vline(uint8_t x, uint8_t y, uint8_t h, uint8_t color)
{
for (uint8_t i = 0; i < h; i++) {
oled_draw_pixel(x, y + i, color);
}
}
void oled_fill_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color)
{
for (uint8_t i = y; i < y + h; i++) {
for (uint8_t j = x; j < x + w; j++) {
oled_draw_pixel(j, i, color);
}
}
}
void oled_update(void)
{
/* Set column address range */
oled_write_cmd(0x21);
oled_write_cmd(0);
oled_write_cmd(127);
/* Set page address range */
oled_write_cmd(0x22);
oled_write_cmd(0);
oled_write_cmd(7);
/* Write buffer */
oled_write_data(display_buffer, sizeof(display_buffer));
}
void oled_display_on(void)
{
oled_write_cmd(0xAF);
}
void oled_display_off(void)
{
oled_write_cmd(0xAE);
}
void oled_set_contrast(uint8_t value)
{
oled_write_cmd(0x81);
oled_write_cmd(value);
}