基于STM32实验:温湿度显示(AHT20)和OLED应用

本次实验内容:
1.学习I2C总线协议,完成基于I2C协议的ATH20温湿度的数据采集,并串口输出。
2.理解OLED屏显和汉字点阵编码原理,完成对应的练习。
使用硬件器材及软件:
STM32F103C8T6、若干杜邦线飞线、USB转TTL、AHT20温湿度传感器、0.96‘’OLED屏、面包板、
Keil 5 MDK、flymuc烧录软件、串口调试助手


一、I2C总线协议及温湿度读取实验

1、I2C总线协议

1.1 什么是I2C总线协议

I2C总线是一个半双工通信协议。由两根线组成,时钟线(SCL)和数据线(SDA)。I2C可以分为主机和从机,同一总线上只能有一个主机、可以有多个从机。需要注意的是从机不能发出传输请求,并且从机的数据传输也是由主机控制的。本次实验内容便是将STM32作为主机,控制AHT20完成温湿度数据采集并传输数据。


1.2 I2C总线的物理层和协议层

  • 以下为物理层接线方式

在这里插入图片描述

  • I2C总线的协议层则是定义了通信的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等。接下来简单介绍以下各个定义:

1.起始信号和停止信号
 起始信号:在SCL为高电平时,SDA从高电平变为低电平。`
 停止信号:在SCL为高电平时,SDA从低电平变为高电平。
 起始信号和停止信号是一种电平跳变时序信号,而不是一个电平信号。以下为演示视图:
起始信号
在这里插入图片描述
停止信号
在这里插入图片描述

2.数据有效性
 如下图,当SCL处于低电平时,SDA数据无效,此时SDA数据互换。
在这里插入图片描述

3.响应
 当SDA传输数据后,接收方对接受到的数据进行一个应答。如果希望继续进行传输数据,则回应应答信号(低电平),否则回应非应答信号(高电平)。
在这里插入图片描述

2、实践练习

实验要求:
使用STM32F103完成基于I2C协议的AHT20温湿度传感器的数据采集,并将采集的温度-湿度值通过串口输出。

  • 以下为功能实现主要函数,详细代码请跳转
void  read_AHT20_once(void)//主函数调用,读取一次温湿度数据
{
	delay_ms(1);

	reset_AHT20();//重置
	delay_ms(1);

	init_AHT20();//初始化
	delay_ms(1);

	startMeasure_AHT20();//开始测量数据
	delay_ms(8);

	read_AHT20();//读取数据
	delay_ms(2);
}
  • 下面是上方调用的各个函数具体实现代码。
void  reset_AHT20(void)
{

	I2C_Start();
	I2C_WriteByte(0x70);
	ack_status = Receive_ACK();
  if(ack_status) printf("1");
  else printf("1-n-");
	I2C_WriteByte(0xBA);
	ack_status = Receive_ACK();
  if(ack_status) printf("2");
  else printf("2-n-");
	I2C_Stop();
}

void  init_AHT20(void)
{
	I2C_Start();
	I2C_WriteByte(0x70);
	ack_status = Receive_ACK();
  if(ack_status) printf("3");
  else printf("3-n-");	
	I2C_WriteByte(0xE1);
	ack_status = Receive_ACK();
  if(ack_status) printf("4");
  else printf("4-n-");
	I2C_WriteByte(0x08);
	ack_status = Receive_ACK();
  if(ack_status) printf("5");
  else printf("5-n-");
	I2C_WriteByte(0x00);
	ack_status = Receive_ACK();
  if(ack_status) printf("6");
  else printf("6-n-");
	I2C_Stop();
}



void  startMeasure_AHT20(void)
{
	I2C_Start();
	I2C_WriteByte(0x70);
	ack_status = Receive_ACK();
  if(ack_status) printf("7");
  else printf("7-n-");
	I2C_WriteByte(0xAC);
	ack_status = Receive_ACK();
  if(ack_status) printf("8");
  else printf("8-n-");
	I2C_WriteByte(0x33);
	ack_status = Receive_ACK();
  if(ack_status) printf("9");
  else printf("9-n-");
	I2C_WriteByte(0x00);
	ack_status = Receive_ACK();
  if(ack_status) printf("10");
  else printf("10-n-");
	I2C_Stop();
}



void read_AHT20(void)
{
	uint8_t   i;
	for(i=0; i<6; i++)
	{
		readByte[i]=0;
	}
	I2C_Start();//I2C启动
 
	I2C_WriteByte(0x71);//I2C写数据
	ack_status = Receive_ACK();//收到的应答信息
	readByte[0]= I2C_ReadByte();//I2C读取数据
	Send_ACK();//发送应答信息
 
	readByte[1]= I2C_ReadByte();
	Send_ACK();
 
	readByte[2]= I2C_ReadByte();
	Send_ACK();
 
	readByte[3]= I2C_ReadByte();
	Send_ACK();
 
	readByte[4]= I2C_ReadByte();
	Send_ACK();
 
	readByte[5]= I2C_ReadByte();
	SendNot_Ack();
	//Send_ACK();
 
	I2C_Stop();//I2C停止函数
	//判断读取到的第一个字节是不是0x08,0x08是该芯片读取流程中规定的,如果读取过程没有问题,就对读到的数据进行相应的处理
	if( (readByte[0] & 0x68) == 0x08 )
	{
		H1 = readByte[1];
		H1 = (H1<<8) | readByte[2];
		H1 = (H1<<8) | readByte[3];
		H1 = H1>>4;
 
		H1 = (H1*1000)/1024/1024;
 
		T1 = readByte[3];
		T1 = T1 & 0x0000000F;
		T1 = (T1<<8) | readByte[4];
		T1 = (T1<<8) | readByte[5];
 
		T1 = (T1*2000)/1024/1024 - 500;
 
		AHT20_OutData[0] = (H1>>8) & 0x000000FF;
		AHT20_OutData[1] = H1 & 0x000000FF;
 
		AHT20_OutData[2] = (T1>>8) & 0x000000FF;
		AHT20_OutData[3] = T1 & 0x000000FF;
	}
	else
	{
		AHT20_OutData[0] = 0xFF;
		AHT20_OutData[1] = 0xFF;
 
		AHT20_OutData[2] = 0xFF;
		AHT20_OutData[3] = 0xFF;
		printf("读取失败!!!");
 
	}
	printf("\r\n");
	//根据AHT20芯片中,温度和湿度的计算公式,得到最终的结果,通过串口显示
	printf("温度:%d%d.%d",T1/100,(T1/10)%10,T1%10);
	printf("湿度:%d%d.%d",H1/100,(H1/10)%10,H1%10);
	printf("\r\n");
}
  • 演示视图
    在这里插入图片描述

二、OLED屏显原理及实验

1、OLED屏显和汉字点阵编码原理

1.1 SPI接口

 SPI(Serial Peripheral interface)是串行外围设备接口。是一种高速、全双工、同步的通信总线,在芯片上只占用四根线。以下为物理层结构
在这里插入图片描述
四条线分别为:
1.MISO 主设备数据输入,从设备数据输出。
2.MOSI 主设备数据输出,从设备数据输入。
3.SCLK 时钟信号,由主设备产生。
4.CS 从设备片选信号,由主设备控制。

SPI的主要特点:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束中断标志;写冲突保护;总线竞争保护等。
下为SPI通信过程
在这里插入图片描述
MOSI与MISO的信号只在NSS为低电平的时候才有效,在SCK的每个时钟周期MOSI和MISO传输一位数据。

1.2 OLED介绍

本次实验使用的是0.96‘’OLED

  • 实物视图:
    在这里插入图片描述
  • 模块原理图:
    在这里插入图片描述
  • 与STM32C8T6接线说明:
    在这里插入图片描述

1.3 OLED字模显示

 OLED显示字符就是点亮对应的点,实现目标字符的显示。在Keil中,我们只能控制点亮对应的点亮,而不能直接去显示目标字符,因此需要使用字模转换工具,安装地址
安装完成后,打开程序,如下图。字符集选择用户定义,编辑码表中填入想要显示的字符,最后文件格式选为C文件即可保存导出,此处以“你好”作为示例。
在这里插入图片描述

  • 保存后的文件用记事本打开,后面的一串16进制数就是需要的显示点。
    在这里插入图片描述

  • 在文件中找到oledfont.h中的cfont16数组存储进去即可。
    在这里插入图片描述
    在这里插入图片描述

2、OLED屏显实验

实验要求:
使用STM32F103的SPI或IIC接口实现以下功能:

  1. 显示自己的学号和姓名;
  2. 显示AHT20的温度和湿度;
  3. 上下或左右的滑动显示长字符,比如“Hello,欢迎来到重庆交通大学物联网205实训室!”或者一段歌词或诗词(最好使用硬件刷屏模式)。

2.1 姓名学号显示

  • 主要实现函数
void TEST_MainPage(void)
{	
    GUI_ShowCHinese(40,20,16,"郑",1);//40、20、16、1分别代表x轴、y轴、字符大小、个数
	GUI_ShowString(8,40,"63200",16,1);
	delay_ms(1500);		
	delay_ms(1500);
}
int main(void)
{	
	delay_init();	    	       //延时函数初始化	  
	NVIC_Configuration(); 	   //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 	
	OLED_Init();			         //初始化OLED  
	OLED_Clear(0);             //清屏(全黑) 
		while(1)
	{
	TEST_MainPage();
    }
}
  • 演示视图

在这里插入图片描述

2.2读取温度湿度显示

  • 思路:在OLED屏显的基础上,将上一温度湿度的读取数据存储到一字符数组内打印出来
  • 主要实现函数
int main(void)
{	
	delay_init();	    	       //延时函数初始化	  
	uart_init(115200);	
	NVIC_Configuration(); 	   //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 	
	OLED_Init();			         //初始化OLED  
	OLED_Clear(0);             //清屏(全黑) 
	IIC_Init();
		while(1)
	{
		
	  
		TEST_MainPage();
		printf("温度湿度显示");
		read_AHT20_once();
		delay_ms(1500);
  }
}
void TEST_MainPage(void)
{	
    *int i;
	u8 str1[4];
	u8 str2[4];
	u8* c=celsius(str1);
	u8* h=humidity(str2);
	
	for(i=0;i<=3;i++)//循环将数字变为数字字符
	{
		if(i!=2)
		{
		c[i]=c[i]+0x30;
		h[i]=h[i]+0x30;
		}
	}
	GUI_ShowCHinese(8,20,16,"现在温度",1);
	GUI_ShowString(72,20,":",16,1);
	GUI_ShowString(96,20,c,16,1);

	GUI_ShowCHinese(8,48,16,"现在湿度",1);
	GUI_ShowString(72,48,":",16,1);
	GUI_ShowString(96,48,h,16,1);
	delay_ms(15);		
	delay_ms(15);
}
void *celsius(u8 *str1)//存储返回温度
{
	str1[0]=T1/100;
	str1[1]=(T1/10)%10;
	str1[2]='.';
	str1[3]=T1%10;
	return str1;
}
void *humidity(u8 *str2)存储返回湿度
{
	str2[0]=H1/100;
	str2[1]=(H1/10)%10;
	str2[2]='.';
	str2[3]=H1%10;
	return str2;
}
  • 演示视图

在这里插入图片描述
在这里插入图片描述

2.3 滚动字幕显示

  • 主要实现函数
int main(void)
{	
	delay_init();	    	       //延时函数初始化	  
	uart_init(115200);	
	NVIC_Configuration(); 	   //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 	
	OLED_Init();			         //初始化OLED  
	OLED_Clear(0);             //清屏(全黑) 
	IIC_Init();
	//滚动
	OLED_WR_Byte(0x2E,OLED_CMD);        //关闭滚动
    OLED_WR_Byte(0x27,OLED_CMD);        //水平向左或者右滚动 26/27
    OLED_WR_Byte(0x00,OLED_CMD);        //虚拟字节
	OLED_WR_Byte(0x00,OLED_CMD);        //起始页 0
    OLED_WR_Byte(0x07,OLED_CMD);        //滚动时间间隔
	OLED_WR_Byte(0x07,OLED_CMD);        //终止页 7
  	OLED_WR_Byte(0x00,OLED_CMD);        //虚拟字节
	OLED_WR_Byte(0xFF,OLED_CMD);        //虚拟字节
  	TEST_MainPage();
	OLED_WR_Byte(0x2F,OLED_CMD);        //开启滚动
}
void TEST_MainPage(void)
{	
	GUI_ShowCHinese(10,24,16,"我是重交校霸",1);
	delay_ms(1500);		
	delay_ms(1500);
}
  • 演示示例

在这里插入图片描述


三、总结

以上就是本文的全部内容,简单学习了I2C和SPI总线的架构,并完成了对应的实验练习。其中也存在着许多问题,例如温度湿度读取失败,最后发现使启动文件有所纰漏,更换了之后便成功了但对具体原因仍一头雾水;还有就是OLED显示字符时,显示字符串用字符数组替代时会出现尾部打印出错,推测是应为数组未定义正确而导致的结束字符也打印上了。总的来说,本次实验受益匪浅,期待下次的实验学习。


四、参考文献

https://blog.csdn.net/qq_19810697/article/details/123569360
https://blog.csdn.net/qq_43279579/article/details/111597278
https://blog.csdn.net/qq_43279579/article/details/111414037
https://blog.csdn.net/qq_46467126/article/details/121439142

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值