目录
一、前言
二、项目简介
1.功能详解
2.主要器件
三、原理图设计
四、PCB硬件设计
PCB图
五、程序设计
六、实验效果
七、包含内容
项目分享
一、前言
项目成品图片:
哔哩哔哩视频链接:
https://www.bilibili.com/video/BV13PijBHEAv/?spm_id_from=333.337.search-card.all.click&vd_source=a90461cae133c0c360a3780433efae62
(资料分享见文末)
二、项目简介
1.功能详解
基于STM32的图书馆环境监测系统
功能如下:
- 环境采集:采集环境温湿度、光照强度、烟雾浓度、噪声大小、刷卡信息
- 显示功能:屏幕显示温湿度、光照强度、烟雾浓度、噪声大小、到场人数
- 模式切换:通过按键可以切换手动模式和自动模式
- 自动模式:温度超过温度上限时开启风扇降温;温度小于温度下限时开启加热片加热;光照小于光照阈值时开启照明灯,烟雾浓度超过阈值时蜂鸣器报警提醒;噪声音量超过阈值时语音播报“请保持安静”;每次有NFC卡片刷入的时候舵机往返旋转一次(代表开关门),且到场人数加1
- 手动模式:通过按键控制LED照明灯、风扇、加热片和舵机(开关门),以及到场人数手动清零
- 阈值调节:可通过按键设置温度上限、温度下限、光照阈值、烟雾阈值、噪声阈值
- 蓝牙APP:通过蓝牙连接手机APP可以查看信息数据和控制指令下发,包括模式切换、手动外设控制
2.主要器件
- STM32F103C8T6最小系统板
- OLED显示屏(4针IIC协议)
- DHT11温湿度传感器
- MQ-2烟雾传感器
- 光敏传感器
- RFID门禁射频模块
- 声音传感器
- JR6001语音模块
- BT04A(蓝牙模块)
- PTC加热片
- 大功率LED灯模块
- 有源蜂鸣器
- 继电器
- 舵机
- 风扇
三、原理图设计
四、PCB硬件设计
PCB图
五、程序设计
#include "stm32f10x.h" #include "led.h" #include "beep.h" #include "usart.h" #include "delay.h" #include "oled.h" #include "key.h" #include "Modules.h" #include "adcx.h" #include "flash.h" #include "usart2.h" #include "usart3.h" #include "TIM2.h" #include "dht11.h" #include "MFRC522.h" #include "timer.h" #include "relay.h" #include "JSQ.h" #include "servo.h" /****************异方辰电子工作室****************** STM32 *文件 : STM32智能图书馆检测系统 *版本 : V1.0 *日期 : 2025.12.24 *MCU : STM32F103C8T6 *接口 : 见代码 *BILIBILI : 异方辰电子 *小红书 : 异方辰电子 *CSDN : 异方辰电子 *授权IP : 辰哥单片机设计、异方辰、YFC电子、北海单片机设计 **********************BEGIN***********************/ #define KEY_Long1 11 #define KEY_1 1 #define KEY_2 2 #define KEY_3 3 #define KEY_4 4 #define FLASH_START_ADDR 0x0801f000 //写入的起始地址 SensorModules sensorData; //声明传感器数据结构体变量 SensorThresholdValue Sensorthreshold; //声明传感器阈值结构体变量 DriveModules driveData; //声明驱动器状态结构体变量 uint8_t mode = 1; //系统模式 1自动 2手动 3设置 u8 dakai;//串口3使用的传递变量 u8 Flag_dakai;//串口3接收标志位 uint8_t is_secondary_menu = 0; // 0一级菜单,1二级菜单 uint8_t secondary_pos = 1; // 二级菜单光标位置(1-3对应时/分/秒) uint8_t secondary_type = 0; // 二级菜单类型:0=RTC时间,1=定时开启,2=定时关闭 uint8_t send_data[] = "A7:00001";//语音播放曲目1 uint8_t send_data1[] = "A7:00002";//语音播放曲目2 //系统静态变量 //static uint8_t count_a = 1; //自动模式按键数 static uint8_t count_m = 1; //手动模式按键数 static uint8_t count_s = 1; //设置模式按键数 //static uint8_t last_mode = 0; // 记录上一次的模式 //static uint8_t last_count_s = 0; // 记录设置模式内上一次的页面 // 防重复触发标志位 uint8_t is_sound_warned = 0; // 噪音报警标志(0=未播报,1=已播报) uint8_t is_nfc_detected = 0; // NFC刷卡标志(0=未刷卡,1=已刷卡) uint8_t last_nfc_state = 0; // 上一次NFC状态(0=无卡,1=有卡)→ 新增 uint8_t last_card_state = 0; // 上一次NFC卡片状态(0=无卡,1=有卡)→ 新增 uint8_t servo_card; void OLED_manualPage2(void); void ManualSettingsDisplay2(void); void OLED_manualOption(uint8_t num); void OLED_manualPage1(void); void ManualSettingsDisplay1(void); int main(void) { SystemInit();//配置系统时钟为72M delay_init(72); ADCX_Init(); LED_Init(); BEEP_Init(); BEEP_OFF; USART1_Config(); USART2_Init(); USART3_Config(); Key_Init(); DHT11_Init(); OLED_Init(); RELAY_Init(); JSQ_Init(); Servo_Init(); MFRC522_Init(); OLED_Clear(); //flash读取 delay_ms(100); FLASH_ReadThreshold(); // 添加的状态管理变量 static uint8_t last_mode = 0; // 记录上一次模式 static uint32_t last_sensor_time = 0; // 传感器扫描时间控制 static uint32_t last_display_time = 0;// 显示刷新时间控制 //超过以下值,恢复默认阈值 if ( Sensorthreshold.tempmaxValue > 40 || Sensorthreshold.tempminValue >30 || Sensorthreshold.luxValue > 500 || Sensorthreshold.SmogeValue > 900 || Sensorthreshold.soundValue > 90) { FLASH_W(FLASH_START_ADDR, 30, 20, 150, 100,70); FLASH_ReadThreshold(); } TIM2_Init(72-1,1000-1); // 2ms定时中断 printf("Start \n"); USART3_SendString("AF:30"); //音量调到最大 delay_ms(300); USART3_SendString("A7:00001"); //欢迎使用 while (1) { // ==================== 优先处理按键和通信 ==================== USART2_ProcessCmd(); // 先处理串口命令 // ==================== 获取当前系统时间 ==================== uint32_t current_time = delay_get_tick(); // 使用系统滴答计数 // ==================== 优化传感器扫描频率 ==================== if(current_time - last_sensor_time > 100) // 每200ms扫描一次传感器 (100 * 2ms = 200ms) { SensorScan(); //获取传感器数据 last_sensor_time = current_time; } // ==================== 立即处理按键 ==================== uint8_t current_key_num = KeyNum; // 保存当前按键值 // 模式切换按键立即处理 if(current_key_num != 0) { switch(mode) { case AUTO_MODE: if(current_key_num == KEY_1) { mode = MANUAL_MODE; count_m = 1; // 切换到手动模式时关闭灯和蜂鸣器 driveData.LED_Flag = 0; driveData.BEEP_Flag = 0; driveData.heat_Flag = 0; driveData.Fan_Flag = 0; driveData.servo_Flag = 0; KeyNum = 0; // 立即清除按键 } else if(current_key_num == KEY_Long1) { mode = SETTINGS_MODE; count_s = 1; KeyNum = 0; // 立即清除按键 } break; case MANUAL_MODE: if(current_key_num == KEY_1) { mode = AUTO_MODE; KeyNum = 0; // 立即清除按键 } break; case SETTINGS_MODE: // 设置模式内部按键在各自模式中处理 break; } } // ==================== 模式切换优化 ==================== if(last_mode != mode) { OLED_Clear(); last_mode = mode; // 立即绘制新模式的固定内容 switch(mode) { case AUTO_MODE: OLED_autoPage1(); break; case MANUAL_MODE: OLED_manualPage1(); break; case SETTINGS_MODE: OLED_settingsPage1(); break; } OLED_Refresh(); // 立即刷新显示 } // ==================== 模式处理 ==================== switch(mode) { case AUTO_MODE: // 直接调用显示, SensorDataDisplay1(); //显示传感器1数据 AutoControl(); Control_Manager(); break; case MANUAL_MODE: { static uint8_t manual_page_initialized = 0; static uint8_t last_manual_count = 0; static uint8_t last_LED_Flag = 0; static uint8_t last_BEEP_Flag = 0; static uint8_t force_refresh = 0; // 强制刷新标志 // 模式切换时重新初始化 if(last_mode != mode) { manual_page_initialized = 0; last_manual_count = 0; last_LED_Flag = driveData.LED_Flag; last_BEEP_Flag = driveData.BEEP_Flag; force_refresh = 1; // 设置强制刷新标志 // 确保光标指向灯光 count_m = 1; // 确保设备状态为关 driveData.LED_Flag = 0; driveData.BEEP_Flag = 0; } uint8_t current_manual_count = SetManual(); // 检查设备状态是否改变,如果改变则强制刷新显示 uint8_t need_refresh = 0; if(driveData.LED_Flag != last_LED_Flag || driveData.BEEP_Flag != last_BEEP_Flag) { need_refresh = 1; last_LED_Flag = driveData.LED_Flag; last_BEEP_Flag = driveData.BEEP_Flag; } // 确保页面已初始化或光标位置改变或设备状态改变或强制刷新时重新绘制 if(!manual_page_initialized || current_manual_count != last_manual_count || need_refresh || force_refresh) { // 根据count_m判断显示第一页还是第二页 if(current_manual_count >=1 && current_manual_count <=4) { OLED_manualPage1(); // 手动模式第一页 OLED_manualOption(current_manual_count); // 光标 ManualSettingsDisplay1(); // 第一页状态 } else if(current_manual_count ==5) { OLED_manualPage2(); // 手动模式第二页(人数统计) OLED_manualOption(current_manual_count); // 光标 ManualSettingsDisplay2(); // 第二页数据 } manual_page_initialized = 1; last_manual_count = current_manual_count; force_refresh = 0; // 清除强制刷新标志 OLED_Refresh(); // 强制刷新显示 } // 立即处理手动模式按键 if(current_key_num != 0) { ManualControl(current_manual_count); // 按键后根据count_m重新绘制对应页面 if(current_manual_count >=1 && current_manual_count <=4) { OLED_manualPage1(); OLED_manualOption(current_manual_count); ManualSettingsDisplay1(); } else if(current_manual_count ==5) { OLED_manualPage2(); // 手动模式第二页 OLED_manualOption(current_manual_count); ManualSettingsDisplay2(); } OLED_Refresh(); // 按键操作后立即刷新 KeyNum = 0; // 立即清除按键标志 } // 确保显示内容始终正确 if(current_manual_count >=1 && current_manual_count <=4) { OLED_manualPage1(); // 固定文字 OLED_manualOption(current_manual_count); // 光标 ManualSettingsDisplay1(); // 状态 } else if(current_manual_count ==5) { OLED_manualPage2(); // 手动模式第二页 OLED_manualOption(current_manual_count); // 光标 ManualSettingsDisplay2(); // 人数统计数据 } Control_Manager(); break; } case SETTINGS_MODE: { static uint8_t is_threshold_page_inited = 0; static uint8_t last_settings_count = 0; static uint8_t force_refresh = 0; uint8_t curr_count_s = SetSelection(); if(last_mode != mode) { is_threshold_page_inited = 0; last_settings_count = 0; force_refresh = 1; count_s = 1; } if(current_key_num != 0) { if (is_secondary_menu == 1) { if (current_key_num == KEY_2 || current_key_num == KEY_3 || current_key_num == KEY_4) { OLED_Refresh(); KeyNum = 0; } else if (current_key_num == KEY_1) { is_secondary_menu = 0; secondary_pos = 1; OLED_Clear(); if(curr_count_s >=1 && curr_count_s <=4) { OLED_settingsPage1(); } else if(curr_count_s ==5) { OLED_settingsPage2(); } SettingsThresholdDisplay1(); OLED_settingsOption(curr_count_s); OLED_Refresh(); KeyNum = 0; } } else { if (current_key_num == KEY_3 || current_key_num == KEY_4) { ThresholdSettings(curr_count_s); if(curr_count_s >=1 && curr_count_s <=4) { SettingsThresholdDisplay1(); } else if(curr_count_s ==5) { SettingsThresholdDisplay2(); } OLED_Refresh(); KeyNum = 0; } else if (current_key_num == KEY_1) { mode = AUTO_MODE; is_threshold_page_inited = 0; FLASH_W(FLASH_START_ADDR, Sensorthreshold.tempmaxValue, Sensorthreshold.tempminValue, Sensorthreshold.luxValue, Sensorthreshold.SmogeValue, Sensorthreshold.soundValue); KeyNum = 0; } } } if(!is_threshold_page_inited || curr_count_s != last_settings_count || force_refresh) { if (is_secondary_menu == 0) { if(curr_count_s >=1 && curr_count_s <=4) { OLED_settingsPage1(); SettingsThresholdDisplay1(); } else if(curr_count_s ==5) { OLED_settingsPage2(); SettingsThresholdDisplay2(); } OLED_settingsOption(curr_count_s); } is_threshold_page_inited = 1; last_settings_count = curr_count_s; force_refresh = 0; OLED_Refresh(); } if (is_secondary_menu == 0) { if(curr_count_s >=1 && curr_count_s <=4) { OLED_settingsPage1(); SettingsThresholdDisplay1(); } else if(curr_count_s ==5) { OLED_settingsPage2(); SettingsThresholdDisplay2(); } OLED_settingsOption(curr_count_s); } break; } } // ==================== 限制显示刷新频率 ==================== if(current_time - last_display_time > 25) // 每50ms刷新一次显示 (25 * 2ms = 50ms) { // 所有模式都需要刷新显示 OLED_Refresh(); last_display_time = current_time; } } }