DHT12温湿度传感器STM32驱动IIC

时间:2021-09-23 09:25:38

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

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

DHT12温湿度传感器STM32驱动IIC

以下将从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

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

DHT12温湿度传感器STM32驱动IIC

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

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

DHT12温湿度传感器STM32驱动IIC

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);
}

//初始化IICSDA:PA1SCL: 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