mirror of
https://github.com/goldenfishs/MRobot.git
synced 2025-04-28 23:39:55 +08:00
278 lines
11 KiB
C
278 lines
11 KiB
C
/*
|
|
* @file oled_i2c.c
|
|
* @brief OLED I2C驱动程序
|
|
* @version 1.0
|
|
* @date 2023-10-01
|
|
* @note 本文件实现了OLED显示屏的I2C通信和基本绘图功能。
|
|
*/
|
|
*/
|
|
|
|
|
|
/* Includes ----------------------------------------------------------------- */
|
|
#include "device/oled_i2c.h"
|
|
#include "bsp/i2c.h"
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
/* 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) {
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|