STM32F103RBT6,使用HAL库模拟IIC驱动AHT20温湿度传感器

前言:

        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出来直接就是湿度和温度啦~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值