这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » Let'sDo第4期【液体流量检测仪】05过程贴:硬件IIC驱动OLED屏幕,并

共1条 1/1 1 跳转至

Let'sDo第4期【液体流量检测仪】05过程贴:硬件IIC驱动OLED屏幕,并显示Helloworld

菜鸟
2025-01-18 21:22:56     打赏

今天和大家分享一下,使用硬件IIC驱动OLED屏幕的开发过程。

一:OLED屏幕知识简单分享

OLED,即有机发光二极管(Organic Light-Emitting Diode),又称为有机电激光显示(Organic Electroluminesence Display, OELD)。因为具备轻薄、省电等特性,因此从 2003 年开始,这种显示设备在 MP3 播放器上得到了广泛应用,而对于同属数码类产品的 DC 与手机,此前只是在一些展会上展示过采用 OLED 屏幕的工程样品。自 2007 年后,寿命得 到很大提高,具备了许多 LCD 不可比拟的优势。

GND:电源地 VCC:2.2V~5.5V SCL(D0):CLK 时钟 (高电平 2.2V~5.5V) SDA(D1):MOSI 数据(高电平 2.2V~5.5V) RST:复位(高电平 2.2V~5.5V) D/C:数据/命令(高电平 2.2V~5.5V) 兼容 3.3V 和 5V 控制芯片的 I/O 电平(无需任何设置,直接兼容)。

1.png

这是本次活动使用的OLED屏幕,虽然背面标识的时SSD1315,但是和驱动SSD1306的方式大体相同,驱动的时候可以参考SSD1306的驱动方式进行驱动;

二:IIC协议的知识分享:

IIC通讯协议的了解

IIC(Inter-Integrated Circuit)是一种串行通信协议,主控制器和从器件间的主从通信。它使用两根线:SDA(数据线)和SCL(时钟线)。SDA(SerialData Line):用于传输数据。SCL(Serial Clock Line):用于同步数据传输的时钟信号。

注意:1.1C不同于485和CAN可长距离通讯,IIC只能短距离,常用干IC与IC通讯。例如存储芯片:24C02;传感器模块:ST30,气压模块MPL MPU6050,温度模块ATH20等等,有很多的模块,当时我们熟悉了IIC 的通讯方式,可以外扩很多模块,适用于电子爱好者和DIY;

2.IIC电路设计时,建议在SDA和SCL两根线外加上拉电阻,以保证时序通讯的稳定性。不过在本次的活动电路板上面并没有外接上拉电阻,也不知道是不是模块上面焊接好了。

2.1 IIC类型

IIC 总线在传送数据过程中共有三种类型信号,分别是:

1.开始信号:SCL 为高电平时,SDA由高电平向低电平跳变,开始传送数据。

2.结束信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。

3.应答信号:从机 在接收到 8bit 数据后,向发送数据的主机发出特定的低电平脉冲,表示已收到数据。主机向从机发出一个信号后等待从机发出一个应答信号,主机接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。

2.2 IIC的总时序图

2.png

起始条件是SCL保持高电平,SDA由高电平变为低电平后,延时(最少4us,这个和线长有关系),SCL变为低电平。只有当SCL线上的时钟信号为低时,数据线上的“高”或“低”状态才可以改变。输出到SDA线上的每个字节必须是8位,数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)。当传输完SDA马上拉低,接着输出低电平信号主机进而产生停止信号,结束传送过程。

2.3 IIC的 发送时序

3.png

1主机首先产生起开始信号,然后紧跟着发送一个从机地址,这个地址共有7位,紧接着的第8位是数据方向位(R/W),0表示主机发送数据(写),1表示主机接收数据(读)。

2.主机发送地址时,总线上的每个从机都将这7位地址码与自己的地址进行比较,若相同,则认为自已正在被主机寻址,根据R/T位将自己确定为发送器和接收器。这时候主机等待从机的应答信号,当主机收到应答信号时,发送要访问从机的那个地址,继续等待从机的应答信号,当主机收到应答信号时,发送N个字节的数据,继续等待从机的N次应答信号,主机产生停止信号,结束传送过程。

2.4 IIC的读时序

4.png

  1. 主机首先产生开始信号然后紧跟着发送一个从机地址,注意此时该地址的第8位为0,表明是向从机写命令。

  2. 2.这时候主机等待从机的应答信号(ACK)当主机收到应答信号时,发送要访问的地址,继续等待从机的应答信号,当主机收到应答信号后,主机要改变通信模式(主机将由发送变为接收,从机将由接收变为发送)所以主机重新发送一个开始start信号,然后紧跟着发送一个从机地址,注意此时该地址的第8位为1,表明将主机设置成接收模式开始读取数据,这时候主机等待从机的应答信号,当主机收到应答信号时,就可以接收1个字节的数据,当接收完成后,主机发送非应答信号,表示不在接收数据主机进而产生停止信号,结束传送过程。

三:字库的生成:

这里只需要软件生成就可以,方法如下:

5.png6.png

字库如下所示:注意字库的驱动方式需要和代码相配合,否则会导致乱码!!!!!!!!!!!!!!!!!

/***************************16*16的点阵字体取模方式:共阴——列行式——逆向输出*********/
const unsigned char F16x16[] =
{

// 电(0) 子(1) 产(2) 品(3) 世(4) 界(5)

0x00,0x00,0xF8,0x88,0x88,0x88,0x88,0xFF,0x88,0x88,0x88,0x88,0xF8,0x00,0x00,0x00,
0x00,0x00,0x1F,0x08,0x08,0x08,0x08,0x7F,0x88,0x88,0x88,0x88,0x9F,0x80,0xF0,0x00,/*"电",0*/
/* (16 X 16 , 宋体 )*/

0x80,0x82,0x82,0x82,0x82,0x82,0x82,0xE2,0xA2,0x92,0x8A,0x86,0x82,0x80,0x80,0x00,
0x00,0x00,0x00,0x00,0x00,0x40,0x80,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,/*"子",1*/
/* (16 X 16 , 宋体 )*/

0x00,0x04,0x84,0x84,0x94,0xE4,0x85,0x86,0x84,0xC4,0xB4,0x84,0x84,0x84,0x80,0x00,
0x80,0x60,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,/*"产",2*/
/* (16 X 16 , 宋体 )*/

0x00,0x00,0x00,0x7E,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x7E,0x00,0x00,0x00,0x00,
0x00,0xFE,0x42,0x42,0x42,0xFE,0x00,0x00,0x00,0xFE,0x42,0x42,0x42,0xFE,0x00,0x00,/*"品",3*/
/* (16 X 16 , 宋体 )*/

0x20,0x20,0x20,0xFE,0x20,0x20,0xFF,0x20,0x20,0x20,0xFF,0x20,0x20,0x20,0x20,0x00,
0x00,0x00,0x00,0x7F,0x40,0x40,0x47,0x44,0x44,0x44,0x47,0x40,0x40,0x40,0x00,0x00,/*"世",4*/
/* (16 X 16 , 宋体 )*/

0x00,0x00,0x00,0xFE,0x92,0x92,0x92,0xFE,0x92,0x92,0x92,0xFE,0x00,0x00,0x00,0x00,
0x08,0x08,0x04,0x84,0x62,0x1E,0x01,0x00,0x01,0xFE,0x02,0x04,0x04,0x08,0x08,0x00,/*"界",5*/
/* (16 X 16 , 宋体 )*/

};

在生成字库的时候,需要根据自己的代码设置 宽度和高度,还有就是设置项里面,我的开发经验时只要是将屏幕点亮基本可以说明IIC的读写指令是没有问题的,只是自己操作的屏幕的指令有问题,大家在开发的时候可以注意下,可以先烧录一下其他人的代码,确定硬件是好的,再去对软件进行调试,达到事半功倍的效果。这里感谢一下我再调试IIC时候,帮助我的大佬。

四:硬件连接和cube配置过程:

4.1 这里我使用硬件的IIC,对OLED进行驱动;

PB8 -----------------SCL

PB9 -----------------SDA

3V3   -----------------VCC

GND------------------GND

这里我是用IIC1的复用功能,并没有使用 PB6和PB7 引脚;

4.2 cube r软件配置如下:

引脚选择,这里我们直接点击PB8引脚,设置成IIC功能就可以。

7.png

IIC 参数配置:

这里IIC最大的通讯速度可以设置成100K,注意需要设置成7位的模式,其他参数就直接默认就可以。这里要注意需要打开 硬件IIC的中断;

8.png9.png

当我们开启的外设功能较多时,需要看一下各个外设的优先级,

10.png

不过在本次活动中,我们开启的活动并不是很多,所以这里随便设置一下也是可以的。

注意:本工程在04帖子上面的工程上进行修改,添加,由于OLED只是作为显示,所以优先级不建议设置的太高就行,只要能正常显示字样就可以。

五:软件代码编写:

5.1 硬件IIC的初始化:

I2C_HandleTypeDef hi2c1;

/* I2C1 init function */
void MX_I2C1_Init(void)
{

  /* USER CODE BEGIN I2C1_Init 0 */

  /* USER CODE END I2C1_Init 0 */

  /* USER CODE BEGIN I2C1_Init 1 */

  /* USER CODE END I2C1_Init 1 */
  hi2c1.Instance = I2C1;
  hi2c1.Init.ClockSpeed = 100000;
  hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
  hi2c1.Init.OwnAddress1 = 0;
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.OwnAddress2 = 0;
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN I2C1_Init 2 */

  /* USER CODE END I2C1_Init 2 */

}

5.2 :IIC的驱动OLED 代码如下:

static void I2C_WriteByte(uint8_t addr, uint8_t value)
{
	unsigned char dta[2] = {0};
	dta[0] = addr;
	dta[1] = value;
	OLED_send_byteS(dta, 2);
}

void WriteCmd(unsigned char I2C_Command)//写命令
{
	I2C_WriteByte(0x00, I2C_Command);
}

void WriteDat(unsigned char I2C_Data)//写数据
{
	I2C_WriteByte(0x40, I2C_Data);
}

OLED初始化部分:

void OLED_ON(void)
{
	WriteCmd(0X8D);  //设置电荷泵
	WriteCmd(0X14);  //开启电荷泵
	WriteCmd(0XAF);  //OLED唤醒
}
//关闭OLED显示 
void OLED_OFF(void)
{
	WriteCmd(0x8D);//电荷泵使能
	WriteCmd(0x10);//关闭电荷泵
	WriteCmd(0xAE);//关闭屏幕
}
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
	OLED_Fill(0xff);//全屏填充
OLED_ON();
}

5.3 部分驱动 字符串、汉字和数字代码如下:

显示汉字:

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;
	}
}

显示字符串:

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;
	}
}

显示数字如下:

//显示2个数字
//x,y :起点坐标	 
//len :数字的位数
//size:字体大小
void OLED_ShowNum(uint8_t x,uint8_t y,uint32_t num,uint8_t len,uint8_t size1)
{
	uint8_t t,temp;
	for(t=0;t<len;t++)
	{
		temp=(num/OLED_Pow(10,len-t-1))%10;
		if(temp==0)
		{
			OLED_ShowChar(x+(size1/2)*t,y,'0',size1);
		}
		else 
		{
			OLED_ShowChar(x+(size1/2)*t,y,temp+'0',size1);
		}
  }
}

主程序代码如下:

在用户代码区添加需要显示的字符串:

/* USER CODE BEGIN PFP */
const uint8_t hello_str[] = "Hello !EEPW?& DigiKey! Hello STM32F103RB!\r\n";  
const uint8_t Output_str[] = "Hello !Let us do it ! autor by clever  !\r\n";
const uint8_t test_str[] = "Hello world";
const uint8_t test_str1[] = "by: clever013";
/* USER CODE END PFP */
  OLED_Init();
  /* USER CODE END 2 */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
	OLED_ColorTurn(0);//0������ʾ��1 ��ɫ��ʾ
  OLED_DisplayTurn(0);//0������ʾ 1 ��Ļ��ת��ʾ
	OLED_Refresh();
	OLED_Clear();
	OLED_ShowCN(16, 0, 0);
	OLED_ShowCN(32, 0, 1);
	OLED_ShowCN(48, 0, 2);
	OLED_ShowCN(64, 0, 3);
	OLED_ShowCN(80, 0, 4);
	OLED_ShowCN(96, 0, 5);
//	sprintf(buffer,"%4.1fML",(float)123/4096*3.3*2);
	OLED_ShowStr(20, 2, (uint8_t *)test_str, 2);
	OLED_ShowStr(20, 4, (uint8_t *)test_str1, 2);
//	OLED_ShowStr(63,3,"HelloWorld:",16); 
	OLED_ShowNum(64,6,123456,6,16);

实物测试图片如下:

11.jpg

至此:使用STM32F103RB 硬件IIC 驱动OLED屏幕 工程,调试完毕;;;1.设置好I0信息,并点亮屏幕显示HelloWorld的任务完成;

由于自己代码写的比较乱,稍后整理一下发到电子产品世界的论坛上面;;;




关键词: 液体     流量     检测仪     IIC     屏幕显示    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]