【STM32练习】基于STM32的PM2.5环境监测系统

一.项目背景

        最近为了完成老师交付的任务,遂重制了一下小项目用STM32做一个小型的环境监测系统。

        项目整体示意框图如下:

二.器件选择

  • 单片机(STM32F103)
  • 数字温湿度模块(DHT11)
  • 液晶显示模块(0.8寸OLED)
  • 粉尘传感器模块(GP2Y10)
  • 报警模块(蜂鸣器)
  • 按键控制模块(独立按键)

        由于笔者觉得时钟模块没什么必要性,就没再加DS1302上去了。 

三.PCB绘制

        PCB推荐使用嘉立创专业版绘制,对于初学者来说简单易上手,而且这种DIY的小玩意绘制的PCB大小控制在10*10以内还可以免费打样。

        嘉立创很方便的一点是可以使用它内部自带的在线库,省去了自己画封装画元件的步骤,非常简单,但是注意要辨别这里的元件封装的正确性,因为有些元件封装是用户贡献的,所以有些地方不一定正确,各位读者如果是小白的话一定要注意辨别是否符合自己的需求!!!

        这里PCB布线也没什么好说的,很简单的几个模块,而且板子的大小很大空间完全足够布线。

四.核心代码

以下只对部分核心代码做展示。

项目文件目录

GP2Y10粉尘传感器的驱动程序

GP2Y10.h

#ifndef   __GP2Y10_H__
#define   __GP2Y10_H__#include <stdint.h>
#include <stdio.h>
#include "main.h"
#include "stm32f1xx_hal.h"
#include "driver_timer.h"
#include "driver_lcd.h"extern ADC_HandleTypeDef hadc1;#define LIMIT(x, min, max) 	( (x) < (min)  ? (min) : ( (x) > (max) ? (max) : (x) ) ) // 限幅函数
#define GP2Y10_LED_ON()	 HAL_GPIO_WritePin(GP_LED_GPIO_Port, GP_LED_Pin, GPIO_PIN_RESET) // 传感器LED灯开
#define GP2Y10_LED_OFF() HAL_GPIO_WritePin(GP_LED_GPIO_Port, GP_LED_Pin, GPIO_PIN_SET)// 传感器LED灯关#define PM25_LED_H			HAL_GPIO_WritePin(GP_LED_GPIO_Port, GP_LED_Pin, GPIO_PIN_SET);
#define PM25_LED_L			HAL_GPIO_WritePin(GP_LED_GPIO_Port, GP_LED_Pin, GPIO_PIN_RESET);#define GP2Y10_SAMP_TIME	280 // 传感器采样时间280us
#define GP2Y10_LEDON_TIME	320 // LED灯开持续时间
#define GP2Y10_PULSE_PERIOD	10000 // 传感器测量脉冲一个周期的时间#define PM25_READ_TIMES	20void GP2Y10_Init(void);
float GP2Y10_Value(void);
float Get_PM25_Average_Data(void);
void GP2Y10_Test(void);#endif

GP2Y10.c

#include "GP2Y10.h"void GP2Y10_Init(void)
{GP2Y10_LED_OFF(); // 初始化传感器LED灯为关HAL_ADC_Start(&hadc1);  //开启ADCHAL_ADC_PollForConversion(&hadc1, 50);   //等待转换完成,50为最大等待时间,单位为ms
}// 计算粉尘浓度
float GP2Y10_Value(void)
{uint16_t ADCVal;int dustVal = 0;float Voltage;PM25_LED_H;	//置1  开启内部LEDudelay(280); 	// 开启LED后的280us的等待时间ADCVal = HAL_ADC_GetValue(&hadc1);  //PA1 采样,读取AD值udelay(19);			  //延时19us,因为这里AD采样的周期为239.5,所以AD转换一次需耗时21us,19加21再加280刚好是320usPM25_LED_L;	//置0  关闭内部LEDudelay(9680);			//需要脉宽比0.32ms/10ms的PWM信号驱动传感器中的LEDVoltage = 3.3f * ADCVal / 4096.f * 2; //获得AO输出口的电压值dustVal = (0.17*Voltage-0.1)*1000;  //乘以1000单位换成ug/m3//if (dustVal < 0)dustVal = 0;            //限位//if (dustVal>500)        dustVal=500;return dustVal;
}float Get_PM25_Average_Data(void)
{float temp_val=0;uint8_t t;for(t=0;t<PM25_READ_TIMES;t++)	//#define PM25_READ_TIMES	20	定义读取次数,读这么多次,然后取平均值{temp_val+=GP2Y10_Value();	//读取ADC值mdelay(5);}temp_val/=PM25_READ_TIMES;//得到平均值return temp_val;//返回算出的ADC平均值
}void GP2Y10_Test(void)
{GP2Y10_Init();float concentration;while(1){LCD_PrintString(0,0,"CON:");concentration = GP2Y10_Value();LCD_PrintSignedVal(0,2,concentration);mdelay(1000);}
}

按键读取程序

#include "Key.h"void Key_Init(void)
{}uint8_t Key_GetValue(void)
{uint8_t value = 0;if(HAL_GPIO_ReadPin(GPIOA,KEY_A_Pin) == GPIO_PIN_SET)value = 1;if(HAL_GPIO_ReadPin(GPIOA,KEY_B_Pin) == GPIO_PIN_SET)value = 2;if(HAL_GPIO_ReadPin(GPIOA,KEY_C_Pin) == GPIO_PIN_SET)value = 3;if(HAL_GPIO_ReadPin(GPIOA,KEY_D_Pin) == GPIO_PIN_SET)value = 4;return value;
}uint8_t Key_Scan(void)
{uint8_t key_number = 0;key_number = Key_GetValue();if(key_number != 0){mdelay(20);while( Key_GetValue() != 0);mdelay(20);return key_number;}return 0;
}void Key_Test(void)
{uint8_t Key_Number = Key_Scan();if(Key_Number == 1)printf("A\n");else if(Key_Number == 2)printf("B\n");else if(Key_Number == 3)printf("C\n");else if(Key_Number == 4)printf("D\n");
}

定时程序

        这里原本用的是韦东山老师的RTOS里的非阻塞延时,但是由于笔者最终选择使用裸机完成整个功能,于是便把ms延时函数换成了HAL库的官方延时函数。

#include "driver_timer.h"
#include "stm32f1xx_hal.h"
#define CPU_FREQUENCY_MHZ    72		// STM32时钟主频void udelay(uint32_t delay)
{int last, curr, val;int temp;while (delay != 0){temp = delay > 900 ? 900 : delay;last = SysTick->VAL;curr = last - CPU_FREQUENCY_MHZ * temp;if (curr >= 0){do{val = SysTick->VAL;}while ((val < last) && (val >= curr));}else{curr += CPU_FREQUENCY_MHZ * 1000;do{val = SysTick->VAL;}while ((val <= last) || (val > curr));}delay -= temp;}
}void mdelay(int ms)
{
//    for (int i = 0; i < ms; i++)
//        udelay(1000);HAL_Delay(ms);
}

逻辑功能实现

Task.h

#ifndef   _TASK_H__
#define   _TASK_H__#include <stdint.h>
#include <stdbool.h>
#include "HeaderConfig.h"#define HUM_MAX      100
#define HUM_MIN      10
#define TEM_MAX      100
#define TEM_MIN      10
#define DUS_MAX      100
#define DUS_MIN      10void Task_Test(void);
void Device_Init(void);
void Task_Prime(void);#endif

Task.c

#include "Task.h"uint8_t Prime_Mode = 0;
uint8_t Start_Mode = 0;
uint8_t Threshold_Mode = 0;//温度调节模式
bool isCollecting = false;int Humidity = 0,Temperature = 0;
float Dust_Concentration = 0;
uint8_t TemperatureMax = 30;
uint8_t TemperatureMin = 10;uint8_t DataRead_Count = 0;//数据采集计次
uint8_t Blink_Count = 0;//闪烁
uint16_t Timer_2000ms = 0;//数据采集间隔
uint16_t Timer_Blink = 0;//void Task_Test(void)
{//LCD_Test();//Key_Test();
}void Device_Init(void)
{LCD_Init();Buzzer_Init();DHT11_Init();GP2Y10_Init();Key_Init();
}void Task_Key(void)
{uint8_t Key_Number = Key_Scan();if(Prime_Mode == 2){if(Key_Number == 1){LCD_Clear();Start_Mode = 1;Prime_Mode = 0;isCollecting = false;}}else{if(Key_Number == 1){LCD_Clear();Start_Mode ^= 1;Prime_Mode = 0;isCollecting = false;}}if(Prime_Mode == 2){if(Key_Number == 2){LCD_Clear();Threshold_Mode ^= 1;}}else{if(Key_Number == 2){LCD_Clear();Prime_Mode = 1;isCollecting = false;}}//设置报警阈值if(Key_Number == 3){LCD_Clear();Prime_Mode = 2;isCollecting = false;if(Threshold_Mode == 0)//高温阈值++{++TemperatureMax;}if(Threshold_Mode == 1)//低温阈值++{++TemperatureMin;}}if(Key_Number == 4){LCD_Clear();Prime_Mode = 2;isCollecting = false;if(Threshold_Mode == 0)//高温阈值++{--TemperatureMax;}if(Threshold_Mode == 1)//低温阈值++{--TemperatureMin;}}
}void filter_and_smooth(float *data, int size, float *smoothed_data) {// 滤除0int count = 0;for (int i = 0; i < size; i++) {if (data[i] != 0) {smoothed_data[count++] = data[i];}}// 平滑曲线for (int i = 0; i < count - 2; i++) {smoothed_data[i] = (smoothed_data[i] + smoothed_data[i + 1] + smoothed_data[i + 2]) / 3.0;}
}void Task_DataCollect(void)
{//DHT11_Read(&Humidity,&Temperature);if ( DHT11_Read(&Humidity,&Temperature) !=0 ){DHT11_Init();}Dust_Concentration = Get_PM25_Average_Data();//采集粉尘浓度数据
//	printf("Humidity:%d\n",Humidity);
//	printf("Temperature:%d\n",Temperature);
//	printf("PM2.5:%f\n",Dust_Concentration);printf("%f\n",Dust_Concentration/10);
}void Task_Alarm(void)
{if(Temperature > TemperatureMax || Temperature < TemperatureMin){Buzzer_Control(ON);mdelay(20);Buzzer_Control(OFF);mdelay(20);}	
}void Task_OLED(void)
{switch(Prime_Mode){case 0:{//控制启动关机if(Start_Mode == 1)//开机{LCD_PrintString(0,0,"Welcome to:");LCD_PrintString(5,2,"System");}else//关机LCD_Clear();break;}case 1://温湿度,PM2.5浓度{if(Start_Mode == 1){if(!isCollecting){LCD_PrintString(2,3,"Collecting...");mdelay(3000);LCD_Clear();isCollecting = true;}LCD_PrintString(0,0,"Data=>");LCD_PrintString(0,2,"Humidity:");LCD_PrintSignedVal(9, 2, Humidity);LCD_PrintString(0,4,"Temperature:");LCD_PrintSignedVal(12, 4, Temperature);LCD_PrintString(0,6,"PM2.5:");LCD_PrintSignedVal(6, 6, (int)Dust_Concentration);}break;}case 2://设置报警阈值{if(Start_Mode == 1){LCD_PrintString(0,2,"High:");LCD_PrintString(0,4,"Low:");if(Threshold_Mode == 0){if(Blink_Count == 0)LCD_PrintSignedVal(6, 2, TemperatureMax);elseLCD_PrintString(6,2,"     ");LCD_PrintSignedVal(6, 4, TemperatureMin);}if(Threshold_Mode == 1){LCD_PrintSignedVal(6, 2, TemperatureMax);if(Blink_Count == 0)LCD_PrintSignedVal(6, 4, TemperatureMin);elseLCD_PrintString(6,4,"     ");}}break;}
//		case 3://出行建议
//		{
//			if(Start_Mode == 1)
//			{
//				if(Humidity > HUM_MAX)
//					LCD_PrintString(0,0,"The humidity is too high!!!");
//				else if(Humidity < HUM_MIN)
//					LCD_PrintString(0,0,"The humidity is too low!!!");
//			}
//			break;
//		}		}
}void Task_Prime(void)
{Task_Key();if(DataRead_Count == 1){Task_DataCollect();DataRead_Count = 0;}Task_Alarm();Task_OLED();
}void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM2){++Timer_2000ms;++Timer_Blink;if(Timer_2000ms >= 2000){DataRead_Count++;Timer_2000ms = 0;}if(Timer_Blink >= 650){Blink_Count ^= 1;Timer_Blink = 0;}}
}

五.最终效果

实物

功能演示 

基于STM32的PM2.5监测系统

六.总结

        通过本项目的实践,不仅实现了基础功能监测,还为未来更复杂的项目打下了坚实的基础。希望读者通过该项目,也能够掌握模块化开发的思路,逐步进阶!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/63129.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《开源数据:开启信息共享与创新的宝藏之门》

《开源数据&#xff1a;开启信息共享与创新的宝藏之门》 一、开源数据概述&#xff08;一&#xff09;开源数据的定义&#xff08;二&#xff09;开源数据的发展历程 二、开源数据的优势&#xff08;一&#xff09;成本效益优势&#xff08;二&#xff09;灵活性与可定制性&…

ReactPress最佳实践—搭建导航网站实战

Github项目地址&#xff1a;https://github.com/fecommunity/easy-blog 欢迎Star。 近期&#xff0c;阮一峰在科技爱好者周刊第 325 期中推荐了一款开源工具——ReactPress&#xff0c;ReactPress一个基于 Next.js 的博客和 CMS 系统&#xff0c;可查看 demo站点。&#xff08;…

2024,大模型杀进“决赛圈”

Henry Chesbrough在著作《通过技术创新盈利势在必行》中&#xff0c;曾提出过一个创新的“漏斗模型”。开放式创新一开始鼓励百花齐放&#xff0c;但最终只有10%的技术能够通过这个漏斗&#xff0c;成功抵达目标市场target market&#xff0c;进入到商业化与产业化的下一个阶段…

STM8单片机学习笔记·GPIO的片上外设寄存器

目录 前言 IC基本定义 三极管基础知识 单片机引脚电路作用 STM8GPIO工作模式 GPIO外设寄存器 寄存器含义用法 CR1&#xff1a;Control Register 1 CR2&#xff1a;Control Register 2 ODR&#xff1a;Output Data Register IDR&#xff1a;Input Data Register 赋值…

页面加载速度优化策略:提升用户体验的关键

文章目录 前言一、为什么需要优化页面加载速度&#xff1f;二、前端优化技术三、后端优化策略四、构建与部署优化五、案例研究&#xff1a;实际效果展示结语 前言 在当今快节奏的互联网环境中&#xff0c;页面加载速度不仅是用户体验的重要组成部分&#xff0c;更是影响网站性…

【CSS in Depth 2 精译_081】 13.1:CSS 渐变效果(下)——CSS 径向渐变(13.1.3)+ CSS 锥形渐变(13.1.4)

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第四部分 视觉增强技术 ✔️【第 13 章 渐变、阴影与混合模式】 ✔️ 13.1 渐变 ✔️ 13.1.1 使用多个颜色节点&#xff08;上&#xff09;13.1.2 颜色插值方法&#xff08;中&#xff09;13.1.3 径…

商务礼仪学习笔记

时间,场合,地点 女士: 1. 着装(裙装套装,最短不能超过膝盖一拳,裙子形状直通,颜色简单不能花里胡哨,上下颜色不能超过三种,深灰深蓝;上下颜色,装饰,面料统一;丝袜不要过于花,肉色透明比较推荐) 2. 妆容和发型(经过搭理,不要毛躁; 肤色保持一致,均衡;腮红…

ubuntu 用 ss-tproxy的最终网络结构

1、包含了AD广告域名筛选 2、Ss-tproxy 国内国外地址分类 3、chinadns-ng解析 4、透明网关 更多细节看之前博客 ubuntu 用ss-TPROXY实现透明代理&#xff0c;基于TPROXY的透明TCP/UDP代理,在 Linux 2.6.28 后进入官方内核。ubuntu 用 ss-tproxy的内置 DNS 前挂上 AdGuardHome…

iOS swift开发系列--如何给swiftui内容视图添加背景图片显示

我需要在swiftui项目中显示背景图&#xff0c;有两种方式&#xff0c;一种是把图片拖入asset资源中&#xff0c;另外一种是直接把图片放在源码目录下。采用第一种方式&#xff0c;直接把图片拖到资源目录&#xff0c;但是swiftui项目没有弹出&#xff0c; “Copy items if need…

BUUCTF Pwn [HarekazeCTF2019]baby_rop2 题解

下载 得到两个文件 checksec 64位 拖入IDA64 查看main函数 看到给了个libc说明这题是ret2libc题 这里的打印函数是printf 所以利用printf函数的plt输出真实地址got 但printf的got好像不行 所以换成了read的got 因为这是64位程序 所以用寄存器传参&#xff1b;又因为printf得…

语音识别失败 chrome下获取浏览器录音功能,因为安全性问题,需要在localhost或127.0.0.1或https下才能获取权限

环境&#xff1a; Win10专业版 谷歌浏览器 版本 131.0.6778.140&#xff08;正式版本&#xff09; &#xff08;64 位&#xff09; 问题描述&#xff1a; 局域网web语音识别出现识别失败 chrome控制台出现下获取浏览器录音功能&#xff0c;因为安全性问题&#xff0c;需要在…

【前端知识】Javascript进阶-类和继承

文章目录 概述一、类&#xff08;Class&#xff09;二、继承&#xff08;Inheritance&#xff09; 三、继承的实现方式作用一、类和作用二、继承和作用 概述 当然可以&#xff0c;以下是对JavaScript中类和继承的详细介绍&#xff1a; 一、类&#xff08;Class&#xff09; 定…

前端搭建企业级项目的具体步骤?

‌前端搭建企业级项目的具体步骤如下‌&#xff1a; ‌确定项目技术栈和规划项目结构‌&#xff1a;首先&#xff0c;确定使用的前端框架&#xff0c;如Vue.js&#xff0c;并规划项目的目录结构&#xff0c;包括src、components、routes、store等‌。 ‌准备开发环境‌&#x…

Less和SCSS,哪个更好用?

前言 Less 和 SCSS 都是流行的 CSS 预处理器&#xff0c;它们的目的都是扩展 CSS 的功能&#xff0c;使样式表更具组织性、可维护性和可重用性。虽然它们有许多相似之处&#xff0c;但在语法、特性和工作方式上也存在一些差异。 Less Less 是一种动态样式表语言&#xff0c;…

【第三节】Git 基本操作指南

目录 前言 一、获取与创建项目 1.1 git init 1.2 git clone 二、基本快照操作 2.1 git add 2.2 git status 2.3 git diff 2.4 git commit 2.5 git reset HEAD 三、 文件管理 3.1 git rm 3.2 git mv 四、 总结 前言 本文将详细介绍 Git 的基本操作&#xff0c;包括…

【Graylog】索引别名deflector的异常处理和索引分片数限制解除

索引别名deflector的异常处理 官方推荐处理步骤 Stop all Graylog nodes (OPTIONAL) If you want to keep the already ingested messages, reindex them into the Elasticsearch index with the greatest number, e. g. graylog_23 if you want to fix the deflector graylo…

PyTorch 2.0 以下版本中设置默认使用 GPU 的方法

PyTorch 2.0 以下版本中设置默认使用 GPU 的方法 在 PyTorch 2.0以下版本中&#xff0c;默认情况下仍然是使用 CPU 进行计算&#xff0c;除非明确指定使用 GPU。在 PyTorch 2.0 以下版本中&#xff0c;虽然没有 torch.set_default_device 的便捷方法&#xff0c;但可以通过显式…

【一本通】输入两个不同的数,通过指针对两个数进行相加和相乘

【一本通】输入两个不同的数&#xff0c;通过指针对两个数进行相加和相乘 C语言代码C代码Java代码 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 输入两个不同的数&#xff0c;通过指针对两个数进行相加和相乘&#xff0c;并输出。 输入 …

X.game解析柚子币提升速效双向利好和年中历史新低原因

柚子币最新消息&#xff0c;币安宣布将于2024年9月25日21:00左右暂停柚子币网络上的代币存取业务&#xff0c;以全力支持即将到来的柚子币网络升级和硬分叉&#xff0c;这一消息为柚子币的未来发展增添了新的期待和变数。 除了速度的提升&#xff0c;Spring1.0还带来了诸多技术…

redis集群安装部署 redis三主三从集群

redis集群安装部署 redis三主三从集群 1、下载redis2、安装redis集群 三主三从3、配置redis开机自启动3.1、建立启动脚本3.2、复制多份redis启动脚本给集群使用3.3、添加可执行权限3.4、配置开机自启动 1、下载redis 本次redis安装部署选择当前最新的稳定版本7.4.1 下载链接: …