基于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爱好者和项目开发者

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐