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温湿度采集