STM32标准库SSD1306OLED屏幕使用

STM32OLED使用

市面上大部分OLED使用SSD1306作为主控芯片,在这里使用STM32F103作为主控芯片,使用IIC总线点亮OLED。

1.IIC设置以及初始化

共需要引用4个头文件“stm32f10x_rcc.h”,“stm32f10x_gpio.h”,“stm32f10x_i2c.h”,“string.h”

void IIC_init()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//开启GPIO时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);//开启IIC时钟

GPIO_InitTypeDef IIC_Gpio;//定义GPIO结构体
IIC_Gpio.GPIO_Pin |= GPIO_Pin_6 | GPIO_Pin_7;//设置GPIO引脚
IIC_Gpio.GPIO_Mode = GPIO_Mode_AF_OD;//设置GPIO为复用推挽输出
IIC_Gpio.GPIO_Speed = GPIO_Speed_50MHz;//设置GPIO速度
GPIO_Init(GPIOB,&IIC_Gpio);//载入GPIO配置

I2C_DeInit(I2C1);//初始化IIC
I2C_InitTypeDef IIC_InitStruct;//定义IIC结构体
IIC_InitStruct.I2C_ClockSpeed = 400000;//设置IIC时钟速率
IIC_InitStruct.I2C_Mode = I2C_Mode_I2C;//设置IIC模式
IIC_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;//设置IIC占空比
IIC_InitStruct.I2C_OwnAddress1 = 0x21;//设置IIC本机地址,可随意设置,但不能与已有设备重复
IIC_InitStruct.I2C_Ack = I2C_Ack_Enable;//设置IIC应答
IIC_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//设置IIC应答地址长度
I2C_Init(I2C1,&IIC_InitStruct);//载入IIC配置

I2C_Cmd(I2C1,ENABLE);//使能IIC
}

2.IIC发送函数

由于STM32标准库的事件查询组不具备阻塞性质,需要进行封装成阻塞式来等待事件完成

void IIC_WaitEvent(I2C_TypeDef* I2Cx,uint32_t Check_Event)//等待事件更新
{
	while(I2C_CheckEvent(I2C1,Check_Event) != SUCCESS);
}

对于OLED屏幕,不但需要发送一次读写位,还需要在发送一个命令位来确定是写入数据还是命令,数据的话需要先发送0x40,命令的化需要发送0x80。

#define OLED_Address 0x78
void IIC_Send(uint8_t data,uint8_t type)//发送数据函数,type为1时发送命令,为0时发送数据
{
	if(type)//发送命令
	{
		I2C_GenerateSTART(I2C1,ENABLE);//生成IIC起始信号
		IIC_WaitEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT);//等待IIC模式被选中事件
		I2C_Send7bitAddress(I2C1,OLED_Address,I2C_Direction_Transmitter);//发送IIC地址和方向
		IIC_WaitEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//等待IIC发送器模式被选中事件
		I2C_SendData(I2C1,0x80);//发送命令位
		IIC_WaitEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED);//等待发送完成事件
		I2C_SendData(I2C1,data);//发送数据
		IIC_WaitEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED);//等待发送完成事件
		I2C_GenerateSTOP(I2C1,ENABLE);//生成IIC停止信号
	}
	else//发送数据
	{
		I2C_GenerateSTART(I2C1,ENABLE);//生成IIC起始信号
		IIC_WaitEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT);//等待IIC模式被选中事件
		I2C_Send7bitAddress(I2C1,OLED_Address,I2C_Direction_Transmitter);//发送IIC地址和方向
		IIC_WaitEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//等待IIC发送器模式被选中事件
		I2C_SendData(I2C1,0x40);//发送数据位
		IIC_WaitEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED);//等待发送完成事件
		I2C_SendData(I2C1,data);//发送数据
		IIC_WaitEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED);//等待发送完成事件
		I2C_GenerateSTOP(I2C1,ENABLE);//生成IIC停止信号
	}
}

void IIC_SendBuff(uint8_t* str,uint16_t len,uint8_t type)//发送多字节
{
	I2C_GenerateSTART(I2C1,ENABLE);//生成IIC起始信号
	IIC_WaitEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT);//等待IIC模式被选中事件
	I2C_Send7bitAddress(I2C1,OLED_Address,I2C_Direction_Transmitter);//发送IIC地址和方向
	IIC_WaitEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//等待IIC发送器模式被选中事件
	if(type)
	{
		I2C_SendData(I2C1,0x80);//发送命令位
		IIC_WaitEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED);//等待发送完成事件
		while(len--)//循环发送数据
		{
			I2C_SendData(I2C1,*str);//发送数据
			str++;
			IIC_WaitEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED);//等待发送完成事件
		}
	}
	else
	{
		I2C_SendData(I2C1,0x40);//发送数据位
		IIC_WaitEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED);//等待发送完成事件
		while(len--)//循环发送数据
		{
			I2C_SendData(I2C1,*str);//发送数据
			str++;
			IIC_WaitEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED);//等待发送完成事件
		}
	}
	I2C_GenerateSTOP(I2C1,ENABLE);//生成IIC停止信号
}

3.OLED初始化

OLED初始化指令解析:

HexD7D6D5D4D3D2D1D0命令内容
0x8110000001设置对比度,下一条命令代表对比度参数
A[7:0]A7A6A5A4A3A2A1A0设置对比度值范围为1~255
0xA410100100开启正常显示
0xA510100101点亮屏幕所所有像素点
0xA610100110正常显示
0xA710100111像素反向显示
0xAE10101110关闭OLED显示,进入睡眠模式
0xAF10101111开启OLED显示,从睡眠模式中唤醒
0x2000100000设置页面地址模式,下一条命令为具体模式设置
A[1:0]******A1A0设置页面地址模式,A[1:0]=00:水平寻址模式,A[1:0]=01:垂直寻址模式,A[1:0]=10:页寻址模式,A[1:0]=11:无效
0x2100100001设置列显示位置,下两条命令为具体模式设置
A[7:0]*A6A5A4A3A2A1A0列起始地址,范围0-127
B[7:0]*B6B5B4B3B2B1B0列结束地址,范围0-127
0x2200100001设置页显示位置,下两条命令为具体模式设置
A[7:0]*****A2A1A0页起始地址,范围0-7
B[7:0]*****B2B1B0页结束地址,范围0-7
0xA010100000设置列扫描方向,从左向右扫描
0xA110100001设置列扫描方向,从右向左扫描
0xA810101000设置多路复用器,下一条命令为具体参数
A[7:0]**A5A4A3A2A1A0设置多路复用器为初始模式,即16bit模式[1]
0xC011000000设置行扫描方向,从上到下扫描
0xC811000100设置行扫描方向,从下到上扫描
0xD311010011设置显示偏移量,下一条命令为具体参数
A[5:0]**A5A4A3A2A1A0设置显示偏移量,范围0~63
0xD511010101设置显示时钟分频比,下一条命令为具体参数
A[7:0]A7A6A5A4A3A2A1A0设置显示时钟,A[3:0]+1为振荡器分频比,A[7:4]为振荡器频率,范围为0~1111,复位值为1000
0xD911011011设置预充电周期,下一条命令为具体参数
A[7:0]A7A6A5A4A3A2A1A0设置预充电周期,在阶段1,A[3:0]个周期为无效周期,范围015;在阶段2,A[7:4]个周期为有效周期,范围015
0xDB11011011设置VCOMH级别,下一条命令为具体参数
A[7:0]A7A6A5A4A3A2A1A0A[6:4]=000b,VCOMH=0.65VCC;A[6:4]=010b,VCOMH=0.77VCC;A[6:4]=011b,VCOMH=0.83*VCC
0xE311100011无操作
0xD611010110设置放大模式,下一条命令为具体参数
A[7:0]*******A0A[0] = 0,关闭放大模式,A[0] = 1,开启放大模式
0x8D10001101开启电荷泵,后续两条指令必须为0x14和0xAF

上述指令并不包括所有SSD1306的指令,在这里只给出配置相关的指令,具体指令请参考SSD1306的数据手册。

详细初始化代码:

uint8_t OLED_InitCmd[] = 
{	0xAE,//关闭OLED显示
	0x00,//设置低列地址
	0x10,//设置高列地址
	0x40,//设置开始行地址
	0x81,//设置对比度控制寄存器
	0xCF,//设置SEG输出电路亮度
	0xA1,//设置SEG列扫描方向,0xA0左右翻转,
	0xC8,//设置COM输出扫描方向,0xC0上下翻转,0xC8正常显示
	0xA6,//设置正常显示
	0xA8,//设置多路复用器,
	0x3F,//设置多路复用为16bit
	0xD3,//设置移位映射寄存器
	0x00,//无操作
	0xD5,//设置显示时钟分频比/振荡器频率
	0x80,//设置分频比,设置时钟为100帧/s
	0xD9,//设置预充电周期
	0xF1,//设置预充电周期具体参数
	0xDA,//设置COM硬件配置
	0x12,//设置为页面寻址模式
	0xDB,//设置VCOMH
	0x40,//设置VCOM取消电平
	0x20,//设置页面寻址模式
	0x02,//设置为页面寻址模式
	0x8D,//设置电荷泵
	0x14,//设置电荷泵启用
	0xA4,//开启正常显示
	0xA6,//禁用反向显示
	0xAF};//开启OLED显示

void OLED_Init()
{
	IIC_init();//初始化IIC
	IIC_SendBuff(OLED_InitCmd,sizeof(OLED_InitCmd)/sizeof(uint8_t),OLED_CMD);将OLED初始化命令发送到OLED
}

OLED画点函数实现

由于OLED使用的显示为页面寻址,此寻址方式有一个很大的问题,假设此时要显示的东西位于页1的后4行和页2的前四行,如果不进行数据的读取而进行刷新,是必然会丢掉页1的前4行和页2的后4行数据。因此必须要使用页面缓冲的刷新方式,而不能单纯使用点刷新或者行刷新,即最小刷新单位为一个页面。
我的思路是将每个页面数据都存放在长度为128的数组中,每次修改显示数据时,先将要更改的数据存入要显示的位置,在将数组中的数据全部发送到OLED,这样就可以保证不会丢失数据。

uint8_t OLED_Page_Rom[8][128] = {};//定义页面缓存,即显存
void OLED_SetPos(uint16_t ram_address,uint8_t page_address)//设置显示起始位置
{
	IIC_Send(0xb0 | page_address,OLED_CMD);//设置行起始位置
	IIC_Send(0x00 | (ram_address&0x00ff),OLED_CMD);//设置列
}

void OLED_Point_Preload(uint16_t x,uint16_t y,uint8_t draw)//显存预加载函数,将要更改的像素数据传入对应的数组位置
{
	uint8_t page_address = y/8;//得到y坐标对应的页地址
	uint8_t line_address = y%8;//得到y坐标对应的页面中的行地址
	if((OLED_Page_Rom[page_address][x] & 0x01<<line_address) != (draw-30))  //代表原显存与要修改的值不同
	{
		OLED_Page_Rom[page_address][x] |= (draw<<line_address&0xff); //将修改的值存入对应位置
	}
}

void OLED_PageRam_Load(uint8_t page_address,uint8_t* str)//显存发送函数
{
	uint8_t blank[128];//定义局部变量
	memcpy(blank,str,128);//将传入的变量传入局部变量blank
	OLED_SetPos(0,page_address);设置显示起始位置
	IIC_SendBuff(blank,sizeof(blank)/sizeof(uint8_t),OLED_DATA);//向OLED发送数据
}

void OLED_Draw_Point(uint16_t x,uint16_t y,uint8_t draw)
{
	uint8_t page_address = y/8;//得到对应的页地址
	OLED_Point_Preload(x,y,draw);//预加载显存
	OLED_PageRam_Load(page_address,OLED_Page_Rom[page_address]);//发送显存
}

  • 46
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值