基于STM32单片机模块练习——OLED模块

基于STM32单片机模块练习——OLED模块

相关知识点

向OLED写一个字节

/**
  * @brief  I2C_WriteByte,向OLED寄存器地址写一个byte的数据
  * @param  addr:寄存器地址
	*					data:要写入的数据
  * @retval 无
  */
void I2C_WriteByte(uint8_t addr,uint8_t data)
	{
	
	i2c_Start();//开启I2C总线
	
	/* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */
	i2c_SendByte(OLED_ADDRESS|OLED_I2C_WR);
	
	/*等待ACK */
	if (i2c_WaitAck() != 0)
	{
		goto cmd_fail;	/* OLED器件无应答 */
	}
		
	i2c_SendByte(addr);//0x00表示发送命令,0x40表示发送数据
	
	/*等待ACK */
	if (i2c_WaitAck() != 0)
	{
		goto cmd_fail;	/* OLED器件无应答 */
	}

	i2c_SendByte(data);//发送数据
	
	/*等待ACK */
	if (i2c_WaitAck() != 0)
	{
		goto cmd_fail;	/* OLED器件无应答 */
	}	
	
 /* 发送I2C总线停止信号 */
	i2c_Stop();

cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
	/* 发送I2C总线停止信号 */
	i2c_Stop();

}

OLED初始化函数
在这里插入图片描述

void OLED_Init(void)
{
	Delay_s(1);		// 1s,这里的延时很重要,上电后延时,没有错误的冗余设计
	               //等待SSD1306的上电初始化
	WriteCmd(0xAE); //关闭有机发光二极管面板显示器

	WriteCmd(0x20);	//设置内存寻址模式


	WriteCmd(0x10);	//00,水平寻址模式;01,垂直寻址模式;10,页寻址模式;11,Invalid
	WriteCmd(0xb0);	//通过命令B0h至B7h设置目标显示位置的页面起始地址
	WriteCmd(0xc8);	//Set COM Output Scan Direction/一旦发出该命令,显示屏将显示
	WriteCmd(0x00); //该命令为页面寻址模式下的显示数据内存指定8位列起始地址的低位半字节。列地址将随着每次数据访问而递增。
	WriteCmd(0x10); //该命令为页面寻址模式下的显示数据内存指定8位列起始地址的高位半字节。列地址将随着每次数据访问而递增。
	WriteCmd(0x40); //该命令设置显示起始行寄存器,通过选择0到63之间的一个值来确定显示内存的起始地址。
	WriteCmd(0x81); //-该命令设置显示器的对比度(亮度)设置
	WriteCmd(0xff); //亮度调节 0x00~0xff
	WriteCmd(0xa1); //列地址127被映射到SEG0


	WriteCmd(0xa6); //内存中正常显示(复位)0:显示面板中关闭1:显示面板中打开


	WriteCmd(0xa8); //--set multiplex ratio(1 to 64)
	WriteCmd(0x3F); //????????
	WriteCmd(0xa4); //A4h命令从整个显示“开”阶段恢复显示。A5h命令强制整个显示器为“开”,不管显示数据内存的内容如何。

	WriteCmd(0xd3); //这是一个双字节命令。第二个命令指定显示起始行到COM0~COM63之一的映射(假设COM0是显示起始行,则显示起始行寄存器等于0)。


	WriteCmd(0x00); //-not offset
	WriteCmd(0xd5); //设置显示时钟分频比/振荡器频率


	WriteCmd(0xf0); //--set divide ratio????????
	WriteCmd(0xd9); //该命令用于设置预充电周期的持续时间。间隔以DCLK数计算,其中复位等于2千位。


	WriteCmd(0x22); //??????????
	WriteCmd(0xda); //--set com pins hardware configuration?????
	WriteCmd(0x12);//??????????
	WriteCmd(0xdb); //设置VCOMH取消选择级别????
	WriteCmd(0x20); //0x20,0.77xVcc?????????
	WriteCmd(0x8d); //--set DC-DC enable电荷泵设置


	WriteCmd(0x14); //启用电荷泵


	WriteCmd(0xaf); //正常模式下显示开


	OLED_Fill(0x00);//全屏灭
}


/**
  * @brief  WriteCmd,向OLED写入命令
  * @param  I2C_Command:命令代码
  * @retval 无
  */
void WriteCmd(unsigned char I2C_Command)//写命令
{
	I2C_WriteByte(0x00, I2C_Command);   //0x00,表示发送的数据是命令
}                                     //0x40,表示发送的是数据

使用前先进行设备的检测

/*
*********************************************************************************************************
*	函 数 名: OLED检测测试
*	功能说明: 检测I2C总线设备,实际是对OLED_CheckDevice()的封装
*	形    参:
*	返 回 值: 返回值 0 表示没有检测到OLED,返回1表示检测到OLED
*********************************************************************************************************
*/

 uint8_t OLED_Test(void) 
{
  if (OLED_CheckDevice(OLED_ADDRESS) == 1)//返回值为1,表示没有从机设备应答
	{
		return 0;
	}
	else
	{
		return 1;
	}
}	
/*
*********************************************************************************************************
*	函 数 名: i2c_CheckDevice
*	功能说明: 检测I2C总线设备,CPU向发送设备地址,然后读取设备应答来判断该设备是否存在
*	形    参:_Address:设备的I2C总线地址
*	返 回 值: 返回值 0 表示正确, 返回1表示未探测到
*********************************************************************************************************
*/
uint8_t OLED_CheckDevice(uint8_t _Address)//_Address为从机设备地址
{
	uint8_t ucAck;
	
	i2c_Start();		/* 发送启动信号 */

	i2c_SendByte(_Address|OLED_I2C_WR);/* 发送设备地址 */
	ucAck = i2c_WaitAck();	/* 检测设备的ACK应答 */

	i2c_Stop();			/* 发送停止信号 */

	return ucAck;
}

全屏填充

/**
  * @brief  OLED_Fill,填充整个屏幕
  * @param  fill_Data:要填充的数据
	* @retval 无
  */
void OLED_Fill(unsigned char fill_Data)//全屏填充
{
	unsigned char m,n;
	for(m=0;m<8;m++)
	{
		WriteCmd(0xb0+m);		通过命令B0h至B7h设置目标显示位置的页面起始地址
		WriteCmd(0x00);	//0	//该命令为页面寻址模式下的显示数据内存指定8位列起始地址的低位半字节。列地址将随着每次数据访问而递增。
		WriteCmd(0x10);	//128	//该命令为页面寻址模式下的显示数据内存指定8位列起始地址的高位半字节。列地址将随着每次数据访问而递增。
		for(n=0;n<128;n++)
			{
				WriteDat(fill_Data);//0x00,清空屏幕;0xff,全屏点亮;
			}
	}
}


/**
  * @brief  WriteDat,向OLED写入数据
  * @param  I2C_Data:数据
  * @retval 无
  */
void WriteDat(unsigned char I2C_Data)//写数据
{
	I2C_WriteByte(0x40, I2C_Data); //0x40,表示发送的是数据
}                                   //0x00,表示发送的数据是命令

显示字符串函数

/**
  * @brief  OLED_SetPos,设置光标
  * @param  x,光标x位置
	*					y,光标y位置
  * @retval 无
  */

void OLED_SetPos(unsigned char x, unsigned char y) //设置起始点坐标
{ 
	WriteCmd(0xb0+y);通过命令B0h至B7h设置目标显示位置的页面起始地址
	WriteCmd(((x&0xf0)>>4)|0x10);//指定格式为:0x1X;该命令为页面寻址模式下的显示数据内存指定8位列起始地址的高4位。列地址将随着每次数据访问而递增。
	WriteCmd((x&0x0f));//指定格式为:0x0X;该命令为页面寻址模式下的显示数据内存指定8位列起始地址的高4位。列地址将随着每次数据访问而递增。
}
**
  * @brief  OLED_ShowStr,显示codetab.h中的ASCII字符,6*88*16可选择
  * @param  x,y : 起始点坐标(x:0~127, y:0~7);
	*					ch[] :- 要显示的字符串; 
	*					TextSize : 字符大小(1:6*8 ; 2:8*16)** @retval 无
  */
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)//因为列最大就是127,所以当x达到>126时,证明已经把这一页全部写完,该换页了
				{
					x = 0;
					y++;//页面换到下一页
				}
				OLED_SetPos(x,y);
				for(i=0;i<6;i++)
					WriteDat(F6x8[c][i]);//[c]是当前字符在字库中所在的行数,因为大小是6*8的,所以有六个字节需要发送
				x += 6;//因为一个字符占用6列,所以起始列地址应该在原来的基础上加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;
	}
}

在这里插入图片描述

在正常显示数据随机存取存储器读或写和页面寻址模式下,需要以下步骤来定义起始随机存取存储器访问指针位置:
■通过命令B0h到B7h设置目标显示位置的页面起始地址。
■通过命令00h-0Fh设置指针的起始列地址的低4位。
■通过命令10h~1Fh设置指针的起始列地址的高4位。
以上三个步骤,顺序可以随便调换

让OLED进入休眠

/**
  * @brief  OLED_OFF,让OLED休眠 -- 休眠模式下,OLED功耗不到10uA
  * @param  无
	* @retval 无
  */
void OLED_OFF(void)
{
	WriteCmd(0X8D);  //设置电荷泵
	WriteCmd(0X10);  //关闭电荷泵
	WriteCmd(0XAE);  //OLED休眠
}

唤醒OLED

/**
  * @brief  OLED_ON,将OLED从休眠中唤醒
  * @param  无
	* @retval 无
  */
void OLED_ON(void)
{
	WriteCmd(0X8D);  //设置电荷泵
	WriteCmd(0X14);  //开启电荷泵
	WriteCmd(0XAF);  //OLED唤醒
}

显示汉字函数

/**
  * @brief  OLED_ShowCN,显示codetab.h中的汉字,16*16点阵
  * @param  x,y: 起始点坐标(x:0~127, y:0~7); 
	*					N:汉字在codetab.h中的索引,也就是第几个汉字
	* @retval 无
  */
void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N)
{
	unsigned char wm=0;
	unsigned int  adder=32*N;//写一个汉字需要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;
	}
	
}
//注意:此函数没有为显示下一个汉字做相应的列地址更换准备,需要在调用的时候,自行更换
for(i=0;i<4;i++)//测试显示中文
		{
			OLED_ShowCN(22+i*16,0,i);//22为起始列地址,i*16为更换列地址,为发送下一个汉字做准备
		}

取模软件选项设置
在这里插入图片描述
显示一个字符函数


/**
  * @brief  OLED_ShowChar,显示codetab.h中的ASCII字符,有6*8和8*16可选择
  * @param  x,y : 起始点坐标(x:0~127, y:0~7);
	*					ch:- 要显示的字符; 
	*					TextSize : 字符大小(1:6*8 ; 2:8*16)列*行
	* @retval 无
  */
void OLED_ShowChar(unsigned char x, unsigned char y, unsigned char ch, unsigned char TextSize)
{
	unsigned char c = 0,i = 0;
	switch(TextSize)
	{
		case 1:
		{
			if(ch != '\0')
			{
				c = ch - 32;//当前字符相对于' '的偏移量
				
				if(x > 126)//因为列最大就是127,所以当x达到>126时,证明已经把这一页全部写完,该换页了
				{
					x = 0;
					y++;//页面换到下一页
				}
				OLED_SetPos(x,y);
				for(i=0;i<6;i++)
					WriteDat(F6x8[c][i]);//[c]是当前字符在字库中所在的行数,因为大小是6*8的,所以有六个字节需要发送
				
			}
		}break;
		case 2:
		{
			if(ch != '\0')
			{
				c = ch - 32;
				if(x > 126)//留够写下最后一个字符的列数
				{
					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]);
				
			}
		}break;
	}
}

显示一个数字函数

//m^n函数
uint32_t oled_pow(uint8_t m,uint8_t n)
{
	uint32_t result=1;	 
	while(n--)result*=m;    
	return result;
}


//x,y :起点坐标	 
//len :数字的位数
//size:字体大小
//num:数值(0~4294967295);	 		  
void OLED_ShowNum(uint8_t x,uint8_t y,uint32_t num,uint8_t len,uint8_t size)
{         	
	uint8_t t,temp;
	uint8_t enshow=0;						   
	for(t=0;t<len;t++)//一位一位地分步发送这个数字
	{
		temp=(num/oled_pow(10,len-t-1))%10;//将这个数字拆分开.比如:23拆成2 和 3 ;如果将10换成2,那么就可以把数字以二进制的形式显示;如果len的长度大于实际的长度,比如输入66,len却是3,那么函数把它拆分成0 6 6,依次发送
		if(enshow==0&&t<(len-1))//
		{
			if(temp==0)//由于len的输入值大于所要显示的数字的实际位数,那么数字前面会多出0,这条语句就会把0给显示为“ ”
			{
				
		switch(size)//按显示的大小尺寸进行选择
		{
			case 1 : OLED_ShowChar((x+6*t),y,' ',size);break;//6*8
			case 2 : OLED_ShowChar((x+8*t),y,' ',size);break;//8*16
		}
				continue;
			}else enshow=1; //如果第一个需要显示的字符不是0的话,那么证明接下来的所有数字都是有效的,都不需要这个if语句来显示‘ ’
		 	 
		}
	 
	
	  	switch(size)
		{
			case 1 : OLED_ShowChar((x+6*t),y,(temp + '0'),size);;break;//6*8
			case 2 : OLED_ShowChar((x+8*t),y,(temp + '0'),size);;break;//8*16
		}
	}
} 

显示图片函数(有待研究)

/**
  * @brief  OLED_DrawBMP,显示BMP位图
  * @param  x0,y0 :起始点坐标(x0:0~127, y0:0~7);
	*					x1,y1 : 起点对角线(结束点)的坐标(x1:1~128,y1:1~8)
	* @retval 无
  */
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_ShowNum(uint8_t x,uint8_t y,float num,uint8_t int_len,uint8_t f_len,uint8_t size)
{         	
	uint8_t t,temp;
	uint8_t enshow=0;	
    int int_temp,f_temp;
	//*******只有输入负数的情况下才显示负号,否则显示空格********
	if(num < 0)
	{
		num = 0 - num;//将负数取反,变为正数,接下来显示它的绝对值
		switch(size)
		{
			case 1 : OLED_ShowChar(x,y,'-',size);break;//6*8
			case 2 : OLED_ShowChar(x,y,'-',size);break;//8*16
		}
		
	}
	//**********************************************************
	
    int_temp = (int)num;//取这个数字的整数部分
	
	//**********************这一块部分与普通显示数字的部分相同**************************
	for(t=0;t<int_len;t++)//一位一位地分步发送这个数字
	{
		temp=(int_temp/oled_pow(10,int_len-t-1))%10;//将这个数字拆分开.比如:23拆成2 和 3 
		if(enshow==0&&t<(int_len-1))
		{
			if(temp==0)
			{
				
		switch(size)
		{
			case 1 : OLED_ShowChar((x+6*(t+1)),y,' ',size);break;//6*8
			case 2 : OLED_ShowChar((x+8*(t+1)),y,' ',size);break;//8*16
		}
				continue;
			}else enshow=1; 
		 	 
		}
	 
	
	  	switch(size)
		{
			case 1 : OLED_ShowChar((x+6*(t+1)),y,(temp + '0'),size);;break;//6*8
			case 2 : OLED_ShowChar((x+8*(t+1)),y,(temp + '0'),size);;break;//8*16
		}
	}
	//*****************************************************************************
	
      if(f_len != 0)//如果希望显示小数部分,那么就先显示一个‘.’
			{
				switch(size)
		{
			case 1 : OLED_ShowChar((x+6*(int_len+1)),y,'.',size);;break;//6*8
			case 2 : OLED_ShowChar((x+8*(int_len+1)),y,'.',size);;break;//8*16
		}
			}
			
			f_temp = (int)((num - int_temp)*(oled_pow(10,f_len)));//将小数部分取出,并扩大为整数,方便显示
			for(t = 0; t < f_len; t++)//将小数部分一位一位的显示
			{
				temp=(f_temp/oled_pow(10,f_len-t-1))%10;//拆分为单个数字
				switch(size)
		{
			case 1 : OLED_ShowChar((x+6*(t+int_len+2)),y,(temp + '0'),size);;break;//6*8
			case 2 : OLED_ShowChar((x+8*(t+int_len+2)),y,(temp + '0'),size);;break;//8*16
		}
			}
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ღ 金龍戲水 ღ

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值