介绍OLED屏
OLED是有机发光二极管。
优点:自发光,不需要背光;功耗更加低,节能;对比度高,色彩艳丽
缺点:烧屏;价格昂贵;低频频闪
OLED驱动原理
OLED屏驱动的核心是:驱动OLED驱动芯片
SSD1306工作时序(8080时序)
并口总线时序,常用于屏幕驱动IC的访问
写命令
- 拉低CS#使能芯片
- 设置D/C#为低电平(命令模式)
- 将命令字节放到数据总线D[7:0]上
- 拉低WR#
- 保持至少twl时间
- 拉高WR#
- 拉高CS#
// 写命令函数
void SSD1306_WriteCommand(uint8_t cmd) {
CS_LOW(); // 片选使能
DC_LOW(); // 命令模式
DATA_PORT = cmd; // 数据放到总线
WR_LOW(); // 写使能
__NOP(); // 延时(根据MCU调整)
WR_HIGH(); // 写禁止
CS_HIGH(); // 片选禁止
}
写数据
- 拉低CS#使能芯片
- 设置D/C#为高电平(数据模式)
- 将数据字节放到数据总线D[7:0]上
- 拉低WR#
- 保持至少twl时间
- 拉高WR#
- 拉高CS#
// 写数据函数
void SSD1306_WriteData(uint8_t data) {
CS_LOW(); // 片选使能
DC_HIGH(); // 数据模式
DATA_PORT = data; // 数据放到总线
WR_LOW(); // 写使能
__NOP(); // 延时(根据MCU调整)
WR_HIGH(); // 写禁止
CS_HIGH(); // 片选禁止
}
GRAM概念
GRAM是显示驱动芯片内部集成的专用寄存器,用于存储待显示的图像数据,用于缓冲。
GRAM缓冲区定义
static uint8_t g_oled_gram[128][8]; /* OLED的显存 */
二维数组,第一维表示128列(x坐标)
第二维表示8个页面(Y坐标/8)
void oled_refresh_gram(void)
{
uint8_t i,n;
for (i = 0; i < 8; i++) // 遍历8个页面
{
// 1. 设置页地址(0xB0~0xB7)
oled_wr_byte(0xb0 + i, OLED_CMD);
// 2. 设置列地址低4位(固定为0x00)
oled_wr_byte(0x00, OLED_CMD);
// 3. 设置列地址高4位(固定为0x10)
oled_wr_byte(0x10, OLED_CMD);
// 4. 连续写入128个字节的数据
for (n = 0; n < 128; n++)
{
oled_wr_byte(g_oled_gram[n][i], OLED_DATA);
}
}
}
清屏
void oled_clear(void)
{
uint8_t i, j;
for(i = 0; i < 8; i++)
{
for(j = 0; j < 128; j++)
{
g_oled_gram[j][i] = 0x00; // 清空缓冲区
}
}
oled_refresh_gram(); // 刷新到屏幕
}
画点函数
void oled_draw_point(uint8_t x, uint8_t y, uint8_t dot)
{
uint8_t page = y / 8;
uint8_t bit = y % 8;
if(dot)
g_oled_gram[x][page] |= (1 << bit); // 置1
else
g_oled_gram[x][page] &= ~(1 << bit); // 清0
// 注意:这里不立即刷新,可以批量修改后统一刷新
}
显示字符
// 完整的字符显示函数示例
void oled_show_char(uint8_t x, uint8_t y, uint8_t chr, uint8_t size, uint8_t mode)
{
uint8_t temp, t1, t;
uint8_t y0 = y;
uint8_t csize = (size / 8 + ((size % 8) ? 1 : 0)) * (size / 2);
chr = chr - ' '; // 得到偏移后的值
for(t = 0; t < csize; t++)
{
if(size == 12)
temp = oled_ascii_1206[chr][t]; // 调用1206字体
else if(size == 16)
temp = oled_ascii_1608[chr][t]; // 调用1608字体
else if(size == 24)
temp = oled_ascii_2412[chr][t]; // 调用2412字体
else
return; // 没有的字库
for(t1 = 0; t1 < 8; t1++)
{
if(temp & 0x80)
oled_draw_point(x, y, mode);
else
oled_draw_point(x, y, !mode);
temp <<= 1;
y++;
if((y - y0) == size)
{
y = y0;
x++;
break;
}
}
}
}
批量显示字符串
void oled_show_string(uint8_t x, uint8_t y, char *str, uint8_t size)
{
while(*str != '\0')
{
oled_show_char(x, y, *str, size, 1);
x += size / 2; // 字符宽度
if(x > 128 - size / 2) // 换行
{
x = 0;
y += size;
}
str++;
}
}