DHT12温湿度传感器STM32驱动IIC

《DHT12温湿度传感器STM32驱动-IIC》

温湿度传感器采用AOSONG的DHT12温湿度传感器;该传感器兼容单总线和标准的IIC通信协议,在本文中将叙述IIC通信协议获取温湿度数据,通过STM32的普通GPIO模拟IIC协议驱动DHT12;

DHT12温湿度传感器STM32驱动IIC_第1张图片

以下将从IIC协议到DHT12驱动逐步进行详细介绍,并附有iic.c、iic.h、dht12.c、dht12.h源代码

IIC协议(Inter-integrated circuit) interface:

IIC(集成电路总线)简化了信号传输总线接口,是由PHILIPS公司设计的一种双向、二进制、同步串行总线,主要是用来连接整体电路,IIC是一种多向控制总线——多个设备可以连接到同一个总线结构下,同时设备都可以作为实时数据传输的控制源。

1.     基本参数&术语

1)     发送器:发送数据到总线的器件;

2)     接收器:从总线接收数据的器件;

3)     主机:发起/停止数据传输、提供时钟信号的器件;

4)     从机:被主机寻址的器件;

5)     多主机:可以由多个主机试图去控制总线,但是不会被破坏;

6)     仲裁:当多个主机试图去控制总线时,通过仲裁可以使得只有一个主机获得总线控制权,并且它传输的信息不被破坏;

7)     同步:多个器件同步时钟信号的过程;

2.     IIC协议

1)     IIC数据传送的位数必须是8bit,总线能挂载设备的总数由总线最大电容400pF限制,但不超过7bit寻址设备数27=128个;

2)     硬件设计时SDA、SCL都需要上拉电阻上拉,在没有主机控制时默认为高;

3)     IIC数据从高位开始传输(MSB);

4)     开始信号(S):SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据;

5)     结束信号(P):SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据;

DHT12温湿度传感器STM32驱动IIC_第2张图片

6)     应答信号(ACK):发送器发完8bit数据后就不再驱动总线了(SDA引脚变为输入),此时SDA总线为高;接收器在接收到8位数据后,在第9个时钟周期,拉低SDA电平;

DHT12温湿度传感器STM32驱动IIC_第3张图片

7)     数据传送接收:SDA上传输的数据必须在SCL为高电平期间保持稳定,由此接收器在SCL为高时接收SDL的数据就不会产生紊乱;SDL上的数据只能在SCL为低电平期间翻转变化;

8)     数据寻址传输格式:首先发送7bit从设备地址和1bitwrite/read标志位,收到应答后发送8bit数据;

DHT12温湿度传感器STM32驱动IIC_第4张图片

9)     如果从机要完成一些功能后(例如一个内部终端服务程序)才能继续接受或发送下一个字节,从机可以拉低SCL迫使主机进入等待状态。当从机准备好接收下一个数据并释放SCL后,数据传输继续。如果主机在传输数据器件也需要完成一些其他功能(例如一个内部终端服务程序)也可以拉低SCL以占总线;

10)  NO ACK处理机制

a)     当从机不能相应从机地址时(例如它忙于其他事而无法相应IIC总线的操作,或这个地址没有对应的主机),在第9个SCL周期内SDA线没有被拉低,即没有ACK信号。这时,主机发出一个P信号终止传输或者重新发出有个S信号开始新的传输;

b)     如果从机接收器在传输过程中不能接收更多的数据时,它也不会发出Ack信号。这样,主机就可以意识到这点,从而发出一个P信号终止传输或者发出一个新的S信号开始新的传输。

c)     主机接收器在接收到最后一个字节后,也不会发出ACK信号。于是,从机发送器释放SDA线,允许主机发出P信号结束传输;

3.     引脚

1)     SDA:双向数据线;所有接到IIC总线设备的SDA都接到总线SDA;

2)     SCL:时钟信号线;所有接到IIC总线设备的SCL都接到总线SCL;


iic.c源文件

//File Name: iic.c
#include "iic.h"
#include "delay.h"

//SDA:PA1
void SDA_IN()
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );	
	   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU  ;   //上拉输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
}
//SDA:PA1
void SDA_OUT()
{
	GPIO_InitTypeDef GPIO_InitStructure;


	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );	
	   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //开漏输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
}

//初始化IIC	SDA:PA1	SCL: PA4
void IIC_Init(void)
{					     
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );	
	   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_4;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //开漏输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
 
	IIC_SCL=1;//高电平
	IIC_SDA=1;
}
//产生IIC起始信号
void IIC_Start(void)
{
	SDA_OUT();     				//SDA线设为输出;
	IIC_SDA=1;	  				//SDA输出高电平;
	IIC_SCL=1;					//SCL输出高电平;
	delay_us(4);			
 	IIC_SDA=0;					//开始信号:当SCL为高,SDA由高变低;
	delay_us(4);
	IIC_SCL=0;					//钳住I2C总线,准备发送或接收数据 
	delay_us(4);
}	  
//产生IIC停止信号
//当SCL为hight时,SDA从low变hight
void IIC_Stop(void)
{
	SDA_OUT();			
	IIC_SCL=0;
	IIC_SDA=0;			
 	delay_us(4);
	IIC_SCL=1; 
	delay_us(4);
	IIC_SDA=1;					//发送I2C总线结束信号
	delay_us(4);									   	
}
//等待应答信号到来
//返回值:1,接收应答失败
//     0,接收应答成功
uint8_t IIC_Wait_Ack(void)
{
	uint8_t ucErrTime=0;
	SDA_IN();      				//SDA设置为输入  
	
	delay_us(4);	   
	IIC_SCL=1;
	delay_us(4);	 
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		}
		delay_us(1);
	}
	IIC_SCL=0;					//时钟输出0 
  delay_us(50);	
	return 0;  
} 
//产生ACK应答
void IIC_Ack(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=0;
	delay_us(5);
	IIC_SCL=1;
	delay_us(5);
	IIC_SCL=0;
}
//不产生ACK应答		    
void IIC_NAck(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=1;
	delay_us(5);
	IIC_SCL=1;
	delay_us(5);
	IIC_SCL=0;
}					 				     
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答			  
void IIC_Send_Byte(uint8_t txd)
{                        
    uint8_t t;   
	SDA_OUT(); 	    
    IIC_SCL=0;					//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
        IIC_SDA=(txd&0x80)>>7;
        txd<<=1; 	  
		delay_us(2);   			//对TEA5767这三个延时都是必须的
		IIC_SCL=1;
		delay_us(2); 
		IIC_SCL=0;	
		delay_us(2);
    }	 
} 	    
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
uint8_t IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();					//SDA设置为输入
    for(i=0;i<8;i++ )
	 {
        IIC_SCL=0; 
        delay_us(50);
	    IIC_SCL=1;
	    delay_us(50);
        receive<<=1;
        if(READ_SDA)
		receive++;	
				
    }					 
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK   
    return receive;
}
iic.h源文件
#ifndef __IIC_H
#define __IIC_H
#include "sys.h"

//IO操作函数	 
#define IIC_SCL    PAout(4) //SCL
#define IIC_SDA    PAout(1) //SDA	 
#define READ_SDA   PAin(1)  //输入SDA 

void SDA_IN(void);
void SDA_OUT(void);

//IIC所有操作函数
void IIC_Init(void);                		//初始化IIC的IO口				 
void IIC_Start(void);				//发送IIC开始信号
void IIC_Stop(void);	  			//发送IIC停止信号
void IIC_Send_Byte(uint8_t txd);		//IIC发送一个字节
uint8_t IIC_Read_Byte(unsigned char ack);	//IIC读取一个字节
uint8_t IIC_Wait_Ack(void); 			//IIC等待ACK信号
void IIC_Ack(void);				//IIC发送ACK信号
void IIC_NAck(void);				//IIC不发送ACK信号

#endif


DHT12硬件连接

       DHT12的SDA和SCL直接与STM32的普通IO连接。同时,SCL、SDA均需通过1K—10K的电阻上拉;DHT12支持的电源范围为2.7V – 5.5V,直接连5V或3.3V均可。安装在电路板上尽可能将传感器原理电子元件,并安装在热源下方,同时保持外壳的良好通风。进行手动焊接时,最高300℃条件下接触时间少于5s;

DHT12数据获取协议分析

1)     IIC的地址为0xB8(DEV SEL);IIC的通信速率不能高于400KHz,对SDA和SCL所连总线的IO进行初始化时,IO的速率设置为2MHz;

BYTE ADDR

R/W

Desc.

Note

0x00

R

湿度整数位

相对湿度值

0x01

R

湿度小数位

0x02

R

温度整数位

温度值

0x03

R

温度小数位

0x04

R

 

校验和

2)     每次读取传感器间隔大于2s即可获得准确的数据;

3)     输出的40位数据中,温度的小数位的第8Bit为1则表示采样得出的温度为负值;

DHT12驱动代码

#include "dht12.h" 
#include "delay.h"

uint8_t dat[5];					//接收数据
static uint8_t check;				//用于保存数据校验的结果

//初始化IIC接口
void DHT12_Init(void)
{
	IIC_Init();
}

void DHT12_ReadByte()
{				  
	uint8_t i;		  	    																 
    	IC_Start( );  				//主机发送总线开始信号;

	IIC_Send_Byte(0xB8);		
	IIC_Wait_Ack( );

	IIC_Send_Byte(0x00);			
	IIC_Wait_Ack( );			

	IIC_Start( );				//进入接收模式	,接收0xB8的数据	
	IIC_Send_Byte(0xB9); 				   
	IIC_Wait_Ack( );	 
	 
	for(i=0;i<4;i++)
	{
		dat[i]=IIC_Read_Byte(1);	//读前四个,发送ACK
	}
	dat[i]=IIC_Read_Byte(0);		//读第5个发送NACK

    IIC_Stop();//产生一个停止条件	    
}

//温湿度诗句读取校验,成功返回 TURE,错误返回 FALSE
void Data_Check()
{ 
	DHT12_ReadByte();
	
	if(dat[4] == (dat[0]+dat[1]+dat[2]+dat[3]))
		check = 0xff;			//读取成功
	else
		check = 0;			//读取失败
}

//读取湿度值
uint16_t Humid_Read( )
{
	
	uint16_t V_Humid;
	Data_Check( );
	if(check)
	{
		V_Humid = ((dat[0]*10+dat[1])-200);//湿度的取值范围为20%-95%,取增量为-20,相对值范围为0-750
						   //计算的结果是相对值 V_Humid =(整数位+小数位)/分辨率+增量;
		return V_Humid;
	}
	else
		return 0;
}
//读取温度值;
uint16_t Temper_Read( )
{
	uint16_t V_Temper;
	if(check)
	{
		if(dat[3]&0x80)
			V_Temper = (200-(dat[2]*10+(dat[3]&0x7F)));//温度为负数,温度取值范围是-20—60,增量为200;
		else
			V_Temper = (200+(dat[2]*10+(dat[3]&0x7F)));//温度为正数;
		check = 0;
		return V_Temper; 
	}
	else
		return 0;
}

dht12.h源文件

#ifndef __DHT12_H
#define __DHT12_H
#include "iic.h" 
#include "stm32f10x.h"


extern uint8_t Humid[3];
extern uint8_t Temper[4];			//第一位是正负号

typedef enum
{
	FALSE=0,
	TURE
}bool;

void DHT12_Init(void); 				//初始化IIC
					  
//读取温湿度并转换为字符串 0失败 1成功
uint16_t Humid_Read(void);
uint16_t Temper_Read(void);
#endif

你可能感兴趣的:(STM32)