从0开始点亮OLED屏幕(二)OLED的初始化

基于篇一我已经介绍了IIC的一些基本时序,本篇文章将教会你们如何初始化程序以及点亮像素点。 

一、OLED屏幕的简单介绍:

1.OLED显存介绍:

        我所使用的OLED为128*64分辨率的,即RAM为128*64位。而RAM分为8个页面,从PAGE0到PAGE7,如图所示:5d78c213f2c146e7887fbfe6d2dcc8d6.png

 而每一页有8个像素,即八位,而刚好一个字节的数据就是八位,8*8=64就是这么来的。我想这不是巧合,可能就是为了让我们可以用一个字节的数据表示某一列的八个像素点的亮灭。从下图可知,其低位是在最上面的,高位依次往下。

f43b122609884f16b7bdc6293b6c41c6.png

而我们点亮OLED相当于将这128*64个像素点点亮的过程,和点阵屏类似。

下面来介绍如何点亮OLED:

二、OLED屏幕的驱动

1.向屏幕写数据:OLED_WR_Byte

        IIC写时序图如下:

2e3694ed71304939b5053a6afae0f116.png

首先开始IIC通信(START),然后发送器件地址(DEVICE ADDRESS) :0X78;等待应答,然后发送命令(OLED命令寄存器地址:0x40;数据寄存器地址:0x00),等待应答,发送数据,等待应答,结束信号;        

//发送一个字节
//mode:数据/命令标志 0,表示命令;1,表示数据;
void OLED_WR_Byte(u8 dat,u8 mode)
{
	I2C_Start();
	Send_Byte(0x78);
	I2C_WaitAck();
	if(mode)
		Send_Byte(0x40);
	else
		Send_Byte(0x00);
	I2C_WaitAck();
	Send_Byte(dat);
	I2C_WaitAck();
	I2C_Stop();
}

2.OLED的初始化

初始化流程图:

2a13bfa109a5457082f80a286e6242c2.png

 

//OLED的初始化
void OLED_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	 //使能A端口时钟
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_12;	 
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; 		 //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
 	GPIO_Init(GPIOA, &GPIO_InitStructure);	  //初始化PA0,1
 	GPIO_SetBits(GPIOA,GPIO_Pin_11|GPIO_Pin_12);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;	 
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
 	GPIO_Init(GPIOA, &GPIO_InitStructure);	  //初始化PA2
 	GPIO_SetBits(GPIOA,GPIO_Pin_15);
	
	OLED_WR_Byte(0xAE,OLED_CMD);//关闭显示(进入睡眠模式)
	OLED_WR_Byte(0x02,OLED_CMD);//设置低列地址
	OLED_WR_Byte(0x10,OLED_CMD);//设置高列地址
	OLED_WR_Byte(0xD5,OLED_CMD);//设置显示时钟分频值/震荡频率(刷新率)
	OLED_WR_Byte(0x80,OLED_CMD);//设置分割比率,设置时钟为100帧/秒
	OLED_WR_Byte(0xA8,OLED_CMD);//设置复用率(0-63)
	OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty	
	OLED_WR_Byte(0xD3,OLED_CMD);//设置显示偏移一位映射RAM计数器(0x00~0x3F)
	OLED_WR_Byte(0x00,OLED_CMD);//不偏移
	OLED_WR_Byte(0x40,OLED_CMD);//设置起始地址  设置映射RAM显示起始行(0x00~0x3F)
	OLED_WR_Byte(0xA1,OLED_CMD);//设置左右方向显示  0xa1正常 0xa0左右反置
	OLED_WR_Byte(0xC8,OLED_CMD);//设置上下方向显示 0xc0上下反置 0xc8正常
	OLED_WR_Byte(0xDA,OLED_CMD);//设置列引脚硬件配置
	OLED_WR_Byte(0x12,OLED_CMD);//12864->0x12;128*32->0x02;
	OLED_WR_Byte(0x81,OLED_CMD);//设置对比度控制寄存器
	OLED_WR_Byte(0xCF,OLED_CMD);//设置对比度控制为0xcf(0x00-0xff)
	OLED_WR_Byte(0xD9,OLED_CMD);//设置充电周期
	OLED_WR_Byte(0xF1,OLED_CMD);//设置预充电为15个时钟,放电为1个时钟
	OLED_WR_Byte(0xDB,OLED_CMD);//设置VCOMH反压值
	OLED_WR_Byte(0x30,OLED_CMD);//设置VCOM取消选择电平
	OLED_WR_Byte(0x20,OLED_CMD);//设置页面寻址模式(0x00/0x01/0x02)
	OLED_WR_Byte(0xA6,OLED_CMD);//设置正常显示
	OLED_Clear();
	OLED_WR_Byte(0x8D,OLED_CMD);//设置电荷泵
	OLED_WR_Byte(0x14,OLED_CMD);//设置电荷泵开启/(0x10)禁用
	OLED_WR_Byte(0xAF,OLED_CMD);//开启显示(进入工作模式)
}

3.点亮一个像素点:

        在OLED的这么多指令中,我们最需要了解的是如何设置列地址与页地址。在此OLED有八页,第一页page0的指令为0XB0、Page1为0xB1、依次类推,Page7则是0xB7;而列地址的设置则需要两次,比如列地址为50,也就是0x32;我们则需要发送指令0x02将列地址第四位设置为2,再发送指令0x13将列高四位设置为3;设置好列地址与页地址就可以发送一串数据点亮某一页的某一列像素亮灭了,相当于点亮八个LED灯。

eg:将第一页的第1列全部点亮:

    OLED_WR_Byte(0xB0,OLED_CMD);
	OLED_WR_Byte(0x00,OLED_CMD);
	OLED_WR_Byte(0x10,OLED_CMD);
	OLED_WR_Byte(0xff,OLED_DATA);

                                ​​​​​​​        ​​​​​​​        374ea826dcc34c89a8d418825423ba38.jpeg

此时就成功点亮OLED了!

但是,这样的效率是比较低的,下面我来介绍另一种方法:首先定义一个二维数组,代表OLED显存,128代表其128列,8则代表8页,u8代表一页的八位数据:

u8 OLED_GRAM[128][8];

 假如要想实现上面的效果,只需要

OLED_GRAM[0][0] = 0xff;
OLED_Refresh();

就能实现和上面同样的效果啦,这里加了OLED显存更新函数,目的是将二维数组OLED_GRAM里面存放的数据显示出来。

4.更新显存函数

下面来介绍更新显存函数:i代表页数,n代表列数。i=0:写入第0页,执行for语句128次便将二维数组中存的数据都显示出来,依次类推....第一个for循环里面的代码与上述一样原理,设置页地址,i等于0时为0xb0,第0页。唯一不同的是我们没有依次设置列地址了,这是由于CH1116芯片有十分人性化的特性:设置完一个字节的8个像素后,其列地址会自动加1!故我们不需要频繁改变列地址;

void OLED_Refresh(void)
{
	u8 i,n;
	for(i=0;i<8;i++)
	{
		OLED_WR_Byte(0xb0+i,OLED_CMD); //设置行起始地址
		OLED_WR_Byte(0x00,OLED_CMD);   //设置低列起始地址
		OLED_WR_Byte(0x10,OLED_CMD);   //设置高列起始地址
		for(n=0;n<128;n++)
		{
			OLED_WR_Byte(OLED_GRAM[n][i],OLED_DATA);
		}
		I2C_Stop();
  }
}

有了此函数,我们就自需把像素数据存入此二维数组,然后调用此函数就可以了!

5.清屏函数

如何实现对OLED的清屏呢,现在很简单了,只需要把二维数组的数据都清零就可以了

void OLED_Clear(void)
{
	u8 i,n;
	for(i=0;i<8;i++)
	{
	   for(n=0;n<128;n++)
			{
			 OLED_GRAM[n][i]=0;//清除所有数据
			}
  }
	OLED_Refresh();//更新显示
}

清除二维数组的方法与显存更新函数大差不差,这里就不再介绍了,自此,控制点亮像素点的方法你已经掌握,还是很简单的吧!

下面附上我的代码:

oled.c

#include "oled.h"

u8 OLED_GRAM[128][8];

//延时
void IIC_delay(void)
{
	u8 t=3;		while(t--);
}

//起始信号
void I2C_Start(void)
{
	SDA_OUT();//将SDA口配置成输出
	OLED_SDA(1);
	OLED_SCL(1);
	IIC_delay();
	OLED_SDA(0);
	IIC_delay();
	OLED_SCL(0);
	IIC_delay();
}

//结束信号,SCL高电平情况下,SDA信号由低变高
void I2C_Stop(void)
{
	SDA_OUT();//将SDA口配置成输出
	OLED_SDA(0);
	OLED_SCL(1);
	IIC_delay();
	OLED_SDA(1);
}

//等待信号响应
void I2C_WaitAck(void) //测数据信号的电平
{
	SDA_IN();//SDA设置为输入
	IIC_delay();
	OLED_SCL(1);
	IIC_delay();
	OLED_SCL(0);
	IIC_delay();
}

//写入一个字节
void Send_Byte(u8 dat)
{
	u8 i;
	SDA_OUT();//将SDA口配置成输出
	OLED_SCL(0);
	for(i=0;i<8;i++)
	{
		OLED_SDA((BitAction)(dat&(0x80>>i)));
		IIC_delay();
		OLED_SCL(1);
		IIC_delay();
		OLED_SCL(0);
	}
}

//发送一个字节
//mode:数据/命令标志 0,表示命令;1,表示数据;
void OLED_WR_Byte(u8 dat,u8 mode)
{
	I2C_Start();
	Send_Byte(0x78);
	I2C_WaitAck();
	if(mode)
		Send_Byte(0x40);
	else
		Send_Byte(0x00);
	I2C_WaitAck();
	Send_Byte(dat);
	I2C_WaitAck();
	I2C_Stop();
}

//更新显存到OLED	
void OLED_Refresh(void)
{
	u8 i,n;
	for(i=0;i<8;i++)
	{
		OLED_WR_Byte(0xb0+i,OLED_CMD); //设置行起始地址
		OLED_WR_Byte(0x00,OLED_CMD);   //设置低列起始地址
		OLED_WR_Byte(0x10,OLED_CMD);   //设置高列起始地址
		for(n=0;n<128;n++)
		{
			OLED_WR_Byte(OLED_GRAM[n][i],OLED_DATA);
		}
		I2C_Stop();
  }
}

//清屏函数
void OLED_Clear(void)
{
	u8 i,n;
	for(i=0;i<8;i++)
	{
	   for(n=0;n<128;n++)
			{
			 OLED_GRAM[n][i]=0;//清除所有数据
			}
  }
	OLED_Refresh();//更新显示
}

void OLED_Test(void)
{
//	OLED_WR_Byte(0xB0,OLED_CMD);
//	OLED_WR_Byte(0x00,OLED_CMD);
//	OLED_WR_Byte(0x10,OLED_CMD);
//	OLED_WR_Byte(0xff,OLED_DATA);
	
	OLED_GRAM[0][0] = 0xff;
	OLED_Refresh();//更新显示
}

//OLED的初始化
void OLED_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	 //使能A端口时钟
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_12;	 
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; 		 //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
 	GPIO_Init(GPIOA, &GPIO_InitStructure);	  //初始化PA0,1
 	GPIO_SetBits(GPIOA,GPIO_Pin_11|GPIO_Pin_12);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;	 
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
 	GPIO_Init(GPIOA, &GPIO_InitStructure);	  //初始化PA2
 	GPIO_SetBits(GPIOA,GPIO_Pin_15);
	
	OLED_WR_Byte(0xAE,OLED_CMD);//关闭显示(进入睡眠模式)
	OLED_WR_Byte(0x02,OLED_CMD);//设置低列地址
	OLED_WR_Byte(0x10,OLED_CMD);//设置高列地址
	OLED_WR_Byte(0xD5,OLED_CMD);//设置显示时钟分频值/震荡频率(刷新率)
	OLED_WR_Byte(0x80,OLED_CMD);//设置分割比率,设置时钟为100帧/秒
	OLED_WR_Byte(0xA8,OLED_CMD);//设置复用率(0-63)
	OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty	
	OLED_WR_Byte(0xD3,OLED_CMD);//设置显示偏移一位映射RAM计数器(0x00~0x3F)
	OLED_WR_Byte(0x00,OLED_CMD);//不偏移
	OLED_WR_Byte(0x40,OLED_CMD);//设置起始地址  设置映射RAM显示起始行(0x00~0x3F)
	OLED_WR_Byte(0xA1,OLED_CMD);//设置左右方向显示  0xa1正常 0xa0左右反置
	OLED_WR_Byte(0xC8,OLED_CMD);//设置上下方向显示 0xc0上下反置 0xc8正常
	OLED_WR_Byte(0xDA,OLED_CMD);//设置列引脚硬件配置
	OLED_WR_Byte(0x12,OLED_CMD);//12864->0x12;128*32->0x02;
	OLED_WR_Byte(0x81,OLED_CMD);//设置对比度控制寄存器
	OLED_WR_Byte(0xCF,OLED_CMD);//设置对比度控制为0xcf(0x00-0xff)
	OLED_WR_Byte(0xD9,OLED_CMD);//设置充电周期
	OLED_WR_Byte(0xF1,OLED_CMD);//设置预充电为15个时钟,放电为1个时钟
	OLED_WR_Byte(0xDB,OLED_CMD);//设置VCOMH反压值
	OLED_WR_Byte(0x30,OLED_CMD);//设置VCOM取消选择电平
	OLED_WR_Byte(0x20,OLED_CMD);//设置页面寻址模式(0x00/0x01/0x02)
	OLED_WR_Byte(0xA6,OLED_CMD);//设置正常显示
	OLED_Clear();
	OLED_WR_Byte(0x8D,OLED_CMD);//设置电荷泵
	OLED_WR_Byte(0x14,OLED_CMD);//设置电荷泵开启/(0x10)禁用
	OLED_WR_Byte(0xAF,OLED_CMD);//开启显示(进入工作模式)
}


oled.h

#ifndef __OLED_H
#define __OLED_H 

#include "sys.h"
#include "stdlib.h"	


//-----------------OLED端口定义---------------- 
#define   GPIO_SCL_PORT			GPIOA
#define   GPIO_SCL_PIN			GPIO_Pin_11                    
#define   GPIO_SCL_ENABLE()     do{ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); }while(0)   /* 所在IO口时钟使能 */

#define   GPIO_SDA_PORT			GPIOA
#define   GPIO_SDA_PIN			GPIO_Pin_12
#define   GPIO_SDA_ENABLE()     do{ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); }while(0)   /* 所在IO口时钟使能 */

#define   OLED_SCL(x)			GPIO_SCL_PORT->BSRR = GPIO_SCL_PIN << (16*(!x))                 
#define   OLED_SDA(x)			GPIO_SDA_PORT->BSRR = GPIO_SDA_PIN << (16*(!x))  

#define SDA_IN()    {GPIOA->CRH &= 0XFFF0FFFF;GPIOA->CRH |= (u32)8<<16;}
#define SDA_OUT()   {GPIOA->CRH &= 0XFFF0FFFF;GPIOA->CRH |= (u32)3<<16;}
#define READ_SDA    (BitAction)(GPIOA->IDR & GPIO_Pin_12)

#define OLED_CMD  0	//写命令
#define OLED_DATA 1	//写数据


void I2C_Start(void);
void I2C_Stop(void);
void I2C_WaitAck(void);
void Send_Byte(u8 dat);
void OLED_WR_Byte(u8 dat,u8 mode);
void OLED_Refresh(void);
void OLED_Clear(void);


void OLED_Init(void);
void OLED_Test(void);

#endif

main.c

int main(void)
{
	OLED_Init();
	while (1)
	{
		OLED_Test();
		
	}
}

 

 

  • 35
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
下面是使用STM32点亮OLED屏幕的基本步骤: 1. 确定OLED屏幕的型号和接口类型。 2. 获得OLED屏幕的驱动程序,包括底层驱动和显示驱动。 3. 在STM32上选择合适的GPIO口,将OLED屏幕的接口连接到STM32上。 4. 在STM32上编写代码,初始化GPIO口,配置SPI接口,初始化OLED屏幕。 5. 在STM32上编写代码,使用SPI接口向OLED屏幕发送数据,实现显示。 具体步骤如下: 1. 确定OLED屏幕的型号和接口类型。 通常OLED屏幕有两种接口类型:SPI和I2C。SPI接口通常速度较快,但需要使用多个GPIO口;I2C接口只需要两个GPIO口,但速度较慢。在选择OLED屏幕时,需要根据自己的需求来选择合适的型号和接口类型。 2. 获得OLED屏幕的驱动程序,包括底层驱动和显示驱动。 可以在网上搜索到相关的驱动程序,或者在OLED屏幕的厂家网站上下载。底层驱动通常包括GPIO初始化、SPI或I2C接口初始化等;显示驱动通常包括屏幕初始化、显示区域设置、字体库等。 3. 在STM32上选择合适的GPIO口,将OLED屏幕的接口连接到STM32上。 需要根据OLED屏幕的接口类型选择合适的GPIO口,将OLED屏幕的接口连接到STM32的GPIO口上。通常需要连接VCC、GND、SCL、SDA等接口。 4. 在STM32上编写代码,初始化GPIO口,配置SPI接口,初始化OLED屏幕。 需要编写初始化代码,包括GPIO初始化、SPI或I2C接口初始化OLED屏幕初始化等。具体实现方法可以参考OLED屏幕的驱动程序。 5. 在STM32上编写代码,使用SPI接口向OLED屏幕发送数据,实现显示。 需要编写显示代码,包括设置显示区域、写入数据等。具体实现方法可以参考OLED屏幕的驱动程序。 以上是使用STM32点亮OLED屏幕的基本步骤,具体实现方法可以参考相关的资料和驱动程序。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值