STM32学习之HAL库模拟IIC实现1.3寸OLED显示

1 OLED原理

1.3寸oled的显示原理与0.96寸的差不多,这里以常见的0.96寸oled来学习。 向下内容可以参考这边文章:

https://blog.csdn.net/qq_36461474/article/details/112591234

2 I2C协议

I2C协议讲解网线内容很多,可以参考下面这篇文章:

https://blog.csdn.net/XiaoCaiDaYong/article/details/106251696

I2C代码调试

1.1 引脚电平更改函数

这里使用(GPIO_PinState)将BitValue强制转化为GPIO_PinState枚举中的值。
这里如果要移植的化,只要将下面的GPIOB以及I2C1_SCL_Pin根据自己的使用的引脚进行更改即可。

void OLED_W_SCL(uint8_t BitValue)
{
	HAL_GPIO_WritePin(GPIOB, I2C1_SCL_Pin, (GPIO_PinState)BitValue);
}

void OLED_W_SDA(uint8_t BitValue)
{
	HAL_GPIO_WritePin(GPIOB, I2C1_SDA_Pin, (GPIO_PinState)BitValue);
}

1.2 起始信号

起始信号代码:

void IIC_Start(void)
{
	OLED_W_SCL(1);
	OLED_W_SDA(1);
	Delay_us(2);
	OLED_W_SDA(0);
	Delay_us(2);
}

测试代码,在main函数的while循环中调用IIC_Start

while (1)
{
	  IIC_Start()}

使用逻辑分析查看输出波形,这里SCL一直为高电平,SDA电平被拉高后,出现低电平。可以看到输出的SDA高电平时长也是延时的2u。这里符合I2C协议中起始信号,SCL为高时,SDA由高电平转化低电平。
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/e9c5e307727d419dafd0c375a1f7034e.png

1.3 停止信号

停止信号代码

void IIC_Stop(void)
{
	OLED_W_SDA(0);
	OLED_W_SCL(1);
	Delay_us(2);
	OLED_W_SDA(1);
	Delay_us(2);
}

测试代码

while (1)
{
	  IIC_Stop()}

逻辑分析仪查看输出波形,SCL为高点,SDA一开始为未知信号,执行代码后,SDA被拉低2u秒,然后再跳转为高电平。符合I2C停止信号的,SCL为高电平,SDA由低电平转化为高电平。
在这里插入图片描述

1.4 发送数据代码

代码实现

void IIC_Send_Byte(uint8_t Byte)
{
	uint8_t i;
	OLED_W_SCL(0);
	for(i=0; i<8; i++)
	{
		OLED_W_SDA(Byte & (0x80 >> i));
		Delay_us(2);
		OLED_W_SCL(1);
		Delay_us(2);
		OLED_W_SCL(0);
		Delay_us(2);		
	}	
	OLED_W_SCL(1);
	Delay_us(2);
	OLED_W_SCL(0);
}

测试代码

while (1)
{
	  IIC_Send_Byte(0xaa);
}

逻辑分析仪查看输出波形,0xaa,的2进制为10101010,从波形上看,在SCL为高电平时,SDA为10101010,SCL为低电平时,SDA实现电平更改,在最后实现了代码中循环完成后的一个ACK。
在这里插入图片描述

1.5 IIC数据发送的整个过程

这里使用了I2C发送数据和发送指令两个代码:

void IIC_Write_Command(uint8_t IIC_Command)
{
	IIC_Start();
	IIC_Send_Byte(0x78);
	IIC_Send_Byte(0x00);
	IIC_Send_Byte(IIC_Command);
	IIC_Stop();
}

void IIC_Write_Data(uint8_t IIC_Data)
{
	IIC_Start();
	IIC_Send_Byte(0x78);
	IIC_Send_Byte(0x40);
	IIC_Send_Byte(IIC_Data);
	IIC_Stop();
}

测试代码
while (1)
{
IIC_Write_Data(0xaa);
}
逻辑分析仪查看输出波形,发送数据整个波形,先发送地址,然后发送显示屏显存地址,最后是发送的数据。在这里ACK由于我们在IIC_Send_Byte中循环完成后加了ACK,所以可以看到每发送一次Byte都会有一个ACK。
在这里插入图片描述

2 所有代码

代码中有部分为一直官方的代码,但有些小改动

2.1 代码改动部分

这里添加了代码无效的处理
在这里插入图片描述
这里在测试时发现,如果不 -size2,显示的数字无法实现在(0,x)位置显示(x为任意值),现象就是显示数字显示位置为(size2,x)。
在这里插入图片描述

2.2 所有代码

#include "oled.h"
#include "oledfont.h"

void OLED_W_SCL(uint8_t BitValue)
{
	HAL_GPIO_WritePin(GPIOB, I2C1_SCL_Pin, (GPIO_PinState)BitValue);
}

void OLED_W_SDA(uint8_t BitValue)
{
	HAL_GPIO_WritePin(GPIOB, I2C1_SDA_Pin, (GPIO_PinState)BitValue);
}

void IIC_Start(void)
{
	OLED_W_SCL(1);
	OLED_W_SDA(1);
	Delay_us(2);
	OLED_W_SDA(0);
	Delay_us(2);
}

void IIC_Stop(void)
{
	OLED_W_SDA(0);
	OLED_W_SCL(1);
	Delay_us(2);
	OLED_W_SDA(1);
	Delay_us(2);
}

void IIC_Send_Byte(uint8_t Byte)
{
	uint8_t i;
	OLED_W_SCL(0);
	for(i=0; i<8; i++)
	{
		OLED_W_SDA(Byte & (0x80 >> i));
		Delay_us(2);
		OLED_W_SCL(1);
		Delay_us(2);
		OLED_W_SCL(0);
		Delay_us(2);		
	}	
	OLED_W_SCL(1);
	Delay_us(2);
	OLED_W_SCL(0);
}

void IIC_Write_Command(uint8_t IIC_Command)
{
	IIC_Start();
	IIC_Send_Byte(0x78);
	IIC_Send_Byte(0x00);
	IIC_Send_Byte(IIC_Command);
	IIC_Stop();
}

void IIC_Write_Data(uint8_t IIC_Data)
{
	IIC_Start();
	IIC_Send_Byte(0x78);
	IIC_Send_Byte(0x40);
	IIC_Send_Byte(IIC_Data);
	IIC_Stop();
}

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


//坐标设置
void OLED_Set_Pos(unsigned char x, unsigned char y) 
{ 	
	IIC_Write_Command(0xb0+y);
	IIC_Write_Command(((x&0xf0)>>4)|0x10);
	IIC_Write_Command((x&0x0f)|0x01); 
}   	  
//开启OLED显示    
void OLED_Display_On(void)
{
	IIC_Write_Command(0X8D);  //SET DCDC命令
	IIC_Write_Command(0X14);  //DCDC ON
	IIC_Write_Command(0XAF);  //DISPLAY ON
}
//关闭OLED显示     
void OLED_Display_Off(void)
{
	IIC_Write_Command(0X8D);  //SET DCDC命令
	IIC_Write_Command(0X10);  //DCDC OFF
	IIC_Write_Command(0XAE);  //DISPLAY OFF
}		   			 
//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!	  
void OLED_Clear(void)  
{  
	uint8_t i,n;		    
	for(i=0;i<8;i++)  
	{  
		IIC_Write_Command (0xb0+i);    //设置页地址(0~7)
		IIC_Write_Command (0x02);      //设置显示位置—列低地址
		IIC_Write_Command (0x10);      //设置显示位置—列高地址   
		for(n=0;n<128;n++)IIC_Write_Data(0); 
	} //更新显示
}
void OLED_On(void)  
{  
	uint8_t i,n;		    
	for(i=0;i<8;i++)  
	{  
		IIC_Write_Command(0xb0+i);    //设置页地址(0~7)
		IIC_Write_Command(0x02);      //设置显示位置—列低地址
		IIC_Write_Command(0x10);      //设置显示位置—列高地址   
		for(n=0;n<128;n++)IIC_Write_Data(1); 
	} //更新显示
}
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示				 
//size:选择字体 16/12 
void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t Char_Size)
{      	
	unsigned char c=0,i=0;
	c = chr - ' ';
	if (chr < ' ' || chr > '~') 
	{
    return; // 如果字符无效,直接返回
	}			
	if(x>Max_Column-1){x=0;y=y+2;}
	if(Char_Size ==16)
	{
		OLED_Set_Pos(x,y);	
		for(i=0;i<8;i++)
		IIC_Write_Data(F8X16[c*16+i]);
		OLED_Set_Pos(x,y+1);
		for(i=0;i<8;i++)
		IIC_Write_Data(F8X16[c*16+i+8]);
	}
	else
	{	
			OLED_Set_Pos(x,y);
			for(i=0;i<6;i++)
			IIC_Write_Data(F6x8[c][i]);
			
	}
}
//m^n函数
uint32_t oled_pow(uint8_t m,uint8_t n)
{
	uint32_t result=1;	 
	while(n--)result*=m;    
	return result;
}				  
//显示2个数字
//x,y :起点坐标	 
//len :数字的位数
//size:字体大小
//num:数值(0~4294967295);	 		  
void OLED_ShowNum(uint8_t x,uint8_t y,uint32_t num,uint8_t len,uint8_t size2)
{         	
	uint8_t t,temp;
	uint8_t enshow=0;						   
	for(t=0;t<len;t++)
	{
		temp=(num/oled_pow(10,len-t-1))%10;
		if(enshow==0&&t<(len-1))
		{
			if(temp==0)
			{
				OLED_ShowChar(x+(size2/2)*t,y,' ',size2);
				continue;
			}else enshow=1; 
		 	 
		}
	 	OLED_ShowChar(x+(size2/2)*t -size2,y,temp+'0',size2); 
	}
} 
//显示一个字符号串
void OLED_ShowString(uint8_t x,uint8_t y,uint8_t *chr,uint8_t Char_Size)
{
	unsigned char j=0;
	while (chr[j]!='\0')
	{		OLED_ShowChar(x,y,chr[j],Char_Size);
			x+=8;
		if(x>120){x=0;y+=2;}
			j++;
	}
}
//显示汉字
void OLED_ShowCHinese(uint8_t x,uint8_t y,uint8_t no)
{      			    
	uint8_t t,adder=0;
	OLED_Set_Pos(x,y);	
    for(t=0;t<16;t++)
	{
		IIC_Write_Data(Hzk[2*no][t]);
		adder+=1;
    }	
	OLED_Set_Pos(x,y+1);	
    for(t=0;t<16;t++)
	{	
		IIC_Write_Data(Hzk[2*no+1][t]);
		adder+=1;
    }					
}
/***********功能描述:显示显示BMP图片128×64起始点坐标(x,y),x的范围0~127,y为页的范围0~7*****************/
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_Set_Pos(x0,y);
    for(x=x0;x<x1;x++)
	    {      
	    	IIC_Write_Data(BMP[j++]);	    	
	    }
	}
} 

void OLED_Init(void)
{ 	
	IIC_Write_Command(0xAE);//--display off
	IIC_Write_Command(0x02);//---set low column address
	IIC_Write_Command(0x10);//---set high column address
	IIC_Write_Command(0x40);//--set start line address  
	IIC_Write_Command(0xB0);//--set page address
	IIC_Write_Command(0x81); // contract control
	IIC_Write_Command(0xFF);//--128   
	IIC_Write_Command(0xA1);//set segment remap 
	IIC_Write_Command(0xA6);//--normal / reverse
	IIC_Write_Command(0xA8);//--set multiplex ratio(1 to 64)
	IIC_Write_Command(0x3F);//--1/64 duty
	IIC_Write_Command(0xAD);//set charge pump enable
	IIC_Write_Command(0x8B);//-0x8B 内供 VCC
	IIC_Write_Command(0x33);//-0X30---0X33 set VPP 9V
	IIC_Write_Command(0xC8);//Com scan direction
	IIC_Write_Command(0xD3);//-set display offset
	IIC_Write_Command(0x00);//
	
	IIC_Write_Command(0xD5);//set osc division
	IIC_Write_Command(0x80);//
	
	IIC_Write_Command(0xD8);//set area color mode off
	IIC_Write_Command(0x05);//
	
	IIC_Write_Command(0xD9);//Set Pre-Charge Period
	IIC_Write_Command(0x1F);//
	
	IIC_Write_Command(0xDA);//set com pin configuartion
	IIC_Write_Command(0x12);//
	
	IIC_Write_Command(0xDB);//set Vcomh
	IIC_Write_Command(0x40);//
	
	
	IIC_Write_Command(0xAF);//--turn on oled panel
	
	OLED_Clear();
}  

头文件

#ifndef __OLED_H
#define __OLED_H

#include "stm32f4xx_hal.h"
#include "main.h"
#include "delay.h"
#include "stdlib.h"	


#define OLED_MODE 0
#define SIZE 8
#define XLevelL		0x00
#define XLevelH		0x10
#define Max_Column	128
#define Max_Row		64
#define	Brightness	0xFF 
#define X_WIDTH 	128
#define Y_WIDTH 	64	


void OLED_W_SCL(uint8_t BitValue);
void OLED_W_SDA(uint8_t BitValue);
void IIC_Start(void);
void IIC_Stop(void);
void IIC_Send_Byte(uint8_t Byte);
void IIC_Write_Command(uint8_t IIC_Command);
void IIC_Write_Data(uint8_t IIC_Data);

void fill_picture(unsigned char fill_Data);
void OLED_Set_Pos(unsigned char x, unsigned char y) ;
void OLED_Display_On(void);
void OLED_Display_Off(void);
void OLED_Clear(void) ;
void OLED_On(void);

void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t Char_Size);
uint32_t oled_pow(uint8_t m,uint8_t n);
void OLED_ShowNum(uint8_t x,uint8_t y,uint32_t num,uint8_t len,uint8_t size2);
void OLED_ShowString(uint8_t x,uint8_t y,uint8_t *chr,uint8_t Char_Size);
void OLED_ShowCHinese(uint8_t x,uint8_t y,uint8_t no);
void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[]);
void OLED_Init(void);

#endif




### HAL与51单片机结合实现OLED显示IIC接口驱动 对于希望利用HAL在51单片机实现对带有IIC接口的OLED显示屏进行驱动的需求,需要注意的是,ST公司的HAL主要针对其自家生产的STM32系列微控制器设计,并未专门为51单片机制作相应的支持包。然而,在理解了如何使用HAL中的通用概念之后,可以通过移植部分功能来适应51单片机平台。 #### 移植思路 为了使基于HAL开发的经验能够应用于51单片机项目中,建议采取如下策略: - **分析现有代码结构**:仔细研究已有的STM32 HAL源码,特别是那些处理IIC通信的部分以及与OLED交互的具体函数[^1]。 - **适配底层硬件操作**:虽然无法直接调用STM32 HAL,但是可以根据这些的设计理念重新编写适用于51单片机的低级APIs。这通常涉及到定义新的寄存器映射、设置GPIO模式等功能[^2]。 - **构建类似的高层抽象层**:模仿HAL的方式建立一套面向对象风格的应用程序编程接口(API),以便于后续应用程序逻辑的编码工作更加简便高效。例如,创建类似于`HAL_I2C_Master_Transmit()`这样的封装函数用于简化数据传输过程[^3]。 #### 实现要点 当考虑具体实施细节时,有几个方面值得特别关注: - **初始化配置** - **命令发送流程** - 启动START条件通知从设备准备接收地址字节; - 发送写入方向位并等待应答信号确认连接成功; - 接着传送控制指令给OLED模块完成特定任务(比如清屏、移动光标位置等); - **图像渲染算法** - 将待显示的内容转换成适合OLED像素矩阵格式的数据流; - 组织好缓冲区内的图形信息并通过上述提到的方法逐帧更新至屏幕上; ```c // 示例代码片段展示了一个简单的字符打印函数 void oled_print_char(unsigned char c){ unsigned int i; for(i=0; i<8; ++i){ // 假设字体宽度固定为8px if(font[c][i]){ // font数组存储ASCII对应图案模板 send_data_to_oled(0x01); // 这里假设send_data_to_oled()负责实际的数据传递动作 }else{ send_data_to_oled(0x00); } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值