STM32:0.96寸OLED屏驱动全解析——SSD1306 I2C通信与显存配置指南

发布于:2025-05-23 ⋅ 阅读:(15) ⋅ 点赞:(0)

知识点1【0.96OLED显示屏介绍】

1、OLED的介绍

OLED被称作为 第三代显示技术。显示技术与传统的LED不同:无需背光灯具有柔性(采用柔性有机基板),有电流通过时,有机材料会发光。并且OLED屏幕可以做的更轻更薄可视角度更大,能够显著节省耗电量。

缺点是:屏幕价格较贵,无法做大屏幕显示。

2、OLED基本概念介绍

像素点:最小成像单元,每个像素点都是有大小的。

单色屏:1个像素点占1bit

彩色屏:RGB565(16bit);RGB666(18bit);RGB888(24bit)

R——红色,G——绿色,B——蓝色

如RGB(565)

R:5bit

G:6bit

B:5bit

总计16bit

像素:拍照过程中,依次曝光采集的像素点的个数

屏幕分辨率:一个固定尺寸的屏幕容纳的最大像素点个数

3、OLED显示原理

OLED的驱动芯片:SSD1306B

SSD1306是通过I2C与OLED显示屏进行通信的。

SSD1306B的作用:

1、接收单片机发送过来的数据——字符取模 坐标

2、确定当前显示字符的位置

4、SSD1306B介绍

SSD1306B是一款OLED / PLED驱动器。

特点

(1)支持最高128*64点阵单色

(2)单色

(3)内有GDDRAM(Graphic Display Data RAM),可存储整个屏幕的 像素点阵 数据

(4)工作电压 2.4-3.5V(典型值3.3V

(5)I2C接口

(6)指令集控制

知识点2【使用原理介绍】

1、GDDRAM(图形展示数据寄存器)

(1)特点总结

1、存储时,每8行像素为一页,屏幕有64行像素,因此被分为8页

2、每改动某行某列的像素,则 对应页对应列的8个像素将会一起被改动

3、每页每列的显示值都被一个字节定义,最低为对应最小行,最高位对应最高行。

(2)解释

如下图,

类似这样的页,还有7个(总计8个)

我们操作0行0列,即是操作红框的内容。

并且com0 - com7 依次对应的位是从低到高

2、指令集介绍

这里不介绍全部指令集,仅介绍一些常用的,或者说是本博客中会用到的指令集

81 + [0~256] :亮度(对比度)配置

A4:正常显示

A5:纯色显示

A6:1亮0灭 正常显示

A7:1灭0亮 反色显示

AE:关闭显示睡眠模式

AF:打开显示,正常刷新画面

0xD5 + [0-256]:设置显示时钟分割比例与振荡器频率

A[3:0]:设置分割比例;A[7:4]:设置振荡器频率

0x8A + A[5:0]:设置多路复用比例

A[5:0]配置 行扫描数(根据屏幕分辨率) 这个值加1为行数

0xD3 + A[5:0]:设置显示偏移量

0x00

设置起始行的位置 0x40 表示从第0行开始

0xDA + A[5:4] 配置引脚顺序和和复用方式

根据屏幕的型号配置:我们这里是

0xDA + 0x12

配置垂直方向扫描方向

选择反向扫描:即0xC8

配置水平方向扫描方向

选择反向扫描:即0xA1

下面是电池配置

3、OLED初始化流程图

上图即使初始化的流程图,这些需要结合指令来理解,内容很杂,但不难。

解释

大家结合上面的指令分析去理解即可。

我们需要发送的指令有:

0xAE,0xD5,0x80,0xA8,0x3F,0xD3, 0x00,0x40,0xDA,0x12,0xC8,0xA1,0x8D,0x10,0xD9,0x22,0xDB, 0x30,0x81,0x66,0xA4,0xA6,0xAF

共计23个,大家最好依次发送。

4、设备地址和寄存器地址

(1)前三段解读:设备地址

我们从 前3段 中可以得知

设备地址是 b0111 10[SA0][R/W]

SA:是从机地址选择,如3C

地址仅7位,大家记住,这时I2C中的地址标准,因此这里的地址可以是0x3C与0x3D

有两个地址的原因,是为了提高硬件的灵活性。当我们需要连接多个SSD1306(如双屏)的时候,可以通过此两个不同的地址来区别两个屏幕

R/W功能位:决定读,还是写

由于屏幕,常用来 读取数据

因此我们这里经常发送0x0C<<1 | 0 即0x78

(2)后四段解读:寄存器地址

由后四段可以知道:

寄存器地址

[Co][D/C]00 0000

寄存器的地址可以用来配置 单字节模式/连续模式(Co位来决定),后续字节是命令还是【显存数据(GDDRAM)(即我们上面说的GDDRAM中的特定行特定列的8个像素)(D/C位决定)】

  • 单字节模式:

    每次传输仅发送 一个字节的数据或命令,后续操作需重新发送控制字节。

  • 连续模式

    允许连续发送 多个字节的数据或命令,无需重复发送控制字节。

我们选择单字节模式

因此我们

发送数据,命令为0x40

发送指令,命令为0x00

知识点3【代码补充——I2C的发送函数】

由于今天我复习I2C 与 OLED屏幕

I2C还没有来得及整理。

大家先看看一下代码 了解I2C配置的步骤

我将在这周之内补充I2C的内容,希望大家能够理解!

代码演示

I2C.c

#include "i2c.h"

int i2c_timeout;

void I2C_Config(void)
{
	
	
	GPIO_InitTypeDef GPIO_InitTSources={0};
	I2C_InitTypeDef  I2C_InitSources;
	//打开时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
	
	GPIO_InitTSources.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitTSources.GPIO_Mode=GPIO_Mode_AF_OD;
	GPIO_InitTSources.GPIO_Speed=GPIO_Speed_10MHz;
	//PB6  PB7
	GPIO_Init(GPIOB,&GPIO_InitTSources);
	//i2c配置
	
	I2C_InitSources.I2C_ClockSpeed=100000;//100KHZ
	I2C_InitSources.I2C_Mode=I2C_Mode_I2C;//IIC模式
	I2C_InitSources.I2C_OwnAddress1=0x00;
	I2C_InitSources.I2C_Ack=I2C_Ack_Enable;//使能应答
	I2C_InitSources.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;//7bit从机地址
	I2C_InitSources.I2C_DutyCycle=I2C_DutyCycle_16_9;
	
	I2C_Init(I2C1,&I2C_InitSources);
	I2C_Cmd(I2C1,ENABLE);
}

u8 IIC_Write_Data(uint8_t sla_addr,uint8_t reg_addr,uint8_t data)
{
	//防止抢占冲突
	i2c_timeout = I2C_MAX_TIMEOUT_MS;
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY) && i2c_timeout--)
	{
		//延时1ms
		Delay_ms(1);
		if(i2c_timeout == 0)
		{
			//关闭I2C1
			I2C_GenerateSTOP(I2C1,ENABLE);
			//返回超时信息
			return I2C_TRANS_TIMEOUT;
		}
	}
	
	//打开 加 防超时操作
	i2c_timeout = I2C_MAX_TIMEOUT_MS;
	I2C_GenerateSTART(I2C1,ENABLE);
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT) && i2c_timeout--)
	{
		Delay_ms(1);
		if(!i2c_timeout)
		{
			I2C_GenerateSTOP(I2C1,ENABLE);
			return I2C_TRANS_TIMEOUT;
		}
	}
	
	
	//发送从设备地址 与 模式,判断是否被选中
	i2c_timeout = I2C_MAX_TIMEOUT_MS;
	I2C_Send7bitAddress(I2C1,sla_addr,I2C_Direction_Transmitter);
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) && i2c_timeout--)
	{
		Delay_ms(1);
		if(!i2c_timeout)
		{
			I2C_GenerateSTOP(I2C1,ENABLE);
			return I2C_TRANS_TIMEOUT;
		}
	}
	
	//判断接收应答 是否正确
	if(I2C_GetFlagStatus(I2C1,I2C_FLAG_AF))
	{
		//清楚标志位
		I2C_ClearFlag(I2C1, I2C_FLAG_AF);
		//关闭I2C
		I2C_GenerateSTOP(I2C1,ENABLE);
		//返回值
		return I2C_TRANS_OTHERERR;
	}
	
	//发送从设备的寄存器地址
	i2c_timeout = I2C_MAX_TIMEOUT_MS;
	I2C_SendData(I2C1,reg_addr);
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED) && i2c_timeout--)
	{
		Delay_ms(1);
		if(!i2c_timeout)
		{
			I2C_GenerateSTOP(I2C1,ENABLE);
			return I2C_TRANS_TIMEOUT;
		}
	}
	
	//发送数据
	i2c_timeout = I2C_MAX_TIMEOUT_MS;
	I2C_SendData(I2C1,data);
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED) && i2c_timeout--)
	{
		Delay_ms(1);
		if(!i2c_timeout)
		{
			I2C_GenerateSTOP(I2C1,ENABLE);
			return I2C_TRANS_TIMEOUT;
		}
	}
	
	//停止
	I2C_GenerateSTOP(I2C1,ENABLE);
	return I2C_TRANS_SUCCESS;
}

I2C.h

#ifndef _I2C_H_
#define _I2C_H_

#include "stm32f10x.h"
#include "delay.h"

#define I2C_MAX_TIMEOUT_MS 1000
#define I2C_TRANS_OTHERERR 2
#define I2C_TRANS_TIMEOUT 1
#define I2C_TRANS_SUCCESS 0

#endif

结束

代码重在练习!

代码重在练习!

代码重在练习!

今天的分享就到此结束了,希望对你有所帮助,如果你喜欢我的分享,请点赞收藏加关注,谢谢大家!!!