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 预期效果
- LED呼吸效果:LED从暗到亮,再从亮到暗,循环变化
- 按键控制:
- 加速键:加快呼吸速度
- 减速键:减慢呼吸速度
- 暂停键:暂停/继续呼吸效果
- 串口输出:实时显示当前亮度值
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 硬件注意事项
- 限流电阻:LED必须串联220Ω-1kΩ限流电阻
- PWM引脚:Arduino必须使用带~标记的PWM引脚
- 电源稳定:确保5V电源稳定,避免LED闪烁
5.2 软件注意事项
- 占空比范围:PWM占空比通常为0-255(8位)或0-100(百分比)
- 延时时间:延时过短可能导致LED闪烁,过长则呼吸效果不明显
- 中断冲突:避免在PWM中断中进行耗时操作
5.3 故障排除
| 问题 | 解决方法 |
|---|---|
| LED不亮 | 检查限流电阻、引脚连接、PWM输出 |
| 呼吸不均匀 | 调整fadeAmount值和延时时间 |
| 按键不灵敏 | 增加消抖延时,检查上拉电阻 |
| 亮度突变 | 确保亮度值在0-255范围内变化 |
六、实验总结
通过这个实验,您可以:
- 理解PWM工作原理和应用
- 掌握LED亮度控制方法
- 学会定时器中断编程
- 实现呼吸灯效果
- 扩展应用到其他PWM控制场景

浙公网安备 33010602011771号