目录
1.DHT11

DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器,应用于专用数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性和卓越的长期稳定性。
传感器包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能八位单片机相连接。采用单线制串口行接口,信号传输距离可达20M以上。
1.硬件线路

- 供电电压:3.3 - 5.5V直流电
- 输出为单总线数字信号
- 温度测量范围0-50度(精度正负2度,分辨率1度)
- 湿度测量范围为20-90%RH(精度为正负5%,分辨率1%)
- 模块的+ 接单片机的5V
- 模块的- 接单片机的GND
- 模块的OUT 接单片机定义的引脚
- vcc和gnd之间可以加一个电容,用于去耦滤波
采用单总线双向串行通信协议,每次采集都要由单片机发起开始信号,然后DHT11会向单片机发送响应并开始传输40位数据帧,高位在前。
2.数据格式
- 第一二个字节: 8bit湿度整数数据+8bit湿度小数数据
- 第三四个字节: 8bit温度整数数据+8bit温度小数数据
- 第五个字节 : 8bit校验位(它是前四个数据相加后八位的数值)
温湿度小数部分默认为0,即单片机采集的数据都是整数,校验位为4个字节的数据相加取结果的低8位数据作为校验和
示例一:
0011 0101 0000 0000 0011 1000 0000 0000 0100 1101
湿度高八位 湿度低八位 温度高八位 温度低八位 检验位
计算 : 0011 0101 (相加)
0011 1000
结果: 01001101 湿度为0011 0101 = 35H = 53%RH
温度为0011 1000 = 18H = 24°
示例二:
0011 0101 0000 0000 0001 1000 0000 0000 0100 1001
湿度高八位 湿度低八位 温度高八位 温度低八位 检验位
计算 : 0011 0101 (相加)
0001 1000
结果: 0100 1101 不等于 01001001 本次接收数据不正确,重新接收数据
3.时序图
- 总线空闲状态为高电平,主机把总线拉低等待DHT11响应,主机把总线拉低必须大于18毫秒,保证DHT11能检测到起始信号。
- DHT11接收到主机的开始信号后, 等待主机开始信号结束,然后发送80us低电平响应信号.
- 主机发送开始信号结束后,延时等待20-40us后, 读取DHT11的响应信号,主机发送开始信号后,可以切换到输入模式,或者输出高电平均可, 总线由上拉电阻拉高。


- 总线为低电平,说明DHT11发送响应信号,DHT11发送响应信号后,再把总线拉高80us,准备发送数据。
- 每一bit数据都以50us低电平时隙开始,高电平的长短定 了数据位是0还是1.格式见下面图示.
- 如果读取响应信号为高电平,则DHT11没有响应,请检查线路是否连接正常.
- 当最后一bit数据传送完毕后,DHT11拉低总线50us,随后总线由上拉电阻拉高进入空闲状态。
数字0信号表示方法

数字1信号表示方法

项目代码
软件I2C_OLED
字模库见最后
cs
#include "stm32f10x.h"
#include "I2C_soft.h"
#include "systick.h"
#include "OLED_Codetab.h"
static void OLED_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_Initstructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_Initstructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Initstructure.GPIO_Pin = GPIO_Pin_0 |GPIO_Pin_1;
GPIO_Initstructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_Initstructure);
OLED_SCLK_Set();
OLED_SDIN_Set();
}
static void OLED_I2C_Start(void)
{
OLED_SCLK_Set();
OLED_SDIN_Set();
us_delay(1);
OLED_SDIN_Clr();
us_delay(1);
OLED_SCLK_Clr();
us_delay(1);
}
static void OLED_I2C_Stop(void)
{
OLED_SDIN_Clr();
us_delay(1);
OLED_SCLK_Set();
us_delay(1);
OLED_SDIN_Set();
us_delay(1);
}
static unsigned char I2C_Wait_Ask(void )
{
unsigned char ask;
OLED_SCLK_Clr();
us_delay(1);
OLED_SDIN_Set();
us_delay(1);
OLED_SCLK_Set();
us_delay(1);
if(OLED_READ_SDIN())
{
ask = OLED_NO_ASK;
}
else
{
ask = OLED_ASK;
}
OLED_SCLK_Clr();
us_delay(1);
return ask;
}
static void Write_I2C_Byte(unsigned char I2C_Byte)
{
unsigned char i;
for(i = 0 ;i < 8;i++)
{
OLED_SCLK_Clr();
us_delay(1);
if(I2C_Byte & 0x80)
OLED_SDIN_Set();
else
OLED_SDIN_Clr();
I2C_Byte = I2C_Byte << 1;
us_delay(1);
OLED_SCLK_Set();
us_delay(1);
}
OLED_SCLK_Clr();
us_delay(1);
I2C_Wait_Ask();
}
static void Write_I2C_Command(unsigned char I2C_Command)
{
OLED_I2C_Start();
Write_I2C_Byte(0x78);
Write_I2C_Byte(0x00);
Write_I2C_Byte(I2C_Command);
OLED_I2C_Stop();
}
static void Write_I2C_Data(unsigned char I2C_Data)
{
OLED_I2C_Start();
Write_I2C_Byte(0x78);
Write_I2C_Byte(0x40);
Write_I2C_Byte(I2C_Data);
OLED_I2C_Stop();
}
void Write_OLED_Byte(unsigned char data,unsigned char command)
{
if(command)
{
Write_I2C_Data(data);
}
else
{
Write_I2C_Command(data);
}
}
void OLED_SetPos(unsigned char x,unsigned char y)
{
Write_OLED_Byte(0xb0+y,OLED_CMD);
Write_OLED_Byte(0X00 | (x & 0X0F),OLED_CMD);
Write_OLED_Byte(0x10 | (x & 0xF0) >> 4,OLED_CMD);
}
void OLED_Display_ON(void)
{
Write_OLED_Byte(0X8D,OLED_CMD);
Write_OLED_Byte(0X14,OLED_CMD);
Write_OLED_Byte(0XAF,OLED_CMD);
}
void OLED_Display_OFF(void)
{
Write_OLED_Byte(0X8D,OLED_CMD);
Write_OLED_Byte(0X10,OLED_CMD);
Write_OLED_Byte(0XAE,OLED_CMD);
}
void OLED_Clear(void)
{
int i,j;
for(j = 0;j < 8;j++)
{
Write_OLED_Byte(0XB0+j,OLED_CMD);
Write_OLED_Byte(0X10,OLED_CMD);
Write_OLED_Byte(0X00,OLED_CMD);
for(i = 0;i < 128;i++)
Write_OLED_Byte(0,OLED_DATA);
}
}
//OELDÆÁÄ>>³õʼ>>¯
void OLED_Init(void)
{
OLED_GPIO_Init();
ms_delay(100);
Write_I2C_Command(0xAE); //display off
Write_I2C_Command(0x20); //Set Memory Addressing Mode
Write_I2C_Command(0x10); //00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
Write_I2C_Command(0xb0); //Set Page Start Address for Page Addressing Mode,0-7
Write_I2C_Command(0xc8); //Set COM Output Scan Direction
Write_I2C_Command(0x00); //---set low column address
Write_I2C_Command(0x10); //---set high column address
Write_I2C_Command(0x40); //--set start line address
Write_I2C_Command(0x81); //--set contrast control register
Write_I2C_Command(0xff); //ÁÁ¶Èµ÷½Ú 0x00~0xff
Write_I2C_Command(0xa1); //--set segment re-map 0 to 127
Write_I2C_Command(0xa6); //--set normal display
Write_I2C_Command(0xa8); //--set multiplex ratio(1 to 64)
Write_I2C_Command(0x3F); //
Write_I2C_Command(0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
Write_I2C_Command(0xd3); //-set display offset
Write_I2C_Command(0x00); //-not offset
Write_I2C_Command(0xd5); //--set display clock divide ratio/oscillator frequency
Write_I2C_Command(0xf0); //--set divide ratio
Write_I2C_Command(0xd9); //--set pre-charge period
Write_I2C_Command(0x22); //
Write_I2C_Command(0xda); //--set com pins hardware configuration
Write_I2C_Command(0x12);
Write_I2C_Command(0xdb); //--set vcomh
Write_I2C_Command(0x20); //0x20,0.77xVcc
Write_I2C_Command(0x8d); //--set DC-DC enable
Write_I2C_Command(0x14); //
Write_I2C_Command(0xaf); //--turn on oled panel
}
void OLED_ShowChar(unsigned char x,unsigned char y,unsigned char chara)
{
unsigned char i = 0,ch = 0;
if(x + 16 > Max_Column)
{
x = 0;
y = y+2;
}
ch = chara - ' ';
if(Size == 16)
{
OLED_SetPos(x,y);
for(i = 0;i < 8;i++)
{
Write_OLED_Byte(F8X16[ch*16+i],OLED_DATA);
}
OLED_SetPos(x,y+1);
for(i = 0;i < 8;i++)
{
Write_OLED_Byte(F8X16[ch*16+i+8],OLED_DATA);
}
}
else
{
OLED_SetPos(x,y);
for(i = 0;i < 6;i++)
{
Write_OLED_Byte(F6x8[ch][i],OLED_DATA);
}
}
}
void OLED_ShowString(unsigned char x,unsigned char y,unsigned char *ch)
{
unsigned char j = 0 ;
while( ch[j] != '\0')
{
OLED_ShowChar(x,y,ch[j]);
x += 8;
if(x >128)
{
x = 0;
y = y + 2;
}
j++;
}
}
unsigned int OLED_pow(unsigned int m,unsigned int n)
{
unsigned int result = 1;
while(n--)
result *= m;
return result;
}
void OLED_ShowNum(unsigned char x,unsigned char y,unsigned int num,unsigned int len,unsigned int size)
{
unsigned char t = 0,temp = 0;
unsigned char enshow = 0;
for(t = 0;t < len;t++)
{
temp = num/OLED_pow(10,len-1-t)%10;
if(enshow == 0 && t < (len-1))
{
if(temp == 0)
{
OLED_ShowChar(x + t*(size/2),y,' ');
continue;
}
}
else
enshow = 1;
OLED_ShowChar(x + t*(size/2),y,temp + '0');//Êý×Ö0ת>>>>³Éasci0
}
}
void OLED_ShowCN(unsigned char x,unsigned char y,unsigned char N)
{
unsigned char i = 0;
OLED_SetPos(x,y);
for(i = 0;i < 16;i++)
{
Write_OLED_Byte(F8X16_TM[N*2][i],OLED_DATA);
}
OLED_SetPos(x,y+1);
for(i = 0;i < 16;i++)
{
Write_OLED_Byte(F8X16_TM[N*2+1][i],OLED_DATA);
}
}
DHT11代码
cs
#include "stdio.h"
#include "dht11.h"
#include "stm32f10x.h"
#include "systick.h"
uint16_t RxBuf[5];
void Dht11_GPIO_Init_Ouput()
{
GPIO_InitTypeDef GPIO_Initstructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_Initstructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Initstructure.GPIO_Pin = GPIO_Pin_11;
GPIO_Initstructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_Initstructure);
GPIO_SetBits(GPIOB,GPIO_Pin_11);
}
void Dht11_GPIO_Init_Input()
{
GPIO_InitTypeDef GPIO_Initstructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_Initstructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Initstructure.GPIO_Pin = GPIO_Pin_11;
GPIO_Initstructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_Initstructure);
GPIO_SetBits(GPIOB,GPIO_Pin_11);
}
static uint8_t Dht11_Ask()
{
uint8_t i = 200;
while(read_data && i--); //等待电平拉低
i = 200;
while(!read_data && i--); //等待电平拉高
return 0;
}
void Dht11_Start()
{
data0;
ms_delay(20); //>=18ms
data1;
us_delay(10); //拉高等待应答
Dht11_GPIO_Init_Input();//转为输入模式
while(Dht11_Ask());
}
void Dht11_ReceptionBuf()
{
uint8_t i = 0,j = 0;
uint16_t rec = 0;
for(i = 0;i < 5;i++) //5组数据
{
rec = 0;
for(j = 0;j < 8;j++)
{
while(read_data)//等待低电平到来
{
__nop();
}
us_delay(40);
while(!read_data)//等待高电平到来
{
__nop();
}
rec = rec << 1;
us_delay(30); //!!!!!!26-28us为0,70us为1
if(read_data)
{
rec = rec | 1;
}
while(read_data);
}
RxBuf[i] = rec;
}
}
void Dht11_UpdateData()
{
Dht11_GPIO_Init_Ouput();
Dht11_Start();
Dht11_ReceptionBuf();
}
cs
#ifndef _DHT11_H_
#define _DHT11_H_
#include "stm32f10x.h"
#define data0 GPIO_ResetBits(GPIOB,GPIO_Pin_11)
#define data1 GPIO_SetBits(GPIOB,GPIO_Pin_11)
#define read_data GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)
void Dht11_GPIO_Init_Ouput(void);
void Dht11_GPIO_Init_Input(void);
static uint8_t Dht11_Ask(void);
void Dht11_Start(void);
void Dht11_ReceptionBuf(void);
void Dht11_UpdateData(void);
#endif
main函数
cs
#include "stm32f10x.h"
#include "main.h"
#include "led.h"
#include "buzzer.h"
#include "key.h"
#include "Relay.h"
#include "Shake.h"
#include "Exti.h"
#include "usart.h"
#include "stdio.h"
#include "Time.h"
#include "sg90.h"
#include "systick.h"
#include "I2C_soft.h"
#include "dht11.h"
extern uint16_t RxBuf[5];
int main()
{
initSysTick();
OLED_Init();
OLED_Clear();
OLED_ShowCN(0,0,0);//当
OLED_ShowCN(16,0,1);//前
OLED_ShowCN(32,0,2);//温
OLED_ShowCN(48,0,3);//度
OLED_ShowCN(64,0,4);//:
OLED_ShowCN(90,0,9);//.
OLED_ShowCN(112,0,5);//°C
OLED_ShowCN(0,2,0);
OLED_ShowCN(16,2,1);
OLED_ShowCN(32,2,6);//湿
OLED_ShowCN(48,2,3);
OLED_ShowCN(64,2,4);
OLED_ShowCN(90,2,9);//.
OLED_ShowCN(112,2,7);//%
while(1)
{
uint16_t i = 0;
uint8_t Humidity_H = 0;
uint8_t Humidity_L = 0;
uint8_t Tempurature_H = 0;
uint8_t Tempurature_L = 0;
Dht11_UpdateData();
i = RxBuf[0] + RxBuf[1] + RxBuf[2] + RxBuf[3];
if(i == RxBuf[4])
{
Humidity_H = RxBuf[0];
Humidity_L = RxBuf[1];
Tempurature_H = RxBuf[2];
Tempurature_L = RxBuf[3];
}
OLED_ShowNum(74,0,Tempurature_H,2,16);
OLED_ShowNum(98,0,Tempurature_L,1,16);
OLED_ShowNum(74,2,Humidity_H,2,16);
OLED_ShowNum(98,2,Humidity_L,1,16);
ms_delay(2000);
}
}
SYSTICK延时函数
当系统时钟为 72 MHz 时:
情况1:不分频
- SysTick 时钟 = 72 MHz
- 1 秒 = 72,000,000 个时钟周期
- 1 毫秒 = 72,000 个时钟周期
- 1 微秒 = 72 个时钟周期
情况2:8分频(原代码的设置)
- SysTick 时钟 = 72 MHz ÷ 8 = 9 MHz
- 1 秒 = 9,000,000 个时钟周期
- 1 毫秒 = 9,000 个时钟周期
- 1 微秒 = 9 个时钟周期
写入 VAL 时:
- 计数器立即被清零
- COUNTFLAG 标志被清除
- 在下一个时钟周期,硬件自动将 LOAD 值加载到 VAL
cs
#include "systick.h"
#include "stm32f10x.h"
/**
* @brief SysTick定时器初始化
* @param 无
* @retval 无
*/
void initSysTick(void)
{
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); // 设置时钟源为HCLK的8分频
SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; // 使能SysTick中断
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; // 开启定时器
SysTick->LOAD = 9; // 设置一个初始重装载值
}
/**
* @brief 微秒级延时函数
* @param xus : 延时时间(单位:微秒)
* @retval 无
*/
void us_delay(u32 xus)
{
SysTick->LOAD = 9 * xus; // 计9次为1us,xus则重装载值要*9
SysTick->VAL = 0; // 计数器归零
while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); // 等待计数完成
}
/**
* @brief 毫秒级延时函数
* @param xms : 延时时间(单位:毫秒)
* @retval 无
*/
void ms_delay(u32 xms)
{
SysTick->LOAD = 9000; // 计9次为1us,1000次为1ms
SysTick->VAL = 0; // 计数器归零
while (xms--)
{
while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); // 等待单次计数完成
}
}