STM32 温湿度采集与OLED显示

news2025/1/15 23:40:29

目录

一、I2C总线通信协议 

1、I2C介绍

2、软件I2C和硬件I2C

(1)硬件I2C

(2)软件I2C

差异

二、AHT20温湿度传感器 

接口原理介绍

1. 温度测量原理

2. 湿度测量原理

实物引脚

 传感器性能

电气特性  

三、任务实现

具体要求

代码示例

结果展示

总结


学习目标:

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

一、I2C总线通信协议 

1、I2C介绍

       对于I2C的介绍,可前往往期博客阅读学习(博客地址:基于I2C协议的OLED显示(利用U82G库)_u8g2-CSDN博客)除了关于I2C的基本内容外,本期博客再介绍软硬件I2C。

2、软件I2C和硬件I2C

(1)硬件I2C

直接利用 STM32 芯片中的硬件 I2C 外设

       硬件实现通常依赖于专门的硬件模块(如I2C控制器),这些控制器内置了I2C协议的主要功能,包括起始条件、数据传输、时序控制和停止条件的生成。硬件I2C的优点包括:

  • 初始化:配置I2C控制器的参数,包括通信速率、模式(主机或从机)、设备地址等。
  • 发送起始条件:主机通过I2C控制器生成起始条件,即将SDA从高电平切换到低电平,同时保持SCL线为高电平。
  • 发送设备地址和操作位:主机发送目标设备的地址和读写操作位(R/W位)通过SDA线。
  • 数据传输:根据需要,主机通过I2C控制器发送或接收数据,确保时序符合I2C协议要求。
  • 接收ACK/NACK:每发送一个数据字节后,主机等待接收方的应答信号(ACK或NACK),由I2C控制器解析处理。
  • 重复传输:根据通信需求,主机可以继续发送或接收数据,并发送相应的ACK或NACK信号。
  • 发送停止条件:传输结束后,主机通过I2C控制器发送停止条件,即将SDA从低电平切换到高电平,同时保持SCL线为高电平。
  • 错误处理:I2C控制器监测和处理通信中的错误条件,如总线忙、设备未响应等情况,以确保通信的可靠性。

(2)软件I2C

直接使用 CPU 内核按照 I2C 协议的要求控制 GPIO 输出高低电平,从而模拟I2C。

在某些硬件资源受限的情况下,可以通过软件实现I2C通信,这种方法被称为Bit-Banging。软件控制每个时钟周期上的数据线变化,以模拟I2C协议。具体步骤包括:

  • 初始化:配置两个GPIO引脚作为SDA(数据线)和SCL(时钟线),并设置它们的初始状态和方向(输入或输出)。
  • 发送起始条件:将SDA从高电平切换到低电平,同时保持SCL为高电平,以生成起始条件。
  • 发送设备地址和操作位:通过控制SDA线的电平变化,发送目标设备的地址和读写操作位(R/W位)。
  • 数据传输:根据读写操作位的指示,发送方(主机或从机)通过控制SDA线上的电平变化来发送或接收数据。
  • 接收ACK/NACK:每发送一个数据字节后,发送方等待接收方的应答信号(ACK或NACK)。这通过控制SDA线上的电平变化来模拟。
  • 重复传输:根据通信需求,继续发送或接收数据,并根据接收方的应答发送相应的ACK或NACK信号。
  • 发送停止条件:传输完成后,将SDA从低电平切换到高电平,同时保持SCL线为高电平,以发送停止条件。
  • 错误处理:在通信过程中,通过检测SDA线和SCL线上的电平变化来处理错误,如超时、无应答等情况。

差异

       当使用硬件 I2C,系统直接使用外设来控制引脚,有效减轻了 CPU 的负担。但是,硬件 I2C 必须使用特定的引脚作为 SCL 和 SDA,这种约束限制了其灵活性。相比之下,软件模拟 I2C 可以利用任意 GPIO 引脚,因此更加灵活。尽管硬件 I2C 提供了更高效的通信和稳定性,但其配置和使用相对复杂。相反,软件 I2C 的工作流程通常更直观和清晰,因此对于初学者理解 I2C 协议可能更为有利。总体而言,在实际应用中,硬件 I2C 往往能够提供更快速和更稳定的通信。

二、AHT20温湿度传感器 

        AHT20是国内奥松生成的I2C接口的MEMS温湿度传感器,ADC位数为20Bit,具有体积小、精度高、成本低等优点。相较于AHT10,最显著的变化是体积更小,缩小到 3*3*1.0mm。相对湿度精度 RH=±2%,温度精度 T=±0.3°C。相对湿度测量范围 RH=0~100%,温度测量范围 T=-40~85°C。AHT10/15/20只是供电电压不同,其他参数没有什么不同,其中AHT15具有聚四氟乙烯防水防尘膜,允许传感器在恶劣环境条件下使用。

接口原理介绍

上图为AHT20封装图。 

 

  • 1. 温度测量原理

       AHT20 传感器使用了一个集成的温度传感器来测量环境的温度。主要原理基于温度对半导体材料电阻值的影响。通常采用的是微电阻(RTD)或热敏电阻(PTC/NTC)原理。AHT20 内部的温度传感器通过测量温度对电阻值的影响,将其转换为数字信号输出,从而得到环境的温度数据。

  • 2. 湿度测量原理

       AHT20 传感器的湿度测量采用了电容湿度传感器的原理。电容湿度传感器利用介电常数与空气中水分含量之间的关系来测量湿度。具体来说,AHT20 使用一对电容传感器电极,当介电常数随环境湿度变化时,传感器的电容值也相应变化。通过测量这种电容值的变化,AHT20 可以准确地计算出环境的相对湿度(RH)。

实物引脚

对我们手上拿到的实物,引脚功能如下(孔面朝前):

  • 电源引脚 VDD GND :

        DHT20的供电范围为 2.2-5.5 V ,VDD 接电压输入,GND 接地

  • 串行时钟 SCL:

        SCL 用于微处理器与 DHT20 之间的通讯同步。由于接口包含了完全静态逻辑,因而不存在最小 SCL 频率

  • 串行数据 SDA:

        SDA 引脚用于传感器的数据输入和输出。当向传感器发送命令时,SDA 在串行时钟( SCL )的上升沿有效,且当 SCL 为高电平时,SDA 必须保持稳定。在 SCL 下降沿之后,SDA 值可被改变。为确保通信安全,SDA 的有效时间在 SCL 上升沿之前和下降沿之后应该分别延长至 TSU and THO 。当从传感器读取数据时,SDA 在 SCL 变低以后有效( TV ),且维持到下一个 SCL 的下降沿。

 连接如下:

 传感器性能

电气特性  

三、任务实现

具体要求

        阅读AHT20数据手册,编程实现:每隔2秒钟采集一次温湿度数据,显示到OLED上,同时通过串口发送到上位机的“串口助手”软件。

代码示例

usart.c

#include "sys.h"
#include "usart.h"
 
 
	 
#if SYSTEM_SUPPORT_UCOS
#include "includes.h"				
#endif
 
 
 
 
#if 1
#pragma import(__use_no_semihosting)             
              
struct __FILE 
{ 
	int handle; 
 
}; 
 
FILE __stdout;       
 
void _sys_exit(int x) 
{ 
	x = x; 
} 
//Öض¨Òåfputcº¯Êý 
int fputc(int ch, FILE *f)
{      
	while((USART1->SR&0X40)==0);
    USART1->DR = (u8) ch;      
	return ch;
}
#endif 
 
 
 
#if EN_USART1_RX   
 
u8 USART_RX_BUF[USART_REC_LEN];    
 
u16 USART_RX_STA=0;       
  
void uart_init(u32 bound){
   
  GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//ʹÄÜUSART1£¬GPIOAʱÖÓ
 
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; 
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	
    GPIO_Init(GPIOA, &GPIO_InitStructure);
   
 
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);  
 
 
 
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			
	NVIC_Init(&NVIC_InitStructure);	
  
 
 
	USART_InitStructure.USART_BaudRate = bound;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	
 
    USART_Init(USART1, &USART_InitStructure); 
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    USART_Cmd(USART1, ENABLE);                    
 
}
 
 
 
void USART1_IRQHandler(void)                
	{
	u8 Res;
#ifdef OS_TICKS_PER_SEC	 	
	OSIntEnter();    
#endif
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  
		{
		Res =USART_ReceiveData(USART1);
		
		if((USART_RX_STA&0x8000)==0)
			{
			if(USART_RX_STA&0x4000)
				{
				if(Res!=0x0a)USART_RX_STA=0;
				else USART_RX_STA|=0x8000;	
				}
			else 
				{	
				if(Res==0x0d)USART_RX_STA|=0x4000;
				else
					{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;
					}		 
				}
			}   		 
     } 
#ifdef OS_TICKS_PER_SEC	 	
	OSIntExit();  											 
#endif
} 
#endif	
 

usart.h

#ifndef __USART_H
#define __USART_H
#include "stdio.h"	
#include "sys.h" 
 
#define USART_REC_LEN  			200  	
#define EN_USART1_RX 			1		    
	  	
extern u8  USART_RX_BUF[USART_REC_LEN];
extern u16 USART_RX_STA;         		
 
void uart_init(u32 bound);
#endif
 

bsp_i2c.c

#include "bsp_i2c.h"
#include "delay.h"
 
uint8_t   ack_status=0;
uint8_t   readByte[6];
uint8_t   AHT20_status=0;
 
uint32_t  H1=0;  
uint32_t  T1=0;  
 
uint8_t  AHT20_OutData[4];
uint8_t  AHT20sendOutData[10] = {0xFA, 0x06, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF};
 
void IIC_Init(void)
{					     
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );	
	   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;  
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
 
	IIC_SCL=1;
	IIC_SDA=1;
 
}
 
void IIC_Start(void)
{
	SDA_OUT();    
	IIC_SDA=1;	  	  
	IIC_SCL=1;
	delay_us(4);
 	IIC_SDA=0;
	delay_us(4);
	IIC_SCL=0;
}	  
 
void IIC_Stop(void)
{
	SDA_OUT();
	IIC_SCL=0;
	IIC_SDA=0;
 	delay_us(4);
	IIC_SCL=1; 
	IIC_SDA=1;
	delay_us(4);							   	
}
 
u8 IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	SDA_IN();     
	IIC_SDA=1;delay_us(1);	   
	IIC_SCL=1;delay_us(1);	 
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL=0;   
	return 0;  
} 
 
void IIC_Ack(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=0;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}
	    
void IIC_NAck(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=1;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}					 				     
		  
void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
		SDA_OUT(); 	    
    IIC_SCL=0;
    for(t=0;t<8;t++)
    {              
        IIC_SDA=(txd&0x80)>>7;
        txd<<=1; 	  
		delay_us(2);   
		IIC_SCL=1;
		delay_us(2); 
		IIC_SCL=0;	
		delay_us(2);
    }	 
} 	    
 
u8 IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();
  for(i=0;i<8;i++ )
	{
    IIC_SCL=0; 
    delay_us(2);
		IIC_SCL=1;
    receive<<=1;
    if(READ_SDA)receive++;   
		delay_us(1); 
  }					 
	if (!ack)
			IIC_NAck();
	else
			IIC_Ack();   
	return receive;
}
 
void IIC_WriteByte(uint16_t addr,uint8_t data,uint8_t device_addr)
{
	IIC_Start();  
	
	if(device_addr==0xA0) 
		IIC_Send_Byte(0xA0 + ((addr/256)<<1));
	else
		IIC_Send_Byte(device_addr);	    
	IIC_Wait_Ack(); 
	IIC_Send_Byte(addr&0xFF);  
	IIC_Wait_Ack(); 
	IIC_Send_Byte(data);     					   
	IIC_Wait_Ack();  		    	   
  IIC_Stop();
	if(device_addr==0xA0) //
		delay_ms(10);
	else
		delay_us(2);
}
 
uint16_t IIC_ReadByte(uint16_t addr,uint8_t device_addr,uint8_t ByteNumToRead) 
{	
		uint16_t data;
		IIC_Start();  
		if(device_addr==0xA0)
			IIC_Send_Byte(0xA0 + ((addr/256)<<1));
		else
			IIC_Send_Byte(device_addr);	
		IIC_Wait_Ack();
		IIC_Send_Byte(addr&0xFF);  
		IIC_Wait_Ack(); 
 
		IIC_Start();  	
		IIC_Send_Byte(device_addr+1);	    
		IIC_Wait_Ack();
		if(ByteNumToRead == 1)
		{
			data=IIC_Read_Byte(0);
		}
		else
			{
				data=IIC_Read_Byte(1);
				data=(data<<8)+IIC_Read_Byte(0);
			}
		IIC_Stop();   
		return data;
}
 
 
 
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);
}
 
 
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_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();
 
 
	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("ʧ°ÜÁË");
 
	}
	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");
}
 
 
 
 
uint8_t  Receive_ACK(void)
{
	uint8_t result=0;
	uint8_t cnt=0;
 
	IIC_SCL = 0;
	SDA_IN(); 
	delay_us(4);
 
	IIC_SCL = 1;
	delay_us(4);
 
	while(READ_SDA && (cnt<100))
	{
		cnt++;
	}
 
	IIC_SCL = 0;
	delay_us(4);
 
	if(cnt<100)
	{
		result=1;
	}
	return result;
}
 
 
 
void  Send_ACK(void)
{
	SDA_OUT();
	IIC_SCL = 0;
	delay_us(4);
 
	IIC_SDA = 0;
	delay_us(4);
 
	IIC_SCL = 1;
	delay_us(4);
	IIC_SCL = 0;
	delay_us(4);
 
	SDA_IN();
}
 
 
 
void  SendNot_Ack(void)
{
	SDA_OUT();
	IIC_SCL = 0;
	delay_us(4);
 
	IIC_SDA = 1;
	delay_us(4);
 
	IIC_SCL = 1;
	delay_us(4);
 
	IIC_SCL = 0;
	delay_us(4);
 
	IIC_SDA = 0;
	delay_us(4);
}
 
 
void I2C_WriteByte(uint8_t  input)
{
	uint8_t  i;
	SDA_OUT();
	for(i=0; i<8; i++)
	{
		IIC_SCL = 0;
		delay_ms(5);
 
		if(input & 0x80)
		{
			IIC_SDA = 1;
 
		}
		else
		{
			IIC_SDA = 0;
 
		}
 
		IIC_SCL = 1;
		delay_ms(5);
 
		input = (input<<1);
	}
 
	IIC_SCL = 0;
	delay_us(4);
 
	SDA_IN();
	delay_us(4);
}	
 
 
uint8_t I2C_ReadByte(void)
{
	uint8_t  resultByte=0;
	uint8_t  i=0, a=0;
 
	IIC_SCL = 0;
	SDA_IN();
	delay_ms(4);
 
	for(i=0; i<8; i++)
	{
		IIC_SCL = 1;
		delay_ms(3);
 
		a=0;
		if(READ_SDA)
		{
			a=1;
		}
		else
		{
			a=0;
		}
 
 
		resultByte = (resultByte << 1) | a;
 
		IIC_SCL = 0;
		delay_ms(3);
	}
 
	SDA_IN();
	delay_ms(10);
 
	return   resultByte;
}
 
 
void  set_AHT20sendOutData(void)
{
 
	AHT20sendOutData[3] = AHT20_OutData[0];
	AHT20sendOutData[4] = AHT20_OutData[1];
	AHT20sendOutData[5] = AHT20_OutData[2];
	AHT20sendOutData[6] = AHT20_OutData[3];
 
 
}
 
 
void  I2C_Start(void)
{
	SDA_OUT();
	IIC_SCL = 1;
	delay_ms(4);
 
	IIC_SDA = 1;
	delay_ms(4);
	IIC_SDA = 0;
	delay_ms(4);
 
	IIC_SCL = 0;
	delay_ms(4);
}
 
 
 
void  I2C_Stop(void)
{
	SDA_OUT();
	IIC_SDA = 0;
	delay_ms(4);
 
	IIC_SCL = 1;
	delay_ms(4);
 
	IIC_SDA = 1;
	delay_ms(4);
}
 

bsp_i2c.h

#ifndef __BSP_I2C_H
#define __BSP_I2C_H
 
#include "sys.h"
#include "delay.h"
#include "usart.h"
//ʹÓÃIIC1 ¹ÒÔØM24C02,OLED,LM75AD,HT1382    PB6,PB7
 
#define SDA_IN()  {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}
 
//IO²Ù×÷º¯Êý	 
#define IIC_SCL    PBout(6) //SCL
#define IIC_SDA    PBout(7) //SDA	 
#define READ_SDA   PBin(7)  //ÊäÈëSDA 
 
 
//IICËùÓвÙ×÷º¯Êý
void IIC_Init(void);                //³õʼ»¯IICµÄIO¿Ú				 
void IIC_Start(void);				//·¢ËÍIIC¿ªÊ¼ÐźÅ
void IIC_Stop(void);	  			//·¢ËÍIICÍ£Ö¹ÐźÅ
void IIC_Send_Byte(u8 txd);			//IIC·¢ËÍÒ»¸ö×Ö½Ú
u8 IIC_Read_Byte(unsigned char ack);//IIC¶ÁÈ¡Ò»¸ö×Ö½Ú
u8 IIC_Wait_Ack(void); 				//IICµÈ´ýACKÐźÅ
void IIC_Ack(void);					//IIC·¢ËÍACKÐźÅ
void IIC_NAck(void);				//IIC²»·¢ËÍACKÐźÅ
 
void IIC_WriteByte(uint16_t addr,uint8_t data,uint8_t device_addr);
uint16_t IIC_ReadByte(uint16_t addr,uint8_t device_addr,uint8_t ByteNumToRead);//¼Ä´æÆ÷µØÖ·£¬Æ÷¼þµØÖ·£¬Òª¶ÁµÄ×Ö½ÚÊý  
 
 
void  read_AHT20_once(void);
void  reset_AHT20(void);
void  init_AHT20(void);	
void  startMeasure_AHT20(void);
void  read_AHT20(void);
uint8_t  Receive_ACK(void);
void  Send_ACK(void);
void  SendNot_Ack(void);
void I2C_WriteByte(uint8_t  input);
uint8_t I2C_ReadByte(void);	
void  set_AHT20sendOutData(void);
void  I2C_Start(void);
void  I2C_Stop(void);
#endif
 

sys.c

#ifndef __BSP_I2C_H
#define __BSP_I2C_H
 
#include "sys.h"
#include "delay.h"
#include "usart.h"
 
 
#define SDA_IN()  {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}
 
//IO²Ù×÷º¯Êý	 
#define IIC_SCL    PBout(6) //SCL
#define IIC_SDA    PBout(7) //SDA	 
#define READ_SDA   PBin(7)  //ÊäÈëSDA 
 
 
//IICËùÓвÙ×÷º¯Êý
void IIC_Init(void);                //³õʼ»¯IICµÄIO¿Ú				 
void IIC_Start(void);				//·¢ËÍIIC¿ªÊ¼ÐźÅ
void IIC_Stop(void);	  			//·¢ËÍIICÍ£Ö¹ÐźÅ
void IIC_Send_Byte(u8 txd);			//IIC·¢ËÍÒ»¸ö×Ö½Ú
u8 IIC_Read_Byte(unsigned char ack);//IIC¶ÁÈ¡Ò»¸ö×Ö½Ú
u8 IIC_Wait_Ack(void); 				//IICµÈ´ýACKÐźÅ
void IIC_Ack(void);					//IIC·¢ËÍACKÐźÅ
void IIC_NAck(void);				//IIC²»·¢ËÍACKÐźÅ
 
void IIC_WriteByte(uint16_t addr,uint8_t data,uint8_t device_addr);
uint16_t IIC_ReadByte(uint16_t addr,uint8_t device_addr,uint8_t ByteNumToRead);
 
 
void  read_AHT20_once(void);
void  reset_AHT20(void);
void  init_AHT20(void);	
void  startMeasure_AHT20(void);
void  read_AHT20(void);
uint8_t  Receive_ACK(void);
void  Send_ACK(void);
void  SendNot_Ack(void);
void I2C_WriteByte(uint8_t  input);
uint8_t I2C_ReadByte(void);	
void  set_AHT20sendOutData(void);
void  I2C_Start(void);
void  I2C_Stop(void);
#endif
 

sys.h

#ifndef __SYS_H
#define __SYS_H	
#include "stm32f10x.h"
//	 
 
 
//STM32F103ºËÐÄ°åÀý³Ì
//¿âº¯Êý°æ±¾Àý³Ì
/********** mcudev.taobao.com ³öÆ·  ********/
 
 
 
// 	 
 
//0,²»Ö§³Öucos
//1,Ö§³Öucos
#define SYSTEM_SUPPORT_UCOS		0		//¶¨ÒåϵͳÎļþ¼ÐÊÇ·ñÖ§³ÖUCOS
																	    
	 
//λ´ø²Ù×÷,ʵÏÖ51ÀàËƵÄGPIO¿ØÖƹ¦ÄÜ
//¾ßÌåʵÏÖ˼Ïë,²Î¿¼<<CM3ȨÍþÖ¸ÄÏ>>µÚÎåÕÂ(87Ò³~92Ò³).
//IO¿Ú²Ù×÷ºê¶¨Òå
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
//IO¿ÚµØÖ·Ó³Éä
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C 
#define GPIOB_ODR_Addr    (GPIOB_BASE+12) //0x40010C0C 
#define GPIOC_ODR_Addr    (GPIOC_BASE+12) //0x4001100C 
#define GPIOD_ODR_Addr    (GPIOD_BASE+12) //0x4001140C 
#define GPIOE_ODR_Addr    (GPIOE_BASE+12) //0x4001180C 
#define GPIOF_ODR_Addr    (GPIOF_BASE+12) //0x40011A0C    
#define GPIOG_ODR_Addr    (GPIOG_BASE+12) //0x40011E0C    
 
#define GPIOA_IDR_Addr    (GPIOA_BASE+8) //0x40010808 
#define GPIOB_IDR_Addr    (GPIOB_BASE+8) //0x40010C08 
#define GPIOC_IDR_Addr    (GPIOC_BASE+8) //0x40011008 
#define GPIOD_IDR_Addr    (GPIOD_BASE+8) //0x40011408 
#define GPIOE_IDR_Addr    (GPIOE_BASE+8) //0x40011808 
#define GPIOF_IDR_Addr    (GPIOF_BASE+8) //0x40011A08 
#define GPIOG_IDR_Addr    (GPIOG_BASE+8) //0x40011E08 
 
//IO¿Ú²Ù×÷,Ö»¶Ôµ¥Ò»µÄIO¿Ú!
//È·±£nµÄֵСÓÚ16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //Êä³ö 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //ÊäÈë 
 
#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //Êä³ö 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //ÊäÈë 
 
#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //Êä³ö 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //ÊäÈë 
 
#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //Êä³ö 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //ÊäÈë 
 
#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //Êä³ö 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //ÊäÈë
 
#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //Êä³ö 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //ÊäÈë
 
#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //Êä³ö 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //ÊäÈë
 
 
 
void NVIC_Configuration(void);
 
 
 
#endif
 

main.c 

#include "delay.h"
#include "usart.h"
#include "bsp_i2c.h"
#include "stm32f10x.h"                  // Device header
 
 
int main(void)
{	
	delay_init();     //ÑÓʱº¯Êý³õʼ»¯	  
	uart_init(115200);	 //´®¿Ú³õʼ»¯Îª115200
	IIC_Init();
		while(1)
	{
		printf("¿ªÊ¼²âÁ¿£¬ÇëÉԵȣº");
		read_AHT20_once();
		delay_ms(1500);
  }
}

 在keil的文件栏的User下添加上述文件,然后尝试烧录运行,查看实现结果。

结果展示

 

总结

       在学习STM32单片机及其应用的过程中,完成了使用I2C协议与AHT20温湿度传感器进行数据采集,并通过串口输出的任务。我学习了I2C总线通信协议的两种实现方式:硬件I2C和软件I2C。硬件I2C利用单片机内部的硬件模块直接控制I2C总线,具有高效稳定的特点,适用于对时序要求严格的应用场景;而软件I2C则通过软件编码模拟I2C通信协议,灵活性更强,适用于特定设计需求。理解并实现了这两种方式为后续的项目实施奠定了基础。深入阅读AHT20传感器的数据手册后,我了解了其基于电容式湿度测量和温度传感器的工作原理,并学习了如何在STM32单片机上配置和操作该传感器。最终,我成功实现了每隔2秒钟从AHT20传感器采集一次温度和湿度数据,并将这些数据实时显示在连接的OLED屏幕上,同时通过串口发送到上位机的串口助手软件。这一过程不仅提升了我的嵌入式系统设计和开发能力,还加深了我对理论知识与实际应用结合的认识。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1853785.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【python】美妆类商品跨境电商数据分析(源码+课程论文+数据集)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

动手学深度学习(Pytorch版)代码实践 -计算机视觉-37微调

37微调 import os import torch import torchvision from torch import nn import liliPytorch as lp import matplotlib.pyplot as plt from d2l import torch as d2l# 获取数据集 d2l.DATA_HUB[hotdog] (d2l.DATA_URL hotdog.zip,fba480ffa8aa7e0febbb511d181409f899b9baa5…

setInterval 定时任务执行时间不准验证

一般在处理定时任务的时候都使用setInterval间隔定时调用任务。 setInterval(() > {console.log("interval"); }, 2 * 1000);我们定义的是两秒执行一次&#xff0c;但是浏览器实际执行的间隔时间只多不少。这是由于浏览器执行 JS 是单线程模式&#xff0c;使用se…

二进制炸弹的fp是什么?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

Go日常分享 - error类型是指针类型吗?

背景 这个问题的产生来源于小泉在开发rpc接口时返回error遇到的问题&#xff0c;开发时想在defer里对err进行最终的统一处理赋值&#xff0c;发现外层接收一直都未生效。问题可以简化为成下面的小demo。 func returnError() error {var err errordefer func() {//err errors…

PMBOK® 第六版 管理项目知识

目录 读后感—PMBOK第六版 目录 在前面的文章中&#xff0c;输入环节都可以看见有事业环境因素、组织过程资产&#xff1b;工具与技术都有专家判断。都是说明知识的重要性。 虽然项目具有其独特的、唯一性&#xff0c;但项目相关的经验却能如同家族传承般&#xff0c;被持续地…

【Python】已解决:安装python-Levenshtein包时遇到的subprocess-exited-with-error问题

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例及解决方案五、注意事项 已解决&#xff1a;安装python-Levenshtein包时遇到的subprocess-exited-with-error问题 一、分析问题背景 在安装python-Levenshtein这个Python包时&#xff0c;有时会…

基于Java的火车订票管理系统【附源码】

火车订票管理登录 摘要&#xff1a;随着我国铁路交通的不断发展&#xff0c;简单的窗口售票模式已经不能满足方便人们出行的目的。采用先进的网络技术开发出方便快捷的火车票订票系统是现代客运业务发展的必然需求。本次设计的火车票订票系统通过访问主页&#xff0c;可以实现…

196.每日一题:检测大写字母(力扣)

代码解决 class Solution { public:bool detectCapitalUse(string word) {int capitalCount 0;int n word.size();// 统计大写字母的数量for (char c : word) {if (isupper(c)) {capitalCount;}}// 检查是否满足三种情况之一if (capitalCount n) {// 全部字母都是大写return…

[最全]设计模式实战(一)UML六大原则

UML类图 UML类图是学习设计模式的基础,学习设计模式,主要关注六种关系。即:继承、实现、组合、聚合、依赖和关联。 UML类图基本用法 继承关系用空心三角形+实线来表示。实现接口用空心三角形+虚线来表示。eg:大雁是最能飞的,它实现了飞翔接口。 关联关系用实线箭头来表示…

Python武器库开发-武器库篇之ThinkPHP 5.0.23-RCE 漏洞复现(六十四)

Python武器库开发-武器库篇之ThinkPHP 5.0.23-RCE 漏洞复现&#xff08;六十四&#xff09; 漏洞环境搭建 这里我们使用Kali虚拟机安装docker并搭建vulhub靶场来进行ThinkPHP漏洞环境的安装&#xff0c;我们进入 ThinkPHP漏洞环境&#xff0c;可以 cd ThinkPHP&#xff0c;然…

c#使用自带库对字符串进行AES加密、解密

文章目录 1 代码1.1 定义Aes加密类块1.2 在主函数中调用 2 获取Key和IV2.1 基本方法2.2 自定义Key2.3 技术方面的原理 参考文章&#xff1a; C#软件加密实例&#xff1f; 。 参考官文&#xff1a; Aes 类。 在使用C#的自带的System.Security.Cryptography.Aes模块进行加密和解…

mediasoup 源码分析 (八)分析PlainTransport

mediasoup 源码分析 &#xff08;六&#xff09;分析PlainTransport 一、接收裸RTP流二、mediasoup 中udp建立过程 tips 一、接收裸RTP流 PlainTransport 可以接收裸RTP流&#xff0c;也可以接收AES加密的RTP流。源码中提供了一个通过ffmpeg发送裸RTP流到mediasoup的脚本&…

基于PyTorch设计的全景图合成系统【文末完整工程源码下载】

前言 本项目实现基于PyTorch将多张图片合成为一张全景图。&#xff08;图像存储路径为/images/1&#xff09;。 作者&#xff1a;阿齐Archie&#xff08;联系我微信公众号&#xff1a;阿齐Archie&#xff09; 使用的图片为&#xff1a; 合成后为&#xff1a; 这个全景图项目主…

eNSP启动设备失败,错误代码40,网卡配置正常,虚拟机导致的错误解决过程

安装eNSP后出现以下错误。 按照帮助文档&#xff0c;查看了相关软件&#xff0c;尤其是vitualbox的版本以及网卡问题。网卡设置正常&#xff0c;vitualbox也匹配成功。 附&#xff1a;vitualbox各个版本的下载地址&#xff1a; 关于网卡名称的修改方法&#xff0c;参照博客 …

python实现技术指标(简单移动平均,加权移动平均线,指数移动平均线)

移动平均线是最常见的技术指标&#xff0c;它能够去除时间序列的短期波动&#xff0c;使得数据变得平滑&#xff0c;从而可以方便看出序列的趋势特征。常见的移动平均线有简单移动平均线&#xff0c;加权移动平均线&#xff0c;指数移动平均线。 一. 简单移动平均(SMA) 简单移…

2.超声波测距模块

1.简介 2.超声波的时序图 3.基于51单片机实现的代码 #include "reg52.h" #include "intrins.h" sbit led1P3^7;//小于10&#xff0c;led1亮&#xff0c;led2灭 sbit led2P3^6;//否则&#xff0c;led1灭&#xff0c;led2亮 sbit trigP1^5; sbit echo…

电容的命名规则

给如下参数给采购&#xff0c;就可以获取 还有一些参数需要重视 容值随着环境温度而保持的程度 常规应用时是可以不用看材质&#xff0c;但是如果使用在新能源汽车和极端环境下的电子产品&#xff0c;就需要关注材质&#xff0c;曾有供应商把可用级电容供应车企&#xff0c;导致…

动手学深度学习(Pytorch版)代码实践 -计算机视觉-36图像增广

6 图片增广 import matplotlib.pyplot as plt import numpy as np import torch import torchvision from d2l import torch as d2l from torch import nn from PIL import Image import liliPytorch as lp from torch.utils.data import Dataset, DataLoaderplt.figure(cat)…

8.DELL R730服务器对RAID5进行扩容

如果服务器的空间不足了&#xff0c;如何进行扩容&#xff1f;我基本上按照如何重新配置虚拟磁盘或添加其他硬盘来进行操作。我的机器上已经有三块硬盘了&#xff0c;组了Raid5&#xff0c;现在再添加一块硬盘。 先把要添加的硬盘插入服务器&#xff0c;无论是在IDRAC还是管理…