PWM控制LED灯渐亮渐灭实验

两个完整方案:基于51单片机的经典方案和基于Arduino的现代方案


方案一:51单片机PWM控制LED渐亮渐灭

一、实验原理

1.1 PWM基本原理

PWM(Pulse Width Modulation,脉冲宽度调制)通过对一系列脉冲的宽度进行调制,来等效获得所需要的波形。LED亮度由PWM占空比决定:

  • 占空比 = 高电平时间 / 周期时间
  • 占空比越大,LED越亮;占空比越小,LED越暗

1.2 渐亮渐灭原理

通过周期性改变PWM占空比

  • 渐亮:占空比从0%逐渐增加到100%
  • 渐灭:占空比从100%逐渐减少到0%

二、硬件设计

2.1 硬件清单

元件 规格 数量
单片机 STC89C52/AT89C52 1
LED 发光二极管(红色) 1
电阻 220Ω(限流) 1
按键 轻触开关 2
晶振 11.0592MHz 1
电容 30pF 2
电解电容 10μF 1
电阻 10kΩ 1

2.2 电路连接图

+5V
 |
 +-+ 单片机最小系统
 |  XTAL1/XTAL2 -> 11.0592MHz晶振
 |  RST -> 10kΩ电阻+10μF电容
 |  EA -> +5V
 |  VCC -> +5V
 |  GND -> GND
 |
 +-+ PWM输出
 |  P1.0 -> 220Ω电阻 -> LED阳极
 |                      LED阴极 -> GND
 |
 +-+ 按键控制
    P3.0 -> 按键K1 -> GND (加速)
    P3.1 -> 按键K2 -> GND (减速)

2.3 引脚分配表

单片机引脚 连接设备 功能说明
P1.0 LED PWM输出控制LED亮度
P3.0 按键K1 加速按键
P3.1 按键K2 减速按键
P2.0-P2.7 可选数码管 显示当前亮度值

三、软件设计

3.1 主程序 (main.c)

#include <reg52.h>
#include "pwm.h"
#include "keyboard.h"
#include "display.h"

// 全局变量
unsigned char pwm_duty = 0;        // PWM占空比 (0-100)
unsigned char brightness = 0;      // 当前亮度等级 (0-100)
bit fade_direction = 0;            // 0:渐亮 1:渐灭
bit system_pause = 0;              // 系统暂停标志

void main(void)
{
    System_Init();
    
    while(1)
    {
        Key_Process();              // 按键处理
        PWM_Update();               // PWM更新
        Display_Update();           // 显示更新
        Brightness_Control();       // 亮度控制
    }
}

void System_Init(void)
{
    // 初始化定时器0(PWM基准时钟)
    Timer0_Init();
    
    // 初始化定时器1(亮度渐变控制)
    Timer1_Init();
    
    // 初始化显示
    Display_Init();
    
    // 显示初始亮度
    Display_Brightness(brightness);
    
    // 开启总中断
    EA = 1;
    
    // 启动PWM
    PWM_Start();
}

void Key_Process(void)
{
    unsigned char key = Key_Scan();
    
    switch(key)
    {
        case KEY_ACCEL:    // 加速渐变
            if(pwm_duty < 100)
                pwm_duty += 5;
            break;
            
        case KEY_DECEL:    // 减速渐变
            if(pwm_duty > 0)
                pwm_duty -= 5;
            break;
            
        case KEY_PAUSE:    // 暂停/继续
            system_pause = !system_pause;
            if(system_pause)
                PWM_Stop();
            else
                PWM_Start();
            break;
            
        default:
            break;
    }
}

void Brightness_Control(void)
{
    static unsigned int fade_counter = 0;
    
    if(!system_pause)
    {
        if(++fade_counter >= 100)  // 每100ms调整一次亮度
        {
            fade_counter = 0;
            
            if(fade_direction == 0)  // 渐亮
            {
                if(brightness < 100)
                    brightness++;
                else
                    fade_direction = 1;  // 转为渐灭
            }
            else  // 渐灭
            {
                if(brightness > 0)
                    brightness--;
                else
                    fade_direction = 0;  // 转为渐亮
            }
            
            // 更新PWM占空比
            PWM_Set_Duty(brightness);
            
            // 更新显示
            Display_Brightness(brightness);
        }
    }
}

3.2 PWM控制模块 (pwm.h)

#ifndef __PWM_H__
#define __PWM_H__

#include <reg52.h>

// PWM相关定义
#define PWM_PIN P1_0      // PWM输出引脚
#define PWM_PERIOD 100    // PWM周期(单位:定时器中断次数)

// 函数声明
void PWM_Init(void);
void PWM_Start(void);
void PWM_Stop(void);
void PWM_Set_Duty(unsigned char duty);
void PWM_Update(void);

#endif

3.3 PWM控制模块 (pwm.c)

#include "pwm.h"

static unsigned char pwm_counter = 0;    // PWM计数器
static unsigned char pwm_high_time = 0; // 高电平时间
static bit pwm_running = 0;             // PWM运行状态

void PWM_Init(void)
{
    PWM_PIN = 0;          // 初始输出低电平
    pwm_counter = 0;
    pwm_high_time = 0;
}

void PWM_Start(void)
{
    pwm_running = 1;
}

void PWM_Stop(void)
{
    pwm_running = 0;
    PWM_PIN = 0;          // 关闭输出
}

void PWM_Set_Duty(unsigned char duty)
{
    if(duty > 100) duty = 100;
    pwm_high_time = duty;
}

void PWM_Update(void)
{
    if(pwm_running)
    {
        pwm_counter++;
        
        if(pwm_counter <= pwm_high_time)
        {
            PWM_PIN = 1;  // 输出高电平
        }
        else
        {
            PWM_PIN = 0;  // 输出低电平
        }
        
        if(pwm_counter >= PWM_PERIOD)
        {
            pwm_counter = 0;  // 重新开始新的周期
        }
    }
}

3.4 定时器配置 (timer.h)

#ifndef __TIMER_H__
#define __TIMER_H__

#include <reg52.h>

// 函数声明
void Timer0_Init(void);
void Timer1_Init(void);

#endif

3.5 定时器配置 (timer.c)

#include "timer.h"

// 定时器0初始化(PWM基准时钟,1ms中断)
void Timer0_Init(void)
{
    TMOD &= 0xF0;  // 清除T0控制位
    TMOD |= 0x01;  // 设置T0为模式1(16位定时器)
    TH0 = 0xFC;    // 1ms定时(11.0592MHz)
    TL0 = 0x66;
    ET0 = 1;       // 允许T0中断
    TR0 = 1;       // 启动T0
}

// 定时器1初始化(亮度控制,10ms中断)
void Timer1_Init(void)
{
    TMOD &= 0x0F;  // 清除T1控制位
    TMOD |= 0x10;  // 设置T1为模式1(16位定时器)
    TH1 = 0xDC;    // 10ms定时
    TL1 = 0x00;
    ET1 = 1;       // 允许T1中断
    TR1 = 1;       // 启动T1
}

// 定时器0中断服务程序(PWM基准)
void Timer0_ISR(void) interrupt 1
{
    TH0 = 0xFC;    // 重装初值
    TL0 = 0x66;
    
    PWM_Update();  // 更新PWM输出
}

// 定时器1中断服务程序(亮度控制)
void Timer1_ISR(void) interrupt 3
{
    TH1 = 0xDC;    // 重装初值
    TL1 = 0x00;
    
    // 亮度控制在主循环中处理
}

3.6 键盘扫描 (keyboard.h)

#ifndef __KEYBOARD_H__
#define __KEYBOARD_H__

#include <reg52.h>

// 按键定义
#define KEY_ACCEL   0x01
#define KEY_DECEL   0x02
#define KEY_PAUSE   0x03
#define KEY_NONE    0xFF

// 按键引脚定义
sbit KEY_ACC_PIN = P3^0;
sbit KEY_DEC_PIN = P3^1;
sbit KEY_PAUS_PIN = P3^2;

// 函数声明
void Key_Init(void);
unsigned char Key_Scan(void);

#endif

3.7 键盘扫描 (keyboard.c)

#include "keyboard.h"
#include "delay.h"

void Key_Init(void)
{
    // 按键端口初始化
}

unsigned char Key_Scan(void)
{
    // 检测加速键
    if(KEY_ACC_PIN == 0)
    {
        Delay_Ms(10);
        if(KEY_ACC_PIN == 0)
        {
            while(KEY_ACC_PIN == 0);
            return KEY_ACCEL;
        }
    }
    
    // 检测减速键
    if(KEY_DEC_PIN == 0)
    {
        Delay_Ms(10);
        if(KEY_DEC_PIN == 0)
        {
            while(KEY_DEC_PIN == 0);
            return KEY_DECEL;
        }
    }
    
    // 检测暂停键
    if(KEY_PAUS_PIN == 0)
    {
        Delay_Ms(10);
        if(KEY_PAUS_PIN == 0)
        {
            while(KEY_PAUS_PIN == 0);
            return KEY_PAUSE;
        }
    }
    
    return KEY_NONE;
}

3.8 显示模块 (display.h)

#ifndef __DISPLAY_H__
#define __DISPLAY_H__

#include <reg52.h>

// 显示函数声明
void Display_Init(void);
void Display_Brightness(unsigned char brightness);
void Display_Clear(void);

#endif

3.9 显示模块 (display.c)

#include "display.h"

void Display_Init(void)
{
    // 如果使用数码管显示,这里初始化数码管
}

void Display_Brightness(unsigned char brightness)
{
    // 在数码管上显示亮度值
    // 这里简化为串口输出
    // printf("Brightness: %d%%\n", brightness);
}

void Display_Clear(void)
{
    // 清屏
}

3.10 延时函数 (delay.h)

#ifndef __DELAY_H__
#define __DELAY_H__

#include <reg52.h>

void Delay_Ms(unsigned int ms);
void Delay_Us(unsigned int us);

#endif

3.11 延时函数 (delay.c)

#include "delay.h"

void Delay_Ms(unsigned int ms)
{
    unsigned int i, j;
    for(i = ms; i > 0; i--)
        for(j = 110; j > 0; j--);
}

void Delay_Us(unsigned int us)
{
    while(us--)
    {
        _nop_();
        _nop_();
        _nop_();
        _nop_();
    }
}

四、Proteus仿真电路

4.1 Proteus元件清单

1. AT89C52 (单片机)
2. LED-RED (红色LED)
3. RES (电阻220Ω)
4. BUTTON (按键×2)
5. RES (电阻10kΩ×2)
6. CRYSTAL (11.0592MHz)
7. CAP (电容30pF×2)
8. CAP-ELEC (电解电容10μF)
9. POWER (电源)
10. GROUND (地)
11. OSCILLOSCOPE (示波器,观察PWM波形)

4.2 仿真连接图

AT89C52:
  P1.0 -> 220Ω电阻 -> LED阳极
  LED阴极 -> GND
  
  P3.0 -> 按键K1 -> GND (加速)
  P3.1 -> 按键K2 -> GND (减速)
  P3.2 -> 按键K3 -> GND (暂停)
  
  XTAL1/XTAL2 -> 11.0592MHz晶振
  RST -> 10kΩ电阻+10μF电容
  VCC -> +5V
  GND -> GND
  
  示波器通道1 -> P1.0 (观察PWM波形)

方案二:Arduino PWM控制LED渐亮渐灭

一、硬件设计

1.1 硬件清单

元件 规格 数量
Arduino板 Arduino UNO 1
LED 发光二极管 1
电阻 220Ω 1
按键 轻触开关 2
面包板 830孔 1
杜邦线 公对公 若干

1.2 电路连接图

Arduino UNO    LED电路
-----------    ---------
5V        ->   220Ω电阻 -> LED阳极
GND       ->   LED阴极
D9 (PWM)  ->   220Ω电阻 -> LED阳极(PWM控制)
D2        ->   按键K1 -> GND(加速)
D3        ->   按键K2 -> GND(减速)

二、软件设计

2.1 Arduino主程序 (breathing_led.ino)

/*
 * PWM控制LED渐亮渐灭实验
 * 功能:实现LED呼吸灯效果,可通过按键调节速度
 */

#include "pwm_control.h"
#include "keyboard.h"

// 全局变量
int brightness = 0;        // 当前亮度 (0-255)
int fadeAmount = 5;       // 每次变化的亮度增量
bool fadeDirection = true; // true:渐亮 false:渐灭
bool systemPause = false; // 系统暂停标志

void setup() {
  Serial.begin(9600);
  
  // 初始化PWM
  PWM_Init();
  
  // 初始化按键
  Keyboard_Init();
  
  // 初始亮度
  PWM_SetBrightness(brightness);
  
  Serial.println("Breathing LED System Started!");
}

void loop() {
  // 按键处理
  int key = Key_Scan();
  
  switch(key) {
    case KEY_ACCEL:
      if(fadeAmount < 20) fadeAmount += 2;
      Serial.print("Speed increased: ");
      Serial.println(fadeAmount);
      break;
      
    case KEY_DECEL:
      if(fadeAmount > 1) fadeAmount -= 2;
      Serial.print("Speed decreased: ");
      Serial.println(fadeAmount);
      break;
      
    case KEY_PAUSE:
      systemPause = !systemPause;
      if(systemPause) {
        PWM_SetBrightness(0);
        Serial.println("System Paused");
      } else {
        Serial.println("System Resumed");
      }
      break;
  }
  
  // 呼吸灯效果
  if(!systemPause) {
    Breathing_Effect();
  }
  
  delay(30);  // 延时30ms
}

void Breathing_Effect(void) {
  if(fadeDirection) {
    // 渐亮
    brightness += fadeAmount;
    if(brightness >= 255) {
      brightness = 255;
      fadeDirection = false;  // 转为渐灭
    }
  } else {
    // 渐灭
    brightness -= fadeAmount;
    if(brightness <= 0) {
      brightness = 0;
      fadeDirection = true;   // 转为渐亮
    }
  }
  
  // 设置PWM亮度
  PWM_SetBrightness(brightness);
  
  // 串口显示亮度
  static unsigned long lastPrint = 0;
  if(millis() - lastPrint > 500) {
    Serial.print("Brightness: ");
    Serial.print(brightness);
    Serial.println("/255");
    lastPrint = millis();
  }
}

2.2 PWM控制模块 (pwm_control.h)

#ifndef __PWM_CONTROL_H__
#define __PWM_CONTROL_H__

#include <Arduino.h>

// PWM引脚定义
#define PWM_PIN 9    // PWM输出引脚(必须是PWM引脚)

// 函数声明
void PWM_Init(void);
void PWM_SetBrightness(int brightness);
void PWM_Start(void);
void PWM_Stop(void);

#endif

2.3 PWM控制模块 (pwm_control.cpp)

#include "pwm_control.h"

void PWM_Init(void) {
  pinMode(PWM_PIN, OUTPUT);
  analogWrite(PWM_PIN, 0);  // 初始关闭
}

void PWM_SetBrightness(int brightness) {
  // 限制亮度范围
  if(brightness > 255) brightness = 255;
  if(brightness < 0) brightness = 0;
  
  analogWrite(PWM_PIN, brightness);
}

void PWM_Start(void) {
  // PWM已经启动,无需额外操作
}

void PWM_Stop(void) {
  analogWrite(PWM_PIN, 0);  // 关闭PWM输出
}

2.4 键盘扫描 (keyboard.h)

#ifndef __KEYBOARD_H__
#define __KEYBOARD_H__

#include <Arduino.h>

// 按键定义
#define KEY_ACCEL 1
#define KEY_DECEL 2
#define KEY_PAUSE 3
#define KEY_NONE 0

// 按键引脚
#define KEY_ACC_PIN 2
#define KEY_DEC_PIN 3
#define KEY_PAU_PIN 4

// 函数声明
void Keyboard_Init(void);
int Key_Scan(void);

#endif

2.5 键盘扫描 (keyboard.cpp)

#include "keyboard.h"

void Keyboard_Init(void) {
  pinMode(KEY_ACC_PIN, INPUT_PULLUP);
  pinMode(KEY_DEC_PIN, INPUT_PULLUP);
  pinMode(KEY_PAU_PIN, INPUT_PULLUP);
}

int Key_Scan(void) {
  // 检测加速键
  if(digitalRead(KEY_ACC_PIN) == LOW) {
    delay(50);  // 消抖
    if(digitalRead(KEY_ACC_PIN) == LOW) {
      while(digitalRead(KEY_ACC_PIN) == LOW);  // 等待释放
      return KEY_ACCEL;
    }
  }
  
  // 检测减速键
  if(digitalRead(KEY_DEC_PIN) == LOW) {
    delay(50);
    if(digitalRead(KEY_DEC_PIN) == LOW) {
      while(digitalRead(KEY_DEC_PIN) == LOW);
      return KEY_DECEL;
    }
  }
  
  // 检测暂停键
  if(digitalRead(KEY_PAU_PIN) == LOW) {
    delay(50);
    if(digitalRead(KEY_PAU_PIN) == LOW) {
      while(digitalRead(KEY_PAU_PIN) == LOW);
      return KEY_PAUSE;
    }
  }
  
  return KEY_NONE;
}

三、实验现象

3.1 预期效果

  1. LED呼吸效果:LED从暗到亮,再从亮到暗,循环变化
  2. 按键控制
    • 加速键:加快呼吸速度
    • 减速键:减慢呼吸速度
    • 暂停键:暂停/继续呼吸效果
  3. 串口输出:实时显示当前亮度值

3.2 参数调整

// 调整呼吸速度
int fadeAmount = 5;  // 增大此值加快呼吸,减小此值减慢呼吸

// 调整延时时间
delay(30);  // 减小此值加快呼吸,增大此值减慢呼吸

参考代码 PWM控制LED灯渐亮渐灭实验 www.youwenfan.com/contentcnv/118243.html

四、扩展功能

4.1 增加多种呼吸模式

// 呼吸模式枚举
enum BreathMode {
  MODE_LINEAR,      // 线性呼吸
  MODE_EXPONENTIAL, // 指数呼吸
  MODE_HEARTBEAT   // 心跳模式
};

void Breathing_Mode_Exponential(void) {
  // 指数呼吸效果
  float expBrightness = pow(2, brightness/32.0) - 1;
  PWM_SetBrightness((int)expBrightness);
}

4.2 增加RGB LED控制

// RGB LED控制
#define RED_PIN 9
#define GREEN_PIN 10
#define BLUE_PIN 11

void RGB_Breathing(void) {
  // 红绿蓝依次呼吸
  for(int i = 0; i < 256; i++) {
    analogWrite(RED_PIN, i);
    delay(10);
  }
  // ... 类似处理绿色和蓝色
}

4.3 增加光敏电阻自动调节

// 光敏电阻自动调节亮度
int lightSensorPin = A0;

void Auto_Brightness(void) {
  int lightValue = analogRead(lightSensorPin);
  int autoBrightness = map(lightValue, 0, 1023, 0, 255);
  PWM_SetBrightness(autoBrightness);
}

五、注意事项

5.1 硬件注意事项

  1. 限流电阻:LED必须串联220Ω-1kΩ限流电阻
  2. PWM引脚:Arduino必须使用带~标记的PWM引脚
  3. 电源稳定:确保5V电源稳定,避免LED闪烁

5.2 软件注意事项

  1. 占空比范围:PWM占空比通常为0-255(8位)或0-100(百分比)
  2. 延时时间:延时过短可能导致LED闪烁,过长则呼吸效果不明显
  3. 中断冲突:避免在PWM中断中进行耗时操作

5.3 故障排除

问题 解决方法
LED不亮 检查限流电阻、引脚连接、PWM输出
呼吸不均匀 调整fadeAmount值和延时时间
按键不灵敏 增加消抖延时,检查上拉电阻
亮度突变 确保亮度值在0-255范围内变化

六、实验总结

通过这个实验,您可以:

  1. 理解PWM工作原理和应用
  2. 掌握LED亮度控制方法
  3. 学会定时器中断编程
  4. 实现呼吸灯效果
  5. 扩展应用到其他PWM控制场景
posted @ 2026-06-10 10:29  hczyydqq  阅读(25)  评论(0)    收藏  举报