基于stm32-DHT 11的多功能时钟2测量温度和湿度。

亲爱的读者们,我又回来了~

?上一章我带你实现了时钟显示和按键调整的功能。在本章中,我将使用DHT11温湿度传感器来测量环境温度和湿度。

DHT11温湿度传感器是数字式的,包括1电阻式湿度传感元件和1 NTC温度测量元件。自带AD转换功能,采用单总线,具有响应速度快、抗干扰能力强、性价比高等特点。该模块共有***4个管脚,其中两个是电源管脚VCC和GND,一个是数据管脚,另一个是空管脚。

目前比较流行的数据传输总线有II2C总线、SPI总线和单总线,DHT11采用单总线传输数据。单总线,顾名思义就是用一根信号线同时传输时钟和数据,数据传输是双向的,所以有主从之分。这里,stm32是作为核心控制器的主机,DHT11是从机。使用单总线进行数据传输,需要看数据手册的时序图。?

总线空闲状态为高,主机下拉总线等待DHT11的响应。主机必须将总线下拉超过18毫秒,以确保DHT11可以检测到启动信号。DHT11收到主机的启动信号后,等待主机启动信号结束,然后发送80us的低电平响应信号。主机的启动信号发出后,DHT11的响应信号在延迟20-40us后被读取。主机发出启动信号后,可以切换到输入模式,也可以输出大功率的平均总线。

根据时序图,微控制器至少需要下拉总线18ms,然后上拉总线20~40us。此时,主机的启动信号结束,检测到DHT11的响应信号。如果检测到低电平,DHT11响应,低电平时间维持80us,然后DHT11将总线拉高80us。此时DHT11准备传输数据,传输数据的间隙为50us低电平。发送的数据可以通过高电平的长度来区分“0”和“1”。数据传输后,DHT11将总线下拉50us,最后主机再次将总线上拉。

(1)写入延迟功能

因为DHT11的时序比较严格,需要毫秒级的延时和细微的延时。这里我们使用Systick来实现延迟。延迟在之前的键扫描功能中也使用过,这里我就描述一下。

我们需要配置系统时钟,然后将Systick设置为72,以便生成1us的时间基准。其次,我们需要编写Systick中断处理函数,让变量自己减少,从而达到延时的效果。最后我们需要写延迟函数,也就是给自身减少的变量赋一个初始值。

_ _ IO uint32 _ t TimingDelay

/*配置SysTick功能*/

void systick_init(void)

{

/*配置Systick过载值,系统时钟为72MHz*/

/*设置72,中断时间:72 *(1/72000000)= 1us */

If(SysTick_Config(72)= = 1)//如果sy stick _ Config函数返回中断信号,返回值为0。

{

while(1);?//SysTick_Config函数的返回值是1,所以等等。

}

}

/*时变自减函数*/

void TimingDelay_Decrement(void)

{

if(TimingDelay!=0x00)

{

timing delay-;

}

}

/*SysTick中断处理器*/

void SysTick_Handler(void)

{

timing delay _ Decrement();

}

/*延时功能,时间基准为1ms*/

无效延迟_毫秒(__IO uint32_t时间)

{

timing delay = nTime * 1000;

while(TimingDelay!=0);

}

/*延时功能,时间基准为1us*/

无效延迟_us(__IO uint32_t nTime)

{

TimingDelay = nTime

while(TimingDelay!=0);

}

(2)将相应的GPIO端口配置为单总线数据端子。

/*配置DHT11数据引脚,并将其设置为浮点输入模式*/

void DHT 11 _ gpio _ portIn(void)

{

GPIO _ init typedef GPIO _ init structure;

RCC _ APB 2 periphclockcmd(RCC _ APB 2 periph _ gpio a,使能);

GPIO_InitStructure。GPIO _ Pin = GPIO _ Pin _ 4;

GPIO_InitStructure。GPIO _ Mode = GPIO _ Mode _ IN _ FLOATING;

GPIO_InitStructure。GPIO _ Speed = GPIO _ Speed _ 50MHz

GPIO _ Init(GPIOA & amp;GPIO _ init structure);

}

/*配置DHT11数据引脚,设置为推挽输出模式*/

void DHT 11 _ gpio _ port out(void)

{

GPIO _ init typedef GPIO _ init structure;

RCC _ APB 2 periphclockcmd(RCC _ APB 2 periph _ gpio a,使能);

GPIO_InitStructure。GPIO _ Pin = GPIO _ Pin _ 4;

GPIO_InitStructure。GPIO _ Mode = GPIO _ Mode _ Out _ PP

GPIO_InitStructure。GPIO _ Speed = GPIO _ Speed _ 50MHz

GPIO _ Init(GPIOA & amp;GPIO _ init structure);

}

由于DHT11采用单总线通信协议,数据传输是双向的,所以数据端口分别设置为浮动输入模式和推挽输出模式。并且数据端口的输入和输出被定义为宏定义。

# define DHT 11 _ OUT _ H GPIO _ set bits(GPIO a,GPIO_Pin_4)

# define DHT 11 _ OUT _ L GPIO _ reset bits(GPIO a,GPIO_Pin_4)

#定义DHT 11 _ IN GPIO _ ReadInputDataBit(GPIOA,GPIO_Pin_4)

(3)根据DHT11时序图,编写时序函数。

现在,我们开始编写总线驱动函数。根据时序图,主机发送命令开始转换,然后等待DHT11的转换响应并输出数据,最后读取数据。

/*启动总线功能*/

void DHT 11 _ reset(void)

{

DHT 11 _ gpio _ port out();//设置为输出模式

DHT 11 _ OUT _ L;?//主机将总线拉低至少18ms。

delay _ ms(18);

DHT 11 _ OUT _ H;?//主机拉起20~40us。

delay _ us(30);

DHT 11 _ gpio _ portIn();?//设置为输入模式,等待DHT11响应。

}

/*DHT11响应函数*/

u8 dht11_scan(void)

{

返回DHT 11 _ IN;?//返回值为DHT11的响应信号

}

实时监控DHT11的数据线,直到产生低电平,表示DHT11响应主机请求,开始传输数据。

/*DHT11读取位功能*/

u8 DHT 11 _ read _ bit(void)

{

while(DHT 11 _ IN = = RESET);//数据位传输前有一个50us的低电平。

delay _ us(40);?//根据高电平持续时间决定电平是1还是0。

if(DHT11_IN==SET)?//“0”电平时长26 ~ 28 us,“1”电平时长70us。

{

while(DHT 11 _ IN = = SET);

返回1;?//如果检测到高电平,返回值为1。

}

其他

{

返回0;?//如果检测到高电平,返回值为0。

}

}

/*DHT11读取字节功能*/

//注意:首先传输数据的最高有效位。

u8 DHT 11 _ read _ byte(void)

{

u8 i,dat = 0x00

for(I = 0;我& lt8;i++)

{

dat = dat & lt& lt1;

dat = dat | DHT 11 _ read _ bit();//读出串行数据。

}

返回dat

}

DHT11响应时,开始通过单总线传输数据。在读位功能中,通过高电平的时间来判断输出是‘1’还是‘0’。在read byte函数中,调用read bit函数将每8位传输的数据整合成字节并读出。

我们参考DHT11数据手册,知道数据传输的结构(按顺序):湿度整数部分(1字节),湿度小数部分(1字节),温度整数部分(1字节),温度小数部分(1字节),等等。在这里,它实际上是一个简单的通信协议。校验和是源数据所有字节总和的低8位,保证了传输数据的正确性和稳定性。

/*DHT11读取数据功能*/

u8 DHT 11 _ read _ data(void)

{

u8i;

DHT 11 _ reset();

if(DHT 11 _ scan()= = reset)//DHT 11发出响应信号。

{

while(DHT 11 _ IN = = RESET);//DHT11下拉总线80us。

while(DHT11_IN!=复位);//DHT11拉高总线80us。

for(I = 0;我& lt5;i++)

{

buffer[I]= DHT 11 _ read _ byte();

}

while(DHT 11 _ IN = = RESET);//发送完最后1位数据后,等待50us低电平结束。

DHT 11 _ gpio _ port out();

DHT 11 _ OUT _ H;//主机将总线拉高

if(缓冲区[0]+缓冲区[1]+缓冲区[2]+缓冲区[3]= =缓冲区[4])

{

返回1;?//验证成功。

}

其他

{

返回0;?//验证失败。

}

}

其他

{

返回0;?//DHT11没有发出响应信号。

}

}

在读取字节中,等待DHT11的响应,然后开始接收数据,并连续读取五次,存储在预定义的数组中。主机发送结束信号,最后检查读取的数据。

(4)测量和显示温度和湿度

主函数调用数据读取函数DHT11和lcd显示函数来显示温度和湿度。

if(DHT11 _ Read _ data()= = 1)//读取数据,前提是DHT 11响应且数据验证成功。

{

湿度=缓冲区[0];//buffer[0]存储湿度的整数部分。

温度=缓冲液[2];//buffer[2]存储温度的整数部分。

}

Lcd_display_string(2,0,“温度”);

lcd_display_num_m(2,32,温度/10);

lcd_display_num_m(2,40,温度% 10);

Lcd_display_string(4,0,“湿度”);

lcd_display_num_m(4,32,湿度/10);

lcd_display_num_m(4,40,湿度% 10);

至此,通过本章的讲解,相信大家对DHT11模块应该有了大致的了解。当然,如果只是看文章,效果可能不太理想。这些东西都是需要动手的,俗话说“熟能生巧。”所以,你可以在网上买开发板和一些模块,不一定要和我的一样。当然,如果你想挑战自己,可以买个最小系统板,这样外围硬件电路可以自己搭建(功能是定制的),灵活,也锻炼了自己的动手能力。如果你是一个会设计PCB的大老板,那就更好了。要估计你的水平,浏览我的文章就行了。