基于STM32的环境监测系统设计与实现

1.1设计思路分析

设计流程图:(设计思路)

 

   用应用光敏电阻传感器和温度传感器,实现LCD显示以及数码管的显示+PC机串口显示摄氏温度、湿度;到达可键盘设定的阈值时LED自动发光、蜂鸣器报警自动启动等功能。采用STM32L431RCT6、以及STM32F411单片机作为数据采集、处理、控制核心,系统的总体设计如图所示。芯片内部ST-LINK对外的端口称为ST-LINK端口,该端口是一个用于STM8和STM32微控制器系列的在线调试器和编程器,也是大家口中的下载器。junction link 接合链路,就是ARM板上要用的JLINK。ST-Link具有SWIM、JTAG / SWD等通信接口,用于与STM8或STM32微控制器进行通信(各版本有差异)。五大模块协同工作,主要实现对温度的检测、显示以及报警功能。

 

1.2 芯片的工作原理

根据STM32L431和STM32F411的芯片数据手册查找对应的引脚,并将对面的引脚打开,让温湿度传感器、光照强度传感器收集温湿度数据和光照强度数据并且将数据显示在LCD上面,最后在达到键盘设定的阀值时自动开启或者关闭LED或电机。并在PC机上读取芯片的温湿度。

1.3芯片设计要求

(1)将板子放在安全区域启动

(2)板子自动收集相应传感器数据,并且在LCD中显示出来,最后根据键盘设定阈值自动开启LED或者电机等功能。

2硬件组成   

本次设计主要是用到芯片上的E53_IA1拓展板E53_IA1扩展板采用了E53标准接口,包含了一个补光灯,一个BH1750光照强度传感器,一个小的贴片电机,一个温湿度传感器SHT30,一个其中补光灯和贴片电机使用普通GPIO控制,BH1750和SHT30使用IIC接口通信。

2.1 基础硬件

(1)传感器:用STM32L431RCT6自带的温湿度和光照强度传感器

DTH11温湿度传感器

(2)显示器:用STM32L431RCT6自带的LCD进行数据显示,STM32F411自带的数码管。

(3)执行器:蜂鸣器

 

2.2 数据采集模块

BH1750模块:
BH1750FV1是两线式串行总线接口(IIC)的16位数字输出型环境光强度传感器,利用它的高分辨率可以探测较大范围内的光照强度变化(1lx

 

BH1750的原理图如下:

SHT30模块:
SHT30温湿度传感器是一个完全校准的、现行的、带有温度补偿的数字输出型传感器,具有 2.4V-5.5V 的宽电压支持,使用IIC接口进行通信,最高速率可达1M并且有两个用户可选地址,除此之外,它还具有8个引脚的DFN超小封装。如图:

 

 

2.3 核心控制模块

配置时钟源

如果选择使用外部高速时钟(HSE),则需要在System Core中配置RCC;

如果使用默认内部时钟(HSI),这一步可以略过;我这里使用外部时钟:

 

 

配置串口

小熊派开发板板载ST-Link并且虚拟了一个串口, 这里我将开关拨到AT-MCU模式,使PC的串口与USART1之间连接。

接下来开始配置USART1:

 

配置硬件I2C

首先查看小熊派开发板的原理图,确定EEPROM接在哪个I2C接口上

接下来开始配置I2C接口1:

 

配置时钟树

STM32L4的最高主频到80M,所以配置PLL,最后使HCLK = 80Mhz即可:

2.4 驱动模块

复制裸机驱动文件到LiteOS工程

E53_IA1扩展板上的 BH1750 光照强度传感器和SHT30温湿度传感器使用的是 IIC 通信接口,所以除了复制 STM32CubeMX 生成的i2c.h和i2c.h文件,还需要在此基础上复制包含了 BH1750 传感器驱动和SHT30传感器驱动的 E53_IA1 扩展板驱动文件。

在复制文件的时候,按照上一篇文章中所说的,复制i2c.h到Inc 文件夹,复制i2c.c到 Src 文件夹,再复制自己编写的驱动文件E53_IA1.c、E53_IA1.h到 Hardware文件夹。

IoT-Studio中提供的默认工程已经复制好了这些文件,无需再次添加,如图:

 

驱动函数mian.c

头文件

#include "dht11.h"

#include "delay.h"



//复位DHT11

void DHT11_Rst(void)    

{                 

DHT11_IO_OUT(); //SET OUTPUT

    DHT11_DQ_OUT=0; //拉低DQ

    delay_ms(20);     //拉低至少18ms

    DHT11_DQ_OUT=1; //DQ=1

delay_us(30);      //主机拉高20~40us

}

//等待DHT11的回应

//返回1:未检测到DHT11的存在

//返回0:存在

u8 DHT11_Check(void)    

{   

u8 retry=0;

DHT11_IO_IN();//SET INPUT  

    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 *temp,u8 *humi)    

{        

  u8 buf[5];

u8 i;

DHT11_Rst();

if(DHT11_Check()==0)

{

for(i=0;i<5;i++)//读取40位数据

{

buf[i]=DHT11_Read_Byte();

}

if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])

{

*humi=buf[0];

*temp=buf[2];

}

}else return 1;

return 0;     

}

//初始化DHT11的IO口 DQ 同时检测DHT11的存在

//返回1:不存在

//返回0:存在      

u8 DHT11_Init(void)

{  

RCC->AHB1ENR|=1<<1;//使能PORTB口时钟

GPIO_Set(GPIOB,PIN9,GPIO_MODE_OUT,GPIO_OTYPE_PP,GPIO_SPEED_50M,GPIO_PUPD_PU);//PB9设置     

DHT11_Rst();  //复位DHT11

return DHT11_Check();//等待DHT11的回应

}

Main.h函数:

#ifndef __DHT11_H

#define __DHT11_H

#include "sys.h"   

//IO方向设置

#define DHT11_IO_IN()  {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=0<<9*2;} //PB9输入模式

#define DHT11_IO_OUT() {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=1<<9*2;} //PB9输出模式

IO操作函数    

#define DHT11_DQ_OUT PBout(9) //数据端口 PB9

#define DHT11_DQ_IN  PBin(9)  //数据端口 PB9





u8 DHT11_Init(void);//初始化DHT11

u8 DHT11_Read_Data(u8 *temp,u8 *humi);//读取温湿度

u8 DHT11_Read_Byte(void);//读出一个字节

u8 DHT11_Read_Bit(void);//读出一个位

u8 DHT11_Check(void);//检测是否存在DHT11

void DHT11_Rst(void);//复位DHT11    

#endif

3.2 软件开发环境

使用的是STM32CubeMX MCU选择器生成代码使用IoT Studio软件来进行代码修改编译以及调试,其开发环境如下图所示:

4调试

 

调试过程以及遇到的问题:

1.将板子用usb端口与电脑连接起来,刚开始连接的时候无法识别出这个板子,然后我们下载了串口驱动,以及STM32F411的驱动模块,以及了解了USB SLAVE和USB JTAG的区别,解决了问题,调试中观察连接的是第几个端口并且将串口配置为对应的端口还有波特率等等(如:我的端口为COM3,波特率为115200)

2.将代码编译并且烧录进板子,这个过程要注意的是DTH11的插线方式是否正确若反着来接会烧坏传感器。

3.根据事先写好的算法逻辑判断板子是否按既定目标运转,比如数码管是否能正常显示,按键能否正常调节,LED灯有没有按照设定好的光照强度阈值开启、蜂鸣器有没有按照设定好的温度阈值开启等等

4.如果板子没有按照既定目标运转则需要回到代码中修改逻辑。如果已经按既定目标运转则调试成功。

附件

源程序

STM32代码:

Main.c

#include "main.h"

#include "string.h"

#include "buzzer.h"

#include "stdio.h"

#include "led.h"

#include "delay.h"

#include "sys.h"

#include "usart.h"  

#include "dht11.h"  

#include "timer.h"

#include "smg.h"

// 共阴数字数组

// 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F, .,全灭

u8 smg_num[]={0xfc,0x60,0xda,0xf2,0x66,0xb6,0xbe,0xe0,0xfe,0xf6,0xee,0x3e,0x9c,0x7a,0x9e,0x8e,0x01,0x00};

u8 smg_wei=0;//数码管位选

u8 num=0;//数值

u16 led_t=0;//led统计时间

u16 dht11_t=0;//dht11采样时间

u8 temperature;//温度值       

u8 humidity;//湿度值

u8 val=30;//温度阈值

u8 val1=30;//湿度阈值

int main(void)

{

u8 ret;//按键flag

Key_Pin_Init();

Led_Pin_Init();

buzzer_Init();

buzzer_off;

Stm32_Clock_Init(96,4,2,4);//设置时钟,96Mhz

delay_init(96);        //初始化延时函数

LED_Init();    //初始化LED时钟

LED_SMG_Init();            //数码管初始化

uart_init(96,115200);    //串口初始化为115200

printf("NANO STM32\r\n");

printf("DHT11 TEST\r\n");

  while(DHT11_Init()) //DHT11初始化

{

printf("DHT11 Error\r\n");

delay_ms(200);

LED3=!LED3;//LED3闪烁表示DHT11初始化失败

}

    LED3=1;

printf("DHT11 OK\r\n");

TIM3_Init(20-1,9600-1);    //2ms定时显示  



while(1)

{

printf("温度:%d,湿度:%d\n",temperature,humidity);

ret=Key_Scan();

//ret=1,key0按下

if(ret==1){

val++;

}

//ret=2,key1按下

if(ret==2){

val--;

}



Delay_Ms(10);

if(val<=temperature && val<=humidity)buzzer_on;

else

buzzer_off;



if(val==32)

val=23;

}

}



void TIM3_IRQHandler(void)   //TIM3中断

{

if(TIM3->SR&0X0001)//溢出中断

{

        dht11_t++;

if(dht11_t==500)//DHT11 1S采样

{

dht11_t=0;

DHT11_Read_Data(&temperature,&humidity);//读取温湿度值

}

switch(smg_wei)

{

   case 0:  num = smg_num[val/10]; break;//温度值

   case 1:  num =smg_num[val%10];break;

   case 2:  num =smg_num[temperature/10];break;

   case 3:  num =smg_num[temperature%10];break;

   case 4:  num = smg_num[val/10]; break;//温度值

   case 5:  num =smg_num[val%10];break;

   case 6:  num = smg_num[humidity/10]; break;//湿度值

   case 7:  num =smg_num[humidity%10];break;    

}

    LED_Write_Data(num,smg_wei);

    LED_Refresh();

    smg_wei++;



if(smg_wei==8)  smg_wei=0;

led_t++;

if(led_t==250)//LED0每500ms闪烁

{

led_t=0;

LED0=!LED0;

}

}

TIM3->SR&=~(1<<0);//清除中断标志位

}

void Key_Pin_Init(void);

PC端显示DTH11传感器读取的温湿度(通过python制作界面)

import serial

import threading

import binascii

from datetime import datetime

import struct

import time

port = serial.Serial("COM3", 115200,timeout = 20)

def port_open(self):

    if not self.port.isOpen():

        self.port.open()

def port_close(self):

    self.port.close()

def send_data(self):

    self.port.write('')



def read_data():



        global is_exit

        global data_bytes

        # self.data = self.data.decode('utf-8')



        while not is_exit:



            count = port.inWaiting()

            data = port.readline()

data.strip()

data = data.decode('GB2312','ignore')

             return data



while(1):

    time.sleep(0.9)#不能超过1

    print(read_data())

上传onenet:

初始化esp8266:

void ESP8266_Init(void)
 {

	GPIO_InitTypeDef GPIO_Initure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

	//ESP8266复位引脚
	GPIO_Initure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Initure.GPIO_Pin = GPIO_Pin_13;					//GPIOC13-复位
	GPIO_Initure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_Initure);
	
	GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET);
	delay_ms(250);
	GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
	delay_ms(500);
	ESP8266_Clear();
	printf("1. AT\r\n");
	while(ESP8266_SendCmd("AT\r\n", "OK"))
		delay_ms(500);
	
	printf("2. CWMODE\r\n");
	while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"))
		delay_ms(500);
	
	printf( "3. AT+CWDHCP\r\n");
	while(ESP8266_SendCmd("AT+CWDHCP=1,1\r\n", "OK"))
		delay_ms(500);
	
	printf("4. CWJAP\r\n");
	while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "GOT IP"))
		delay_ms(500);
	
	printf( "5. CIPSTART\r\n");
	while(ESP8266_SendCmd(ESP8266_ONENET_INFO, "CONNECT"))
		delay_ms(500);
	
	printf("6. ESP8266 Init OK\r\n");

}

发送数据到onenet:

void OneNet_SendData(void)
{

MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0};												//协议包

char buf[128];

short body_len = 0, i = 0;

printf( "Tips:	OneNet_SendData-MQTT\r\n");

memset(buf, 0, sizeof(buf));//清空数组内容

body_len = OneNet_FillBuf(buf);																	//获取当前需要发送的数据流的总长度

if(body_len)
{
	if(MQTT_PacketSaveData(DEVID, body_len, NULL, 5, &mqttPacket) == 0)							//封包
	{
		for(; i < body_len; i++)
			mqttPacket._data[mqttPacket._len++] = buf[i];
		
		ESP8266_SendData(mqttPacket._data, mqttPacket._len);									//上传数据到平台
		printf( "Send %d Bytes\r\n", mqttPacket._len);
		
		MQTT_DeleteBuffer(&mqttPacket);															//删包
	}
	else
		printf(  "WARN:	EDP_NewBuffer Failed\r\n");
}
}

最后效果

 

 

 

 

物联沃分享整理
物联沃-IOTWORD物联网 » 基于STM32的环境监测系统设计与实现

发表评论