前言:
AHT20温湿度传感器使用的是IIC数据传输模式,他精度高,误差低,检测快,数据稳定,博主用它来代替DHT11来开发的。
本着互帮互助的原则,欢迎路过的大佬指点!!也欢迎调不会的同志留言、私信,很着急的可轻易加Q:1426108484
硬件说明:
软件须知:
博主最近一直在座手搓IIC这个事情,好多时候只需要回复ACK就可以,但是AHT20需要在CRC校验位之后回复NACK!!!自己手搓IIC的家人请给格外注意。
以下是手册给的读取流程:
手册说详见官网历程,但是博主并未没找到相关的寄存器操作,所以博主自己总结了一下流程仅供参考【反正我跑起来了(^v^)】
官网链接:AHT20集成式温湿度传感器-温湿度传感器-温湿度传感器 温湿度芯片 温湿度变送器模块 气体传感器 流量传感器 广州奥松电子股份有限公司 (aosong.com)
博主手搓的IIC流程图:
/*=======================第一次发送开始检测指令唤醒======================*/
1、开始信号-->发送0x70-->检测ACK-->发送0xAC-->检测ACK-->发送0x33-->检测ACK-->发送0x00-->检测ACK-->结束信号
/*======================第二次发送读取信号读取数据并处理==================*/
2、至少等待80ms-->开始信号-->发送0x71-->检测ACK-->
{ 读取数据-->回复ACK(0)}持续6次(data[0]~data[5])-->读取数据(CRC)-->回复NACK(1)-->结束信号
手册给出的信号处理:
附带代码,请按需拿取(o^v^o)
Delay.h
#ifndef __DELAY_H
#define __DELAY_H
#include "main.h"
void My_Delay_us_init(uint8_t SYSCLK);
void My_Delay_us(uint32_t nus);
void My_Delay_ms(uint32_t nms);
#endif
Delay.c
#include "Delay.h"
uint32_t fac_us;
/*==================延时标定函数====================*/
/* 参数uint8_t SYSCLK是系统时钟 */
/*==================================================*/
void My_Delay_us_init(uint8_t SYSCLK)
{
fac_us=SYSCLK;
}
/*==================纳秒级别的Delay==================*/
void My_Delay_us(uint32_t nus)
{
uint32_t ticks;
uint32_t told,tnow,tcnt=0;
uint32_t reload=SysTick->LOAD;
ticks=nus*fac_us;
told=SysTick->VAL;
while(1)
{
tnow=SysTick->VAL;
if(tnow!=told)
{
if(tnow<told)tcnt+=told-tnow;
else tcnt+=reload-tnow+told;
told=tnow;
if(tcnt>=ticks)break;
}
};
}
/*==================毫秒级别的Delay==================*/
void My_Delay_ms(uint32_t nms)
{
while(nms--)
{
My_Delay_us(1000);
}
}
AHT20.h
#ifndef __AHT20_H
#define __AHT20_H
#include "main.h"
#include "Delay.h"
/*=========================地址信息=========================*/
#define AHT20_Address 0x70 //AHT20的设备唯一地址0111000(0),1为读取,0为写入
/*=========================命令指令=========================*/
#define AHT20_Init_Command 0xe1 //AHT20初始化命令11100001
#define AHT20_Read_Command 0xac //AHT20读取数据命令10101100
#define AHT20_Resat_Command 0xba //AHT20软件复位命令10111010(软件复位时间不超过20ms)
/*-----------------温度读取函数----------------------------*/
void AHT20_RecvData(GPIO_TypeDef *sda_GPIO, uint16_t sda_Pin, GPIO_TypeDef *scl_GPIO, uint16_t scl_Pin, float* Data);
/*-----------------函数内部需要的支持函数------------------*/
void AHT20_Start(void); //开始信号
void AHT20_Stop(void); //结束信号
static void GPIO_Write(void); //设置SDA为输出模式
static void GPIO_Read(void); //设置SDA为输入模式
void AHT20_SendACK(int ack); //发送ACK信号
int AHT20_RecvACK(void); //读取ACK信号
uint8_t AHT20_RecvByte(uint8_t ACK); //读取一个字节的数据
void AHT20_SendByte(uint8_t dat); //写入一个字节的数据
#endif
AHT20.c
#include "AHT20.h"
uint8_t CheckCrc8(unsigned char *pDat,unsigned char Lenth); //CRC校验声明
/*=====================全局变量部分======================*/
static GPIO_InitTypeDef GPIO_InitStruct; //修改GPIO用的结构体
/*------------记录初始化函数传来的两线管脚信息----------*/
static GPIO_TypeDef *SDA_GPIO = NULL; //sda总线数据
static uint16_t SDA_Pin; //sda管脚数据
static GPIO_TypeDef *SCL_GPIO = NULL; //scl总线数据
static uint16_t SCL_Pin; //scl管脚数据
static uint8_t Address = AHT20_Address; //BH1750地址数据,默认为低电平
/*===========================读取函数============================*/
/* 参数列表: */
/* GPIO_TypeDef *sda_GPIO 为SDA总线信息 */
/* uint16_t sda_Pin 为SDA管脚信息 */
/* GPIO_TypeDef *scl_GPIO 为SCL总线信息 */
/* uint16_t scl_Pin 为SCL管脚信息 */
/* float* Data 传递一个成员为2的float数组首地址*/
/*===============================================================*/
void AHT20_RecvData(GPIO_TypeDef *sda_GPIO, uint16_t sda_Pin, \
GPIO_TypeDef *scl_GPIO, uint16_t scl_Pin, float* Data)
{
uint32_t Int_Date = 0; //将数据从char转成float的过度
uint8_t read_Byte[7]; //读取数据用的中间数组
uint8_t i = 0;
/*---------------将数据存入到全局变量方便移植----------------*/
SDA_GPIO = sda_GPIO;
SDA_Pin = sda_Pin;
SCL_GPIO = scl_GPIO;
SCL_Pin = scl_Pin;
/*------------------------开始读取数据------------------------*/
/*-------------------第一次写入指令开始检测------------------*/
AHT20_Start(); //开始信号
AHT20_SendByte(Address); //发送地址与写命令
AHT20_SendByte(0xAC); //发送检测命令
AHT20_SendByte(0X33); //两条检测内部指令
AHT20_SendByte(0x00);
AHT20_Stop(); //停止信号
/*------------------中间需要等待至少80毫秒-------------------*/
My_Delay_ms(80); //这里延时100ms
/*------------------------开始读取数据------------------------*/
AHT20_Start(); //开始信号
AHT20_SendByte(Address | 1); //发送地址与读信号
/*----------------------连续读取7位数据-----------------------*/
/* 0是状态字用作判断,1,2,3的一半是湿度数据 */
/* 3的一半,4,5,是温度数据,6是CRC8校验位 */
for(i = 0; i < 6; i++)
{
read_Byte[i] = AHT20_RecvByte(0);
}
read_Byte[6] = AHT20_RecvByte(1);
AHT20_Stop(); //停止信号
/*---------------------对数据进行校验处理--------------------*/
if((CheckCrc8(&read_Byte[0], 6) == read_Byte[6]) && ((read_Byte[0]&0x98) == 0x18))
{
//将湿度数据整合
Int_Date = read_Byte[1];
Int_Date <<= 8;
Int_Date += read_Byte[2];
Int_Date <<= 8;
Int_Date += read_Byte[3];
Int_Date >>= 4;
//将湿度数据转化赋值
Data[0] = Int_Date;
Data[0] = Data[0] *100/1048576;
//将温度数据整合
Int_Date = read_Byte[3] & 0x0f;
Int_Date <<= 8;
Int_Date += read_Byte[4];
Int_Date <<= 8;
Int_Date += read_Byte[5];
//将湿度数据整合
Data[1] = Int_Date;
Data[1] = Data[1] * 200 / 1048576 - 50;
}
}
/*=======================功能函数:开始信号=====================*/
/*开始信号为SCL高时SDA由高拉低至少250nm,SCL跟着拉低 */
/*===============================================================*/
void AHT20_Start()
{
HAL_GPIO_WritePin(SDA_GPIO, SDA_Pin,GPIO_PIN_SET); //拉高数据线
HAL_GPIO_WritePin(SCL_GPIO, SCL_Pin,GPIO_PIN_SET); //拉高时钟线
//在HAL库的初始阶段设置两个GPIO为输出模式高电平,这里为了保险再次设置;
My_Delay_us(2);
HAL_GPIO_WritePin(SDA_GPIO, SDA_Pin ,GPIO_PIN_RESET); //产生下降沿
My_Delay_us(2);
HAL_GPIO_WritePin(SCL_GPIO, SCL_Pin,GPIO_PIN_RESET); //拉低时钟线
My_Delay_us(2);
}
/*=======================功能函数:停止信号=====================*/
/*停止信号为SCL高时SDA由低拉高至少250nm的动作 */
/*===============================================================*/
void AHT20_Stop()
{
HAL_GPIO_WritePin(SDA_GPIO, SDA_Pin,GPIO_PIN_RESET); //拉低数据线
HAL_GPIO_WritePin(SCL_GPIO, SCL_Pin,GPIO_PIN_SET); //拉高时钟线
My_Delay_us(2);
HAL_GPIO_WritePin(SDA_GPIO, SDA_Pin ,GPIO_PIN_SET); //产生上升沿
My_Delay_us(2);
}
/*==========================发送应答信号========================*/
/* 参数列表:ack (0:ACK 1:NAK) */
/*===============================================================*/
void AHT20_SendACK(int ack)
{
GPIO_Write();
if(ack == 1) //写应答信号
HAL_GPIO_WritePin(SDA_GPIO, SDA_Pin,GPIO_PIN_SET);
else if(ack == 0)
HAL_GPIO_WritePin(SDA_GPIO, SDA_Pin,GPIO_PIN_RESET);
else
return;
HAL_GPIO_WritePin(SCL_GPIO, SCL_Pin,GPIO_PIN_SET);
My_Delay_us(2);
HAL_GPIO_WritePin(SCL_GPIO, SCL_Pin,GPIO_PIN_RESET);
My_Delay_us(2);
}
/*========================接收应答信号=======================*/
/* 这里没有起到任何作用,没有ACK照样跑~~ */
/*============================================================*/
int AHT20_RecvACK()
{
int ACK = 0;
GPIO_Read();
HAL_GPIO_WritePin(SCL_GPIO, SCL_Pin,GPIO_PIN_SET); //拉高时钟线
My_Delay_us(2);
if(HAL_GPIO_ReadPin(SDA_GPIO, SDA_Pin) == 1 ) //读应答信号
ACK = 1 ;
else
ACK = 0 ;
HAL_GPIO_WritePin(SCL_GPIO, SCL_Pin,GPIO_PIN_RESET); //拉低时钟线
My_Delay_us(2);
GPIO_Write();
return ACK;
}
/*=================向iic总线发送一个字节数据================*/
/* 参数列表:dat——想要发送的数据 */
/*===========================================================*/
void AHT20_SendByte(uint8_t dat)
{
uint8_t i;
for (i=0; i<8; i++) //8位计数器
{
if( 0X80 & dat )
HAL_GPIO_WritePin(SDA_GPIO, SDA_Pin,GPIO_PIN_SET);
else
HAL_GPIO_WritePin(SDA_GPIO, SDA_Pin,GPIO_PIN_RESET);
dat <<= 1;
HAL_GPIO_WritePin(SCL_GPIO, SCL_Pin,GPIO_PIN_SET); //拉高时钟线
My_Delay_us(2);
HAL_GPIO_WritePin(SCL_GPIO, SCL_Pin,GPIO_PIN_RESET); //拉低时钟线
My_Delay_us(2);
}
AHT20_RecvACK();
}
/*=================从iic总线读取一个字节地址================*/
/* 返回值为读取到的一个字节数据 */
/*===========================================================*/
uint8_t AHT20_RecvByte(uint8_t ACK)
{
uint8_t i;
uint8_t dat = 0;
uint8_t bit;
GPIO_Read();
for (i=0; i<8; i++) //8位计数器
{
dat <<= 1;
HAL_GPIO_WritePin(SCL_GPIO, SCL_Pin,GPIO_PIN_SET); //拉高时钟线
My_Delay_us(2);
if( SET == HAL_GPIO_ReadPin(SDA_GPIO, SDA_Pin) )
bit = 0X01;
else
bit = 0x00;
dat |= bit; //读数据
HAL_GPIO_WritePin(SCL_GPIO, SCL_Pin,GPIO_PIN_RESET); //拉低时钟线
My_Delay_us(2);
}
AHT20_SendACK(ACK); //函数内部有GPIO_Write();
return dat;
}
/*======================将SDA设置为输出模式====================*/
void GPIO_Write(void)
{
GPIO_InitStruct.Pin = SDA_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(SDA_GPIO, &GPIO_InitStruct);
}
/*======================将SDA设置为输入模式====================*/
void GPIO_Read(void)
{
GPIO_InitStruct.Pin = SDA_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(SDA_GPIO, &GPIO_InitStruct);
}
/*=========================CRC校验函数==========================*/
uint8_t CheckCrc8(unsigned char *pDat,unsigned char Lenth)
{
unsigned char crc = 0xff, i, j;
for (i = 0; i < Lenth ; i++)
{
crc = crc ^ *pDat;
for (j = 0; j < 8; j++)
{
if (crc & 0x80) crc = (crc << 1) ^ 0x31;
else crc <<= 1;
}
pDat++;
}
return crc;
}
main.c
float data[2];
data出来直接就是湿度和温度啦~