news 2026/9/7 20:31:36

STM32毕设项目分享:基于STM32的窗控系统(蓝牙)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32毕设项目分享:基于STM32的窗控系统(蓝牙)
一、成品简介
1.实物成品

成品展示:

总体展示

窗户模型

APP软件界面

二、项目 简介
1.功能详解
基于STM32的智能窗控系统(蓝牙)

信号采集:DS18B20数字温度传感器、光敏电阻光照传感器、雨滴检测传感器模块,分别采集温度、光照强度、降雨状态。DS18B20用单总线去通信、光敏传感器和雨滴传感器通过单片机的ADC通道采集。

数据处理:低通滤波,设置动态的数据缓冲区,对温度、光照数据滤波。通过90%上次输出和+10%当前采样数据的计算,抑制环境干扰、采样波动带来的数据误差。

窗户的逻辑控制:降雨检测最高优先级,其次是光照、温度检测。无降雨、光照强度高于阈值时,自动控制窗户开启,实现采光通风;检测到降雨、光照强度不足或环境温度低于阈值时,自动关闭窗户,实现防雨保温、避光防护。

模式切换:按键去参数配置与模式切换,支持手动/自动工作模式,自定义设置光照阈值、温度上下限阈值,手动模式优先级高于自动模式。

可视化显示:0.96寸OLED单色显示屏,界面分为参数设置界面与状态显示界面,可展示环境温度、光照强度、降雨状态、窗户运行状态、系统工作模式及各项控制阈值。

蓝牙远程控制:蓝牙模块,手机移远程操控。可远程切换系统手动/自动、控制窗户开闭、远程修改光照、温度控制阈值等。

电机驱动:TB6612电机驱动模块和TT双轴减速电机,通过PWM信号控制电机正反转、速度实现开窗关窗动作。

硬件框图:

流程图:

2.主要器件
3.原理图设计

4.PCB设计

三、主要模块
1.电机驱动

电机驱动模块采用TB6612FBG驱动模块和TT双轴减速电机组成。TB6612FNG是东芝半导体推出的一款高性能双H桥直流电机驱动IC,可同时独立驱动两台直流电机,也可驱动一台步进电机实现正反转、调速等功能。相较于传统的L298N驱动模块,体积更小、效率更高,工作时发热低,无需额外加装散热片,

TT双轴减速电机是种微型直流有刷电机与减速齿轮箱一体化的动力模组,转速适中、扭矩大、驱动简单且价格极低。

2.蓝牙模块

蓝牙模块采用JDY-31基于蓝牙3.0标准的纯从机的无线透传模块,主要用作低成本、低功耗短距离串口数据传输。价格便宜、省电、接线简单,不需要复杂的操作,适合各种微控制器,有较好的性价比。

四、写作内容参考

1.摘要参考内容

传统的窗户虽然在一定程度上起到了通风换气、自然采光、安全防护的作用,但是操作起来比较繁琐,只能通过人工推拉或者手摇机械结构来控制,没有智能感知和自动调节的功能。此时操作不及时、不能对环境的变化做出反应的问题就会出现,不但会降低居住体验,还会造成财产损失和能源浪费。

本系统依托STM32F103C8T6微处理器作为核心控制器,融合了多类环境传感器与执行机构,开发出一种面向智能化窗户调节领域的创新性系统。该系统能够实时监测室内外环境参数,包括降雨情况温度、光照强度、,并根据预设条件自动控制窗户的开闭,实现防雨、散热、通风等功能。

本系统架构由软件与硬件两大核心模块组成。在硬件设计方面,采用STM32F103C8T6微控制器作为主控单元,并集成DS18B20高精度温度传感器、光敏电阻(用于环境光照强度检测)、雨滴感知模块、OLED显示屏幕以及直流电机驱动电路等关键组件。以STM32F103C8T6单片机为控制中心处理传感器采集的环境数据,液晶屏显示当前环境参数和窗户的状态,直流电机做执行机构来控制窗户的开关。软件部分主要是主程序设计以及各个子程序模块的设计,用编程的方式实现窗户的智能控制和状态显示的功能。系统仿真研究与实物测试结果表明,本智能窗户控制电路在结构设计、测量精确度及运行稳定性方面展现出显著的技术优势。用户可以利用按键来设定工作模式以及控制阈值,系统会根据环境的变化自动调节窗户的状态,还可以通过蓝牙进行远程控制。本研究所设计的智能窗户控制系统可以提高家居生活舒适度、安全性、便捷性,对智能家居有广阔的应用前景。

2.目录参考内容

五、项目主要源码讲解
5.1.电机驱动

TB6612FNG接收指令后,根据指令类型控制TT减速电机正转或者反转。正转带动窗户打开,反转带动窗户关闭。到位后电机停转,流程结束,返回主循环。

使用单片机的定时器打开一个通道的PWM输出,然后再使用两个GPIO口就可以控制电机运动。

5.2蓝牙模块

蓝牙JDY-31模块上电初始化以后会进入到等待连接的状态,此时STATE引脚输出低电平,LED进行闪烁提示没有连接。手机连接成功之后,STATE引脚变为高电平,LED一直常亮。模块收到串口指令之后进行解析,开窗指令使电机正转,关窗指令使电机反转,阈值设置指令更新对应参数并保存。执行完之后回到主循环。

使用单片机的UART外设,初始化出一个串口,一般串口的波特率设为9600,即可使用串口完成蓝牙传输数据。

六、项目主要源码
5.1.单片机源码
5.1.1主程序
#include "bsp.h" /* TIM1: 1MS TIM2:Delay ADC:PA0-CH0-光敏 PA1-CH1 Temperature UART1:TX:PA9 RX:PA10 UART2:TX PA2 RX:PA3 蓝牙 RAIN:PA4 KEY:PA5 PA6 PA7 PB0---KEY1-KEY4 MOTOR:B13-A2 B14-A1 PA8-PWM IIC:SDA-PB7 SCL-PB6 光敏电阻: 获取光照 STM32内部温度传感器:获取温度 蓝牙模块: 修改阈值 雨滴检测器模块: 下雨检测 直流电机+tb6612: 通过电机正反转控制模型打开关闭窗户 0.96显示模块: 显示温度 按键1: 切换手动自动模式,默认自动模式 按键2: 修改光照低阈值,光照大于阈值开窗 按键3:如果是手动模式 开窗户 按键4:如果是手动模式关窗户 蓝牙0XAA:如果是手动模式 开窗户 蓝牙0XBB:如果是手动模式 关窗户 蓝牙0XCC:修改光照低阈值,光照大于阈值开窗 蓝牙0XDD:修改光照低阈值,光照大于阈值开窗 蓝牙0XFF:切换手动自动模式 开窗->光高于阈值且无雨(采光通风) 关窗->天气亮或有雨(防淋雨 ,没光天黑关窗户) */ /* UART */ uint8_t rx1; #define TX_SIZE 128 uint8_t txbuffer[TX_SIZE]; /* OLED */ char oled_buffer[32]={0}; /* ADC */ uint16_t ch0_value ; uint16_t ch1_value ; uint16_t temp_value; uint16_t Light; uint16_t Temp; /* Light low */ uint8_t light_low=25;uint8_t light_high=58; uint8_t temp_low=15;uint8_t temp_high=40; /* WINDOWS */ uint8_t mode=1; uint8_t window_state;//手动 开关 uint8_t window_zidongstate;//自动开关 即使执行又是状态 /* RAIN */ uint8_t rain_flag; /* BLUE */ uint8_t rx2; uint8_t BLE; uint8_t BLEMODE; #define TX_BLE 128 uint8_t txbuffer[TX_BLE]; #define RX_BLE 128 uint8_t rxbuffer[RX_BLE]; void uart_send(char* fmt, ...) ; uint8_t dis_mode;//0-set 1-dis uint8_t dis_mode_set;//0-set 1-dis uint32_t low_pass_filter(uint32_t input); /* */ #define close moror_run(1,1500) #define open moror_run(2,1500) void bsp_init() { HAL_UART_Receive_IT(&huart1, &rx1, 1); HAL_UART_Receive_IT(&huart2, &rx2, 1); HAL_TIM_Base_Start_IT(&htim1); delay_init(); OLED_Init(); OLED_NewFrame(); MY_ADC_Init(); motor_stop(); close; HAL_Delay(500); OLED_Init(); OLED_NewFrame(); OLED_PrintString(0, 0,"状态" ,&font16x16, OLED_COLOR_NORMAL); OLED_PrintString(42, 0,"温度" ,&font16x16, ((dis_mode_set==0||dis_mode_set==1)?OLED_COLOR_REVERSED:OLED_COLOR_NORMAL)); // OLED_PrintString(84, 0,"光照" ,&font16x16, ((dis_mode_set==2||dis_mode_set==3)?OLED_COLOR_REVERSED:OLED_COLOR_NORMAL)); OLED_PrintString(0, 16,"上限" ,&font16x16, ((dis_mode_set==0||dis_mode_set==2)?OLED_COLOR_REVERSED:OLED_COLOR_NORMAL)); OLED_PrintString(0, 32,"下限" ,&font16x16, ((dis_mode_set==1||dis_mode_set==3)?OLED_COLOR_REVERSED:OLED_COLOR_NORMAL)); sprintf((char *)oled_buffer,"%2d",light_low); OLED_PrintString(84, 32,oled_buffer ,&font16x16, OLED_COLOR_NORMAL); sprintf((char *)oled_buffer,"%2d",light_high); OLED_PrintString(84, 16,oled_buffer ,&font16x16, OLED_COLOR_NORMAL); sprintf((char *)oled_buffer,"%2d",temp_low); OLED_PrintString(42, 32,oled_buffer ,&font16x16, OLED_COLOR_NORMAL); sprintf((char *)oled_buffer,"%2d",temp_high); OLED_PrintString(42, 16,oled_buffer ,&font16x16, OLED_COLOR_NORMAL); } ; /* */ uint16_t temper; void loop() { temper=DS18B20_Get_Temp(); uint8_t key=0;uint8_t temp; HAL_Delay(1); //ADC--Light&&Temp MY_ADC_ReadValues(); ch0_value = MY_ADC_GetValue(0); // 通道0值 ch1_value = MY_ADC_GetValue(1); // 通道1值 temp_value = MY_ADC_GetValue(2); // 温度传感器值 ch0_value=low_pass_filter(ch0_value);// Light; ch1_value=low_pass_filter(ch1_value); temp_value=low_pass_filter(temp_value);// Temp; Light=((float)ch0_value)/4095.0*100.0;//read/4095=x/100 Light=100-Light; Temp = ((((float)temp_value * 3.3 / 4095.0 - 1.43) / 0.0043) + 25.0)/10; // 温度值(摄氏度) Temp=temper/10; //RAIN temp=HAL_GPIO_ReadPin(RAIN_GPIO_Port, RAIN_Pin); if(temp==0){rain_flag=1;} else {rain_flag=0;} //KEY key1自动mode=0 手动模式mode=1 key2 设置阈值 3-开 4-关 //window_state 手动 0关 1开 key=KEY_Scan(); switch(key) { case 1:if(dis_mode==0){(dis_mode_set=(dis_mode_set+1)%4);}//切换界面 if(dis_mode==1){mode=(mode+1)%2;} break; //默认自动 case 2:if(dis_mode==0) {if(dis_mode_set==2){light_high=light_high+1;} if(dis_mode_set==3){light_low=light_low+1;} if(dis_mode_set==0){temp_high=temp_high+1;} if(dis_mode_set==1){temp_low=temp_low+1;} } //++ if(dis_mode==1){if(mode==1) window_state=2; } break; case 3:if(dis_mode==0) {if(dis_mode_set==2){light_high=light_high-1;} if(dis_mode_set==3){light_low=light_low-1;} if(dis_mode_set==0){temp_high=temp_high-1;} if(dis_mode_set==1){temp_low=temp_low-1;}} //-- if(dis_mode==1){if(mode==1) window_state=1;} break; case 4: dis_mode=(dis_mode+1)%2; break; } key=0; //OLED //模式:自动/手动 自动mode=0 手动模式mode=1 //光照:100% Light //无雨/下雨 10 ℃ rain_flag=1下雨 Temp //窗户状态:开窗/关窗 window_zidongstate= //℃模式自动手光照无下雨窗户状态开关温度速度° if(dis_mode==0) { OLED_NewFrame(); OLED_PrintString(0, 0,"状态" ,&font16x16, OLED_COLOR_NORMAL); OLED_PrintString(42, 0,"温度" ,&font16x16, ((dis_mode_set==0||dis_mode_set==1)?OLED_COLOR_REVERSED:OLED_COLOR_NORMAL)); // OLED_PrintString(84, 0,"光照" ,&font16x16, ((dis_mode_set==2||dis_mode_set==3)?OLED_COLOR_REVERSED:OLED_COLOR_NORMAL)); OLED_PrintString(0, 16,"上限" ,&font16x16, ((dis_mode_set==0||dis_mode_set==2)?OLED_COLOR_REVERSED:OLED_COLOR_NORMAL)); OLED_PrintString(0, 32,"下限" ,&font16x16, ((dis_mode_set==1||dis_mode_set==3)?OLED_COLOR_REVERSED:OLED_COLOR_NORMAL)); sprintf((char *)oled_buffer,"%2d",light_low); OLED_PrintString(84, 32,oled_buffer ,&font16x16, OLED_COLOR_NORMAL); sprintf((char *)oled_buffer,"%2d",light_high); OLED_PrintString(84, 16,oled_buffer ,&font16x16, OLED_COLOR_NORMAL); sprintf((char *)oled_buffer,"%2d",temp_low); OLED_PrintString(42, 32,oled_buffer ,&font16x16, OLED_COLOR_NORMAL); sprintf((char *)oled_buffer,"%2d",temp_high); OLED_PrintString(42, 16,oled_buffer ,&font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); } else { OLED_NewFrame(); sprintf((char *)oled_buffer,"模式:%s", mode==0?"自动":"手动"); OLED_PrintString(0, 0,oled_buffer ,&font16x16, OLED_COLOR_NORMAL); sprintf((char *)oled_buffer,"%2d℃",Temp); OLED_PrintString(88, 0,oled_buffer ,&font16x16, OLED_COLOR_REVERSED); sprintf((char *)oled_buffer,"光照:%2d", Light); OLED_PrintString(0, 16,oled_buffer ,&font16x16, OLED_COLOR_NORMAL); sprintf((char *)oled_buffer,"室外环境:%s ", rain_flag==1?"下雨":"无雨"); OLED_PrintString(0, 32,oled_buffer ,&font16x16, OLED_COLOR_NORMAL); sprintf((char *)oled_buffer,"窗户状态:%s", window_zidongstate==0?"关窗":"开窗"); OLED_PrintString(0, 48,oled_buffer ,&font16x16, OLED_COLOR_NORMAL); OLED_ShowFrame(); } //MOTOR //Blue if(BLE==0xAA){BLE=0x00;BLEMODE=1;} if(BLE==0xBB){BLE=0x00;BLEMODE=2;} if(BLE==0xCC){BLE=0x00;BLEMODE=3;} if(BLE==0xDD){BLE=0x00;BLEMODE=4;} if(BLE==0xFF){BLE=0x00;BLEMODE=5;} if(BLEMODE==1){BLEMODE=0;if(mode==1) window_state=1;}//开窗户 前提手动模式 if(BLEMODE==2){BLEMODE=0;if(mode==1) window_state=2;}//关窗户前提手动模式 if(BLEMODE==3){BLEMODE=0;if(light_low<80)light_low=light_low+3;}//设置开窗阈值++ if(BLEMODE==4){BLEMODE=0;light_low=light_low-3;if(light_low>200)light_low=0;}//设置开窗阈值-- if(BLEMODE==5){BLEMODE=0;mode=(mode+1)%2;}//手动自动模式切换 屏幕可看自动mode=0手动模式mode=1 //windows以及执行逻辑 //window_zidongstate //关窗户 不管是下雨 还是太暗 开窗户必须无雨且亮 //阈值指的是高于这个阈值开窗户 //手机或按键切换模式 手动自动开关 if((Light>light_low&&rain_flag==0)&&mode==0&&window_zidongstate==0) {//自动开模式 window_zidongstate=1; //开窗户motor_set(uint8_t speed, uint16_t angle); open; }//+++++++++++++++++++添加了关窗户冷也关+++++++++ if((Light<light_low||rain_flag==1||Temp<temp_low)&&mode==0&&window_zidongstate==1) {//自动模式关窗户 window_zidongstate=0; //关窗户motor_set(uint8_t speed, uint16_t angle); // motor_set(2, -180); close; } if(mode==1)//手动模式 { if(window_state==1)//开 { if(window_zidongstate==0) { window_zidongstate=1;window_state=0; //开窗户motor_set(uint8_t speed, uint16_t angle); // motor_set(2, 180); open; } } if(window_state==2)//关 { if(window_zidongstate==1) {//开窗户motor_set(uint8_t speed, uint16_t angle); // motor_set(2, -180); window_state=0; window_zidongstate=0; close; } } // 当为关闭时候不再打开 当为打开时候不再关闭 停驶修改 } } void uart_send(char* fmt, ...) { memset(txbuffer, 0, TX_SIZE); va_list ap; va_start(ap, fmt); vsprintf((char *)txbuffer, fmt, ap); va_end(ap); uint16_t length = strlen((char *)txbuffer); HAL_UART_Transmit(&huart1, (uint8_t*)txbuffer, length, 1000); } uint32_t low_pass_filter(uint32_t input) { static uint32_t last_output = 0; // 初始值为0,可根据需要初始化为首个输入值 // 使用整数加权:70% 上次输出 + 30% 当前输入 uint64_t temp = (uint64_t)last_output * 7U + (uint64_t)input * 3U; uint32_t output = (uint32_t)(temp / 10U); last_output = output; // 更新历史值 return output; }
5.1.2电机驱动代码
#include "motor.h" //MOTOR:B13-A2 B14-A1 PA8-PWM #define AIN1_ON HAL_GPIO_WritePin(AIN1_GPIO_Port, AIN1_Pin, GPIO_PIN_SET); #define AIN2_ON HAL_GPIO_WritePin(AIN2_GPIO_Port, AIN2_Pin, GPIO_PIN_SET); #define AIN1_OFF HAL_GPIO_WritePin(AIN1_GPIO_Port, AIN1_Pin, GPIO_PIN_RESET); #define AIN2_OFF HAL_GPIO_WritePin(AIN2_GPIO_Port, AIN2_Pin, GPIO_PIN_RESET); #define PWMA_ON HAL_GPIO_WritePin(PWMA_GPIO_Port, PWMA_Pin, GPIO_PIN_SET); #define PWMA_OFF HAL_GPIO_WritePin(PWMA_GPIO_Port, PWMA_Pin, GPIO_PIN_RESET); void motor_stop() { AIN2_OFF;AIN1_OFF;PWMA_OFF; } void moror_run(uint8_t dir,uint16_t time) { if(dir==1){AIN1_ON;AIN2_OFF;PWMA_ON;} if(dir==2){AIN1_OFF;AIN2_ON;PWMA_ON;} HAL_Delay(time); motor_stop(); }
5.1.3ADC驱动代码
uint16_t adc_buffer[3] = {0}; uint32_t adc_multi_conv_result[3] = {0}; // 用于存储多通道转换结果 extern ADC_HandleTypeDef hadc1; /* 在 USER CODE BEGIN 1 区域添加 */ void MY_ADC_Init(void) { ADC_ChannelConfTypeDef sConfig = {0}; /* USER CODE BEGIN ADC1_Init 1 */ /* USER CODE END ADC1_Init 1 */ /** Common config */ hadc1.Instance = ADC1; hadc1.Init.ScanConvMode = DISABLE; hadc1.Init.ContinuousConvMode = DISABLE; hadc1.Init.DiscontinuousConvMode = DISABLE; hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion = 3; if (HAL_ADC_Init(&hadc1) != HAL_OK) { Error_Handler(); } /** Configure Regular Channel */ sConfig.Channel = ADC_CHANNEL_0; sConfig.Rank = ADC_REGULAR_RANK_1; sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } /** Configure Regular Channel */ sConfig.Channel = ADC_CHANNEL_1; sConfig.Rank = ADC_REGULAR_RANK_2; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } /** Configure Regular Channel */ sConfig.Channel = ADC_CHANNEL_TEMPSENSOR; sConfig.Rank = ADC_REGULAR_RANK_3; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } HAL_ADCEx_Calibration_Start(&hadc1); HAL_ADC_Start(&hadc1); } /** * @brief 读取三个通道的ADC值(非DMA,非连续模式) * @note 按顺序转换每个通道,等待转换完成 */ void MY_ADC_ReadValues(void) { ADC_ChannelConfTypeDef sConfig = {0}; // 通道0配置 sConfig.Channel = ADC_CHANNEL_0; sConfig.Rank = 1; sConfig.SamplingTime = ADC_SAMPLETIME_55CYCLES_5; HAL_ADC_ConfigChannel(&hadc1, &sConfig); if (HAL_ADC_Start(&hadc1) == HAL_OK) { if (HAL_ADC_PollForConversion(&hadc1, 10000) == HAL_OK) { adc_buffer[0] = HAL_ADC_GetValue(&hadc1); } } // 通道1配置 sConfig.Channel = ADC_CHANNEL_1; HAL_ADC_ConfigChannel(&hadc1, &sConfig); if (HAL_ADC_Start(&hadc1) == HAL_OK) { if (HAL_ADC_PollForConversion(&hadc1, 10000) == HAL_OK) { adc_buffer[1] = HAL_ADC_GetValue(&hadc1); } } // 通道2配置 sConfig.Channel = ADC_CHANNEL_2; HAL_ADC_ConfigChannel(&hadc1, &sConfig); if (HAL_ADC_Start(&hadc1) == HAL_OK) { if (HAL_ADC_PollForConversion(&hadc1, 10000) == HAL_OK) { adc_buffer[2] = HAL_ADC_GetValue(&hadc1); } } } /** * @brief 获取指定通道的ADC值 * @param channel 通道号 (0-2) * @retval 通道转换值 (0-4095) */ uint16_t MY_ADC_GetValue(uint8_t channel) { if (channel < 3) { return adc_buffer[channel]; } else { return 0; // 无效通道返回0 } }
5.1.4DS18B20驱动代码
#include "ds18b20.h" // ==================== 配置区(可根据实际修改) ==================== #define DS18B20_GPIO_PORT GPIOA // DS18B20的GPIO端口 #define DS18B20_GPIO_PIN GPIO_PIN_11// DS18B20的GPIO引脚 // ================================================================= /** * @brief 设置DS18B20引脚为输出模式 * @retval 无 */ static void DS18B20_Set_Output(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = DS18B20_GPIO_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(DS18B20_GPIO_PORT, &GPIO_InitStruct); } /** * @brief 设置DS18B20引脚为输入上拉模式 * @retval 无 */ static void DS18B20_Set_Input(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = DS18B20_GPIO_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(DS18B20_GPIO_PORT, &GPIO_InitStruct); } // -------------------------- 核心驱动函数 -------------------------- /** * @brief 复位DS18B20传感器 * @retval 无 */ void DS18B20_Rst(void) { DS18B20_Set_Output(); // 设置为输出模式 HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_RESET); // 拉低总线 delay_us(750); // 拉低750us(符合DS18B20复位时序) HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_SET); // 释放总线 delay_us(15); // 等待传感器回应 } /** * @brief 检测DS18B20是否存在 * @retval 1: 未检测到传感器 | 0: 传感器存在 */ uint8_t DS18B20_Check(void) { uint8_t retry = 0; DS18B20_Set_Input(); // 设置为输入模式 // 等待传感器拉低总线(超时200us) while (HAL_GPIO_ReadPin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN) && retry < 200) { retry++; delay_us(1); }; if (retry >= 200) return 1; // 无回应,传感器不存在 else retry = 0; // 等待传感器释放总线(超时240us) while (!HAL_GPIO_ReadPin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN) && retry < 240) { retry++; delay_us(1); }; if (retry >= 240) return 1; // 回应超时 return 0; // 传感器存在 } /** * @brief 从DS18B20读取1个位数据 * @retval 0/1: 读取到的位值 */ uint8_t DS18B20_Read_Bit(void) { uint8_t data = 0; DS18B20_Set_Output(); // 设置为输出模式 HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_RESET); // 拉低总线2us delay_us(2); HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_SET); // 释放总线 DS18B20_Set_Input(); // 设置为输入模式 delay_us(12); // 等待数据稳定 if (HAL_GPIO_ReadPin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN)) data = 1; // 读取到1 else data = 0; // 读取到0 delay_us(50); // 等待时序结束 return data; } /** * @brief 从DS18B20读取1个字节数据(已修正:低位在前拼接) * @retval 读取到的字节值 */ uint8_t DS18B20_Read_Byte(void) { uint8_t i, bit, dat = 0; for (i = 0; i < 8; i++) // 循环8次,读取8个位 { bit = DS18B20_Read_Bit(); dat |= (bit << i); // 修正:低位在前,依次拼接(原代码核心错误) } return dat; } /** * @brief 向DS18B20写入1个字节数据 * @param dat: 要写入的字节值 * @retval 无 */ void DS18B20_Write_Byte(uint8_t dat) { uint8_t j; uint8_t testb; DS18B20_Set_Output(); // 设置为输出模式 for (j = 0; j < 8; j++) // 逐位写入 { testb = dat & 0x01; // 取最低位 dat = dat >> 1; // 右移,准备下一位 if (testb) // 写1:拉低2us,释放60us { HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_RESET); delay_us(2); HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_SET); delay_us(60); } else // 写0:拉低60us,释放2us { HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_RESET); delay_us(60); HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_SET); delay_us(2); } } } /** * @brief 启动DS18B20温度转换 * @retval 无 */ void DS18B20_Start(void) { DS18B20_Rst(); DS18B20_Check(); DS18B20_Write_Byte(0xcc); // 跳过ROM指令(单传感器时用) DS18B20_Write_Byte(0x44); // 启动温度转换指令 } /** * @brief 初始化DS18B20传感器 * @retval 1: 初始化失败(无传感器) | 0: 初始化成功 */ uint8_t DS18B20_Init(void) { // 先初始化引脚为输出模式 DS18B20_Set_Output(); HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_SET); DS18B20_Rst(); // 复位传感器 return DS18B20_Check(); // 检测传感器是否存在 } /** * @brief 读取DS18B20温度值(已修正:系数0.0625) * @retval 温度值(单位:0.1℃,范围:-550~1250 → -55.0℃~125.0℃) */ short DS18B20_Get_Temp(void) { uint8_t TL, TH; short tem; DS18B20_Start(); // 启动温度转换 // HAL_Delay(750); // 等待转换完成(最大750ms) DS18B20_Rst(); DS18B20_Check(); DS18B20_Write_Byte(0xcc); // 跳过ROM DS18B20_Write_Byte(0xbe); // 读取暂存器指令 TL = DS18B20_Read_Byte(); // 读取温度低字节 TH = DS18B20_Read_Byte(); // 读取温度高字节 // 处理正负温度 if((TH & 0xf8) != 0) // 高5位为1,表示负温度 { TL = ~TL + 1; // 补码转原码 TH = ~TH; tem = -((TH << 8) | TL); } else // 正温度 { tem = (TH << 8) | TL; } tem = (float)tem * 0.0625 * 10; // 修正:系数0.0625(原代码0.625错误),×10转为0.1℃精度 return tem; }
六、工程主要内容

工程的具体内容包括但不限于原理图、程序代码、流程图等等。完整工程、原理图、APP 源码我已经开源到 Gitee,需要的同学可以评论区留言或者私信,009--基于STM32的窗控系统(蓝牙)

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 20:30:25

深入剖析MySQL索引优化:提升数据库性能的核心技巧

目录 一、MySQL B树索引回顾 &#xff08;一&#xff09;索引简单背景 &#xff08;二&#xff09;B树索引简单分析 扩展&#xff1a;聚簇索引普通索引 二、索引相关基本操作 &#xff08;一&#xff09;创建索引 &#xff08;二&#xff09;查看索引 &#xff08;三&a…

作者头像 李华
网站建设 2026/9/7 20:28:08

int* p[4]和int(*p)[4]的区别:指针数组与数组指针详解

int* p[4] 和 int(*p)[4] 的区别&#xff08;超详细&#xff09;如果你写过 C 或者 C&#xff0c;肯定被这两个声明折磨过。int* p[4]和int(*p)[4]&#xff0c;肉眼看去就差一对括号&#xff0c;可两者表达的东西完全不同&#xff1a;一个是装了 4 个指针的数组&#xff0c;一个…

作者头像 李华
网站建设 2026/9/7 20:27:37

LabVIEW调用外部程序全攻略:从System Exec到ActiveX控制Word/Excel

做LabVIEW上位机开发的这几年&#xff0c;我几乎每年都会碰到同一个需求&#xff1a;程序跑完了得把测试报告弹出来给操作员看&#xff0c;或者把生成的PDF、Word、Excel文档自动打开、按模板填数据、打印归档。有些项目更复杂一点&#xff0c;要求直接在LabVIEW里调起Word往固…

作者头像 李华
网站建设 2026/9/7 20:27:22

海康威视闸机对接程序源码与SDK二次开发实战指南

简介&#xff1a;面向 Java 开发者与系统集成商&#xff0c;提供海康威视智能闸机设备对接的完整 SDK 调用示例。源码基于海康官方 Java SDK 封装设备连接、身份认证、闸机开关控制、通行记录读取及事件监听等核心逻辑&#xff0c;适用于办公门禁、园区出入口、地铁站等人员通行…

作者头像 李华
网站建设 2026/9/7 20:23:56

yao-meta-skill - runtime-conformance-method

运行时一致性方法 运行时一致性把平台兼容性从打包的事后考虑变成发布门槛。 目的 当技能为 OpenAI、Claude、Agent Skills、VS Code / Copilot 或通用目标打包时&#xff0c;使用此检查。目标不是证明每个运行时行为完全一致。目标是证明包暴露了足够的元数据、文件和降级说明…

作者头像 李华