【OLED驱动函数详解】

前提

SSD1306是一款带控制器的单片CMOS OLED/PLED驱动,用于OLED点阵图形显示系统。SSD1306 MCU接口由8个数据引脚和5个控制引脚组成。下表总结了不同接口模式下的引脚分配。可以通过BS[2:0]引脚上的硬件选择来设置不同的MCU模式
请添加图片描述

通讯方式

可以通过BS[2:0]引脚上的硬件选择来设置不同的MCU模式,本文选择的是IIC通讯。对于模拟iic,大家可以参考一下这篇文章,很详细的:IIC的基本原理
请添加图片描述

地址排列

OLED本身是没有显存的,它的显存是依赖于SSD1306提供的。SSD1306的显存总共为128 * 64bit大小,SSD1306将这些显存分为了8页。每页向下偏移8bit,并包含128个字节,总共8页,这样刚好是8*8*64=64*128的点阵大小。所以行地址范围为0-7,列地址范围为0-127,对应下文驱动函数的Y轴和X轴。地址如下排序
请添加图片描述

寻址方式

想要在OLED中正常显示(0/1),必须将对应的显存地址给定相应的数据。这就涉及到了寻址方式。本文采用的寻址方式是水平寻址,在水平寻址模式中,在读取/写入显示RAM之后,列地址指针自动增加1。如果列地址指针到达列结束地址,则将列地址指针重置为列开始地址,并将页地址指针增加1。水平寻址模式下页面和列地址点的移动顺序如图所示。当列和页地址指针都到达结束地址时,指针将重置为列起始地址和页起始地址。字库点阵的显示方式需要和采用的寻址方式一致,不然会显示错误。

请添加图片描述

地址我们可以找到了,该如何写入数据呢?当我们定位到一个地址后(x,y)在物理空间上高位在下,低位在上写入8bit数据,刚好一页就是8bit,所以当前页的当前列的数据就完全写入了。如下图所示便是在(3,2)处写入0xF0的结果。

请添加图片描述

以上内容均可从SSD1306数据手册中获得:SSD1306

正文

详细说明一下各种格式的驱动函数

初始化

//#include "i2c.h"
#include "IIC.h"
#include "oledfont.h"
#include "stm32f1xx_hal.h"
#include "mpu6050.h"
void WriteCmd(unsigned char I2C_Command)
 	{
	//IIC写命令函数
	 MPU_Write_Len(OLED0561_ADD>>1,COM,1,&I2C_Command);
 	}
		
void WriteDat(unsigned char I2C_Data)
	{
	//IIC写数据函数
	 MPU_Write_Len(OLED0561_ADD>>1,DAT,1,&I2C_Data);
  	}
//初始化函数,直接copy就行,想进一步理解得啃数据手册
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);		//设置页地址(0~7)
		WriteCmd(0x00);			//设置显示位置—列低地址
		WriteCmd(0x10);			//设置显示位置—列高地址   
		for(n=0;n<128;n++)
			{
				WriteDat(fill_Data);
			}
	}
}
//清屏
void OLED_CLS(void)
{
	OLED_Fill(0x00);
}
//开启OLED显示  
void OLED_ON(void)
{
	WriteCmd(0X8D);  //SET DCDC命令
	WriteCmd(0X14);  //DCDC ON
	WriteCmd(0XAF);  //DISPLAY ON
}
//关闭OLED显示  
void OLED_OFF(void)
{
	WriteCmd(0X8D);  //SET DCDC命令
	WriteCmd(0X10);  //DCDC OFF
	WriteCmd(0XAE);  //DISPLAY OFF
}

显示一个字符

//x:0~127
//y:0~7				 
//size:选择字体 16:8*16 / 12:6*8
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size)
{      	
	unsigned char c=0,i=0;	
		c=chr-' ';//获取偏移,定位该字符数组起始下标 c*16
		if(x>128-1){ x=0; y=y+2; }//处理越界
		if(Char_Size ==16)
		{	//绘制8*16
			OLED_SetPos(x,y);	
			for(i=0;i<8;i++)
			WriteDat(F8X16[c*16+i]);//8*16的上半部分即8*8
			OLED_SetPos(x,y+1);		//前往下一页,+1即 Y 偏移8位
			for(i=0;i<8;i++)
			WriteDat(F8X16[c*16+i+8]);//8*16的下半部分即8*8
		}
		else {	
			//绘制6*8
			OLED_SetPos(x,y);
			for(i=0;i<6;i++)
			WriteDat(F6x8[c][i]);
		}
}

在指定位置显示一个字符串

//x:0~127
//y:0~7				 
//size:选择字体 16:8*16 / 12:6*8
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 12:
		case 1:						//打印6*8点阵
		{
			while(ch[j] != '\0')	//字符串结束符
			{
				//当前字符减去32(ASCII为'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,y坐标依次打印6*8点阵
				x += 6;
				j++;
			}
		}break;
		case 16:
		case 2:
		{	//打印16*16点阵,同上
			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);	//前往下一页,+1即 Y 偏移8位
				for(i=0;i<8;i++)
					WriteDat(F8X16[c*16+i+8]);
				x += 8;
				j++;
			}
		}break;
	}
}

字符串居左显示

void OLED_ShowStr_Left( unsigned char y, unsigned char ch[], unsigned char TextSize){
		//这个好理解,x设置为0即可
		OLED_ShowStr(0, y, ch, TextSize);
}

字符串居右显示

void OLED_ShowStr_Right( unsigned char y, unsigned char ch[], unsigned char len, unsigned char TextSize){
		//使用方法 OLED_ShowStr_Right(5, name,sizeof (name), 16);
		//sizeof包含了\0,比实际显示的大1,因此减去1
		//TextSize= 16/12 想要居右 X = 128-字符总长度 = 128 - TextSize/2*(len-1)
		OLED_ShowStr ((128 - TextSize/2*(len-1)), y, ch, TextSize);
}

字符串居中显示

void OLED_ShowStr_Center( unsigned char y, unsigned char ch[], unsigned char len, unsigned char TextSize){
	//使用方法 OLED_ShowStr_Center(5, name,sizeof (name), 16);
	//TextSize= 16/12 想要居中 X 为居右时的一半 即 64 - TextSize/4*(len-1)
		OLED_ShowStr ((64 - TextSize/4*(len-1)), y, ch, TextSize);
}

在指定位置显示一个中文字符

//在指定位置显示一个中文字符
//x:0~127
//y:0~7				 
//size2:选择字体 16:8*16 / 12:6*8
void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N)
{
	unsigned char wm=0;
	unsigned int  adder=32*N;	//每个字符占32byte,所以偏移量是32,其实还是数组下标
	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;
	}
}

在指定区域显示图片

//在指定区域显示图片
//x0,y0是左上角坐标,x1,y1是右下角坐标,显示该范围的图像
//x:0~127
//y:0~7				 
//BMP[]:位图数组名
void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[])
{
	
	unsigned char x,y;
	
	for(y=y0;y<y1;y++)
	{
		OLED_SetPos(x0,y);
		for(x=x0;x<x1;x++)
			{
				//Y轴一行是128BIT,加上x的偏移,就是XY处的数据(数组下标)
				WriteDat(BMP[128 * y + x]);
				//WriteDat(BMP[j++]);
			}
	}
}

在指定位置显示一定长度的整数

//返回 M 的 N 次幂
u32 oled_pow(u8 m,u8 n)
{
	u32 result=1;	 
	while(n--)result*=m;    
	return result;
}	
	
//显示数字
//x,y :起点坐标	 
//len :数字的位数
//size2:选择字体 16:8*16 / 12:6*8
//num:数值(0~4294967295);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size2)
{   //总体思想是把数字的每一位提取出来再转化成字符格式用OLED_ShowChar输出。

	u8 t,temp;		//临时存储每位的值
	u8 enshow=0;	//使能输出标志
	//len为整形长度
	for(t=0;t<len;t++)
	{
		//在某进制数中,用某数除以某权位再对基数求余,即可得到该数位的值。
		//例如:用一个十进制整形数除以100,再对10求余可以得到百位的数值
		//      用一个十进制整形数除以 10,再对10求余可以得到十位的数值
		//      用一个十进制整形数除以  1,再对10求余可以得到个位的数值
		//下一行便是以此执行上述步骤,分别取到每数位的值。从高到低
		temp = (num / oled_pow(10,len-t-1)) % 10;
		//无效高位(高位位0)用空格代替
		if(enshow==0 && t<(len-1))
		{
			//某高位为0
			if(temp==0)
			{
				//输出空格
				if(size2 == 16)
					OLED_ShowChar(x+(8)*t,y,' ',size2);
				else 
					OLED_ShowChar(x+(6)*t,y,' ',size2);
				continue;
			}else enshow=1; 
		 	 
		}
		//int转字符ASCII并输出。temp+'0':'0'加上整形的偏移就是阿拉伯数字相应字符型ASCII
		//x+(6)*t	根据不同的字体宽度计算同一行下一个字符偏移
		if(size2 == 16)
			OLED_ShowChar(x+(8)*t,y,temp+'0',size2);
		else 
			OLED_ShowChar(x+(6)*t,y,temp+'0',size2);
	}
}

在指定位置显示一定长度的小数

//显示小数
//x,y :起点坐标	 
//len :整数数字的位数
//size2:选择字体 16:8*16 / 12:6*8
void OLED_ShowFloat(uint8_t x,uint8_t y,float num, uint8_t len, uint8_t size2)
{
	uint32_t temp;
	float num1=num;
	float temp2;
	temp=(int)num1;		//强制转化为整形,保留了小数点之前的数
	OLED_ShowNum(x,y,temp,len,size2);	//显示小数点之前的数字
	temp2=num-temp;						//取出小数部分
	temp2=temp2*100;					//乘以100转化为整数
	temp=(int)(temp2);
	//x+(size2/2)*len根据不同的字体宽度计算同一行下一个字符偏移
	OLED_ShowChar(x+(size2/2)*len,y,'.',size2);//打印小数点
	OLED_ShowNum(x+(size2/2)*(len+1),y,temp,2,size2);//打印小数部分(2位)
}

结束

神功大成。给个链接:oled参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值