资讯详情

【强烈推荐】基于stm32的OLED各种显示实现(含动态图)

作为人们日常生活中常见的屏幕类型之一,受众非常广泛。等等都离不开他的身影。可以说学会了OLED模块是嵌入式开发同时,它也是驱动开发技能之一重要的手段和技巧!(

STM32F103C8T6;0.96寸OLED

一、OLED简介

OLED,即(Organic Light-Emitting Diode),也叫有机电激光显示(, OELD)。OLED 由于同时下一代平面显示器认为是下一代平面显示器的新兴应用技术。

,而 ,因为它是自发光的。同样的显示,OLED 效果更好。以目前的技术,,但是分辨率确实很高。在市场上很常见有以下

(1)模块有可选,单色为,而双色则为。 (2)尺寸小,显示尺寸小 0.96 模块的尺寸仅为 27mm*26mm 大小。 (3)模块高分辨率。 (4)模块提供多种接口方式的总共 5 接口包括6800和8080 两种并行接口方式,,、(只需要 2 可以控制根线 OLED 了!)。 (5),直接接你可以工作。

,市场上有一些OLED屏幕不能直接5.0v电压,否则

目前市面上主要有屏幕通信方式两种!SPI为较多,而I2C为。2种通讯协议:总所周知,明显I2C通信速度,所以通常使用SPI通讯协议的OLED屏幕可以实现,画面更为

当然,OLED屏幕显示的帧数不仅取决于通信协议,也可以使用。作者将在下一篇博客文章中特别介绍这一点,感兴趣的读者可以关注!

本实验采用0.96寸OLED屏幕为I2C通讯方式,在这里给读者一点介绍I2C通讯原理。

总线是一种由 用于连接微控制器及其外围设备的两线串行总线。串行总线可以发送和接收数据。 CPU 与被控 IC 之间、IC 与 IC 双向传输,。 I2C 在传输数据的过程中,总线共有, 它们分别是::SCL 高电平时,SDA 数据从高电平跳转到低电平跳转。 :SCL 高电平时,SDA 数据从低电平跳转到高电平跳转结束。 :接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲, 表示已收到数据。CPU 向受控单位发出信号后,等待受控单位发出响应信号,CPU 接收响应信号后,根据实际情况判断信号是否继续传输。

在这些信号中,,没有结束信号和响应信号。

目前大部分 MCU 都带有 IIC 总线接口,STM32 也不例外。但我们不在这里使用它。 ,而是通过。STM32 的硬件 IIC 非常复杂,,故不推荐使用。所以我们这里就通过模拟来实现了。

        本文的主要目的是为实现OLED各种显示(),I2C的通讯原理只给大家稍微科普梳理一下。如果有需要进一步了解的读者,可以移步笔者的其他文章进行详细学习。

三、CubexMX配置

        为了缩短开发周期和方便后续讲解,这里使用。这部分的Cubex配置较为简单,具体过程如下:

        1、SYS配置:Debug选择Serial Wire(否则芯片可能自

        2、RCC配置:

         3、I2C配置:

         4、时钟树配置:

 四、代码实现与实验效果

4.1 OLED基础的初始化

        oled.c中的代码:

#include "oled.h"
#include "asc.h"    //字库(可以自己制作)
#include "main.h"

void WriteCmd(unsigned char I2C_Command) //写命令利用I2C通讯
 {
	HAL_I2C_Mem_Write(&hi2c1,OLED0561_ADD,COM,I2C_MEMADD_SIZE_8BIT,&I2C_Command,1,100);
 }
		
void WriteDat(unsigned char I2C_Data)    //写数据利用I2C通讯
 {
		HAL_I2C_Mem_Write(&hi2c1,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休眠
}

        以上代码都是OLED初始化等必不可少的基础函数,目的在于配置和方便以后使用OLED。用户可以直接移植使用,

4.2 OLED各种显示API函数

        其实从来说OLED等一众屏幕显示都是基于,这里的像素点就可以等效于一个(当然,LED可以是单色的,也可以是基于三原色的RGB灯),我们利用取模工具将我们表现得等做成字库,进而利用字库去点亮规定好得LED灯及像素点,即可完成OLED得显示目的。

4.2.1 显示字符串

        字符串的显示是在基础上实现的,是OLED显示中常见的API函数之一。

// Parameters     : x,y -- 起始点坐标(x:0~127, y:0~7); ch[] -- 要显示的字符串; TextSize -- 字符大小(1:6*8 ; 2:8*16)
// Description    : 显示codetab.h中的ASCII字符,有6*8和8*16可选择
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[c*16+i]);
				OLED_SetPos(x,y+1);
				for(i=0;i<8;i++)
					WriteDat(F8X16[c*16+i+8]);
				x += 8;
				j++;
			}
		}break;
	}
}

        

OLED_ShowStr(20,3,"hello world",2);

4.2.2 显示汉字

        汉字显示也是我们实际工程运用中非常常见的一种显示要求,这里我们通常会用运用到

        笔者选用了,取模过程如下:

       读者使用的取模设置方式一定要和起来,否则可能会出现乱码现象。

        单个汉字显示的API函数:

// Parameters     : x,y -- 起始点坐标(x:0~127, y:0~7); N:汉字在.h中的索引
// Description    : 显示ASCII_8x16.h中的汉字,16*16点阵
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;
	}
}

        汉字串显示的API函数:

// 这是自己写的显示中文字符串的函数,要先把中文字符串“共阴——列行式——逆向输出”取字模后存入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显示标题
}

        

	OLED_ShowCN_STR(10,3,0,7);

 4.2.3 显示图片

        这里的图片可以是软件下的也可以是将图片转换为变成——,而且大小应该在128*64内。

       

         本次重点讲解如何把读者所需要的上,使用的软件为

        打开软件后如下进行设置,之后,可以得到一个

        样例图片(分辨率50*59):

unsigned char BMP1[] = { 0X32,0X01,0X00,0X3B,0X00,0X3B,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X80,0XF8,
0XFC,0X78,0X70,0X30,0X00,0X08,0X00,0X04,0X04,0X04,0X04,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X80,0XC8,0XC8,0XE8,0XF0,0XF0,0XF0,0X00,0X20,0X40,0X40,
0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X60,0X10,0X0C,0X02,0X01,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X70,0X00,0X00,0X00,0X80,0X00,0X00,0X00,0X00,0X00,
0X00,0X01,0X01,0X03,0X03,0X07,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0X06,0X08,0X30,
0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X18,0X13,0X10,
0X08,0X0C,0X0E,0X20,0X00,0X10,0X10,0X08,0X04,0X06,0X01,0X20,0X20,0X10,0X08,0X04,
0X01,0X00,0X00,0X00,0X00,0X01,0X06,0X18,0X20,0X60,0X78,0X00,0X00,0X00,0X40,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7E,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0X80,0X00,
0X00,0X00,0X00,0X00,0X07,0X07,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X0C,0X1C,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X02,0X01,0X01,
0X42,0X7C,0X00,0X80,0X20,0X10,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0X06,0X04,0X08,0X10,0X30,0X20,
0X20,0X40,0X41,0XC0,0XC0,0X80,0X81,0X81,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X20,0X20,0X18,0X08,0X04,0X00,0X00,0X0C,0X04,0X02,0X01,0X03,0X03,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X80,0XC0,0X40,0X60,0X20,0X10,0X10,0X08,0X04,0X04,0X02,0X02,0X02,0X01,0X03,0X03,
0X02,0X03,0X03,0X02,0X02,0X04,0X04,0X0C,0X08,0X10,0X30,0X20,0X40,0X80,0X80,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X40,0X70,0XFC,0XF0,0X60,
0X00,0X00,0X00,0X00,0X00,0X00,0X10,0X08,0X04,0X02,0X03,0X01,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0X03,0X06,0X08,0X30,0X60,0XC0,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X80,0XC0,0X61,0X3F,0X0C,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0X01,
0X01,0X01,0X00,0X00,0X00,0X00,0X00,0X00,};

        如果大家图片不能被打开处理,可能是图片类型不对,可以借助PNG转JPG - 在线转换图像文件 (aconvert.com)进行处理转换。

        显示图片的API函数:

// Parameters     : x0,y0 -- 起始点坐标(x0:0~127, y0:0~7); x1,y1 -- 起点对角线(结束点)的坐标(x1:1~128,y1:1~8)
// 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++]);
		}
	}
}

		OLED_DrawBMP(30,0,89,8,BMP1);

       

  4.2.3 显示动态图片

        一般情况下的大小都是在OLED显示的,所以需要进行大小亦或是帧数变化操作。这里读者推荐使用:

         之后采用进行每张图片的提取,笔者这里使用的是:

后的图片:

        之后就是了,这里就给大家省略了。

       

/*
	@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_DrawGIF(30,2,78,8,12,294,BMP2);			

        这里取模的了,取模实在太烦了,所以就少取了很多帧数的模。如果诸位有批量取模的软件请务必告知,感谢!

OLED动态火苗

        如果读者对笔者文章所述的有需要的可以,笔者可以提供,

标签: 面板led连接器dc二极管2c3蓝双色二极管高速双二极管high

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台