51驱动SSD1306OLED显示屏(详细教程)

2022.7.3更新:分享一下文件自取

链接:https://pan.baidu.com/s/1oEu-XXo5MCHzxKD0NWpjbA 
提取码:4a42


以下为原文:

51驱动SSD1306OLED显示屏(详细教程)_第1张图片

前几天买了个0.96寸的OLED显示屏,商家说支持I2C和SPI通信,就想着应该比较简单,于是兴冲冲准备大干一场。然而发现好像并没有想象中那么简单,还是有很多要点需要了解,下面细说。

首先拿到SSD1306后,接上GND,VCC,SCL,SDA四条线后,屏幕是不会自动亮起的,需要CPU写入数据驱动它显示。查阅SSD1306的手册,我们可以发现与其通信的方式。在这里我们使用的是I2C通信方式:

  1. 起始信号
  2. 写入芯片地址,一般为0x78或者0x7a
  3. 应答信号
  4. 写入控制命令(D/C#)选择数据模式(0x40)/控制模式(0x00)
  5. 应答信号
  6. 写入数据/控制字(一个字节)
  7. 应答信号
  8. 结束信号

51驱动SSD1306OLED显示屏(详细教程)_第2张图片

 由于一开始我们需要初始化SSD1306,因此我们需要先写入一些控制字来命令SSD1306的工作模式。鉴于指令集网上的中文手册中已有许多资料,这里不再赘述,只简单介绍一些用到的指令。

OLED_WR_Byte(0xAE,OLED_CMD);//开启OLED屏显示

 以上是开启OLED屏关闭的命令,0xAE是控制字,OLED_CMD是写入命令。以下同理,便不再赘述

OLED_WR_Byte(0x00,OLED_CMD);//设置列起始地址的高位
OLED_WR_Byte(0x10,OLED_CMD);//设置列起始地址的低位
OLED_WR_Byte(0xB0,OLED_CMD);//设置目标显示位置页的起始地址

 这三行代码用来控制显示的位置。这里就必须提一下SSD1306的内存地址模式,因为不同的模式下写入相同的数据输出的图像不同。SSD1306中有三种不同的内存地址模式:页地址模式,水平地址模式,垂直地址模式。

这里我们使用的是页地址模式,其结构如下所示:
51驱动SSD1306OLED显示屏(详细教程)_第3张图片

可以看到,这是128*64的显示屏,其中横向分为128段(列),竖向分为8页,每页有8行。选择页地址模式后,数据的填充如下所示:

51驱动SSD1306OLED显示屏(详细教程)_第4张图片

在用户定义的地址写入数据后,列地址会自动加1,写完最后一列后,列地址指针会重置为列开始地址,而页地址不会变。至此,我们就很好理解上述的三行命令,其中0x00和0x10设置了列起始地址为0,而0xb0则设置了页起始地址为0。

OLED_WR_Byte(0x81,OLED_CMD); //设置显示的对比度(00H~FFH)
OLED_WR_Byte(0xFF,OLED_CMD);//对比度为256
OLED_WR_Byte(0xA1,OLED_CMD);/设置Segment重映射
OLED_WR_Byte(0xA6,OLED_CMD);//设置正常显示
OLED_WR_Byte(0xA8,OLED_CMD);//设置复用率(16~63)
OLED_WR_Byte(0x3F,OLED_CMD);//设置复用率为63
OLED_WR_Byte(0xC8,OLED_CMD);//设置COM输出扫描方向
OLED_WR_Byte(0xD3,OLED_CMD);//设置显示偏移
OLED_WR_Byte(0x00,OLED_CMD);//偏移量为0
OLED_WR_Byte(0xD5,OLED_CMD);//设置时钟分频率(1~16),振荡器频率
OLED_WR_Byte(0x80,OLED_CMD);//0~3位为时钟分频率(1),4~7位为振荡器频率(1000)
OLED_WR_Byte(0xD8,OLED_CMD);//set area color mode off
OLED_WR_Byte(0x05,OLED_CMD);//
OLED_WR_Byte(0xD9,OLED_CMD);//设置重充电周期
OLED_WR_Byte(0xF1,OLED_CMD);//
OLED_WR_Byte(0xDA,OLED_CMD);//设置COM引脚硬件配置
OLED_WR_Byte(0x12,OLED_CMD);//
OLED_WR_Byte(0xDB,OLED_CMD);//设置Vcomh取消选择水平
OLED_WR_Byte(0x30,OLED_CMD);//
OLED_WR_Byte(0x8D,OLED_CMD);//set charge pump enable
OLED_WR_Byte(0x14,OLED_CMD);//
OLED_WR_Byte(0xAF,OLED_CMD);//打开OLED显示

以上我们就把OLED初始化完成了。接下来我们可以开始写显示函数了。

	void OLED_Set_Pos(unsigned char x, unsigned char y) 
{ 	OLED_WR_Byte(0xb0+y,OLED_CMD);//设置目标显示位置页起始地址
	OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);//设置列起始地址高位
	OLED_WR_Byte((x&0x0f),OLED_CMD);//设置列起始地址低位 
}   //起始列为x

这里我们先写一个设置坐标的函数,方便后面调用。

1.显示一个字符的函数如下:

void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size)
{      	   //8x16规格
	unsigned char c=0,i=0;	
	c=chr-' ';//得到偏移后的值
		      //' '为ASCII码第32个			
	if(x>Max_Column-1)
        {
        x=0;y=y+2;//如果x大于最大列,则置0,y+2?
        }
	if(Char_Size ==16)
        {
		OLED_Set_Pos(x,y);//起始列为x,起始页为y	
		for(i=0;i<8;i++)
		    {
            OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);//第c*16行前半行的数据
            }
		OLED_Set_Pos(x,y+1);//换下一页
		for(i=0;i<8;i++)
		    {
            OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);//后半行数据
            }
		}
	else {	
			OLED_Set_Pos(x,y);
			for(i=0;i<6;i++)
			OLED_WR_Byte(F6x8[c][i],OLED_DATA);
		}
}

由于规格为8*16,因此我们需要16个16进制数来表示一个字符,表的一部分如下:

 其中最后一个十进制数为所在数组中的第几行,即字符减去偏移量32后的十进制序号。

51驱动SSD1306OLED显示屏(详细教程)_第5张图片

上图为SSD1306页地址模式下单个字符的写入模式。

 2.显示一个汉字的函数如下:

void OLED_ShowCHinese(u8 x,u8 y,u8 no)
{   //16*16   			    
	u8 t,adder=0;
	OLED_Set_Pos(x,y);	
    for(t=0;t<16;t++)
		{
				OLED_WR_Byte(Hzk[2*no][t],OLED_DATA);//字模数组
				adder+=1;
     }	
		OLED_Set_Pos(x,y+1);	
    for(t=0;t<16;t++)
			{	
				OLED_WR_Byte(Hzk[2*no+1][t],OLED_DATA);
				adder+=1;
      }					
}

可以看到,显示汉字与显示字符的方式大同小异,只是汉字为16*16的规格。

 利用取模工具生成汉字的字模,再利用以上函数,就可以显示汉字。

3.如果要显示BMP图片,也需要用取模工具取模,再利用函数即可实现。

void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[])
{ 	
 unsigned int j=0;
 unsigned char x,y;
  
  if(y1%8==0) y=y1/8;      
  else y=y1/8+1;
	for(y=y0;y

以上基本就可以实现SSD1306的显示功能,当然,有很多汉字和图片需要自己用取模工具(例如PCtoLCD2002)去取模。

51驱动SSD1306OLED显示屏(详细教程)_第6张图片

以上就是SSD1306的显示功能的实现过程,不足之处,还望担待。

51驱动SSD1306OLED显示屏(详细教程)_第7张图片

你可能感兴趣的:(单片机)