基于I2C协议的OLED显示(利用U8G2库)

基于I2C协议的OLED显示(利用U8G2库)

一、I2C协议

1.基本原理
I2C 也叫 IIC(Inter-Integrated Circuit)总线,是一种由PHILIPS公司在80年代开发的两线式串行总线,用于连接微控制器及其外围设备。它是半双工通信方式。
IIC协议可分为物理层与协议层。物理层规定通讯系统中具有机械、电子功能部分的特性,确保原始数据在物理媒体的传输。
协议层主要规定通讯逻辑,统一收发双方的数据打包、解包标准。
I2C通信协议采用主从结构,其中一个设备充当主设备(Master),而其他设备则是从设备(Slave)。主设备负责控制通信的开始和结束,而从设备在主设备的控制下进行响应。I2C通信协议使用起始和停止条件来标识通信的开始和结束。起始条件是SDA线由高电平切换到低电平,而停止条件则是SDA线由低电平切换到高电平。2.时序协议
启动信号:

SCL为高电平的时候,SDA由高电平向低电平跳变。结束信号:SCL为高电平的时候,SDA由低电平向高电平跳变。

应答信号:

I2C总线上的所有数据都是以8位字节传送的,发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功,对于反馈有效应答位ACK的要求是,接收器在第9个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。如果接收器是主控器,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器结束数据发送,并释放SDA线,以便主控接收器发送一个停止信号P。

写时序:

开始信号:主机+从设备地址+写命令,从机应答,应答成功,表示有这个设备,然后主机+设备内部寄存器地址,此时不用再加写命令控制字,从机应答,应答成功,表示设备内有这个地址,主机写入数据,从机应答,是否继续发送,不发送的话,发送停止信号P。

读时序:

要想读设备,首先要知道将要所读取设备的地址告诉从设备,从设备才能将数据放到(发送)SDA上使主设备读取,从设备将数据放入SDA上的过程,由硬件主动完成,不用人为的写入。所以首先先写入从机地址,然后+写控制命令,从机应答,应答成功,表示有这个设备,然后写入内部寄存器地址,此时不用再加写命令控制字,从机应答,应答成功,表示设备内有这个地址。然后主机继续发出:写入从机地址,然后+读命令,从机应答,应答成功,此时便可以读取数据了,从设备已经将数据放入到SDA上了。地址跟设备已经验证了,不用再进行验证。

二、0.96寸OLED屏的工作原理

1.基本概念
0.96寸4针OLED屏模块是一种显示屏模块,它包括一个0.96英寸的OLED显示屏和4个引脚。这种OLED屏幕模块通常用于嵌入式系统和小型电子设备中,可以显示文本、图像和其他类型的信息。由于其小尺寸和低功耗,它们也常用于智能手表、健康追踪器和其他便携式设备中。此类模块通常使用SPI或I2C接口进行通信,并且支持多种分辨率和颜色模式。 该模块有4个引脚,分别为VCC、GND、SCL和SDA。VCC是电源引脚,用于提供模块的电源,一般是3.3V或5V电源。GND是地引脚,用于提供模块的接地。需要接到负极电源上。SCL是时钟引脚,用于传输数据时的时钟信号。通常需要连接到主控芯片的时钟引脚。SDA是数据引脚,用于传输数据。通常需要连接到主控芯片的数据引脚。在使用I2C接口时,这个引脚也可以被称为SDA(串行数据线)。

2.工作原理
在32单片机中使用0.96寸4针OLED屏模块时,需要将单片机的输出信号转化为OLED屏幕可识别的信号。因此,需要使用相应的OLED控制器,通过I2C总线进行通信,将单片机的输出信号转化为OLED屏幕可识别的信号。代码中应定义OLED屏模块的驱动函数,OLED屏模块的驱动程序主要包括以下几个部分:写入函数(用于向OLED发送命令或数据)、清屏函数(用于清除OLED上的所有内容)、显示函数(用于将缓冲区中的内容写入到OLED上)、点亮或熄灭像素点的函数、显示字符或字符串的函数等。最后初始化OLED屏模块,具体步骤为:首先开启GPIOB的时钟;然后设置GPIOB的8、9引脚为输出模式,推挽输出,100MHz速率,上拉;延时200ms,等待OLED屏幕启动;发送OLED的初始化命令序列;最后清空OLED屏幕。由于环境参数是变动的,所以可以考虑将环境参数变量和环境阈值转化为字符串形式再由OLED屏输出。

三、开源GUI库U8G2与stm32移植编译

U8g2是嵌入式设备的单色图形库,主要应用于嵌入式设备,包括我们常见的单片机,基本上主流的显示控制器都支持。在移植u8g2之前,需要使用CubeMX配置MCU的功能。

参考博客:基于STM32移植U8g2图形库——OLED显示(HAL库)_stm32 u8g2-CSDN博客

1、RCC配置外部高速晶振(精度更高)——HSE;

img

2、SYS配置:Debug设置成Serial Wire否则可能导致芯片自锁);

img

3、I2C2配置:作为OLED的通讯方式;

img

4、TIM1配置:U8g2图形库需要us级延迟推动(U8g2的心跳)

img

5、时钟树配置:

img

6、工程配置

img

四、显示自己的学号和名字

4.2.1、创建工程

工程创建和上述3.1过程一样

4.2.2、字模准备
4.2.3、编写代码

#include “oled.h”
#include “asc.h”
#include “main.h”

void WriteCmd(unsigned char I2C_Command)//???
{
HAL_I2C_Mem_Write(&hi2c2,OLED0561_ADD,COM,I2C_MEMADD_SIZE_8BIT,&I2C_Command,1,100);
}

void WriteDat(unsigned char I2C_Data)//???
{
HAL_I2C_Mem_Write(&hi2c2,OLED0561_ADD,DAT,I2C_MEMADD_SIZE_8BIT,&I2C_Data,1,100);
}
void OLED_Init(void)
{
HAL_Delay(100); //???

WriteCmd(0xAE); //display off
WriteCmd(0x20);	//Set Memory Addressing Mode	
WriteCmd(0x10);	//00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
WriteCmd(0xb0);	//Set Page Start Address for Page Addressing Mode,0-7
WriteCmd(0xc8);	//Set COM Output Scan Direction
WriteCmd(0x00); //---set low column address
WriteCmd(0x10); //---set high column address
WriteCmd(0x40); //--set start line address
WriteCmd(0x81); //--set contrast control register
WriteCmd(0xff); //???? 0x00~0xff
WriteCmd(0xa1); //--set segment re-map 0 to 127
WriteCmd(0xa6); //--set normal display
WriteCmd(0xa8); //--set multiplex ratio(1 to 64)
WriteCmd(0x3F); //
WriteCmd(0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
WriteCmd(0xd3); //-set display offset
WriteCmd(0x00); //-not offset
WriteCmd(0xd5); //--set display clock divide ratio/oscillator frequency
WriteCmd(0xf0); //--set divide ratio
WriteCmd(0xd9); //--set pre-charge period
WriteCmd(0x22); //
WriteCmd(0xda); //--set com pins hardware configuration
WriteCmd(0x12);
WriteCmd(0xdb); //--set vcomh
WriteCmd(0x20); //0x20,0.77xVcc
WriteCmd(0x8d); //--set DC-DC enable
WriteCmd(0x14); //
WriteCmd(0xaf); //--turn on oled panel

}

void OLED_SetPos(unsigned char x, unsigned char y) //???
{
WriteCmd(0xb0+y);
WriteCmd(((x&0xf0)>>4)|0x10);
WriteCmd((x&0x0f)|0x01);
}

void OLED_Fill(unsigned char fill_Data)//???
{
unsigned char m,n;
for(m=0;m<8;m++)
{
WriteCmd(0xb0+m); //page0-page1
WriteCmd(0x00); //low column start address
WriteCmd(0x10); //high column start address
for(n=0;n<128;n++)
{
WriteDat(fill_Data);
}
}
}

void OLED_CLS(void)//??
{
OLED_Fill(0x00);
}

void OLED_ON(void)
{
WriteCmd(0X8D); //???
WriteCmd(0X14); //???
WriteCmd(0XAF); //OLED??
}

void OLED_OFF(void)
{
WriteCmd(0X8D); //???
WriteCmd(0X10); //???
WriteCmd(0XAE); //OLED??
}

// Parameters : x,y – ???(x:0~127, y:0~7); ch[] – ???; TextSize – ???(1:68 ; 2:816)
// Description : ??codetab.h??ASCII??,?68?816???
void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize)
{
unsigned char c = 0,i = 0,j = 0;
switch(TextSize)
{
case 1:
{
while(ch[j] != ‘\0’)
{
c = ch[j] - 32;
if(x > 126)
{
x = 0;
y++;
}
OLED_SetPos(x,y);
for(i=0;i<6;i++)
WriteDat(F6x8[c][i]);
x += 6;
j++;
}
}break;
case 2:
{
while(ch[j] != ‘\0’)
{
c = ch[j] - 32;
if(x > 120)
{
x = 0;
y++;
}
OLED_SetPos(x,y);
for(i=0;i<8;i++)
WriteDat(F8X16[c16+i]);
OLED_SetPos(x,y+1);
for(i=0;i<8;i++)
WriteDat(F8X16[c
16+i+8]);
x += 8;
j++;
}
}break;
}
}

// Parameters : x,y – ???(x:0~127, y:0~7); N:???.h???
// Description : ??ASCII_8x16.h???,1616??
void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N)
{
unsigned char wm=0;
unsigned int adder=32
N;
OLED_SetPos(x , y);
for(wm = 0;wm < 16;wm++)
{
WriteDat(F16x16[adder]);
adder += 1;
}
OLED_SetPos(x,y + 1);
for(wm = 0;wm < 16;wm++)
{
WriteDat(F16x16[adder]);
adder += 1;
}
}

// ???,???ascll.h???(???)
//???❌???
// y:???(??0-7)
// begin:???ascll.c???
// num:???
// ???0,1,???0,???,??❌0,y:2,begin:0,num:2
void OLED_ShowCN_STR(u8 x , u8 y , u8 begin , u8 num)
{
u8 i;
for(i=0;i<num;i++){OLED_ShowCN(i*16+x,y,i+begin);} //OLED???
}

// Parameters : x0,y0 – ???(x0:0~127, y0:0~7); x1,y1 – ???(???)???(x1:1128,y1:18)
// Description : ??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<y1;y++)
{
OLED_SetPos(x0,y);
for(x=x0;x<x1;x++)
{
WriteDat(BMP[j++]);
}
}
}

void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size)
{
unsigned char c=0,i=0;
c=chr-’ ';//???
if(x>128-1){x=0;y=y+2;}
if(Char_Size ==16)
{
OLED_SetPos(x,y);
for(i=0;i<8;i++)
WriteDat(F8X16[c16+i]);
OLED_SetPos(x,y+1);
for(i=0;i<8;i++)
WriteDat(F8X16[c
16+i+8]);
}
else {
OLED_SetPos(x,y);
for(i=0;i<6;i++)
WriteDat(F6x8[c][i]);

		}

}
u32 oled_pow(u8 m,u8 n)
{
u32 result=1;
while(n–)result*=m;
return result;
}
//??2???
//x,y :???
//len :???
//size:???
//mode:?? 0,???;1,???
//num:??(0~4294967295);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size2)
{
u8 t,temp;
u8 enshow=0;
for(t=0;t<len;t++)
{
temp=(num/oled_pow(10,len-t-1))%10;
if(enshow0&&t<(len-1))
{
if(temp
0)
{
OLED_ShowChar(x+(size2/2)*t,y,’ ',size2);
continue;
}else enshow=1;
}
OLED_ShowChar(x+(size2/2)*t,y,temp+‘0’,size2);
}
}

/*
@brief ???
@param x0:???
y0:???
x1:???
y1:???
k: ???
m: ???
BMP[][m]:???
@retval ?
*/

void OLED_DrawGIF(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1, unsigned char k, int m, unsigned char GIF[][m])
{
unsigned int j=0; //???
unsigned char x,y,i; //???

if(y1%8==0) y=y1/8;   //????????8????
 else y=y1/8+1;
for (i=0;i<k;i++) //???????
{
	j = 0;
	for(y=y0;y<y1;y++) //??????,?????
	{
		OLED_SetPos(x0,y); //?????????
		
		for(x=x0;x<x1;x++) //?x1 - x0 ?
    		{
					
    			WriteDat(GIF[i][j++]);	//?????    	
    		}
	}
	//delay_ms(80);//?????????
 
}

}

OLED.h

#ifndef __OLED_H
#define __OLED_H

#include “i2c.h”

#define u8 uint8_t
#define u32 uint32_t
// ???
// OLED_ShowStr(0, 0, “hello world”, 2);//???
// OLED_ShowStr(0, 2, “hello world”, 1);//???
// OLED_ShowCN_STR(0, 4 , 0 , 8);
// sprintf(num_temp_buffer,“show num:%0.2f”,num);
// OLED_ShowStr(0, 6, num_temp_buffer, 2);//???
// OLED_CLS();
// OLED_DrawBMP(0,0,128,7,BMP2);

#define OLED0561_ADD 0x78 // OLED?I2C??(???)
#define COM 0x00 // OLED ??(???)
#define DAT 0x40 // OLED ??(???)

void WriteCmd(unsigned char I2C_Command);//???
void WriteDat(unsigned char I2C_Data);//???
void OLED_Init(void);//???
void OLED_SetPos(unsigned char x, unsigned char y);
void OLED_Fill(unsigned char fill_Data);//???
void OLED_CLS(void);
void OLED_ON(void);
void OLED_OFF(void);
void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize);//???
void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N);//???
void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[]);//???(???)

void OLED_ShowCN_STR(u8 x , u8 y , u8 begin , u8 num); //???

void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size);
u32 oled_pow(u8 m,u8 n);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size2);//size2(16|12)
void OLED_DrawGIF(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1, unsigned char k, int m, unsigned char GIF[][m]);

#endif

汉字显示的函数

// 这是自己写的显示中文字符串的函数,要先把中文字符串“共阴——列行式——逆向输出”取字模后存入asc.h相应的位置(连续存入)
//传入参数分别为:x:起始横坐标
// y:纵坐标(填入0-7)
// begin:填入的中文字符串的第一个字在我们asc.c字库里面的序号
// num:我们要填写几个字
// 比如要填“测试”,取完字模存入后这两个字在字库中序号为0,1,横坐标0,纵坐标第二行,就填:x:0,y:2,begin:0,num:2
void OLED_ShowCN_STR(u8 x , u8 y , u8 begin , u8 num)
{
u8 i;
for(i=0;i<num;i++){OLED_ShowCN(i*16+x,y,i+begin);} //OLED显示标题
}

main.c

OLED_ShowCN_STR(40,1,0,3);	



OLED_ShowStr(30,5,"632207060327",1);	
  • 20
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值