STM32项目实战:基于STM32F103的智能台灯控制

文章目录

    • 一、前言
      • [1.1 技术背景](#1.1 技术背景)
      • [1.2 应用场景](#1.2 应用场景)
      • [1.3 读者收获](#1.3 读者收获)
      • [1.4 技术栈](#1.4 技术栈)
    • 二、环境准备
      • [2.1 硬件要求](#2.1 硬件要求)
      • [2.2 模块连接](#2.2 模块连接)
    • 三、核心实现
      • [3.1 PWM调光驱动](#3.1 PWM调光驱动)
      • [3.2 人体感应与自动调光](#3.2 人体感应与自动调光)
      • [3.3 触摸按键](#3.3 触摸按键)
      • [3.4 主控程序](#3.4 主控程序)
    • 四、系统架构
    • 五、测试验证
      • [5.1 功能测试](#5.1 功能测试)
      • [5.2 性能测试](#5.2 性能测试)
    • 六、故障排查
      • [6.1 LED闪烁](#6.1 LED闪烁)
      • [6.2 人体感应误触发](#6.2 人体感应误触发)
    • 七、总结
      • [7.1 核心知识点](#7.1 核心知识点)
      • [7.2 扩展方向](#7.2 扩展方向)

一、前言

1.1 技术背景

智能台灯是现代智能家居的重要组成部分,不仅提供基本的照明功能,还集成了亮度调节、色温控制、定时开关、人体感应等智能化功能。通过环境光感应和人体存在检测,智能台灯可以实现自动开关和亮度调节,提供更加舒适和节能的照明体验。

STM32F103系列微控制器凭借其丰富的外设接口、较高的性价比和完善的生态系统,成为智能台灯控制系统的理想选择。

1.2 应用场景

  • 学习办公:自动亮度调节,护眼模式
  • 卧室床头:人体感应,夜间模式
  • 儿童房间:定时关闭,防近视提醒
  • 智能家具:语音控制,APP远程控制

1.3 读者收获

完成本教程后,你将掌握:

  • PWM调光技术
  • 人体红外感应(PIR)
  • 环境光检测(光敏电阻/BH1750)
  • 触摸按键设计
  • 定时器应用
  • 低功耗设计

1.4 技术栈

硬件平台:

  • 主控芯片:STM32F103C8T6
  • 人体感应:HC-SR501 PIR模块
  • 环境光检测:BH1750 / 光敏电阻
  • 调光方式:PWM + MOS管
  • 显示模块:OLED12864
  • 通信模块:ESP8266 WiFi

软件工具:

  • IDE:Keil MDK-ARM / STM32CubeIDE
  • 固件库:STM32Cube HAL库

二、环境准备

2.1 硬件要求

系统框图:
通信模块
LED驱动
传感器组
STM32F103主控
电源系统
PWM1
PWM2
AC 220V
DC 5V
DC 3.3V
TIM2

PWM输出
ADC
I2C1
GPIO
USART1
HC-SR501

人体感应
BH1750

光照强度
触摸按键
冷白光

LED
暖白光

LED
MOS管1
MOS管2
OLED显示
ESP8266

WiFi

硬件清单:

  • STM32F103C8T6最小系统板 × 1
  • HC-SR501人体感应模块 × 1
  • BH1750光照传感器 × 1
  • 触摸按键模块 × 3
  • 冷白LED灯带 × 1
  • 暖白LED灯带 × 1
  • IRF540N MOS管 × 2
  • OLED12864显示屏 × 1
  • ESP8266 WiFi模块 × 1
  • 5V电源适配器 × 1
  • 杜邦线若干

2.2 模块连接

PWM输出:

  • PA0(TIM2_CH1)→ 冷白LED驱动
  • PA1(TIM2_CH2)→ 暖白LED驱动

传感器连接:

  • PIR输出 → PA2
  • BH1750 SCL → PB6
  • BH1750 SDA → PB7

三、核心实现

3.1 PWM调光驱动

📄 创建文件:Inc/led_pwm.h

c 复制代码
/* led_pwm.h - LED PWM调光驱动 */
#ifndef __LED_PWM_H
#define __LED_PWM_H

#include "main.h"

#define PWM_MAX     1000    /* PWM最大值 */

/* 函数声明 */
void LED_PWM_Init(void);
void LED_SetCold(uint16_t brightness);
void LED_SetWarm(uint16_t brightness);
void LED_SetColorTemp(uint16_t cold, uint16_t warm);
void LED_SetBrightness(uint16_t brightness);
void LED_SetColorTempPercent(uint8_t percent);
void LED_Off(void);

#endif /* __LED_PWM_H */

📄 创建文件:Src/led_pwm.c

c 复制代码
/* led_pwm.c - LED PWM调光驱动实现 */
#include "led_pwm.h"

extern TIM_HandleTypeDef htim2;

/**
 * @brief PWM初始化
 */
void LED_PWM_Init(void)
{
    TIM_OC_InitTypeDef sConfigOC = {0};
    
    htim2.Instance = TIM2;
    htim2.Init.Prescaler = 71;      /* 72MHz / 72 = 1MHz */
    htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim2.Init.Period = PWM_MAX - 1;  /* 1kHz PWM频率 */
    htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_PWM_Init(&htim2);
    
    /* 配置通道1 - 冷白LED */
    sConfigOC.OCMode = TIM_OCMODE_PWM1;
    sConfigOC.Pulse = 0;
    sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
    sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
    HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1);
    
    /* 配置通道2 - 暖白LED */
    HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2);
    
    /* 启动PWM */
    HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
    HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
}

/**
 * @brief 设置冷白LED亮度
 */
void LED_SetCold(uint16_t brightness)
{
    if (brightness > PWM_MAX) brightness = PWM_MAX;
    __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, brightness);
}

/**
 * @brief 设置暖白LED亮度
 */
void LED_SetWarm(uint16_t brightness)
{
    if (brightness > PWM_MAX) brightness = PWM_MAX;
    __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, brightness);
}

/**
 * @brief 设置混合色温
 */
void LED_SetColorTemp(uint16_t cold, uint16_t warm)
{
    LED_SetCold(cold);
    LED_SetWarm(warm);
}

/**
 * @brief 设置总亮度(保持色温比例)
 */
void LED_SetBrightness(uint16_t brightness)
{
    static uint16_t current_cold = 500;
    static uint16_t current_warm = 500;
    
    if (brightness > PWM_MAX) brightness = PWM_MAX;
    
    /* 按比例调整 */
    uint32_t total = current_cold + current_warm;
    if (total == 0) total = 1;
    
    uint16_t new_cold = (uint32_t)brightness * current_cold / total;
    uint16_t new_warm = brightness - new_cold;
    
    LED_SetCold(new_cold);
    LED_SetWarm(new_warm);
}

/**
 * @brief 设置色温比例(0-100,0=最冷,100=最暖)
 */
void LED_SetColorTempPercent(uint8_t percent)
{
    if (percent > 100) percent = 100;
    
    uint16_t warm = (uint32_t)percent * PWM_MAX / 100;
    uint16_t cold = PWM_MAX - warm;
    
    LED_SetColorTemp(cold, warm);
}

/**
 * @brief 关闭LED
 */
void LED_Off(void)
{
    LED_SetColorTemp(0, 0);
}

3.2 人体感应与自动调光

📄 创建文件:Src/auto_control.c

c 复制代码
/* auto_control.c - 自动控制 */
#include "main.h"
#include "led_pwm.h"
#include "bh1750.h"

#define PIR_PIN     GPIO_PIN_2
#define PIR_PORT    GPIOA

#define LIGHT_THRESHOLD_LOW     50      /* 低于此值开灯 */
#define LIGHT_THRESHOLD_HIGH    200     /* 高于此值关灯 */
#define DELAY_OFF_TIME          30000   /* 无人后30秒关灯 */

/* 状态 */
typedef enum {
    MODE_MANUAL,    /* 手动模式 */
    MODE_AUTO       /* 自动模式 */
} LampModeTypeDef;

static LampModeTypeDef lamp_mode = MODE_MANUAL;
static uint8_t lamp_on = 0;
static uint32_t last_pir_time = 0;
static uint16_t target_brightness = 500;

/**
 * @brief 初始化PIR
 */
void PIR_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    GPIO_InitStruct.Pin = PIR_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    HAL_GPIO_Init(PIR_PORT, &GPIO_InitStruct);
}

/**
 * @brief 检查PIR状态
 */
uint8_t PIR_Check(void)
{
    return HAL_GPIO_ReadPin(PIR_PORT, PIR_PIN) == GPIO_PIN_SET;
}

/**
 * @brief 自动亮度调节
 */
void Auto_Brightness_Adjust(void)
{
    uint16_t light;
    uint16_t brightness;
    
    /* 读取环境光 */
    if (BH1750_ReadLight(&light) != 0) {
        return;
    }
    
    /* 根据环境光计算目标亮度 */
    /* 环境光越暗,台灯越亮 */
    if (light < LIGHT_THRESHOLD_LOW) {
        brightness = PWM_MAX;  /* 最亮 */
    } else if (light > LIGHT_THRESHOLD_HIGH) {
        brightness = 0;        /* 关闭 */
    } else {
        /* 线性插值 */
        brightness = PWM_MAX * (LIGHT_THRESHOLD_HIGH - light) / 
                    (LIGHT_THRESHOLD_HIGH - LIGHT_THRESHOLD_LOW);
    }
    
    target_brightness = brightness;
    
    /* 平滑过渡 */
    static uint16_t current_brightness = 0;
    if (current_brightness < target_brightness) {
        current_brightness += 10;
        if (current_brightness > target_brightness) {
            current_brightness = target_brightness;
        }
    } else if (current_brightness > target_brightness) {
        current_brightness -= 10;
        if (current_brightness < target_brightness) {
            current_brightness = target_brightness;
        }
    }
    
    LED_SetBrightness(current_brightness);
}

/**
 * @brief 自动控制任务
 */
void Auto_Control_Task(void)
{
    if (lamp_mode != MODE_AUTO) {
        return;
    }
    
    /* 检测人体 */
    if (PIR_Check()) {
        last_pir_time = HAL_GetTick();
        
        if (!lamp_on) {
            lamp_on = 1;
            DEBUG_PRINT("检测到人体,开灯\r\n");
        }
    }
    
    /* 无人检测 */
    if (lamp_on && (HAL_GetTick() - last_pir_time > DELAY_OFF_TIME)) {
        lamp_on = 0;
        LED_Off();
        DEBUG_PRINT("长时间无人,关灯\r\n");
        return;
    }
    
    /* 自动调光 */
    if (lamp_on) {
        Auto_Brightness_Adjust();
    }
}

/**
 * @brief 设置模式
 */
void Lamp_SetMode(LampModeTypeDef mode)
{
    lamp_mode = mode;
    if (mode == MODE_MANUAL) {
        DEBUG_PRINT("切换到手动模式\r\n");
    } else {
        DEBUG_PRINT("切换到自动模式\r\n");
    }
}

/**
 * @brief 手动开关灯
 */
void Lamp_Toggle(void)
{
    if (lamp_on) {
        lamp_on = 0;
        LED_Off();
        DEBUG_PRINT("手动关灯\r\n");
    } else {
        lamp_on = 1;
        LED_SetBrightness(500);
        DEBUG_PRINT("手动开灯\r\n");
    }
}

3.3 触摸按键

📄 创建文件:Src/touch_key.c

c 复制代码
/* touch_key.c - 触摸按键 */
#include "main.h"

#define KEY_POWER_PIN   GPIO_PIN_3
#define KEY_UP_PIN      GPIO_PIN_4
#define KEY_DOWN_PIN    GPIO_PIN_5
#define KEY_PORT        GPIOA

#define KEY_DEBOUNCE    20
#define KEY_LONG_TIME   1000

typedef enum {
    KEY_NONE,
    KEY_POWER,
    KEY_UP,
    KEY_DOWN
} KeyTypeDef;

typedef enum {
    KEY_STATE_IDLE,
    KEY_STATE_PRESS,
    KEY_STATE_HOLD
} KeyStateTypeDef;

/* 函数声明 */
void TouchKey_Init(void);
KeyTypeDef TouchKey_Scan(void);
void TouchKey_Process(void);

static KeyStateTypeDef key_state = KEY_STATE_IDLE;
static uint32_t key_press_time = 0;
static KeyTypeDef last_key = KEY_NONE;

/**
 * @brief 初始化触摸按键
 */
void TouchKey_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    GPIO_InitStruct.Pin = KEY_POWER_PIN | KEY_UP_PIN | KEY_DOWN_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    HAL_GPIO_Init(KEY_PORT, &GPIO_InitStruct);
}

/**
 * @brief 扫描按键
 */
KeyTypeDef TouchKey_Scan(void)
{
    if (HAL_GPIO_ReadPin(KEY_PORT, KEY_POWER_PIN) == GPIO_PIN_SET) {
        return KEY_POWER;
    }
    if (HAL_GPIO_ReadPin(KEY_PORT, KEY_UP_PIN) == GPIO_PIN_SET) {
        return KEY_UP;
    }
    if (HAL_GPIO_ReadPin(KEY_PORT, KEY_DOWN_PIN) == GPIO_PIN_SET) {
        return KEY_DOWN;
    }
    return KEY_NONE;
}

/**
 * @brief 处理按键
 */
void TouchKey_Process(void)
{
    KeyTypeDef key = TouchKey_Scan();
    uint32_t current_time = HAL_GetTick();
    
    switch (key_state) {
        case KEY_STATE_IDLE:
            if (key != KEY_NONE) {
                key_state = KEY_STATE_PRESS;
                key_press_time = current_time;
                last_key = key;
            }
            break;
            
        case KEY_STATE_PRESS:
            if (key == KEY_NONE) {
                /* 短按释放 */
                key_state = KEY_STATE_IDLE;
                
                /* 执行短按操作 */
                switch (last_key) {
                    case KEY_POWER:
                        Lamp_Toggle();
                        break;
                    case KEY_UP:
                        /* 增加亮度 */
                        LED_SetBrightness(target_brightness + 100);
                        break;
                    case KEY_DOWN:
                        /* 降低亮度 */
                        LED_SetBrightness(target_brightness - 100);
                        break;
                    default:
                        break;
                }
            } else if ((current_time - key_press_time) > KEY_LONG_TIME) {
                /* 长按 */
                key_state = KEY_STATE_HOLD;
                
                /* 执行长按操作 */
                if (last_key == KEY_POWER) {
                    /* 长按电源键切换模式 */
                    Lamp_SetMode(lamp_mode == MODE_MANUAL ? MODE_AUTO : MODE_MANUAL);
                }
            }
            break;
            
        case KEY_STATE_HOLD:
            if (key == KEY_NONE) {
                key_state = KEY_STATE_IDLE;
            } else if (last_key == KEY_UP) {
                /* 长按增加亮度 */
                LED_SetBrightness(target_brightness + 50);
                HAL_Delay(50);
            } else if (last_key == KEY_DOWN) {
                /* 长按降低亮度 */
                LED_SetBrightness(target_brightness - 50);
                HAL_Delay(50);
            }
            break;
    }
}

3.4 主控程序

📄 创建文件:Src/main.c

c 复制代码
/* main.c - 智能台灯主程序 */
#include "main.h"
#include "led_pwm.h"
#include "bh1750.h"

/* 函数声明 */
void System_Init(void);

/**
 * @brief 系统初始化
 */
void System_Init(void)
{
    HAL_Init();
    SystemClock_Config();
    
    /* 初始化外设 */
    MX_GPIO_Init();
    MX_TIM2_Init();
    MX_I2C1_Init();
    MX_USART1_UART_Init();
    
    /* 初始化模块 */
    LED_PWM_Init();
    BH1750_Init();
    PIR_Init();
    TouchKey_Init();
    
    DEBUG_PRINT("\r\n===========================\r\n");
    DEBUG_PRINT("智能台灯控制系统启动\r\n");
    DEBUG_PRINT("===========================\r\n");
}

/**
 * @brief 主函数
 */
int main(void)
{
    uint32_t last_auto_time = 0;
    
    System_Init();
    
    while (1) {
        /* 处理触摸按键 */
        TouchKey_Process();
        
        /* 自动控灯任务(每100ms) */
        if (HAL_GetTick() - last_auto_time >= 100) {
            last_auto_time = HAL_GetTick();
            Auto_Control_Task();
        }
        
        HAL_Delay(10);
    }
}

四、系统架构

短按电源
长按电源
上下键
无按键
有人
无人


开始
系统初始化
主循环
扫描按键
按键处理
开关灯
切换模式
调节亮度
自动控灯任务
更新显示
检测人体
检测环境光
超时?
自动调光
关灯


五、测试验证

5.1 功能测试

  • PWM调光:0-100%亮度调节
  • 色温调节:冷白到暖白切换
  • 人体感应:检测范围5米
  • 自动调光:根据环境光自动调节

5.2 性能测试

  • PWM频率:1kHz(无频闪)
  • 响应时间:<100ms
  • 功耗:<5W

六、故障排查

6.1 LED闪烁

排查:

  1. 提高PWM频率
  2. 检查电源滤波
  3. 增加MOS管驱动能力

6.2 人体感应误触发

优化:

  1. 调整PIR灵敏度
  2. 增加延时判断
  3. 优化安装位置

七、总结

7.1 核心知识点

  1. PWM调光:频率与占空比控制
  2. 人体感应:PIR原理与应用
  3. 自动调光:环境光反馈控制
  4. 触摸按键:电容触摸原理

7.2 扩展方向

  • 添加WiFi远程控制
  • 集成语音控制
  • 添加定时功能
  • 实现场景模式

💡 提示:PWM频率建议>1kHz,避免人眼可见频闪。

相关推荐
rjszcb2 小时前
mcu.之armv7 contex-M3/M4系列,时钟树,中断, cpu架构,上电启动过程(二)
单片机
姓刘的哦2 小时前
RK3568之I2C子系统(协议)
单片机·嵌入式硬件
咕噜咕噜啦啦3 小时前
一些单片机学习相关名词
单片机·嵌入式硬件
芋头莎莎3 小时前
ESP32 +VSCode与搭建开发环境教程
vscode·单片机
小谦32513 小时前
第十二篇、CubeMX | 可见光颜色传感器 TCS3472
单片机·嵌入式硬件
Chat_zhanggong3453 小时前
主推22AP10作用有哪些?
嵌入式硬件
Chat_zhanggong34511 小时前
主推RK3567J作用有哪些?
人工智能·嵌入式硬件
Ww.xh11 小时前
STM32与ESP8266AT指令超时重传方案
stm32·单片机·嵌入式硬件
LCG元12 小时前
STM32实战:基于STM32F103的智能共享充电宝管理系统
stm32·单片机·嵌入式硬件