基于stm32单片机老人居家监护报警系统毕业设计
1、采用stm32单片机+LCD1602显示屏+独立按键+MQ4传感器(电位器模拟)+MQ2传感器(电位器模拟)+蜂鸣器+电机,制作一个基于stm32单片机老人居家监护报警系统Proteus仿真;
2、通过MQ2传感器(电位器调节)检测烟雾浓度,并且显示到LCD1602显示屏上面;
3、通过MQ4传感器(电位器调节)检测天然气浓度,并且显示到LCD1602显示屏上面;
4、通过按键设置MQ2和MQ4浓度的阈值,显示到LCD1602屏幕上面;
5、当MQ2检测浓度或者MQ4检测浓度大于设置的阈值,蜂鸣器进行报警提醒,自动打开通风排气电机;
基于您提供的毕业设计要求,以下是一个简化的基于STM32单片机的老人居家监护报警系统的设计和实现方案。由于Proteus(可能是Proteus Design Suite的误写,通常是Proteus或Proteus 8 Professional)主要用于电路图绘制和仿真,我们将主要关注于系统设计和仿真设置。
1. 系统设计
1.1 硬件设计
- STM32单片机:作为控制核心,负责读取传感器数据、处理按键输入、控制显示屏和电机等外设。
- LCD1602显示屏:用于显示烟雾和天然气浓度以及阈值设置。
- 独立按键:用于设置MQ2和MQ4的浓度阈值。
- MQ2传感器(电位器模拟):模拟烟雾浓度检测,通过电位器调节模拟值。
- MQ4传感器(电位器模拟):模拟天然气浓度检测,同样通过电位器调节模拟值。
- 蜂鸣器:当检测到浓度超过阈值时,发出报警声。
- 电机:当检测到浓度超过阈值时,自动打开通风排气。
1.2 软件设计
- 初始化:配置STM32的GPIO、USART(如果需要)、定时器、中断等。
- 传感器读取:通过ADC(模拟数字转换器)读取MQ2和MQ4传感器(电位器模拟)的模拟值,并转换为浓度值。
- LCD显示:将检测到的烟雾和天然气浓度值,以及设置的阈值显示在LCD1602上。
- 按键处理:读取按键输入,并根据按键设置MQ2和MQ4的浓度阈值。
- 报警和电机控制:当检测到的浓度超过阈值时,启动蜂鸣器报警,并控制电机打开通风排气。
2. Proteus仿真设置
2.1 电路图绘制
- 在Proteus中绘制电路图,包括STM32单片机、LCD1602、按键、电位器(模拟MQ2和MQ4传感器)、蜂鸣器和电机等元件。
- 连接STM32的GPIO口到LCD1602、按键、电位器和电机的控制引脚。
2.2 仿真设置
- 配置STM32的GPIO口为适当的模式(输入/输出、上拉/下拉等)。
- 配置ADC以读取电位器的模拟值。
- 配置LCD1602的接口(通常为并行接口)。
- 设置按键的输入模式。
- 配置蜂鸣器和电机的控制引脚为输出模式。
3. 代码编写
使用STM32的HAL库或标准外设库编写代码,实现以下功能:
- 初始化系统。
- 读取MQ2和MQ4传感器的模拟值,并转换为浓度值。
- 显示浓度值和阈值到LCD1602。
- 处理按键输入,设置阈值。
- 检测浓度是否超过阈值,并执行报警和电机控制。
4. 注意事项
- 由于MQ2和MQ4传感器在Proteus中无法直接模拟,我们使用电位器来模拟传感器的输出。
- 在实际系统中,您可能需要添加额外的电路来驱动电机和蜂鸣器,以及处理传感器的真实输出。
- 考虑到老人使用的便捷性,界面和按键设计应尽可能简单直观。
- 报警系统应确保在紧急情况下能够可靠工作,因此应进行充分的测试和验证。
为了提供一个基于STM32单片机的老人居家监护报警系统的简化代码示例,我们需要考虑系统的几个关键部分:传感器读取(虽然在这里用电位器模拟)、LCD显示、按键输入处理、报警逻辑以及电机控制。以下是一个简化的伪代码/代码框架,用于指导您如何开始编写实际的代码。
请注意,这里假设您已经为STM32配置了必要的HAL库,并且了解如何使用STM32CubeMX或STM32CubeIDE进行初始化设置。
伪代码/代码框架
void System_Init(void) {
// 初始化HAL库
HAL_Init();
// 配置系统时钟
SystemClock_Config();
// 初始化GPIO、ADC(用于读取电位器值)、LCD1602、按键等
MX_GPIO_Init();
MX_ADC_Init(); // 初始化ADC以读取电位器值
LCD1602_Init();
Keypad_Init();
// 初始化蜂鸣器和电机控制GPIO
Buzzer_Init();
Motor_Init();
// 其他初始化...
}
// 系统主函数
int main(void) {
System_Init();
// 主循环
while (1) {
// 读取传感器值(这里用电位器模拟)
float smokeLevel = Read_Potentiometer_Value_As_SmokeLevel(ADC1_CHANNEL_SMOKE); // 假设ADC1_CHANNEL_SMOKE是模拟烟雾的电位器通道
float gasLevel = Read_Potentiometer_Value_As_GasLevel(ADC1_CHANNEL_GAS); // 假设ADC1_CHANNEL_GAS是模拟天然气的电位器通道
// 显示浓度值
Display_Concentration(LCD1602, smokeLevel, gasLevel);
// 处理按键输入
if (Key_Pressed(KEY_SET_SMOKE_THRESHOLD)) {
// 设置烟雾浓度阈值...
}
if (Key_Pressed(KEY_SET_GAS_THRESHOLD)) {
// 设置天然气浓度阈值...
}
// 检查是否超过阈值并触发报警
if (smokeLevel > smokeThreshold || gasLevel > gasThreshold) {
Trigger_Alarm(Buzzer);
Start_Motor();
} else {
Stop_Alarm(Buzzer);
if (Motor_IsRunning()) {
Stop_Motor(); // 延时一段时间后停止电机,以防误报
}
}
// 延时或其他任务...
HAL_Delay(100); // 假设的延时,用于降低主循环的频率
}
}
- 读取电位器值并转换为浓度
float Read_Potentiometer_Value_As_SmokeLevel(uint32_t adcChannel) { // 读取ADC值并转换为烟雾浓度(这里需要您定义转换逻辑) // 假设ADC值范围为0-1023,对应浓度0-1000 ppm uint32_t adcValue = HAL_ADC_GetValue(&hadc); // hadc是ADC句柄,需要您在初始化中配置 float smokeLevel = (float)adcValue * 1000.0f / 1023.0f; return smokeLevel; } // 同理,对于天然气浓度 float Read_Potentiometer_Value_As_GasLevel(uint32_t adcChannel) { // ... }
- LCD显示、按键处理、报警和电机控制
- 这只是一个简化的框架,用于指导您开始编写代码。在实际应用中,您可能需要添加错误处理、更复杂的用户界面、网络通信(以便远程监控)等功能。
- 请确保在编写代码时参考您所使用的STM32单片机的具体数据手册和参考手册,以及LCD、按键、ADC、蜂鸣器和电机等外设的文档。
- 如果您不熟悉STM32的HAL库或标准外设库,建议您先学习这些库的基本用法和API。
-
这些函数的具体实现将取决于您使用的LCD模块、按键模块、蜂鸣器和电机的具体型号和接口。通常,您需要编写初始化函数来配置GPIO,然后编写读写函数来与这些外设进行通信。
-
为了更具体地展示基于STM32单片机的老人居家监护报警系统的模块化代码,我们可以将每个功能拆分成单独的函数或模块。下面是一个更详细的代码示例,其中包含了一些简化的函数定义,以指导您如何编写和组织代码。
1. 初始化函数
#include "stm32f1xx_hal.h" // 根据您的STM32型号选择正确的头文件 #include "lcd1602.h" // 假设您有一个LCD1602的驱动头文件 #include "keypad.h" // 假设您有一个按键处理的头文件 #include "buzzer.h" // 假设您有一个蜂鸣器控制的头文件 #include "motor.h" // 假设您有一个电机控制的头文件 // 初始化函数 void System_Init(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_ADC_Init(); LCD1602_Init(); Keypad_Init(); Buzzer_Init(); Motor_Init(); // 初始化阈值等变量 float smokeThreshold = 500.0f; // 假设的烟雾浓度阈值 float gasThreshold = 100.0f; // 假设的天然气浓度阈值 // ... 其他初始化代码 ... } int main(void) { System_Init(); while (1) { // 主循环代码 } }
2. ADC读取和转换函数
#include "stm32f1xx_hal.h" // 假设hadc是ADC句柄,已经在MX_ADC_Init()中初始化 extern ADC_HandleTypeDef hadc; // 读取ADC值并转换为烟雾浓度 float Read_ADC_As_SmokeLevel(uint32_t adcChannel) { HAL_ADC_Start(&hadc); // 开始ADC转换 HAL_ADC_PollForConversion(&hadc, 10); // 等待转换完成(可选的,取决于配置) uint32_t adcValue = HAL_ADC_GetValue(&hadc); // 读取ADC值 // 转换逻辑(这里简化处理) float smokeLevel = (float)adcValue * 1000.0f / 1023.0f; // 假设ADC满量程为1023,对应浓度0-1000 ppm return smokeLevel; } // 同理,对于天然气浓度 float Read_ADC_As_GasLevel(uint32_t adcChannel) { // ... }
3. LCD显示函数
在
lcd1602.c
和lcd1602.h
文件中实现。// lcd1602.h void LCD1602_WriteString(const char* str); void LCD1602_WriteFloat(float value, uint8_t decimalPlaces); // 假设的函数,用于显示浮点数 // ... 其他LCD函数声明 ... // lcd1602.c // ... 函数的具体实现 ...
4. 按键处理函数
在
keypad.c
和keypad.h
文件中实现。// keypad.h typedef enum { KEY_NONE, KEY_SET_SMOKE_THRESHOLD, KEY_SET_GAS_THRESHOLD, // ... 其他按键 ... } Keypad_Key; Keypad_Key Keypad_GetKey(void); // ... 其他按键函数声明 ... // keypad.c // ... 函数的具体实现 ...
5. 报警和电机控制函数
在
buzzer.c
、buzzer.h
、motor.c
和motor.h
文件中实现。// buzzer.h void Buzzer_Start(void); void Buzzer_Stop(void); // ... 其他蜂鸣器函数声明 ... // buzzer.c // ... 函数的具体实现 ... // motor.h void Motor_Start(void); void Motor_Stop(void); // ... 其他电机函数声明 ... // motor.c // ... 函数的具体实现 ...
6. 主循环中的逻辑
#include "stm32f1xx_hal.h" #include "lcd1602.h" #include "keypad.h" #include "buzzer.h" #include "motor.h" // 阈值变量(全局或静态,根据需要在初始化时设置) static float smokeThreshold = 500.0f; // 烟雾浓度阈值 static float gasThreshold = 100.0f; // 天然气浓度阈值 int main(void) { System_Init(); while (1) { // 读取传感器值 float smokeLevel = Read_ADC_As_SmokeLevel(ADC1_CHANNEL_SMOKE); float gasLevel = Read_ADC_As_GasLevel(ADC1_CHANNEL_GAS); // 显示浓度值 char smokeBuffer[16]; // 假设LCD显示字符串的最大长度 char gasBuffer[16]; sprintf(smokeBuffer, "Smoke: %.1f ppm", smokeLevel); sprintf(gasBuffer, "Gas: %.1f ppm", gasLevel); LCD1602_Clear(); // 清除LCD显示 LCD1602_WriteString(smokeBuffer); LCD1602_SetCursor(0, 1); // 设置LCD光标到第二行 LCD1602_WriteString(gasBuffer); // 处理按键输入 Keypad_Key key = Keypad_GetKey(); switch (key) { case KEY_SET_SMOKE_THRESHOLD: // 实现设置烟雾浓度阈值的逻辑(可能需要一个循环来接收输入) // ... break; case KEY_SET_GAS_THRESHOLD: // 实现设置天然气浓度阈值的逻辑 // ... break; // ... 处理其他按键 ... default: break; } // 检查是否超过阈值并触发报警 if (smokeLevel > smokeThreshold || gasLevel > gasThreshold) { Buzzer_Start(); // 触发报警 Motor_Start(); // 启动电机(可能是门窗打开或其他动作) } else { Buzzer_Stop(); // 停止报警 // 如果电机在运行,可以延时一段时间后停止 if (Motor_IsRunning()) { HAL_Delay(5000); // 假设5秒后停止电机 Motor_Stop(); } } // 延时或其他任务... HAL_Delay(100); // 100ms的延时 } }
程序部分展示,有中文注释,新手容易看懂 void IO_out( void ) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC ,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //按键 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0| GPIO_Pin_1| GPIO_Pin_2| GPIO_Pin_3; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;; GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); } void anjiansaomiao(void) { static char k1,k2,k3,k4; static char K1Flag=0,K2Flag=0,K3Flag=0,K4Flag=0; //按键扫描,获取按键状态 k1 = GPIO_ReadInputDataBit(GPIOC , GPIO_Pin_0); k2 = GPIO_ReadInputDataBit(GPIOC , GPIO_Pin_1); k3 = GPIO_ReadInputDataBit(GPIOC , GPIO_Pin_2); k4 = GPIO_ReadInputDataBit(GPIOC , GPIO_Pin_3); //表明按下 if(k1 == 0) { K1Flag = 1; } else { //释放后进行响应 if(K1Flag ) { K1Flag = 0; MQ2++; if(MQ2>100) MQ2=100; } } //表明按下 if(k2 == 0) { K2Flag = 1; } else { //释放后进行响应 if(K2Flag ) { K2Flag = 0; MQ2--; if(MQ2<1) MQ2=1; } } //表明按下 if(k3 == 0) { K3Flag = 1; } else { //释放后进行响应 if(K3Flag ) { K3Flag = 0; MQ4++; if(MQ4>100) MQ4=100; } } //表明按下 if(k4 == 0) { K4Flag = 1; } else { //释放后进行响应 if(K4Flag ) { K4Flag = 0; MQ4--; if(MQ4<1) MQ4=1; } } } int main(void) { int ADC_num1,ADC_num2; int temp1,temp2; GPIO_Configuration();//初始化 ADC1_GPIO_Config(); ADC_Config(); Init1602(); IO_out(); WrByte1602(0,1,'M'); //字符显示 WrByte1602(0,2,'Q'); WrByte1602(0,3,'2'); WrByte1602(0,4,'='); WrByte1602(1,1,'M'); WrByte1602(1,2,'Q'); WrByte1602(1,3,'4'); WrByte1602(1,4,'='); WrByte1602(0,9,'S'); WrByte1602(0,10,'E'); WrByte1602(0,11,'T'); WrByte1602(0,12,'='); WrByte1602(1,9,'S'); WrByte1602(1,10,'E'); WrByte1602(1,11,'T'); WrByte1602(1,12,'='); delay_ms(500); while(1) { ADC_num1=Get_ADC(ADC_Channel_0);//读取MQ2浓度 temp1=ADC_num1*100/4096; delay_ms(100); ADC_num2=Get_ADC(ADC_Channel_1);//读取MQ4浓度 temp2=ADC_num2*100/4096; WrByte1602(0,5,AsciiCode[temp1%1000/100]);//显示MQ2浓度 WrByte1602(0,6,AsciiCode[temp1%100/10]); WrByte1602(0,7,AsciiCode[temp1%10]); WrByte1602(1,5,AsciiCode[temp2%1000/100]); //显示MQ4浓度 WrByte1602(1,6,AsciiCode[temp2%100/10]); WrByte1602(1,7,AsciiCode[temp2%10]);