【嵌入式08.2 - 实验】基于IIC和SPI协议的温湿度采集与OLED显示

一、题目要求

学习I2C总线通信协议,使用STM32F103完成基于I2C协议的AHT20温湿度传感器的数据采集,并将采集的温度-湿度值通过串口和OLED显示屏输出。

1)阅读AHT20数据手册,编程实现:每隔2秒钟采集一次温湿度数据,并通过串口发送到上位机(win10)。

2)理解OLED屏显和汉字点阵编码原理,使用STM32F103的SPI或IIC接口实现以下功能:

显示AHT20的温度和湿度;

二、“软件I2C”和“硬件I2C”

(一)I2C 通讯协议

I2C 通讯协议 (Inter - Integrated Circuit) 是由 Phiilps 公司开发的,由于它引脚少,硬件实现简单, 可扩展性强,不需要 USART、CAN 等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路 (IC) 间的通讯。

STM32 的 I2C 特性及架构
如果我们直接控制 STM32 的两个 GPIO 引脚,分别用作 SCL 及 SDA,按照上述信号的时序要求,直接像控制 LED 灯那样控制引脚的输出 (若是接收数据时则读取 SDA 电平), 就可以实现 I2C 通讯。同样,假如我们按照 USART 的要求去控制引脚,也能实现 USART 通讯。所以只要遵守协议,就是标准的通讯,不管您如何实现它, 不管是 ST 生产的控制器还是 ATMEL 生产的存储器,都能按通讯标准交互。

由于直接控制 GPIO 引脚电平产生通讯时序时,需要由 CPU 控制每个时刻的引脚状态,所以称之为“软件模拟协议”方式。相对地,还有“硬件协议”方式,STM32 的 I2C 片上外设专门负责实现 I2C 通讯协议,只要配置好该外设,它就会自动根据协议要求产生通讯信号, 收发数据并缓存起来,CPU 只要检测该外设的状态和访问数据寄存器,就能完成数据收发。这种由硬件外设处理 I2C 协议的方式减轻了CPU 的工作,且使软件设计更加简单。

下图为STM32 的 I2C 架构剖析:
在这里插入图片描述

(二)硬件I2C

所谓硬件I2C对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚也是专用的;软件I2C一般是用GPIO管脚,用软件控制管脚状态以模拟I2C通信波形。

硬件I2C, 32芯片上有片上外设可以实现I2C通信协议,片上外设可不是开发板的硬件哈,只要配置好该外设,它就会自动根据协议要求产生通讯信号,收发数据并缓存起来,CPU只要检测该外设的状态和访问数据寄存器,就能完成数据收发。硬件I2C的GPIO引脚是固定的

(三)软件I2C

软件I2C,顾名思义,就是编写程序来模拟I2C通信,直接控制GPIO引脚电平产生通信时序,通俗点就是配置GPIO引脚”有序“地输入输出0或1。软件I2C控制的GPIO引脚是任意的。

(四)硬件I2C和软件I2C

  • 硬件I2C用法比较复杂,模拟IIC的流程更清楚一些。
  • 硬件I2C速度比模拟快,并且可以用DMA
  • 模拟I2C可以在任何管脚上,而硬件只能在固定管脚上。

一般较硬件i2c稳定,但是程序较为繁琐,但不难。硬件i2c程序员只要调用i2c的控制函数即可,不用直接的去控制SCL,SDA高低电平的输出。但是有些单片机的硬件i2c不太稳定,调试问题较多。

三、AHT20采集温湿度串口显示

(一)工程建立

1.准备
温湿度传感器AHT20
串口调试助手

了解AHT20芯片的相关信息,具体信息请到官方下载对应产品介绍文档
资料链接:http://www.aosong.com/class-36.html

2.核心代码

已在文章末端上传整个工程代码,可供参考。

  • main.c主函数
> #include "delay.h"
#include "usart.h"
#include "bsp_i2c.h"
int main(void)
{	
	delay_init();     //?óê±oˉêy3?ê??ˉ	  
	uart_init(115200);	 //′??ú3?ê??ˉ?a115200
	IIC_Init();
		while(1)
	{
		printf("温度湿度显示");
		read_AHT20_once();
		delay_ms(1500);
  }
}
  • AHT20芯片使用过程read_AHT20_once函数
> void  read_AHT20_once(void)
>  { 	
>   delay_ms(10); 	
>   reset_AHT20();
> 	delay_ms(10); 
> 	init_AHT20(); 
> 	delay_ms(10); 
> 	startMeasure_AHT20();
> 	delay_ms(80); 
> 	read_AHT20(); 	
>   delay_ms(5);
>  }
  • AHT20芯片读取数据read_AHT20函数
void read_AHT20(void)
{
	uint8_t   i;

	for(i=0; i<6; i++)
	{
		readByte[i]=0;
	}

	//-------------
	I2C_Start();

	I2C_WriteByte(0x71);
	ack_status = Receive_ACK();
	readByte[0]= I2C_ReadByte();
	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();

	//--------------
	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("lyy");

	}
	printf("\r\n");
	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");
}

整体布局:
在这里插入图片描述

(二)电路连接

AHT20对应引脚及描述如下所示:

在这里插入图片描述
AHT20与STM32F103C8T6的对应

AHT20STM32F103C8T6
VDD3V3
GNDGND
SDAPB7
SCLPB6

USB to TTL与STM32F103C8T6的连接

USB to TTLSTM32F103C8T6
3V33V3
GNDGND
RXDA9
TXDA10

(三)编译烧录

1.编译结果
在这里插入图片描述

2.烧录
在这里插入图片描述

(四)效果展示

串口温度显示

四、AHT20采集温度OLED显示

(一)代码撰写

1.取字模
打开字模软件提取“温湿度显示”的字模

  • 在文字输入区输入需要的文字
  • 按下电脑上Ctrl和Enter
  • 点击“取模方式”中的C51模式
    在点阵生成区就可以得到对应的字模。

在这里插入图片描述
在这里插入图片描述
2.代码编写
在OLED的工程文件中直接进行修改。
将上述中温湿度显示的bsp_i2c.h、bsp_i2c.c、sys.h(修改名称为AHT20_sys.h)、sys.c(修改名称为AHT20_sys.c)进行移植。

  • 将对应文件粘贴至USER目录下

在这里插入图片描述

  • 添加文件(如果出现对usart头文件的报错,说明可能原工程下没有对应文件,需要进行同步骤添加)
    List item

  • 对于AHT20_sys.c和AHT20_sys.h文件的重命名,对应文件中的头文件也需要进行修改

在这里插入图片描述

  • 对文件进行编译会出现 NVIC_Configuration重定义的报错,原因是AHT20_sys.c和sys.c的内容均是这个函数,因此需要进行修改。
#include "sys.h"
 
//THUMB指令不支持汇编内联
//采用如下方法实现执行汇编指令WFI  
void WFI_SET(void)
{
	__ASM volatile("wfi");		  
}
//关闭所有中断
void INTX_DISABLE(void)
{		  
	__ASM volatile("cpsid i");
}
//开启所有中断
void INTX_ENABLE(void)
{
	__ASM volatile("cpsie i");		  
}
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(u32 addr) 
{
    MSR MSP, r0 			//set Main Stack value
    BX r14
}

  • 在bsp_i2c.c文件首部添加以下定义内容
uint32_t  H1=0;  //Humility
uint32_t  T1=0;  //Temperature
uint8_t  t;
u8 *strTemp1;
u8 *strTemp2;
u8 *strTemp3;
u8 *strHumi1;
u8 *strHumi2;
u8 *strHumi3;

在这里插入图片描述

  • 在bsp_i2c.c文件中void read_AHT20(void)函数末尾添加
 Show_OLED();

如下图所示
在这里插入图片描述

  • 在bsp_i2c.c文件中void read_AHT20(void)函数下方添加void Show_OLED(void)函数
void Show_OLED(void)
{
	t = T1/100;
	switch(t)
	{
		case 0:break;
		case 1:strTemp1 = "1";break;
		case 2:strTemp1 = "2";break;
		case 3:strTemp1 = "3";break;
		case 4:strTemp1 = "4";break;
		case 5:strTemp1 = "5";break;
		case 6:strTemp1 = "6";break;
		case 7:strTemp1 = "7";break;
		case 8:strTemp1 = "8";break;
		case 9:strTemp1 = "9";break;
	}
	t = (T1/10)%10;
	switch(t)
	{
		case 0:strTemp2 = "0";break;
		case 1:strTemp2 = "1";break;
		case 2:strTemp2 = "2";break;
		case 3:strTemp2 = "3";break;
		case 4:strTemp2 = "4";break;
		case 5:strTemp2 = "5";break;
		case 6:strTemp2 = "6";break;
		case 7:strTemp2 = "7";break;
		case 8:strTemp2 = "8";break;
		case 9:strTemp2 = "9";break;
	}
	t = T1%10;
	switch(t)
	{
		case 0:strTemp3 = "0";break;
		case 1:strTemp3 = "1";break;
		case 2:strTemp3 = "2";break;
		case 3:strTemp3 = "3";break;
		case 4:strTemp3 = "4";break;
		case 5:strTemp3 = "5";break;
		case 6:strTemp3 = "6";break;
		case 7:strTemp3 = "7";break;
		case 8:strTemp3 = "8";break;
		case 9:strTemp3 = "9";break;
	}
	t = H1/100;
	switch(t)
	{
		case 0:break;
		case 1:strHumi1 = "1";break;
		case 2:strHumi1 = "2";break;
		case 3:strHumi1 = "3";break;
		case 4:strHumi1 = "4";break;
		case 5:strHumi1 = "5";break;
		case 6:strHumi1 = "6";break;
		case 7:strHumi1 = "7";break;
		case 8:strHumi1 = "8";break;
		case 9:strHumi1 = "9";break;
	}
	t = H1/100;
	switch(t)
	{
		case 0:strHumi2 = "0";break;
		case 1:strHumi2 = "1";break;
		case 2:strHumi2 = "2";break;
		case 3:strHumi2 = "3";break;
		case 4:strHumi2 = "4";break;
		case 5:strHumi2 = "5";break;
		case 6:strHumi2 = "6";break;
		case 7:strHumi2 = "7";break;
		case 8:strHumi2 = "8";break;
		case 9:strHumi2 = "9";break;
	}
	t = H1/100;
	switch(t)
	{
		case 0:strHumi3 = "0";break;
		case 1:strHumi3 = "1";break;
		case 2:strHumi3 = "2";break;
		case 3:strHumi3 = "3";break;
		case 4:strHumi3 = "4";break;
		case 5:strHumi3 = "5";break;
		case 6:strHumi3 = "6";break;
		case 7:strHumi3 = "7";break;
		case 8:strHumi3 = "8";break;
		case 9:strHumi3 = "9";break;
	}
	GUI_ShowString(40,32,"    ",16,1);
	GUI_ShowString(40,48,"    ",16,1);
  GUI_ShowCHinese(0,32,16,"温度:",1);
	GUI_ShowString(40,32,strTemp1,16,1);
	GUI_ShowString(48,32,strTemp2,16,1);
	GUI_ShowString(56,32,".",16,1);
	GUI_ShowString(64,32,strTemp3,16,1);
	GUI_ShowCHinese(72,32,16,"℃",1);
  GUI_ShowCHinese(0,48,16,"湿度:",1);
	GUI_ShowString(40,48,strHumi1,16,1);
	GUI_ShowString(48,48,strHumi2,16,1);
	GUI_ShowString(56,48,".",16,1);
	GUI_ShowString(64,48,strHumi3,16,1);
	GUI_ShowCHinese(72,48,16,"%",1);
}
  • 在oledfont.h文件中的const typFNT_GB16 cfont16[]中添加字模内容,如下图所示
    在这里插入图片描述
  • main.c中的代码如下(注意添加头文件
#include "bsp_i2c.h"
#include "AHT20_sys.h"
int main(void)
{	
	delay_init();	    	       //延时函数初始化	  
	NVIC_Configuration(); 	   //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 	
	OLED_Init();
  IIC_Init();	//初始化OLED  
	OLED_Clear(0);             //清屏(全黑)
	while(1) 
	{	
	read_AHT20_once();    	       //延时函数初始化    	  
	
	delay_ms(5000); //显示5s
	OLED_Clear(0); 	//清屏刷新温湿度数据
	}
}

在这里插入图片描述

(二)电路连接

各板块线路连接如下图所示:
在这里插入图片描述
在这里插入图片描述
OLED模块和STM32F103C8T6开发板:
在这里插入图片描述

(三)编译烧录

编译结果如下所示
在这里插入图片描述
烧录可参考“AHT20采集温湿度串口显示”的烧录步骤。

(四)效果展示

OLED和AHT20温湿度

五、总结

刚开始做实验的时候,出现了温湿度读取为00的情况,原因是没有读取到温湿度数据而不是传感器的问题,这个是需要注意的。在OLED显示温湿度进行文件移植的时候,在编译过程中出现了函数NVIC_Configuration重定义的问题,原因是因为移植的AHT20_sys.c的文件和原本的sys.c的文件中均包含了这个函数,后面是根据其他成功的示例工程文件对sys.c的文件进行了修改才能够编译成功。还需要注意一下,如果下载了以下提供的工程文件再编译会出现报错,这里需要自行启动文件的路径修修改。

以上两个工程文件如下:
链接:https://pan.baidu.com/s/1ELHGZWbQTZoopZJPLUz1Gg
提取码:uj5o

六、参考文献

https://blog.csdn.net/m0_58892312/article/details/121410862
https://blog.csdn.net/qq_43279579/article/details/111597278
https://blog.csdn.net/qq_46467126/article/details/121439142
https://blog.csdn.net/qq_31860135/article/details/88658136
https://doc.embedfire.com/module/module_tutorial/zh/latest/Module_Manual/iic_class/iic.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值