STM32F103使用DHT11采集温湿度通过软件I2C驱动OLED显示

Posted studying~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32F103使用DHT11采集温湿度通过软件I2C驱动OLED显示相关的知识,希望对你有一定的参考价值。

一.温湿度传感器介绍



1.主要参数
供电电压:3.3 - 5.5V直流电
输出为单总线数字信号
温度测量范围0-50度(精度正负2度,分辨率1度)
湿度测量范围为20-90%RH(精度为正负5%,分辨率1%)

2.硬件连接
模块的VCC 接单片机的5V
模块的GND 接单片机的GND
模块的DAT 接单片机任意引脚
vcc和gnd之间可以加一个电容,用于去耦滤波

3.温湿度采集原理
采用单总线双向串行通信协议,每次采集都要由单片机发起开始信号,然后DHT11会向单片机发送响应并开始传输40位数据帧,高位在前。

(1)数据格式为:

第一二个字节: 8bit湿度整数数据+8bit湿度小数数据
第三四个字节: 8bit温度整数数据+8bit温度小数数据
第五个字节 : 8bit校验位(它是前四个数据相加后八位的数值)
温湿度小数部分默认为0,即单片机采集的数据都是整数,校验位为4个字节的数据相加取结果的低8位数据作为校验和;

示例:
0011 0101 0000 0000 0001 1000 0000 0000 0100 1001
湿度高八位 湿度低八位 温度高八位 温度低八位 检验位
计算 : 0011 0101 + 0001 1000
结果: 0100 1101 若不等于 01001101 ,则本次接收数据不正确,重新接收数据

(2)温湿度传感器时序介绍
总线空闲状态为高电平,主机把总线拉低等待DHT11响应,主机把总线拉低必须大于18毫秒,保证DHT11能检测起始信号。DHT11接收到主机的开始信号后,等待主机开始信号结束,然后发送80us低电平响应信号,主机发送开始信号结束后,延时等待20

二.代码示例


DHT11.h头文件

#ifndef __DHT11_H
#define __DHT11_H 
#include "stm32f10x.h"  
 
//IO方向设置
#define DHT11_IO_IN()  {GPIOB->CRL&=0XFFFFFFF0;GPIOB->CRL|=0x08;}//上拉/下拉输入模式
#define DHT11_IO_OUT() {GPIOB->CRL&=0XFFFFFFF0;GPIOB->CRL|=0x03;}//推挽输出模式,50MHZ
IO操作函数											   
#define	DHT11_DQ_OUT PBout(0) //数据端口	PB0 
#define	DHT11_DQ_IN  PBin(0)  //数据端口	PB0 

u8 DHT11_Init(void);//初始化DHT11
u8 DHT11_Read_Data(void);//读取温湿度
u8 DHT11_Read_Byte(void);//读出一个字节
u8 DHT11_Read_Bit(void);//读出一个位
u8 DHT11_Check(void);//检测是否存在DHT11
void DHT11_Rst(void);//复位DHT11    
#endif

DHT11.c文件

#include "dht11.h"
#include "delay.h"
#include "oled.h"
    
//起始信号
void DHT11_Rst(void)	   
{                 
	  DHT11_IO_OUT(); 	//设置推挽输出模式,50MHZ
    DHT11_DQ_OUT=0; 	//拉低电平,主机开始发送起始信号
    delay_ms(20);    	//至少18ms,保证从机检测到起始信号
	                    //从机检测到后,从机等待主机起始信号 结束-->>拉高电平	
    DHT11_DQ_OUT=1; 	//主机拉高电平,起始信号结束,延时等待,准备接收回响信号
	  delay_us(30);     //主机拉高20~40us
}

//主机接收起始信号,等待从机DHT11的回应
//返回1:未检测到DHT11的存在
//返回0:存在
u8 DHT11_Check(void) 	   
{   
	u8 retry=0;
	DHT11_IO_IN();//设置上拉/下拉输入模式
  while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~80us
	{
		retry++;
		delay_us(1);
	};	 
	if(retry>=100)return 1;
	else retry=0;
  while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~80us,准备输出,只是准备,并没有输出
	{
		retry++;
		delay_us(1);
	};
	if(retry>=100)return 1;	    
	return 0;
}

//从DHT11读取一个位
//返回值:1/0
u8 DHT11_Read_Bit(void) 			 
{
 	u8 retry=0;
	while(DHT11_DQ_IN&&retry<100)//等待变为低电平
	{
		retry++;
		delay_us(1);
	}
	retry=0;
	while(!DHT11_DQ_IN&&retry<100)//等待变高电平
	{
		retry++;
		delay_us(1);
	}
	delay_us(40);//等待40us
	if(DHT11_DQ_IN)return 1;
	else return 0;		   
}

//从DHT11读取一个字节
//返回值:读到的数据
u8 DHT11_Read_Byte(void)    
{        
  u8 i,dat;
  dat=0;
	for (i=0;i<8;i++) 
	{
   		dat<<=1; 
	    dat|=DHT11_Read_Bit();
  }						    
  return dat;
}

//从DHT11读取一次数据
//temp:温度值(范围:0~50°)
//humi:湿度值(范围:20%~90%)
//返回值:0,正常;1,读取失败
u8 DHT11_Read_Data()    
{        
 	u8 buf[5];
	u8 i;
	DHT11_Rst();         //main函数对端口初始化时已调用一次,这里调用是为了保证数据最新
	if(DHT11_Check()==0) //main函数对端口初始化时已调用一次,这里调用是为了保证数据最新
	{
		for(i=0;i<5;i++)//读取40位数据
		{
			buf[i]=DHT11_Read_Byte();
		}
		if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
		{
		 OLED_ShowNum(74,2,buf[2],2);//显示温度数据整数
	   OLED_ShowNum(96,2,buf[3],1);//显示温度数据小数
	   OLED_ShowNum(74,5,buf[0],2);//显示湿度数据整数
	   OLED_ShowNum(96,5,buf[1],1);//显示湿度数据小数
		}
	}else return 1;
	return 0;	    
}

//初始化DHT11的IO口  同时检测DHT11的存在
//返回1:不存在
//返回0:存在    	 
u8 DHT11_Init(void)
{	 
 	GPIO_InitTypeDef  GPIO_InitStructure;
 	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	 //使能PB端口时钟
	
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;				 //PB0端口配置
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);				 //初始化IO口
 	GPIO_SetBits(GPIOB,GPIO_Pin_0);						 //PB0 输出高
			    
	DHT11_Rst();  //起始信号
	return DHT11_Check();//等待DHT11的回应
} 

OLED.h头文件

#ifndef _OLED_H 
#define _OLED_H
#include "stm32f10x.h"

#define OLED_SCL_Set()    GPIO_SetBits(GPIOA,GPIO_Pin_6);
#define OLED_SCL_CLr()	  GPIO_ResetBits(GPIOA,GPIO_Pin_6);
#define OLED_SDA_Set()    GPIO_SetBits(GPIOA,GPIO_Pin_7);
#define OLED_SDA_CLr()    GPIO_ResetBits(GPIOA,GPIO_Pin_7);
#define OLED_SDA_RCV()		GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_7)

#define OLED_CMD  1
#define OLED_DATA 0

void IIC_config(void);							
void IIC_Start(void);
void IIC_Stop(void);
char OLED_Wait_ACK(void);
void IIC_Send(uint8_t Write_Byte);

void OLED_WR_CMD(uint8_t cmd);
void OLED_WR_DATA(uint8_t data);
void OLED_WR_Byte(uint8_t data,uint8_t cmd);
void OLED_ON(void);
void OLED_OFF(void);
void OLED_Init(void);
void OLED_Clear(void);
void OLED_ShowChar(uint8_t x,uint8_t y, uint8_t ch,uint8_t temp);
void OLED_ShowStr(uint8_t x,uint8_t y,char *ch,uint8_t temp);
int OLED_Getpos(unsigned char length);
void OLED_ShowNum(unsigned char x,unsigned char y,unsigned int Num,unsigned char length);
void OLED_WriteCF(uint8_t x,uint8_t y,uint8_t m,uint8_t server);
void OLED_WriteCN(uint8_t x,uint8_t y,uint8_t m,uint8_t count);
void OLED_ShowBMP(uint8_t x0,uint8_t y0, uint8_t x1,uint8_t y1,uint8_t* BMP);
#endif

fontstr.h头文件
OLED.c文件

#include "stm32f10x.h"
#include "oled.h"
#include "delay.h"
#include "fontstr.h"

//GPIO模拟IIC初始化
void IIC_config()
{
   GPIO_InitTypeDef GPIO_Initstrue;
	
	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	  
	 GPIO_Initstrue.GPIO_Mode = GPIO_Mode_Out_OD; //开漏输出
	 GPIO_Initstrue.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 ;
	 GPIO_Initstrue.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init( GPIOA, &GPIO_Initstrue);           //PA6:SCL PA7:SDA
 	 OLED_SCL_Set();
	 OLED_SDA_Set();
}

//模拟IIC起始信号:当SCL为高电平期间,SDA有高到低的跳变
void IIC_Start()
{
   OLED_SCL_Set();   //
	 OLED_SDA_Set();   //空闲状态下拉高时钟总线和数据总线   
	 delay_us(1);
	
	 OLED_SDA_CLr();   //数据总线低电平
	 delay_us(1);
	 
	 OLED_SCL_CLr();   //时钟总线低电平
	 delay_us(1);
}

//模拟IIC停止信号:当SCL为高电平期间,SDA由低到高的跳变
void IIC_Stop()
{
	 OLED_SDA_CLr();   //数据总线低电平
	 delay_us(1);
	 
	 OLED_SCL_Set();   //时钟总线高电平
	 delay_us(1);
	
	 OLED_SDA_Set();   //数据总线高电平
	 delay_us(1);
}

//模拟IIC从机反馈主机应答信号:接收器在“第九个时钟脉冲之前的低电平期间”将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平
char OLED_Wait_ACK()
{
	 OLED_SCL_CLr();   //时钟总线低电平
	 delay_us(1);
	
	 OLED_SDA_CLr();   //数据总线低电平
	 delay_us(1);
	
	 OLED_SCL_Set();   //时钟总线高电平
	 delay_us(1);
	  
	 if(OLED_SDA_RCV())//应答信号为低电平时,为有效应答
	 {
	    return 1;      
	 }
	 else              //应答信号为高电平时,为非应答
	 {
		 	OLED_SCL_CLr();//拉低电平接收下一个数据
		  return 0;     
	 }

}

//模拟IIC发送数据:数据在SCL的上升沿到来之前就需准备好。并在下降沿到来之前必须稳定  
//Write_Byte:要发送的数据
void IIC_Send(uint8_t Write_Byte)
{
	 unsigned char i;
	 for(i=0;i<8;i++)            //每个时钟周期发送1个bit大小的数据,八个时钟周期代表一个完整的数据
	 {
			 OLED_SCL_CLr();         //时钟总线低电平
			 delay_us(1);
			
			 if(Write_Byte & 0x80)   //每次从数据的首位开始接收
			 {
						 OLED_SDA_Set();   //数据总线高电平,代表发送 位1
			 }else{
						 OLED_SDA_CLr();   //数据总线低电平,代表发送 位0
			 }		
			 delay_us(1);
			 OLED_SCL_Set();         //时钟总线高电平
			 delay_us(1);	 
			 Write_Byte <<= 1;       
			 delay_us(1);
  }
	while(OLED_Wait_ACK());      //等待从机应答
}

//对OLED写入一个命令
void OLED_WR_CMD(uint8_t cmd)
{
   IIC_Start();    //起始信号
   IIC_Send(0x78); //发送从设备地址,厂家规定0x78即OLED的地址
	 IIC_Send(0x00); //发送从设备内部的寄存器或存储器地址,之后是要对该地址的内容进行读或写
	                 //发送命令的首地址为0x00
	 IIC_Send(cmd);  //发送命令
	 IIC_Stop();     //停止信号
}

//对OLED写入一个数据
void OLED_WR_DATA(uint8_t data)
{
   IIC_Start();    //起始信号
   IIC_Send(0x78); //发送从设备地址,厂家规定0x78即OLED的地址
	 IIC_Send(0x40); //发送从设备内部的寄存器或存储器地址,之后是要对该地址的内容进行读或写
	                 //发送数据的首地址为0x40
	 IIC_Send(data); //发送数据
	 IIC_Stop();     //停止信号
}

//对OLED写入一个字节
//mount
//1:写命令
//0:写数据
void OLED_WR_Byte(uint8_t data,uint8_t mount)
{
   if(mount)
	 {
	    OLED_WR_CMD(data);
	 }else{
	    OLED_WR_DATA(data);
	 }
}

//设置起始地址
void OLED_SetPos(uint8_t x,uint8_t y)
{
   OLED_WR_Byte(0xb0+y,OLED_CMD);           //设置页地址
	 OLED_WR_Byte(0x0f&x,OLED_CMD);           //设置列地址的低四位
	 OLED_WR_Byte((0xf0&x)>>4|0x10,OLED_CMD); //设置列地址的高四位
}

//打开OLED
void OLED_ON()
{
    OLED_WR_Byte(0x8D,OLED_CMD);  //设置电荷泵
	  OLED_WR_Byte(0x14,OLED_CMD);  //开启电荷泵
	  OLED_WR_Byte(0xAF,OLED_CMD);  //OLED唤醒
}

//关闭OLED
void OLED_OFF()
{
    OLED_WR_Byte(0x8D,OLED_CMD);  //设置电荷泵
	  OLED_WR_Byte(0x10,OLED_CMD);  //关闭电荷泵
	  OLED_WR_Byte(0xAE,OLED_CMD);  //关闭OLED
}

//OLED屏幕初始化,厂家自给,不比深究
void OLED_Init()
{
  delay_init();      //延时初始化
  delay_ms(200);     //必须延时
	OLED_WR_CMD(0xAE); //display off
	OLED_WR_CMD(0x20);	//Set Memory Addressing Mode	
	OLED_WR_CMD(0x10);	//00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
	OLED_WR_CMD(0xb0);	//Set Page Start Address for Page Addressing Mode,0-7
	OLED_WR_CMD(0xc8);	//Set COM Output Scan Direction
	OLED_WR_CMD(0x00); //---set low column address
	OLED_WR_CMD(0x10); //---set high column address
	OLED_WR_CMD(0x40); //--set start line address
	OLED_WR_CMD(0x81); //--set contrast control register
	OLED_WR_CMD(0xff); //áá?èμ÷?ú 0x00~0xff
	OLED_WR_CMD(0xa1); //--set segment re-map 0 to 127
	OLED_WR_CMD(0xa6); //--set normal display
	OLED_WR_CMD(0xa8); //--set multiplex ratio(1 to 64)
	OLED_WR_CMD(0x3F); //
	OLED_WR_CMD(0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
	OLED_WR_CMD(0xd3); //-set display offset
	OLED_WR_CMD(0x00); //-not offset
	OLED_WR_CMD(0xd5); //--set display clock divide ratio/oscillator frequency
	OLED_WR_CMD(0xf0); //--set divide ratio
	OLED_WR_CMD(0xd9); //--set pre-charge period
	OLED_WR_CMD(0x22); //
	OLED_WR_CMD(0xda); //--set com pins hardware configuration
	OLED_WR_CMD(0x12);
	OLED_WR_CMD(0xdb); //--set vcomh
	OLED_WR_CMD(0x20); //0x20,0.77xVcc
	OLED_WR_CMD(0x8d); //--set DC-DC enable
	OLED_WR_CMD(0x14); //
	OLED_WR_CMD(0xaf); //--turn on oled panel
}

//清屏
void OLED_Clear()
{
   uint8_t m,n;
	 for(m=0;m<8;m++)
	{
		 OLED_SetPos(0,m);
	   for(n=0;n<128;n++)
		{
		   OLED_WR_Byte(0x00,OLED_DATA);
		}
	}
}

//向OLED写入一个字符
//x:0 ~ 127
//y:0 ~ 63
//ch:写入的字符
//temp:   6:6x8字体  8:8x16字体
void OLED_ShowChar(uint8_t x,uint8_t y, uint8_t ch,uint8_t temp)
{
   unsigned char i = 0,c = 0;
	 if(ch != 

以上是关于STM32F103使用DHT11采集温湿度通过软件I2C驱动OLED显示的主要内容,如果未能解决你的问题,请参考以下文章

STM32F103VE基于标准库下DHT11数据串口打印输出

STM32实例——基于STM32开发板实现传感数据采集-DHT11温湿度采集

STM32实例——基于STM32开发板实现传感数据采集-DHT11温湿度采集

STM32实例——基于STM32开发板实现传感数据采集-DHT11温湿度采集

STM32实例——基于STM32开发板实现传感数据采集-DHT11温湿度采集

基于STM32开发板实现传感数据采集-DHT11温湿度采集