前阵子帮学弟改毕设的时候翻到这么个STM32做的智能窗帘晾衣架方案,刚好是那种没实物但资料全到能直接套的DIY向项目,特别适合手头上没零件又想攒项目的朋友唠唠
基于STM32单片机的智能窗帘/晾衣架系统资料,无实物,仅提供资料和配套报告支持光线+雨滴+定时开关功能STM32单片机负责数据处理,OLED显示模式、光照强度、时间等信息光敏传感器采集光照,雨滴传感器检测天气,定时模式可设置关闭时间按键用于模式切换和参数调整适合DIY爱好者和项目开发者
先给大家捋捋整体逻辑:这玩意就是把STM32当大脑,光敏传感器当“眼睛”测光照,雨滴传感器当“天气预报员”测有没有下雨,按键用来切模式改参数,OLED屏幕把当前状态全给你摆出来,还能定时开关,不管是学生党做课程设计还是DIY爱好者折腾小玩意都够用。
先唠最基础的光照采集模块
毕竟自动开关窗帘的核心依据就是光照强度嘛,很多新手一开始怕ADC配置麻烦,其实STM32的HAL库已经把坑填得差不多了,我把精简后的核心代码扒出来给大家看:
// ADC初始化,PA0接光敏传感器模块 void ADC1_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; ADC_HandleTypeDef hadc1; // 先开时钟,STM32的传统艺能 __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_ADC1_CLK_ENABLE(); // 把PA0配置成模拟输入,光敏模块直接插这就行 GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // ADC基础配置,用12位分辨率足够日常用了 hadc1.Instance = ADC1; hadc1.Init.ScanConvMode = DISABLE; hadc1.Init.ContinuousConvMode = ENABLE; hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion = 1; HAL_ADC_Init(&hadc1); // 绑定通道0,也就是PA0 ADC_ChannelConfTypeDef sConfig = {0}; sConfig.Channel = ADC_CHANNEL_0; sConfig.Rank = 1; sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES; HAL_ADC_ConfigChannel(&hadc1, &sConfig); HAL_ADC_Start(&hadc1); } // 直接调用就能拿到当前光敏的ADC数值,范围0-4095 uint16_t Get_Light(void) { HAL_ADC_PollForConversion(&hadc1, 100); return HAL_ADC_GetValue(&hadc1); }这段代码其实没啥花活,就是把ADC配置成连续读取模式,每次调用就能拿到当前的光照数值。我当初第一次用的时候踩过坑:没开对应引脚的时钟,结果死活读不到值,后来翻了HAL库的官方例程才搞定。而且不用非得转换成lux,直接判断数值高低就行——比如晴天的时候数值大概在3000+,阴天可能就1000以内,直接写if(Get_Light() < 1000)就可以触发开窗帘的动作,新手不用纠结校准的事,先跑通再说。
雨滴传感器就更简单了
大部分模块都是输出高低电平,没雨的时候拉高,下雨的时候拉低,初始化代码甚至比ADC还短:
// 雨滴传感器初始化,PA1接模块输出 void Rain_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); // 配置成上拉输入,避免浮空乱跳 GPIO_InitStruct.Pin = GPIO_PIN_1; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } // 检测是否下雨,返回1就是下雨了 uint8_t Get_Rain(void) { // 模块下雨时输出低电平,取反一下就能拿到正确状态 return !HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1); }我当初第一次接雨滴模块的时候,居然忘了加消抖,结果下雨的时候传感器疯狂跳变,后来加了个10ms的延时滤波就搞定了,这点新手一定要注意。
接下来是OLED显示模块
这个是让项目看起来像那么回事的关键,我用的是12864的SSD1306屏幕,I2C接口,直接抄正点原子的驱动改改引脚就能用。贴一段主循环里的显示代码:
// 主循环里的显示逻辑,每100ms刷新一次 char buf[32]; uint16_t light_val = Get_Light(); uint8_t rain_state = Get_Rain(); OLED_Clear(); // 一定要清屏!不然会叠残影,我当初踩过这个坑 // 显示光照强度 sprintf(buf, "Light: %d", light_val); OLED_ShowString(0, 0, (uint8_t*)buf, 16); // 显示下雨状态 sprintf(buf, "Rain: %s", rain_state ? "YES" : "NO"); OLED_ShowString(0, 20, (uint8_t*)buf, 16); // 显示当前模式和定时时间 sprintf(buf, "Mode: Auto Time: %dh", set_time); OLED_ShowString(0, 40, (uint8_t*)buf, 16); OLED_Refresh(); // 刷新屏幕才会显示出来这里用sprintf格式化字符串是最省事的写法,不用自己一个个点阵拼字符,新手直接抄就行,记得改一下字体大小和显示位置就行。
按键切换和定时功能
按键这块我推荐用扫描法,不用搞外部中断,主循环里扫一下就行,还能顺便做消抖:
// 按键扫描,返回键值:1=模式切换,2=加定时时间,3=减定时时间 uint8_t Key_Scan(void) { static uint8_t key_up = 1; // 静态变量记录按键状态 uint8_t key_val = 0; if(key_up && (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2) == 0 || HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) == 0 || HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == 0)) { HAL_Delay(10); // 消抖延时,避免误触发 key_up = 0; // 判断按下的是哪个按键 if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2) == 0) key_val = 1; else if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) == 0) key_val = 2; else if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == 0) key_val = 3; } else if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2) == 1 && HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) == 1 && HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == 1) { key_up = 1; // 按键松开了,重置状态 } return key_val; }定时功能我是用定时器中断做的,设置1秒中断一次,计数到3600就是1小时,配合按键修改定时时间,逻辑特别简单:比如短按加1小时,长按减1小时,稍微改一下扫描代码就能实现。
最后是主循环的整体逻辑
其实就是个流水线:先扫按键改参数,再读传感器数据,然后根据当前模式执行动作,最后刷新屏幕。贴一段精简后的主循环:
int main(void) { HAL_Init(); SystemClock_Config(); // 这个是STM32CubeMX自动生成的,不用自己改 ADC1_Init(); Rain_Init(); OLED_Init(); uint8_t current_mode = 0; // 0=自动模式,1=定时模式,2=手动模式 uint8_t set_time = 1; // 默认定时1小时 uint32_t timer_cnt = 0; while (1) { uint8_t key = Key_Scan(); // 按键逻辑 if(key == 1) current_mode = (current_mode + 1) % 3; // 循环切换模式 else if(key == 2) set_time = (set_time + 1) % 24; // 最多定时23小时 else if(key == 3) set_time = set_time > 0 ? set_time -1 : 23; // 定时模式逻辑 if(current_mode == 1) { timer_cnt++; if(timer_cnt >= 3600) { timer_cnt = 0; set_time--; if(set_time == 0) { // 到点了,执行开关动作 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); } } } // 自动模式逻辑 if(current_mode == 0) { if(light_val < 1000) HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); // 开窗帘 else if(light_val > 3000) HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); // 关窗帘 if(rain_state == 1) HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET); // 下雨关晾衣架 } Update_OLED(); // 就是之前的显示代码,封装成函数就行 HAL_Delay(100); } }这里要注意!STM32的GPIO输出电流不够驱动电机,一定要加继电器或者L298N电机驱动模块,我当初差点直接用GPIO接电机,还好学长提醒了,不然板子直接冒烟。
最后唠两句
这个项目真的特别适合入门,不管是想练手STM32,还是想凑个课程设计/毕设项目,都能直接套现成的资料和代码。而且就算没有实物,用Proteus画个仿真图就能跑起来,论坛里也有很多配套的报告模板可以直接改。唯一要注意的就是引脚对应和传感器阈值调整,别直接抄代码就完事,根据自己用的模块改一改就能用。
基于STM32单片机的智能窗帘/晾衣架系统资料,无实物,仅提供资料和配套报告支持光线+雨滴+定时开关功能STM32单片机负责数据处理,OLED显示模式、光照强度、时间等信息光敏传感器采集光照,雨滴传感器检测天气,定时模式可设置关闭时间按键用于模式切换和参数调整适合DIY爱好者和项目开发者
