/* Includes ----------------------------------------------------------------- */ #include "device/oled.h" #include "bsp/i2c.h" #include #include /* Private define ----------------------------------------------------------- */ #define OLED_I2C_ADDR 0x78 // OLED I2C 地址 #define OLED_WIDTH 128 #define OLED_HEIGHT 64 /* Private variables -------------------------------------------------------- */ static uint8_t oled_buffer[OLED_WIDTH * OLED_HEIGHT / 8]; static struct { uint8_t x_min; uint8_t x_max; uint8_t y_min; uint8_t y_max; uint8_t dirty; // 标志是否有脏区域 } dirty_rect = {0, 0, 0, 0, 0}; /* Private function prototypes ---------------------------------------------- */ static void OLED_WriteCommand(uint8_t cmd); static void OLED_WriteData(uint8_t *data, uint16_t size); static void OLED_MarkDirty(uint8_t x, uint8_t y); static void OLED_UpdateDirtyScreen(void); static const uint8_t oled_font[95][8] = { {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,}, /* " ", 0 */ {0x00,0x00,0x00,0xcf,0xcf,0x00,0x00,0x00,}, /* "!", 1 */ {0x00,0x0c,0x06,0x00,0x0c,0x06,0x00,0x00,}, /* """, 2 */ {0x24,0xe4,0x3c,0x27,0xe4,0x3c,0x27,0x24,}, /* "#", 3 */ {0x00,0x20,0x46,0xf9,0x9f,0x62,0x04,0x00,}, /* "$", 4 */ {0x06,0x09,0xc6,0x30,0x0c,0x63,0x90,0x60,}, /* "%", 5 */ {0x00,0x00,0x6e,0x91,0xa9,0x46,0xa0,0x00,}, /* "&", 6 */ {0x00,0x00,0x00,0x1c,0x0e,0x00,0x00,0x00,}, /* "'", 7 */ {0x00,0x00,0x3c,0x42,0x81,0x00,0x00,0x00,}, /* "(", 8 */ {0x00,0x00,0x00,0x81,0x42,0x3c,0x00,0x00,}, /* ")", 9 */ {0x00,0x10,0x54,0x38,0x38,0x54,0x10,0x00,}, /* "*", 10 */ {0x00,0x10,0x10,0xfc,0x10,0x10,0x00,0x00,}, /* "+", 11 */ {0x00,0x00,0x00,0xc0,0x60,0x00,0x00,0x00,}, /* ",", 12 */ {0x00,0x00,0x10,0x10,0x10,0x10,0x00,0x00,}, /* "-", 13 */ {0x00,0x00,0x00,0x00,0xc0,0xc0,0x00,0x00,}, /* ".", 14 */ {0x00,0x00,0x00,0xc0,0x38,0x07,0x00,0x00,}, /* "/", 15 */ {0x00,0x00,0x7c,0x92,0x8a,0x7c,0x00,0x00,}, /* "0", 16 */ {0x00,0x00,0x00,0x84,0xfe,0x80,0x00,0x00,}, /* "1", 17 */ {0x00,0x00,0x8c,0xc2,0xa2,0x9c,0x00,0x00,}, /* "2", 18 */ {0x00,0x00,0x44,0x92,0x92,0x6c,0x00,0x00,}, /* "3", 19 */ {0x00,0x20,0x38,0x24,0xfe,0x20,0x00,0x00,}, /* "4", 20 */ {0x00,0x00,0x5e,0x92,0x92,0x62,0x00,0x00,}, /* "5", 21 */ {0x00,0x00,0x78,0x94,0x92,0x62,0x00,0x00,}, /* "6", 22 */ {0x00,0x00,0x82,0x62,0x1a,0x06,0x00,0x00,}, /* "7", 23 */ {0x00,0x00,0x6c,0x92,0x92,0x6c,0x00,0x00,}, /* "8", 24 */ {0x00,0x00,0x8c,0x52,0x32,0x1c,0x00,0x00,}, /* "9", 25 */ {0x00,0x00,0x00,0x6c,0x6c,0x00,0x00,0x00,}, /* ":", 26 */ {0x00,0x00,0x80,0xec,0x6c,0x00,0x00,0x00,}, /* ";", 27 */ {0x00,0x00,0x10,0x28,0x44,0x00,0x00,0x00,}, /* "<", 28 */ {0x00,0x00,0x24,0x24,0x24,0x24,0x00,0x00,}, /* "=", 29 */ {0x00,0x00,0x00,0x44,0x28,0x10,0x00,0x00,}, /* ">", 30 */ {0x00,0x00,0x0c,0xa2,0x92,0x1c,0x00,0x00,}, /* "?", 31 */ {0x00,0x3c,0x42,0x99,0xa5,0xa2,0x3c,0x00,}, /* "@", 32 */ {0x00,0xe0,0x1c,0x12,0x12,0x1c,0xe0,0x00,}, /* "A", 33 */ {0x00,0xfe,0x92,0x92,0x9c,0x90,0x60,0x00,}, /* "B", 34 */ {0x00,0x38,0x44,0x82,0x82,0x82,0x44,0x00,}, /* "C", 35 */ {0x00,0xfe,0x82,0x82,0x82,0x82,0x7c,0x00,}, /* "D", 36 */ {0x00,0xfe,0x92,0x92,0x92,0x92,0x92,0x00,}, /* "E", 37 */ {0x00,0xfe,0x12,0x12,0x12,0x12,0x02,0x00,}, /* "F", 38 */ {0x00,0x7c,0x82,0x92,0x92,0x72,0x00,0x00,}, /* "G", 39 */ {0x00,0xfe,0x10,0x10,0x10,0x10,0xfe,0x00,}, /* "H", 40 */ {0x00,0x82,0x82,0xfe,0x82,0x82,0x00,0x00,}, /* "I", 41 */ {0x00,0x82,0x82,0x7e,0x02,0x02,0x00,0x00,}, /* "J", 42 */ {0x00,0xfe,0x10,0x28,0x44,0x82,0x00,0x00,}, /* "K", 43 */ {0x00,0xfe,0x80,0x80,0x80,0x80,0x00,0x00,}, /* "L", 44 */ {0xfc,0x02,0x04,0xf8,0x04,0x02,0xfc,0x00,}, /* "M", 45 */ {0x00,0xfe,0x04,0x18,0x30,0x40,0xfe,0x00,}, /* "N", 46 */ {0x00,0x7c,0x82,0x82,0x82,0x82,0x7c,0x00,}, /* "O", 47 */ {0x00,0x00,0xfe,0x12,0x12,0x0c,0x00,0x00,}, /* "P", 48 */ {0x00,0x00,0x3c,0x42,0xc2,0xbc,0x00,0x00,}, /* "Q", 49 */ {0x00,0x00,0xfe,0x32,0x52,0x8c,0x00,0x00,}, /* "R", 50 */ {0x00,0x00,0x4c,0x92,0x92,0x64,0x00,0x00,}, /* "S", 51 */ {0x00,0x02,0x02,0xfe,0x02,0x02,0x00,0x00,}, /* "T", 52 */ {0x00,0x7e,0x80,0x80,0x80,0x80,0x7e,0x00,}, /* "U", 53 */ {0x00,0x0c,0x30,0xc0,0x30,0x0c,0x00,0x00,}, /* "V", 54 */ {0x7c,0x80,0x80,0x78,0x80,0x80,0x7c,0x00,}, /* "W", 55 */ {0x00,0x84,0x48,0x30,0x30,0x48,0x84,0x00,}, /* "X", 56 */ {0x00,0x06,0x08,0xf0,0x08,0x06,0x00,0x00,}, /* "Y", 57 */ {0x00,0x00,0xc2,0xa2,0x92,0x8e,0x00,0x00,}, /* "Z", 58 */ {0x00,0x00,0xfe,0x82,0x82,0x82,0x00,0x00,}, /* "[", 59 */ {0x00,0x00,0x06,0x18,0x60,0x80,0x00,0x00,}, /* "\", 60 */ {0x00,0x00,0x82,0x82,0x82,0xfe,0x00,0x00,}, /* "]", 61 */ {0x00,0x30,0x0c,0x02,0x0c,0x30,0x00,0x00,}, /* "^", 62 */ {0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,}, /* "_", 63 */ {0x00,0x00,0x04,0x0c,0x18,0x00,0x00,0x00,}, /* "`", 64 */ {0x00,0x00,0x60,0x90,0x90,0xe0,0x00,0x00,}, /* "a", 65 */ {0x00,0x00,0xf8,0xa0,0xe0,0x00,0x00,0x00,}, /* "b", 66 */ {0x00,0x00,0x60,0x90,0x90,0x00,0x00,0x00,}, /* "c", 67 */ {0x00,0x00,0xe0,0xa0,0xf8,0x00,0x00,0x00,}, /* "d", 68 */ {0x00,0x00,0x70,0xa8,0xa8,0x90,0x00,0x00,}, /* "e", 69 */ {0x00,0x00,0x10,0xf8,0x14,0x00,0x00,0x00,}, /* "f", 70 */ {0x00,0x00,0xd8,0xa4,0x7c,0x00,0x00,0x00,}, /* "g", 71 */ {0x00,0x00,0xf8,0x20,0xe0,0x00,0x00,0x00,}, /* "h", 72 */ {0x00,0x00,0x00,0xe8,0x00,0x00,0x00,0x00,}, /* "i", 73 */ {0x00,0x00,0x40,0x90,0x74,0x00,0x00,0x00,}, /* "j", 74 */ {0x00,0x00,0xf8,0x60,0x90,0x00,0x00,0x00,}, /* "k", 75 */ {0x00,0x00,0x78,0x80,0x80,0x00,0x00,0x00,}, /* "l", 76 */ {0x00,0xe0,0x10,0xe0,0x10,0xe0,0x00,0x00,}, /* "m", 77 */ {0x00,0x00,0xf0,0x10,0x10,0xe0,0x00,0x00,}, /* "n", 78 */ {0x00,0x00,0x60,0x90,0x90,0x60,0x00,0x00,}, /* "o", 79 */ {0x00,0x00,0xf0,0x48,0x48,0x30,0x00,0x00,}, /* "p", 80 */ {0x00,0x00,0x30,0x48,0x48,0xf0,0x00,0x00,}, /* "q", 81 */ {0x00,0x00,0x00,0xf0,0x20,0x10,0x00,0x00,}, /* "r", 82 */ {0x00,0x00,0x90,0xa8,0xa8,0x48,0x00,0x00,}, /* "s", 83 */ {0x00,0x10,0x10,0xf8,0x90,0x90,0x00,0x00,}, /* "t", 84 */ {0x00,0x00,0x78,0x80,0x80,0xf8,0x00,0x00,}, /* "u", 85 */ {0x00,0x18,0x60,0x80,0x60,0x18,0x00,0x00,}, /* "v", 86 */ {0x00,0x38,0xc0,0x38,0xc0,0x38,0x00,0x00,}, /* "w", 87 */ {0x00,0x88,0x50,0x20,0x50,0x88,0x00,0x00,}, /* "x", 88 */ {0x00,0x8c,0x50,0x20,0x10,0x0c,0x00,0x00,}, /* "y", 89 */ {0x00,0x88,0xc8,0xa8,0x98,0x88,0x00,0x00,}, /* "z", 90 */ {0x00,0x00,0x10,0x7c,0x82,0x00,0x00,0x00,}, /* "{", 91 */ {0x00,0x00,0x00,0xfe,0x00,0x00,0x00,0x00,}, /* "|", 92 */ {0x00,0x00,0x00,0x82,0x7c,0x10,0x00,0x00,}, /* "}", 93 */ {0x00,0x08,0x04,0x04,0x08,0x10,0x10,0x08,}, /* "~", 94 */ }; /* Exported functions ------------------------------------------------------- */ void OLED_Init(void) { OLED_WriteCommand(0xAE); // 关闭显示 OLED_WriteCommand(0x20); // 设置内存寻址模式 OLED_WriteCommand(0x10); // 页寻址模式 OLED_WriteCommand(0xB0); // 设置页起始地址 OLED_WriteCommand(0xC8); // 设置COM扫描方向 OLED_WriteCommand(0x00); // 设置低列地址 OLED_WriteCommand(0x10); // 设置高列地址 OLED_WriteCommand(0x40); // 设置显示起始行 OLED_WriteCommand(0x81); // 设置对比度 OLED_WriteCommand(0xFF); // 最大对比度 OLED_WriteCommand(0xA1); // 设置段重映射 OLED_WriteCommand(0xA6); // 正常显示 OLED_WriteCommand(0xA8); // 设置多路复用比率 OLED_WriteCommand(0x3F); // 1/64 OLED_WriteCommand(0xA4); // 输出跟随 RAM 内容 OLED_WriteCommand(0xD3); // 设置显示偏移 OLED_WriteCommand(0x00); // 无偏移 OLED_WriteCommand(0xD5); // 设置显示时钟分频比/振荡频率 OLED_WriteCommand(0xF0); // 高频 OLED_WriteCommand(0xD9); // 设置预充电周期 OLED_WriteCommand(0x22); // 修复缺少分号 OLED_WriteCommand(0xDA); // 设置COM引脚硬件配置 OLED_WriteCommand(0x12); // 修复缺少分号 OLED_WriteCommand(0xDB); // 设置VCOMH电压 OLED_WriteCommand(0x20); // 修复缺少分号 OLED_WriteCommand(0x8D); // 设置充电泵 OLED_WriteCommand(0x14); // 修复缺少分号 OLED_WriteCommand(0xAF); // 打开显示 } void OLED_Clear(void) { memset(oled_buffer, 0, sizeof(oled_buffer)); dirty_rect.x_min = 0; dirty_rect.x_max = OLED_WIDTH - 1; dirty_rect.y_min = 0; dirty_rect.y_max = OLED_HEIGHT - 1; dirty_rect.dirty = 1; OLED_UpdateScreen(); } void OLED_UpdateScreen(void) { OLED_UpdateDirtyScreen(); } void OLED_DrawPixel(uint8_t x, uint8_t y, uint8_t color) { if (x >= OLED_WIDTH || y >= OLED_HEIGHT) return; if (color) { if (!(oled_buffer[x + (y / 8) * OLED_WIDTH] & (1 << (y % 8)))) { oled_buffer[x + (y / 8) * OLED_WIDTH] |= (1 << (y % 8)); OLED_MarkDirty(x, y); } } else { if (oled_buffer[x + (y / 8) * OLED_WIDTH] & (1 << (y % 8))) { oled_buffer[x + (y / 8) * OLED_WIDTH] &= ~(1 << (y % 8)); OLED_MarkDirty(x, y); } } } void OLED_DrawChar(uint8_t x, uint8_t y, char ch, uint8_t color) { if (ch < ' ' || ch > '~') return; if (x >= OLED_WIDTH || y >= OLED_HEIGHT || x + 8 > OLED_WIDTH || y + 8 > OLED_HEIGHT) { return; } const uint8_t *font_data = oled_font[ch - ' ']; for (uint8_t i = 0; i < 8; i++) { uint8_t column_data = font_data[i]; for (uint8_t j = 0; j < 8; j++) { if (column_data & (1 << j)) { OLED_DrawPixel(x + i, y + j, color); } else { OLED_DrawPixel(x + i, y + j, !color); } } } } void OLED_DrawString(uint8_t x, uint8_t y, const char *str, uint8_t color) { while (*str) { OLED_DrawChar(x, y, *str, color); x += 8; if (x + 8 > OLED_WIDTH) { x = 0; y += 8; } if (y + 8 > OLED_HEIGHT) { break; } str++; } } /* Private functions -------------------------------------------------------- */ static void OLED_MarkDirty(uint8_t x, uint8_t y) { if (!dirty_rect.dirty) { dirty_rect.x_min = x; dirty_rect.x_max = x; dirty_rect.y_min = y; dirty_rect.y_max = y; dirty_rect.dirty = 1; } else { if (x < dirty_rect.x_min) dirty_rect.x_min = x; if (x > dirty_rect.x_max) dirty_rect.x_max = x; if (y < dirty_rect.y_min) dirty_rect.y_min = y; if (y > dirty_rect.y_max) dirty_rect.y_max = y; } } static void OLED_UpdateDirtyScreen(void) { if (!dirty_rect.dirty) return; uint8_t y_start = dirty_rect.y_min / 8; uint8_t y_end = dirty_rect.y_max / 8; for (uint8_t i = y_start; i <= y_end; i++) { OLED_WriteCommand(0xB0 + i); OLED_WriteCommand(dirty_rect.x_min & 0x0F); OLED_WriteCommand(0x10 | (dirty_rect.x_min >> 4)); uint8_t width = dirty_rect.x_max - dirty_rect.x_min + 1; OLED_WriteData(&oled_buffer[dirty_rect.x_min + i * OLED_WIDTH], width); } dirty_rect.dirty = 0; } static void OLED_WriteCommand(uint8_t cmd) { uint8_t data[2] = {0x00, cmd}; HAL_I2C_Master_Transmit(BSP_I2C_GetHandle(BSP_I2C_OLED), OLED_I2C_ADDR, data, 2, HAL_MAX_DELAY); } static void OLED_WriteData(uint8_t *data, uint16_t size) { uint8_t buffer[size + 1]; buffer[0] = 0x40; memcpy(&buffer[1], data, size); HAL_I2C_Master_Transmit(BSP_I2C_GetHandle(BSP_I2C_OLED), OLED_I2C_ADDR, buffer, size + 1, HAL_MAX_DELAY); }