一、原理图
根据数据手册中的要求(以上两图),可以看出SDA和SCL接主芯片的引脚就可以,但是注意我们在接的过程中,给了两个上拉电阻。
上拉电阻的作用:
- 提高抗干扰能力:适当的上拉电阻值可以帮助减少外部噪声对信号的影响,增强系统的稳定性。
- 降低功耗:相对于内部集成的上拉结构,外部选择合适的上拉电阻可以在一定程度上帮助控制整个电路的静态电流消耗。
ADR需要接地,NC就保持悬空,悬空就是保持直接什么也不接就可以。
二、原理介绍
1、启动与停止信号
首先,一个完整的传输序列一定是启动信号开始,停止信号结尾的,无论这个信号要给相应的器件传输何种信号。
启动信号的执行顺序与原理
这里的启动信号确切的说,就是I2C协议中的启动信号,如果大家了解I2C协议的启动原理,这里完全可以跳过,包括下面的停止信号
启动传输状态( S )当 SCL 为高电平时,SDA 由高电平转换为低电平。因此一般在写启动信号时,我们都会先把SCL时钟线和SDA数据线先同时置高位,然后把SDA置低位,这个时候就可以百分百保证是在SCL时钟线高电平期间将SDA置低位,形成了开始信号
/**
* 函 数:I2C起始
* 参 数:无
* 返 回 值:无
*/
void MyI2C_Start(void)
{
MyI2C_W_SDA(1); //释放SDA,确保SDA为高电平
MyI2C_W_SCL(1); //释放SCL,确保SCL为高电平
MyI2C_W_SDA(0); //在SCL高电平期间,拉低SDA,产生起始信号
MyI2C_W_SCL(0); //起始后把SCL也拉低,即为了占用总线,也为了方便总线时序的拼接
}
最后将时钟线置低位的作用(强调一下,在正常发送数据期间,当SCL高电平期间,SDA不可以发生跳变,以保证数据稳定,产生启动和停止信号的时候除外)
- 控制总线:一旦产生起始条件后,主设备需要继续控制总线,防止其他可能成为主设备的节点抢占总线。
- 同步时序:将SCL拉低可以确保下一个上升沿之前有足够的时间进行数据设置。I²C通信中的每一位数据都是在SCL的上升沿采样的。因此,在发送下一位数据之前,先将SCL保持在低电平状态,可以让发送方有时间改变SDA的状态。
- 准备传输:它还允许系统准备好接下来的数据位或地址位的发送。通过这种方式,可以确保每次SCL上升沿时,SDA上的数据已经被正确设置了。
停止信号的执行顺序与原理
停止传输状态(P)-当SCL 高电平时,SDA 线上从 低电平转换为高电平。停止状态是由主机控制的一种特殊的 总线状态,指示从机传输结束(Stop 之后,BUS 总线一般被 认为处于闲置状态)。
/**
* 函 数:I2C终止
* 参 数:无
* 返 回 值:无
*/
void MyI2C_Stop(void)
{
MyI2C_W_SDA(0); //拉低SDA,确保SDA为低电平
MyI2C_W_SCL(1); //释放SCL,使SCL呈现高电平
MyI2C_W_SDA(1); //在SCL高电平期间,释放SDA,产生终止信号
}
先拉低SDA数据线,拉高时钟线,再拉低数据线,保证绝对产生停止信号
2、发送测量命令函数
将图中所示信息构成中的二进制转化为十六进制分别是0x70、0xAC、0x33、0x00。
/*
*********************************************************************************************************
* 函 数 名: void AHT10_SendAC(void)
* 功能说明: 触发传感器进行测量
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void AHT10_SendAC(void)
{
MyI2C_Start();
MyI2C_SendByte(0x70);
MyI2C_ReceiveAck();
MyI2C_SendByte(0xAC);
MyI2C_ReceiveAck();
MyI2C_SendByte(0x33);
MyI2C_ReceiveAck();
MyI2C_SendByte(0x00);
MyI2C_ReceiveAck();
MyI2C_Stop();
}
3、软复位函数
这个命令(见表9)用于在无需关闭和再次打 开电源的情况下,重新启动传感器系统。在接 收到这个命令之后,传感器系统开始重新初始 化,并恢复默认设置状态,软复位所需时间不超过20毫秒
/*
*********************************************************************************************************
* 函 数 名: void AHT10_SendBA(void)
* 功能说明: 软复位模块
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void AHT10_SendBA(void)
{
MyI2C_Start();
MyI2C_SendByte(0x70);
MyI2C_ReceiveAck();
MyI2C_SendByte(0xBA);
MyI2C_ReceiveAck();
MyI2C_Stop();
}
4、读取检测到的数据
根据上图所示,读取信息的过程应该是发送读信号,写入0x71,这个信号意味着要读取信息了,其中蓝色代表的是从机发过来的数据,第一位字节代表的是状态信息,可以忽略,后面五个字节对半分为湿度和温度的对应的字节
/*
*********************************************************************************************************
* 函 数 名: void AHT10_Read_CTdata(uint32_t *ct) //读取AHT10的温度和湿度数据
* 功能说明:
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void AHT10_Read_CTdata(uint32_t *ct) //读取AHT10的温度和湿度数据
{
uint8_t Byte_1th = 0;
uint8_t Byte_2th = 0;
uint8_t Byte_3th = 0;
uint8_t Byte_4th = 0;
uint8_t Byte_5th = 0;
uint8_t Byte_6th = 0;
uint32_t RetuData = 0;
uint16_t cnt = 0;
AHT10_SendAC(); //向AHT10发送AC指令
Delay_ms(75); //等待75ms
cnt = 0;
while(((AHT10_Read_Status()&0x80)==0x80))//等待忙状态结束
{
Delay_us(1508);
if(cnt++>=100)
{
break;
}
}
MyI2C_Start();
MyI2C_SendByte(0x71);
MyI2C_ReceiveAck();
Byte_1th = MyI2C_ReceiveByte();
MyI2C_ReceiveAck();
Byte_2th = MyI2C_ReceiveByte();
MyI2C_ReceiveAck();
Byte_3th = MyI2C_ReceiveByte();
MyI2C_ReceiveAck();
Byte_4th = MyI2C_ReceiveByte();
MyI2C_ReceiveAck();
Byte_5th = MyI2C_ReceiveByte();
MyI2C_ReceiveAck();
Byte_6th = MyI2C_ReceiveByte();
MyI2C_ReceiveAck();
MyI2C_Stop();
RetuData = (RetuData|Byte_2th)<<8;
RetuData = (RetuData|Byte_3th)<<8;
RetuData = (RetuData|Byte_4th);
RetuData =RetuData >>4;
ct[0] = RetuData;
RetuData = 0;
RetuData = (RetuData|Byte_4th)<<8;
RetuData = (RetuData|Byte_5th)<<8;
RetuData = (RetuData|Byte_6th);
RetuData = RetuData&0xfffff;
ct[1] =RetuData;
}
5、AHT10初始化函数
/*
*********************************************************************************************************
* 函 数 名: uint8_t AHT10_Init(void)//初始化AHT10
* 功能说明: AHT10初始化函数
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
uint8_t AHT10_Init(void)//初始化AHT10
{
uint16_t count=0;
MyI2C_Start();
MyI2C_SendByte(0x70); //进行写操作
MyI2C_ReceiveAck();
MyI2C_SendByte(0xE1); //写系统配置寄存器
MyI2C_ReceiveAck();
MyI2C_SendByte(0x08); //校准位置1
MyI2C_ReceiveAck();
MyI2C_SendByte(0x00);
MyI2C_ReceiveAck();
MyI2C_Stop();
Delay_ms(500);
while( AHT10_Read_Cal_Enable() == 0)//如果校准位没有置1,就一直初始化
{
AHT10_SendBA();
Delay_ms(100);
MyI2C_Start();
MyI2C_SendByte(0x70); //进行写操作
MyI2C_ReceiveAck();
MyI2C_SendByte(0xE1); //写系统配置寄存器
MyI2C_ReceiveAck();
MyI2C_SendByte(0x08); //校准位置1
MyI2C_ReceiveAck();
MyI2C_SendByte(0x00);
MyI2C_ReceiveAck();
MyI2C_Stop();
if(count++>=10)return 0;
Delay_ms(500);
}
return 1;
}
6、应答位
大家一路看过来,应该发现每段信号的发送都有一个MyI2C_ReceiveAck();不要急,我现在就给大家解释一下这里。
应答位的作用
- 确认数据接收:当从设备正确接收到主设备发送的数据字节后,它会通过拉低SDA线来发送一个应答位,表示已经成功接收了数据。
- 错误检测:如果从设备没有准备好接收数据或未能识别出其地址,则不会响应应答位,SDA保持高电平(称为NACK, No Acknowledge)。这可以提示主设备存在通信问题,如地址不匹配、从设备忙或未准备好等。
- 控制流:在某些情况下,例如读操作期间,主设备也可以发送NACK来通知从设备停止发送更多的数据。
应答时序
- 在每个数据字节的第9个时钟周期(即最后一个时钟周期),SCL为高时,SDA的状态由接收方决定:
-
- 如果是应答(ACK),则SDA会被拉低;
- 如果是非应答(NACK),则SDA保持高电平。
/**
* 函 数:I2C接收应答位
* 参 数:无
* 返 回 值:接收到的应答位,范围:0~1,0表示应答,1表示非应答
*/
uint8_t MyI2C_ReceiveAck(void)
{
uint8_t AckBit; //定义应答位变量
MyI2C_W_SDA(1); //接收前,主机先确保释放SDA,避免干扰从机的数据发送
MyI2C_W_SCL(1); //释放SCL,主机机在SCL高电平期间读取SDA
AckBit = MyI2C_R_SDA(); //将应答位存储到变量里
MyI2C_W_SCL(0); //拉低SCL,开始下一个时序模块
return AckBit; //返回定义应答位变量
}
三、源码分享
AHT10.c
#include <stm32f10x.h>
#include "I2C.h"
#include "Delay.h"
#include "uart.h"
#include "SHT10.h"
#define AHT10_ADDRESS 0x38 //Sht10的I2C从机地址
/*
*********************************************************************************************************
* 函 数 名: uint8_t AHT10_Read_Status(void)
* 功能说明: 读取AHT10状态寄存器
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
uint8_t AHT10_Read_Status(void)
{
uint8_t Byte_first;
MyI2C_Start();
MyI2C_SendByte(0x71);
MyI2C_ReceiveAck();
Byte_first = MyI2C_ReceiveByte();
MyI2C_Stop();
return Byte_first;
}
/*
*********************************************************************************************************
* 函 数 名: uint8_t AHT10_Read_Cal_Enable(void)
* 功能说明: 查询校准使能位有没有使能
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
uint8_t AHT10_Read_Cal_Enable(void)
{
uint8_t val = 0;
val = AHT10_Read_Status();
if((val & 0x68)==0x08) //判断NOR模式和校准输出是否有效
return 1;
else return 0;
}
/*
*********************************************************************************************************
* 函 数 名: void AHT10_SendAC(void)
* 功能说明: 触发传感器进行测量
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void AHT10_SendAC(void)
{
MyI2C_Start();
MyI2C_SendByte(0x70);
MyI2C_ReceiveAck();
MyI2C_SendByte(0xAC);
MyI2C_ReceiveAck();
MyI2C_SendByte(0x33);
MyI2C_ReceiveAck();
MyI2C_SendByte(0x00);
MyI2C_ReceiveAck();
MyI2C_Stop();
}
/*
*********************************************************************************************************
* 函 数 名: void AHT10_SendBA(void)
* 功能说明: 软复位模块
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void AHT10_SendBA(void)
{
MyI2C_Start();
MyI2C_SendByte(0x70);
MyI2C_ReceiveAck();
MyI2C_SendByte(0xBA);
MyI2C_ReceiveAck();
MyI2C_Stop();
}
/*
*********************************************************************************************************
* 函 数 名: void AHT10_Read_CTdata(uint32_t *ct) //读取AHT10的温度和湿度数据
* 功能说明:
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void AHT10_Read_CTdata(uint32_t *ct) //读取AHT10的温度和湿度数据
{
uint8_t Byte_1th = 0;
uint8_t Byte_2th = 0;
uint8_t Byte_3th = 0;
uint8_t Byte_4th = 0;
uint8_t Byte_5th = 0;
uint8_t Byte_6th = 0;
uint32_t RetuData = 0;
uint16_t cnt = 0;
AHT10_SendAC(); //向AHT10发送AC指令
Delay_ms(75); //等待75ms
cnt = 0;
while(((AHT10_Read_Status()&0x80)==0x80))//等待忙状态结束
{
Delay_us(1508);
if(cnt++>=100)
{
break;
}
}
MyI2C_Start();
MyI2C_SendByte(0x71);
MyI2C_ReceiveAck();
Byte_1th = MyI2C_ReceiveByte();
MyI2C_ReceiveAck();
Byte_2th = MyI2C_ReceiveByte();
MyI2C_ReceiveAck();
Byte_3th = MyI2C_ReceiveByte();
MyI2C_ReceiveAck();
Byte_4th = MyI2C_ReceiveByte();
MyI2C_ReceiveAck();
Byte_5th = MyI2C_ReceiveByte();
MyI2C_ReceiveAck();
Byte_6th = MyI2C_ReceiveByte();
MyI2C_ReceiveAck();
MyI2C_Stop();
RetuData = (RetuData|Byte_2th)<<8;
RetuData = (RetuData|Byte_3th)<<8;
RetuData = (RetuData|Byte_4th);
RetuData =RetuData >>4;
ct[0] = RetuData;
RetuData = 0;
RetuData = (RetuData|Byte_4th)<<8;
RetuData = (RetuData|Byte_5th)<<8;
RetuData = (RetuData|Byte_6th);
RetuData = RetuData&0xfffff;
ct[1] =RetuData;
}
/*
*********************************************************************************************************
* 函 数 名: uint8_t AHT10_Init(void)//初始化AHT10
* 功能说明: AHT10初始化函数
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
uint8_t AHT10_Init(void)//初始化AHT10
{
uint16_t count=0;
MyI2C_Start();
MyI2C_SendByte(0x70);
MyI2C_ReceiveAck();
MyI2C_SendByte(0xE1);
MyI2C_ReceiveAck();
MyI2C_SendByte(0x08);
MyI2C_ReceiveAck();
MyI2C_SendByte(0x00);
MyI2C_ReceiveAck();
MyI2C_Stop();
Delay_ms(500);
while( AHT10_Read_Cal_Enable() == 0)
{
AHT10_SendBA();
Delay_ms(100);
MyI2C_Start();
MyI2C_SendByte(0x70);
MyI2C_ReceiveAck();
MyI2C_SendByte(0xE1);
MyI2C_ReceiveAck();
MyI2C_SendByte(0x08);
MyI2C_ReceiveAck();
MyI2C_SendByte(0x00);
MyI2C_ReceiveAck();
MyI2C_Stop();
if(count++>=10)return 0;
Delay_ms(500);
}
return 1;
}
/*
*********************************************************************************************************
* 函 数 名: Get_Value()
* 功能说明: 获取温湿度的值
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void Set_temp_humid()
{
float c1,t1;
uint32_t CT_data[2];
while(AHT10_Read_Cal_Enable()==0)
{
AHT10_Init();
Delay_ms(10);
}
AHT10_Read_CTdata(CT_data); //读取温度和湿度 , 可间隔1.5S读一次
c1= CT_data[0]*1000/1024/1024/10.00; //计算得到湿度值(放大了10倍,如果c1=523,表示现在湿度为52.3%)
t1= (CT_data[1] *200*10/1024/1024-500)/10.00;//计算得到温度值(放大了10倍,如果t1=245,表示现在温度为24.5℃)
Serial_Printf("湿度:%.2f,温度:%.2f",c1,t1);
}
AHT10.h
#ifndef __SHT10_H
#define __SHT10_H
uint8_t AHT10_Read_Status(void);//读取状态
uint8_t AHT10_Read_Cal_Enable(void);//检测校准位
uint8_t AHT10_Init(void);//初始化AHT10
void AHT10_SendAC(void);//发送触发检测信号
void AHT10_SendBA(void);//软复位信号
void AHT10_Read_CTdata(uint32_t *ct);//读取AHT10的温度和湿度数据
void Set_temp_humid();//计算湿度和温度
#endif
main.c
#include "stm32f10x.h" // Device header
#include "uart.h"
#include "Sht10.h"
#include "I2C.h"
#include "Delay.h"
uint8_t RxData;
int main(void)
{
UART_Init();
MyI2C_Init();
AHT10_Init();
while (1)
{
// Serial_Printf("湿度:%.2f,温度:%.2f",AHT10_Data -> humi,AHT10_Data -> temp);
Set_temp_humid();
Delay_s(2);
}
}
四、工程分享
以下是我分享的资源,我设置了免费下载,大家同时可以在评论区留言邮箱