258 lines
8.0 KiB
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);
|
|
}
|