一、前言
随着城市人口规模的不断扩大和城市化进程的不断加速,城市环境污染问题越来越受到人们的关注。环境监测是评估环境污染状况、制定环保政策、维护人民身体健康的重要手段之一。传统的环境监测手段需要人工参与,成本高、效率低,不能满足大规模的实时监测需求。
本项目基于STM32设计的城市环境监测看板,实现对城市环境噪声、温湿度和粉尘浓度的实时监测和数据采集。 主控芯片采用STM32F103C8T6,具有较高的性能和稳定性,能够有效处理各种传感器采集的数据;环境噪声传感器采用ADC输出模拟信号表示环境噪声强度,能够准确测量城市环境噪声;环境温湿度传感器采用DHT11,具有高精度、快速响应和成本低廉等优点,能够准确测量城市环境的温湿度;粉尘浓度模块采用PM2.5粉尘浓度检测模块GP2Y10,能够实时快速地检测城市空气中的PM2.5粉尘浓度,为环境污染控制提供数据支持。
在本项目中,通过对不同传感器和主控芯片的选择和应用,成功设计了一款城市环境监测看板,能够实时监测和记录城市环境的噪声、温湿度和粉尘浓度等信息,为城市环保管理部门和公众提供了有力的数据支持。
二、项目整体设计思路
2.1 硬件设计思路
(1)主控芯片选择:本项目选用了STM32F103C8T6作为主控芯片,该芯片具有高性能、低功耗和丰富的外设资源,可以满足环境监测系统的要求。
(2)传感器选择:环境噪声传感器采用ADC输出模拟信号的方式来表示环境噪声强度,这里可以选择常见的电容麦克风或者MEMS麦克风作为传感器。环境温湿度传感器采用DHT11,它是一种数字式温湿度传感器,具有成本低廉、响应速度快的特点。粉尘浓度模块采用GP2Y10,它是一种激光散射式粉尘传感器,能够实时检测空气中的PM2.5粉尘浓度。
(3)连接方式:将传感器与主控芯片连接,可以通过使用模拟输入通道连接环境噪声传感器的输出,通过GPIO口连接DHT11传感器和GP2Y10传感器。
(4)电源设计:根据系统需求选择合适的电源模块,保证系统的稳定供电。
2.2 软件设计思路
(1)系统初始化:在程序开始时进行系统的初始化,包括GPIO口初始化、ADC模块初始化等。
(2)传感器数据采集:通过主控芯片的GPIO口或者ADC模块采集环境噪声、温湿度和粉尘浓度传感器的数据。对于环境噪声传感器,利用ADC模块将模拟信号转换为数字量;对于DHT11和GP2Y10传感器,直接读取其数字输出。
(3)数据处理与显示:将采集到的传感器数据进行处理,并通过LCD显示屏或者其他输出方式实时展示结果。可以设计相应的算法进行数据滤波、校正或者转换为可读格式。同时,根据不同的数据范围,可以设置合适的阈值来判断环境的状态是否达到预警水平。
(4)网络通信:通过网络模块(ESP8266)将数据传输到远程监控终端和云平台,实现远程监控和数据存储。可以使用HTTP、MQTT等协议进行数据传输,保证数据的安全性和可靠性。
(5)系统控制:设计合适的用户界面和控制方式,使用户能够方便地操作本项目。可以通过按键、触摸屏或者无线遥控等方式进行系统的开关、参数调节等操作。
三、硬件连线
(1)环境噪声传感器(模拟信号输出):
将传感器的模拟输出连接到STM32的一个ADC输入通道(PA0)。
(2)环境温湿度传感器(DHT11):
将DHT11的VCC引脚连接到STM32的3.3V电源。
将DHT11的GND引脚连接到STM32的GND引脚。
将DHT11的DATA引脚连接到STM32的一个GPIO输入引脚(PB0)。
(3)粉尘浓度模块(GP2Y10):
将GP2Y10的VCC引脚连接到STM32的3.3V电源。
将GP2Y10的GND引脚连接到STM32的GND引脚。
将GP2Y10的Vo引脚连接到STM32的一个ADC输入通道(PA1)。
(4)OLED显示屏(0.96寸OLED):
将OLED显示屏的SDA引脚连接到STM32的I2C总线的SDA引脚(PB7)。
将OLED显示屏的SCL引脚连接到STM32的I2C总线的SCL引脚(PB6)。
将OLED显示屏的VCC引脚连接到STM32的3.3V电源。
将OLED显示屏的GND引脚连接到STM32的GND引脚。
四、项目代码设计
4.1 主核心代码
代码语言:javascript复制// 初始化GPIO和ADC模块
// 启用I2C总线
// 初始化OLED显示屏驱动程序
// 循环读取传感器数据并显示
while(1) {
// 读取环境噪声传感器的模拟信号并转换为数字值
int noise = read_adc();
// 通过GPIO读取DHT11传感器的温湿度数据
float temperature, humidity;
read_DHT11(&temperature, &humidity);
// 读取粉尘浓度的模拟电压信号并转换为PM2.5颗粒物浓度值
float dust_level = read_GP2Y10();
// 将读取的数据显示在OLED屏幕上
oled_clear();
oled_print_string("Noise: " noise "dB");
oled_print_string("Temp: " temperature "C");
oled_print_string("Humidity: " humidity "%");
oled_print_string("Dust Level: " dust_level "ug/m^3");
// 根据需要设置警报阈值并发出警报
if (noise > 70 || temperature > 30 || dust_level > 50) {
beep_alarm();
}
// 等待一段时间再进行下一次循环
delay_ms(1000);
}
4.2 模块子函数代码
代码语言:javascript复制#include "stm32f1xx_hal.h"
#include "ssd1306.h"
void GPIO_Init(void) {
// 初始化GPIO引脚
// 设置环境噪声传感器的模拟输入引脚为模拟输入模式
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // 使能GPIOA时钟
GPIOA->CRL &= ~GPIO_CRL_MODE0; // 复位模式寄存器中的MODE0位
GPIOA->CRL &= ~GPIO_CRL_CNF0; // 复位配置寄存器中的CNF0位
// 设置DHT11的DATA引脚为上拉输入模式
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; // 使能GPIOB时钟
GPIOB->CRL &= ~GPIO_CRL_MODE0; // 复位模式寄存器中的MODE0位
GPIOB->CRL &= ~GPIO_CRL_CNF0_0; // 复位配置寄存器中的CNF0位
GPIOB->CRL |= GPIO_CRL_CNF0_1; // 设置为上拉输入模式
// 初始化其他GPIO引脚,例如GP2Y10的Vo引脚和I2C总线的引脚等
}
void ADC_Init(void) {
// 初始化ADC模块
// 使能ADC1时钟
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
// 配置ADC通道和采样时间
ADC1->SMPR2 |= ADC_SMPR2_SMP0_2; // 设置ADC通道为第0个通道,并设置采样时间为28.5个周期
// 配置ADC转换模式为单次转换模式
ADC1->CR2 &= ~ADC_CR2_CONT; // 关闭连续转换模式,使用单次转换模式
// 配置ADC转换触发源为软件触发
ADC1->CR2 &= ~ADC_CR2_EXTSEL; // 清除外部触发选择位
ADC1->CR2 |= ADC_CR2_EXTSEL_0; // 设置为软件触发模式
// 使能ADC模块
ADC1->CR2 |= ADC_CR2_ADON;
}
void I2C_Init(void) {
// 初始化I2C总线
// 使能I2C1时钟
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
// 配置I2C总线时钟频率
I2C1->CR2 |= 36; // 根据系统时钟频率和所需的I2C总线速率进行配置
// 配置I2C总线模式和器件地址
I2C1->CR1 &= ~I2C_CR1_SMBUS; // 关闭SMBus模式
I2C1->CR1 |= I2C_CR1_PE; // 使能I2C总线
// 配置I2C总线的GPIO引脚
// 设置SCL引脚为开漏输出模式
GPIOB->CRH |= GPIO_CRH_MODE6_0;
GPIOB->CRH &= ~GPIO_CRH_MODE6_1;
GPIOB->CRH |= GPIO_CRH_CNF6_0;
GPIOB->CRH |= GPIO_CRH_CNF6_1;
// 设置SDA引脚为开漏输出模式
GPIOB->CRH |= GPIO_CRH_MODE7_0;
GPIOB->CRH &= ~GPIO_CRH_MODE7_1;
GPIOB->CRH |= GPIO_CRH_CNF7_0;
GPIOB->CRH |= GPIO_CRH_CNF7_1;
}
void OLED_Init(void) {
// 初始化OLED显示屏驱动程序
ssd1306_Init(); // 使用 SSD1306 驱动程序进行初始化
}
void System_Init(void) {
// 初始化系统组件
HAL_Init(); // 使用HAL库进行初始化,如果没有使用HAL库,可以根据芯片厂商提供的库进行初始化
GPIO_Init(); // 初始化GPIO
ADC_Init(); // 初始化ADC模块
I2C_Init(); // 初始化I2C总线
OLED_Init(); // 初始化OLED显示屏驱动程序
}
4.3 OLED驱动代码
代码语言:javascript复制#include "stm32f10x.h"
#include "OLED.h"
#define OLED_CS_GPIO GPIOB
#define OLED_CS_PIN GPIO_Pin_12
#define OLED_DC_GPIO GPIOB
#define OLED_DC_PIN GPIO_Pin_13
#define OLED_RST_GPIO GPIOB
#define OLED_RST_PIN GPIO_Pin_14
void Delay_ms(uint16_t time) {
while(time--) {
uint16_t i = 12000; // 延时大概1ms
while(i--);
}
}
void GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// CS引脚配置
GPIO_InitStruct.GPIO_Pin = OLED_CS_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(OLED_CS_GPIO, &GPIO_InitStruct);
// DC引脚配置
GPIO_InitStruct.GPIO_Pin = OLED_DC_PIN;
GPIO_Init(OLED_DC_GPIO, &GPIO_InitStruct);
// RST引脚配置
GPIO_InitStruct.GPIO_Pin = OLED_RST_PIN;
GPIO_Init(OLED_RST_GPIO, &GPIO_InitStruct);
}
void SPI_Init(void) {
SPI_InitTypeDef SPI_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
// SPI总线配置
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStruct.SPI_CPOL = SPI_CPOL_High;
SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_Init(SPI1, &SPI_InitStruct);
SPI_Cmd(SPI1, ENABLE);
}
void OLED_WriteCmd(uint8_t cmd) {
GPIO_ResetBits(OLED_CS_GPIO, OLED_CS_PIN); // 片选信号拉低,开始传输数据命令
GPIO_ResetBits(OLED_DC_GPIO, OLED_DC_PIN); // DC信号拉低,表示发送命令
SPI_SendData8(SPI1, cmd); // 发送命令
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);
GPIO_SetBits(OLED_CS_GPIO, OLED_CS_PIN); // 片选信号拉高,结束传输数据命令
}
void OLED_WriteData(uint8_t data) {
GPIO_ResetBits(OLED_CS_GPIO, OLED_CS_PIN); // 片选信号拉低,开始传输数据
GPIO_SetBits(OLED_DC_GPIO, OLED_DC_PIN); // DC信号拉高,表示发送数据
SPI_SendData8(SPI1, data); // 发送数据
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);
GPIO_SetBits(OLED_CS_GPIO, OLED_CS_PIN); // 片选信号拉高,结束传输数据
}
void OLED_Init(void) {
GPIO_Init();
SPI_Init();
GPIO_SetBits(OLED_RST_GPIO, OLED_RST_PIN); // OLED复位
Delay_ms(100);
GPIO_ResetBits(OLED_RST_GPIO, OLED_RST_PIN);
Delay_ms(100);
GPIO_SetBits(OLED_RST_GPIO, OLED_RST_PIN);
OLED_WriteCmd(0xAE); // 关闭OLED显示屏
OLED_WriteCmd(0x20); // 设置内存地址模式:水平地址模式
OLED_WriteCmd(0x10); // 设置高四位地址
OLED_WriteCmd(0xb0); // 设置起始行为0
OLED_WriteCmd(0xc8); // 设置扫描方向:从上到下
OLED_WriteCmd(0x00); // 设置低八位地址
OLED_WriteCmd(0x10); // 设置高四位地址
OLED_WriteCmd(0x40); // 设置起始列为0
OLED_WriteCmd(0x81); // 设置对比度控制
OLED_WriteCmd(0xff); // 对比度值(0-255)
OLED_WriteCmd(0xa1); // 设置段重映射
OLED_WriteCmd(0xa6); // 设置正常/反相显示模式:正常显示
OLED_WriteCmd(0xa8); // 设置多路复用比率
OLED_WriteCmd(0x3F); // 值越大,亮度越大
OLED_WriteCmd(0xa4); // 设置全局显示:开启显示
OLED_WriteCmd(0xd3); // 设置显示偏移
OLED_WriteCmd(0x00); // 不偏移
OLED_WriteCmd(0xd5); // 设置频率分区
OLED_WriteCmd(0xf0); // 频率分区
OLED_WriteCmd(0xd9); // 设置预充电周期
OLED_WriteCmd(0x22); // 值越大,亮度越弱
OLED_WriteCmd(0xda); // 设置COM硬件扫描方式
OLED_WriteCmd(0x12); // 按列扫描
OLED_WriteCmd(0xdb); // 设置VCOMH电压倍率
OLED_WriteCmd(0x20); // 1.00*VCC
OLED_WriteCmd(0x8d); // 设置充电泵开关
OLED_WriteCmd(0x14); // 开启充电泵
OLED_WriteCmd(0xaf); // 开启OLED显示屏
}
void OLED_Puts(uint8_t x, uint8_t y, char *str) {
uint8_t i = 0;
while(str[i] != '