Files
ZhaTianRC/Core/Src/oled.c

258 lines
8.0 KiB
C

/**
* OLED SSD1306 / SSD1315 128x64 I2C 驱动实现
*/
#include "oled.h"
#include "words.h"
#include "image.h"
#include <string.h>
/* 显存缓冲区 */
uint8_t OLED_Buffer[OLED_WIDTH * OLED_HEIGHT / 8];
/* 内部命令发送 */
static void OLED_WriteCmd(uint8_t cmd) {
uint8_t buf[2] = {0x00, cmd}; /* Co=0, D/C#=0 */
HAL_I2C_Master_Transmit(&hi2c2, OLED_ADDR << 1, buf, 2, 10);
}
static void OLED_WriteData(uint8_t dat) {
uint8_t buf[2] = {0x40, dat}; /* Co=0, D/C#=1 */
HAL_I2C_Master_Transmit(&hi2c2, OLED_ADDR << 1, buf, 2, 10);
}
void OLED_Init(void) {
/* 通用初始化序列 (SSD1306 & SSD1315 共用) */
OLED_WriteCmd(0xAE); /* display off */
OLED_WriteCmd(0xD5); /* set display clock divide */
OLED_WriteCmd(0x80);
OLED_WriteCmd(0xA8); /* set multiplex */
OLED_WriteCmd(0x3F); /* 64 */
OLED_WriteCmd(0xD3); /* set display offset */
OLED_WriteCmd(0x00);
OLED_WriteCmd(0x40); /* set start line */
#if OLED_CHIP == OLED_CHIP_SSD1306
/* SSD1306: 外部电荷泵 */
OLED_WriteCmd(0x8D); /* charge pump setting */
OLED_WriteCmd(0x14); /* enable charge pump */
#elif OLED_CHIP == OLED_CHIP_SSD1315
/* SSD1315: 内置 DC-DC 升压 */
OLED_WriteCmd(0xAD); /* DC-DC control */
OLED_WriteCmd(0x8A); /* enable DC-DC (0x8A=on with 7.5V VPP) */
/* 也可用 0x8B 关闭 DC-DC (外部供电模式) */
#endif
OLED_WriteCmd(0x20); /* memory mode */
OLED_WriteCmd(0x00); /* horizontal */
OLED_WriteCmd(0xA1); /* segment remap (左右镜像) */
OLED_WriteCmd(0xC8); /* COM scan direction (上下镜像) */
OLED_WriteCmd(0xDA); /* set COM pins */
OLED_WriteCmd(0x12);
OLED_WriteCmd(0x81); /* set contrast */
OLED_WriteCmd(0xCF);
OLED_WriteCmd(0xD9); /* set pre-charge */
OLED_WriteCmd(0xF1);
OLED_WriteCmd(0xDB); /* set VCOMH */
OLED_WriteCmd(0x40);
OLED_WriteCmd(0xA4); /* display on resume */
OLED_WriteCmd(0xA6); /* normal display (not inverted) */
OLED_WriteCmd(0x2E); /* deactivate scroll */
OLED_WriteCmd(0xAF); /* display on */
OLED_Clear();
OLED_Display();
}
void OLED_Clear(void) {
memset(OLED_Buffer, 0x00, sizeof(OLED_Buffer));
}
void OLED_Display(void) {
for (uint8_t page = 0; page < OLED_PAGES; page++) {
OLED_WriteCmd(0xB0 + page); /* set page address */
OLED_WriteCmd(0x00); /* set lower column */
OLED_WriteCmd(0x10); /* set higher column */
for (uint8_t col = 0; col < OLED_WIDTH; col++) {
OLED_WriteData(OLED_Buffer[page * OLED_WIDTH + col]);
}
}
}
void OLED_SetCursor(uint8_t x, uint8_t y) {
/* 仅用于记录,实际绘制由各函数自行计算位置 */
(void)x; (void)y;
}
void OLED_DrawPixel(uint8_t x, uint8_t y, uint8_t color) {
if (x >= OLED_WIDTH || y >= OLED_HEIGHT) return;
uint8_t page = y / 8;
uint8_t bit = y % 8;
if (color)
OLED_Buffer[page * OLED_WIDTH + x] |= (1 << bit);
else
OLED_Buffer[page * OLED_WIDTH + x] &= ~(1 << bit);
}
void OLED_DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) {
int16_t dx = (int16_t)x2 - x1;
int16_t dy = (int16_t)y2 - y1;
int16_t sx = dx > 0 ? 1 : -1;
int16_t sy = dy > 0 ? 1 : -1;
dx = dx > 0 ? dx : -dx;
dy = dy > 0 ? dy : -dy;
int16_t err = dx - dy;
while (1) {
OLED_DrawPixel(x1, y1, 1);
if (x1 == x2 && y1 == y2) break;
int16_t e2 = err * 2;
if (e2 > -dy) { err -= dy; x1 += sx; }
if (e2 < dx) { err += dx; y1 += sy; }
}
}
void OLED_DrawRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h) {
OLED_DrawLine(x, y, x + w - 1, y);
OLED_DrawLine(x, y + h - 1, x + w - 1, y + h - 1);
OLED_DrawLine(x, y, x, y + h - 1);
OLED_DrawLine(x + w - 1, y, x + w - 1, y + h - 1);
}
void OLED_FillRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color) {
for (uint8_t i = 0; i < w; i++)
for (uint8_t j = 0; j < h; j++)
OLED_DrawPixel(x + i, y + j, color);
}
void OLED_DrawCircle(uint8_t cx, uint8_t cy, uint8_t r) {
int16_t x = 0, y = r;
int16_t d = 3 - 2 * r;
while (x <= y) {
OLED_DrawPixel(cx + x, cy + y, 1);
OLED_DrawPixel(cx - x, cy + y, 1);
OLED_DrawPixel(cx + x, cy - y, 1);
OLED_DrawPixel(cx - x, cy - y, 1);
OLED_DrawPixel(cx + y, cy + x, 1);
OLED_DrawPixel(cx - y, cy + x, 1);
OLED_DrawPixel(cx + y, cy - x, 1);
OLED_DrawPixel(cx - y, cy - x, 1);
if (d < 0) d += 4 * x + 6;
else { d += 4 * (x - y) + 10; y--; }
x++;
}
}
/* ---- 文字绘制 ---- */
/* 6x8 ASCII 字体 (来自 words.h 的 font[][6]) */
void OLED_ShowChar(uint8_t x, uint8_t y, char ch, uint8_t size) {
if (size == 6) {
/* 使用小字体 6x8 */
uint8_t idx = (uint8_t)ch - 32;
if (idx > 94) return;
for (uint8_t i = 0; i < 6; i++) {
uint8_t line = font[idx][i];
for (uint8_t j = 0; j < 8; j++) {
if (line & (1 << j))
OLED_DrawPixel(x + i, y + j, 1);
}
}
} else {
/* 使用大字体 8x16 (font_big) */
uint8_t idx = (uint8_t)ch - 32;
if (idx > 94) return;
for (uint8_t i = 0; i < 8; i++) {
uint8_t line_l = font_big[idx][i * 2];
uint8_t line_h = font_big[idx][i * 2 + 1];
for (uint8_t j = 0; j < 8; j++) {
if (line_l & (1 << j))
OLED_DrawPixel(x + i, y + j, 1);
if (line_h & (1 << j))
OLED_DrawPixel(x + i, y + 8 + j, 1);
}
}
}
}
void OLED_ShowString(uint8_t x, uint8_t y, const char *str, uint8_t size) {
while (*str) {
OLED_ShowChar(x, y, *str, size);
x += size;
if (x + size > OLED_WIDTH) { x = 0; y += (size == 6 ? 1 : 2); }
str++;
}
}
void OLED_ShowNum(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size) {
char buf[12];
uint8_t i;
for (i = 0; i < len; i++) {
buf[len - 1 - i] = '0' + (num % 10);
num /= 10;
}
buf[len] = '\0';
OLED_ShowString(x, y, buf, size);
}
/* ---- 汉字绘制 ---- */
void OLED_ShowCN(uint8_t x, uint8_t y, uint8_t index) {
/* hzk 数组索引: 0=空, 1=→, 2=空, 3=v, 4=←, 5=→, 6=工... */
/* 每个汉字 12x12 点阵,占 24 bytes */
for (uint8_t i = 0; i < 12; i++) {
uint8_t line_l = hzk[index][i * 2];
uint8_t line_h = hzk[index][i * 2 + 1];
for (uint8_t j = 0; j < 8; j++) {
if (line_l & (0x80 >> j))
OLED_DrawPixel(x + i, y + j, 1);
if (line_h & (0x80 >> j))
OLED_DrawPixel(x + i, y + 8 + j, 1);
}
}
}
/* ---- 图片绘制 ---- */
void OLED_DrawBitmap(uint8_t x, uint8_t y, const uint8_t *bmp, uint8_t w, uint8_t h) {
uint8_t bytes_per_col = (h + 7) / 8;
for (uint8_t i = 0; i < w; i++) {
for (uint8_t j = 0; j < h; j++) {
uint8_t byte_idx = i * bytes_per_col + j / 8;
uint8_t bit_idx = j % 8;
if (bmp[byte_idx] & (1 << bit_idx))
OLED_DrawPixel(x + i, y + j, 1);
}
}
}
/* ---- 反色/滚动 ---- */
void OLED_InvertDisplay(uint8_t invert) {
OLED_WriteCmd(invert ? 0xA7 : 0xA6);
}
void OLED_StartScrollRight(uint8_t start_page, uint8_t end_page) {
OLED_WriteCmd(0x2E); /* stop scroll first */
OLED_WriteCmd(0x26); /* horizontal scroll right */
OLED_WriteCmd(0x00); /* dummy */
OLED_WriteCmd(start_page);
OLED_WriteCmd(0x00); /* speed */
OLED_WriteCmd(end_page);
OLED_WriteCmd(0x00); /* dummy */
OLED_WriteCmd(0xFF); /* dummy */
OLED_WriteCmd(0x2F); /* activate scroll */
}
void OLED_StopScroll(void) {
OLED_WriteCmd(0x2E);
}
/* ---- 进度条 ---- */
void OLED_DrawProgressBar(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t percent) {
if (percent > 100) percent = 100;
OLED_DrawRect(x, y, w, h);
uint8_t fill_w = (uint16_t)(w - 2) * percent / 100;
if (fill_w > 0)
OLED_FillRect(x + 1, y + 1, fill_w, h - 2, 1);
}